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
Этот коммит содержится в:
родитель
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)}
|
||||
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":
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 "runtime.sliceCopy":
|
||||
case name == "runtime.sliceCopy":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime.trackPointer":
|
||||
case name == "runtime.trackPointer":
|
||||
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
|
||||
}
|
||||
if fn.IsDeclaration() {
|
||||
|
|
36
interp/testdata/map.ll
предоставленный
Обычный файл
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
предоставленный
Обычный файл
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,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() {
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче