mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-15 21:43:30 +00:00
253 lines
4.3 KiB
Go
253 lines
4.3 KiB
Go
package amd64
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
var callConvArgs map[int]Register = map[int]Register{
|
|
1: DI,
|
|
2: SI,
|
|
3: DX,
|
|
4: CX,
|
|
5: R8,
|
|
6: R9,
|
|
}
|
|
|
|
type Program struct {
|
|
Functions []Function
|
|
MainFunction *Function
|
|
}
|
|
|
|
func (p *Program) executableAsmHeader() string {
|
|
|
|
if p.MainFunction.HasReturnValue {
|
|
return executableAsmHeader
|
|
}
|
|
return executableAsmHeaderNoReturnValue
|
|
}
|
|
|
|
// This calls the main function and uses it's return value to exit
|
|
const executableAsmHeader = "format ELF64 executable\n" +
|
|
"segment readable executable\n" +
|
|
"entry _start\n" +
|
|
"_start:\n" +
|
|
" call main\n" +
|
|
" mov rdi, rax\n" +
|
|
" mov rax, 60\n" +
|
|
" syscall\n"
|
|
|
|
const executableAsmHeaderNoReturnValue = "format ELF64 executable\n" +
|
|
"segment readable executable\n" +
|
|
"entry _start\n" +
|
|
"_start:\n" +
|
|
" call main\n" +
|
|
" mov rdi, 0\n" +
|
|
" mov rax, 60\n" +
|
|
" syscall\n"
|
|
|
|
func (p *Program) Emit() string {
|
|
var builder strings.Builder
|
|
builder.WriteString(p.executableAsmHeader())
|
|
|
|
for _, function := range p.Functions {
|
|
builder.WriteString(function.Emit())
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
return builder.String()
|
|
}
|
|
|
|
type Function struct {
|
|
StackOffset int64
|
|
Name string
|
|
HasReturnValue bool
|
|
Instructions []Instruction
|
|
}
|
|
|
|
func (f *Function) Emit() string {
|
|
var builder strings.Builder
|
|
|
|
builder.WriteString(fmt.Sprintf("%s:\n push rbp\n mov rbp, rsp\n add rsp, %d\n", f.Name, f.StackOffset))
|
|
|
|
for _, inst := range f.Instructions {
|
|
builder.WriteString(fmt.Sprintf(" %s\n", inst.InstructionString()))
|
|
}
|
|
|
|
return builder.String()
|
|
}
|
|
|
|
type CondCode string
|
|
|
|
const (
|
|
Equal CondCode = "e"
|
|
NotEqual CondCode = "ne"
|
|
Greater CondCode = "g"
|
|
GreaterEqual CondCode = "ge"
|
|
Less CondCode = "l"
|
|
LessEqual CondCode = "le"
|
|
)
|
|
|
|
type Opcode string
|
|
|
|
const (
|
|
|
|
// Two operands
|
|
Mov Opcode = "mov" // Lhs: dst, Rhs: src, or better said intel syntax
|
|
Add Opcode = "add"
|
|
Sub Opcode = "sub"
|
|
Imul Opcode = "imul"
|
|
Cmp Opcode = "cmp"
|
|
|
|
// One operand
|
|
Idiv Opcode = "idiv"
|
|
|
|
// No operands
|
|
Ret Opcode = "ret"
|
|
Cdq Opcode = "cdq"
|
|
)
|
|
|
|
type Instruction interface {
|
|
InstructionString() string
|
|
}
|
|
|
|
type SimpleInstruction struct {
|
|
Opcode Opcode
|
|
// Dst
|
|
Lhs Operand
|
|
// Src
|
|
Rhs Operand
|
|
}
|
|
|
|
func (i *SimpleInstruction) InstructionString() string {
|
|
if i.Opcode == Ret {
|
|
return fmt.Sprintf("mov rsp, rbp\n pop rbp\n ret\n")
|
|
}
|
|
|
|
// No operands
|
|
if i.Lhs == nil {
|
|
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))
|
|
}
|
|
|
|
type Label string
|
|
|
|
func (l Label) InstructionString() string {
|
|
return fmt.Sprintf("%s:", l)
|
|
}
|
|
|
|
type JumpCCInstruction struct {
|
|
Cond CondCode
|
|
Dst string
|
|
}
|
|
|
|
func (j *JumpCCInstruction) InstructionString() string {
|
|
return fmt.Sprintf("j%s %s", j.Cond, j.Dst)
|
|
}
|
|
|
|
type JmpInstruction string
|
|
|
|
func (j JmpInstruction) InstructionString() string {
|
|
return fmt.Sprintf("jmp %s", j)
|
|
}
|
|
|
|
type SetCCInstruction struct {
|
|
Cond CondCode
|
|
Dst Operand
|
|
}
|
|
|
|
func (si *SetCCInstruction) InstructionString() string {
|
|
return fmt.Sprintf("set%s %s", si.Cond, si.Dst.OperandString(One))
|
|
}
|
|
|
|
type OperandSize int
|
|
|
|
type Operand interface {
|
|
OperandString(OperandSize) string
|
|
}
|
|
|
|
type Register int
|
|
|
|
const (
|
|
AX Register = iota
|
|
CX
|
|
DX
|
|
DI
|
|
SI
|
|
R8
|
|
R9
|
|
R10
|
|
R11
|
|
|
|
One OperandSize = iota
|
|
Four
|
|
Eight
|
|
)
|
|
|
|
func (r Register) OperandString(size OperandSize) string {
|
|
switch r {
|
|
case AX:
|
|
switch size {
|
|
case One:
|
|
return "al"
|
|
case Four:
|
|
return "eax"
|
|
}
|
|
return "rax"
|
|
case R10:
|
|
switch size {
|
|
case One:
|
|
return "r10b"
|
|
case Four:
|
|
return "r10d"
|
|
}
|
|
return "r10"
|
|
case R11:
|
|
switch size {
|
|
case One:
|
|
return "r11b"
|
|
case Four:
|
|
return "r11d"
|
|
}
|
|
return "r11"
|
|
}
|
|
return "INVALID_REGISTER"
|
|
}
|
|
|
|
type Imm int64
|
|
|
|
func (i Imm) OperandString(size OperandSize) string {
|
|
return fmt.Sprintf("%d", i)
|
|
}
|
|
|
|
type Stack int64
|
|
|
|
func (s Stack) OperandString(size OperandSize) string {
|
|
|
|
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")
|
|
}
|