feat: almost complete clib calls

This commit is contained in:
Alex Ling 2025-02-22 21:38:12 +00:00
parent 7f2870e340
commit 24dddcadab
3 changed files with 198 additions and 55 deletions

View File

@ -15,12 +15,15 @@ object asmGenerator {
val progAsm = val progAsm =
LabelDef("main") :: LabelDef("main") ::
funcPrologue() ++
alignStack() ++
main.flatMap(generateStmt) ++ main.flatMap(generateStmt) ++
funcEpilogue() ++
List(assemblyIR.Return()) ++ List(assemblyIR.Return()) ++
generateFuncs() generateFuncs()
val strDirs = strings.toList.zipWithIndex.flatMap { case (str, i) => val strDirs = strings.toList.zipWithIndex.flatMap { case (str, i) =>
List(Directive.Int(str.size), LabelDef(s".L.str$i:"), Directive.Asciz(str)) List(Directive.Int(str.size), LabelDef(s".L.str$i"), Directive.Asciz(str))
} }
List(Directive.IntelSyntax, Directive.Global("main"), Directive.RoData) ++ List(Directive.IntelSyntax, Directive.Global("main"), Directive.RoData) ++
@ -42,31 +45,47 @@ object asmGenerator {
)(using stack: LinkedHashMap[Ident, Int], strings: ListBuffer[String]): List[AsmLine] = )(using stack: LinkedHashMap[Ident, Int], strings: ListBuffer[String]): List[AsmLine] =
stmt match { stmt match {
case microWacc.Call(Builtin.Exit, code :: _) => case microWacc.Call(Builtin.Exit, code :: _) =>
alignStack() ++ // alignStack() ++
evalExprIntoReg(code, Register(RegSize.R64, RegName.DI)) ++ evalExprIntoReg(code, Register(RegSize.R64, RegName.DI)) ++
List(assemblyIR.Call(CLibFunc.Exit)) List(assemblyIR.Call(CLibFunc.Exit))
case microWacc.Call(Builtin.Println, expr :: _) => case microWacc.Call(Builtin.Println, expr :: _) =>
alignStack() ++ // alignStack() ++
evalExprIntoReg(expr, Register(RegSize.R64, RegName.DI)) ++ printF(expr) ++
List( printLn()
assemblyIR.Call(CLibFunc.Puts),
Move(Register(RegSize.R64, RegName.DI), ImmediateVal(0)),
assemblyIR.Call(CLibFunc.Fflush)
) ++
restoreStack()
case microWacc.Call(Builtin.ReadInt, expr :: _) => case microWacc.Call(Builtin.Print, expr :: _) =>
List() // alignStack() ++
printF(expr)
case Assign(lhs, rhs) => case Assign(lhs, rhs) =>
lhs match { var dest: IndexAddress =
IndexAddress(Register(RegSize.R64, RegName.SP), 0) // gets overrwitten
(lhs match {
case ident: Ident => case ident: Ident =>
stack += (ident -> stack.size) if (!stack.contains(ident)) {
evalExprIntoReg(rhs, Register(RegSize.R64, RegName.AX)) ++ stack += (ident -> (stack.size + 1))
List(Push(Register(RegSize.R64, RegName.AX))) dest = accessVar(ident)
case _ => List() List(Subtract(Register(RegSize.R64, RegName.SP), ImmediateVal(16)))
} else {
dest = accessVar(ident)
List()
} }
// TODO lhs = arrayElem
case _ =>
// dest = ???
List()
}) ++
(rhs match {
case microWacc.Call(Builtin.ReadInt, _) =>
readIntoVar(dest, Builtin.ReadInt)
case microWacc.Call(Builtin.ReadChar, _) =>
readIntoVar(dest, Builtin.ReadChar)
case _ =>
evalExprIntoReg(rhs, Register(RegSize.R64, RegName.AX)) ++
List(Move(dest, Register(RegSize.R64, RegName.AX)))
})
// TODO other statements
case _ => List() case _ => List()
} }
@ -74,22 +93,20 @@ object asmGenerator {
stack: LinkedHashMap[Ident, Int], stack: LinkedHashMap[Ident, Int],
strings: ListBuffer[String] strings: ListBuffer[String]
): List[AsmLine] = { ): List[AsmLine] = {
var src: Src = ImmediateVal(0) // Placeholder expr match {
(expr match {
case IntLiter(v) => case IntLiter(v) =>
src = ImmediateVal(v) List(Move(dest, ImmediateVal(v)))
List() case CharLiter(v) =>
List(Move(dest, ImmediateVal(v.toInt)))
case ident: Ident => case ident: Ident =>
List( List(Move(dest, accessVar(ident)))
Move(
dest,
IndexAddress(Register(RegSize.R64, RegName.SP), (stack.size - stack(ident)) * 4)
)
)
case ArrayLiter(elems) => case ArrayLiter(elems) =>
expr.ty match { expr.ty match {
case KnownType.Char => case KnownType.String =>
strings += elems.mkString strings += elems.map {
case CharLiter(v) => v
case _ => ""
}.mkString
List( List(
Load( Load(
dest, dest,
@ -99,22 +116,59 @@ object asmGenerator {
) )
) )
) )
// TODO other array types
case _ => List() case _ => List()
} }
// TODO other expr types
case _ => List() case _ => List()
}) ++ List(Move(dest, src))
} }
}
// TODO make sure EOF doenst override the value in the stack
// probably need labels implemented for conditional jumps
def readIntoVar(dest: IndexAddress, readType: Builtin.ReadInt.type | Builtin.ReadChar.type)(using
stack: LinkedHashMap[Ident, Int],
strings: ListBuffer[String]
): List[AsmLine] = {
readType match {
case Builtin.ReadInt =>
strings += PrintFormat.Int.toString
case Builtin.ReadChar =>
strings += PrintFormat.Char.toString
}
List(
Load(
Register(RegSize.R64, RegName.DI),
IndexAddress(
Register(RegSize.R64, RegName.IP),
LabelArg(s".L.str${strings.size - 1}")
)
),
Load(Register(RegSize.R64, RegName.SI), dest)
) ++
// alignStack() ++
List(assemblyIR.Call(CLibFunc.Scanf))
}
def accessVar(ident: Ident)(using stack: LinkedHashMap[Ident, Int]): IndexAddress =
IndexAddress(Register(RegSize.R64, RegName.SP), (stack.size - stack(ident)) * 16)
def alignStack()(using stack: LinkedHashMap[Ident, Int]): List[AsmLine] = { def alignStack()(using stack: LinkedHashMap[Ident, Int]): List[AsmLine] = {
List( List(
And(Register(RegSize.R64, RegName.SP), ImmediateVal(-16)), And(Register(RegSize.R64, RegName.SP), ImmediateVal(-16))
// Store stack pointer in rbp as it is callee saved )
}
// Missing a sub instruction but dont think we need it
def funcPrologue(): List[AsmLine] = {
List(
Push(Register(RegSize.R64, RegName.BP)), Push(Register(RegSize.R64, RegName.BP)),
Move(Register(RegSize.R64, RegName.BP), Register(RegSize.R64, RegName.SP)) Move(Register(RegSize.R64, RegName.BP), Register(RegSize.R64, RegName.SP))
) )
} }
def restoreStack()(using stack: LinkedHashMap[Ident, Int]): List[AsmLine] = { def funcEpilogue(): List[AsmLine] = {
List( List(
Move(Register(RegSize.R64, RegName.SP), Register(RegSize.R64, RegName.BP)), Move(Register(RegSize.R64, RegName.SP), Register(RegSize.R64, RegName.BP)),
Pop(Register(RegSize.R64, RegName.BP)) Pop(Register(RegSize.R64, RegName.BP))
@ -123,4 +177,84 @@ object asmGenerator {
// def saveRegs(regList: List[Register]): List[AsmLine] = regList.map(Push(_)) // def saveRegs(regList: List[Register]): List[AsmLine] = regList.map(Push(_))
// def restoreRegs(regList: List[Register]): List[AsmLine] = regList.reverse.map(Pop(_)) // def restoreRegs(regList: List[Register]): List[AsmLine] = regList.reverse.map(Pop(_))
// TODO: refactor, really ugly function
def printF(expr: Expr)(using
stack: LinkedHashMap[Ident, Int],
strings: ListBuffer[String]
): List[AsmLine] = {
// determine the format string
expr.ty match {
case KnownType.String =>
strings += PrintFormat.String.toString
case KnownType.Char =>
strings += PrintFormat.Char.toString
case KnownType.Int =>
strings += PrintFormat.Int.toString
case _ =>
strings += PrintFormat.String.toString
}
List(
Load(
Register(RegSize.R64, RegName.DI),
IndexAddress(
Register(RegSize.R64, RegName.IP),
LabelArg(s".L.str${strings.size - 1}")
)
)
)
++
// determine the actual value to print
(if (expr.ty == KnownType.Bool) {
expr match {
case BoolLiter(true) => {
strings += "true"
}
case _ => {
strings += "false"
}
}
List(
Load(
Register(RegSize.R64, RegName.DI),
IndexAddress(
Register(RegSize.R64, RegName.IP),
LabelArg(s".L.str${strings.size - 1}")
)
)
)
} else {
evalExprIntoReg(expr, Register(RegSize.R64, RegName.SI))
})
// print the value
++
List(
assemblyIR.Call(CLibFunc.PrintF),
Move(Register(RegSize.R64, RegName.DI), ImmediateVal(0)),
assemblyIR.Call(CLibFunc.Fflush)
)
}
// prints a new line
def printLn()(using
stack: LinkedHashMap[Ident, Int],
strings: ListBuffer[String]
): List[AsmLine] = {
strings += ""
Load(
Register(RegSize.R64, RegName.DI),
IndexAddress(
Register(RegSize.R64, RegName.IP),
LabelArg(s".L.str${strings.size - 1}")
)
)
::
List(
assemblyIR.Call(CLibFunc.Puts),
Move(Register(RegSize.R64, RegName.DI), ImmediateVal(0)),
assemblyIR.Call(CLibFunc.Fflush)
)
}
} }

View File

@ -143,8 +143,17 @@ object assemblyIR {
case Text => ".text" case Text => ".text"
case RoData => ".section .rodata" case RoData => ".section .rodata"
case Int(value) => s".int $value" case Int(value) => s".int $value"
case Asciz(string) => s".asciz $string" case Asciz(string) => s".asciz \"$string\""
}
}
enum PrintFormat {
case Int, Char, String
override def toString(): String = this match {
case Int => "%d"
case Char => "%c"
case String => "%s"
} }
} }
} }

