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() { }
Этот коммит содержится в:
родитель
98eee7c22a
коммит
4605cbbc6e
4 изменённых файлов: 55 добавлений и 3 удалений
18
interp/testdata/basic.ll
предоставленный
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"
|
||||
|
||||
@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()
|
||||
|
|
9
interp/testdata/basic.out.ll
предоставленный
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 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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче