Skip to content

Commit 39f6beb

Browse files
committed
compiler: use LLVM intrinsics for math trig operations
I think this failed in the past, but presumably those failures have been fixed by now. So these intrinsics can now be used. Using these intrinsics instead of the native Go implementations helps LLVM to reason about them: it can for example evaluate the value at compile time or do optimizations like convert `float32(math.Sin(float64(x)))` into a 32-bit sin operation. If the math operation is not available on the target platform the C library implementation will be used instead.
1 parent 0a5875a commit 39f6beb

2 files changed

Lines changed: 43 additions & 10 deletions

File tree

compiler/compiler.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -869,10 +869,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
869869
}
870870
// Create the function definition.
871871
b := newBuilder(c, irbuilder, member)
872-
if _, ok := mathToLLVMMapping[member.RelString(nil)]; ok {
872+
if ok := b.defineMathOp(); ok {
873873
// The body of this function (if there is one) is ignored and
874874
// replaced with a LLVM intrinsic call.
875-
b.defineMathOp()
876875
continue
877876
}
878877
if ok := b.defineMathBitsIntrinsic(); ok {

compiler/intrinsics.go

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strconv"
88
"strings"
99

10+
"github.com/tinygo-org/tinygo/compiler/llvmutil"
1011
"tinygo.org/x/go-llvm"
1112
)
1213

@@ -166,12 +167,22 @@ func (b *builder) createMachineKeepAliveImpl() {
166167
}
167168

168169
var mathToLLVMMapping = map[string]string{
170+
"math.Acos": "llvm.acos.f64",
171+
"math.Asin": "llvm.asin.f64",
172+
"math.Atan": "llvm.atan.f64",
173+
"math.Atan2": "llvm.atan2.f64",
169174
"math.Ceil": "llvm.ceil.f64",
175+
"math.Cos": "llvm.cos.f64",
176+
"math.Cosh": "llvm.cosh.f64",
170177
"math.Exp": "llvm.exp.f64",
171178
"math.Exp2": "llvm.exp2.f64",
172179
"math.Floor": "llvm.floor.f64",
173180
"math.Log": "llvm.log.f64",
181+
"math.Sin": "llvm.sin.f64",
182+
"math.Sinh": "llvm.sinh.f64",
174183
"math.Sqrt": "llvm.sqrt.f64",
184+
"math.Tan": "llvm.tan.f64",
185+
"math.Tanh": "llvm.tanh.f64",
175186
"math.Trunc": "llvm.trunc.f64",
176187
}
177188

@@ -185,18 +196,40 @@ var mathToLLVMMapping = map[string]string{
185196
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
186197
// beneficial on architectures where 64-bit floating point operations are (much)
187198
// more expensive than 32-bit ones.
188-
func (b *builder) defineMathOp() {
189-
b.createFunctionStart(true)
190-
llvmName := mathToLLVMMapping[b.fn.RelString(nil)]
191-
if llvmName == "" {
192-
panic("unreachable: unknown math operation") // sanity check
199+
func (b *builder) defineMathOp() bool {
200+
llvmName, ok := mathToLLVMMapping[b.fn.RelString(nil)]
201+
if !ok {
202+
return false
203+
}
204+
if strings.HasSuffix(b.Triple, "-wasi") || llvmutil.Version() < 19 {
205+
// We don't have a real libc for wasip2. Until that is fixed, we need to
206+
// limit math intrinsics on WASI to a subset supported natively in
207+
// WebAssembly.
208+
// Also, since we don't know the specific libc we will target, disallow
209+
// these for all WASI targets.
210+
//
211+
// We also need to limit ourselves to LLVM 19 and above for the extended
212+
// set of math intrinsics, see:
213+
// https://discourse.llvm.org/t/rfc-all-the-math-intrinsics/78294
214+
switch b.fn.Name() {
215+
case "Ceil", "Exp", "Exp2", "Floor", "Log", "Sqrt", "Trunc":
216+
default:
217+
return false
218+
}
193219
}
220+
b.createFunctionStart(true)
194221
llvmFn := b.mod.NamedFunction(llvmName)
195222
if llvmFn.IsNil() {
196223
// The intrinsic doesn't exist yet, so declare it.
197-
// At the moment, all supported intrinsics have the form "double
198-
// foo(double %x)" so we can hardcode the signature here.
199-
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
224+
var llvmType llvm.Type
225+
switch b.fn.Name() {
226+
case "Atan2":
227+
// double atan2(double %y, double %x)
228+
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)
229+
default:
230+
// double foo(double %x)
231+
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
232+
}
200233
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
201234
}
202235
// Create a call to the intrinsic.
@@ -206,6 +239,7 @@ func (b *builder) defineMathOp() {
206239
}
207240
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
208241
b.CreateRet(result)
242+
return true
209243
}
210244

211245
func (b *builder) defineCryptoIntrinsic() bool {

0 commit comments

Comments
 (0)