q/src/build/Result.go

42 lines
1.2 KiB
Go

package build
import (
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/os/linux"
)
// Result contains all the compiled functions in a build.
type Result struct {
Functions map[string]*Function
instructionCount int
}
// Finalize generates the final machine code.
func (r Result) Finalize() ([]byte, []byte) {
// This will be the entry point of the executable.
// The only job of the entry function is to call `main` and exit cleanly.
// The reason we call `main` instead of using `main` itself is to place
// a return address on the stack, which allows return statements in `main`.
final := asm.Assembler{
Instructions: make([]asm.Instruction, 0, r.instructionCount+4),
}
final.Call("main")
final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[0], linux.Exit)
final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[1], 0)
final.Syscall()
// Place the `main` function immediately after the entry point.
main := r.Functions["main"]
delete(r.Functions, "main")
final.Merge(&main.Assembler)
// Merge all the remaining functions.
for _, f := range r.Functions {
final.Merge(&f.Assembler)
}
code, data := final.Finalize()
return code, data
}