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
Этот коммит содержится в:
Ayke van Laethem 2019-12-19 14:16:58 +01:00 коммит произвёл Ron Evans
родитель 34ee3883d6
коммит 9aeb8d9e06
6 изменённых файлов: 75 добавлений и 12 удалений

Просмотреть файл

@ -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)} fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
case callee.Name() == "llvm.dbg.value": case callee.Name() == "llvm.dbg.value":
// do nothing // do nothing
case strings.HasPrefix(callee.Name(), "llvm.lifetime."):
// do nothing
case callee.Name() == "runtime.trackPointer": case callee.Name() == "runtime.trackPointer":
// do nothing // do nothing
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic": case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":

Просмотреть файл

@ -14,6 +14,7 @@ func TestInterp(t *testing.T) {
"basic", "basic",
"slice-copy", "slice-copy",
"consteval", "consteval",
"map",
} { } {
name := name // make tc local to this closure name := name // make tc local to this closure
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {

Просмотреть файл

@ -1,6 +1,8 @@
package interp package interp
import ( import (
"strings"
"tinygo.org/x/go-llvm" "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 // returns whether this function has side effects and if it does, which globals
// it mentions anywhere in this function or any called functions. // it mentions anywhere in this function or any called functions.
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) { func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
switch fn.Name() { name := fn.Name()
case "runtime.alloc": switch {
case name == "runtime.alloc":
// Cannot be scanned but can be interpreted. // Cannot be scanned but can be interpreted.
return &sideEffectResult{severity: sideEffectNone}, nil return &sideEffectResult{severity: sideEffectNone}, nil
case "runtime.nanotime": case name == "runtime.nanotime":
// Fixed value at compile time. // Fixed value at compile time.
return &sideEffectResult{severity: sideEffectNone}, nil return &sideEffectResult{severity: sideEffectNone}, nil
case "runtime._panic": case name == "runtime._panic":
return &sideEffectResult{severity: sideEffectLimited}, nil return &sideEffectResult{severity: sideEffectLimited}, nil
case "runtime.interfaceImplements": case name == "runtime.interfaceImplements":
return &sideEffectResult{severity: sideEffectNone}, nil return &sideEffectResult{severity: sideEffectNone}, nil
case "runtime.sliceCopy": case name == "runtime.sliceCopy":
return &sideEffectResult{severity: sideEffectNone}, nil return &sideEffectResult{severity: sideEffectNone}, nil
case "runtime.trackPointer": case name == "runtime.trackPointer":
return &sideEffectResult{severity: sideEffectNone}, nil return &sideEffectResult{severity: sideEffectNone}, nil
case "llvm.dbg.value": case name == "llvm.dbg.value":
return &sideEffectResult{severity: sideEffectNone}, nil
case strings.HasPrefix(name, "llvm.lifetime."):
return &sideEffectResult{severity: sideEffectNone}, nil return &sideEffectResult{severity: sideEffectNone}, nil
} }
if fn.IsDeclaration() { if fn.IsDeclaration() {

36
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 }
@main.m = global %runtime.hashmap* null
@main.init.string = 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
}

15
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 }
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
@main.init.string = 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
}

Просмотреть файл

@ -367,11 +367,15 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
} }
} }
keyPtr.Underlying.Dump()
println()
if !keyPtr.Underlying.IsAConstantExpr().IsNil() {
if keyPtr.Underlying.Opcode() == llvm.BitCast { if keyPtr.Underlying.Opcode() == llvm.BitCast {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)} keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr { } else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)} keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
} }
}
key := keyPtr.Load() key := keyPtr.Load()
if v.KeyType.IsNil() { if v.KeyType.IsNil() {
v.KeyType = key.Type() v.KeyType = key.Type()