diff --git a/src/main/wacc/backend/LabelGenerator.scala b/src/main/wacc/backend/LabelGenerator.scala index fd0006f..7653cb4 100644 --- a/src/main/wacc/backend/LabelGenerator.scala +++ b/src/main/wacc/backend/LabelGenerator.scala @@ -2,6 +2,7 @@ package wacc import scala.collection.mutable import cats.data.Chain +import wacc.ast.Position private class LabelGenerator { import assemblyIR._ @@ -9,7 +10,9 @@ private class LabelGenerator { import asmGenerator.escaped private val strings = mutable.HashMap[String, String]() + private val files = mutable.HashMap[String, Int]() private var labelVal = -1 + private var permittedFuncFile: Option[String] = None /** Get an arbitrary label. */ def getLabel(): String = { @@ -39,6 +42,25 @@ private class LabelGenerator { def getLabelArg(src: String, name: String): LabelArg = LabelArg(strings.getOrElseUpdate(src, s".L.$name.str${strings.size}")) + /** Get a debug directive for a file. */ + def getDebugFile(file: java.io.File): Int = + files.getOrElseUpdate(file.getCanonicalPath, files.size) + + /** Get a debug directive for a function. */ + def getDebugFunc(pos: Position, name: String, label: LabelDef): Chain[AsmLine] = { + permittedFuncFile match { + case Some(f) if f != pos.file.getCanonicalPath => Chain.empty + case _ => + permittedFuncFile = Some(pos.file.getCanonicalPath) + Chain( + LabelDef(name), + Directive.Location(getDebugFile(pos.file), pos.line, None), + Directive.Type(label, SymbolType.Function), + Directive.Func(name, label) + ) + } + } + /** Generate the assembly labels for constants that were labelled using the LabelGenerator. */ def generateConstants: Chain[AsmLine] = strings.foldLeft(Chain.empty) { case (acc, (str, label)) => @@ -47,4 +69,10 @@ private class LabelGenerator { Directive.Asciz(str.escaped) ) } + + /** Generates debug directives that were created using the LabelGenerator. */ + def generateDebug: Chain[AsmLine] = + files.foldLeft(Chain.empty) { case (acc, (file, no)) => + acc :+ Directive.File(no, file) + } } diff --git a/src/main/wacc/backend/asmGenerator.scala b/src/main/wacc/backend/asmGenerator.scala index adb437c..4c31386 100644 --- a/src/main/wacc/backend/asmGenerator.scala +++ b/src/main/wacc/backend/asmGenerator.scala @@ -36,11 +36,18 @@ object asmGenerator { given labelGenerator: LabelGenerator = LabelGenerator() val Program(funcs, main) = microProg - val progAsm = Chain(LabelDef("main")).concatAll( + val mainLabel = LabelDef("main") + val mainAsm = main.headOption match { + case Some(stmt) => + labelGenerator.getDebugFunc(stmt.pos, "$main", mainLabel) + mainLabel + case None => Chain.one(mainLabel) + } + val progAsm = mainAsm.concatAll( funcPrologue(), main.foldMap(generateStmt(_)), Chain.one(Xor(RAX, RAX)), funcEpilogue(), + Chain(Directive.Size(mainLabel, SizeExpr.Relative(mainLabel)), Directive.EndFunc), generateBuiltInFuncs(), RuntimeError.all.foldMap(_.generate), funcs.foldMap(generateUserFunc(_)) @@ -51,6 +58,7 @@ object asmGenerator { Directive.Global("main"), Directive.RoData ).concatAll( + labelGenerator.generateDebug, labelGenerator.generateConstants, Chain.one(Directive.Text), progAsm @@ -75,7 +83,10 @@ object asmGenerator { // Setup the stack with param 7 and up func.params.drop(argRegs.size).foreach(stack.reserve(_)) stack.reserve(Size.Q64) // Reserve return pointer slot - var asm = Chain.one[AsmLine](labelGenerator.getLabelDef(func.name)) + val funcLabel = labelGenerator.getLabelDef(func.name) + var asm = labelGenerator.getDebugFunc(func.pos, func.name.name, funcLabel) + val debugFunc = asm.size > 0 + asm += funcLabel asm ++= funcPrologue() // Push the rest of params onto the stack for simplicity argRegs.zip(func.params).foreach { (reg, param) => @@ -83,6 +94,10 @@ object asmGenerator { } asm ++= func.body.foldMap(generateStmt(_)) // No need for epilogue here since all user functions must return explicitly + if (debugFunc) { + asm += Directive.Size(funcLabel, SizeExpr.Relative(funcLabel)) + asm += Directive.EndFunc + } asm } @@ -159,8 +174,8 @@ object asmGenerator { stack: Stack, labelGenerator: LabelGenerator ): Chain[AsmLine] = { - var asm = Chain.empty[AsmLine] - asm += Comment(stmt.toString) + val fileNo = labelGenerator.getDebugFile(stmt.pos.file) + var asm = Chain.one[AsmLine](Directive.Location(fileNo, stmt.pos.line, None)) stmt match { case Assign(lhs, rhs) => lhs match { diff --git a/src/main/wacc/backend/assemblyIR.scala b/src/main/wacc/backend/assemblyIR.scala index d265358..a6dbe20 100644 --- a/src/main/wacc/backend/assemblyIR.scala +++ b/src/main/wacc/backend/assemblyIR.scala @@ -199,10 +199,15 @@ object assemblyIR { } enum Directive extends AsmLine { - case IntelSyntax, RoData, Text + case IntelSyntax, RoData, Text, EndFunc case Global(name: String) case Int(value: scala.Int) case Asciz(string: String) + case File(no: scala.Int, file: String) + case Location(fileNo: scala.Int, lineNo: scala.Int, colNo: Option[scala.Int]) + case Func(name: String, label: LabelDef) + case Type(label: LabelDef, symbolType: SymbolType) + case Size(label: LabelDef, expr: SizeExpr) override def toString(): String = this match { case IntelSyntax => ".intel_syntax noprefix" @@ -211,6 +216,32 @@ object assemblyIR { case RoData => ".section .rodata" case Int(value) => s"\t.int $value" case Asciz(string) => s"\t.asciz \"$string\"" + case File(no, file) => s".file $no \"${file}\"" + case Location(fileNo, lineNo, colNo) => + s"\t.loc $fileNo $lineNo" + colNo.map(c => s" $c").getOrElse("") + case Func(name, label) => + s".func $name, ${label.name}" + case EndFunc => ".endfunc" + case Type(label, symbolType) => + s".type ${label.name}, @${symbolType.toString}" + case Directive.Size(label, expr) => + s".size ${label.name}, ${expr.toString}" + } + } + + enum SymbolType { + case Function + + override def toString(): String = this match { + case Function => "function" + } + } + + enum SizeExpr { + case Relative(label: LabelDef) + + override def toString(): String = this match { + case Relative(label) => s".-${label.name}" } } diff --git a/src/main/wacc/frontend/microWacc.scala b/src/main/wacc/frontend/microWacc.scala index 656b433..d99e67a 100644 --- a/src/main/wacc/frontend/microWacc.scala +++ b/src/main/wacc/frontend/microWacc.scala @@ -92,6 +92,6 @@ object microWacc { case class Return(expr: Expr)(val pos: Position) extends Stmt // Program - case class FuncDecl(name: Ident, params: List[Ident], body: Chain[Stmt]) + case class FuncDecl(name: Ident, params: List[Ident], body: Chain[Stmt])(val pos: Position) case class Program(funcs: Chain[FuncDecl], stmts: Chain[Stmt]) } diff --git a/src/main/wacc/frontend/typeChecker.scala b/src/main/wacc/frontend/typeChecker.scala index 325fa64..4957718 100644 --- a/src/main/wacc/frontend/typeChecker.scala +++ b/src/main/wacc/frontend/typeChecker.scala @@ -95,7 +95,7 @@ object typeChecker { microWacc.Ident(ident.v, ident.guid)(ty) }, body - ), + )(func.pos), bodyErrors ) }