Compare commits

..

No commits in common. "8137b45788ed6272c44dbec29dffe55a62fbfe4b" and "bcfa9fbde88af6b6fc7d49e09030d6f21ca57e8e" have entirely different histories.

9 changed files with 45 additions and 248 deletions

View File

@ -101,7 +101,6 @@ const (
// One operand // One operand
Idiv Opcode = "idiv" Idiv Opcode = "idiv"
Push Opcode = "push"
// No operands // No operands
Ret Opcode = "ret" Ret Opcode = "ret"
@ -160,12 +159,6 @@ func (j JmpInstruction) InstructionString() string {
return fmt.Sprintf("jmp %s", j) return fmt.Sprintf("jmp %s", j)
} }
type Call string
func (c Call) InstructionString() string {
return fmt.Sprintf("call %s", c)
}
type SetCCInstruction struct { type SetCCInstruction struct {
Cond CondCode Cond CondCode
Dst Operand Dst Operand
@ -175,18 +168,6 @@ func (si *SetCCInstruction) InstructionString() string {
return fmt.Sprintf("set%s %s", si.Cond, si.Dst.OperandString(One)) return fmt.Sprintf("set%s %s", si.Cond, si.Dst.OperandString(One))
} }
type AllocateStack uint
func (as AllocateStack) InstructionString() string {
return fmt.Sprintf("sub rsp, %d", as)
}
type DeallocateStack uint
func (ds DeallocateStack) InstructionString() string {
return fmt.Sprintf("add rsp, %d", ds)
}
type OperandSize int type OperandSize int
type Operand interface { type Operand interface {
@ -221,54 +202,6 @@ func (r Register) OperandString(size OperandSize) string {
return "eax" return "eax"
} }
return "rax" return "rax"
case CX:
switch size {
case One:
return "cl"
case Four:
return "ecx"
}
return "rcx"
case DX:
switch size {
case One:
return "dl"
case Four:
return "edx"
}
return "rdx"
case DI:
switch size {
case One:
return "dil"
case Four:
return "edi"
}
return "rdi"
case SI:
switch size {
case One:
return "sil"
case Four:
return "esi"
}
return "rsi"
case R8:
switch size {
case One:
return "r8b"
case Four:
return "r8d"
}
return "r8"
case R9:
switch size {
case One:
return "r9b"
case Four:
return "r9d"
}
return "r9"
case R10: case R10:
switch size { switch size {
case One: case One:
@ -285,9 +218,8 @@ func (r Register) OperandString(size OperandSize) string {
return "r11d" return "r11d"
} }
return "r11" return "r11"
default:
panic(fmt.Sprintf("unexpected amd64.Register: %#v", r))
} }
return "INVALID_REGISTER"
} }
type Imm int64 type Imm int64

View File

