diff --git a/src/main/scala/millfork/assembly/m6809/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/m6809/opt/RuleBasedAssemblyOptimization.scala index 8e4be81d..cf1da515 100644 --- a/src/main/scala/millfork/assembly/m6809/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/m6809/opt/RuleBasedAssemblyOptimization.scala @@ -30,7 +30,7 @@ object FlowInfoRequirement extends Enumeration { } def assertLabels(x: FlowInfoRequirement.Value): Unit = x match { - case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required") + case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required") case _ => () } } @@ -43,29 +43,30 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf private val actualRules = rules.flatMap(_.flatten) actualRules.foreach(_.pattern.validate(needsFlowInfo)) + private val actualRulesWithIndex = actualRules.zipWithIndex override def optimize(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[MLine] = { val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo) - val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext) - if (changed) optimized else code + optimizeImpl(f, code, taggedCode, optimizationContext) } - def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, MLine)], optimizationContext: OptimizationContext): (Boolean, List[MLine]) = { + final def optimizeImpl(f: NormalFunction, code: List[MLine], taggedCode: List[(FlowInfo, MLine)], optimizationContext: OptimizationContext): List[MLine] = { val log = optimizationContext.log - code match { - case Nil => false -> Nil + taggedCode match { + case Nil => code case head :: tail => - for ((rule, index) <- actualRules.zipWithIndex) { + for ((rule, index) <- actualRulesWithIndex) { val ctx = new AssemblyMatchingContext( optimizationContext.options, optimizationContext.labelMap, optimizationContext.niceFunctionProperties, head._1.labelUseCount(_) ) - rule.pattern.matchTo(ctx, code) match { + rule.pattern.matchTo(ctx, taggedCode) match { case Some(rest: List[(FlowInfo, MLine)]) => - val matchedChunkToOptimize: List[MLine] = code.take(code.length - rest.length).map(_._2) + val optimizedChunkLengthBefore = taggedCode.length - rest.length + val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore) val optimizedChunk: List[MLine] = rule.result(matchedChunkToOptimize, ctx) val optimizedChunkWithSource = if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk @@ -74,28 +75,34 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source)))) else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source)))) else optimizedChunk - log.debug(s"Applied $name ($index)") - if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { - val before = code.head._1.statusBefore - val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter - log.trace(s"Before: $before") - log.trace(s"After: $after") + if (log.debugEnabled) { + log.debug(s"Applied $name ($index)") } if (log.traceEnabled) { + if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { + val before = head._1.statusBefore + val after = taggedCode(matchedChunkToOptimize.length - 1)._1.importanceAfter + log.trace(s"Before: $before") + log.trace(s"After: $after") + } matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString)) log.trace(" ↓") optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString)) } if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { - return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2) + return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext) } else { - return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext) + return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext) } case None => () } } - val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext) - (changedTail, head._2 :: optimizedTail) + val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext) + if (optimizedTail eq code.tail) { + code + } else { + code.head :: optimizedTail + } } } } diff --git a/src/main/scala/millfork/assembly/mos/opt/EmptyParameterStoreRemoval.scala b/src/main/scala/millfork/assembly/mos/opt/EmptyParameterStoreRemoval.scala index 9a9987d5..39d5efdf 100644 --- a/src/main/scala/millfork/assembly/mos/opt/EmptyParameterStoreRemoval.scala +++ b/src/main/scala/millfork/assembly/mos/opt/EmptyParameterStoreRemoval.scala @@ -8,6 +8,8 @@ import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext import millfork.env._ import millfork.error.ConsoleLogger +import scala.collection.mutable + /** * @author Karol Stasiak */ @@ -23,48 +25,52 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] { case AssemblyLine0(JSR | BSR | JMP, _, NumericConstant(addr, _)) => Some("$" + addr.toHexString) case _ => None }.toSet - val foreignVariables = f.environment.root.things.values.flatMap { + val foreignVariables = mutable.Set[String]() + f.environment.root.things.values.foreach { case other: NormalFunction if !other.name.endsWith(".trampoline") => val address = other.address match { case Some(NumericConstant(addr, _)) => "$" + addr.toHexString case _ => "" } if (other.name == f.name || usedFunctions(other.name) || usedFunctions(address)) { - Nil + // do nothing } else { - val params = other.params match { - case NormalParamSignature(ps) => ps.map(_.name) - case _ => Nil + other.params match { + case NormalParamSignature(ps) => + ps.foreach(p => foreignVariables += p.name) + case _ => } - val locals = other.environment.things.values.flatMap{ - case th: MemoryVariable if th.alloc == VariableAllocationMethod.Auto => Some(th.name) - case th: MemoryVariable if th.alloc == VariableAllocationMethod.Zeropage => Some(th.name) // TODO: ??? - case _ => None + other.environment.things.values.foreach { + case th: MemoryVariable if th.alloc == VariableAllocationMethod.Auto => + foreignVariables += th.name + case th: MemoryVariable if th.alloc == VariableAllocationMethod.Zeropage => + foreignVariables += th.name // TODO: ??? + case _ => } if (other.returnType.size > Cpu.getMaxSizeReturnableViaRegisters(optimizationContext.options.platform.cpu, optimizationContext.options)) { - other.name + ".return" :: (params ++ locals) - } else { - params ++ locals + foreignVariables += other.name + ".return" } } - case _ => Nil - }.toSet - val stillReadOrStoredVariables = code.flatMap { - case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name) - case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name) - case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name) - case _ => None - }.toSet - val stillReadVariables = code.flatMap { + case _ => + } + val stillReadOrStoredVariables = mutable.Set[String]() + code.foreach { + case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillReadOrStoredVariables += th.name + case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => stillReadOrStoredVariables += th.name + case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => stillReadOrStoredVariables += th.name + case _ => + } + val stillReadVariables = mutable.Set[String]() + code.foreach { case AssemblyLine(op, am, MemoryAddressConstant(th), Elidability.Elidable, _) - if storeInstructions(op) && storeAddrModes(am) => Nil + if storeInstructions(op) && storeAddrModes(am) => case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), Elidability.Elidable, _) - if storeInstructions(op) && storeAddrModes(am) => Nil - case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name) - case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name) - case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name) - case _ => None - }.toSet + if storeInstructions(op) && storeAddrModes(am) => + case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillReadVariables += th.name + case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => stillReadVariables += th.name + case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => stillReadVariables += th.name + case _ => + } val unusedForeignVariables = (foreignVariables & stillReadOrStoredVariables) -- stillReadVariables if (unusedForeignVariables.isEmpty) { diff --git a/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala index 26836175..75a991a1 100644 --- a/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala @@ -30,7 +30,7 @@ object FlowInfoRequirement extends Enumeration { } def assertLabels(x: FlowInfoRequirement.Value): Unit = x match { - case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required") + case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required") case _ => () } } @@ -43,20 +43,20 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf private val actualRules = rules.flatMap(_.flatten) actualRules.foreach(_.pattern.validate(needsFlowInfo)) + private val actualRulesWithIndex = actualRules.zipWithIndex override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = { val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo) - val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext) - if (changed) optimized else code + optimizeImpl(f, code, taggedCode, optimizationContext) } - def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, AssemblyLine)], optimizationContext: OptimizationContext): (Boolean, List[AssemblyLine]) = { + final def optimizeImpl(f: NormalFunction, code: List[AssemblyLine], taggedCode: List[(FlowInfo, AssemblyLine)], optimizationContext: OptimizationContext): List[AssemblyLine] = { val log = optimizationContext.log - code match { - case Nil => false -> Nil + taggedCode match { + case Nil => code case head :: tail => - for ((rule, index) <- actualRules.zipWithIndex) { + for ((rule, index) <- actualRulesWithIndex) { val ctx = new AssemblyMatchingContext( optimizationContext.options, optimizationContext.labelMap, @@ -64,9 +64,10 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf optimizationContext.niceFunctionProperties, head._1.labelUseCount(_) ) - rule.pattern.matchTo(ctx, code) match { + rule.pattern.matchTo(ctx, taggedCode) match { case Some(rest: List[(FlowInfo, AssemblyLine)]) => - val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2) + val optimizedChunkLengthBefore = taggedCode.length - rest.length + val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore) val optimizedChunk: List[AssemblyLine] = rule.result(matchedChunkToOptimize, ctx) val optimizedChunkWithSource = if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk @@ -75,28 +76,34 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source)))) else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source)))) else optimizedChunk - log.debug(s"Applied $name ($index)") - if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { - val before = code.head._1.statusBefore - val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter - log.trace(s"Before: $before") - log.trace(s"After: $after") + if (log.debugEnabled) { + log.debug(s"Applied $name ($index)") } if (log.traceEnabled) { + if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { + val before = head._1.statusBefore + val after = taggedCode(matchedChunkToOptimize.length - 1)._1.importanceAfter + log.trace(s"Before: $before") + log.trace(s"After: $after") + } matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString)) log.trace(" ↓") optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString)) } if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { - return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2) + return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext) } else { - return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext) + return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext) } case None => () } } - val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext) - (changedTail, head._2 :: optimizedTail) + val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext) + if (optimizedTail eq code.tail) { + code + } else { + code.head :: optimizedTail + } } } } diff --git a/src/main/scala/millfork/assembly/mos/opt/VariableToRegisterOptimization.scala b/src/main/scala/millfork/assembly/mos/opt/VariableToRegisterOptimization.scala index c71b9a38..07d05c6f 100644 --- a/src/main/scala/millfork/assembly/mos/opt/VariableToRegisterOptimization.scala +++ b/src/main/scala/millfork/assembly/mos/opt/VariableToRegisterOptimization.scala @@ -9,6 +9,7 @@ import millfork.env._ import millfork.error.Logger import millfork.node.MosNiceFunctionProperty +import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.util.control.TailCalls.{TailRec, done, tailcall} @@ -147,24 +148,26 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] // assembly functions do not get this optimization return code } - val stillUsedVariables = code.flatMap { - case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name) + val stillUsedVariables = mutable.Set[String]() + val variablesWithAddressesTaken = mutable.Set[String]() + code.foreach { + case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillUsedVariables += th.name + case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => variablesWithAddressesTaken += th.name case _ => None - }.toSet - val variablesWithAddressesTaken = code.flatMap { - case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name) - case _ => None - }.toSet - val localVariables = f.environment.getAllLocalVariables.filter { - case v@MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Register) => - typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile + } + val localVariables = mutable.Set[Variable]() + val variablesWithRegisterHint = mutable.Set[String]() + + f.environment.getAllLocalVariables.foreach { + case v@MemoryVariable(name, typ, alloc@(VariableAllocationMethod.Auto | VariableAllocationMethod.Register)) => + if (typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile){ + localVariables += v + if (alloc == VariableAllocationMethod.Register) { + variablesWithRegisterHint += v.name + } + } case _ => false } - val variablesWithRegisterHint = f.environment.getAllLocalVariables.filter { - case v@MemoryVariable(name, typ, VariableAllocationMethod.Register) => - typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile - case _ => false - }.map(_.name).toSet val variablesWithLifetimes = localVariables.map(v => v.name -> VariableLifetime.apply(v.name, code) @@ -187,10 +190,12 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] log = log ) - val labelsUsedOnce: Set[String] = code.flatMap { - case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if op != Opcode.LABEL => Some(l) - case _ => None - }.groupBy(identity).filter(_._2.size == 1).keySet + val tmpUsedLabelList = mutable.ListBuffer[String]() + code.foreach{ + case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if op != Opcode.LABEL => tmpUsedLabelList += l + case _ => + } + val labelsUsedOnce: Set[String] = tmpUsedLabelList.groupBy(identity).filter(_._2.size == 1).keySet val featuresForAcc = FeaturesForAccumulator( cmos = options.flag(CompilationFlag.EmitCmosOpcodes), diff --git a/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala index 3283b960..80116b84 100644 --- a/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala @@ -30,7 +30,7 @@ object FlowInfoRequirement extends Enumeration { } def assertLabels(x: FlowInfoRequirement.Value): Unit = x match { - case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required") + case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required") case _ => () } } @@ -43,23 +43,24 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf private val actualRules = rules.flatMap(_.flatten) actualRules.foreach(_.pattern.validate(needsFlowInfo)) + private val actualRulesWithIndex = actualRules.zipWithIndex override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = { val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo) - val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext) - if (changed) optimized else code + optimizeImpl(f, code, taggedCode, optimizationContext) } - def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, ZLine)], optimizationContext: OptimizationContext): (Boolean, List[ZLine]) = { + final def optimizeImpl(f: NormalFunction, code:List[ZLine], taggedCode: List[(FlowInfo, ZLine)], optimizationContext: OptimizationContext): List[ZLine] = { val log = optimizationContext.log - code match { - case Nil => (false, Nil) + taggedCode match { + case Nil => code case head :: tail => - for ((rule, index) <- actualRules.zipWithIndex) { + for ((rule, index) <- actualRulesWithIndex) { val ctx = new AssemblyMatchingContext(optimizationContext.options) - rule.pattern.matchTo(ctx, code) match { + rule.pattern.matchTo(ctx, taggedCode) match { case Some(rest: List[(FlowInfo, ZLine)]) => - val matchedChunkToOptimize: List[ZLine] = code.take(code.length - rest.length).map(_._2) + val optimizedChunkLengthBefore = taggedCode.length - rest.length + val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore) val optimizedChunk: List[ZLine] = rule.result(matchedChunkToOptimize, ctx) val optimizedChunkWithSource = if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk @@ -68,30 +69,34 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source)))) else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source)))) else optimizedChunk - log.debug(s"Applied $name ($index)") - if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { - val before = code.head._1.statusBefore - val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter - if (log.traceEnabled) { + if (log.debugEnabled) { + log.debug(s"Applied $name ($index)") + } + if (log.traceEnabled) { + if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { + val before = head._1.statusBefore + val after = taggedCode(matchedChunkToOptimize.length - 1)._1.importanceAfter log.trace(s"Before: $before") log.trace(s"After: $after") } - } - if (log.traceEnabled) { matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString)) log.trace(" ↓") optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString)) } if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { - return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2) + return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext) } else { - return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext) + return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext) } case None => () } } - val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext) - (changedTail, head._2 :: optimizedTail) + val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext) + if (optimizedTail eq code.tail) { + code + } else { + code.head :: optimizedTail + } } } }