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) }