@ -2,7 +2,6 @@ package amd64
import ( import (
"fmt" "fmt"
"slices"
"robaertschi.xyz/robaertschi/tt/ast" "robaertschi.xyz/robaertschi/tt/ast"
"robaertschi.xyz/robaertschi/tt/ttir" "robaertschi.xyz/robaertschi/tt/ttir"
@ -123,67 +122,6 @@ func cgInstruction(i ttir.Instruction) []Instruction {
return []Instruction{JmpInstruction(i)} return []Instruction{JmpInstruction(i)}
case *ttir.Copy: case *ttir.Copy:
return []Instruction{&SimpleInstruction{Opcode: Mov, Lhs: toAsmOperand(i.Dst), Rhs: toAsmOperand(i.Src)}} return []Instruction{&SimpleInstruction{Opcode: Mov, Lhs: toAsmOperand(i.Dst), Rhs: toAsmOperand(i.Src)}}
case *ttir.Call:
registerArgs := i.Arguments[0:min(len(callConvArgs), len(i.Arguments))]
stackArgs := []ttir.Operand{}
if len(callConvArgs) < len(i.Arguments) {
stackArgs = i.Arguments[len(callConvArgs):len(i.Arguments)]
}
stackPadding := 0
if len(stackArgs)%2 != 0 {
stackPadding = 8
}
instructions := []Instruction{}
if stackPadding > 0 {
instructions = append(instructions, AllocateStack(stackPadding))
}
for i, arg := range registerArgs {
instructions = append(instructions,
&SimpleInstruction{
Opcode: Mov,
Rhs: toAsmOperand(arg),
Lhs: callConvArgs[i],
},
)
}
for _, arg := range slices.Backward(stackArgs) {
switch arg := toAsmOperand(arg).(type) {
case Imm:
instructions = append(instructions, &SimpleInstruction{Opcode: Push, Lhs: arg})
case Register:
instructions = append(instructions, &SimpleInstruction{Opcode: Push, Lhs: arg})
case Pseudo:
instructions = append(instructions,
&SimpleInstruction{Opcode: Mov, Rhs: arg, Lhs: AX},
&SimpleInstruction{Opcode: Push, Lhs: AX},
)
case Stack:
instructions = append(instructions,
&SimpleInstruction{Opcode: Mov, Rhs: arg, Lhs: AX},
&SimpleInstruction{Opcode: Push, Lhs: AX},
)
default:
panic(fmt.Sprintf("unexpected amd64.Operand: %#v", arg))
}
}
instructions = append(instructions, Call(i.FunctionName))
bytesToRemove := 8*len(stackArgs) + stackPadding
if bytesToRemove != 0 {
instructions = append(instructions, DeallocateStack(bytesToRemove))
}
if i.ReturnValue != nil {
asmDst := toAsmOperand(i.ReturnValue)
instructions = append(instructions, &SimpleInstruction{Opcode: Mov, Rhs: AX, Lhs: asmDst})
}
return instructions
default: default:
panic(fmt.Sprintf("unexpected ttir.Instruction: %#v", i)) panic(fmt.Sprintf("unexpected ttir.Instruction: %#v", i))
} }
@ -301,11 +239,11 @@ func rpInstruction(i Instruction, r *replacePseudoPass) Instruction {
Cond: i.Cond, Cond: i.Cond,
Dst: pseudoToStack(i.Dst, r), Dst: pseudoToStack(i.Dst, r),
} }
case *JumpCCInstruction, JmpInstruction, Label, AllocateStack, DeallocateStack, Call: case *JumpCCInstruction, JmpInstruction, Label:
return i return i
default:
panic(fmt.Sprintf("unexpected amd64.Instruction: %#v", i))
} }
panic("invalid instruction")
} }
func pseudoToStack(op Operand, r *replacePseudoPass) Operand { func pseudoToStack(op Operand, r *replacePseudoPass) Operand {
@ -409,9 +347,9 @@ func fixupInstruction(i Instruction) []Instruction {
case *SetCCInstruction: case *SetCCInstruction:
return []Instruction{i} return []Instruction{i}
case *JumpCCInstruction, JmpInstruction, Label, AllocateStack, DeallocateStack, Call: case *JumpCCInstruction, JmpInstruction, Label:
return []Instruction{i} return []Instruction{i}
default:
panic(fmt.Sprintf("unexpected amd64.Instruction: %#v", i))
} }
panic("invalid instruction")
} }

View File

@ -22,12 +22,7 @@ func extraLabel() string {
var Stub string var Stub string
func emitf(w io.Writer, format string, args ...any) error { func emitf(w io.Writer, format string, args ...any) error {
_, err := w.Write(fmt.Appendf(nil, format, args...)) _, err := w.Write([]byte(fmt.Sprintf(format, args...)))
return err
}
func emit(w io.Writer, content string) error {
_, err := w.Write(fmt.Append(nil, content))
return err return err
} }
@ -106,9 +101,14 @@ func emitInstruction(w io.Writer, i ttir.Instruction) error {
switch i := i.(type) { switch i := i.(type) {
case *ttir.Ret: case *ttir.Ret:
if op := i.Op; op != nil { if op := i.Op; op != nil {
return emitf(w, "\tret %s\n", emitOperand(i.Op)) if err := emitf(w, "\tret %s\n", emitOperand(i.Op)); err != nil {
return err
}
} else { } else {
return emitf(w, "\tret\n") if err := emitf(w, "\tret\n"); err != nil {
return err
}
} }
case *ttir.Binary: case *ttir.Binary:
var inst string var inst string
@ -134,36 +134,31 @@ func emitInstruction(w io.Writer, i ttir.Instruction) error {
case ast.LessThanEqual: case ast.LessThanEqual:
inst = "cslel" inst = "cslel"
} }
return emitf(w, "\t%s =l %s %s, %s\n", emitOperand(i.Dst), inst, emitOperand(i.Lhs), emitOperand(i.Rhs)) 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
}
case *ttir.Copy: case *ttir.Copy:
emitf(w, "\t%s =l copy %s\n", emitOperand(i.Dst), emitOperand(i.Src)) if err := emitf(w, "\t%s =l copy %s\n", emitOperand(i.Dst), emitOperand(i.Src)); err != nil {
return err
}
case ttir.Label: case ttir.Label:
return emitf(w, "@%s\n", string(i)) if err := emitf(w, "@%s\n", string(i)); err != nil {
return err
}
case ttir.Jump: case ttir.Jump:
return emitf(w, "\tjmp @%s\n", string(i)) if err := emitf(w, "\tjmp @%s\n", string(i)); err != nil {
return err
}
case *ttir.JumpIfNotZero: case *ttir.JumpIfNotZero:
after := extraLabel() after := extraLabel()
return emitf(w, "\tjnz %s, @%s, @%s\n@%s\n", emitOperand(i.Value), i.Label, after, after) if err := emitf(w, "\tjnz %s, @%s, @%s\n@%s\n", emitOperand(i.Value), i.Label, after, after); err != nil {
return err
}
case *ttir.JumpIfZero: case *ttir.JumpIfZero:
after := extraLabel() after := extraLabel()
return emitf(w, "\tjnz %s, @%s, @%s\n@%s\n", emitOperand(i.Value), after, i.Label, after) if err := emitf(w, "\tjnz %s, @%s, @%s\n@%s\n", emitOperand(i.Value), after, i.Label, after); err != nil {
case *ttir.Call: return err
b := strings.Builder{}
b.WriteRune('\t')
if i.ReturnValue != nil {
b.WriteString(emitOperand(i.ReturnValue) + " =l ")
} }
b.WriteString("call $" + i.FunctionName + "(")
for j, arg := range i.Arguments {
b.WriteString("l " + emitOperand(arg))
if j < (len(i.Arguments) - 1) {
b.WriteString(", ")
}
}
b.WriteString(")\n")
return emit(w, b.String())
default: default:
panic("unkown instruction") panic("unkown instruction")
} }

