tt/asm/qbe/qbe.go
2025-01-31 21:22:57 +01:00

122 lines
2.2 KiB
Go

package qbe
import (
"fmt"
"io"
"robaertschi.xyz/robaertschi/tt/ast"
"robaertschi.xyz/robaertschi/tt/ttir"
_ "embed"
)
//go:embed qbe_stub.asm
var Stub string
func emitf(w io.Writer, format string, args ...any) error {
_, err := w.Write([]byte(fmt.Sprintf(format, args...)))
return err
}
func Emit(output io.Writer, input *ttir.Program) error {
if input.MainFunction.HasReturnValue {
emitf(output, `
export function $_start() {
@start
%%result =l call $main()
call $syscall1(l 60, l %%result)
hlt
}
`,
)
} else {
emitf(output, `
export function $_start() {
@start
call $main()
call $syscall1(l 60, l 0)
hlt
}
`,
)
}
for _, f := range input.Functions {
err := emitFunction(output, f)
if err != nil {
return err
}
}
return nil
}
func emitFunction(w io.Writer, f *ttir.Function) error {
emitf(w, "export function ")
if f.HasReturnValue {
if err := emitf(w, "l "); err != nil {
return err
}
}
if err := emitf(w, "$%s() {\n@start\n", f.Name); err != nil {
return err
}
for _, i := range f.Instructions {
if err := emitInstruction(w, i); err != nil {
return err
}
}
return emitf(w, "}\n")
}
func emitOperand(op ttir.Operand) string {
switch op := op.(type) {
case *ttir.Constant:
return fmt.Sprintf("%d", op.Value)
case *ttir.Var:
return "%" + op.Value
}
panic(fmt.Sprintf("invalid operand %T", op))
}
func emitInstruction(w io.Writer, i ttir.Instruction) error {
switch i := i.(type) {
case *ttir.Ret:
if op := i.Op; op != nil {
if err := emitf(w, "\tret %s\n", emitOperand(i.Op)); err != nil {
return err
}
} else {
if err := emitf(w, "\tret\n"); err != nil {
return err
}
}
case *ttir.Binary:
var inst string
switch i.Operator {
case ast.Add:
inst = "add"
case ast.Subtract:
inst = "sub"
case ast.Multiply:
inst = "mul"
case ast.Divide:
inst = "div"
case ast.GreaterThan:
inst = "csgtl"
case ast.GreaterThanEqual:
inst = "csgel"
case ast.LessThan:
inst = "csltl"
case ast.LessThanEqual:
inst = "cslel"
}
if err := emitf(w, "\t%s =l %s %s, %s\n", emitOperand(i.Dst), inst, emitOperand(i.Lhs), emitOperand(i.Rhs)); err != nil {
return err
}
}
return nil
}