q/src/compiler/finalize.go
2025-02-25 17:16:09 +01:00

55 lines
1.7 KiB
Go

package compiler
import (
"git.urbach.dev/cli/q/src/asm"
"git.urbach.dev/cli/q/src/asmc"
"git.urbach.dev/cli/q/src/config"
"git.urbach.dev/cli/q/src/core"
"git.urbach.dev/cli/q/src/x86"
)
// finalize generates the final machine code.
func (r *Result) finalize() {
// 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+8),
Data: make(map[string][]byte, r.DataCount),
}
r.Traversed = make(map[*core.Function]bool, len(r.Functions))
// This will place the init function immediately after the entry point
// and also add everything the init function calls recursively.
r.eachFunction(r.Init, r.Traversed, func(f *core.Function) {
final.Merge(f.Assembler)
for _, library := range f.DLLs {
for _, fn := range library.Functions {
r.DLLs = r.DLLs.Append(library.Name, fn)
}
}
})
final.Label(asm.LABEL, "_crash")
switch config.TargetOS {
case config.Linux:
final.RegisterNumber(asm.MOVE, x86.SyscallInputRegisters[0], LinuxExit)
final.RegisterNumber(asm.MOVE, x86.SyscallInputRegisters[1], 1)
final.Syscall()
case config.Mac:
final.RegisterNumber(asm.MOVE, x86.SyscallInputRegisters[0], MacExit)
final.RegisterNumber(asm.MOVE, x86.SyscallInputRegisters[1], 1)
final.Syscall()
case config.Windows:
final.RegisterNumber(asm.MOVE, x86.WindowsInputRegisters[0], 1)
final.RegisterNumber(asm.AND, x86.RSP, -16)
final.DLLCall("kernel32.ExitProcess")
}
r.Code, r.Data = asmc.Finalize(final, r.DLLs)
}