From 8fcf628c8f85886c6ba53d361a77589a1f964cac Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Mon, 17 Dec 2018 00:35:32 +0100 Subject: [PATCH] Better tracking of the original source --- docs/api/command-line.md | 2 + docs/api/getting-started.md | 2 + .../scala/millfork/CompilationOptions.scala | 2 +- src/main/scala/millfork/Main.scala | 9 ++++ .../scala/millfork/assembly/z80/ZLine.scala | 2 + .../millfork/compiler/MacroExpander.scala | 4 +- .../compiler/z80/Z80StatementCompiler.scala | 2 +- .../scala/millfork/error/ConsoleLogger.scala | 5 +++ .../millfork/error/ErrorsOnlyLogger.scala | 3 ++ src/main/scala/millfork/error/Logger.scala | 3 ++ src/main/scala/millfork/node/Node.scala | 13 +++--- .../millfork/node/opt/UnreachableCode.scala | 8 ++-- .../node/opt/UnusedGlobalVariables.scala | 26 ++++++------ .../node/opt/UnusedLocalVariables.scala | 41 +++++++++++++------ .../millfork/output/AbstractAssembler.scala | 14 ++++++- .../output/MosInliningCalculator.scala | 16 ++++++-- .../output/Z80InliningCalculator.scala | 26 ++++++++---- 17 files changed, 126 insertions(+), 52 deletions(-) diff --git a/docs/api/command-line.md b/docs/api/command-line.md index 7832eebc..d40d611d 100644 --- a/docs/api/command-line.md +++ b/docs/api/command-line.md @@ -49,6 +49,8 @@ Choose syntax for assembly output on 8080-like targets. * `-fline-numbers`, `-fno-line-numbers` – Whether should show source line numbers in assembly output. +* `-fline-numbers`, `-fno-line-numbers` – Whether should show the original source in assembly output. Implies `-fline-numbers` + ## Verbosity options * `-q` – Suppress all messages except for errors. diff --git a/docs/api/getting-started.md b/docs/api/getting-started.md index 44facdc9..44d8d6d8 100644 --- a/docs/api/getting-started.md +++ b/docs/api/getting-started.md @@ -50,6 +50,8 @@ You may be also interested in the following: * `-s` – additionally generate assembly output (if targeting Intel 8080, use `--syntax=intel` or `--syntax=zilog` to choose the preferred assembly syntax) +* `-fsource-in-asm` – show original Millfork source in the assembly output + * `-g` – additionally generate a label file, in format compatible with VICE emulator * `-r PROGRAM` – automatically launch given program after successful compilation diff --git a/src/main/scala/millfork/CompilationOptions.scala b/src/main/scala/millfork/CompilationOptions.scala index 5857500a..b2ef2a92 100644 --- a/src/main/scala/millfork/CompilationOptions.scala +++ b/src/main/scala/millfork/CompilationOptions.scala @@ -304,7 +304,7 @@ object Cpu extends Enumeration { object CompilationFlag extends Enumeration { val // common compilation options: - EmitIllegals, DecimalMode, ReadOnlyArrays, LenientTextEncoding, LineNumbersInAssembly, + EmitIllegals, DecimalMode, ReadOnlyArrays, LenientTextEncoding, LineNumbersInAssembly, SourceInAssembly, // compilation options for MOS: EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack, diff --git a/src/main/scala/millfork/Main.scala b/src/main/scala/millfork/Main.scala index 4a2c98fb..475e8234 100644 --- a/src/main/scala/millfork/Main.scala +++ b/src/main/scala/millfork/Main.scala @@ -239,6 +239,7 @@ object Main { result } + //noinspection NameBooleanParameters private def parser(errorReporting: Logger): CliParser[Context] = new CliParser[Context] { fluff("Main options:", "") @@ -302,6 +303,14 @@ object Main { c.changeFlag(CompilationFlag.LineNumbersInAssembly, v) ).description("Show source line numbers in assembly.") + boolean("-fsource-in-asm", "-fno-source-in-asm").action((c,v) => + if (v) { + c.changeFlag(CompilationFlag.SourceInAssembly, true).changeFlag(CompilationFlag.LineNumbersInAssembly, true) + } else { + c.changeFlag(CompilationFlag.LineNumbersInAssembly, false) + } + ).description("Show source in assembly.") + endOfFlags("--").description("Marks the end of options.") fluff("", "Verbosity options:", "") diff --git a/src/main/scala/millfork/assembly/z80/ZLine.scala b/src/main/scala/millfork/assembly/z80/ZLine.scala index a566e104..1fd31cba 100644 --- a/src/main/scala/millfork/assembly/z80/ZLine.scala +++ b/src/main/scala/millfork/assembly/z80/ZLine.scala @@ -222,6 +222,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta def position(s: Option[Position]): ZLine = pos(SourceLine.of(s)) + def positionIfEmpty(s: Option[Position]): ZLine = if (s.isEmpty || source.isDefined) this else pos(SourceLine.of(s)) + def pos(s: Seq[Option[SourceLine]]): ZLine = pos(SourceLine.merge(s)) def mergePos(s: Seq[Option[SourceLine]]): ZLine = if (s.isEmpty) this else pos(SourceLine.merge(this.source, s)) diff --git a/src/main/scala/millfork/compiler/MacroExpander.scala b/src/main/scala/millfork/compiler/MacroExpander.scala index 6e1ff15a..67aa89b2 100644 --- a/src/main/scala/millfork/compiler/MacroExpander.scala +++ b/src/main/scala/millfork/compiler/MacroExpander.scala @@ -25,7 +25,7 @@ abstract class MacroExpander[T <: AbstractCode] { def h(s: String) = if (s == paramName) target.asInstanceOf[VariableExpression].name else s - stmt match { + (stmt match { case RawBytesStatement(contents) => RawBytesStatement(contents.replaceVariable(paramName, target)) case ExpressionStatement(e) => ExpressionStatement(e.replaceVariable(paramName, target)) case ReturnStatement(e) => ReturnStatement(e.map(f)) @@ -45,7 +45,7 @@ abstract class MacroExpander[T <: AbstractCode] { case _ => println(stmt) ??? - } + }).pos(stmt.position) } def inlineFunction(ctx: CompilationContext, i: MacroFunction, params: List[Expression], position: Option[Position]): (List[T], List[ExecutableStatement]) = { diff --git a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala index a656ad40..7eb20604 100644 --- a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala +++ b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala @@ -192,7 +192,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] { case _ => reg } List(ZLine(op, registers, param, elidability)) - }).map(_.position(statement.position)) + }).map(_.positionIfEmpty(statement.position)) } private def fixStackOnReturn(ctx: CompilationContext): List[ZLine] = { diff --git a/src/main/scala/millfork/error/ConsoleLogger.scala b/src/main/scala/millfork/error/ConsoleLogger.scala index 827e9c11..f6568e0f 100644 --- a/src/main/scala/millfork/error/ConsoleLogger.scala +++ b/src/main/scala/millfork/error/ConsoleLogger.scala @@ -1,5 +1,6 @@ package millfork.error +import millfork.assembly.SourceLine import millfork.node.Position import scala.collection.mutable @@ -117,4 +118,8 @@ class ConsoleLogger extends Logger { sourceLines(filename) = lines } + override def getLine(line: SourceLine): Option[String] = for { + file <- sourceLines.get(line.moduleName) + line <- file.lift(line.line - 1) + } yield line } \ No newline at end of file diff --git a/src/main/scala/millfork/error/ErrorsOnlyLogger.scala b/src/main/scala/millfork/error/ErrorsOnlyLogger.scala index 42563e72..6890c54a 100644 --- a/src/main/scala/millfork/error/ErrorsOnlyLogger.scala +++ b/src/main/scala/millfork/error/ErrorsOnlyLogger.scala @@ -1,5 +1,6 @@ package millfork.error +import millfork.assembly.SourceLine import millfork.node.Position /** @@ -27,4 +28,6 @@ class ErrorsOnlyLogger(inner: Logger) extends Logger { override def assertNoErrors(msg: String): Unit = inner.assertNoErrors(msg) override def addSource(filename: String, lines: IndexedSeq[String]): Unit = () + + override def getLine(line: SourceLine): Option[String] = None } diff --git a/src/main/scala/millfork/error/Logger.scala b/src/main/scala/millfork/error/Logger.scala index dea882b4..311a8ad1 100644 --- a/src/main/scala/millfork/error/Logger.scala +++ b/src/main/scala/millfork/error/Logger.scala @@ -1,5 +1,6 @@ package millfork.error +import millfork.assembly.SourceLine import millfork.node.Position /** @@ -28,4 +29,6 @@ trait Logger { def assertNoErrors(msg: String): Unit def addSource(filename: String, lines: IndexedSeq[String]): Unit + + def getLine(line: SourceLine): Option[String] } \ No newline at end of file diff --git a/src/main/scala/millfork/node/Node.scala b/src/main/scala/millfork/node/Node.scala index b3c43e2c..e59c0055 100644 --- a/src/main/scala/millfork/node/Node.scala +++ b/src/main/scala/millfork/node/Node.scala @@ -82,7 +82,7 @@ case class SeparateBytesExpression(hi: Expression, lo: Expression) extends LhsEx def replaceVariable(variable: String, actualParam: Expression): Expression = SeparateBytesExpression( hi.replaceVariable(variable, actualParam), - lo.replaceVariable(variable, actualParam)) + lo.replaceVariable(variable, actualParam)).pos(position) override def containsVariable(variable: String): Boolean = hi.containsVariable(variable) || lo.containsVariable(variable) override def isPure: Boolean = hi.isPure && lo.isPure override def getAllIdentifiers: Set[String] = hi.getAllIdentifiers ++ lo.getAllIdentifiers @@ -90,7 +90,7 @@ case class SeparateBytesExpression(hi: Expression, lo: Expression) extends LhsEx case class SumExpression(expressions: List[(Boolean, Expression)], decimal: Boolean) extends Expression { override def replaceVariable(variable: String, actualParam: Expression): Expression = - SumExpression(expressions.map { case (n, e) => n -> e.replaceVariable(variable, actualParam) }, decimal) + SumExpression(expressions.map { case (n, e) => n -> e.replaceVariable(variable, actualParam) }, decimal).pos(position) override def containsVariable(variable: String): Boolean = expressions.exists(_._2.containsVariable(variable)) override def isPure: Boolean = expressions.forall(_._2.isPure) override def getAllIdentifiers: Set[String] = expressions.map(_._2.getAllIdentifiers).fold(Set[String]())(_ ++ _) @@ -100,7 +100,7 @@ case class FunctionCallExpression(functionName: String, expressions: List[Expres override def replaceVariable(variable: String, actualParam: Expression): Expression = FunctionCallExpression(functionName, expressions.map { _.replaceVariable(variable, actualParam) - }) + }).pos(position) override def containsVariable(variable: String): Boolean = expressions.exists(_.containsVariable(variable)) override def isPure: Boolean = false // TODO override def getAllIdentifiers: Set[String] = expressions.map(_.getAllIdentifiers).fold(Set[String]())(_ ++ _) + functionName @@ -108,7 +108,7 @@ case class FunctionCallExpression(functionName: String, expressions: List[Expres case class HalfWordExpression(expression: Expression, hiByte: Boolean) extends Expression { override def replaceVariable(variable: String, actualParam: Expression): Expression = - HalfWordExpression(expression.replaceVariable(variable, actualParam), hiByte) + HalfWordExpression(expression.replaceVariable(variable, actualParam), hiByte).pos(position) override def containsVariable(variable: String): Boolean = expression.containsVariable(variable) override def isPure: Boolean = expression.isPure override def getAllIdentifiers: Set[String] = expression.getAllIdentifiers @@ -178,10 +178,11 @@ case class IndexedExpression(name: String, index: Expression) extends LhsExpress override def replaceVariable(variable: String, actualParam: Expression): Expression = if (name == variable) { actualParam match { - case VariableExpression(actualVariable) => IndexedExpression(actualVariable, index.replaceVariable(variable, actualParam)) + case VariableExpression(actualVariable) => + IndexedExpression(actualVariable, index.replaceVariable(variable, actualParam)).pos(position) case _ => ??? // TODO } - } else IndexedExpression(name, index.replaceVariable(variable, actualParam)) + } else IndexedExpression(name, index.replaceVariable(variable, actualParam)).pos(position) override def containsVariable(variable: String): Boolean = name == variable || index.containsVariable(variable) override def isPure: Boolean = index.isPure override def getAllIdentifiers: Set[String] = index.getAllIdentifiers + name diff --git a/src/main/scala/millfork/node/opt/UnreachableCode.scala b/src/main/scala/millfork/node/opt/UnreachableCode.scala index 7715f05e..e968e8a4 100644 --- a/src/main/scala/millfork/node/opt/UnreachableCode.scala +++ b/src/main/scala/millfork/node/opt/UnreachableCode.scala @@ -10,15 +10,15 @@ object UnreachableCode extends NodeOptimization { override def optimize(nodes: List[Node], options: CompilationOptions): List[Node] = nodes match { case (x:FunctionDeclarationStatement)::xs => - x.copy(statements = x.statements.map(optimizeStatements(_, options))) :: optimize(xs, options) + x.copy(statements = x.statements.map(optimizeStatements(_, options))).pos(x.position) :: optimize(xs, options) case (x:IfStatement)::xs => x.copy( thenBranch = optimizeExecutableStatements(x.thenBranch, options), - elseBranch = optimizeExecutableStatements(x.elseBranch, options)) :: optimize(xs, options) + elseBranch = optimizeExecutableStatements(x.elseBranch, options)).pos(x.position) :: optimize(xs, options) case (x:WhileStatement)::xs => - x.copy(body = optimizeExecutableStatements(x.body, options)) :: optimize(xs, options) + x.copy(body = optimizeExecutableStatements(x.body, options)).pos(x.position) :: optimize(xs, options) case (x:DoWhileStatement)::xs => - x.copy(body = optimizeExecutableStatements(x.body, options)) :: optimize(xs, options) + x.copy(body = optimizeExecutableStatements(x.body, options)).pos(x.position) :: optimize(xs, options) case (x:ReturnStatement) :: xs => x :: Nil case x :: xs => diff --git a/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala b/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala index cf98bd30..2319cbb2 100644 --- a/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala +++ b/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala @@ -72,7 +72,7 @@ object UnusedGlobalVariables extends NodeOptimization { params.flatMap { case VariableExpression(_) => None case LiteralExpression(_, _) => None - case x => Some(ExpressionStatement(x)) + case x => Some(ExpressionStatement(x).pos(s.position)) } } else Some(s) case s@Assignment(VariableExpression(n), VariableExpression(_)) => @@ -80,35 +80,35 @@ object UnusedGlobalVariables extends NodeOptimization { case s@Assignment(VariableExpression(n), LiteralExpression(_, _)) => if (globalsToRemove(n)) Nil else Some(s) case s@Assignment(VariableExpression(n), expr) => - if (globalsToRemove(n)) Some(ExpressionStatement(expr)) else Some(s) - case s@Assignment(SeparateBytesExpression(VariableExpression(h), VariableExpression(l)), expr) => + if (globalsToRemove(n)) Some(ExpressionStatement(expr).pos(s.position)) else Some(s) + case s@Assignment(SeparateBytesExpression(he@VariableExpression(h), le@VariableExpression(l)), expr) => if (globalsToRemove(h)) { if (globalsToRemove(l)) - Some(ExpressionStatement(expr)) + Some(ExpressionStatement(expr).pos(s.position)) else - Some(Assignment(SeparateBytesExpression(BlackHoleExpression, VariableExpression(l)), expr)) + Some(Assignment(SeparateBytesExpression(BlackHoleExpression, le).pos(he.position), expr).pos(s.position)) } else { if (globalsToRemove(l)) - Some(Assignment(SeparateBytesExpression(VariableExpression(h), BlackHoleExpression), expr)) + Some(Assignment(SeparateBytesExpression(he, BlackHoleExpression).pos(he.position), expr).pos(s.position)) else Some(s) } - case s@Assignment(SeparateBytesExpression(h, VariableExpression(l)), expr) => - if (globalsToRemove(l)) Some(Assignment(SeparateBytesExpression(h, BlackHoleExpression), expr)) + case s@Assignment(SeparateBytesExpression(h, le@VariableExpression(l)), expr) => + if (globalsToRemove(l)) Some(Assignment(SeparateBytesExpression(h, BlackHoleExpression).pos(h.position), expr).pos(s.position)) else Some(s) - case s@Assignment(SeparateBytesExpression(VariableExpression(h), l), expr) => - if (globalsToRemove(h)) Some(Assignment(SeparateBytesExpression(BlackHoleExpression, l), expr)) + case s@Assignment(SeparateBytesExpression(he@VariableExpression(h), l), expr) => + if (globalsToRemove(h)) Some(Assignment(SeparateBytesExpression(BlackHoleExpression, l).pos(he.position), expr).pos(s.position)) else Some(s) case s: IfStatement => Some(s.copy( thenBranch = removeVariablesFromStatement(s.thenBranch, globalsToRemove).asInstanceOf[List[ExecutableStatement]], - elseBranch = removeVariablesFromStatement(s.elseBranch, globalsToRemove).asInstanceOf[List[ExecutableStatement]])) + elseBranch = removeVariablesFromStatement(s.elseBranch, globalsToRemove).asInstanceOf[List[ExecutableStatement]]).pos(s.position)) case s: WhileStatement => Some(s.copy( - body = removeVariablesFromStatement(s.body, globalsToRemove).asInstanceOf[List[ExecutableStatement]])) + body = removeVariablesFromStatement(s.body, globalsToRemove).asInstanceOf[List[ExecutableStatement]]).pos(s.position)) case s: DoWhileStatement => Some(s.copy( - body = removeVariablesFromStatement(s.body, globalsToRemove).asInstanceOf[List[ExecutableStatement]])) + body = removeVariablesFromStatement(s.body, globalsToRemove).asInstanceOf[List[ExecutableStatement]]).pos(s.position)) case s => Some(s) } diff --git a/src/main/scala/millfork/node/opt/UnusedLocalVariables.scala b/src/main/scala/millfork/node/opt/UnusedLocalVariables.scala index db895795..12a43376 100644 --- a/src/main/scala/millfork/node/opt/UnusedLocalVariables.scala +++ b/src/main/scala/millfork/node/opt/UnusedLocalVariables.scala @@ -80,7 +80,7 @@ object UnusedLocalVariables extends NodeOptimization { params.flatMap { case VariableExpression(_) => None case LiteralExpression(_, _) => None - case x => Some(ExpressionStatement(x)) + case x => Some(ExpressionStatement(x).pos(s.position)) } } else Some(s) case s@Assignment(VariableExpression(n), VariableExpression(_)) => @@ -88,35 +88,52 @@ object UnusedLocalVariables extends NodeOptimization { case s@Assignment(VariableExpression(n), LiteralExpression(_, _)) => if (localsToRemove(n)) Nil else Some(s) case s@Assignment(VariableExpression(n), expr) => - if (localsToRemove(n)) Some(ExpressionStatement(expr)) else Some(s) - case s@Assignment(SeparateBytesExpression(VariableExpression(h), VariableExpression(l)), expr) => + if (localsToRemove(n)) Some(ExpressionStatement(expr).pos(s.position)) else Some(s) + case s@Assignment(SeparateBytesExpression(he@VariableExpression(h), le@VariableExpression(l)), expr) => if (localsToRemove(h)) { if (localsToRemove(l)) - Some(ExpressionStatement(expr)) + Some(ExpressionStatement(expr).pos(s.position)) else - Some(Assignment(SeparateBytesExpression(BlackHoleExpression, VariableExpression(l)), expr)) + Some(Assignment( + SeparateBytesExpression( + BlackHoleExpression, + VariableExpression(l).pos(le.position) + ).pos(he.position), + expr + ).pos(s.position)) } else { if (localsToRemove(l)) - Some(Assignment(SeparateBytesExpression(VariableExpression(h), BlackHoleExpression), expr)) + Some(Assignment( + SeparateBytesExpression( + VariableExpression(h).pos(he.position), + BlackHoleExpression + ).pos(he.position), + expr + ).pos(s.position)) else Some(s) } case s@Assignment(SeparateBytesExpression(h, VariableExpression(l)), expr) => - if (localsToRemove(l)) Some(Assignment(SeparateBytesExpression(h, BlackHoleExpression), expr)) + if (localsToRemove(l)) Some(Assignment( + SeparateBytesExpression(h, BlackHoleExpression).pos(h.position), + expr + ).pos(s.position)) else Some(s) - case s@Assignment(SeparateBytesExpression(VariableExpression(h), l), expr) => - if (localsToRemove(h)) Some(Assignment(SeparateBytesExpression(BlackHoleExpression, l), expr)) + case s@Assignment(SeparateBytesExpression(he@VariableExpression(h), l), expr) => + if (localsToRemove(h)) Some(Assignment( + SeparateBytesExpression(BlackHoleExpression, l).pos(he.position), expr + ).pos(s.position)) else Some(s) case s: IfStatement => Some(s.copy( thenBranch = removeVariables(s.thenBranch, localsToRemove).asInstanceOf[List[ExecutableStatement]], - elseBranch = removeVariables(s.elseBranch, localsToRemove).asInstanceOf[List[ExecutableStatement]])) + elseBranch = removeVariables(s.elseBranch, localsToRemove).asInstanceOf[List[ExecutableStatement]]).pos(s.position)) case s: WhileStatement => Some(s.copy( - body = removeVariables(s.body, localsToRemove).asInstanceOf[List[ExecutableStatement]])) + body = removeVariables(s.body, localsToRemove).asInstanceOf[List[ExecutableStatement]]).pos(s.position)) case s: DoWhileStatement => Some(s.copy( - body = removeVariables(s.body, localsToRemove).asInstanceOf[List[ExecutableStatement]])) + body = removeVariables(s.body, localsToRemove).asInstanceOf[List[ExecutableStatement]]).pos(s.position)) case s => Some(s) } diff --git a/src/main/scala/millfork/output/AbstractAssembler.scala b/src/main/scala/millfork/output/AbstractAssembler.scala index dceb493c..2952ee9b 100644 --- a/src/main/scala/millfork/output/AbstractAssembler.scala +++ b/src/main/scala/millfork/output/AbstractAssembler.scala @@ -482,7 +482,9 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program private def outputFunction(bank: String, code: List[T], startFrom: Int, assOut: mutable.ArrayBuffer[String], options: CompilationOptions): Int = { val printLineNumbers = options.flag(CompilationFlag.LineNumbersInAssembly) + val sourceInAssembly = options.flag(CompilationFlag.SourceInAssembly) var index = startFrom + assOut.append(" ") assOut.append("* = $" + startFrom.toHexString) var lastSource = Option.empty[SourceLine] for (instr <- code) { @@ -491,9 +493,18 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program lastSource = instr.source if (printLineNumbers) { lastSource match { - case Some(SourceLine(moduleName, line)) if line > 0 => + case Some(sl@SourceLine(moduleName, line)) if line > 0 => + if (sourceInAssembly) { + assOut.append("; ") + } assOut.append(s";line:$line:$moduleName") + if (sourceInAssembly) { + log.getLine(sl).foreach(l => assOut.append("; " + l)) + } case _ => + if (sourceInAssembly) { + assOut.append("; ") + } assOut.append(s";line") } } @@ -517,6 +528,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program index = emitInstruction(bank, options, index, instr) } if (printLineNumbers && lastSource.isDefined){ + if (sourceInAssembly) assOut.append("; ") assOut.append(s";line") } index diff --git a/src/main/scala/millfork/output/MosInliningCalculator.scala b/src/main/scala/millfork/output/MosInliningCalculator.scala index 43e792ed..7754e4a2 100644 --- a/src/main/scala/millfork/output/MosInliningCalculator.scala +++ b/src/main/scala/millfork/output/MosInliningCalculator.scala @@ -64,17 +64,25 @@ object MosInliningCalculator extends AbstractInliningCalculator[AssemblyLine] { def inline(code: List[AssemblyLine], inlinedFunctions: Map[String, List[AssemblyLine]], jobContext: JobContext): List[AssemblyLine] = { code.flatMap { - case AssemblyLine(Opcode.JSR, AddrMode.Absolute | AddrMode.LongAbsolute, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) => + case callInstr@AssemblyLine(Opcode.JSR, AddrMode.Absolute | AddrMode.LongAbsolute, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) => val labelPrefix = jobContext.nextLabel("ai") - inlinedFunctions(p.toString).map { + var inlinedCode = inlinedFunctions(p.toString) + if (inlinedCode.forall(_.source.isEmpty)) { + inlinedCode = inlinedCode.map(_.copy(source = callInstr.source)) + } + inlinedCode.map { case line@AssemblyLine0(_, _, MemoryAddressConstant(Label(label))) => val newLabel = MemoryAddressConstant(Label(labelPrefix + label)) line.copy(parameter = newLabel) case l => l } - case AssemblyLine(Opcode.JMP, AddrMode.Absolute, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) => + case callInstr@AssemblyLine(Opcode.JMP, AddrMode.Absolute, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) => val labelPrefix = jobContext.nextLabel("ai") - inlinedFunctions(p.toString).map { + var inlinedCode = inlinedFunctions(p.toString) + if (inlinedCode.forall(_.source.isEmpty)) { + inlinedCode = inlinedCode.map(_.copy(source = callInstr.source)) + } + inlinedCode.map { case line@AssemblyLine0(_, _, MemoryAddressConstant(Label(label))) => val newLabel = MemoryAddressConstant(Label(labelPrefix + label)) line.copy(parameter = newLabel) diff --git a/src/main/scala/millfork/output/Z80InliningCalculator.scala b/src/main/scala/millfork/output/Z80InliningCalculator.scala index e2cc4704..c427faf0 100644 --- a/src/main/scala/millfork/output/Z80InliningCalculator.scala +++ b/src/main/scala/millfork/output/Z80InliningCalculator.scala @@ -56,24 +56,34 @@ object Z80InliningCalculator extends AbstractInliningCalculator[ZLine] { override def inline(code: List[ZLine], inlinedFunctions: Map[String, List[ZLine]], jobContext: JobContext): List[ZLine] = { code.flatMap { - case ZLine(CALL, registers, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) => + case callInstr@ZLine(CALL, registers, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) => val labelPrefix = jobContext.nextLabel("ai") - wrap(registers, jobContext, - inlinedFunctions(p.toString).map { + wrap(registers, jobContext, { + var inlinedCode = inlinedFunctions(p.toString) + if (inlinedCode.forall(_.source.isEmpty)) { + inlinedCode = inlinedCode.map(_.copy(source = callInstr.source)) + } + inlinedCode.map { case line@ZLine0(_, _, MemoryAddressConstant(Label(label))) => val newLabel = MemoryAddressConstant(Label(labelPrefix + label)) line.copy(parameter = newLabel) case l => l - }) - case ZLine(JP | JR, registers, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) => + } + }) + case callInstr@ZLine(JP | JR, registers, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) => val labelPrefix = jobContext.nextLabel("ai") - wrap(registers, jobContext, - inlinedFunctions(p.toString).map { + wrap(registers, jobContext, { + var inlinedCode = inlinedFunctions(p.toString) + if (inlinedCode.forall(_.source.isEmpty)) { + inlinedCode = inlinedCode.map(_.copy(source = callInstr.source)) + } + inlinedCode.map { case line@ZLine0(_, _, MemoryAddressConstant(Label(label))) => val newLabel = MemoryAddressConstant(Label(labelPrefix + label)) line.copy(parameter = newLabel) case l => l - } :+ ZLine.implied(RET)) + } :+ ZLine.implied(RET) + }) case x => List(x) } }