From 332c00b15b3de87e4a9efb059c0ba72939d5b044 Mon Sep 17 00:00:00 2001 From: Jonny Date: Thu, 27 Feb 2025 19:13:17 +0000 Subject: [PATCH 01/15] feat: added runtime errors class and object --- src/main/wacc/backend/RuntimeErrors.scala | 16 ++++++++++++++++ src/main/wacc/backend/asmGenerator.scala | 20 ++------------------ 2 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 src/main/wacc/backend/RuntimeErrors.scala diff --git a/src/main/wacc/backend/RuntimeErrors.scala b/src/main/wacc/backend/RuntimeErrors.scala new file mode 100644 index 0000000..f457337 --- /dev/null +++ b/src/main/wacc/backend/RuntimeErrors.scala @@ -0,0 +1,16 @@ +package wacc + +import cats.data.Chain +import wacc.assemblyIR._ + +case class RuntimeError(strLabel: String, errStr: String, errLabel: String) { + def stringDef: Chain[AsmLine] = Chain( + Directive.Int(errStr.size), + LabelDef(strLabel), + Directive.Asciz(errStr) + ) +} + +object RuntimeErrors { + val zeroDivError = RuntimeError(".L._errDivZero_str0", "fatal error: division or modulo by zero", ".L._errDivZero") +} diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 60e0b47..9e2772c 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -3,6 +3,8 @@ package wacc import scala.collection.mutable.ListBuffer import cats.data.Chain import cats.syntax.foldable._ +import wacc.RuntimeErrors._ + object asmGenerator { import microWacc._ @@ -13,24 +15,6 @@ object asmGenerator { import sizeExtensions._ import lexer.escapedChars - abstract case class Error() { - def strLabel: String - def errStr: String - def errLabel: String - - def stringDef: Chain[AsmLine] = Chain( - Directive.Int(errStr.size), - LabelDef(strLabel), - Directive.Asciz(errStr) - ) - } - object zeroDivError extends Error { - // TODO: is this bad? Can we make an error case class/some other structure? - def strLabel = ".L._errDivZero_str0" - def errStr = "fatal error: division or modulo by zero" - def errLabel = ".L._errDivZero" - } - private val RAX = Register(Q64, AX) private val EAX = Register(D32, AX) private val RDI = Register(Q64, DI) From ea262e9a56c926bb6917bebdebb7d80cac3b61ba Mon Sep 17 00:00:00 2001 From: Guy C Date: Thu, 27 Feb 2025 19:34:55 +0000 Subject: [PATCH 02/15] feat: implements divByZero and badChr runtime errors --- src/main/wacc/backend/RuntimeErrors.scala | 16 ++++++++- src/main/wacc/backend/asmGenerator.scala | 42 +++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/main/wacc/backend/RuntimeErrors.scala b/src/main/wacc/backend/RuntimeErrors.scala index f457337..d5df01c 100644 --- a/src/main/wacc/backend/RuntimeErrors.scala +++ b/src/main/wacc/backend/RuntimeErrors.scala @@ -12,5 +12,19 @@ case class RuntimeError(strLabel: String, errStr: String, errLabel: String) { } object RuntimeErrors { - val zeroDivError = RuntimeError(".L._errDivZero_str0", "fatal error: division or modulo by zero", ".L._errDivZero") + val zeroDivError = RuntimeError( + ".L._errDivZero_str0", + "fatal error: division or modulo by zero", + ".L._errDivZero" + ) + val badChrError = RuntimeError( + ".L._errBadChr_str0", + "fatal error: int %d is not ascii character 0-127", + ".L._errBadChr" + ) + val nullPtrError = RuntimeError( + ".L._errNullPtr_str0", + "fatal error: null pair dereferenced or freed", + ".L._errNullPtr" + ) } diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 9e2772c..38ed7a5 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -5,7 +5,6 @@ import cats.data.Chain import cats.syntax.foldable._ import wacc.RuntimeErrors._ - object asmGenerator { import microWacc._ import assemblyIR._ @@ -65,6 +64,8 @@ object asmGenerator { Directive.Asciz(str.escaped) ) } ++ zeroDivError.stringDef + ++ badChrError.stringDef + ++ nullPtrError.stringDef // TODO COLLATE TO ONE LIST INSTANCE Chain( Directive.IntelSyntax, @@ -145,7 +146,16 @@ object asmGenerator { // Out of memory check is optional ) - chain ++= wrapBuiltinFunc(labelGenerator.getLabel(Builtin.Free), Chain.empty) + chain ++= wrapBuiltinFunc( + labelGenerator.getLabel(Builtin.Free), + Chain( + stackAlign, + Move(RDI, RAX), + Compare(RDI, ImmediateVal(0)), + Jump(LabelArg(nullPtrError.errLabel), Cond.Equal), + assemblyIR.Call(CLibFunc.Free) + ) + ) chain ++= wrapBuiltinFunc( labelGenerator.getLabel(Builtin.Read), @@ -170,6 +180,25 @@ object asmGenerator { assemblyIR.Call(CLibFunc.Exit) ) + chain ++= Chain( + LabelDef(badChrError.errLabel), + Pop(RSI), + stackAlign, + Load(RDI, IndexAddress(RIP, LabelArg(badChrError.strLabel))), + assemblyIR.Call(CLibFunc.PrintF), + Move(RDI, ImmediateVal(-1)), + assemblyIR.Call(CLibFunc.Exit) + ) + + chain ++= Chain( + LabelDef(nullPtrError.errLabel), + stackAlign, + Load(RDI, IndexAddress(RIP, LabelArg(nullPtrError.strLabel))), + assemblyIR.Call(CLibFunc.PrintF), + Move(RDI, ImmediateVal(255)), + assemblyIR.Call(CLibFunc.Exit) + ) + chain } @@ -305,7 +334,12 @@ object asmGenerator { case UnaryOp(x, op) => chain ++= evalExprOntoStack(x) op match { - case UnaryOperator.Chr | UnaryOperator.Ord => // No op needed + case UnaryOperator.Chr => + chain += Move(EAX, stack.head) + chain += And(EAX, ImmediateVal(-128)) + chain += Compare(EAX, ImmediateVal(0)) + chain += Jump(LabelArg(badChrError.errLabel), Cond.NotEqual) + case UnaryOperator.Ord => // No op needed case UnaryOperator.Len => chain += stack.pop(RAX) chain += Move(EAX, MemLocation(RAX, D32)) @@ -343,6 +377,8 @@ object asmGenerator { chain += stack.push(destX.size, RAX) case BinaryOperator.Mod => + chain += Compare(stack.head, ImmediateVal(0)) + chain += Jump(LabelArg(zeroDivError.errLabel), Cond.Equal) chain += CDQ() chain += Divide(stack.head) chain += stack.drop() From 9a1728fb3fd6bd1899a270265341bd9d6a919b6c Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Thu, 27 Feb 2025 19:39:48 +0000 Subject: [PATCH 03/15] fix: exit from Main with exit code rather than voiding --- src/main/wacc/Main.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/wacc/Main.scala b/src/main/wacc/Main.scala index 020cbcd..e1f07e1 100644 --- a/src/main/wacc/Main.scala +++ b/src/main/wacc/Main.scala @@ -82,9 +82,9 @@ def compile(filename: String, outFile: Option[File] = None)(using def main(args: Array[String]): Unit = OParser.parse(cliParser, args, CliConfig()) match { case Some(config) => - compile( + System.exit(compile( config.file.getAbsolutePath, outFile = Some(File(".", config.file.getName.stripSuffix(".wacc") + ".s")) - ) + )) case None => } From a20f28977b510808fae1d730dcf83667b42e95ef Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Thu, 27 Feb 2025 19:47:33 +0000 Subject: [PATCH 04/15] fix: handle runtime_error during testing --- src/test/wacc/examples.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala index 7c895ab..fc7d544 100644 --- a/src/test/wacc/examples.scala +++ b/src/test/wacc/examples.scala @@ -92,7 +92,13 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { ) assert(process.exitValue == expectedExit) - assert(stdout.toString.replaceAll("0x[0-9a-f]+", "#addrs#") == expectedOutput) + assert( + stdout.toString + .replaceAll("0x[0-9a-f]+", "#addrs#") + .replaceAll("fatal error:.*", "#runtime_error#\u0000") + .takeWhile(_ != '\u0000') + == expectedOutput + ) } } From cb4f899b8ce58b672e79ced71d518ab78b9d5ed7 Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Thu, 27 Feb 2025 19:50:46 +0000 Subject: [PATCH 05/15] test: provide stdin as space-delimited tokens --- src/test/wacc/examples.scala | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala index fc7d544..f0e1a48 100644 --- a/src/test/wacc/examples.scala +++ b/src/test/wacc/examples.scala @@ -41,23 +41,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { val inputLine = contents .find(_.matches("^# ?[Ii]nput:.*$")) - .map(line => - ("" :: line.split(":").last.strip.split(" ").toList) - .sliding(2) - .flatMap { arr => - if ( - // First entry has no space in front - arr(0) == "" || - // int followed by non-digit, space can be removed - arr(0).toIntOption.nonEmpty && !arr(1)(0).isDigit || - // non-int followed by int, space can be removed - !arr(0).last.isDigit && arr(1).toIntOption.nonEmpty - ) - then List(arr(1)) - else List(" ", arr(1)) - } - .mkString - ) + .map(_.split(":").last.strip + "\n") .getOrElse("") val outputLineIdx = contents.indexWhere(_.matches("^# ?[Oo]utput:.*$")) val expectedOutput = @@ -95,7 +79,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { assert( stdout.toString .replaceAll("0x[0-9a-f]+", "#addrs#") - .replaceAll("fatal error:.*", "#runtime_error#\u0000") + .replaceAll("fatal error:.*", "#runtime_error#\n\u0000") .takeWhile(_ != '\u0000') == expectedOutput ) From 9e6970de62a6955c8ec478d1582c8c6e2bbcccb7 Mon Sep 17 00:00:00 2001 From: Guy C Date: Thu, 27 Feb 2025 19:55:35 +0000 Subject: [PATCH 06/15] feat: implements overflow runtime error handling --- src/main/wacc/backend/RuntimeErrors.scala | 5 +++++ src/main/wacc/backend/asmGenerator.scala | 20 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/wacc/backend/RuntimeErrors.scala b/src/main/wacc/backend/RuntimeErrors.scala index d5df01c..06062ad 100644 --- a/src/main/wacc/backend/RuntimeErrors.scala +++ b/src/main/wacc/backend/RuntimeErrors.scala @@ -27,4 +27,9 @@ object RuntimeErrors { "fatal error: null pair dereferenced or freed", ".L._errNullPtr" ) + val overflowError = RuntimeError( + ".L._errOverflow_str0", + "fatal error: integer overflow or underflow occurred", + ".L._errOverflow" + ) } diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 38ed7a5..411d02e 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -66,6 +66,7 @@ object asmGenerator { } ++ zeroDivError.stringDef ++ badChrError.stringDef ++ nullPtrError.stringDef // TODO COLLATE TO ONE LIST INSTANCE + ++ overflowError.stringDef Chain( Directive.IntelSyntax, @@ -186,7 +187,7 @@ object asmGenerator { stackAlign, Load(RDI, IndexAddress(RIP, LabelArg(badChrError.strLabel))), assemblyIR.Call(CLibFunc.PrintF), - Move(RDI, ImmediateVal(-1)), + Move(RDI, ImmediateVal(255)), assemblyIR.Call(CLibFunc.Exit) ) @@ -199,6 +200,15 @@ object asmGenerator { assemblyIR.Call(CLibFunc.Exit) ) + chain ++= Chain( + LabelDef(overflowError.errLabel), + stackAlign, + Load(RDI, IndexAddress(RIP, LabelArg(overflowError.strLabel))), + assemblyIR.Call(CLibFunc.PrintF), + Move(RDI, ImmediateVal(255)), + assemblyIR.Call(CLibFunc.Exit) + ) + chain } @@ -345,7 +355,11 @@ object asmGenerator { chain += Move(EAX, MemLocation(RAX, D32)) chain += stack.push(D32, RAX) case UnaryOperator.Negate => - chain += Negate(stack.head) + chain += Xor(EAX, EAX) + chain += Subtract(EAX, stack.head) + chain += Jump(LabelArg(overflowError.errLabel), Cond.Overflow) + chain += stack.drop() + chain += stack.push(Q64, RAX) case UnaryOperator.Not => chain += Xor(stack.head, ImmediateVal(1)) } @@ -359,12 +373,14 @@ object asmGenerator { op match { case BinaryOperator.Add => chain += Add(stack.head, destX) + chain += Jump(LabelArg(overflowError.errLabel), Cond.Overflow) case BinaryOperator.Sub => chain += Subtract(destX, stack.head) chain += stack.drop() chain += stack.push(destX.size, RAX) case BinaryOperator.Mul => chain += Multiply(destX, stack.head) + chain += Jump(LabelArg(overflowError.errLabel), Cond.Overflow) chain += stack.drop() chain += stack.push(destX.size, RAX) From edce23615857a8f210cac64bf42ebd9a8f748327 Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Thu, 27 Feb 2025 19:58:57 +0000 Subject: [PATCH 07/15] fix: read space-delimited tokens (as per spec) --- src/main/wacc/frontend/typeChecker.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/wacc/frontend/typeChecker.scala b/src/main/wacc/frontend/typeChecker.scala index 002876d..a628b69 100644 --- a/src/main/wacc/frontend/typeChecker.scala +++ b/src/main/wacc/frontend/typeChecker.scala @@ -180,8 +180,8 @@ object typeChecker { microWacc.Builtin.Read, List( destTy match { - case KnownType.Int => "%d".toMicroWaccCharArray - case KnownType.Char | _ => "%c".toMicroWaccCharArray + case KnownType.Int => " %d".toMicroWaccCharArray + case KnownType.Char | _ => " %c".toMicroWaccCharArray }, destTyped ) From c31dd9de25807d874eecd0434c71c54ced99df95 Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Thu, 27 Feb 2025 20:00:07 +0000 Subject: [PATCH 08/15] style: scala format --- src/main/wacc/Main.scala | 10 ++++++---- src/test/wacc/examples.scala | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/wacc/Main.scala b/src/main/wacc/Main.scala index e1f07e1..fc9fb45 100644 --- a/src/main/wacc/Main.scala +++ b/src/main/wacc/Main.scala @@ -82,9 +82,11 @@ def compile(filename: String, outFile: Option[File] = None)(using def main(args: Array[String]): Unit = OParser.parse(cliParser, args, CliConfig()) match { case Some(config) => - System.exit(compile( - config.file.getAbsolutePath, - outFile = Some(File(".", config.file.getName.stripSuffix(".wacc") + ".s")) - )) + System.exit( + compile( + config.file.getAbsolutePath, + outFile = Some(File(".", config.file.getName.stripSuffix(".wacc") + ".s")) + ) + ) case None => } diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala index f0e1a48..449e5ac 100644 --- a/src/test/wacc/examples.scala +++ b/src/test/wacc/examples.scala @@ -81,7 +81,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { .replaceAll("0x[0-9a-f]+", "#addrs#") .replaceAll("fatal error:.*", "#runtime_error#\n\u0000") .takeWhile(_ != '\u0000') - == expectedOutput + == expectedOutput ) } } From 6f5fcd4d85074b24aa4d5a400266e69dbb41ff6d Mon Sep 17 00:00:00 2001 From: Jonny Date: Thu, 27 Feb 2025 21:20:17 +0000 Subject: [PATCH 09/15] refactor: redesigned runtime errors with added functionality --- src/main/wacc/backend/RuntimeError.scala | 104 ++++++++++++++++++++++ src/main/wacc/backend/RuntimeErrors.scala | 35 -------- src/main/wacc/backend/asmGenerator.scala | 62 +++---------- 3 files changed, 115 insertions(+), 86 deletions(-) create mode 100644 src/main/wacc/backend/RuntimeError.scala delete mode 100644 src/main/wacc/backend/RuntimeErrors.scala diff --git a/src/main/wacc/backend/RuntimeError.scala b/src/main/wacc/backend/RuntimeError.scala new file mode 100644 index 0000000..e179494 --- /dev/null +++ b/src/main/wacc/backend/RuntimeError.scala @@ -0,0 +1,104 @@ +package wacc + +import cats.data.Chain +import wacc.assemblyIR._ + +sealed trait RuntimeError { + def strLabel: String + def errStr: String + def errLabel: String + + def stringDef: Chain[AsmLine] = Chain( + Directive.Int(errStr.length), + LabelDef(strLabel), + Directive.Asciz(errStr) + ) + + def generateHandler: Chain[AsmLine] + +} + +object RuntimeError { + + // TODO: Refactor to mitigate imports and redeclared vals perhaps + + import wacc.asmGenerator.stackAlign + import assemblyIR.Size._ + import assemblyIR.RegName._ + + // private val RAX = Register(Q64, AX) + // private val EAX = Register(D32, AX) + private val RDI = Register(Q64, DI) + private val RIP = Register(Q64, IP) + // private val RBP = Register(Q64, BP) + private val RSI = Register(Q64, SI) + // private val RDX = Register(Q64, DX) + // private val RCX = Register(Q64, CX) + + case object ZeroDivError extends RuntimeError { + val strLabel = ".L._errDivZero_str0" + val errStr = "fatal error: division or modulo by zero" + val errLabel = ".L._errDivZero" + + def generateHandler: Chain[AsmLine] = Chain( + LabelDef(ZeroDivError.errLabel), + stackAlign, + Load(RDI, IndexAddress(RIP, LabelArg(ZeroDivError.strLabel))), + assemblyIR.Call(CLibFunc.PrintF), + Move(RDI, ImmediateVal(-1)), + assemblyIR.Call(CLibFunc.Exit) + ) + + } + + case object BadChrError extends RuntimeError { + val strLabel = ".L._errBadChr_str0" + val errStr = "fatal error: int %d is not an ASCII character 0-127" + val errLabel = ".L._errBadChr" + + def generateHandler: Chain[AsmLine] = Chain( + LabelDef(BadChrError.errLabel), + Pop(RSI), + stackAlign, + Load(RDI, IndexAddress(RIP, LabelArg(BadChrError.strLabel))), + assemblyIR.Call(CLibFunc.PrintF), + Move(RDI, ImmediateVal(255)), + assemblyIR.Call(CLibFunc.Exit) + ) + + } + + case object NullPtrError extends RuntimeError { + val strLabel = ".L._errNullPtr_str0" + val errStr = "fatal error: null pair dereferenced or freed" + val errLabel = ".L._errNullPtr" + + def generateHandler: Chain[AsmLine] = Chain( + LabelDef(NullPtrError.errLabel), + stackAlign, + Load(RDI, IndexAddress(RIP, LabelArg(NullPtrError.strLabel))), + assemblyIR.Call(CLibFunc.PrintF), + Move(RDI, ImmediateVal(255)), + assemblyIR.Call(CLibFunc.Exit) + ) + + } + + case object OverflowError extends RuntimeError { + val strLabel = ".L._errOverflow_str0" + val errStr = "fatal error: integer overflow or underflow occurred" + val errLabel = ".L._errOverflow" + + def generateHandler: Chain[AsmLine] = Chain( + LabelDef(OverflowError.errLabel), + stackAlign, + Load(RDI, IndexAddress(RIP, LabelArg(OverflowError.strLabel))), + assemblyIR.Call(CLibFunc.PrintF), + Move(RDI, ImmediateVal(255)), + assemblyIR.Call(CLibFunc.Exit) + ) + + } + + val all: Chain[RuntimeError] = Chain(ZeroDivError, BadChrError, NullPtrError, OverflowError) +} diff --git a/src/main/wacc/backend/RuntimeErrors.scala b/src/main/wacc/backend/RuntimeErrors.scala deleted file mode 100644 index 06062ad..0000000 --- a/src/main/wacc/backend/RuntimeErrors.scala +++ /dev/null @@ -1,35 +0,0 @@ -package wacc - -import cats.data.Chain -import wacc.assemblyIR._ - -case class RuntimeError(strLabel: String, errStr: String, errLabel: String) { - def stringDef: Chain[AsmLine] = Chain( - Directive.Int(errStr.size), - LabelDef(strLabel), - Directive.Asciz(errStr) - ) -} - -object RuntimeErrors { - val zeroDivError = RuntimeError( - ".L._errDivZero_str0", - "fatal error: division or modulo by zero", - ".L._errDivZero" - ) - val badChrError = RuntimeError( - ".L._errBadChr_str0", - "fatal error: int %d is not ascii character 0-127", - ".L._errBadChr" - ) - val nullPtrError = RuntimeError( - ".L._errNullPtr_str0", - "fatal error: null pair dereferenced or freed", - ".L._errNullPtr" - ) - val overflowError = RuntimeError( - ".L._errOverflow_str0", - "fatal error: integer overflow or underflow occurred", - ".L._errOverflow" - ) -} diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 411d02e..5b3db3e 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -3,7 +3,7 @@ package wacc import scala.collection.mutable.ListBuffer import cats.data.Chain import cats.syntax.foldable._ -import wacc.RuntimeErrors._ +import wacc.RuntimeError._ object asmGenerator { import microWacc._ @@ -63,10 +63,7 @@ object asmGenerator { LabelDef(s".L.str$i"), Directive.Asciz(str.escaped) ) - } ++ zeroDivError.stringDef - ++ badChrError.stringDef - ++ nullPtrError.stringDef // TODO COLLATE TO ONE LIST INSTANCE - ++ overflowError.stringDef + } ++ RuntimeError.all.foldMap(_.stringDef) Chain( Directive.IntelSyntax, @@ -153,7 +150,7 @@ object asmGenerator { stackAlign, Move(RDI, RAX), Compare(RDI, ImmediateVal(0)), - Jump(LabelArg(nullPtrError.errLabel), Cond.Equal), + Jump(LabelArg(NullPtrError.errLabel), Cond.Equal), assemblyIR.Call(CLibFunc.Free) ) ) @@ -170,44 +167,7 @@ object asmGenerator { ) ) - chain ++= Chain( - // TODO can this be done with a call to generateStmt? - // Consider other error cases -> look to generalise - LabelDef(zeroDivError.errLabel), - stackAlign, - Load(RDI, IndexAddress(RIP, LabelArg(zeroDivError.strLabel))), - assemblyIR.Call(CLibFunc.PrintF), - Move(RDI, ImmediateVal(-1)), - assemblyIR.Call(CLibFunc.Exit) - ) - - chain ++= Chain( - LabelDef(badChrError.errLabel), - Pop(RSI), - stackAlign, - Load(RDI, IndexAddress(RIP, LabelArg(badChrError.strLabel))), - assemblyIR.Call(CLibFunc.PrintF), - Move(RDI, ImmediateVal(255)), - assemblyIR.Call(CLibFunc.Exit) - ) - - chain ++= Chain( - LabelDef(nullPtrError.errLabel), - stackAlign, - Load(RDI, IndexAddress(RIP, LabelArg(nullPtrError.strLabel))), - assemblyIR.Call(CLibFunc.PrintF), - Move(RDI, ImmediateVal(255)), - assemblyIR.Call(CLibFunc.Exit) - ) - - chain ++= Chain( - LabelDef(overflowError.errLabel), - stackAlign, - Load(RDI, IndexAddress(RIP, LabelArg(overflowError.strLabel))), - assemblyIR.Call(CLibFunc.PrintF), - Move(RDI, ImmediateVal(255)), - assemblyIR.Call(CLibFunc.Exit) - ) + chain ++= RuntimeError.all.foldMap(_.generateHandler) chain } @@ -348,7 +308,7 @@ object asmGenerator { chain += Move(EAX, stack.head) chain += And(EAX, ImmediateVal(-128)) chain += Compare(EAX, ImmediateVal(0)) - chain += Jump(LabelArg(badChrError.errLabel), Cond.NotEqual) + chain += Jump(LabelArg(BadChrError.errLabel), Cond.NotEqual) case UnaryOperator.Ord => // No op needed case UnaryOperator.Len => chain += stack.pop(RAX) @@ -357,7 +317,7 @@ object asmGenerator { case UnaryOperator.Negate => chain += Xor(EAX, EAX) chain += Subtract(EAX, stack.head) - chain += Jump(LabelArg(overflowError.errLabel), Cond.Overflow) + chain += Jump(LabelArg(OverflowError.errLabel), Cond.Overflow) chain += stack.drop() chain += stack.push(Q64, RAX) case UnaryOperator.Not => @@ -373,20 +333,20 @@ object asmGenerator { op match { case BinaryOperator.Add => chain += Add(stack.head, destX) - chain += Jump(LabelArg(overflowError.errLabel), Cond.Overflow) + chain += Jump(LabelArg(OverflowError.errLabel), Cond.Overflow) case BinaryOperator.Sub => chain += Subtract(destX, stack.head) chain += stack.drop() chain += stack.push(destX.size, RAX) case BinaryOperator.Mul => chain += Multiply(destX, stack.head) - chain += Jump(LabelArg(overflowError.errLabel), Cond.Overflow) + chain += Jump(LabelArg(OverflowError.errLabel), Cond.Overflow) chain += stack.drop() chain += stack.push(destX.size, RAX) case BinaryOperator.Div => chain += Compare(stack.head, ImmediateVal(0)) - chain += Jump(LabelArg(zeroDivError.errLabel), Cond.Equal) + chain += Jump(LabelArg(ZeroDivError.errLabel), Cond.Equal) chain += CDQ() chain += Divide(stack.head) chain += stack.drop() @@ -394,7 +354,7 @@ object asmGenerator { case BinaryOperator.Mod => chain += Compare(stack.head, ImmediateVal(0)) - chain += Jump(LabelArg(zeroDivError.errLabel), Cond.Equal) + chain += Jump(LabelArg(ZeroDivError.errLabel), Cond.Equal) chain += CDQ() chain += Divide(stack.head) chain += stack.drop() @@ -480,7 +440,7 @@ object asmGenerator { chain } - private def stackAlign: AsmLine = And(Register(Q64, SP), ImmediateVal(-16)) + def stackAlign: AsmLine = And(Register(Q64, SP), ImmediateVal(-16)) private def zeroRest(dest: Dest, size: Size): Chain[AsmLine] = size match { case Q64 | D32 => Chain.empty case _ => Chain.one(And(dest, ImmediateVal((1 << (size.toInt * 8)) - 1))) From 617f6759d3ebd63a361f51ef3af0713f644b2520 Mon Sep 17 00:00:00 2001 From: Jonny Date: Thu, 27 Feb 2025 21:53:48 +0000 Subject: [PATCH 10/15] fix: underflow detected --- src/main/wacc/backend/asmGenerator.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 5b3db3e..b7c2f5a 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -336,6 +336,7 @@ object asmGenerator { chain += Jump(LabelArg(OverflowError.errLabel), Cond.Overflow) case BinaryOperator.Sub => chain += Subtract(destX, stack.head) + chain += Jump(LabelArg(OverflowError.errLabel), Cond.Overflow) chain += stack.drop() chain += stack.push(destX.size, RAX) case BinaryOperator.Mul => From 4727d6c39965900381960bad9bb444f7f8d00998 Mon Sep 17 00:00:00 2001 From: Jonny Date: Thu, 27 Feb 2025 22:32:11 +0000 Subject: [PATCH 11/15] fix: remove incorrect newline --- src/test/wacc/examples.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala index 449e5ac..efdbedf 100644 --- a/src/test/wacc/examples.scala +++ b/src/test/wacc/examples.scala @@ -79,7 +79,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { assert( stdout.toString .replaceAll("0x[0-9a-f]+", "#addrs#") - .replaceAll("fatal error:.*", "#runtime_error#\n\u0000") + .replaceAll("fatal error:.*", "#runtime_error#\u0000") .takeWhile(_ != '\u0000') == expectedOutput ) From 3a2af6f95d2b63d3ba05c443cf7a478554765f80 Mon Sep 17 00:00:00 2001 From: Jonny Date: Thu, 27 Feb 2025 23:02:10 +0000 Subject: [PATCH 12/15] feat: implements outofbounds error. array negative bounds check added --- src/main/wacc/backend/RuntimeError.scala | 20 +++++++++++++++++++- src/main/wacc/backend/asmGenerator.scala | 6 ++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/wacc/backend/RuntimeError.scala b/src/main/wacc/backend/RuntimeError.scala index e179494..7a24d9a 100644 --- a/src/main/wacc/backend/RuntimeError.scala +++ b/src/main/wacc/backend/RuntimeError.scala @@ -100,5 +100,23 @@ object RuntimeError { } - val all: Chain[RuntimeError] = Chain(ZeroDivError, BadChrError, NullPtrError, OverflowError) + case object OutOfBoundsError extends RuntimeError { + + val strLabel = ".L._errOutOfBounds_str0" + val errStr = "fatal error: array index %d out of bounds" + val errLabel = ".L._errOutOfBounds" + + def generateHandler: Chain[AsmLine] = Chain( + LabelDef(OutOfBoundsError.errLabel), + Pop(RSI), // le index + stackAlign, + Load(RDI, IndexAddress(RIP, LabelArg(OutOfBoundsError.strLabel))), + assemblyIR.Call(CLibFunc.PrintF), + Move(RDI, ImmediateVal(255)), + assemblyIR.Call(CLibFunc.Exit) + ) + } + + val all: Chain[RuntimeError] = + Chain(ZeroDivError, BadChrError, NullPtrError, OverflowError, OutOfBoundsError) } diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index b7c2f5a..2f15cc0 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -190,9 +190,13 @@ object asmGenerator { case ArrayElem(x, i) => chain ++= evalExprOntoStack(rhs) chain ++= evalExprOntoStack(i) + chain += Compare(stack.head, ImmediateVal(0)) + chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.Less) chain ++= evalExprOntoStack(x) chain += stack.pop(RAX) chain += stack.pop(RCX) + // chain += Compare(RAX, RCX) + // chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.GreaterEqual) chain += stack.pop(RDX) chain += Move( @@ -294,6 +298,8 @@ object asmGenerator { chain ++= evalExprOntoStack(x) chain ++= evalExprOntoStack(i) chain += stack.pop(RCX) + chain += Compare(RCX, ImmediateVal(0)) + chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.Less) chain += stack.pop(RAX) // + Int because we store the length of the array at the start chain += Move( From 1b2df507ba47797818a21a2b06113e2cdd4baa0e Mon Sep 17 00:00:00 2001 From: Jonny Date: Thu, 27 Feb 2025 23:46:51 +0000 Subject: [PATCH 13/15] fix: array bounds checks in place --- src/main/wacc/backend/asmGenerator.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 2f15cc0..5c584af 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -22,6 +22,7 @@ object asmGenerator { private val RSI = Register(Q64, SI) private val RDX = Register(Q64, DX) private val RCX = Register(Q64, CX) + private val ECX = Register(D32, CX) private val argRegs = List(DI, SI, DX, CX, R8, R9) extension [T](chain: Chain[T]) @@ -195,8 +196,8 @@ object asmGenerator { chain ++= evalExprOntoStack(x) chain += stack.pop(RAX) chain += stack.pop(RCX) - // chain += Compare(RAX, RCX) - // chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.GreaterEqual) + chain += Compare(MemLocation(RAX, D32), ECX) + chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.LessEqual) chain += stack.pop(RDX) chain += Move( @@ -301,6 +302,8 @@ object asmGenerator { chain += Compare(RCX, ImmediateVal(0)) chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.Less) chain += stack.pop(RAX) + chain += Compare(MemLocation(RAX, D32), ECX) + chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.LessEqual) // + Int because we store the length of the array at the start chain += Move( Register(x.ty.elemSize, AX), From edcac2782b91f031dc716094c5c6edbeb99b903e Mon Sep 17 00:00:00 2001 From: Jonny Date: Thu, 27 Feb 2025 23:57:42 +0000 Subject: [PATCH 14/15] fix: implements null pointer checks --- src/main/wacc/backend/RuntimeError.scala | 2 +- src/main/wacc/backend/asmGenerator.scala | 4 ++++ src/test/wacc/examples.scala | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/wacc/backend/RuntimeError.scala b/src/main/wacc/backend/RuntimeError.scala index 7a24d9a..ee52375 100644 --- a/src/main/wacc/backend/RuntimeError.scala +++ b/src/main/wacc/backend/RuntimeError.scala @@ -108,7 +108,7 @@ object RuntimeError { def generateHandler: Chain[AsmLine] = Chain( LabelDef(OutOfBoundsError.errLabel), - Pop(RSI), // le index + Pop(RSI), stackAlign, Load(RDI, IndexAddress(RIP, LabelArg(OutOfBoundsError.strLabel))), assemblyIR.Call(CLibFunc.PrintF), diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 5c584af..926359a 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -196,6 +196,8 @@ object asmGenerator { chain ++= evalExprOntoStack(x) chain += stack.pop(RAX) chain += stack.pop(RCX) + chain += Compare(EAX, ImmediateVal(0)) + chain += Jump(LabelArg(NullPtrError.errLabel), Cond.Equal) chain += Compare(MemLocation(RAX, D32), ECX) chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.LessEqual) chain += stack.pop(RDX) @@ -302,6 +304,8 @@ object asmGenerator { chain += Compare(RCX, ImmediateVal(0)) chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.Less) chain += stack.pop(RAX) + chain += Compare(EAX, ImmediateVal(0)) + chain += Jump(LabelArg(NullPtrError.errLabel), Cond.Equal) chain += Compare(MemLocation(RAX, D32), ECX) chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.LessEqual) // + Int because we store the length of the array at the start diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala index efdbedf..6114afd 100644 --- a/src/test/wacc/examples.scala +++ b/src/test/wacc/examples.scala @@ -107,7 +107,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { // "^.*wacc-examples/valid/IO/IOLoop.wacc.*$", // "^.*wacc-examples/valid/IO/IOSequence.wacc.*$", // "^.*wacc-examples/valid/pairs.*$", - "^.*wacc-examples/valid/runtimeErr.*$", + //"^.*wacc-examples/valid/runtimeErr.*$", // "^.*wacc-examples/valid/scope.*$", // "^.*wacc-examples/valid/sequence.*$", // "^.*wacc-examples/valid/variables.*$", From cdf32d93c3dbfaf627a69bafffd70289a9d2ba0f Mon Sep 17 00:00:00 2001 From: Guy C Date: Fri, 28 Feb 2025 00:15:20 +0000 Subject: [PATCH 15/15] feat: outofboundserror message includes given bad value --- src/main/wacc/backend/RuntimeError.scala | 2 +- src/main/wacc/backend/asmGenerator.scala | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/wacc/backend/RuntimeError.scala b/src/main/wacc/backend/RuntimeError.scala index ee52375..9085b63 100644 --- a/src/main/wacc/backend/RuntimeError.scala +++ b/src/main/wacc/backend/RuntimeError.scala @@ -108,7 +108,7 @@ object RuntimeError { def generateHandler: Chain[AsmLine] = Chain( LabelDef(OutOfBoundsError.errLabel), - Pop(RSI), + Move(RSI, Register(Q64, CX)), stackAlign, Load(RDI, IndexAddress(RIP, LabelArg(OutOfBoundsError.strLabel))), assemblyIR.Call(CLibFunc.PrintF), diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index 926359a..71a99b5 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -191,8 +191,10 @@ object asmGenerator { case ArrayElem(x, i) => chain ++= evalExprOntoStack(rhs) chain ++= evalExprOntoStack(i) - chain += Compare(stack.head, ImmediateVal(0)) + chain += stack.pop(RCX) + chain += Compare(ECX, ImmediateVal(0)) chain += Jump(LabelArg(OutOfBoundsError.errLabel), Cond.Less) + chain += stack.push(Q64, RCX) chain ++= evalExprOntoStack(x) chain += stack.pop(RAX) chain += stack.pop(RCX)