mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-16 05:53:30 +00:00
268 lines
6.2 KiB
Go
268 lines
6.2 KiB
Go
package amd64
|
|
|
|
import (
|
|
_ "embed"
|
|
"strings"
|
|
"testing"
|
|
|
|
"robaertschi.xyz/robaertschi/tt/ast"
|
|
"robaertschi.xyz/robaertschi/tt/ttir"
|
|
)
|
|
|
|
func expectProgram(t *testing.T, expected Program, actual Program) {
|
|
t.Helper()
|
|
if len(expected.Functions) != len(actual.Functions) {
|
|
t.Errorf("Expected %d functions but got %d", len(expected.Functions), len(actual.Functions))
|
|
return
|
|
}
|
|
|
|
for i := range expected.Functions {
|
|
expectFunction(t, expected.Functions[i], actual.Functions[i])
|
|
}
|
|
}
|
|
|
|
func expectFunction(t *testing.T, expected Function, actual Function) {
|
|
t.Helper()
|
|
if expected.Name != actual.Name {
|
|
t.Errorf("Expected function name %q but got %q", expected.Name, actual.Name)
|
|
}
|
|
|
|
if len(expected.Instructions) != len(actual.Instructions) {
|
|
t.Errorf("Expected %d instructions but got %d, expected: %v, actual: %v", len(expected.Instructions), len(actual.Instructions), expected.Instructions, actual.Instructions)
|
|
return
|
|
}
|
|
|
|
for i := range expected.Instructions {
|
|
expectInstruction(t, expected.Instructions[i], actual.Instructions[i])
|
|
}
|
|
}
|
|
|
|
func expectInstruction(t *testing.T, expected Instruction, actual Instruction) {
|
|
t.Helper()
|
|
|
|
switch expected := expected.(type) {
|
|
case *SimpleInstruction:
|
|
actual, ok := actual.(*SimpleInstruction)
|
|
if !ok {
|
|
t.Errorf("Expected SimpleInstruction but got %T", actual)
|
|
return
|
|
}
|
|
|
|
if expected.Opcode != actual.Opcode {
|
|
t.Errorf("Expected opcode %q but got %q", expected.Opcode, actual.Opcode)
|
|
}
|
|
|
|
switch expected.Opcode {
|
|
case Mov:
|
|
expectOperand(t, expected.Lhs, actual.Lhs)
|
|
expectOperand(t, expected.Rhs, actual.Rhs)
|
|
case Ret:
|
|
// nothing to do
|
|
}
|
|
case *SetCCInstruction:
|
|
actual, ok := actual.(*SetCCInstruction)
|
|
if !ok {
|
|
t.Errorf("Expected SetCCInstruction but got %T", actual)
|
|
return
|
|
}
|
|
|
|
if expected.Cond != actual.Cond {
|
|
t.Errorf("Expected condition %q but got %q", expected.Cond, actual.Cond)
|
|
}
|
|
|
|
expectOperand(t, expected.Dst, actual.Dst)
|
|
}
|
|
|
|
}
|
|
|
|
func expectOperand(t *testing.T, expected Operand, actual Operand) {
|
|
t.Helper()
|
|
|
|
switch expected := expected.(type) {
|
|
case Register:
|
|
actual, ok := actual.(Register)
|
|
if !ok {
|
|
t.Errorf("Expected Register but got %T", actual)
|
|
return
|
|
}
|
|
|
|
if expected != actual {
|
|
t.Errorf("Expected Register %q but got %q", expected, actual)
|
|
}
|
|
case Imm:
|
|
actual, ok := actual.(Imm)
|
|
if !ok {
|
|
t.Errorf("Expected Immediate but got %T", actual)
|
|
return
|
|
}
|
|
|
|
if expected != actual {
|
|
t.Errorf("Expected Immediate %q but got %q", expected, actual)
|
|
}
|
|
case Stack:
|
|
actual, ok := actual.(Stack)
|
|
|
|
if !ok {
|
|
t.Errorf("Expected Stack but got %T", actual)
|
|
}
|
|
|
|
if expected != actual {
|
|
t.Errorf("Expected Stack value %q but got %q", expected, actual)
|
|
}
|
|
case Pseudo:
|
|
actual, ok := actual.(Pseudo)
|
|
|
|
if !ok {
|
|
t.Errorf("Expected Stack but got %T", actual)
|
|
}
|
|
|
|
if expected != actual {
|
|
t.Errorf("Expected Stack value %q but got %q", expected, actual)
|
|
}
|
|
default:
|
|
t.Errorf("Unknown operand type %T", expected)
|
|
}
|
|
}
|
|
|
|
func trim(s string) string {
|
|
return strings.Trim(s, " \n\t")
|
|
}
|
|
|
|
func TestOperands(t *testing.T) {
|
|
var op Operand
|
|
|
|
op = AX
|
|
if str := op.OperandString(One); str != "al" {
|
|
t.Errorf("The register AX should be \"al\" but got %q", str)
|
|
}
|
|
|
|
op = Imm(3)
|
|
if str := op.OperandString(One); str != "3" {
|
|
t.Errorf("The immediate value 3 should be \"3\" but got %q", str)
|
|
}
|
|
}
|
|
|
|
//go:embed basic_test.txt
|
|
var basicTest string
|
|
|
|
func TestCodegen(t *testing.T) {
|
|
program := &ttir.Program{
|
|
Functions: []ttir.Function{
|
|
{
|
|
Name: "main",
|
|
HasReturnValue: true,
|
|
Instructions: []ttir.Instruction{
|
|
&ttir.Ret{Op: &ttir.Constant{Value: 0}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
expectedProgram := Program{
|
|
|
|
Functions: []Function{
|
|
{
|
|
Name: "main",
|
|
Instructions: []Instruction{
|
|
&SimpleInstruction{
|
|
Opcode: Mov,
|
|
Lhs: AX,
|
|
Rhs: Imm(0),
|
|
},
|
|
&SimpleInstruction{
|
|
Opcode: Ret,
|
|
},
|
|
},
|
|
HasReturnValue: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
actualProgram := CgProgram(program)
|
|
expectProgram(t, expectedProgram, *actualProgram)
|
|
|
|
actual := actualProgram.Emit()
|
|
expected := basicTest
|
|
if trim(actual) != trim(expected) {
|
|
t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", trim(expected), trim(actual))
|
|
}
|
|
}
|
|
|
|
//go:embed binary_test.txt
|
|
var binaryTest string
|
|
|
|
func TestBinary(t *testing.T) {
|
|
program := &ttir.Program{
|
|
Functions: []ttir.Function{
|
|
{
|
|
Name: "main",
|
|
Instructions: []ttir.Instruction{
|
|
&ttir.Binary{
|
|
Lhs: &ttir.Constant{Value: 3},
|
|
Rhs: &ttir.Constant{Value: 3},
|
|
Operator: ast.Add,
|
|
Dst: &ttir.Var{Value: "temp.1"},
|
|
},
|
|
&ttir.Ret{Op: &ttir.Var{Value: "temp.1"}},
|
|
},
|
|
HasReturnValue: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
actual := CgProgram(program).Emit()
|
|
if trim(actual) != trim(binaryTest) {
|
|
t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", trim(binaryTest), trim(actual))
|
|
}
|
|
}
|
|
|
|
//go:embed equality_test.txt
|
|
var equalityTest string
|
|
|
|
// There was once a bug with how the cmp instructions were generated, this check should fail if it happens again
|
|
func TestEqualityOperators(t *testing.T) {
|
|
program := ttir.Program{
|
|
Functions: []ttir.Function{
|
|
{
|
|
Name: "main",
|
|
HasReturnValue: false,
|
|
Instructions: []ttir.Instruction{
|
|
&ttir.Binary{
|
|
Lhs: &ttir.Constant{Value: 5},
|
|
Rhs: &ttir.Constant{Value: 4},
|
|
Dst: &ttir.Var{Value: "temp.1"},
|
|
Operator: ast.LessThanEqual,
|
|
},
|
|
&ttir.Binary{
|
|
Lhs: &ttir.Constant{Value: 5},
|
|
Rhs: &ttir.Constant{Value: 4},
|
|
Dst: &ttir.Var{Value: "temp.2"},
|
|
Operator: ast.LessThan,
|
|
},
|
|
&ttir.Binary{
|
|
Lhs: &ttir.Constant{Value: 5},
|
|
Rhs: &ttir.Constant{Value: 4},
|
|
Dst: &ttir.Var{Value: "temp.3"},
|
|
Operator: ast.GreaterThanEqual,
|
|
},
|
|
&ttir.Binary{
|
|
Lhs: &ttir.Constant{Value: 5},
|
|
Rhs: &ttir.Constant{Value: 4},
|
|
Dst: &ttir.Var{Value: "temp.4"},
|
|
Operator: ast.GreaterThan,
|
|
},
|
|
&ttir.Ret{},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
actual := CgProgram(&program).Emit()
|
|
actualTrimmed := trim(actual)
|
|
expectedTrimmed := trim(actualTrimmed)
|
|
|
|
if expectedTrimmed != actualTrimmed {
|
|
t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", expectedTrimmed, actualTrimmed)
|
|
}
|
|
}
|