Skip to content

Commit 4605cbb

Browse files
aykevldeadprogram
authored andcommitted
interp: fix inserting non-const values in a const aggregate
This bug was triggered by the following code: package main func foo() byte var array = [1]byte{foo()} func main() { }
1 parent 98eee7c commit 4605cbb

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

interp/testdata/basic.ll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
22
target triple = "x86_64--linux"
33

44
@main.v1 = internal global i64 0
5+
@main.nonConst1 = global [4 x i64] zeroinitializer
6+
@main.nonConst2 = global i64 0
57

68
declare void @runtime.printint64(i64) unnamed_addr
79

@@ -31,6 +33,20 @@ define internal void @main.init() unnamed_addr {
3133
entry:
3234
store i64 3, i64* @main.v1
3335
call void @"main.init#1"()
36+
37+
; test the following pattern:
38+
; func someValue() int // extern function
39+
; var nonConst1 = [4]int{someValue(), 0, 0, 0}
40+
%value1 = call i64 @someValue()
41+
%gep1 = getelementptr [4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0
42+
store i64 %value1, i64* %gep1
43+
44+
; Test that the global really is marked dirty:
45+
; var nonConst2 = nonConst1[0]
46+
%gep2 = getelementptr [4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0
47+
%value2 = load i64, i64* %gep2
48+
store i64 %value2, i64* @main.nonConst2
49+
3450
ret void
3551
}
3652

@@ -40,3 +56,5 @@ entry:
4056
call void @runtime.printnl()
4157
ret void
4258
}
59+
60+
declare i64 @someValue()

interp/testdata/basic.out.ll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
22
target triple = "x86_64--linux"
33

4+
@main.nonConst1 = local_unnamed_addr global [4 x i64] zeroinitializer
5+
@main.nonConst2 = local_unnamed_addr global i64 0
6+
47
declare void @runtime.printint64(i64) unnamed_addr
58

69
declare void @runtime.printnl() unnamed_addr
@@ -9,6 +12,10 @@ define void @runtime.initAll() unnamed_addr {
912
entry:
1013
call void @runtime.printint64(i64 5)
1114
call void @runtime.printnl()
15+
%value1 = call i64 @someValue()
16+
store i64 %value1, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0)
17+
%value2 = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0)
18+
store i64 %value2, i64* @main.nonConst2
1219
ret void
1320
}
1421

@@ -18,3 +25,5 @@ entry:
1825
call void @runtime.printnl()
1926
ret void
2027
}
28+
29+
declare i64 @someValue() local_unnamed_addr

interp/utils.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,22 @@ func isPointerNil(v llvm.Value) (result bool, ok bool) {
7676
}
7777
return false, false // not valid
7878
}
79+
80+
// unwrap returns the underlying value, with GEPs removed. This can be useful to
81+
// get the underlying global of a GEP pointer.
82+
func unwrap(value llvm.Value) llvm.Value {
83+
for {
84+
if !value.IsAConstantExpr().IsNil() {
85+
switch value.Opcode() {
86+
case llvm.GetElementPtr:
87+
value = value.Operand(0)
88+
continue
89+
}
90+
} else if !value.IsAGetElementPtrInst().IsNil() {
91+
value = value.Operand(0)
92+
continue
93+
}
94+
break
95+
}
96+
return value
97+
}

interp/values.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (v *LocalValue) Type() llvm.Type {
3636
}
3737

3838
func (v *LocalValue) IsConstant() bool {
39-
if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok {
39+
if _, ok := v.Eval.dirtyGlobals[unwrap(v.Underlying)]; ok {
4040
return false
4141
}
4242
return v.Underlying.IsConstant()
@@ -75,6 +75,11 @@ func (v *LocalValue) Store(value llvm.Value) {
7575
}
7676
return
7777
}
78+
if !value.IsConstant() {
79+
v.MarkDirty()
80+
v.Eval.builder.CreateStore(value, v.Underlying)
81+
return
82+
}
7883
switch v.Underlying.Opcode() {
7984
case llvm.GetElementPtr:
8085
indices := v.getConstGEPIndices()
@@ -150,13 +155,14 @@ func (v *LocalValue) getConstGEPIndices() []uint32 {
150155
// MarkDirty marks this global as dirty, meaning that every load from and store
151156
// to this global (from now on) must be performed at runtime.
152157
func (v *LocalValue) MarkDirty() {
153-
if v.Underlying.IsAGlobalVariable().IsNil() {
158+
underlying := unwrap(v.Underlying)
159+
if underlying.IsAGlobalVariable().IsNil() {
154160
panic("trying to mark a non-global as dirty")
155161
}
156162
if !v.IsConstant() {
157163
return // already dirty
158164
}
159-
v.Eval.dirtyGlobals[v.Underlying] = struct{}{}
165+
v.Eval.dirtyGlobals[underlying] = struct{}{}
160166
}
161167

162168
// MapValue implements a Go map which is created at compile time and stored as a

0 commit comments

Comments
 (0)