View File

@ -22,24 +22,3 @@ fn main() = {
}; };
``` ```
## ABI
ABI Considarations:
Each file is a module. Everything inside a module is prefixed with the module name. For Example:
```asm
# - In module1 folder
# mod module1;
# fn test(): i64 = ...
module1_test:
# - In module1/module2 folder
# mod module2;
# fn test(): i64 = ...
module1_module2_test:
```
Except the main module, all members of the main module are exposed with their concrete name.
If we ever add something like generics, that will have to be encoded in the function name too.

View File

@ -44,8 +44,8 @@
qbe qbe
( (
if system == "x86_64-linux" if system == "x86_64-linux"
then [fasm gdb] then [fasm]
else [lldb] else []
) )
]; ];
}; };

View File

@ -454,10 +454,6 @@ func (p *Parser) parseFunctionCall() ast.Expression {
p.nextToken() p.nextToken()
args = append(args, p.parseExpression(PrecLowest)) args = append(args, p.parseExpression(PrecLowest))
if !p.peekTokenIs(token.Comma) {
break
}
p.nextToken()
} }
// Move onto the ')' // Move onto the ')'

17
test.tt
View File

@ -1,12 +1,17 @@
fn main(): i64 = { fn main(): i64 = {
test2(3, 0) i := 5;
};
fn test2(until: i64, i: i64): i64 = {
if i >= until { if i == 5 {
0 0
} else { } else {
test2(until, i+1) + 1 1
} };
test2(3)
};
fn test2(hello: i64): i64 = {
hello // Comment test
}; };

View File

@ -148,24 +148,6 @@ func emitExpression(expr tast.Expression) (Operand, []Instruction) {
return nil, instructions return nil, instructions
case *tast.VariableReference: case *tast.VariableReference:
return &Var{Value: expr.Identifier}, []Instruction{} return &Var{Value: expr.Identifier}, []Instruction{}
case *tast.FunctionCall:
var dst Operand
if !expr.ReturnType.IsSameType(types.Unit) {
dst = &Var{Value: temp()}
}
args := []Operand{}
instructions := []Instruction{}
for _, arg := range expr.Arguments {
dst, argInstructions := emitExpression(arg)
instructions = append(instructions, argInstructions...)
args = append(args, dst)
}
instructions = append(instructions, &Call{FunctionName: expr.Identifier, Arguments: args, ReturnValue: dst})
return dst, instructions
default: default:
panic(fmt.Sprintf("unexpected tast.Expression: %#v", expr)) panic(fmt.Sprintf("unexpected tast.Expression: %#v", expr))
} }

View File

@ -116,36 +116,6 @@ func (l Label) String() string {
} }
func (l Label) instruction() {} func (l Label) instruction() {}
type Call struct {
FunctionName string
Arguments []Operand
// NOTE: Nullable
ReturnValue Operand
}
func (c *Call) String() string {
b := strings.Builder{}
if c.ReturnValue != nil {
b.WriteString(c.ReturnValue.String() + " = ")
}
b.WriteString("call " + c.FunctionName + " ")
for i, arg := range c.Arguments {
b.WriteString(arg.String())
if i < (len(c.Arguments) - 1) {
b.WriteString(", ")
}
}
b.WriteRune('\n')
return b.String()
}
func (c *Call) instruction() {}
type Operand interface { type Operand interface {
String() string String() string
operand() operand()