diff --git a/compiler/intrinsics.go b/compiler/intrinsics.go index 3c7edd7c9..5f1ba6f7d 100644 --- a/compiler/intrinsics.go +++ b/compiler/intrinsics.go @@ -121,6 +121,29 @@ func (b *builder) createKeepAliveImpl() { b.CreateRetVoid() } +// createAbiEscapeImpl implements the generic internal/abi.Escape function. It +// currently only supports pointer types. +func (b *builder) createAbiEscapeImpl() { + b.createFunctionStart(true) + + // The first parameter is assumed to be a pointer. This is checked at the + // call site of createAbiEscapeImpl. + pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn)) + + // 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.dataPtrType, []llvm.Type{b.dataPtrType}, false) + asmFn := llvm.InlineAsm(asmType, "", "=r,0", true, false, 0, false) + result := b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "") + + b.CreateRet(result) +} + var mathToLLVMMapping = map[string]string{ "math.Ceil": "llvm.ceil.f64", "math.Exp": "llvm.exp.f64", diff --git a/compiler/symbol.go b/compiler/symbol.go index c56e0792f..2507b3eed 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -253,6 +253,23 @@ func (c *compilerContext) maybeCreateSyntheticFunction(fn *ssa.Function, llvmFn // The exception is the package initializer, which does appear in the // *ssa.Package members and so shouldn't be created here. if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" && fn.Synthetic != "range-over-func yield" { + if origin := fn.Origin(); origin != nil && origin.RelString(nil) == "internal/abi.Escape" { + // This is a special implementation or internal/abi.Escape, which + // can only really be implemented in the compiler. + // For simplicity we'll only implement pointer parameters for now. + if _, ok := fn.Params[0].Type().Underlying().(*types.Pointer); ok { + irbuilder := c.ctx.NewBuilder() + defer irbuilder.Dispose() + b := newBuilder(c, irbuilder, fn) + b.createAbiEscapeImpl() + llvmFn.SetLinkage(llvm.LinkOnceODRLinkage) + llvmFn.SetUnnamedAddr(true) + } + // If the parameter is not of a pointer type, it will be left + // unimplemented. This will result in a linker error if the function + // is really called, making it clear it needs to be implemented. + return + } if len(fn.Blocks) == 0 { c.addError(fn.Pos(), "missing function body") return diff --git a/src/internal/abi/escape.go b/src/internal/abi/escape.go index 0ecdf8030..1f4c76331 100644 --- a/src/internal/abi/escape.go +++ b/src/internal/abi/escape.go @@ -8,3 +8,10 @@ import "unsafe" func NoEscape(p unsafe.Pointer) unsafe.Pointer { return p } + +func Escape[T any](x T) T { + // This function is either implemented in the compiler, or left undefined + // for some variation of T. The body of this function should not be compiled + // as-is. + panic("internal/abi.Escape: unreachable (implemented in the compiler)") +}