From 1681ed02d3035f3216885d560433bc1ad953ed24 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 11 Nov 2021 02:41:29 +0100 Subject: [PATCH] interp: take care of constant globals Constant globals can't have been modified, even if a pointer is passed externally. Therefore, don't treat it as such in hasExternalStore. In addition, it doesn't make sense to update values of constant globals after the interp pass is finished. So don't do this. TODO: track whether objects are actually modified and only update the globals if this is the case. --- interp/interp.go | 6 ++++++ interp/memory.go | 7 ++++++- interp/testdata/basic.ll | 8 ++++++++ interp/testdata/basic.out.ll | 5 +++++ 4 files changed, 25 insertions(+), 1 deletion(-) 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