Files
WACC_37/src/main/wacc/backend/assemblyIR.scala

273 lines
8.0 KiB
Scala

package wacc
object assemblyIR {
sealed trait AsmLine
sealed trait Operand
sealed trait Src extends Operand // mem location, register and imm value
sealed trait Dest extends Operand // mem location and register
enum Size {
case Q64, D32, W16, B8
def toInt: Int = this match {
case Q64 => 8
case D32 => 4
case W16 => 2
case B8 => 1
}
private val ptr = "ptr "
override def toString(): String = this match {
case Q64 => "qword " + ptr
case D32 => "dword " + ptr
case W16 => "word " + ptr
case B8 => "byte " + ptr
}
}
enum RegName {
case AX, BX, CX, DX, SI, DI, SP, BP, IP, R8, R9, R10, R11, R12, R13, R14, R15
}
case class Register(size: Size, name: RegName) extends Dest with Src {
import RegName._
if (size == Size.B8 && name == RegName.IP) {
throw new IllegalArgumentException("Cannot have 8 bit register for IP")
}
override def toString = name match {
case AX => tradToString("ax", "al")
case BX => tradToString("bx", "bl")
case CX => tradToString("cx", "cl")
case DX => tradToString("dx", "dl")
case SI => tradToString("si", "sil")
case DI => tradToString("di", "dil")
case SP => tradToString("sp", "spl")
case BP => tradToString("bp", "bpl")
case IP => tradToString("ip", "#INVALID")
case R8 => newToString(8)
case R9 => newToString(9)
case R10 => newToString(10)
case R11 => newToString(11)
case R12 => newToString(12)
case R13 => newToString(13)
case R14 => newToString(14)
case R15 => newToString(15)
}
private def tradToString(base: String, byteName: String): String =
size match {
case Size.Q64 => "r" + base
case Size.D32 => "e" + base
case Size.W16 => base
case Size.B8 => byteName
}
private def newToString(base: Int): String = {
val b = base.toString
"r" + (size match {
case Size.Q64 => b
case Size.D32 => b + "d"
case Size.W16 => b + "w"
case Size.B8 => b + "b"
})
}
}
// arguments
enum CLibFunc extends Operand {
case Scanf,
Fflush,
Exit,
PrintF,
Malloc,
Free
private val plt = "@plt"
override def toString = this match {
case Scanf => "scanf" + plt
case Fflush => "fflush" + plt
case Exit => "exit" + plt
case PrintF => "printf" + plt
case Malloc => "malloc" + plt
case Free => "free" + plt
}
}
case class MemLocation(
base: Register,
offset: Int | LabelArg = 0,
// scale 0 will make register irrelevant, no other reason as to why it's RAX
scaledIndex: (Register, Int) = (Register(Size.Q64, RegName.AX), 0),
opSize: Option[Size] = None
) extends Dest
with Src {
def copy(
base: Register = this.base,
offset: Int | LabelArg = this.offset,
scaledIndex: (Register, Int) = this.scaledIndex,
opSize: Option[Size] = this.opSize
): MemLocation = MemLocation(base, offset, scaledIndex, opSize)
override def toString(): String = {
val opSizeStr = opSize.map(_.toString).getOrElse("")
val baseStr = base.toString
val offsetStr = offset match {
case 0 => ""
case off => s" + $off"
}
val scaledIndexStr = scaledIndex match {
case (reg, scale) if scale != 0 => s" + $reg * $scale"
case _ => ""
}
s"$opSizeStr[$baseStr$scaledIndexStr$offsetStr]"
}
}
case class ImmediateVal(value: Int) extends Src {
override def toString = value.toString
}
case class LabelArg(name: String) extends Operand {
override def toString = name
}
abstract class Operation(ins: String, ops: Operand*) extends AsmLine {
override def toString: String = s"\t$ins ${ops.mkString(", ")}"
}
// arithmetic operations
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("imul", ops*)
case class Divide(op1: Src) extends Operation("idiv", op1)
case class Negate(op: Dest) extends Operation("neg", op)
// bitwise operations
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 Xor(op1: Dest, op2: Src) extends Operation("xor", op1, op2)
case class Compare(op1: Dest, op2: Src) extends Operation("cmp", op1, op2)
case class CDQ() extends Operation("cdq")
// stack operations
case class Push(op1: Src) extends Operation("push", op1)
case class Pop(op1: Src) extends Operation("pop", op1)
// move operations
case class Move(op1: Dest, op2: Src) extends Operation("mov", op1, op2)
case class Load(op1: Register, op2: MemLocation) extends Operation("lea ", op1, op2)
// function call operations
case class Call(op1: CLibFunc | LabelArg) extends Operation("call", op1)
case class Return() extends Operation("ret")
// conditional operations
case class Jump(op1: LabelArg, condition: Cond = Cond.Always)
extends Operation(s"j${condition.toString}", op1)
case class Set(op1: Dest, condition: Cond = Cond.Always)
extends Operation(s"set${condition.toString}", op1)
case class LabelDef(name: String) extends AsmLine {
override def toString = s"$name:"
}
case class Comment(comment: String) extends AsmLine {
override def toString =
comment.split("\n").map(line => s"# ${line}").mkString("\n")
}
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"
}
}
enum Directive extends AsmLine {
case IntelSyntax, RoData, Text, EndFunc
case Global(name: String)
case Int(value: scala.Int)
case Asciz(string: String)
case File(no: scala.Int, file: String)
case Location(fileNo: scala.Int, lineNo: scala.Int, colNo: Option[scala.Int])
case Func(name: String, label: LabelDef)
case Type(label: LabelDef, symbolType: SymbolType)
case Size(label: LabelDef, expr: SizeExpr)
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"\t.int $value"
case Asciz(string) => s"\t.asciz \"$string\""
case File(no, file) => s".file $no \"${file}\""
case Location(fileNo, lineNo, colNo) =>
s"\t.loc $fileNo $lineNo" + colNo.map(c => s" $c").getOrElse("")
case Func(name, label) =>
s".func $name, ${label.name}"
case EndFunc => ".endfunc"
case Type(label, symbolType) =>
s".type ${label.name}, @${symbolType.toString}"
case Directive.Size(label, expr) =>
s".size ${label.name}, ${expr.toString}"
}
}
enum SymbolType {
case Function
override def toString(): String = this match {
case Function => "function"
}
}
enum SizeExpr {
case Relative(label: LabelDef)
override def toString(): String = this match {
case Relative(label) => s".-${label.name}"
}
}
enum PrintFormat {
case Int, Char, String
override def toString(): String = this match {
case Int => "%d"
case Char => "%c"
case String => "%s"
}
}
object commonRegisters {
import Size._
import RegName._
val RAX = Register(Q64, AX)
val EAX = Register(D32, AX)
val RDI = Register(Q64, DI)
val RIP = Register(Q64, IP)
val RBP = Register(Q64, BP)
val RSI = Register(Q64, SI)
val RDX = Register(Q64, DX)
val RCX = Register(Q64, CX)
val ECX = Register(D32, CX)
}
}