Skip to content

Commit 452143e

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 452143e

2 files changed

Lines changed: 49 additions & 17 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: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,24 @@ func (b *builder) createMachineKeepAliveImpl() {
166166
}
167167

168168
var mathToLLVMMapping = map[string]string{
169-
"math.Ceil": "llvm.ceil.f64",
170-
"math.Exp": "llvm.exp.f64",
171-
"math.Exp2": "llvm.exp2.f64",
172-
"math.Floor": "llvm.floor.f64",
173-
"math.Log": "llvm.log.f64",
174-
"math.Sqrt": "llvm.sqrt.f64",
175-
"math.Trunc": "llvm.trunc.f64",
169+
"math.Acos": "llvm.acos.f64",
170+
"math.Asin": "llvm.asin.f64",
171+
"math.Atan": "llvm.atan.f64",
172+
"math.Atan2": "llvm.atan2.f64",
173+
"math.Ceil": "llvm.ceil.f64",
174+
"math.Cos": "llvm.cos.f64",
175+
"math.Cosh": "llvm.cosh.f64",
176+
"math.Exp": "llvm.exp.f64",
177+
"math.Exp2": "llvm.exp2.f64",
178+
"math.Floor": "llvm.floor.f64",
179+
"math.Log": "llvm.log.f64",
180+
"math.Sin": "llvm.sin.f64",
181+
"math.Sincos": "llvm.sincos.f64",
182+
"math.Sinh": "llvm.sinh.f64",
183+
"math.Sqrt": "llvm.sqrt.f64",
184+
"math.Tan": "llvm.tan.f64",
185+
"math.Tanh": "llvm.tanh.f64",
186+
"math.Trunc": "llvm.trunc.f64",
176187
}
177188

178189
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
@@ -185,18 +196,39 @@ 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") {
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+
switch b.fn.Name() {
211+
case "Ceil", "Exp", "Exp2", "Floor", "Log", "Sqrt", "Trunc":
212+
default:
213+
return false
214+
}
193215
}
216+
b.createFunctionStart(true)
194217
llvmFn := b.mod.NamedFunction(llvmName)
195218
if llvmFn.IsNil() {
196219
// 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)
220+
var llvmType llvm.Type
221+
switch b.fn.Name() {
222+
case "Atan2":
223+
// double atan2(double %y, double %x)
224+
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)
225+
case "Sincos":
226+
// {double, double} sincos(double %x)
227+
llvmType = llvm.FunctionType(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false), []llvm.Type{b.ctx.DoubleType()}, false)
228+
default:
229+
// double foo(double %x)
230+
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
231+
}
200232
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
201233
}
202234
// Create a call to the intrinsic.
@@ -206,6 +238,7 @@ func (b *builder) defineMathOp() {
206238
}
207239
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
208240
b.CreateRet(result)
241+
return true
209242
}
210243

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

0 commit comments

Comments
 (0)