mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-19 23:33:30 +00:00
Compare commits
3 Commits
bcfa9fbde8
...
8137b45788
Author | SHA1 | Date | |
---|---|---|---|
8137b45788 | |||
9cfb80e790 | |||
b57ec5f5cf |
@ -101,6 +101,7 @@ const (
|
|||||||
|
|
||||||
// One operand
|
// One operand
|
||||||
Idiv Opcode = "idiv"
|
Idiv Opcode = "idiv"
|
||||||
|
Push Opcode = "push"
|
||||||
|
|
||||||
// No operands
|
// No operands
|
||||||
Ret Opcode = "ret"
|
Ret Opcode = "ret"
|
||||||
@ -159,6 +160,12 @@ 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
|
||||||
@ -168,6 +175,18 @@ 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 {
|
||||||
@ -202,6 +221,54 @@ 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:
|
||||||
@ -218,8 +285,9 @@ 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
|
||||||
|
@ -2,6 +2,7 @@ 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"
|
||||||
@ -122,6 +123,67 @@ 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))
|
||||||
}
|
}
|
||||||
@ -239,11 +301,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:
|
case *JumpCCInstruction, JmpInstruction, Label, AllocateStack, DeallocateStack, Call:
|
||||||
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 {
|
||||||
@ -347,9 +409,9 @@ func fixupInstruction(i Instruction) []Instruction {
|
|||||||
case *SetCCInstruction:
|
case *SetCCInstruction:
|
||||||
|
|
||||||
return []Instruction{i}
|
return []Instruction{i}
|
||||||
case *JumpCCInstruction, JmpInstruction, Label:
|
case *JumpCCInstruction, JmpInstruction, Label, AllocateStack, DeallocateStack, Call:
|
||||||
return []Instruction{i}
|
return []Instruction{i}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected amd64.Instruction: %#v", i))
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("invalid instruction")
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,12 @@ 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([]byte(fmt.Sprintf(format, args...)))
|
_, err := w.Write(fmt.Appendf(nil, format, args...))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func emit(w io.Writer, content string) error {
|
||||||
|
_, err := w.Write(fmt.Append(nil, content))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,14 +106,9 @@ 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 {
|
||||||
if err := emitf(w, "\tret %s\n", emitOperand(i.Op)); err != nil {
|
return emitf(w, "\tret %s\n", emitOperand(i.Op))
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if err := emitf(w, "\tret\n"); err != nil {
|
return emitf(w, "\tret\n")
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
case *ttir.Binary:
|
case *ttir.Binary:
|
||||||
var inst string
|
var inst string
|
||||||
@ -134,31 +134,36 @@ func emitInstruction(w io.Writer, i ttir.Instruction) error {
|
|||||||
case ast.LessThanEqual:
|
case ast.LessThanEqual:
|
||||||
inst = "cslel"
|
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 emitf(w, "\t%s =l %s %s, %s\n", emitOperand(i.Dst), inst, emitOperand(i.Lhs), emitOperand(i.Rhs))
|
||||||
return err
|
|
||||||
}
|
|
||||||
case *ttir.Copy:
|
case *ttir.Copy:
|
||||||
if err := emitf(w, "\t%s =l copy %s\n", emitOperand(i.Dst), emitOperand(i.Src)); err != nil {
|
emitf(w, "\t%s =l copy %s\n", emitOperand(i.Dst), emitOperand(i.Src))
|
||||||
return err
|
|
||||||
}
|
|
||||||
case ttir.Label:
|
case ttir.Label:
|
||||||
if err := emitf(w, "@%s\n", string(i)); err != nil {
|
return emitf(w, "@%s\n", string(i))
|
||||||
return err
|
|
||||||
}
|
|
||||||
case ttir.Jump:
|
case ttir.Jump:
|
||||||
if err := emitf(w, "\tjmp @%s\n", string(i)); err != nil {
|
return emitf(w, "\tjmp @%s\n", string(i))
|
||||||
return err
|
|
||||||
}
|
|
||||||
case *ttir.JumpIfNotZero:
|
case *ttir.JumpIfNotZero:
|
||||||
after := extraLabel()
|
after := extraLabel()
|
||||||
if err := emitf(w, "\tjnz %s, @%s, @%s\n@%s\n", emitOperand(i.Value), i.Label, after, after); err != nil {
|
return emitf(w, "\tjnz %s, @%s, @%s\n@%s\n", emitOperand(i.Value), i.Label, after, after)
|
||||||
return err
|
|
||||||
}
|
|
||||||
case *ttir.JumpIfZero:
|
case *ttir.JumpIfZero:
|
||||||
after := extraLabel()
|
after := extraLabel()
|
||||||
if err := emitf(w, "\tjnz %s, @%s, @%s\n@%s\n", emitOperand(i.Value), after, i.Label, after); err != nil {
|
return emitf(w, "\tjnz %s, @%s, @%s\n@%s\n", emitOperand(i.Value), after, i.Label, after)
|
||||||
return err
|
case *ttir.Call:
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
|
21
design.md
21
design.md
@ -22,3 +22,24 @@ 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.
|
||||||
|
@ -44,8 +44,8 @@
|
|||||||
qbe
|
qbe
|
||||||
(
|
(
|
||||||
if system == "x86_64-linux"
|
if system == "x86_64-linux"
|
||||||
then [fasm]
|
then [fasm gdb]
|
||||||
else []
|
else [lldb]
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
@ -454,6 +454,10 @@ 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
17
test.tt
@ -1,17 +1,12 @@
|
|||||||
fn main(): i64 = {
|
fn main(): i64 = {
|
||||||
i := 5;
|
test2(3, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
fn test2(until: i64, i: i64): i64 = {
|
||||||
if i == 5 {
|
if i >= until {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
1
|
test2(until, i+1) + 1
|
||||||
};
|
}
|
||||||
|
|
||||||
test2(3)
|
|
||||||
};
|
|
||||||
|
|
||||||
fn test2(hello: i64): i64 = {
|
|
||||||
hello // Comment test
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
18
ttir/emit.go
18
ttir/emit.go
@ -148,6 +148,24 @@ 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))
|
||||||
}
|
}
|
||||||
|
30
ttir/ttir.go
30
ttir/ttir.go
@ -116,6 +116,36 @@ 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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user