amd64: function call support WIP broken

This commit is contained in:
Robin Bärtschi 2025-03-12 22:28:59 +01:00
parent 9cfb80e790
commit 8137b45788
4 changed files with 160 additions and 9 deletions

View File

@ -101,6 +101,7 @@ const (
// One operand
Idiv Opcode = "idiv"
Push Opcode = "push"
// No operands
Ret Opcode = "ret"
@ -159,6 +160,12 @@ func (j JmpInstruction) InstructionString() string {
return fmt.Sprintf("jmp %s", j)
}
type Call string
func (c Call) InstructionString() string {
return fmt.Sprintf("call %s", c)
}
type SetCCInstruction struct {
Cond CondCode
Dst Operand
@ -168,6 +175,18 @@ func (si *SetCCInstruction) InstructionString() string {
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 Operand interface {
@ -202,6 +221,54 @@ func (r Register) OperandString(size OperandSize) string {
return "eax"
}
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:
switch size {
case One:
@ -218,8 +285,9 @@ func (r Register) OperandString(size OperandSize) string {
return "r11d"
}
return "r11"
default:
panic(fmt.Sprintf("unexpected amd64.Register: %#v", r))
}
return "INVALID_REGISTER"
}
type Imm int64

View File

@ -2,6 +2,7 @@ package amd64
import (
"fmt"
"slices"
"robaertschi.xyz/robaertschi/tt/ast"
"robaertschi.xyz/robaertschi/tt/ttir"
@ -122,6 +123,67 @@ func cgInstruction(i ttir.Instruction) []Instruction {
return []Instruction{JmpInstruction(i)}
case *ttir.Copy:
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:
panic(fmt.Sprintf("unexpected ttir.Instruction: %#v", i))
}
@ -239,11 +301,11 @@ func rpInstruction(i Instruction, r *replacePseudoPass) Instruction {
Cond: i.Cond,
Dst: pseudoToStack(i.Dst, r),
}
case *JumpCCInstruction, JmpInstruction, Label:
case *JumpCCInstruction, JmpInstruction, Label, AllocateStack, DeallocateStack, Call:
return i
default:
panic(fmt.Sprintf("unexpected amd64.Instruction: %#v", i))
}
panic("invalid instruction")
}
func pseudoToStack(op Operand, r *replacePseudoPass) Operand {
@ -347,9 +409,9 @@ func fixupInstruction(i Instruction) []Instruction {
case *SetCCInstruction:
return []Instruction{i}
case *JumpCCInstruction, JmpInstruction, Label:
case *JumpCCInstruction, JmpInstruction, Label, AllocateStack, DeallocateStack, Call:
return []Instruction{i}
default:
panic(fmt.Sprintf("unexpected amd64.Instruction: %#v", i))
}
panic("invalid instruction")
}

View File

@ -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.

View File

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