From efa0410075ea492a1113224236a1210be74e1650 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 14 Jul 2021 20:10:37 +0200 Subject: [PATCH] interp: fix bug in compiler-time/run-time package initializers Make sure that if a package initializer cannot be run, later package initializers won't try to access any global variables touched by the uninterpretable package initializer. --- interp/interp.go | 23 +++++++++++++++++++++++ interp/testdata/revert.ll | 11 +++++++++++ interp/testdata/revert.out.ll | 6 ++++++ 3 files changed, 40 insertions(+) diff --git a/interp/interp.go b/interp/interp.go index 574fe01a..cab9d9e3 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -116,9 +116,16 @@ func Run(mod llvm.Module, debug bool) error { if r.debug { fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error()) } + // Remove instructions that were created as part of interpreting + // the package. mem.revert() + // Create a call to the package initializer (which was + // previously deleted). i8undef := llvm.Undef(r.i8ptrType) r.builder.CreateCall(fn, []llvm.Value{i8undef, i8undef}, "") + // Make sure that any globals touched by the package + // initializer, won't be accessed by later package initializers. + r.markExternalLoad(fn) continue } return callErr @@ -272,3 +279,19 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function { r.functionCache[llvmFn] = fn return fn } + +// markExternalLoad marks the given llvmValue as being loaded externally. This +// is primarily used to mark package initializers that could not be run at +// compile time. As an example, a package initialize might store to a global +// variable. Another package initializer might read from the same global +// variable. By marking this function as being run at runtime, that load +// instruction will need to be run at runtime instead of at compile time. +func (r *runner) markExternalLoad(llvmValue llvm.Value) { + mem := memoryView{r: r} + mem.markExternalLoad(llvmValue) + for index, obj := range mem.objects { + if obj.marked > r.objects[index].marked { + r.objects[index].marked = obj.marked + } + } +} diff --git a/interp/testdata/revert.ll b/interp/testdata/revert.ll index 49354ce9..41fb6a81 100644 --- a/interp/testdata/revert.ll +++ b/interp/testdata/revert.ll @@ -3,17 +3,28 @@ target triple = "x86_64--linux" declare void @externalCall(i64) +@foo.knownAtRuntime = global i64 0 +@bar.knownAtRuntime = global i64 0 + define void @runtime.initAll() unnamed_addr { entry: call void @foo.init(i8* undef, i8* undef) + call void @bar.init(i8* undef, i8* undef) call void @main.init(i8* undef, i8* undef) ret void } define internal void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr { + store i64 5, i64* @foo.knownAtRuntime unreachable ; this triggers a revert of @foo.init. } +define internal void @bar.init(i8* %context, i8* %parentHandle) unnamed_addr { + %val = load i64, i64* @foo.knownAtRuntime + store i64 %val, i64* @bar.knownAtRuntime + ret void +} + define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr { entry: call void @externalCall(i64 3) diff --git a/interp/testdata/revert.out.ll b/interp/testdata/revert.out.ll index 7309439f..4f38e4c4 100644 --- a/interp/testdata/revert.out.ll +++ b/interp/testdata/revert.out.ll @@ -1,15 +1,21 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64--linux" +@foo.knownAtRuntime = local_unnamed_addr global i64 0 +@bar.knownAtRuntime = local_unnamed_addr global i64 0 + declare void @externalCall(i64) local_unnamed_addr define void @runtime.initAll() unnamed_addr { entry: call fastcc void @foo.init(i8* undef, i8* undef) + %val = load i64, i64* @foo.knownAtRuntime, align 8 + store i64 %val, i64* @bar.knownAtRuntime, align 8 call void @externalCall(i64 3) ret void } define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr { + store i64 5, i64* @foo.knownAtRuntime, align 8 unreachable }