aboutsummaryrefslogtreecommitdiffhomepage
path: root/interp
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-12-19 14:16:58 +0100
committerRon Evans <[email protected]>2019-12-20 01:43:12 +0100
commit9aeb8d9e06464ac8ea8ae7f99ea51235db6dbf5a (patch)
tree2c96d718a25b2b249d706d0f4a5fa66f4ed890aa /interp
parent34ee3883d64da99e216ed478d72556ef76274851 (diff)
downloadtinygo-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.go2
-rw-r--r--interp/interp_test.go1
-rw-r--r--interp/scan.go21
-rw-r--r--interp/testdata/map.ll36
-rw-r--r--interp/testdata/map.out.ll15
-rw-r--r--interp/values.go12
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() {