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.
Этот коммит содержится в:
родитель
607d824211
коммит
efa0410075
3 изменённых файлов: 40 добавлений и 0 удалений
|
@ -116,9 +116,16 @@ func Run(mod llvm.Module, debug bool) error {
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
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()
|
mem.revert()
|
||||||
|
// Create a call to the package initializer (which was
|
||||||
|
// previously deleted).
|
||||||
i8undef := llvm.Undef(r.i8ptrType)
|
i8undef := llvm.Undef(r.i8ptrType)
|
||||||
r.builder.CreateCall(fn, []llvm.Value{i8undef, i8undef}, "")
|
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
|
continue
|
||||||
}
|
}
|
||||||
return callErr
|
return callErr
|
||||||
|
@ -272,3 +279,19 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
||||||
r.functionCache[llvmFn] = fn
|
r.functionCache[llvmFn] = fn
|
||||||
return 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
11
interp/testdata/revert.ll
предоставленный
11
interp/testdata/revert.ll
предоставленный
|
@ -3,17 +3,28 @@ target triple = "x86_64--linux"
|
||||||
|
|
||||||
declare void @externalCall(i64)
|
declare void @externalCall(i64)
|
||||||
|
|
||||||
|
@foo.knownAtRuntime = global i64 0
|
||||||
|
@bar.knownAtRuntime = global i64 0
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
call void @foo.init(i8* undef, i8* undef)
|
call void @foo.init(i8* undef, i8* undef)
|
||||||
|
call void @bar.init(i8* undef, i8* undef)
|
||||||
call void @main.init(i8* undef, i8* undef)
|
call void @main.init(i8* undef, i8* undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
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.
|
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 {
|
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
call void @externalCall(i64 3)
|
call void @externalCall(i64 3)
|
||||||
|
|
6
interp/testdata/revert.out.ll
предоставленный
6
interp/testdata/revert.out.ll
предоставленный
|
@ -1,15 +1,21 @@
|
||||||
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"
|
||||||
|
|
||||||
|
@foo.knownAtRuntime = local_unnamed_addr global i64 0
|
||||||
|
@bar.knownAtRuntime = local_unnamed_addr global i64 0
|
||||||
|
|
||||||
declare void @externalCall(i64) local_unnamed_addr
|
declare void @externalCall(i64) local_unnamed_addr
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
call fastcc void @foo.init(i8* undef, i8* undef)
|
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)
|
call void @externalCall(i64 3)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
store i64 5, i64* @foo.knownAtRuntime, align 8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче