77 lines
2.4 KiB
Go
77 lines
2.4 KiB
Go
package compiler
|
|
|
|
import (
|
|
"git.akyoto.dev/cli/q/src/asm"
|
|
"git.akyoto.dev/cli/q/src/asmc"
|
|
"git.akyoto.dev/cli/q/src/config"
|
|
"git.akyoto.dev/cli/q/src/core"
|
|
"git.akyoto.dev/cli/q/src/dll"
|
|
"git.akyoto.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),
|
|
}
|
|
|
|
final.Call("main.main")
|
|
final.Label(asm.LABEL, "_exit")
|
|
|
|
switch config.TargetOS {
|
|
case config.Linux:
|
|
final.RegisterNumber(asm.MOVE, x86.SyscallInputRegisters[0], LinuxExit)
|
|
final.RegisterNumber(asm.MOVE, x86.SyscallInputRegisters[1], 0)
|
|
final.Syscall()
|
|
case config.Mac:
|
|
final.RegisterNumber(asm.MOVE, x86.SyscallInputRegisters[0], MacExit)
|
|
final.RegisterNumber(asm.MOVE, x86.SyscallInputRegisters[1], 0)
|
|
final.Syscall()
|
|
case config.Windows:
|
|
final.RegisterNumber(asm.MOVE, x86.WindowsInputRegisters[0], 0)
|
|
final.RegisterNumber(asm.AND, x86.RSP, -16)
|
|
final.DLLCall("kernel32.ExitProcess")
|
|
}
|
|
|
|
r.DLLs = dll.List{
|
|
{Name: "kernel32", Functions: []string{"ExitProcess"}},
|
|
}
|
|
|
|
r.Traversed = make(map[*core.Function]bool, len(r.Functions))
|
|
|
|
// This will place the main function immediately after the entry point
|
|
// and also add everything the main function calls recursively.
|
|
r.eachFunction(r.Main, 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)
|
|
}
|