Documentation
¶
Overview ¶
Go0 is a [self] educational toy Go compiler for a minimalistic subset of Go. It demonstrates how to use the modernc.org/libqbe compiler back end.
Supported targets ¶
Go0 builds, runs and was tested to correctly produce native executable files on
GOOS/GOARCH ----------- darwin/amd64 darwin/arm64 freebsd/amd64 freebsd/arm64 linux/amd64 linux/arm64 linux/riscv64 netbsd/amd64 openbsd/amd64
The input ¶
This is a valid, self-contained Go program:
package main
func fib(a int) int {
if a < 2 {
return a
}
return fib(a-1) + fib(a-2)
}
func main() {
println(fib(42))
}
Go0 can handle the code above and nothing else.
What Go0 does ¶
When executed, the "compiler" creates a package in a temporary directory with import path "example.com/fib" in its go.mod file, containing one Go file, fib.go with the input program as seen above.
It then uses golang.org/x/tools/go/packages to load the package and the golang.org/x/tools/go/ssa{,/ssautil} packages to construct its SSA form.
Next step is to generate QBE IL from the SSA. Then we use modernc.org/libqbe to generate fib.s, its assembler version.
We can use the system assembler (like GNU as) and linker to produce the executable. However, it is easier to just hand over the fib.s file to the system C compiler. It'll do the same for us in one step. It also takes care of adding proper startup code etc.
Then Go0 shows the size of the executable binary file, its type via file(1) and finally the output the executable produces along with time used to compute the result.
The compiler SLOC ¶
~/src/modernc.org/go0$ gocloc main.go ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Go 1 31 3 192 ------------------------------------------------------------------------------- TOTAL 1 31 3 192 ------------------------------------------------------------------------------- ~/src/modernc.org/go0$
Example session ¶
This output was produced on a Linux/Amd64 machine.
~/src/modernc.org/go0$ go0
---- Go
package main
func fib(a int) int {
if a < 2 {
return a
}
return fib(a-1) + fib(a-2)
}
func main() {
println(fib(42))
}
---- SSA
# Name: example.com/fib.init
# Package: example.com/fib
# Synthetic: package initializer
func init():
0: entry P:0 S:2
t0 = *init$guard bool
if t0 goto 2 else 1
1: init.start P:1 S:1
*init$guard = true:bool
jump 2
2: init.done P:2 S:0
return
# Name: example.com/fib.fib
# Package: example.com/fib
# Location: /tmp/go0-1743633831/fib.go:3:6
func fib(a int) int:
0: entry P:0 S:2
t0 = a < 2:int bool
if t0 goto 1 else 2
1: if.then P:1 S:0
return a
2: if.done P:1 S:0
t1 = a - 1:int int
t2 = fib(t1) int
t3 = a - 2:int int
t4 = fib(t3) int
t5 = t2 + t4 int
return t5
# Name: example.com/fib.main
# Package: example.com/fib
# Location: /tmp/go0-1743633831/fib.go:10:6
func main():
0: entry P:0 S:0
t0 = fib(42:int) int
t1 = println(t0) ()
return
---- QBE
function l $fib(l %a) {
@.0
%t0 =w csltl %a, 2
jnz %t0, @.1, @.2
@.1
ret %a
@.2
%t1 =l sub %a, 1
%t2 =l call $fib(l %t1)
%t3 =l sub %a, 2
%t4 =l call $fib(l %t3)
%t5 =l add %t2, %t4
ret %t5
}
export function w $main() {
@.0
%t0 =l call $fib(l 42)
call $printf(l $fmt, ..., l %t0)
ret 0
}
data $fmt = { b "%d\n", b 0 }
---- ASM
.text
fib:
pushq %rbp
movq %rsp, %rbp
subq $8, %rsp
pushq %rbx
movq %rdi, %rbx
cmpq $2, %rbx
jl .Lbb2
movq %rbx, %rdi
subq $1, %rdi
callq fib
xchgq %rax, %rbx
movq %rax, %rdi
subq $2, %rdi
callq fib
addq %rbx, %rax
jmp .Lbb3
.Lbb2:
movq %rbx, %rax
.Lbb3:
popq %rbx
leave
ret
.type fib, @function
.size fib, .-fib
/* end function fib */
.text
.globl main
main:
pushq %rbp
movq %rsp, %rbp
movl $42, %edi
callq fib
movq %rax, %rsi
leaq fmt(%rip), %rdi
movl $0, %eax
callq printf
movl $0, %eax
leave
ret
.type main, @function
.size main, .-main
/* end function main */
.data
.balign 8
fmt:
.ascii "%d\n"
.byte 0
/* end data */
.section .note.GNU-stack,"",@progbits
---- Outputs
-rwxr-xr-x 1 jnml jnml 16024 Nov 17 15:07 /tmp/go0-1743633831/fib
/tmp/go0-1743633831/fib: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=058411db3a5de732ea39648339e88d636aef0e2a, for GNU/Linux 3.2.0, not stripped
267914296
1.94user 0.00system 0:01.94elapsed 100%CPU (0avgtext+0avgdata 1408maxresident)k
0inputs+0outputs (0major+92minor)pagefaults 0swaps
~/src/modernc.org/go0$