diff --git a/interp/testdata/basic.ll b/interp/testdata/basic.ll index 8b482c71..8aebff31 100644 --- a/interp/testdata/basic.ll +++ b/interp/testdata/basic.ll @@ -2,6 +2,8 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64--linux" @main.v1 = internal global i64 0 +@main.nonConst1 = global [4 x i64] zeroinitializer +@main.nonConst2 = global i64 0 declare void @runtime.printint64(i64) unnamed_addr @@ -31,6 +33,20 @@ define internal void @main.init() unnamed_addr { entry: store i64 3, i64* @main.v1 call void @"main.init#1"() + + ; test the following pattern: + ; func someValue() int // extern function + ; var nonConst1 = [4]int{someValue(), 0, 0, 0} + %value1 = call i64 @someValue() + %gep1 = getelementptr [4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0 + store i64 %value1, i64* %gep1 + + ; Test that the global really is marked dirty: + ; var nonConst2 = nonConst1[0] + %gep2 = getelementptr [4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0 + %value2 = load i64, i64* %gep2 + store i64 %value2, i64* @main.nonConst2 + ret void } @@ -40,3 +56,5 @@ entry: call void @runtime.printnl() ret void } + +declare i64 @someValue() diff --git a/interp/testdata/basic.out.ll b/interp/testdata/basic.out.ll index 457a6c2b..41f0aacb 100644 --- a/interp/testdata/basic.out.ll +++ b/interp/testdata/basic.out.ll @@ -1,6 +1,9 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64--linux" +@main.nonConst1 = local_unnamed_addr global [4 x i64] zeroinitializer +@main.nonConst2 = local_unnamed_addr global i64 0 + declare void @runtime.printint64(i64) unnamed_addr declare void @runtime.printnl() unnamed_addr @@ -9,6 +12,10 @@ define void @runtime.initAll() unnamed_addr { entry: call void @runtime.printint64(i64 5) call void @runtime.printnl() + %value1 = call i64 @someValue() + store i64 %value1, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0) + %value2 = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0) + store i64 %value2, i64* @main.nonConst2 ret void } @@ -18,3 +25,5 @@ entry: call void @runtime.printnl() ret void } + +declare i64 @someValue() local_unnamed_addr diff --git a/interp/utils.go b/interp/utils.go index 3131e819..abf93103 100644 --- a/interp/utils.go +++ b/interp/utils.go @@ -76,3 +76,22 @@ func isPointerNil(v llvm.Value) (result bool, ok bool) { } return false, false // not valid } + +// unwrap returns the underlying value, with GEPs removed. This can be useful to +// get the underlying global of a GEP pointer. +func unwrap(value llvm.Value) llvm.Value { + for { + if !value.IsAConstantExpr().IsNil() { + switch value.Opcode() { + case llvm.GetElementPtr: + value = value.Operand(0) + continue + } + } else if !value.IsAGetElementPtrInst().IsNil() { + value = value.Operand(0) + continue + } + break + } + return value +} diff --git a/interp/values.go b/interp/values.go index 40bc3c90..e24a80d6 100644 --- a/interp/values.go +++ b/interp/values.go @@ -36,7 +36,7 @@ func (v *LocalValue) Type() llvm.Type { } func (v *LocalValue) IsConstant() bool { - if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok { + if _, ok := v.Eval.dirtyGlobals[unwrap(v.Underlying)]; ok { return false } return v.Underlying.IsConstant() @@ -75,6 +75,11 @@ func (v *LocalValue) Store(value llvm.Value) { } return } + if !value.IsConstant() { + v.MarkDirty() + v.Eval.builder.CreateStore(value, v.Underlying) + return + } switch v.Underlying.Opcode() { case llvm.GetElementPtr: indices := v.getConstGEPIndices() @@ -150,13 +155,14 @@ func (v *LocalValue) getConstGEPIndices() []uint32 { // MarkDirty marks this global as dirty, meaning that every load from and store // to this global (from now on) must be performed at runtime. func (v *LocalValue) MarkDirty() { - if v.Underlying.IsAGlobalVariable().IsNil() { + underlying := unwrap(v.Underlying) + if underlying.IsAGlobalVariable().IsNil() { panic("trying to mark a non-global as dirty") } if !v.IsConstant() { return // already dirty } - v.Eval.dirtyGlobals[v.Underlying] = struct{}{} + v.Eval.dirtyGlobals[underlying] = struct{}{} } // MapValue implements a Go map which is created at compile time and stored as a