diff --git a/.gitignore b/.gitignore index 64ecf6a..03801cc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .scala-build/ .vscode/ wacc-examples/ +.idea/ + diff --git a/src/main/wacc/backend/assemblyIR.scala b/src/main/wacc/backend/assemblyIR.scala new file mode 100644 index 0000000..0d167c2 --- /dev/null +++ b/src/main/wacc/backend/assemblyIR.scala @@ -0,0 +1,110 @@ +package wacc + +object assemblyIR { + + sealed trait Operand + sealed trait Src extends Operand // mem location, register and imm value + sealed trait Dest extends Operand // mem location and register + enum RegSize { + case R64 + case E32 + + override def toString = this match { + case R64 => "r" + case E32 => "e" + } + } + + // arguments + enum CLibFunc extends Operand { + case Scanf, + Puts, + Fflush, + Exit, + PrintF + + private val plt = "@plt" + + override def toString = this match { + case Scanf => "scanf" + plt + case Puts => "puts" + plt + case Fflush => "fflush" + plt + case Exit => "exit" + plt + case PrintF => "printf" + plt + } + } + + enum Register extends Dest with Src { + case Named(name: String, size: RegSize) + case Scratch(num: Int, size: RegSize) + override def toString = this match { + case Named(name, size) => s"${size}${name.toLowerCase()}" + case Scratch(num, size) => s"r${num}${if (size == RegSize.E32) "d" else ""}" + } + } + case class MemLocation(pointer: Long | Register) extends Dest with Src { + override def toString = pointer match { + case hex: Long => f"[0x$hex%X]" + case reg: Register => s"[$reg]" + } + } + + case class ImmediateVal(value: Int) extends Src { + override def toString = value.toString + } + + case class LabelArg(name: String) extends Operand { + override def toString = name + } + + // TODO Check if dest and src are not both memory locations + abstract class Operation(ins: String, ops: Operand*) { + override def toString: String = s"\t$ins ${ops.mkString(", ")}" + } + case class Add(op1: Dest, op2: Src) extends Operation("add", op1, op2) + case class Subtract(op1: Dest, op2: Src) extends Operation("sub", op1, op2) + case class Multiply(ops: Operand*) extends Operation("mul", ops*) + case class Divide(op1: Src) extends Operation("div", op1) + + case class And(op1: Dest, op2: Src) extends Operation("and", op1, op2) + case class Or(op1: Dest, op2: Src) extends Operation("or", op1, op2) + case class Compare(op1: Dest, op2: Src) extends Operation("cmp", op1, op2) + + // stack operations + case class Push(op1: Src) extends Operation("push", op1) + case class Pop(op1: Src) extends Operation("pop", op1) + case class Call(op1: CLibFunc) extends Operation("call", op1) + + case class Move(op1: Dest, op2: Src) extends Operation("mov", op1, op2) + case class Load(op1: Register, op2: MemLocation) extends Operation("lea ", op1, op2) + + case class Return() extends Operation("ret") + + case class Jump(op1: LabelArg, condition: Cond = Cond.Always) + extends Operation(s"j${condition.toString}", op1) + + case class LabelDef(name: String) { + override def toString = s"$name:" + } + + enum Cond { + case Equal, + NotEqual, + Greater, + GreaterEqual, + Less, + LessEqual, + Overflow, + Always + override def toString(): String = this match { + case Equal => "e" + case NotEqual => "ne" + case Greater => "g" + case GreaterEqual => "ge" + case Less => "l" + case LessEqual => "le" + case Overflow => "o" + case Always => "mp" + } + } +} diff --git a/src/main/wacc/Frontend/Error.scala b/src/main/wacc/frontend/Error.scala similarity index 100% rename from src/main/wacc/Frontend/Error.scala rename to src/main/wacc/frontend/Error.scala diff --git a/src/main/wacc/Frontend/ast.scala b/src/main/wacc/frontend/ast.scala similarity index 100% rename from src/main/wacc/Frontend/ast.scala rename to src/main/wacc/frontend/ast.scala diff --git a/src/main/wacc/Frontend/lexer.scala b/src/main/wacc/frontend/lexer.scala similarity index 100% rename from src/main/wacc/Frontend/lexer.scala rename to src/main/wacc/frontend/lexer.scala diff --git a/src/main/wacc/Frontend/microWacc.scala b/src/main/wacc/frontend/microWacc.scala similarity index 100% rename from src/main/wacc/Frontend/microWacc.scala rename to src/main/wacc/frontend/microWacc.scala diff --git a/src/main/wacc/Frontend/parser.scala b/src/main/wacc/frontend/parser.scala similarity index 100% rename from src/main/wacc/Frontend/parser.scala rename to src/main/wacc/frontend/parser.scala diff --git a/src/main/wacc/Frontend/renamer.scala b/src/main/wacc/frontend/renamer.scala similarity index 100% rename from src/main/wacc/Frontend/renamer.scala rename to src/main/wacc/frontend/renamer.scala diff --git a/src/main/wacc/Frontend/typeChecker.scala b/src/main/wacc/frontend/typeChecker.scala similarity index 100% rename from src/main/wacc/Frontend/typeChecker.scala rename to src/main/wacc/frontend/typeChecker.scala diff --git a/src/main/wacc/Frontend/types.scala b/src/main/wacc/frontend/types.scala similarity index 100% rename from src/main/wacc/Frontend/types.scala rename to src/main/wacc/frontend/types.scala diff --git a/src/test/wacc/instructionSpec.scala b/src/test/wacc/instructionSpec.scala new file mode 100644 index 0000000..6d427a5 --- /dev/null +++ b/src/test/wacc/instructionSpec.scala @@ -0,0 +1,65 @@ +import org.scalatest.funsuite.AnyFunSuite +import wacc.assemblyIR._ + +class instructionSpec extends AnyFunSuite { + + val named64BitRegister = Register.Named("ax", RegSize.R64) + + test("named 64-bit register toString") { + assert(named64BitRegister.toString == "rax") + } + + val named32BitRegister = Register.Named("ax", RegSize.E32) + + test("named 32-bit register toString") { + assert(named32BitRegister.toString == "eax") + } + + val scratch64BitRegister = Register.Scratch(1, RegSize.R64) + + test("scratch 64-bit register toString") { + assert(scratch64BitRegister.toString == "r1") + } + + val scratch32BitRegister = Register.Scratch(1, RegSize.E32) + + test("scratch 32-bit register toString") { + assert(scratch32BitRegister.toString == "r1d") + } + + val memLocationWithHex = MemLocation(0x12345678) + + test("mem location with hex toString") { + assert(memLocationWithHex.toString == "[0x12345678]") + } + + val memLocationWithRegister = MemLocation(named64BitRegister) + + test("mem location with register toString") { + assert(memLocationWithRegister.toString == "[rax]") + } + + val immediateVal = ImmediateVal(123) + + test("immediate value toString") { + assert(immediateVal.toString == "123") + } + + val addInstruction = Add(named64BitRegister, immediateVal) + + test("x86: add instruction toString") { + assert(addInstruction.toString == "\tadd rax, 123") + } + + val subInstruction = Subtract(scratch64BitRegister, named64BitRegister) + + test("x86: sub instruction toString") { + assert(subInstruction.toString == "\tsub r1, rax") + } + + val callInstruction = Call(CLibFunc.Scanf) + + test("x86: call instruction toString") { + assert(callInstruction.toString == "\tcall scanf@plt") + } +}