diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala new file mode 100644 index 0000000..7440791 --- /dev/null +++ b/src/main/wacc/backend/asmGenerator.scala @@ -0,0 +1,111 @@ +package wacc + +import scala.collection.mutable.LinkedHashMap +import scala.collection.mutable.ListBuffer + +object asmGenerator { + import microWacc._ + import assemblyIR._ + import wacc.types._ + + def generateAsm(microProg: Program): List[AsmLine] = { + given stack: LinkedHashMap[Ident, Int] = LinkedHashMap[Ident, Int]() + given strings: ListBuffer[String] = ListBuffer[String]() + val Program(funcs, main) = microProg + + val progAsm = + LabelDef("main") :: + main.flatMap(generateStmt) ++ + List(assemblyIR.Return()) ++ + generateFuncs() + + val strDirs = strings.toList.zipWithIndex.flatMap { case (str, i) => + List(Directive.Int(str.size), LabelDef(s".L.str$i:"), Directive.Asciz(str)) + } + + List(Directive.IntelSyntax, Directive.Global("main"), Directive.RoData) ++ + strDirs ++ + List(Directive.Text) ++ + progAsm + } + +//TODO + def generateFuncs()(using stack: LinkedHashMap[Ident, Int], strings: ListBuffer[String]): List[AsmLine] = { + List() + } + + def generateStmt(stmt: Stmt)(using stack: LinkedHashMap[Ident, Int], strings: ListBuffer[String]): List[AsmLine] = + stmt match { + case microWacc.Call(Builtin.Exit, code :: _) => + alignStack() ++ + evalExprIntoReg(code, Register(RegSize.R64, RegName.DI)) ++ + List(assemblyIR.Call(CLibFunc.Exit)) + + case microWacc.Call(Builtin.Println, expr :: _) => + alignStack() ++ + evalExprIntoReg(expr, Register(RegSize.R64, RegName.DI)) ++ + List( + assemblyIR.Call(CLibFunc.Puts), + Move(Register(RegSize.R64, RegName.DI), ImmediateVal(0)), + assemblyIR.Call(CLibFunc.Fflush)) ++ + restoreStack() + + case microWacc.Call(Builtin.ReadInt, expr :: _) => + List() + + case Assign(lhs, rhs) => + lhs match { + case ident: Ident => + stack += (ident -> stack.size) + evalExprIntoReg(rhs, Register(RegSize.R64, RegName.AX)) ++ + List(Push(Register(RegSize.R64, RegName.AX))) + case _ => List() + } + case _ => List() + } + + def evalExprIntoReg(expr: Expr, dest: Register) + (using stack: LinkedHashMap[Ident, Int], strings: ListBuffer[String]): List[AsmLine] = { + var src: Src = ImmediateVal(0) // Placeholder + (expr match { + case IntLiter(v) => + src = ImmediateVal(v) + List() + case ident: Ident => + List( + Move( + dest, + IndexAddress(Register(RegSize.R64, RegName.SP), (stack.size - stack(ident)) * 4) + ) + ) + case ArrayLiter(elems) => expr.ty match { + case KnownType.Char => + strings += elems.mkString + List( + Load(dest, IndexAddress(Register(RegSize.R64, RegName.IP),LabelArg(s".L.str${strings.size - 1}"))) + ) + case _ => List() + } + case _ => List() + }) ++ List(Move(dest, src)) + } + + def alignStack()(using stack: LinkedHashMap[Ident, Int]): List[AsmLine] = { + List( + And(Register(RegSize.R64, RegName.SP), ImmediateVal(-16)), + // Store stack pointer in rbp as it is callee saved + Push(Register(RegSize.R64, RegName.BP)), + Move(Register(RegSize.R64, RegName.BP), Register(RegSize.R64, RegName.SP)) + ) + } + + def restoreStack()(using stack: LinkedHashMap[Ident, Int]): List[AsmLine] = { + List( + Move(Register(RegSize.R64, RegName.SP), Register(RegSize.R64, RegName.BP)), + Pop(Register(RegSize.R64, RegName.BP)) + ) + } + + // def saveRegs(regList: List[Register]): List[AsmLine] = regList.map(Push(_)) + // def restoreRegs(regList: List[Register]): List[AsmLine] = regList.reverse.map(Pop(_)) +} diff --git a/src/main/wacc/backend/assemblyIR.scala b/src/main/wacc/backend/assemblyIR.scala index 323742b..e1b16ee 100644 --- a/src/main/wacc/backend/assemblyIR.scala +++ b/src/main/wacc/backend/assemblyIR.scala @@ -16,6 +16,29 @@ object assemblyIR { } } + enum RegName { + case AX, BX, CX, DX, SI, DI, SP, BP, IP, Reg8, Reg9, Reg10, Reg11, Reg12, Reg13, Reg14, Reg15 + override def toString = this match { + case AX => "ax" + case BX => "bx" + case CX => "cx" + case DX => "dx" + case SI => "si" + case DI => "di" + case SP => "sp" + case BP => "bp" + case IP => "ip" + case Reg8 => "8" + case Reg9 => "9" + case Reg10 => "10" + case Reg11 => "11" + case Reg12 => "12" + case Reg13 => "13" + case Reg14 => "14" + case Reg15 => "15" + } + } + // arguments enum CLibFunc extends Operand { case Scanf, @@ -35,13 +58,8 @@ object assemblyIR { } } - 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 Register(size: RegSize, name: RegName) extends Dest with Src { + override def toString = s"${size}${name}" } case class MemLocation(pointer: Long | Register) extends Dest with Src { override def toString = pointer match { @@ -49,6 +67,9 @@ object assemblyIR { case reg: Register => s"[$reg]" } } + case class IndexAddress(base: Register, offset: Int | LabelArg) extends Dest with Src { + override def toString = s"[$base + $offset]" + } case class ImmediateVal(value: Int) extends Src { override def toString = value.toString @@ -74,10 +95,10 @@ object assemblyIR { // 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 Call(op1: CLibFunc | LabelArg) 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 Load(op1: Register, op2: MemLocation | IndexAddress) extends Operation("lea ", op1, op2) case class Return() extends Operation("ret") @@ -108,4 +129,21 @@ object assemblyIR { case Always => "mp" } } + + enum Directive extends AsmLine { + case IntelSyntax, RoData, Text + case Global(name: String) + case Int(value: scala.Int) + case Asciz(string: String) + + override def toString(): String = this match { + case IntelSyntax => ".intel_syntax noprefix" + case Global(name) => s".globl $name" + case Text => ".text" + case RoData => ".section .rodata" + case Int(value) => s".int $value" + case Asciz(string) => s".asciz $string" + + } + } }