– 1 –
One day, there was a programmer named Bob who wrote a factorial function implemented in Go. However, as the data size grew, the running speed became very slow. So, his boss asked him to rewrite it in assembly language. Bob was reluctant but learned assembly language and wrote a factorial function that ran extremely fast.
In the end, Bob became an outstanding assembly programmer and gained tremendous success due to his factorial function. He realized that assembly language could make his programs faster and more concise.
So, if you want to write efficient and concise programs, you need to learn assembly language.
– 2 –
Let’s start simple by implementing a complete factorial function in Go:
package main
import "fmt"
func factorial(n int) int { result := 1 for i := 1; i <= n; i++ { result *= i } return result}
func main() { fmt.Println(factorial(5)) // Output: 120}
– 3 –
Next, we will convert the factorial function into assembly language.
First, we need to remove the body of the factorial function in the Go code, leaving only the function definition (declaration), which tells the Go compiler that the function will be implemented in another file using assembly.
package main
import "fmt"
func factorial(n int) int
func main() { fmt.Println(factorial(5))}
Then, we create a new file fac.s with the following content:
TEXT ·factorial(SB), $0-8 MOVQ n+0(FP), CX MOVQ $1, DXLOOP: IMULQ CX, DX DECQ CX JNZ LOOP MOVQ DX, result+8(FP) RET
Compile and run:
$ go run .
120
– 4 –
This code is a factorial function implemented in assembly language. Its purpose is to calculate the factorial of a given number n.
The implementation uses a loop to calculate the factorial. In each iteration of the loop, it multiplies the result by the current number and then decrements the current number by 1. Finally, it stores the result in the result variable and exits the function.
Next, let’s analyze this code line by line:
-
TEXT ·factorial(SB), $0-8
-
TEXT indicates that this method is in the TEXT segment
-
· is the Unicode ‘middle dot’ (the key to the left of 1 in Chinese input), omitting the package name indicates that this is the factorial function of the main package
-
SB is the stack base pointer, a ‘pseudo-register’ in Go ASM (not a hardware register), roughly equivalent to the program’s starting address
-
$0-8: 0 indicates that this function has no local variables, 8 indicates that the return value occupies 8 bytes
-
MOVQ n+0(FP), CX
-
MOVQ’s Q indicates 8 bytes
-
n+0(FP) indicates the variable n at the position FP (Frame Pointer, pseudo-register indicating the start of this function’s stack frame) + 0 (i.e., the first parameter). Note that Go ASM requires the form ‘variable name + offset(FP)’, but the variable name n is not meaningful, just for mnemonic purposes.
-
CX refers to the x86/x86_64 CX (16bit), ECX (32bit), RCX (64bit) registers, the length depends on the preceding instruction (MOVQ is 64bit)
-
This line means to write the value of the first parameter into RCX
-
MOVQ $1, DX
-
$1: a number starting with $ is an immediate value
-
This line means to assign the value 1 to RDX
-
IMULQ CX, DX
-
DX = DX * CX
-
DECQ CX
-
CX = CX – 1
-
JNZ LOOP
-
JNZ: Jump if Not Zero
-
Jump to LOOP when CX is not equal to 0
-
MOVQ DX, result+8(FP)
-
Writes the value of RDX to the position FP+8.
-
RET
-
Returns to the caller.
– 5 –
It is important to note that for the sake of simplicity, this assembly code is not equivalent to the Go code.
If the input n is negative, it will cause the code to fail. This is because there is no boundary condition check in this code, so if the input n is negative, it will enter an infinite loop, causing the program to run for too long or crash.
– 6 –
Finally, this article was completed with the help of ChatGPT and me.
If you are observant, you should have noticed what contributions it made and the mistake it made.
p.s. The title image was generated by 6pen.art with the keywords ‘Golang Assembly Language Factorial’.
References:
[1] A Brief Tutorial on Golang ASM: https://jiajunhuang.com/articles/2020_04_22-go_asm.md.html
[2] A Quick Guide to Go’s Assembler: https://go.dev/doc/asm