mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-16 05:53:30 +00:00
122 lines
2.2 KiB
Go
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
|
|
}
|