Browse Source

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() {
    }
pull/740/head
Ayke van Laethem 5 years ago
committed by Ron Evans
parent
commit
4605cbbc6e
  1. 18
      interp/testdata/basic.ll
  2. 9
      interp/testdata/basic.out.ll
  3. 19
      interp/utils.go
  4. 12
      interp/values.go

18
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" target triple = "x86_64--linux"
@main.v1 = internal global i64 0 @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 declare void @runtime.printint64(i64) unnamed_addr
@ -31,6 +33,20 @@ define internal void @main.init() unnamed_addr {
entry: entry:
store i64 3, i64* @main.v1 store i64 3, i64* @main.v1
call void @"main.init#1"() 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 ret void
} }
@ -40,3 +56,5 @@ entry:
call void @runtime.printnl() call void @runtime.printnl()
ret void ret void
} }
declare i64 @someValue()

9
interp/testdata/basic.out.ll

@ -1,6 +1,9 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux" 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.printint64(i64) unnamed_addr
declare void @runtime.printnl() unnamed_addr declare void @runtime.printnl() unnamed_addr
@ -9,6 +12,10 @@ define void @runtime.initAll() unnamed_addr {
entry: entry:
call void @runtime.printint64(i64 5) call void @runtime.printint64(i64 5)
call void @runtime.printnl() 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 ret void
} }
@ -18,3 +25,5 @@ entry:
call void @runtime.printnl() call void @runtime.printnl()
ret void ret void
} }
declare i64 @someValue() local_unnamed_addr

19
interp/utils.go

@ -76,3 +76,22 @@ func isPointerNil(v llvm.Value) (result bool, ok bool) {
} }
return false, false // not valid 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
interp/values.go

@ -36,7 +36,7 @@ func (v *LocalValue) Type() llvm.Type {
} }
func (v *LocalValue) IsConstant() bool { 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 false
} }
return v.Underlying.IsConstant() return v.Underlying.IsConstant()
@ -75,6 +75,11 @@ func (v *LocalValue) Store(value llvm.Value) {
} }
return return
} }
if !value.IsConstant() {
v.MarkDirty()
v.Eval.builder.CreateStore(value, v.Underlying)
return
}
switch v.Underlying.Opcode() { switch v.Underlying.Opcode() {
case llvm.GetElementPtr: case llvm.GetElementPtr:
indices := v.getConstGEPIndices() 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 // MarkDirty marks this global as dirty, meaning that every load from and store
// to this global (from now on) must be performed at runtime. // to this global (from now on) must be performed at runtime.
func (v *LocalValue) MarkDirty() { 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") panic("trying to mark a non-global as dirty")
} }
if !v.IsConstant() { if !v.IsConstant() {
return // already dirty 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 // MapValue implements a Go map which is created at compile time and stored as a

Loading…
Cancel
Save