mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-18 23:13:29 +00:00
finished binary expression
This commit is contained in:
parent
28d161ed62
commit
8bb0bb9a4e
@ -9,22 +9,6 @@ type Program struct {
|
|||||||
Functions []Function
|
Functions []Function
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Program) Emit() string {
|
|
||||||
var builder strings.Builder
|
|
||||||
|
|
||||||
for _, function := range p.Functions {
|
|
||||||
builder.WriteString(function.Emit())
|
|
||||||
builder.WriteString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Function struct {
|
|
||||||
Name string
|
|
||||||
Instructions []Instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
// This calls the main function and uses it's return value to exit
|
// This calls the main function and uses it's return value to exit
|
||||||
const executableAsmHeader = "format ELF64 executable\n" +
|
const executableAsmHeader = "format ELF64 executable\n" +
|
||||||
"segment readable executable\n" +
|
"segment readable executable\n" +
|
||||||
@ -35,11 +19,28 @@ const executableAsmHeader = "format ELF64 executable\n" +
|
|||||||
" mov rax, 60\n" +
|
" mov rax, 60\n" +
|
||||||
" syscall\n"
|
" syscall\n"
|
||||||
|
|
||||||
|
func (p *Program) Emit() string {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString(executableAsmHeader)
|
||||||
|
|
||||||
|
for _, function := range p.Functions {
|
||||||
|
builder.WriteString(function.Emit())
|
||||||
|
builder.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Function struct {
|
||||||
|
StackOffset int64
|
||||||
|
Name string
|
||||||
|
Instructions []Instruction
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Function) Emit() string {
|
func (f *Function) Emit() string {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
|
||||||
builder.WriteString(executableAsmHeader)
|
builder.WriteString(fmt.Sprintf("%s:\n push rbp\n mov rbp, rsp\n add rsp, %d\n", f.Name, f.StackOffset))
|
||||||
builder.WriteString(fmt.Sprintf("%s:\n", f.Name))
|
|
||||||
|
|
||||||
for _, inst := range f.Instructions {
|
for _, inst := range f.Instructions {
|
||||||
builder.WriteString(fmt.Sprintf(" %s\n", inst.InstructionString()))
|
builder.WriteString(fmt.Sprintf(" %s\n", inst.InstructionString()))
|
||||||
@ -51,25 +52,46 @@ func (f *Function) Emit() string {
|
|||||||
type Opcode string
|
type Opcode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Mov Opcode = "mov"
|
|
||||||
Ret Opcode = "ret"
|
// Two operands
|
||||||
Add Opcode = "add"
|
Mov Opcode = "mov" // Lhs: dst, Rhs: src, or better said intel syntax
|
||||||
Sub Opcode = "sub"
|
Add Opcode = "add"
|
||||||
Imull Opcode = "imul"
|
Sub Opcode = "sub"
|
||||||
Idiv Opcode = "idiv"
|
Imul Opcode = "imul"
|
||||||
|
|
||||||
|
// One operand
|
||||||
|
Idiv Opcode = "idiv"
|
||||||
|
|
||||||
|
// No operands
|
||||||
|
Ret Opcode = "ret"
|
||||||
|
Cdq Opcode = "cdq"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Instruction struct {
|
type Instruction struct {
|
||||||
Opcode Opcode
|
Opcode Opcode
|
||||||
Lhs Operand
|
// Dst
|
||||||
Rhs Operand
|
Lhs Operand
|
||||||
|
// Src
|
||||||
|
Rhs Operand
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Instruction) InstructionString() string {
|
func (i *Instruction) InstructionString() string {
|
||||||
|
|
||||||
|
if i.Opcode == Ret {
|
||||||
|
return fmt.Sprintf("mov rsp, rbp\n pop rbp\n ret\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// No operands
|
||||||
if i.Lhs == nil {
|
if i.Lhs == nil {
|
||||||
return fmt.Sprintf("%s", i.Opcode)
|
return fmt.Sprintf("%s", i.Opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// One operand
|
||||||
|
if i.Rhs == nil {
|
||||||
|
return fmt.Sprintf("%s %s", i.Opcode, i.Lhs.OperandString(Eight))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two operands
|
||||||
return fmt.Sprintf("%s %s, %s", i.Opcode, i.Lhs.OperandString(Eight), i.Rhs.OperandString(Eight))
|
return fmt.Sprintf("%s %s, %s", i.Opcode, i.Lhs.OperandString(Eight), i.Rhs.OperandString(Eight))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +106,7 @@ type Register int
|
|||||||
const (
|
const (
|
||||||
AX Register = iota
|
AX Register = iota
|
||||||
R10
|
R10
|
||||||
|
R11
|
||||||
|
|
||||||
One OperandSize = iota
|
One OperandSize = iota
|
||||||
Four
|
Four
|
||||||
@ -108,6 +131,14 @@ func (r Register) OperandString(size OperandSize) string {
|
|||||||
return "r10d"
|
return "r10d"
|
||||||
}
|
}
|
||||||
return "r10"
|
return "r10"
|
||||||
|
case R11:
|
||||||
|
switch size {
|
||||||
|
case One:
|
||||||
|
return "r11b"
|
||||||
|
case Four:
|
||||||
|
return "r11d"
|
||||||
|
}
|
||||||
|
return "r11"
|
||||||
}
|
}
|
||||||
return "INVALID_REGISTER"
|
return "INVALID_REGISTER"
|
||||||
}
|
}
|
||||||
@ -121,5 +152,22 @@ func (i Imm) OperandString(size OperandSize) string {
|
|||||||
type Stack int64
|
type Stack int64
|
||||||
|
|
||||||
func (s Stack) OperandString(size OperandSize) string {
|
func (s Stack) OperandString(size OperandSize) string {
|
||||||
return fmt.Sprintf("rbp(%d)", s)
|
|
||||||
|
var sizeString string
|
||||||
|
switch size {
|
||||||
|
case One:
|
||||||
|
sizeString = "byte"
|
||||||
|
case Four:
|
||||||
|
sizeString = "dword"
|
||||||
|
case Eight:
|
||||||
|
sizeString = "qword"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s [rsp %+d]", sizeString, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pseudo string
|
||||||
|
|
||||||
|
func (s Pseudo) OperandString(size OperandSize) string {
|
||||||
|
panic("Pseudo Operands cannot be represented in asm")
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package amd64
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"robaertschi.xyz/robaertschi/tt/ast"
|
||||||
"robaertschi.xyz/robaertschi/tt/ttir"
|
"robaertschi.xyz/robaertschi/tt/ttir"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,6 +11,8 @@ func toAsmOperand(op ttir.Operand) Operand {
|
|||||||
switch op := op.(type) {
|
switch op := op.(type) {
|
||||||
case *ttir.Constant:
|
case *ttir.Constant:
|
||||||
return Imm(op.Value)
|
return Imm(op.Value)
|
||||||
|
case *ttir.Var:
|
||||||
|
return Pseudo(op.Value)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unkown operand %T", op))
|
panic(fmt.Sprintf("unkown operand %T", op))
|
||||||
}
|
}
|
||||||
@ -22,9 +25,14 @@ func CgProgram(prog *ttir.Program) Program {
|
|||||||
funcs = append(funcs, cgFunction(f))
|
funcs = append(funcs, cgFunction(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Program{
|
newProgram := Program{
|
||||||
Functions: funcs,
|
Functions: funcs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newProgram = replacePseudo(newProgram)
|
||||||
|
newProgram = instructionFixup(newProgram)
|
||||||
|
|
||||||
|
return newProgram
|
||||||
}
|
}
|
||||||
|
|
||||||
func cgFunction(f ttir.Function) Function {
|
func cgFunction(f ttir.Function) Function {
|
||||||
@ -53,7 +61,156 @@ func cgInstruction(i ttir.Instruction) []Instruction {
|
|||||||
Opcode: Ret,
|
Opcode: Ret,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
case *ttir.Binary:
|
||||||
|
return cgBinary(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return []Instruction{}
|
return []Instruction{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cgBinary(b *ttir.Binary) []Instruction {
|
||||||
|
switch b.Operator {
|
||||||
|
case ast.Add, ast.Subtract, ast.Multiply:
|
||||||
|
var opcode Opcode
|
||||||
|
switch b.Operator {
|
||||||
|
case ast.Add:
|
||||||
|
opcode = Add
|
||||||
|
case ast.Subtract:
|
||||||
|
opcode = Sub
|
||||||
|
case ast.Multiply:
|
||||||
|
opcode = Imul
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Instruction{
|
||||||
|
{Opcode: Mov, Lhs: toAsmOperand(b.Dst), Rhs: toAsmOperand(b.Lhs)},
|
||||||
|
{Opcode: opcode, Lhs: toAsmOperand(b.Dst), Rhs: toAsmOperand(b.Rhs)},
|
||||||
|
}
|
||||||
|
case ast.Divide:
|
||||||
|
return []Instruction{
|
||||||
|
{Opcode: Mov, Lhs: Register(AX), Rhs: toAsmOperand(b.Lhs)},
|
||||||
|
{Opcode: Cdq},
|
||||||
|
{Opcode: Idiv, Lhs: toAsmOperand(b.Rhs)},
|
||||||
|
{Opcode: Mov, Lhs: toAsmOperand(b.Dst), Rhs: Register(AX)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("unknown binary operator, %v", b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass, replace all the pseudos with stack addresses
|
||||||
|
type replacePseudoPass struct {
|
||||||
|
identToOffset map[string]int64
|
||||||
|
currentOffset int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func replacePseudo(prog Program) Program {
|
||||||
|
newFunctions := make([]Function, 0)
|
||||||
|
|
||||||
|
for _, f := range prog.Functions {
|
||||||
|
newFunctions = append(newFunctions, rpFunction(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Program{Functions: newFunctions}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rpFunction(f Function) Function {
|
||||||
|
newInstructions := make([]Instruction, 0)
|
||||||
|
|
||||||
|
r := &replacePseudoPass{
|
||||||
|
identToOffset: make(map[string]int64),
|
||||||
|
}
|
||||||
|
for _, i := range f.Instructions {
|
||||||
|
newInstructions = append(newInstructions, rpInstruction(i, r))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Function{Instructions: newInstructions, Name: f.Name, StackOffset: r.currentOffset}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rpInstruction(i Instruction, r *replacePseudoPass) Instruction {
|
||||||
|
|
||||||
|
newInstruction := Instruction{Opcode: i.Opcode}
|
||||||
|
if i.Lhs != nil {
|
||||||
|
newInstruction.Lhs = pseudoToStack(i.Lhs, r)
|
||||||
|
}
|
||||||
|
if i.Rhs != nil {
|
||||||
|
newInstruction.Rhs = pseudoToStack(i.Rhs, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newInstruction
|
||||||
|
}
|
||||||
|
|
||||||
|
func pseudoToStack(op Operand, r *replacePseudoPass) Operand {
|
||||||
|
if pseudo, ok := op.(Pseudo); ok {
|
||||||
|
if offset, ok := r.identToOffset[string(pseudo)]; ok {
|
||||||
|
return Stack(offset)
|
||||||
|
} else {
|
||||||
|
r.currentOffset -= 4
|
||||||
|
r.identToOffset[string(pseudo)] = r.currentOffset
|
||||||
|
return Stack(r.currentOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return op
|
||||||
|
}
|
||||||
|
|
||||||
|
// Third pass, fixup invalid instructions
|
||||||
|
|
||||||
|
func instructionFixup(prog Program) Program {
|
||||||
|
newFuncs := make([]Function, 0)
|
||||||
|
|
||||||
|
for _, f := range prog.Functions {
|
||||||
|
newFuncs = append(newFuncs, fixupFunction(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Program{Functions: newFuncs}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixupFunction(f Function) Function {
|
||||||
|
// The function will at minimum require the same amount of instructions, but never less
|
||||||
|
newInstructions := make([]Instruction, 0)
|
||||||
|
|
||||||
|
for _, i := range f.Instructions {
|
||||||
|
newInstructions = append(newInstructions, fixupInstruction(i)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Function{Name: f.Name, Instructions: newInstructions, StackOffset: f.StackOffset}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixupInstruction(i Instruction) []Instruction {
|
||||||
|
|
||||||
|
switch i.Opcode {
|
||||||
|
case Mov:
|
||||||
|
if lhs, ok := i.Lhs.(Stack); ok {
|
||||||
|
if rhs, ok := i.Rhs.(Stack); ok {
|
||||||
|
return []Instruction{
|
||||||
|
{Opcode: Mov, Lhs: Register(R10), Rhs: rhs},
|
||||||
|
{Opcode: Mov, Lhs: lhs, Rhs: Register(R10)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Imul:
|
||||||
|
if lhs, ok := i.Lhs.(Stack); ok {
|
||||||
|
return []Instruction{
|
||||||
|
{Opcode: Mov, Lhs: Register(R11), Rhs: lhs},
|
||||||
|
{Opcode: Imul, Lhs: Register(R11), Rhs: i.Rhs},
|
||||||
|
{Opcode: Mov, Lhs: lhs, Rhs: Register(R11)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case Add, Sub, Idiv /* Imul (fallthrough) */ :
|
||||||
|
if lhs, ok := i.Lhs.(Stack); ok {
|
||||||
|
if rhs, ok := i.Rhs.(Stack); ok {
|
||||||
|
return []Instruction{
|
||||||
|
{Opcode: Mov, Lhs: Register(R10), Rhs: rhs},
|
||||||
|
{Opcode: i.Opcode, Lhs: lhs, Rhs: Register(R10)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if lhs, ok := i.Lhs.(Imm); ok && i.Opcode == Idiv {
|
||||||
|
return []Instruction{
|
||||||
|
{Opcode: Mov, Lhs: Register(R10), Rhs: lhs},
|
||||||
|
{Opcode: Idiv, Lhs: Register(R10)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Instruction{i}
|
||||||
|
}
|
||||||
|
19
cmd/cmd.go
19
cmd/cmd.go
@ -17,10 +17,19 @@ import (
|
|||||||
"robaertschi.xyz/robaertschi/tt/typechecker"
|
"robaertschi.xyz/robaertschi/tt/typechecker"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ToPrintFlags int
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrintAst ToPrintFlags = 1 << iota
|
||||||
|
PrintTAst
|
||||||
|
PrintIr
|
||||||
|
)
|
||||||
|
|
||||||
type Arguments struct {
|
type Arguments struct {
|
||||||
Output string
|
Output string
|
||||||
Input string
|
Input string
|
||||||
OnlyEmitAsm bool
|
OnlyEmitAsm bool
|
||||||
|
ToPrint ToPrintFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefix writer writes a prefix before each new line from another io.Writer
|
// Prefix writer writes a prefix before each new line from another io.Writer
|
||||||
@ -109,19 +118,27 @@ func Compile(args Arguments) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
program := p.ParseProgram()
|
program := p.ParseProgram()
|
||||||
|
|
||||||
if p.Errors() > 0 {
|
if p.Errors() > 0 {
|
||||||
fmt.Printf("Parser encountered 1 or more errors, quiting...\n")
|
fmt.Printf("Parser encountered 1 or more errors, quiting...\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if (args.ToPrint & PrintAst) != 0 {
|
||||||
|
fmt.Printf("AST:\n%s\n", program.String())
|
||||||
|
}
|
||||||
|
|
||||||
tprogram, err := typechecker.New().CheckProgram(program)
|
tprogram, err := typechecker.New().CheckProgram(program)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Typechecker failed with %e\n", err)
|
fmt.Printf("Typechecker failed with %e\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if (args.ToPrint & PrintTAst) != 0 {
|
||||||
|
fmt.Printf("TAST:\n%s\n", tprogram.String())
|
||||||
|
}
|
||||||
|
|
||||||
ir := ttir.EmitProgram(tprogram)
|
ir := ttir.EmitProgram(tprogram)
|
||||||
|
if (args.ToPrint & PrintIr) != 0 {
|
||||||
|
fmt.Printf("TTIR:\n%s\n", ir.String())
|
||||||
|
}
|
||||||
asm := amd64.CgProgram(ir)
|
asm := amd64.CgProgram(ir)
|
||||||
|
|
||||||
asmOutput := asm.Emit()
|
asmOutput := asm.Emit()
|
||||||
|
17
main.go
17
main.go
@ -20,6 +20,10 @@ func main() {
|
|||||||
flag.StringVar(&output, "o", "", "Output a executable named `file`")
|
flag.StringVar(&output, "o", "", "Output a executable named `file`")
|
||||||
flag.StringVar(&output, "output", "", "Output a executable named `file`")
|
flag.StringVar(&output, "output", "", "Output a executable named `file`")
|
||||||
onlyEmitAsm := flag.Bool("S", false, "Only emit the asembly file and exit")
|
onlyEmitAsm := flag.Bool("S", false, "Only emit the asembly file and exit")
|
||||||
|
|
||||||
|
printAst := flag.Bool("ast", false, "Print the AST out to stdout")
|
||||||
|
printTAst := flag.Bool("tast", false, "Print the typed AST out to stdout")
|
||||||
|
printIr := flag.Bool("ttir", false, "Print the TTIR out to stdout")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
input := flag.Arg(0)
|
input := flag.Arg(0)
|
||||||
@ -32,5 +36,16 @@ func main() {
|
|||||||
output = strings.TrimSuffix(input, filepath.Ext(input))
|
output = strings.TrimSuffix(input, filepath.Ext(input))
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Compile(cmd.Arguments{Output: output, Input: input, OnlyEmitAsm: *onlyEmitAsm})
|
var toPrint cmd.ToPrintFlags
|
||||||
|
if *printAst {
|
||||||
|
toPrint |= cmd.PrintAst
|
||||||
|
}
|
||||||
|
if *printTAst {
|
||||||
|
toPrint |= cmd.PrintTAst
|
||||||
|
}
|
||||||
|
if *printIr {
|
||||||
|
toPrint |= cmd.PrintIr
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Compile(cmd.Arguments{Output: output, Input: input, OnlyEmitAsm: *onlyEmitAsm, ToPrint: toPrint})
|
||||||
}
|
}
|
||||||
|
10
ttir/ttir.go
10
ttir/ttir.go
@ -11,6 +11,14 @@ type Program struct {
|
|||||||
Functions []Function
|
Functions []Function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Program) String() string {
|
||||||
|
var builder strings.Builder
|
||||||
|
for _, f := range p.Functions {
|
||||||
|
builder.WriteString(f.String())
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
type Function struct {
|
type Function struct {
|
||||||
Name string
|
Name string
|
||||||
Instructions []Instruction
|
Instructions []Instruction
|
||||||
@ -48,7 +56,7 @@ type Binary struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Binary) String() string {
|
func (b *Binary) String() string {
|
||||||
return fmt.Sprintf("%s = %s %s, %s", b.Dst, b.Operator, b.Lhs, b.Rhs)
|
return fmt.Sprintf("%s = %s %s, %s\n", b.Dst, b.Operator, b.Lhs, b.Rhs)
|
||||||
}
|
}
|
||||||
func (b *Binary) instruction() {}
|
func (b *Binary) instruction() {}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user