interp: support big-endian targets

The interp package was assuming that all targets were little-endian. But
that's not true: we now have a big-endian target (GOARCH=mips).

This fixes the interp package to use the appropriate byte order for a
given target.
This commit is contained in:
Ayke van Laethem
2024-08-22 16:42:03 +02:00
committed by Ron Evans
parent 25abfff632
commit 73f519b589
5 changed files with 110 additions and 85 deletions
+11
View File
@@ -8,6 +8,7 @@
package llvmutil package llvmutil
import ( import (
"encoding/binary"
"strconv" "strconv"
"strings" "strings"
@@ -216,3 +217,13 @@ func Version() int {
} }
return major return major
} }
// Return the byte order for the given target triple. Most targets are little
// endian, but for example MIPS can be big-endian.
func ByteOrder(target string) binary.ByteOrder {
if strings.HasPrefix(target, "mips-") {
return binary.BigEndian
} else {
return binary.LittleEndian
}
}
+4
View File
@@ -3,11 +3,13 @@
package interp package interp
import ( import (
"encoding/binary"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"time" "time"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -24,6 +26,7 @@ type runner struct {
dataPtrType llvm.Type // often used type so created in advance dataPtrType llvm.Type // often used type so created in advance
uintptrType llvm.Type // equivalent to uintptr in Go uintptrType llvm.Type // equivalent to uintptr in Go
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
byteOrder binary.ByteOrder // big-endian or little-endian
debug bool // log debug messages debug bool // log debug messages
pkgName string // package name of the currently executing package pkgName string // package name of the currently executing package
functionCache map[llvm.Value]*function // cache of compiled functions functionCache map[llvm.Value]*function // cache of compiled functions
@@ -38,6 +41,7 @@ func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
r := runner{ r := runner{
mod: mod, mod: mod,
targetData: llvm.NewTargetData(mod.DataLayout()), targetData: llvm.NewTargetData(mod.DataLayout()),
byteOrder: llvmutil.ByteOrder(mod.Target()),
debug: debug, debug: debug,
functionCache: make(map[llvm.Value]*function), functionCache: make(map[llvm.Value]*function),
objects: []object{{}}, objects: []object{{}},
+49 -49
View File
@@ -173,7 +173,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
case 3: case 3:
// Conditional branch: [cond, thenBB, elseBB] // Conditional branch: [cond, thenBB, elseBB]
lastBB = currentBB lastBB = currentBB
switch operands[0].Uint() { switch operands[0].Uint(r) {
case 1: // true -> thenBB case 1: // true -> thenBB
currentBB = int(operands[1].(literalValue).value.(uint32)) currentBB = int(operands[1].(literalValue).value.(uint32))
case 0: // false -> elseBB case 0: // false -> elseBB
@@ -191,12 +191,12 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
} }
case llvm.Switch: case llvm.Switch:
// Switch statement: [value, defaultLabel, case0, label0, case1, label1, ...] // Switch statement: [value, defaultLabel, case0, label0, case1, label1, ...]
value := operands[0].Uint() value := operands[0].Uint(r)
targetLabel := operands[1].Uint() // default label targetLabel := operands[1].Uint(r) // default label
// Do a lazy switch by iterating over all cases. // Do a lazy switch by iterating over all cases.
for i := 2; i < len(operands); i += 2 { for i := 2; i < len(operands); i += 2 {
if value == operands[i].Uint() { if value == operands[i].Uint(r) {
targetLabel = operands[i+1].Uint() targetLabel = operands[i+1].Uint(r)
break break
} }
} }
@@ -211,7 +211,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// Select is much like a ternary operator: it picks a result from // Select is much like a ternary operator: it picks a result from
// the second and third operand based on the boolean first operand. // the second and third operand based on the boolean first operand.
var result value var result value
switch operands[0].Uint() { switch operands[0].Uint(r) {
case 1: case 1:
result = operands[1] result = operands[1]
case 0: case 0:
@@ -282,7 +282,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// by creating a global variable. // by creating a global variable.
// Get the requested memory size to be allocated. // Get the requested memory size to be allocated.
size := operands[1].Uint() size := operands[1].Uint(r)
// Get the object layout, if it is available. // Get the object layout, if it is available.
llvmLayoutType := r.getLLVMTypeFromLayout(operands[2]) llvmLayoutType := r.getLLVMTypeFromLayout(operands[2])
@@ -318,9 +318,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// memmove(dst, src, n*elemSize) // memmove(dst, src, n*elemSize)
// return int(n) // return int(n)
// } // }
dstLen := operands[3].Uint() dstLen := operands[3].Uint(r)
srcLen := operands[4].Uint() srcLen := operands[4].Uint(r)
elemSize := operands[5].Uint() elemSize := operands[5].Uint(r)
n := srcLen n := srcLen
if n > dstLen { if n > dstLen {
n = dstLen n = dstLen
@@ -374,7 +374,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if err != nil { if err != nil {
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
nBytes := uint32(operands[3].Uint()) nBytes := uint32(operands[3].Uint(r))
dstObj := mem.getWritable(dst.index()) dstObj := mem.getWritable(dst.index())
dstBuf := dstObj.buffer.asRawValue(r) dstBuf := dstObj.buffer.asRawValue(r)
if mem.get(src.index()).buffer == nil { if mem.get(src.index()).buffer == nil {
@@ -661,8 +661,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// pointer into the underlying object. // pointer into the underlying object.
var offset int64 var offset int64
for i := 1; i < len(operands); i += 2 { for i := 1; i < len(operands); i += 2 {
index := operands[i].Int() index := operands[i].Int(r)
elementSize := operands[i+1].Int() elementSize := operands[i+1].Int(r)
if elementSize < 0 { if elementSize < 0 {
// This is a struct field. // This is a struct field.
offset += index offset += index
@@ -677,7 +677,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
// GEP on fixed pointer value (for example, memory-mapped I/O). // GEP on fixed pointer value (for example, memory-mapped I/O).
ptrValue := operands[0].Uint() + uint64(offset) ptrValue := operands[0].Uint(r) + uint64(offset)
locals[inst.localIndex] = makeLiteralInt(ptrValue, int(operands[0].len(r)*8)) locals[inst.localIndex] = makeLiteralInt(ptrValue, int(operands[0].len(r)*8))
continue continue
} }
@@ -739,11 +739,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
var lhs, rhs float64 var lhs, rhs float64
switch operands[0].len(r) { switch operands[0].len(r) {
case 8: case 8:
lhs = math.Float64frombits(operands[0].Uint()) lhs = math.Float64frombits(operands[0].Uint(r))
rhs = math.Float64frombits(operands[1].Uint()) rhs = math.Float64frombits(operands[1].Uint(r))
case 4: case 4:
lhs = float64(math.Float32frombits(uint32(operands[0].Uint()))) lhs = float64(math.Float32frombits(uint32(operands[0].Uint(r))))
rhs = float64(math.Float32frombits(uint32(operands[1].Uint()))) rhs = float64(math.Float32frombits(uint32(operands[1].Uint(r))))
default: default:
panic("unknown float type") panic("unknown float type")
} }
@@ -782,23 +782,23 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if inst.opcode == llvm.Add { if inst.opcode == llvm.Add {
// This likely means this is part of a // This likely means this is part of a
// unsafe.Pointer(uintptr(ptr) + offset) pattern. // unsafe.Pointer(uintptr(ptr) + offset) pattern.
lhsPtr, err = lhsPtr.addOffset(int64(rhs.Uint())) lhsPtr, err = lhsPtr.addOffset(int64(rhs.Uint(r)))
if err != nil { if err != nil {
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
locals[inst.localIndex] = lhsPtr locals[inst.localIndex] = lhsPtr
} else if inst.opcode == llvm.Xor && rhs.Uint() == 0 { } else if inst.opcode == llvm.Xor && rhs.Uint(r) == 0 {
// Special workaround for strings.noescape, see // Special workaround for strings.noescape, see
// src/strings/builder.go in the Go source tree. This is // src/strings/builder.go in the Go source tree. This is
// the identity operator, so we can return the input. // the identity operator, so we can return the input.
locals[inst.localIndex] = lhs locals[inst.localIndex] = lhs
} else if inst.opcode == llvm.And && rhs.Uint() < 8 { } else if inst.opcode == llvm.And && rhs.Uint(r) < 8 {
// This is probably part of a pattern to get the lower bits // This is probably part of a pattern to get the lower bits
// of a pointer for pointer tagging, like this: // of a pointer for pointer tagging, like this:
// uintptr(unsafe.Pointer(t)) & 0b11 // uintptr(unsafe.Pointer(t)) & 0b11
// We can actually support this easily by ANDing with the // We can actually support this easily by ANDing with the
// pointer offset. // pointer offset.
result := uint64(lhsPtr.offset()) & rhs.Uint() result := uint64(lhsPtr.offset()) & rhs.Uint(r)
locals[inst.localIndex] = makeLiteralInt(result, int(lhs.len(r)*8)) locals[inst.localIndex] = makeLiteralInt(result, int(lhs.len(r)*8))
} else { } else {
// Catch-all for weird operations that should just be done // Catch-all for weird operations that should just be done
@@ -813,31 +813,31 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
var result uint64 var result uint64
switch inst.opcode { switch inst.opcode {
case llvm.Add: case llvm.Add:
result = lhs.Uint() + rhs.Uint() result = lhs.Uint(r) + rhs.Uint(r)
case llvm.Sub: case llvm.Sub:
result = lhs.Uint() - rhs.Uint() result = lhs.Uint(r) - rhs.Uint(r)
case llvm.Mul: case llvm.Mul:
result = lhs.Uint() * rhs.Uint() result = lhs.Uint(r) * rhs.Uint(r)
case llvm.UDiv: case llvm.UDiv:
result = lhs.Uint() / rhs.Uint() result = lhs.Uint(r) / rhs.Uint(r)
case llvm.SDiv: case llvm.SDiv:
result = uint64(lhs.Int() / rhs.Int()) result = uint64(lhs.Int(r) / rhs.Int(r))
case llvm.URem: case llvm.URem:
result = lhs.Uint() % rhs.Uint() result = lhs.Uint(r) % rhs.Uint(r)
case llvm.SRem: case llvm.SRem:
result = uint64(lhs.Int() % rhs.Int()) result = uint64(lhs.Int(r) % rhs.Int(r))
case llvm.Shl: case llvm.Shl:
result = lhs.Uint() << rhs.Uint() result = lhs.Uint(r) << rhs.Uint(r)
case llvm.LShr: case llvm.LShr:
result = lhs.Uint() >> rhs.Uint() result = lhs.Uint(r) >> rhs.Uint(r)
case llvm.AShr: case llvm.AShr:
result = uint64(lhs.Int() >> rhs.Uint()) result = uint64(lhs.Int(r) >> rhs.Uint(r))
case llvm.And: case llvm.And:
result = lhs.Uint() & rhs.Uint() result = lhs.Uint(r) & rhs.Uint(r)
case llvm.Or: case llvm.Or:
result = lhs.Uint() | rhs.Uint() result = lhs.Uint(r) | rhs.Uint(r)
case llvm.Xor: case llvm.Xor:
result = lhs.Uint() ^ rhs.Uint() result = lhs.Uint(r) ^ rhs.Uint(r)
default: default:
panic("unreachable") panic("unreachable")
} }
@@ -855,11 +855,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// and then truncating it as necessary. // and then truncating it as necessary.
var value uint64 var value uint64
if inst.opcode == llvm.SExt { if inst.opcode == llvm.SExt {
value = uint64(operands[0].Int()) value = uint64(operands[0].Int(r))
} else { } else {
value = operands[0].Uint() value = operands[0].Uint(r)
} }
bitwidth := operands[1].Uint() bitwidth := operands[1].Uint(r)
if r.debug { if r.debug {
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth) fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth)
} }
@@ -868,11 +868,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
var value float64 var value float64
switch inst.opcode { switch inst.opcode {
case llvm.SIToFP: case llvm.SIToFP:
value = float64(operands[0].Int()) value = float64(operands[0].Int(r))
case llvm.UIToFP: case llvm.UIToFP:
value = float64(operands[0].Uint()) value = float64(operands[0].Uint(r))
} }
bitwidth := operands[1].Uint() bitwidth := operands[1].Uint(r)
if r.debug { if r.debug {
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth) fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth)
} }
@@ -918,21 +918,21 @@ func (r *runner) interpretICmp(lhs, rhs value, predicate llvm.IntPredicate) bool
} }
return result return result
case llvm.IntUGT: case llvm.IntUGT:
return lhs.Uint() > rhs.Uint() return lhs.Uint(r) > rhs.Uint(r)
case llvm.IntUGE: case llvm.IntUGE:
return lhs.Uint() >= rhs.Uint() return lhs.Uint(r) >= rhs.Uint(r)
case llvm.IntULT: case llvm.IntULT:
return lhs.Uint() < rhs.Uint() return lhs.Uint(r) < rhs.Uint(r)
case llvm.IntULE: case llvm.IntULE:
return lhs.Uint() <= rhs.Uint() return lhs.Uint(r) <= rhs.Uint(r)
case llvm.IntSGT: case llvm.IntSGT:
return lhs.Int() > rhs.Int() return lhs.Int(r) > rhs.Int(r)
case llvm.IntSGE: case llvm.IntSGE:
return lhs.Int() >= rhs.Int() return lhs.Int(r) >= rhs.Int(r)
case llvm.IntSLT: case llvm.IntSLT:
return lhs.Int() < rhs.Int() return lhs.Int(r) < rhs.Int(r)
case llvm.IntSLE: case llvm.IntSLE:
return lhs.Int() <= rhs.Int() return lhs.Int(r) <= rhs.Int(r)
default: default:
// _should_ be unreachable, until LLVM adds new icmp operands (unlikely) // _should_ be unreachable, until LLVM adds new icmp operands (unlikely)
panic("interp: unsupported icmp") panic("interp: unsupported icmp")
+41 -35
View File
@@ -361,8 +361,8 @@ type value interface {
clone() value clone() value
asPointer(*runner) (pointerValue, error) asPointer(*runner) (pointerValue, error)
asRawValue(*runner) rawValue asRawValue(*runner) rawValue
Uint() uint64 Uint(*runner) uint64
Int() int64 Int(*runner) int64
toLLVMValue(llvm.Type, *memoryView) (llvm.Value, error) toLLVMValue(llvm.Type, *memoryView) (llvm.Value, error)
String() string String() string
} }
@@ -405,7 +405,8 @@ func (v literalValue) len(r *runner) uint32 {
} }
func (v literalValue) String() string { func (v literalValue) String() string {
return strconv.FormatInt(v.Int(), 10) // Note: passing a nil *runner to v.Int because we know it won't use it.
return strconv.FormatInt(v.Int(nil), 10)
} }
func (v literalValue) clone() value { func (v literalValue) clone() value {
@@ -421,13 +422,13 @@ func (v literalValue) asRawValue(r *runner) rawValue {
switch value := v.value.(type) { switch value := v.value.(type) {
case uint64: case uint64:
buf = make([]byte, 8) buf = make([]byte, 8)
binary.LittleEndian.PutUint64(buf, value) r.byteOrder.PutUint64(buf, value)
case uint32: case uint32:
buf = make([]byte, 4) buf = make([]byte, 4)
binary.LittleEndian.PutUint32(buf, uint32(value)) r.byteOrder.PutUint32(buf, uint32(value))
case uint16: case uint16:
buf = make([]byte, 2) buf = make([]byte, 2)
binary.LittleEndian.PutUint16(buf, uint16(value)) r.byteOrder.PutUint16(buf, uint16(value))
case uint8: case uint8:
buf = []byte{uint8(value)} buf = []byte{uint8(value)}
default: default:
@@ -440,7 +441,7 @@ func (v literalValue) asRawValue(r *runner) rawValue {
return raw return raw
} }
func (v literalValue) Uint() uint64 { func (v literalValue) Uint(r *runner) uint64 {
switch value := v.value.(type) { switch value := v.value.(type) {
case uint64: case uint64:
return value return value
@@ -455,7 +456,7 @@ func (v literalValue) Uint() uint64 {
} }
} }
func (v literalValue) Int() int64 { func (v literalValue) Int(r *runner) int64 {
switch value := v.value.(type) { switch value := v.value.(type) {
case uint64: case uint64:
return int64(value) return int64(value)
@@ -553,11 +554,11 @@ func (v pointerValue) asRawValue(r *runner) rawValue {
return rv return rv
} }
func (v pointerValue) Uint() uint64 { func (v pointerValue) Uint(r *runner) uint64 {
panic("cannot convert pointer to integer") panic("cannot convert pointer to integer")
} }
func (v pointerValue) Int() int64 { func (v pointerValue) Int(r *runner) int64 {
panic("cannot convert pointer to integer") panic("cannot convert pointer to integer")
} }
@@ -702,7 +703,12 @@ func (v rawValue) String() string {
} }
// Format as number if none of the buf is a pointer. // Format as number if none of the buf is a pointer.
if !v.hasPointer() { if !v.hasPointer() {
return strconv.FormatInt(v.Int(), 10) // Construct a fake runner, which is little endian.
// We only use String() for debugging, so this is is good enough
// (the printed value will just be slightly wrong when debugging the
// interp package with GOOS=mips for example).
r := &runner{byteOrder: binary.LittleEndian}
return strconv.FormatInt(v.Int(r), 10)
} }
} }
return "<[…" + strconv.Itoa(len(v.buf)) + "]>" return "<[…" + strconv.Itoa(len(v.buf)) + "]>"
@@ -738,33 +744,33 @@ func (v rawValue) bytes() []byte {
return buf return buf
} }
func (v rawValue) Uint() uint64 { func (v rawValue) Uint(r *runner) uint64 {
buf := v.bytes() buf := v.bytes()
switch len(v.buf) { switch len(v.buf) {
case 1: case 1:
return uint64(buf[0]) return uint64(buf[0])
case 2: case 2:
return uint64(binary.LittleEndian.Uint16(buf)) return uint64(r.byteOrder.Uint16(buf))
case 4: case 4:
return uint64(binary.LittleEndian.Uint32(buf)) return uint64(r.byteOrder.Uint32(buf))
case 8: case 8:
return binary.LittleEndian.Uint64(buf) return r.byteOrder.Uint64(buf)
default: default:
panic("unknown integer size") panic("unknown integer size")
} }
} }
func (v rawValue) Int() int64 { func (v rawValue) Int(r *runner) int64 {
switch len(v.buf) { switch len(v.buf) {
case 1: case 1:
return int64(int8(v.Uint())) return int64(int8(v.Uint(r)))
case 2: case 2:
return int64(int16(v.Uint())) return int64(int16(v.Uint(r)))
case 4: case 4:
return int64(int32(v.Uint())) return int64(int32(v.Uint(r)))
case 8: case 8:
return int64(int64(v.Uint())) return int64(int64(v.Uint(r)))
default: default:
panic("unknown integer size") panic("unknown integer size")
} }
@@ -878,11 +884,11 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
var n uint64 var n uint64
switch llvmType.IntTypeWidth() { switch llvmType.IntTypeWidth() {
case 64: case 64:
n = rawValue{v.buf[:8]}.Uint() n = rawValue{v.buf[:8]}.Uint(mem.r)
case 32: case 32:
n = rawValue{v.buf[:4]}.Uint() n = rawValue{v.buf[:4]}.Uint(mem.r)
case 16: case 16:
n = rawValue{v.buf[:2]}.Uint() n = rawValue{v.buf[:2]}.Uint(mem.r)
case 8: case 8:
n = uint64(v.buf[0]) n = uint64(v.buf[0])
case 1: case 1:
@@ -951,7 +957,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
} }
// This is either a null pointer or a raw pointer for memory-mapped I/O // This is either a null pointer or a raw pointer for memory-mapped I/O
// (such as 0xe000ed00). // (such as 0xe000ed00).
ptr := rawValue{v.buf[:mem.r.pointerSize]}.Uint() ptr := rawValue{v.buf[:mem.r.pointerSize]}.Uint(mem.r)
if ptr == 0 { if ptr == 0 {
// Null pointer. // Null pointer.
return llvm.ConstNull(llvmType), nil return llvm.ConstNull(llvmType), nil
@@ -969,11 +975,11 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
} }
return llvm.ConstIntToPtr(ptrValue, llvmType), nil return llvm.ConstIntToPtr(ptrValue, llvmType), nil
case llvm.DoubleTypeKind: case llvm.DoubleTypeKind:
b := rawValue{v.buf[:8]}.Uint() b := rawValue{v.buf[:8]}.Uint(mem.r)
f := math.Float64frombits(b) f := math.Float64frombits(b)
return llvm.ConstFloat(llvmType, f), nil return llvm.ConstFloat(llvmType, f), nil
case llvm.FloatTypeKind: case llvm.FloatTypeKind:
b := uint32(rawValue{v.buf[:4]}.Uint()) b := uint32(rawValue{v.buf[:4]}.Uint(mem.r))
f := math.Float32frombits(b) f := math.Float32frombits(b)
return llvm.ConstFloat(llvmType, float64(f)), nil return llvm.ConstFloat(llvmType, float64(f)), nil
default: default:
@@ -1065,19 +1071,19 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
switch llvmValue.Type().IntTypeWidth() { switch llvmValue.Type().IntTypeWidth() {
case 64: case 64:
var buf [8]byte var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], n) r.byteOrder.PutUint64(buf[:], n)
for i, b := range buf { for i, b := range buf {
v.buf[i] = uint64(b) v.buf[i] = uint64(b)
} }
case 32: case 32:
var buf [4]byte var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], uint32(n)) r.byteOrder.PutUint32(buf[:], uint32(n))
for i, b := range buf { for i, b := range buf {
v.buf[i] = uint64(b) v.buf[i] = uint64(b)
} }
case 16: case 16:
var buf [2]byte var buf [2]byte
binary.LittleEndian.PutUint16(buf[:], uint16(n)) r.byteOrder.PutUint16(buf[:], uint16(n))
for i, b := range buf { for i, b := range buf {
v.buf[i] = uint64(b) v.buf[i] = uint64(b)
} }
@@ -1109,14 +1115,14 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
case llvm.DoubleTypeKind: case llvm.DoubleTypeKind:
f, _ := llvmValue.DoubleValue() f, _ := llvmValue.DoubleValue()
var buf [8]byte var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], math.Float64bits(f)) r.byteOrder.PutUint64(buf[:], math.Float64bits(f))
for i, b := range buf { for i, b := range buf {
v.buf[i] = uint64(b) v.buf[i] = uint64(b)
} }
case llvm.FloatTypeKind: case llvm.FloatTypeKind:
f, _ := llvmValue.DoubleValue() f, _ := llvmValue.DoubleValue()
var buf [4]byte var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], math.Float32bits(float32(f))) r.byteOrder.PutUint32(buf[:], math.Float32bits(float32(f)))
for i, b := range buf { for i, b := range buf {
v.buf[i] = uint64(b) v.buf[i] = uint64(b)
} }
@@ -1166,11 +1172,11 @@ func (v localValue) asRawValue(r *runner) rawValue {
panic("interp: localValue.asRawValue") panic("interp: localValue.asRawValue")
} }
func (v localValue) Uint() uint64 { func (v localValue) Uint(r *runner) uint64 {
panic("interp: localValue.Uint") panic("interp: localValue.Uint")
} }
func (v localValue) Int() int64 { func (v localValue) Int(r *runner) int64 {
panic("interp: localValue.Int") panic("interp: localValue.Int")
} }
@@ -1254,7 +1260,7 @@ func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
ptr, err := layoutValue.asPointer(r) ptr, err := layoutValue.asPointer(r)
if err == errIntegerAsPointer { if err == errIntegerAsPointer {
// It's an integer, which means it's a small object or unknown. // It's an integer, which means it's a small object or unknown.
layout := layoutValue.Uint() layout := layoutValue.Uint(r)
if layout == 0 { if layout == 0 {
// Nil pointer, which means the layout is unknown. // Nil pointer, which means the layout is unknown.
return 0, nil return 0, nil
@@ -1287,7 +1293,7 @@ func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
// Read the object size in words and the bitmap from the global. // Read the object size in words and the bitmap from the global.
buf := r.objects[ptr.index()].buffer.(rawValue) buf := r.objects[ptr.index()].buffer.(rawValue)
objectSizeWords := rawValue{buf: buf.buf[:r.pointerSize]}.Uint() objectSizeWords := rawValue{buf: buf.buf[:r.pointerSize]}.Uint(r)
rawByteValues := buf.buf[r.pointerSize:] rawByteValues := buf.buf[r.pointerSize:]
rawBytes := make([]byte, len(rawByteValues)) rawBytes := make([]byte, len(rawByteValues))
for i, v := range rawByteValues { for i, v := range rawByteValues {
+5 -1
View File
@@ -181,9 +181,13 @@ func TestBuild(t *testing.T) {
// Run a single test for GOARCH=mips to see whether it works at all. // Run a single test for GOARCH=mips to see whether it works at all.
// Big-endian MIPS isn't fully supported yet, but simple examples // Big-endian MIPS isn't fully supported yet, but simple examples
// should work. // should work.
// Once big-endian is fully supported, we can probably flip this
// around and do full testing of MIPS big-endian support and only do
// limited testing of MIPS little-endian (because the two are some
// similar).
t.Parallel() t.Parallel()
options := optionsFromOSARCH("linux/mips/softfloat", sema) options := optionsFromOSARCH("linux/mips/softfloat", sema)
runTest("alias.go", options, t, nil, nil) runTest("map.go", options, t, nil, nil)
}) })
t.Run("WebAssembly", func(t *testing.T) { t.Run("WebAssembly", func(t *testing.T) {
t.Parallel() t.Parallel()