Skip to content

Commit 73f519b

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent 25abfff commit 73f519b

File tree

5 files changed

+110
-85
lines changed

5 files changed

+110
-85
lines changed

compiler/llvmutil/llvm.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package llvmutil
99

1010
import (
11+
"encoding/binary"
1112
"strconv"
1213
"strings"
1314

@@ -216,3 +217,13 @@ func Version() int {
216217
}
217218
return major
218219
}
220+
221+
// Return the byte order for the given target triple. Most targets are little
222+
// endian, but for example MIPS can be big-endian.
223+
func ByteOrder(target string) binary.ByteOrder {
224+
if strings.HasPrefix(target, "mips-") {
225+
return binary.BigEndian
226+
} else {
227+
return binary.LittleEndian
228+
}
229+
}

interp/interp.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
package interp
44

55
import (
6+
"encoding/binary"
67
"fmt"
78
"os"
89
"strings"
910
"time"
1011

12+
"github.com/tinygo-org/tinygo/compiler/llvmutil"
1113
"tinygo.org/x/go-llvm"
1214
)
1315

@@ -24,6 +26,7 @@ type runner struct {
2426
dataPtrType llvm.Type // often used type so created in advance
2527
uintptrType llvm.Type // equivalent to uintptr in Go
2628
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
29+
byteOrder binary.ByteOrder // big-endian or little-endian
2730
debug bool // log debug messages
2831
pkgName string // package name of the currently executing package
2932
functionCache map[llvm.Value]*function // cache of compiled functions
@@ -38,6 +41,7 @@ func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
3841
r := runner{
3942
mod: mod,
4043
targetData: llvm.NewTargetData(mod.DataLayout()),
44+
byteOrder: llvmutil.ByteOrder(mod.Target()),
4145
debug: debug,
4246
functionCache: make(map[llvm.Value]*function),
4347
objects: []object{{}},

interp/interpreter.go

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
173173
case 3:
174174
// Conditional branch: [cond, thenBB, elseBB]
175175
lastBB = currentBB
176-
switch operands[0].Uint() {
176+
switch operands[0].Uint(r) {
177177
case 1: // true -> thenBB
178178
currentBB = int(operands[1].(literalValue).value.(uint32))
179179
case 0: // false -> elseBB
@@ -191,12 +191,12 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
191191
}
192192
case llvm.Switch:
193193
// Switch statement: [value, defaultLabel, case0, label0, case1, label1, ...]
194-
value := operands[0].Uint()
195-
targetLabel := operands[1].Uint() // default label
194+
value := operands[0].Uint(r)
195+
targetLabel := operands[1].Uint(r) // default label
196196
// Do a lazy switch by iterating over all cases.
197197
for i := 2; i < len(operands); i += 2 {
198-
if value == operands[i].Uint() {
199-
targetLabel = operands[i+1].Uint()
198+
if value == operands[i].Uint(r) {
199+
targetLabel = operands[i+1].Uint(r)
200200
break
201201
}
202202
}
@@ -211,7 +211,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
211211
// Select is much like a ternary operator: it picks a result from
212212
// the second and third operand based on the boolean first operand.
213213
var result value
214-
switch operands[0].Uint() {
214+
switch operands[0].Uint(r) {
215215
case 1:
216216
result = operands[1]
217217
case 0:
@@ -282,7 +282,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
282282
// by creating a global variable.
283283

284284
// Get the requested memory size to be allocated.
285-
size := operands[1].Uint()
285+
size := operands[1].Uint(r)
286286

287287
// Get the object layout, if it is available.
288288
llvmLayoutType := r.getLLVMTypeFromLayout(operands[2])
@@ -318,9 +318,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
318318
// memmove(dst, src, n*elemSize)
319319
// return int(n)
320320
// }
321-
dstLen := operands[3].Uint()
322-
srcLen := operands[4].Uint()
323-
elemSize := operands[5].Uint()
321+
dstLen := operands[3].Uint(r)
322+
srcLen := operands[4].Uint(r)
323+
elemSize := operands[5].Uint(r)
324324
n := srcLen
325325
if n > dstLen {
326326
n = dstLen
@@ -374,7 +374,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
374374
if err != nil {
375375
return nil, mem, r.errorAt(inst, err)
376376
}
377-
nBytes := uint32(operands[3].Uint())
377+
nBytes := uint32(operands[3].Uint(r))
378378
dstObj := mem.getWritable(dst.index())
379379
dstBuf := dstObj.buffer.asRawValue(r)
380380
if mem.get(src.index()).buffer == nil {
@@ -661,8 +661,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
661661
// pointer into the underlying object.
662662
var offset int64
663663
for i := 1; i < len(operands); i += 2 {
664-
index := operands[i].Int()
665-
elementSize := operands[i+1].Int()
664+
index := operands[i].Int(r)
665+
elementSize := operands[i+1].Int(r)
666666
if elementSize < 0 {
667667
// This is a struct field.
668668
offset += index
@@ -677,7 +677,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
677677
return nil, mem, r.errorAt(inst, err)
678678
}
679679
// GEP on fixed pointer value (for example, memory-mapped I/O).
680-
ptrValue := operands[0].Uint() + uint64(offset)
680+
ptrValue := operands[0].Uint(r) + uint64(offset)
681681
locals[inst.localIndex] = makeLiteralInt(ptrValue, int(operands[0].len(r)*8))
682682
continue
683683
}
@@ -739,11 +739,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
739739
var lhs, rhs float64
740740
switch operands[0].len(r) {
741741
case 8:
742-
lhs = math.Float64frombits(operands[0].Uint())
743-
rhs = math.Float64frombits(operands[1].Uint())
742+
lhs = math.Float64frombits(operands[0].Uint(r))
743+
rhs = math.Float64frombits(operands[1].Uint(r))
744744
case 4:
745-
lhs = float64(math.Float32frombits(uint32(operands[0].Uint())))
746-
rhs = float64(math.Float32frombits(uint32(operands[1].Uint())))
745+
lhs = float64(math.Float32frombits(uint32(operands[0].Uint(r))))
746+
rhs = float64(math.Float32frombits(uint32(operands[1].Uint(r))))
747747
default:
748748
panic("unknown float type")
749749
}
@@ -782,23 +782,23 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
782782
if inst.opcode == llvm.Add {
783783
// This likely means this is part of a
784784
// unsafe.Pointer(uintptr(ptr) + offset) pattern.
785-
lhsPtr, err = lhsPtr.addOffset(int64(rhs.Uint()))
785+
lhsPtr, err = lhsPtr.addOffset(int64(rhs.Uint(r)))
786786
if err != nil {
787787
return nil, mem, r.errorAt(inst, err)
788788
}
789789
locals[inst.localIndex] = lhsPtr
790-
} else if inst.opcode == llvm.Xor && rhs.Uint() == 0 {
790+
} else if inst.opcode == llvm.Xor && rhs.Uint(r) == 0 {
791791
// Special workaround for strings.noescape, see
792792
// src/strings/builder.go in the Go source tree. This is
793793
// the identity operator, so we can return the input.
794794
locals[inst.localIndex] = lhs
795-
} else if inst.opcode == llvm.And && rhs.Uint() < 8 {
795+
} else if inst.opcode == llvm.And && rhs.Uint(r) < 8 {
796796
// This is probably part of a pattern to get the lower bits
797797
// of a pointer for pointer tagging, like this:
798798
// uintptr(unsafe.Pointer(t)) & 0b11
799799
// We can actually support this easily by ANDing with the
800800
// pointer offset.
801-
result := uint64(lhsPtr.offset()) & rhs.Uint()
801+
result := uint64(lhsPtr.offset()) & rhs.Uint(r)
802802
locals[inst.localIndex] = makeLiteralInt(result, int(lhs.len(r)*8))
803803
} else {
804804
// 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
813813
var result uint64
814814
switch inst.opcode {
815815
case llvm.Add:
816-
result = lhs.Uint() + rhs.Uint()
816+
result = lhs.Uint(r) + rhs.Uint(r)
817817
case llvm.Sub:
818-
result = lhs.Uint() - rhs.Uint()
818+
result = lhs.Uint(r) - rhs.Uint(r)
819819
case llvm.Mul:
820-
result = lhs.Uint() * rhs.Uint()
820+
result = lhs.Uint(r) * rhs.Uint(r)
821821
case llvm.UDiv:
822-
result = lhs.Uint() / rhs.Uint()
822+
result = lhs.Uint(r) / rhs.Uint(r)
823823
case llvm.SDiv:
824-
result = uint64(lhs.Int() / rhs.Int())
824+
result = uint64(lhs.Int(r) / rhs.Int(r))
825825
case llvm.URem:
826-
result = lhs.Uint() % rhs.Uint()
826+
result = lhs.Uint(r) % rhs.Uint(r)
827827
case llvm.SRem:
828-
result = uint64(lhs.Int() % rhs.Int())
828+
result = uint64(lhs.Int(r) % rhs.Int(r))
829829
case llvm.Shl:
830-
result = lhs.Uint() << rhs.Uint()
830+
result = lhs.Uint(r) << rhs.Uint(r)
831831
case llvm.LShr:
832-
result = lhs.Uint() >> rhs.Uint()
832+
result = lhs.Uint(r) >> rhs.Uint(r)
833833
case llvm.AShr:
834-
result = uint64(lhs.Int() >> rhs.Uint())
834+
result = uint64(lhs.Int(r) >> rhs.Uint(r))
835835
case llvm.And:
836-
result = lhs.Uint() & rhs.Uint()
836+
result = lhs.Uint(r) & rhs.Uint(r)
837837
case llvm.Or:
838-
result = lhs.Uint() | rhs.Uint()
838+
result = lhs.Uint(r) | rhs.Uint(r)
839839
case llvm.Xor:
840-
result = lhs.Uint() ^ rhs.Uint()
840+
result = lhs.Uint(r) ^ rhs.Uint(r)
841841
default:
842842
panic("unreachable")
843843
}
@@ -855,11 +855,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
855855
// and then truncating it as necessary.
856856
var value uint64
857857
if inst.opcode == llvm.SExt {
858-
value = uint64(operands[0].Int())
858+
value = uint64(operands[0].Int(r))
859859
} else {
860-
value = operands[0].Uint()
860+
value = operands[0].Uint(r)
861861
}
862-
bitwidth := operands[1].Uint()
862+
bitwidth := operands[1].Uint(r)
863863
if r.debug {
864864
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth)
865865
}
@@ -868,11 +868,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
868868
var value float64
869869
switch inst.opcode {
870870
case llvm.SIToFP:
871-
value = float64(operands[0].Int())
871+
value = float64(operands[0].Int(r))
872872
case llvm.UIToFP:
873-
value = float64(operands[0].Uint())
873+
value = float64(operands[0].Uint(r))
874874
}
875-
bitwidth := operands[1].Uint()
875+
bitwidth := operands[1].Uint(r)
876876
if r.debug {
877877
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth)
878878
}
@@ -918,21 +918,21 @@ func (r *runner) interpretICmp(lhs, rhs value, predicate llvm.IntPredicate) bool
918918
}
919919
return result
920920
case llvm.IntUGT:
921-
return lhs.Uint() > rhs.Uint()
921+
return lhs.Uint(r) > rhs.Uint(r)
922922
case llvm.IntUGE:
923-
return lhs.Uint() >= rhs.Uint()
923+
return lhs.Uint(r) >= rhs.Uint(r)
924924
case llvm.IntULT:
925-
return lhs.Uint() < rhs.Uint()
925+
return lhs.Uint(r) < rhs.Uint(r)
926926
case llvm.IntULE:
927-
return lhs.Uint() <= rhs.Uint()
927+
return lhs.Uint(r) <= rhs.Uint(r)
928928
case llvm.IntSGT:
929-
return lhs.Int() > rhs.Int()
929+
return lhs.Int(r) > rhs.Int(r)
930930
case llvm.IntSGE:
931-
return lhs.Int() >= rhs.Int()
931+
return lhs.Int(r) >= rhs.Int(r)
932932
case llvm.IntSLT:
933-
return lhs.Int() < rhs.Int()
933+
return lhs.Int(r) < rhs.Int(r)
934934
case llvm.IntSLE:
935-
return lhs.Int() <= rhs.Int()
935+
return lhs.Int(r) <= rhs.Int(r)
936936
default:
937937
// _should_ be unreachable, until LLVM adds new icmp operands (unlikely)
938938
panic("interp: unsupported icmp")

0 commit comments

Comments
 (0)