mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-16 05:53:30 +00:00
wip qbe support
This commit is contained in:
parent
dee1914d90
commit
f1ff94c357
@ -1 +1,99 @@
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"robaertschi.xyz/robaertschi/tt/asm"
|
||||
"robaertschi.xyz/robaertschi/tt/asm/qbe"
|
||||
"robaertschi.xyz/robaertschi/tt/utils"
|
||||
)
|
||||
|
||||
@ -73,9 +74,16 @@ func (sp *SourceProgram) Build(backend asm.Backend, emitAsmOnly bool, toPrint To
|
||||
return id - 1
|
||||
}
|
||||
|
||||
err := sp.buildFasm(addRootNode, addNode, emitAsmOnly, toPrint)
|
||||
if err != nil {
|
||||
return err
|
||||
if backend == asm.Fasm {
|
||||
err := sp.buildFasm(addRootNode, addNode, emitAsmOnly, toPrint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if backend == asm.Qbe {
|
||||
err := sp.buildQbe(addRootNode, addNode, emitAsmOnly, toPrint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return runTasks(nodes, rootNodes, l)
|
||||
@ -93,7 +101,7 @@ func (sp *SourceProgram) buildFasm(addRootNode func(task) int, addNode func(task
|
||||
mainAsmOutput := strings.TrimSuffix(sp.InputFile, filepath.Ext(sp.InputFile)) + ".asm"
|
||||
|
||||
asmFile := addRootNode(NewFuncTask("generating assembly for "+sp.InputFile, func(output io.Writer) error {
|
||||
return build(output, sp.InputFile, mainAsmOutput, toPrint)
|
||||
return build(output, sp.InputFile, mainAsmOutput, toPrint, asm.Fasm)
|
||||
}))
|
||||
|
||||
if !emitAsmOnly {
|
||||
@ -108,3 +116,80 @@ func (sp *SourceProgram) buildFasm(addRootNode func(task) int, addNode func(task
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sp *SourceProgram) buildQbe(addRootNode func(task) int, addNode func(task, ...int) int, emitAsmOnly bool, toPrint ToPrintFlags) error {
|
||||
fasmPath, err := exec.LookPath("fasm")
|
||||
if err != nil {
|
||||
fasmPath, err = exec.LookPath("fasm2")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find fasm or fasm2, please install any those two using your systems package manager or from https://flatassembler.net")
|
||||
}
|
||||
}
|
||||
qbePath, err := exec.LookPath("qbe")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find qbe, please install using your systems package manager or from https://https://c9x.me/compile")
|
||||
}
|
||||
|
||||
asPath, err := exec.LookPath("as")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find the system `as` assembler, please install it using your systems package manager")
|
||||
}
|
||||
|
||||
ldPath, err := exec.LookPath("ld")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find the system `ld` linker, please install it using your systems package manager")
|
||||
}
|
||||
|
||||
mainAsmOutput := strings.TrimSuffix(sp.InputFile, filepath.Ext(sp.InputFile)) + ".qbe"
|
||||
|
||||
asmFile := addRootNode(NewFuncTask("generating assembly for "+sp.InputFile, func(output io.Writer) error {
|
||||
return build(output, sp.InputFile, mainAsmOutput, toPrint, asm.Qbe)
|
||||
}))
|
||||
|
||||
if !emitAsmOnly {
|
||||
|
||||
objectFileTasks := []int{}
|
||||
qbeStubAsm := "qbe_stub.asm"
|
||||
qbeStubO := "qbe_stub.o"
|
||||
generatedAsmFile := addRootNode(NewCreateFileTask(qbeStubAsm, qbe.Stub))
|
||||
id := addNode(NewProcessTask(fasmPath, qbeStubAsm, qbeStubO), generatedAsmFile)
|
||||
objectFileTasks = append(objectFileTasks, id)
|
||||
sp.ObjectFiles = append(sp.ObjectFiles, qbeStubO)
|
||||
|
||||
qbeOutput := strings.TrimSuffix(mainAsmOutput, filepath.Ext(mainAsmOutput)) + ".S"
|
||||
task := NewProcessTask(qbePath, mainAsmOutput, "-o", qbeOutput)
|
||||
task.WithName("running qbe on " + mainAsmOutput)
|
||||
qbeTask := addNode(task, asmFile)
|
||||
sp.InputAssemblies = append(sp.InputAssemblies, qbeOutput)
|
||||
|
||||
for _, asmFile := range sp.InputAssemblies {
|
||||
outputFile := strings.TrimSuffix(asmFile, filepath.Ext(asmFile)) + ".o"
|
||||
|
||||
if filepath.Ext(asmFile) == ".asm" {
|
||||
id := addRootNode(NewProcessTask(fasmPath, asmFile, outputFile))
|
||||
objectFileTasks = append(objectFileTasks, id)
|
||||
} else if filepath.Ext(asmFile) == ".S" {
|
||||
id := addRootNode(NewProcessTask(asPath, asmFile, "-o", outputFile))
|
||||
objectFileTasks = append(objectFileTasks, id)
|
||||
} else {
|
||||
panic(fmt.Sprintf("unkown asm file extension %q", filepath.Ext(asmFile)))
|
||||
}
|
||||
|
||||
sp.ObjectFiles = append(sp.ObjectFiles, outputFile)
|
||||
}
|
||||
ldTask := NewProcessTask(ldPath, append([]string{"-o", sp.OutputFile}, sp.ObjectFiles...)...)
|
||||
ldTaskId := addNode(ldTask, append([]int{generatedAsmFile}, objectFileTasks...)...)
|
||||
|
||||
for _, object := range sp.ObjectFiles {
|
||||
// Cleanup object files
|
||||
addNode(NewRemoveFileTask(object), ldTaskId)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
addNode(NewRemoveFileTask(mainAsmOutput), ldTaskId, qbeTask)
|
||||
addNode(NewRemoveFileTask(qbeStubAsm), ldTaskId, generatedAsmFile)
|
||||
addNode(NewRemoveFileTask(qbeOutput), ldTaskId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"robaertschi.xyz/robaertschi/tt/asm"
|
||||
"robaertschi.xyz/robaertschi/tt/asm/amd64"
|
||||
"robaertschi.xyz/robaertschi/tt/asm/qbe"
|
||||
"robaertschi.xyz/robaertschi/tt/lexer"
|
||||
"robaertschi.xyz/robaertschi/tt/parser"
|
||||
"robaertschi.xyz/robaertschi/tt/token"
|
||||
@ -84,6 +86,42 @@ func (rft *removeFileTask) Name() string { return rft.name }
|
||||
|
||||
func (rft *removeFileTask) WithName(name string) { rft.name = name }
|
||||
|
||||
type createFileTask struct {
|
||||
file string
|
||||
content string
|
||||
name string
|
||||
}
|
||||
|
||||
func NewCreateFileTask(file string, content string) task {
|
||||
return &createFileTask{
|
||||
file: file,
|
||||
content: content,
|
||||
name: fmt.Sprintf("writing file %q", file),
|
||||
}
|
||||
}
|
||||
|
||||
func (cft *createFileTask) Run(id int, output io.Writer, doneChan chan taskResult) {
|
||||
file, err := os.Create(cft.file)
|
||||
if err != nil {
|
||||
doneChan <- taskResult{
|
||||
Id: id,
|
||||
Err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_, err = file.WriteString(cft.content)
|
||||
doneChan <- taskResult{
|
||||
Id: id,
|
||||
Err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cft *createFileTask) Name() string { return cft.name }
|
||||
|
||||
func (cft *createFileTask) WithName(name string) { cft.name = name }
|
||||
|
||||
type funcTask struct {
|
||||
f func(io.Writer) error
|
||||
name string
|
||||
@ -108,7 +146,7 @@ func (rft *funcTask) WithName(name string) {
|
||||
rft.name = name
|
||||
}
|
||||
|
||||
func build(outputWriter io.Writer, input string, output string, toPrint ToPrintFlags) error {
|
||||
func build(outputWriter io.Writer, input string, output string, toPrint ToPrintFlags, backend asm.Backend) error {
|
||||
file, err := os.Open(input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open file %q because: %v", input, err)
|
||||
@ -158,19 +196,26 @@ func build(outputWriter io.Writer, input string, output string, toPrint ToPrintF
|
||||
io.WriteString(outputWriter,
|
||||
fmt.Sprintf("TTIR:\n%s\n%+#v\n", ir.String(), ir))
|
||||
}
|
||||
asm := amd64.CgProgram(ir)
|
||||
|
||||
asmOutput := asm.Emit()
|
||||
|
||||
asmOutputFile, err := os.Create(output)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create/truncate asm file %q because: %v\n", output, err)
|
||||
}
|
||||
defer asmOutputFile.Close()
|
||||
|
||||
if backend == asm.Fasm {
|
||||
asm := amd64.CgProgram(ir)
|
||||
asmOutput := asm.Emit()
|
||||
_, err = asmOutputFile.WriteString(asmOutput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to file %q because: %v\n", output, err)
|
||||
}
|
||||
} else if backend == asm.Qbe {
|
||||
err := qbe.Emit(asmOutputFile, ir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to file %q because: %v\n", output, err)
|
||||
}
|
||||
|
||||
_, err = asmOutputFile.WriteString(asmOutput)
|
||||
asmOutputFile.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to file %q because: %v\n", output, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -284,7 +329,9 @@ func runTasks(nodes map[int]*node, rootNodes []int, l *utils.Logger) error {
|
||||
}
|
||||
|
||||
for id, node := range nodes {
|
||||
if output[id].Len() > 0 {
|
||||
if output[id] == nil {
|
||||
l.Errorf("output of task %q is nil", nodes[id].task.Name())
|
||||
} else if output[id].Len() > 0 {
|
||||
l.Infof("task %q output: %s", node.task.Name(), output[id])
|
||||
}
|
||||
}
|
||||
|
21
main.go
21
main.go
@ -14,18 +14,20 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := term.EnterRawMode()
|
||||
if err != nil {
|
||||
fmt.Printf("could not enter raw mode %v", err)
|
||||
return
|
||||
}
|
||||
defer term.LeaveRawMode()
|
||||
// err := term.EnterRawMode()
|
||||
// if err != nil {
|
||||
// fmt.Printf("could not enter raw mode %v", err)
|
||||
// return
|
||||
// }
|
||||
// defer term.LeaveRawMode()
|
||||
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s [flags] input\nPossible flags:\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
qbe := flag.Bool("qbe", false, "Use the qbe backend")
|
||||
|
||||
var output string
|
||||
flag.StringVar(&output, "o", "", "Output a executable named `file`")
|
||||
flag.StringVar(&output, "output", "", "Output a executable named `file`")
|
||||
@ -59,7 +61,12 @@ func main() {
|
||||
|
||||
logger := log.New(os.Stderr, "", log.Lshortfile)
|
||||
|
||||
err = build.NewSourceProgram(input, output).Build(asm.Fasm, *emitAsmOnly, build.ToPrintFlags(toPrint))
|
||||
backend := asm.Fasm
|
||||
if *qbe {
|
||||
backend = asm.Qbe
|
||||
}
|
||||
|
||||
err := build.NewSourceProgram(input, output).Build(backend, *emitAsmOnly, build.ToPrintFlags(toPrint))
|
||||
if err != nil {
|
||||
logger.Fatalln(err)
|
||||
term.Exit(1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user