Go Assembly by Example: Arcsin
The following example is the implementation of the Arcsin function (the inverse of sin) taken from the standard Go library.
package main
import (
"fmt"
)
func Asin(x float64) float64
func main() {
fmt.Println(Asin(1))
}
This #include
is required to use Go Assembly’s constants like NOSPLIT. Otherwise their associated number can be used instead (NOSPLIT=4).
#include "textflag.h"
In this example, we use the TEXT
instruction with 3 arguments instead of 2. The new argument NOSPLIT
is an optimization to warn the compiler that no expansion of the stack will be needed. Indeed, the third argument indicates a stack of 0 bytes.
TEXT ·Asin(SB),NOSPLIT,$0-16
To implement the Arcsin function on a float64 argument, the FPU registers will be used. FMOVD
sets F0 and F1 to the function’s input x.
FMOVD x+0(FP), F0
FMOVD F0, F1
FMULD F0, F0
FLD1
pushes +1.0
onto the FPU
stack. This makes F0 = 1, F1 = F0 and F2 = F1
.
FLD1
FSUBRDP substracts F0 to F1 and stores it in F1. It then pops the FPU stack, this makes F0 = F1, F1 = F2 and F2 = NaN.
In other words: F0 = 1-x*x and F1 = x
.
FSUBRDP F0, F1
FSQRT computes the square root of F0 and stores it in F0.
FSQRT
FPATAN
computes arctan(F1/F0)
and stores it in F1, then it pops the FPU stack placing the result in F0 and setting F1 to NaN
.
This gives us arctan(x / sqrt(1 - x * x))
which is a way to compute the arcsin function on x.
FPATAN
FMOVDP
moves the result to the return address (the memory offset from FP after the argument of 64-bit) and pops the FPU stack, getting rid of the last FPU
value F0
.
FMOVDP F0, ret+8(FP)
RET
Next example: Sync Atomic.
ft_update_time2018-02-03 13:08