diff --git a/interp/interp.go b/interp/interp.go index cab9d9e3..ba358384 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -145,6 +145,9 @@ func Run(mod llvm.Module, debug bool) error { if obj.buffer == nil { continue } + if obj.constant { + continue // constant buffers can't have been modified + } initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem) if err == errInvalidPtrToIntSize { // This can happen when a previous interp run did not have the @@ -248,6 +251,9 @@ func RunFunc(fn llvm.Value, debug bool) error { if obj.buffer == nil { continue } + if obj.constant { + continue // constant, so can't have been modified + } initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem) if err != nil { return err diff --git a/interp/memory.go b/interp/memory.go index 61c83890..e358124e 100644 --- a/interp/memory.go +++ b/interp/memory.go @@ -41,6 +41,7 @@ type object struct { globalName string // name, if not yet created (not guaranteed to be the final name) buffer value // buffer with value as given by interp, nil if external size uint32 // must match buffer.len(), if available + constant bool // true if this is a constant global marked uint8 // 0 means unmarked, 1 means external read, 2 means external write } @@ -223,7 +224,7 @@ func (mv *memoryView) hasExternalLoadOrStore(v pointerValue) bool { // possible for the interpreter to read from the object. func (mv *memoryView) hasExternalStore(v pointerValue) bool { obj := mv.get(v.index()) - return obj.marked >= 2 + return obj.marked >= 2 && !obj.constant } // get returns an object that can only be read from, as it may return an object @@ -263,6 +264,9 @@ func (mv *memoryView) put(index uint32, obj object) { if checks && mv.get(index).buffer.len(mv.r) != obj.buffer.len(mv.r) { panic("put() with a differently-sized object") } + if checks && obj.constant { + panic("interp: store to a constant") + } mv.objects[index] = obj } @@ -1138,6 +1142,7 @@ func (r *runner) getValue(llvmValue llvm.Value) value { obj.size = uint32(r.targetData.TypeAllocSize(llvmValue.Type().ElementType())) if initializer := llvmValue.Initializer(); !initializer.IsNil() { obj.buffer = r.getValue(initializer) + obj.constant = llvmValue.IsGlobalConstant() } } else if !llvmValue.IsAFunction().IsNil() { // OK diff --git a/interp/testdata/basic.ll b/interp/testdata/basic.ll index d0af3af3..30c0bf08 100644 --- a/interp/testdata/basic.ll +++ b/interp/testdata/basic.ll @@ -6,6 +6,7 @@ target triple = "x86_64--linux" @main.nonConst2 = global i64 0 @main.someArray = global [8 x {i16, i32}] zeroinitializer @main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1] +@main.exportedConst = constant i64 42 @main.exposedValue1 = global i16 0 @main.exposedValue2 = global i16 0 @main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer @@ -62,6 +63,11 @@ entry: call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*)) store i16 5, i16* @main.exposedValue1 + ; Test that marking a constant as external still allows loading from it. + call void @readExternal(i32* bitcast (i64* @main.exportedConst to i32*)) + %constLoad = load i64, i64 * @main.exportedConst + call void @runtime.printint64(i64 %constLoad) + ; Test that this even propagates through functions. call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*)) store i16 7, i16* @main.exposedValue2 @@ -96,6 +102,8 @@ declare i64 @someValue() declare void @modifyExternal(i32*) +declare void @readExternal(i32*) + ; This function will modify an external value. By passing this function as a ; function pointer to an external function, @main.exposedValue2 should be ; marked as external. diff --git a/interp/testdata/basic.out.ll b/interp/testdata/basic.out.ll index 9279606e..5c68ec5e 100644 --- a/interp/testdata/basic.out.ll +++ b/interp/testdata/basic.out.ll @@ -5,6 +5,7 @@ target triple = "x86_64--linux" @main.nonConst2 = local_unnamed_addr global i64 0 @main.someArray = global [8 x { i16, i32 }] zeroinitializer @main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1] +@main.exportedConst = constant i64 42 @main.exposedValue1 = global i16 0 @main.exposedValue2 = local_unnamed_addr global i16 0 @main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer @@ -24,6 +25,8 @@ entry: call void @modifyExternal(i32* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ([8 x { i16, i32 }]* @main.someArray to i8*), i32 28) to i32*)) call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*)) store i16 5, i16* @main.exposedValue1, align 2 + call void @readExternal(i32* bitcast (i64* @main.exportedConst to i32*)) + call void @runtime.printint64(i64 42) call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*)) store i16 7, i16* @main.exposedValue2, align 2 call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*)) @@ -54,6 +57,8 @@ declare i64 @someValue() local_unnamed_addr declare void @modifyExternal(i32*) local_unnamed_addr +declare void @readExternal(i32*) local_unnamed_addr + define void @willModifyGlobal() { entry: store i16 8, i16* @main.exposedValue2, align 2