diff options
author | Ayke van Laethem <[email protected]> | 2019-12-19 14:16:58 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2019-12-20 01:43:12 +0100 |
commit | 9aeb8d9e06464ac8ea8ae7f99ea51235db6dbf5a (patch) | |
tree | 2c96d718a25b2b249d706d0f4a5fa66f4ed890aa /interp | |
parent | 34ee3883d64da99e216ed478d72556ef76274851 (diff) | |
download | tinygo-9aeb8d9e06464ac8ea8ae7f99ea51235db6dbf5a.tar.gz tinygo-9aeb8d9e06464ac8ea8ae7f99ea51235db6dbf5a.zip |
interp: support llvm.lifetime.* calls
This fixes a bug found when updating a map with string keys.
Originally reported here:
https://github.com/hybridgroup/gdg-2019/issues/6
Diffstat (limited to 'interp')
-rw-r--r-- | interp/frame.go | 2 | ||||
-rw-r--r-- | interp/interp_test.go | 1 | ||||
-rw-r--r-- | interp/scan.go | 21 | ||||
-rw-r--r-- | interp/testdata/map.ll | 36 | ||||
-rw-r--r-- | interp/testdata/map.out.ll | 15 | ||||
-rw-r--r-- | interp/values.go | 12 |
6 files changed, 75 insertions, 12 deletions
diff --git a/interp/frame.go b/interp/frame.go index 04a9fa0cd..65d76c8cb 100644 --- a/interp/frame.go +++ b/interp/frame.go @@ -433,6 +433,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)} case callee.Name() == "llvm.dbg.value": // do nothing + case strings.HasPrefix(callee.Name(), "llvm.lifetime."): + // do nothing case callee.Name() == "runtime.trackPointer": // do nothing case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic": diff --git a/interp/interp_test.go b/interp/interp_test.go index 789181b52..702392f17 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -14,6 +14,7 @@ func TestInterp(t *testing.T) { "basic", "slice-copy", "consteval", + "map", } { name := name // make tc local to this closure t.Run(name, func(t *testing.T) { diff --git a/interp/scan.go b/interp/scan.go index 7a256c27c..9def32402 100644 --- a/interp/scan.go +++ b/interp/scan.go @@ -1,6 +1,8 @@ package interp import ( + "strings" + "tinygo.org/x/go-llvm" ) @@ -39,22 +41,25 @@ type sideEffectResult struct { // returns whether this function has side effects and if it does, which globals // it mentions anywhere in this function or any called functions. func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) { - switch fn.Name() { - case "runtime.alloc": + name := fn.Name() + switch { + case name == "runtime.alloc": // Cannot be scanned but can be interpreted. return &sideEffectResult{severity: sideEffectNone}, nil - case "runtime.nanotime": + case name == "runtime.nanotime": // Fixed value at compile time. return &sideEffectResult{severity: sideEffectNone}, nil - case "runtime._panic": + case name == "runtime._panic": return &sideEffectResult{severity: sideEffectLimited}, nil - case "runtime.interfaceImplements": + case name == "runtime.interfaceImplements": + return &sideEffectResult{severity: sideEffectNone}, nil + case name == "runtime.sliceCopy": return &sideEffectResult{severity: sideEffectNone}, nil - case "runtime.sliceCopy": + case name == "runtime.trackPointer": return &sideEffectResult{severity: sideEffectNone}, nil - case "runtime.trackPointer": + case name == "llvm.dbg.value": return &sideEffectResult{severity: sideEffectNone}, nil - case "llvm.dbg.value": + case strings.HasPrefix(name, "llvm.lifetime."): return &sideEffectResult{severity: sideEffectNone}, nil } if fn.IsDeclaration() { diff --git a/interp/testdata/map.ll b/interp/testdata/map.ll new file mode 100644 index 000000000..05d9e9168 --- /dev/null +++ b/interp/testdata/map.ll @@ -0,0 +1,36 @@ +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "armv6m-none-eabi" + +%runtime._string = type { i8*, i32 } +%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 } + [email protected] = global %runtime.hashmap* null [email protected] = internal unnamed_addr constant [7 x i8] c"CONNECT" + +declare %runtime.hashmap* @runtime.hashmapMake(i8, i8, i32, i8* %context, i8* %parentHandle) +declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8* %context, i8* %parentHandle) +declare void @llvm.lifetime.end.p0i8(i64, i8*) +declare void @llvm.lifetime.start.p0i8(i64, i8*) + +define void @runtime.initAll() unnamed_addr { +entry: + call void @main.init(i8* undef, i8* null) + ret void +} + +define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %hashmap.key = alloca i8 + %hashmap.value = alloca %runtime._string + %0 = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 8, i32 1, i8* undef, i8* null) + %hashmap.value.bitcast = bitcast %runtime._string* %hashmap.value to i8* + call void @llvm.lifetime.start.p0i8(i64 8, i8* %hashmap.value.bitcast) + store %runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string* %hashmap.value + call void @llvm.lifetime.start.p0i8(i64 1, i8* %hashmap.key) + store i8 1, i8* %hashmap.key + call void @runtime.hashmapBinarySet(%runtime.hashmap* %0, i8* %hashmap.key, i8* %hashmap.value.bitcast, i8* undef, i8* null) + call void @llvm.lifetime.end.p0i8(i64 1, i8* %hashmap.key) + call void @llvm.lifetime.end.p0i8(i64 8, i8* %hashmap.value.bitcast) + store %runtime.hashmap* %0, %runtime.hashmap** @main.m + ret void +} diff --git a/interp/testdata/map.out.ll b/interp/testdata/map.out.ll new file mode 100644 index 000000000..7806226bd --- /dev/null +++ b/interp/testdata/map.out.ll @@ -0,0 +1,15 @@ +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "armv6m-none-eabi" + +%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 } +%runtime._string = type { i8*, i32 } + [email protected] = local_unnamed_addr global %runtime.hashmap* @"main$map" [email protected] = internal unnamed_addr constant [7 x i8] c"CONNECT" +@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, [8 x i8] c"\01\00\00\00\00\00\00\00", [8 x %runtime._string] [%runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer] } +@"main$map" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }, { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 } + +define void @runtime.initAll() unnamed_addr { +entry: + ret void +} diff --git a/interp/values.go b/interp/values.go index e24a80d61..c1fc23e3c 100644 --- a/interp/values.go +++ b/interp/values.go @@ -367,10 +367,14 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) { } } - if keyPtr.Underlying.Opcode() == llvm.BitCast { - keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)} - } else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr { - keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)} + keyPtr.Underlying.Dump() + println() + if !keyPtr.Underlying.IsAConstantExpr().IsNil() { + if keyPtr.Underlying.Opcode() == llvm.BitCast { + keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)} + } else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr { + keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)} + } } key := keyPtr.Load() if v.KeyType.IsNil() { |