feat: x86 code generation implementation without runtime checking #29

Merged
gk1623 merged 58 commits from asm-gen into master 2025-02-27 18:54:57 +00:00
7 changed files with 601 additions and 221 deletions
Showing only changes of commit f30cf42c4b - Show all commits

View File

@@ -19,7 +19,7 @@ object asmGenerator {
val RBP = Register(RegSize.R64, RegName.BP)
val RSI = Register(RegSize.R64, RegName.SI)
val _8_BIT_MASK = 0xFF
val _8_BIT_MASK = 0xff
object labelGenerator {
var labelVal = -1
@@ -27,8 +27,8 @@ object asmGenerator {
labelVal += 1
s".L$labelVal"
}
def getLabel(target: CallTarget): String = target match{
case Ident(v,_) => s"wacc_$v"
def getLabel(target: CallTarget): String = target match {
case Ident(v, _) => s"wacc_$v"
case Builtin(name) => s"_$name"
}
}
@@ -61,33 +61,31 @@ object asmGenerator {
strings: ListBuffer[String]
): List[AsmLine] = {
LabelDef(labelName) ::
funcPrologue() ++
funcPrologue() ++
funcBody ++
funcEpilogue()
funcEpilogue()
}
def generateBuiltInFuncs()(using
stack: LinkedHashMap[Ident, Int],
strings: ListBuffer[String]
): List[AsmLine] = {
wrapFunc(labelGenerator.getLabel(Builtin.Exit),
wrapFunc(
labelGenerator.getLabel(Builtin.Exit),
alignStack() ++
List(Pop(RDI),
assemblyIR.Call(CLibFunc.Exit))
List(Pop(RDI), assemblyIR.Call(CLibFunc.Exit))
) ++
wrapFunc(labelGenerator.getLabel(Builtin.Printf),
alignStack() ++
List(assemblyIR.Call(CLibFunc.PrintF),
Move(RDI, ImmediateVal(0)),
assemblyIR.Call(CLibFunc.Fflush)
)
)++
wrapFunc(labelGenerator.getLabel(Builtin.Malloc),
List()
)++
wrapFunc(labelGenerator.getLabel(Builtin.Free),
List()
)
wrapFunc(
labelGenerator.getLabel(Builtin.Printf),
alignStack() ++
List(
assemblyIR.Call(CLibFunc.PrintF),
Move(RDI, ImmediateVal(0)),
assemblyIR.Call(CLibFunc.Fflush)
)
) ++
wrapFunc(labelGenerator.getLabel(Builtin.Malloc), List()) ++
wrapFunc(labelGenerator.getLabel(Builtin.Free), List())
}
def generateStmt(
@@ -95,7 +93,7 @@ object asmGenerator {
)(using stack: LinkedHashMap[Ident, Int], strings: ListBuffer[String]): List[AsmLine] =
stmt match {
case microWacc.Call(Builtin.Exit, code :: _) =>
List()
List()
case Assign(lhs, rhs) =>
var dest: IndexAddress =
IndexAddress(RSP, 0) // gets overrwitten
@@ -188,97 +186,100 @@ object asmGenerator {
// TODO other array types
case _ => List()
}
case BoolLiter(v) => List(Push(ImmediateVal(if (v) 1 else 0)))
case NullLiter() => List(Push(ImmediateVal(0)))
case BoolLiter(v) => List(Push(ImmediateVal(if (v) 1 else 0)))
case NullLiter() => List(Push(ImmediateVal(0)))
case ArrayElem(value, indices) => List()
case UnaryOp(x, op) => op match {
// TODO: chr and ord are TYPE CASTS. They do not change the internal value,
// but will need bound checking e.t.c.
case UnaryOperator.Chr => List()
case UnaryOperator.Ord => List()
case UnaryOperator.Len => List()
case UnaryOperator.Negate => List(
Negate(MemLocation(RSP,SizeDir.Word))
)
case UnaryOperator.Not =>
evalExprOntoStack(x) ++
List(
Xor(MemLocation(RSP, SizeDir.Word), ImmediateVal(1))
)
}
case BinaryOp(x, y, op) => op match {
case BinaryOperator.Add =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Add(MemLocation(RSP, SizeDir.Word), EAX)
// TODO OVERFLOWING
)
case BinaryOperator.Sub =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Subtract(MemLocation(RSP, SizeDir.Word), EAX)
// TODO OVERFLOWING
)
case BinaryOperator.Mul =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Multiply(MemLocation(RSP, SizeDir.Word), EAX)
// TODO OVERFLOWING
)
case BinaryOperator.Div =>
evalExprOntoStack(y) ++
evalExprOntoStack(x) ++
List(
Pop(EAX),
Divide(MemLocation(RSP, SizeDir.Word)),
Add(RSP, ImmediateVal(8)),
Push(EAX)
// TODO CHECK DIVISOR IS NOT 0
)
case BinaryOperator.Mod =>
evalExprOntoStack(y) ++
evalExprOntoStack(x) ++
List(
Pop(EAX),
Divide(MemLocation(RSP, SizeDir.Word)),
Add(RSP, ImmediateVal(8)),
Push(EDX)
// TODO CHECK DIVISOR IS NOT 0
case UnaryOp(x, op) =>
op match {
// TODO: chr and ord are TYPE CASTS. They do not change the internal value,
// but will need bound checking e.t.c.
case UnaryOperator.Chr => List()
case UnaryOperator.Ord => List()
case UnaryOperator.Len => List()
case UnaryOperator.Negate =>
List(
Negate(MemLocation(RSP, SizeDir.Word))
)
case BinaryOperator.Eq =>
generateComparison(x, y, Cond.Equal)
case BinaryOperator.Neq =>
generateComparison(x, y, Cond.NotEqual)
case BinaryOperator.Greater =>
generateComparison(x, y, Cond.Greater)
case BinaryOperator.GreaterEq =>
generateComparison(x, y, Cond.GreaterEqual)
case BinaryOperator.Less =>
generateComparison(x, y, Cond.Less)
case BinaryOperator.LessEq =>
generateComparison(x, y, Cond.LessEqual)
case BinaryOperator.And =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
And(MemLocation(RSP, SizeDir.Word), EAX),
)
case BinaryOperator.Or =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Or(MemLocation(RSP, SizeDir.Word), EAX),
)
}
case UnaryOperator.Not =>
evalExprOntoStack(x) ++
List(
Xor(MemLocation(RSP, SizeDir.Word), ImmediateVal(1))
)
}
case BinaryOp(x, y, op) =>
op match {
case BinaryOperator.Add =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Add(MemLocation(RSP, SizeDir.Word), EAX)
// TODO OVERFLOWING
)
case BinaryOperator.Sub =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Subtract(MemLocation(RSP, SizeDir.Word), EAX)
// TODO OVERFLOWING
)
case BinaryOperator.Mul =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Multiply(MemLocation(RSP, SizeDir.Word), EAX)
// TODO OVERFLOWING
)
case BinaryOperator.Div =>
evalExprOntoStack(y) ++
evalExprOntoStack(x) ++
List(
Pop(EAX),
Divide(MemLocation(RSP, SizeDir.Word)),
Add(RSP, ImmediateVal(8)),
Push(EAX)
// TODO CHECK DIVISOR IS NOT 0
)
case BinaryOperator.Mod =>
evalExprOntoStack(y) ++
evalExprOntoStack(x) ++
List(
Pop(EAX),
Divide(MemLocation(RSP, SizeDir.Word)),
Add(RSP, ImmediateVal(8)),
Push(EDX)
// TODO CHECK DIVISOR IS NOT 0
)
case BinaryOperator.Eq =>
generateComparison(x, y, Cond.Equal)
case BinaryOperator.Neq =>
generateComparison(x, y, Cond.NotEqual)
case BinaryOperator.Greater =>
generateComparison(x, y, Cond.Greater)
case BinaryOperator.GreaterEq =>
generateComparison(x, y, Cond.GreaterEqual)
case BinaryOperator.Less =>
generateComparison(x, y, Cond.Less)
case BinaryOperator.LessEq =>
generateComparison(x, y, Cond.LessEqual)
case BinaryOperator.And =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
And(MemLocation(RSP, SizeDir.Word), EAX)
)
case BinaryOperator.Or =>
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Or(MemLocation(RSP, SizeDir.Word), EAX)
)
}
case microWacc.Call(target, args) => List()
}
}
@@ -308,19 +309,19 @@ object asmGenerator {
// }
def generateComparison(x : Expr, y: Expr, cond: Cond)(using
def generateComparison(x: Expr, y: Expr, cond: Cond)(using
stack: LinkedHashMap[Ident, Int],
strings: ListBuffer[String]
): List[AsmLine] = {
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Compare(MemLocation(RSP, SizeDir.Word), EAX),
Set(Register(RegSize.Byte, RegName.AL), cond),
And(EAX, ImmediateVal(_8_BIT_MASK)),
Push(EAX)
)
evalExprOntoStack(x) ++
evalExprOntoStack(y) ++
List(
Pop(EAX),
Compare(MemLocation(RSP, SizeDir.Word), EAX),
Set(Register(RegSize.Byte, RegName.AL), cond),
And(EAX, ImmediateVal(_8_BIT_MASK)),
Push(EAX)
)
}
def accessVar(ident: Ident)(using stack: LinkedHashMap[Ident, Int]): IndexAddress =
IndexAddress(RSP, (stack.size - stack(ident)) * 8)
@@ -397,8 +398,8 @@ object asmGenerator {
)
} else {
evalExprOntoStack(expr) ++
List(Pop(RSI))
evalExprOntoStack(expr) ++
List(Pop(RSI))
})
// print the value
++

