55 lines
1.7 KiB
Go
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)
|
|
}
|