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 }