Skip to content

Commit

Permalink
interp: fix inserting non-const values in a const aggregate
Browse files Browse the repository at this point in the history
This bug was triggered by the following code:

    package main

    func foo() byte

    var array = [1]byte{foo()}

    func main() {
    }
  • Loading branch information
aykevl authored and deadprogram committed Nov 18, 2019
1 parent 98eee7c commit 4605cbb
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 3 deletions.
18 changes: 18 additions & 0 deletions interp/testdata/basic.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
}

Expand All @@ -40,3 +56,5 @@ entry:
call void @runtime.printnl()
ret void
}

declare i64 @someValue()
9 changes: 9 additions & 0 deletions interp/testdata/basic.out.ll
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
}

Expand All @@ -18,3 +25,5 @@ entry:
call void @runtime.printnl()
ret void
}

declare i64 @someValue() local_unnamed_addr
19 changes: 19 additions & 0 deletions interp/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
12 changes: 9 additions & 3 deletions interp/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 4605cbb

Please sign in to comment.