q/src/core/Compile.go
Eduard Urbach 562c839835
All checks were successful
/ test (push) Successful in 15s
Added Windows support
2025-07-01 13:41:14 +02:00

147 lines
No EOL
3.2 KiB
Go

package core
import (
"slices"
"git.urbach.dev/cli/q/src/asm"
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/cli/q/src/ssa"
"git.urbach.dev/cli/q/src/types"
"git.urbach.dev/cli/q/src/x86"
)
// Compile turns a function into machine code.
func (f *Function) Compile() {
extra := 0
for i, input := range f.Input {
if input.Name == "_" {
continue
}
array, isArray := input.Typ.(*types.Array)
if isArray {
pointer := &ssa.Parameter{
Index: uint8(i + extra),
Name: input.Name,
Typ: &types.Pointer{To: array.Of},
Source: input.Source,
}
f.Append(pointer)
f.Identifiers[pointer.Name] = pointer
extra++
length := &ssa.Parameter{
Index: uint8(i + extra),
Name: input.Name + ".len",
Typ: types.AnyInt,
Source: input.Source,
}
f.Append(length)
f.Identifiers[length.Name] = length
continue
}
input.Index = uint8(i + extra)
f.Append(input)
f.Identifiers[input.Name] = input
}
for instr := range f.Body.Instructions {
f.Err = f.CompileInstruction(instr)
if f.Err != nil {
return
}
}
f.Err = f.CheckDeadCode()
if f.Err != nil {
return
}
f.Assembler.Append(&asm.Label{Name: f.UniqueName})
if !f.IsLeaf() && f.UniqueName != "core.init" {
f.Assembler.Append(&asm.FunctionStart{})
}
for instr := range f.Values {
switch instr := instr.(type) {
case *ssa.Call:
arg := instr.Arguments[0].(*ssa.Function)
fn := f.All.Functions[arg.UniqueName]
if fn.IsExtern() {
f.mv(instr.Arguments[1:], f.CPU.ExternCall)
f.Assembler.Append(&asm.MoveRegisterRegister{Destination: x86.R5, Source: x86.SP})
f.Assembler.Append(&asm.AndRegisterNumber{Destination: x86.SP, Source: x86.SP, Number: -16})
f.Assembler.Append(&asm.SubRegisterNumber{Destination: x86.SP, Source: x86.SP, Number: 32})
f.Assembler.Append(&asm.CallExtern{Library: fn.Package, Function: fn.Name})
f.Assembler.Append(&asm.MoveRegisterRegister{Destination: x86.SP, Source: x86.R5})
} else {
f.mv(instr.Arguments[1:], f.CPU.Call)
f.Assembler.Append(&asm.Call{Label: fn.UniqueName})
}
case *ssa.Syscall:
f.mv(instr.Arguments, f.CPU.Syscall)
f.Assembler.Append(&asm.Syscall{})
case *ssa.Return:
f.Assembler.Append(&asm.Return{})
}
}
if !f.IsLeaf() && f.UniqueName != "core.init" {
f.Assembler.Append(&asm.FunctionEnd{})
}
if f.UniqueName != "core.exit" {
f.Assembler.Append(&asm.Return{})
}
}
func (f *Function) mv(args []ssa.Value, registers []cpu.Register) {
extra := 0
for _, arg := range args {
switch arg.(type) {
case *ssa.Bytes:
extra++
}
}
for i, arg := range slices.Backward(args) {
switch arg := arg.(type) {
case *ssa.Int:
f.Assembler.Append(&asm.MoveRegisterNumber{
Destination: registers[i+extra],
Number: arg.Int,
})
case *ssa.Parameter:
f.Assembler.Append(&asm.MoveRegisterRegister{
Destination: registers[i+extra],
Source: f.CPU.Call[arg.Index],
})
case *ssa.Bytes:
f.Assembler.SetData("data0", arg.Bytes)
f.Assembler.Append(&asm.MoveRegisterNumber{
Destination: registers[i+extra],
Number: len(arg.Bytes),
})
extra--
f.Assembler.Append(&asm.MoveRegisterLabel{
Destination: registers[i+extra],
Label: "data0",
})
}
}
}