feat: almost implemented arrays

This commit is contained in:
Alex Ling 2025-02-26 21:12:50 +00:00 committed by Gleb Koval
parent 18534a64a6
commit 52ed404a73
Signed by: cyclane
GPG Key ID: 15E168A8B332382C
5 changed files with 75 additions and 19 deletions

View File

@ -148,9 +148,22 @@ object asmGenerator {
) )
) )
chain ++= wrapBuiltinFunc(
labelGenerator.getLabel(Builtin.PrintCharArray),
Chain(
stack.align(),
Load(RDX, IndexAddress(RSI, 8)),
Move(RSI, MemLocation(RSI)),
assemblyIR.Call(CLibFunc.PrintF),
Xor(RDI, RDI),
assemblyIR.Call(CLibFunc.Fflush)
)
)
chain ++= wrapBuiltinFunc( chain ++= wrapBuiltinFunc(
labelGenerator.getLabel(Builtin.Malloc), labelGenerator.getLabel(Builtin.Malloc),
Chain.one(stack.align()) Chain(stack.align(), assemblyIR.Call(CLibFunc.Malloc))
// Out of memory check is optional
) )
chain ++= wrapBuiltinFunc(labelGenerator.getLabel(Builtin.Free), Chain.empty) chain ++= wrapBuiltinFunc(labelGenerator.getLabel(Builtin.Free), Chain.empty)
@ -211,19 +224,25 @@ object asmGenerator {
var chain = Chain.empty[AsmLine] var chain = Chain.empty[AsmLine]
stmt match { stmt match {
case Assign(lhs, rhs) => case Assign(lhs, rhs) =>
var dest: () => IndexAddress = () => IndexAddress(RAX, 0) // overwritten below
lhs match { lhs match {
case ident: Ident => case ident: Ident =>
dest = stack.accessVar(ident) val dest = stack.accessVar(ident)
if (!stack.contains(ident)) chain += stack.reserve(ident) if (!stack.contains(ident)) chain += stack.reserve(ident)
// TODO lhs = arrayElem
case _ =>
}
chain ++= evalExprOntoStack(rhs) chain ++= evalExprOntoStack(rhs)
chain += stack.pop(RAX) chain += stack.pop(RDX)
chain += Move(dest(), RAX) chain += Move(dest(), RDX)
case ArrayElem(x, i) =>
chain ++= evalExprOntoStack(x)
chain ++= evalExprOntoStack(i)
chain ++= evalExprOntoStack(rhs)
chain += stack.pop(RAX)
chain += stack.pop(RCX)
chain += stack.pop(RDX)
chain += Move(IndexAddress(RDX, 8, RCX, 8), RAX)
}
case If(cond, thenBranch, elseBranch) => case If(cond, thenBranch, elseBranch) =>
val elseLabel = labelGenerator.getLabel() val elseLabel = labelGenerator.getLabel()
@ -290,19 +309,43 @@ object asmGenerator {
strings += elems.collect { case CharLiter(v) => v }.mkString strings += elems.collect { case CharLiter(v) => v }.mkString
chain += Load(RAX, IndexAddress(RIP, LabelArg(s".L.str${strings.size - 1}"))) chain += Load(RAX, IndexAddress(RIP, LabelArg(s".L.str${strings.size - 1}")))
chain += stack.push(RAX) chain += stack.push(RAX)
case _ => // Other array types TODO case _ =>
chain ++= generateCall(
microWacc.Call(Builtin.Malloc, List(IntLiter((elems.size + 1) * 8))),
isTail = false
)
chain += stack.push(RAX)
// Store the length of the array at the start
chain += Move(MemLocation(RAX, SizeDir.DWord), ImmediateVal(elems.size))
elems.zipWithIndex.foldMap { (elem, i) =>
chain ++= evalExprOntoStack(elem)
chain += stack.pop(RCX)
chain += stack.pop(RAX)
chain += Move(IndexAddress(RAX, 8 * (i + 1)), RCX)
chain += stack.push(RAX)
}
} }
case BoolLiter(true) => chain += stack.push(ImmediateVal(1)) case BoolLiter(true) => chain += stack.push(ImmediateVal(1))
case BoolLiter(false) => case BoolLiter(false) =>
chain += Xor(RAX, RAX) chain += Xor(RAX, RAX)
chain += stack.push(RAX) chain += stack.push(RAX)
case NullLiter() => chain += stack.push(ImmediateVal(0)) case NullLiter() => chain += stack.push(ImmediateVal(0))
case ArrayElem(_, _) => // TODO: Implement handling case ArrayElem(x, i) =>
chain ++= evalExprOntoStack(x)
chain ++= evalExprOntoStack(i)
chain += stack.pop(RCX)
chain += stack.pop(RAX)
// + 1 because we store the length of the array at the start
chain += stack.push(IndexAddress(RAX, 8, RCX, 8))
case UnaryOp(x, op) => case UnaryOp(x, op) =>
chain ++= evalExprOntoStack(x) chain ++= evalExprOntoStack(x)
op match { op match {
case UnaryOperator.Chr | UnaryOperator.Ord | UnaryOperator.Len => // No op needed case UnaryOperator.Chr | UnaryOperator.Ord => // No op needed
case UnaryOperator.Len =>
// Access the elem
chain += stack.pop(RAX)
chain += Push(MemLocation(RAX))
case UnaryOperator.Negate => chain += Negate(stack.head(SizeDir.DWord)) case UnaryOperator.Negate => chain += Negate(stack.head(SizeDir.DWord))
case UnaryOperator.Not => case UnaryOperator.Not =>
chain += Xor(stack.head(SizeDir.DWord), ImmediateVal(1)) chain += Xor(stack.head(SizeDir.DWord), ImmediateVal(1))

View File

@ -48,7 +48,9 @@ object assemblyIR {
case Scanf, case Scanf,
Fflush, Fflush,
Exit, Exit,
PrintF PrintF,
Malloc,
Free
private val plt = "@plt" private val plt = "@plt"
@ -57,6 +59,8 @@ object assemblyIR {
case Fflush => "fflush" + plt case Fflush => "fflush" + plt
case Exit => "exit" + plt case Exit => "exit" + plt
case PrintF => "printf" + plt case PrintF => "printf" + plt
case Malloc => "malloc" + plt
case Free => "free" + plt
} }
} }
@ -72,13 +76,20 @@ object assemblyIR {
case reg: Register => opSize.toString + s"[$reg]" case reg: Register => opSize.toString + s"[$reg]"
} }
} }
// TODO to string is wacky
case class IndexAddress( case class IndexAddress(
base: Register, base: Register,
offset: Int | LabelArg, offset: Int | LabelArg,
opSize: SizeDir = SizeDir.Unspecified indexReg: Register = Register(RegSize.R64, RegName.AX),
scale: Int = 0
) extends Dest ) extends Dest
with Src { with Src {
override def toString = s"$opSize[$base + $offset]" override def toString = if (scale != 0) {
s"[$base + $indexReg * $scale + $offset]"
} else {
s"[$base + $offset]"
}
} }
case class ImmediateVal(value: Int) extends Src { case class ImmediateVal(value: Int) extends Src {
@ -185,7 +196,7 @@ object assemblyIR {
override def toString(): String = this match { override def toString(): String = this match {
case Byte => "byte " + ptr case Byte => "byte " + ptr
case Word => "word " + ptr // TODO check word/doubleword/quadword case Word => "word " + ptr
case DWord => "dword " + ptr case DWord => "dword " + ptr
case Unspecified => "" case Unspecified => ""
} }

View File

@ -74,6 +74,7 @@ object microWacc {
object Exit extends Builtin("exit")(?) object Exit extends Builtin("exit")(?)
object Free extends Builtin("free")(?) object Free extends Builtin("free")(?)
object Malloc extends Builtin("malloc")(?) object Malloc extends Builtin("malloc")(?)
object PrintCharArray extends Builtin("printCharArray")(?)
} }
case class Assign(lhs: LValue, rhs: Expr) extends Stmt case class Assign(lhs: LValue, rhs: Expr) extends Stmt

View File

@ -218,6 +218,7 @@ object typeChecker {
val exprTyped = checkValue(expr, Constraint.Unconstrained) val exprTyped = checkValue(expr, Constraint.Unconstrained)
val exprFormat = exprTyped.ty match { val exprFormat = exprTyped.ty match {
case KnownType.Bool | KnownType.String => "%s" case KnownType.Bool | KnownType.String => "%s"
case KnownType.Array(KnownType.Char) => "%.*s"
case KnownType.Char => "%c" case KnownType.Char => "%c"
case KnownType.Int => "%d" case KnownType.Int => "%d"
case KnownType.Pair(_, _) | KnownType.Array(_) | ? => "%p" case KnownType.Pair(_, _) | KnownType.Array(_) | ? => "%p"

View File

@ -73,7 +73,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll {
) )
assert(process.exitValue == expectedExit) assert(process.exitValue == expectedExit)
assert(stdout.toString == expectedOutput) assert(stdout.toString.replaceAll("0x[0-9a-f]+", "#addrs#") == expectedOutput)
} }
} }
@ -86,7 +86,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll {
// 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.*$",