From a3c4421f39a219772817a8497ef7e31ace48ff2f Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 6 Aug 2021 17:53:12 +0200 Subject: [PATCH] compiler: move math aliases from the runtime to the compiler This makes them more flexible, especially with Go 1.17 making the situation more complicated (see https://github.com/golang/go/commit/1d20a362d0ca4898d77865e314ef6f73582daef0). It also makes it possible to do the same for many other functions, such as assembly implementations of cryptographich functions which are similarly dependent on the architecture. --- compiler/alias.go | 81 ++++++++++++++++ compiler/compiler.go | 25 ++++- src/runtime/math.go | 224 ------------------------------------------- 3 files changed, 105 insertions(+), 225 deletions(-) create mode 100644 compiler/alias.go delete mode 100644 src/runtime/math.go diff --git a/compiler/alias.go b/compiler/alias.go new file mode 100644 index 00000000..d599e2ea --- /dev/null +++ b/compiler/alias.go @@ -0,0 +1,81 @@ +package compiler + +// This file defines alias functions for functions that are normally defined in +// Go assembly. +// +// The Go toolchain defines many performance critical functions in assembly +// instead of plain Go. This is a problem for TinyGo as it currently (as of +// august 2021) is not able to compile these assembly files and even if it +// could, it would not be able to make use of them for many targets that are +// supported by TinyGo (baremetal RISC-V, AVR, etc). Therefore, many of these +// functions are aliased to their generic Go implementation. +// This results in slower than possible implementations, but at least they are +// usable. + +import "tinygo.org/x/go-llvm" + +var stdlibAliases = map[string]string{ + // math package + "math.Asin": "math.asin", + "math.Asinh": "math.asinh", + "math.Acos": "math.acos", + "math.Acosh": "math.acosh", + "math.Atan": "math.atan", + "math.Atanh": "math.atanh", + "math.Atan2": "math.atan2", + "math.Cbrt": "math.cbrt", + "math.Ceil": "math.ceil", + "math.Cos": "math.cos", + "math.Cosh": "math.cosh", + "math.Erf": "math.erf", + "math.Erfc": "math.erfc", + "math.Exp": "math.exp", + "math.Expm1": "math.expm1", + "math.Exp2": "math.exp2", + "math.Floor": "math.floor", + "math.Frexp": "math.frexp", + "math.Hypot": "math.hypot", + "math.Ldexp": "math.ldexp", + "math.Log": "math.log", + "math.Log1p": "math.log1p", + "math.Log10": "math.log10", + "math.Log2": "math.log2", + "math.Max": "math.max", + "math.Min": "math.min", + "math.Mod": "math.mod", + "math.Modf": "math.modf", + "math.Pow": "math.pow", + "math.Remainder": "math.remainder", + "math.Sin": "math.sin", + "math.Sinh": "math.sinh", + "math.Sqrt": "math.sqrt", + "math.Tan": "math.tan", + "math.Tanh": "math.tanh", + "math.Trunc": "math.trunc", +} + +// createAlias implements the function (in the builder) as a call to the alias +// function. +func (b *builder) createAlias(alias llvm.Value) { + if b.Debug { + if b.fn.Syntax() != nil { + // Create debug info file if present. + b.difunc = b.attachDebugInfo(b.fn) + } + pos := b.program.Fset.Position(b.fn.Pos()) + b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{}) + } + entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry") + b.SetInsertPointAtEnd(entryBlock) + if b.llvmFn.Type() != alias.Type() { + b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name()) + b.CreateUnreachable() + return + } + result := b.CreateCall(alias, b.llvmFn.Params(), "") + if result.Type().TypeKind() == llvm.VoidTypeKind { + b.CreateRetVoid() + } else { + b.CreateRet(result) + } +} diff --git a/compiler/compiler.go b/compiler/compiler.go index 94bbc1af..f8273039 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -23,7 +23,7 @@ import ( // Version of the compiler pacakge. Must be incremented each time the compiler // package changes in a way that affects the generated LLVM module. // This version is independent of the TinyGo version number. -const Version = 13 // last change: implement LLVM math builtins in the compiler +const Version = 14 // last change: add math assembly aliases func init() { llvm.InitializeAllTargets() @@ -768,6 +768,29 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package } } } + + // Add forwarding functions for functions that would otherwise be + // implemented in assembly. + for _, name := range members { + member := pkg.Members[name] + switch member := member.(type) { + case *ssa.Function: + if member.Blocks != nil { + continue // external function + } + info := c.getFunctionInfo(member) + if aliasName, ok := stdlibAliases[info.linkName]; ok { + alias := c.mod.NamedFunction(aliasName) + if alias.IsNil() { + // Shouldn't happen, but perhaps best to just ignore. + // The error will be a link error, if there is an error. + continue + } + b := newBuilder(c, irbuilder, member) + b.createAlias(alias) + } + } + } } // createFunction builds the LLVM IR implementation for this function. The diff --git a/src/runtime/math.go b/src/runtime/math.go deleted file mode 100644 index 73af1591..00000000 --- a/src/runtime/math.go +++ /dev/null @@ -1,224 +0,0 @@ -package runtime - -// This file redirects math stubs to their fallback implementation. -// TODO: use optimized versions if possible. - -import ( - _ "unsafe" -) - -//go:linkname math_Asin math.Asin -func math_Asin(x float64) float64 { return math_asin(x) } - -//go:linkname math_asin math.asin -func math_asin(x float64) float64 - -//go:linkname math_Asinh math.Asinh -func math_Asinh(x float64) float64 { return math_asinh(x) } - -//go:linkname math_asinh math.asinh -func math_asinh(x float64) float64 - -//go:linkname math_Acos math.Acos -func math_Acos(x float64) float64 { return math_acos(x) } - -//go:linkname math_acos math.acos -func math_acos(x float64) float64 - -//go:linkname math_Acosh math.Acosh -func math_Acosh(x float64) float64 { return math_acosh(x) } - -//go:linkname math_acosh math.acosh -func math_acosh(x float64) float64 - -//go:linkname math_Atan math.Atan -func math_Atan(x float64) float64 { return math_atan(x) } - -//go:linkname math_atan math.atan -func math_atan(x float64) float64 - -//go:linkname math_Atanh math.Atanh -func math_Atanh(x float64) float64 { return math_atanh(x) } - -//go:linkname math_atanh math.atanh -func math_atanh(x float64) float64 - -//go:linkname math_Atan2 math.Atan2 -func math_Atan2(y, x float64) float64 { return math_atan2(y, x) } - -//go:linkname math_atan2 math.atan2 -func math_atan2(y, x float64) float64 - -//go:linkname math_Cbrt math.Cbrt -func math_Cbrt(x float64) float64 { return math_cbrt(x) } - -//go:linkname math_cbrt math.cbrt -func math_cbrt(x float64) float64 - -//go:linkname math_Ceil math.Ceil -func math_Ceil(x float64) float64 { return math_ceil(x) } - -//go:linkname math_ceil math.ceil -func math_ceil(x float64) float64 - -//go:linkname math_Cos math.Cos -func math_Cos(x float64) float64 { return math_cos(x) } - -//go:linkname math_cos math.cos -func math_cos(x float64) float64 - -//go:linkname math_Cosh math.Cosh -func math_Cosh(x float64) float64 { return math_cosh(x) } - -//go:linkname math_cosh math.cosh -func math_cosh(x float64) float64 - -//go:linkname math_Erf math.Erf -func math_Erf(x float64) float64 { return math_erf(x) } - -//go:linkname math_erf math.erf -func math_erf(x float64) float64 - -//go:linkname math_Erfc math.Erfc -func math_Erfc(x float64) float64 { return math_erfc(x) } - -//go:linkname math_erfc math.erfc -func math_erfc(x float64) float64 - -//go:linkname math_Exp math.Exp -func math_Exp(x float64) float64 { return math_exp(x) } - -//go:linkname math_exp math.exp -func math_exp(x float64) float64 - -//go:linkname math_Expm1 math.Expm1 -func math_Expm1(x float64) float64 { return math_expm1(x) } - -//go:linkname math_expm1 math.expm1 -func math_expm1(x float64) float64 - -//go:linkname math_Exp2 math.Exp2 -func math_Exp2(x float64) float64 { return math_exp2(x) } - -//go:linkname math_exp2 math.exp2 -func math_exp2(x float64) float64 - -//go:linkname math_Floor math.Floor -func math_Floor(x float64) float64 { return math_floor(x) } - -//go:linkname math_floor math.floor -func math_floor(x float64) float64 - -//go:linkname math_Frexp math.Frexp -func math_Frexp(x float64) (float64, int) { return math_frexp(x) } - -//go:linkname math_frexp math.frexp -func math_frexp(x float64) (float64, int) - -//go:linkname math_Hypot math.Hypot -func math_Hypot(p, q float64) float64 { return math_hypot(p, q) } - -//go:linkname math_hypot math.hypot -func math_hypot(p, q float64) float64 - -//go:linkname math_Ldexp math.Ldexp -func math_Ldexp(frac float64, exp int) float64 { return math_ldexp(frac, exp) } - -//go:linkname math_ldexp math.ldexp -func math_ldexp(frac float64, exp int) float64 - -//go:linkname math_Log math.Log -func math_Log(x float64) float64 { return math_log(x) } - -//go:linkname math_log math.log -func math_log(x float64) float64 - -//go:linkname math_Log1p math.Log1p -func math_Log1p(x float64) float64 { return math_log1p(x) } - -//go:linkname math_log1p math.log1p -func math_log1p(x float64) float64 - -//go:linkname math_Log10 math.Log10 -func math_Log10(x float64) float64 { return math_log10(x) } - -//go:linkname math_log10 math.log10 -func math_log10(x float64) float64 - -//go:linkname math_Log2 math.Log2 -func math_Log2(x float64) float64 { return math_log2(x) } - -//go:linkname math_log2 math.log2 -func math_log2(x float64) float64 - -//go:linkname math_Max math.Max -func math_Max(x, y float64) float64 { return math_max(x, y) } - -//go:linkname math_max math.max -func math_max(x, y float64) float64 - -//go:linkname math_Min math.Min -func math_Min(x, y float64) float64 { return math_min(x, y) } - -//go:linkname math_min math.min -func math_min(x, y float64) float64 - -//go:linkname math_Mod math.Mod -func math_Mod(x, y float64) float64 { return math_mod(x, y) } - -//go:linkname math_mod math.mod -func math_mod(x, y float64) float64 - -//go:linkname math_Modf math.Modf -func math_Modf(x float64) (float64, float64) { return math_modf(x) } - -//go:linkname math_modf math.modf -func math_modf(x float64) (float64, float64) - -//go:linkname math_Pow math.Pow -func math_Pow(x, y float64) float64 { return math_pow(x, y) } - -//go:linkname math_pow math.pow -func math_pow(x, y float64) float64 - -//go:linkname math_Remainder math.Remainder -func math_Remainder(x, y float64) float64 { return math_remainder(x, y) } - -//go:linkname math_remainder math.remainder -func math_remainder(x, y float64) float64 - -//go:linkname math_Sin math.Sin -func math_Sin(x float64) float64 { return math_sin(x) } - -//go:linkname math_sin math.sin -func math_sin(x float64) float64 - -//go:linkname math_Sinh math.Sinh -func math_Sinh(x float64) float64 { return math_sinh(x) } - -//go:linkname math_sinh math.sinh -func math_sinh(x float64) float64 - -//go:linkname math_Sqrt math.Sqrt -func math_Sqrt(x float64) float64 { return math_sqrt(x) } - -//go:linkname math_sqrt math.sqrt -func math_sqrt(x float64) float64 - -//go:linkname math_Tan math.Tan -func math_Tan(x float64) float64 { return math_tan(x) } - -//go:linkname math_tan math.tan -func math_tan(x float64) float64 - -//go:linkname math_Tanh math.Tanh -func math_Tanh(x float64) float64 { return math_tanh(x) } - -//go:linkname math_tanh math.tanh -func math_tanh(x float64) float64 - -//go:linkname math_Trunc math.Trunc -func math_Trunc(x float64) float64 { return math_trunc(x) } - -//go:linkname math_trunc math.trunc -func math_trunc(x float64) float64