aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2023-02-17 01:19:53 +0100
committerRon Evans <[email protected]>2023-02-18 10:50:26 +0100
commitc02cc339c564afd767dba0cf2b8a05e959e6ef5b (patch)
tree702e3b5abe629dc76ab96b76a5620cd5413af606
parent361ecf9ea452b8787f9b68689216bb26227bd64a (diff)
downloadtinygo-c02cc339c564afd767dba0cf2b8a05e959e6ef5b.tar.gz
tinygo-c02cc339c564afd767dba0cf2b8a05e959e6ef5b.zip
runtime: implement KeepAlive using inline assembly
-rw-r--r--compiler/intrinsics.go25
-rw-r--r--src/runtime/runtime.go9
-rw-r--r--testdata/gc.go10
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)
+}