diff options
author | Ayke van Laethem <[email protected]> | 2023-02-17 01:19:53 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2023-02-18 10:50:26 +0100 |
commit | c02cc339c564afd767dba0cf2b8a05e959e6ef5b (patch) | |
tree | 702e3b5abe629dc76ab96b76a5620cd5413af606 | |
parent | 361ecf9ea452b8787f9b68689216bb26227bd64a (diff) | |
download | tinygo-c02cc339c564afd767dba0cf2b8a05e959e6ef5b.tar.gz tinygo-c02cc339c564afd767dba0cf2b8a05e959e6ef5b.zip |
runtime: implement KeepAlive using inline assembly
-rw-r--r-- | compiler/intrinsics.go | 25 | ||||
-rw-r--r-- | src/runtime/runtime.go | 9 | ||||
-rw-r--r-- | testdata/gc.go | 10 |
3 files changed, 38 insertions, 6 deletions
diff --git a/compiler/intrinsics.go b/compiler/intrinsics.go index a511e518b..e59fdc449 100644 --- a/compiler/intrinsics.go +++ b/compiler/intrinsics.go @@ -24,6 +24,8 @@ func (b *builder) defineIntrinsicFunction() { b.createMemoryCopyImpl() case name == "runtime.memzero": b.createMemoryZeroImpl() + case name == "runtime.KeepAlive": + b.createKeepAliveImpl() case strings.HasPrefix(name, "runtime/volatile.Load"): b.createVolatileLoad() case strings.HasPrefix(name, "runtime/volatile.Store"): @@ -87,6 +89,29 @@ func (b *builder) createMemoryZeroImpl() { b.CreateRetVoid() } +// createKeepAlive creates the runtime.KeepAlive function. It is implemented +// using inline assembly. +func (b *builder) createKeepAliveImpl() { + b.createFunctionStart(true) + + // Get the underlying value of the interface value. + interfaceValue := b.getValue(b.fn.Params[0]) + pointerValue := b.CreateExtractValue(interfaceValue, 1, "") + + // Create an equivalent of the following C code, which is basically just a + // nop but ensures the pointerValue is kept alive: + // + // __asm__ __volatile__("" : : "r"(pointerValue)) + // + // It should be portable to basically everything as the "r" register type + // exists basically everywhere. + asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false) + asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false) + b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "") + + b.CreateRetVoid() +} + var mathToLLVMMapping = map[string]string{ "math.Ceil": "llvm.ceil.f64", "math.Exp": "llvm.exp.f64", diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 586a610ae..03bdb450e 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -88,12 +88,9 @@ func LockOSThread() { func UnlockOSThread() { } -func KeepAlive(x interface{}) { - // Unimplemented. - // TODO: This function needs to be implemented in a way that LLVM doesn't optimize away the x - // parameter. This will likely need either a volatile operation or calling an assembly stub - // that simply returns. -} +// KeepAlive makes sure the value in the interface is alive until at least the +// point of the call. +func KeepAlive(x interface{}) var godebugUpdate func(string, string) diff --git a/testdata/gc.go b/testdata/gc.go index eb594db6c..456d763b4 100644 --- a/testdata/gc.go +++ b/testdata/gc.go @@ -1,5 +1,7 @@ package main +import "runtime" + var xorshift32State uint32 = 1 func xorshift32(x uint32) uint32 { @@ -17,6 +19,7 @@ func randuint32() uint32 { func main() { testNonPointerHeap() + testKeepAlive() } var scalarSlices [4][]byte @@ -64,3 +67,10 @@ func testNonPointerHeap() { } println("ok") } + +func testKeepAlive() { + // There isn't much we can test, but at least we can test that + // runtime.KeepAlive compiles correctly. + var x int + runtime.KeepAlive(&x) +} |