From 52ed404a734d33be9e9730e52c4d1c241460c7f4 Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Wed, 26 Feb 2025 21:12:50 +0000 Subject: [PATCH] feat: almost implemented arrays --- src/main/wacc/backend/asmGenerator.scala | 69 +++++++++++++++++++----- src/main/wacc/backend/assemblyIR.scala | 19 +++++-- src/main/wacc/frontend/microWacc.scala | 1 + src/main/wacc/frontend/typeChecker.scala | 1 + src/test/wacc/examples.scala | 4 +- 5 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 54fc7db..688e474 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -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( 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) @@ -211,19 +224,25 @@ object asmGenerator { var chain = Chain.empty[AsmLine] stmt match { case Assign(lhs, rhs) => - var dest: () => IndexAddress = () => IndexAddress(RAX, 0) // overwritten below lhs match { case ident: Ident => - dest = stack.accessVar(ident) + val dest = stack.accessVar(ident) if (!stack.contains(ident)) chain += stack.reserve(ident) - // TODO lhs = arrayElem - case _ => - } - chain ++= evalExprOntoStack(rhs) - chain += stack.pop(RAX) - chain += Move(dest(), RAX) + chain ++= evalExprOntoStack(rhs) + chain += stack.pop(RDX) + 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) => val elseLabel = labelGenerator.getLabel() @@ -290,19 +309,43 @@ object asmGenerator { strings += elems.collect { case CharLiter(v) => v }.mkString chain += Load(RAX, IndexAddress(RIP, LabelArg(s".L.str${strings.size - 1}"))) 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(false) => chain += Xor(RAX, RAX) chain += stack.push(RAX) - case NullLiter() => chain += stack.push(ImmediateVal(0)) - case ArrayElem(_, _) => // TODO: Implement handling + case NullLiter() => chain += stack.push(ImmediateVal(0)) + 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) => chain ++= evalExprOntoStack(x) 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.Not => chain += Xor(stack.head(SizeDir.DWord), ImmediateVal(1)) diff --git a/src/main/wacc/backend/assemblyIR.scala b/src/main/wacc/backend/assemblyIR.scala index 1ff8906..f8bbf38 100644 --- a/src/main/wacc/backend/assemblyIR.scala +++ b/src/main/wacc/backend/assemblyIR.scala @@ -48,7 +48,9 @@ object assemblyIR { case Scanf, Fflush, Exit, - PrintF + PrintF, + Malloc, + Free private val plt = "@plt" @@ -57,6 +59,8 @@ object assemblyIR { case Fflush => "fflush" + plt case Exit => "exit" + 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]" } } + + // TODO to string is wacky case class IndexAddress( base: Register, offset: Int | LabelArg, - opSize: SizeDir = SizeDir.Unspecified + indexReg: Register = Register(RegSize.R64, RegName.AX), + scale: Int = 0 ) extends Dest 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 { @@ -185,7 +196,7 @@ object assemblyIR { override def toString(): String = this match { case Byte => "byte " + ptr - case Word => "word " + ptr // TODO check word/doubleword/quadword + case Word => "word " + ptr case DWord => "dword " + ptr case Unspecified => "" } diff --git a/src/main/wacc/frontend/microWacc.scala b/src/main/wacc/frontend/microWacc.scala index 099fcc3..e2c1bdc 100644 --- a/src/main/wacc/frontend/microWacc.scala +++ b/src/main/wacc/frontend/microWacc.scala @@ -74,6 +74,7 @@ object microWacc { object Exit extends Builtin("exit")(?) object Free extends Builtin("free")(?) object Malloc extends Builtin("malloc")(?) + object PrintCharArray extends Builtin("printCharArray")(?) } case class Assign(lhs: LValue, rhs: Expr) extends Stmt diff --git a/src/main/wacc/frontend/typeChecker.scala b/src/main/wacc/frontend/typeChecker.scala index f571e11..2c430e5 100644 --- a/src/main/wacc/frontend/typeChecker.scala +++ b/src/main/wacc/frontend/typeChecker.scala @@ -218,6 +218,7 @@ object typeChecker { val exprTyped = checkValue(expr, Constraint.Unconstrained) val exprFormat = exprTyped.ty match { case KnownType.Bool | KnownType.String => "%s" + case KnownType.Array(KnownType.Char) => "%.*s" case KnownType.Char => "%c" case KnownType.Int => "%d" case KnownType.Pair(_, _) | KnownType.Array(_) | ? => "%p" diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala index 8ac0aa4..87def2a 100644 --- a/src/test/wacc/examples.scala +++ b/src/test/wacc/examples.scala @@ -73,7 +73,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { ) 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 // disable formatting to avoid binPack "^.*wacc-examples/valid/advanced.*$", - "^.*wacc-examples/valid/array.*$", + // "^.*wacc-examples/valid/array.*$", // "^.*wacc-examples/valid/basic/exit.*$", // "^.*wacc-examples/valid/basic/skip.*$", // "^.*wacc-examples/valid/expressions.*$",