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" ) // Finalize generates the final machine code. func Finalize(functions map[string]*Function) ([]byte, []byte) { a := entry() main := functions["main"] delete(functions, "main") a.Merge(&main.Assembler) for _, f := range functions { a.Merge(&f.Assembler) } code, data := a.Finalize() return code, data } // entry returns 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`. func entry() *asm.Assembler { entry := asm.New() entry.Call("main") entry.RegisterNumber(asm.MOVE, x64.SyscallRegisters[0], linux.Exit) entry.RegisterNumber(asm.MOVE, x64.SyscallRegisters[1], 0) entry.Syscall() return entry }