273 lines
8.0 KiB
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)
|
|
}
|
|
}
|