View File

@ -47,7 +47,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll with Paral
.drop(outputLineIdx + 1) .drop(outputLineIdx + 1)
.takeWhile(_.startsWith("#")) .takeWhile(_.startsWith("#"))
.map(_.stripPrefix("#").stripLeading) .map(_.stripPrefix("#").stripLeading)
.mkString("\n") .mkString("")
val exitLineIdx = contents.indexWhere(_.matches("^# ?[Ee]xit:.*$")) val exitLineIdx = contents.indexWhere(_.matches("^# ?[Ee]xit:.*$"))
val expectedExit = val expectedExit =
@ -79,24 +79,24 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll with Paral
Seq( Seq(
// format: off // format: off
// disable formatting to avoid binPack // disable formatting to avoid binPack
"^.*wacc-examples/valid/advanced.*$", // "^.*wacc-examples/valid/advanced.*$",
"^.*wacc-examples/valid/array.*$", // "^.*wacc-examples/valid/array.*$",
"^.*wacc-examples/valid/basic/exit.*$", // "^.*wacc-examples/valid/basic/exit.*$",
"^.*wacc-examples/valid/basic/skip.*$", // "^.*wacc-examples/valid/basic/skip.*$",
"^.*wacc-examples/valid/expressions.*$", // "^.*wacc-examples/valid/expressions.*$",
"^.*wacc-examples/valid/function/nested_functions.*$", // "^.*wacc-examples/valid/function/nested_functions.*$",
"^.*wacc-examples/valid/function/simple_functions.*$", // "^.*wacc-examples/valid/function/simple_functions.*$",
"^.*wacc-examples/valid/if.*$", // "^.*wacc-examples/valid/if.*$",
"^.*wacc-examples/valid/IO/print.*$", // "^.*wacc-examples/valid/IO/print.*$",
"^.*wacc-examples/valid/IO/read.*$", // "^.*wacc-examples/valid/IO/read.*$",
"^.*wacc-examples/valid/IO/IOLoop.wacc.*$", // "^.*wacc-examples/valid/IO/IOLoop.wacc.*$",
"^.*wacc-examples/valid/IO/IOSequence.wacc.*$", // "^.*wacc-examples/valid/IO/IOSequence.wacc.*$",
"^.*wacc-examples/valid/pairs.*$", // "^.*wacc-examples/valid/pairs.*$",
"^.*wacc-examples/valid/runtimeErr.*$", // "^.*wacc-examples/valid/runtimeErr.*$",
"^.*wacc-examples/valid/scope.*$", // "^.*wacc-examples/valid/scope.*$",
"^.*wacc-examples/valid/sequence.*$", // "^.*wacc-examples/valid/sequence.*$",
"^.*wacc-examples/valid/variables.*$", // "^.*wacc-examples/valid/variables.*$",
"^.*wacc-examples/valid/while.*$", // "^.*wacc-examples/valid/while.*$",
// format: on // format: on
).find(filename.matches).isDefined ).find(filename.matches).isDefined
} }