View File

@@ -12,14 +12,15 @@ object assemblyIR {
case Byte
override def toString = this match {
case R64 => "r"
case E32 => "e"
case R64 => "r"
case E32 => "e"
case Byte => ""
}
}
enum RegName {
case AX, AL, BX, CX, DX, SI, DI, SP, BP, IP, Reg8, Reg9, Reg10, Reg11, Reg12, Reg13, Reg14, Reg15
case AX, AL, 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 AL => "al"
@@ -59,17 +60,24 @@ object assemblyIR {
}
}
//TODO register naming conventions are wrong
// TODO register naming conventions are wrong
case class Register(size: RegSize, name: RegName) extends Dest with Src {
override def toString = s"${size}${name}"
}
case class MemLocation(pointer: Long | Register, opSize: SizeDir = SizeDir.Unspecified) extends Dest with Src {
case class MemLocation(pointer: Long | Register, opSize: SizeDir = SizeDir.Unspecified)
extends Dest
with Src {
override def toString = pointer match {
case hex: Long => opSize.toString + f"[0x$hex%X]"
case reg: Register => opSize.toString + s"[$reg]"
}
}
case class IndexAddress(base: Register, offset: Int | LabelArg, opSize: SizeDir = SizeDir.Unspecified) extends Dest with Src {
case class IndexAddress(
base: Register,
offset: Int | LabelArg,
opSize: SizeDir = SizeDir.Unspecified
) extends Dest
with Src {
override def toString = s"$opSize[$base + $offset]"
}
@@ -111,7 +119,7 @@ object assemblyIR {
extends Operation(s"j${condition.toString}", op1)
case class Set(op1: Dest, condition: Cond = Cond.Always)
extends Operation(s"set${condition.toString}", op1)
extends Operation(s"set${condition.toString}", op1)
case class LabelDef(name: String) extends AsmLine {
override def toString = s"$name:"
@@ -170,8 +178,8 @@ object assemblyIR {
private val ptr = "ptr "
override def toString(): String = this match {
case Byte => "byte " + ptr
case Word => "word " + ptr
case Byte => "byte " + ptr
case Word => "word " + ptr
case Unspecified => ""
}
}

View File

@@ -180,7 +180,7 @@ object typeChecker {
microWacc.Builtin.Read,
List(
destTy match {
case KnownType.Int => "%d".toMicroWaccCharArray
case KnownType.Int => "%d".toMicroWaccCharArray
case KnownType.Char | _ => "%c".toMicroWaccCharArray
},
destTyped
@@ -218,9 +218,9 @@ object typeChecker {
val exprTyped = checkValue(expr, Constraint.Unconstrained)
val format = exprTyped.ty match {
case KnownType.Bool | KnownType.String => "%s"
case KnownType.Char => "%c"
case KnownType.Int => "%d"
case _ => "%p"
case KnownType.Char => "%c"
case KnownType.Int => "%d"
case _ => "%p"
}
List(
microWacc.Call(