1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-12 06:29:34 +00:00

Optimize the optimizer

This commit is contained in:
Karol Stasiak 2022-02-08 14:42:30 +01:00
parent 7b205a2754
commit 6af84d1628
5 changed files with 135 additions and 105 deletions

View File

@ -30,7 +30,7 @@ object FlowInfoRequirement extends Enumeration {
} }
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match { def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required") case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
case _ => () case _ => ()
} }
} }
@ -43,29 +43,30 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
private val actualRules = rules.flatMap(_.flatten) private val actualRules = rules.flatMap(_.flatten)
actualRules.foreach(_.pattern.validate(needsFlowInfo)) actualRules.foreach(_.pattern.validate(needsFlowInfo))
private val actualRulesWithIndex = actualRules.zipWithIndex
override def optimize(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[MLine] = { override def optimize(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[MLine] = {
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo) val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext) optimizeImpl(f, code, taggedCode, optimizationContext)
if (changed) optimized else code
} }
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 val log = optimizationContext.log
code match { taggedCode match {
case Nil => false -> Nil case Nil => code
case head :: tail => case head :: tail =>
for ((rule, index) <- actualRules.zipWithIndex) { for ((rule, index) <- actualRulesWithIndex) {
val ctx = new AssemblyMatchingContext( val ctx = new AssemblyMatchingContext(
optimizationContext.options, optimizationContext.options,
optimizationContext.labelMap, optimizationContext.labelMap,
optimizationContext.niceFunctionProperties, optimizationContext.niceFunctionProperties,
head._1.labelUseCount(_) head._1.labelUseCount(_)
) )
rule.pattern.matchTo(ctx, code) match { rule.pattern.matchTo(ctx, taggedCode) match {
case Some(rest: List[(FlowInfo, MLine)]) => 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 optimizedChunk: List[MLine] = rule.result(matchedChunkToOptimize, ctx)
val optimizedChunkWithSource = val optimizedChunkWithSource =
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk 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 (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 if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else optimizedChunk else optimizedChunk
log.debug(s"Applied $name ($index)") if (log.debugEnabled) {
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { log.debug(s"Applied $name ($index)")
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.traceEnabled) { 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)) matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
log.trace(" ↓") log.trace(" ↓")
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString)) optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
} }
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2) return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
} else { } else {
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext) return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
} }
case None => () case None => ()
} }
} }
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext) val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
(changedTail, head._2 :: optimizedTail) if (optimizedTail eq code.tail) {
code
} else {
code.head :: optimizedTail
}
} }
} }
} }

View File

@ -8,6 +8,8 @@ import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext
import millfork.env._ import millfork.env._
import millfork.error.ConsoleLogger import millfork.error.ConsoleLogger
import scala.collection.mutable
/** /**
* @author Karol Stasiak * @author Karol Stasiak
*/ */
@ -23,48 +25,52 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] {
case AssemblyLine0(JSR | BSR | JMP, _, NumericConstant(addr, _)) => Some("$" + addr.toHexString) case AssemblyLine0(JSR | BSR | JMP, _, NumericConstant(addr, _)) => Some("$" + addr.toHexString)
case _ => None case _ => None
}.toSet }.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") => case other: NormalFunction if !other.name.endsWith(".trampoline") =>
val address = other.address match { val address = other.address match {
case Some(NumericConstant(addr, _)) => "$" + addr.toHexString case Some(NumericConstant(addr, _)) => "$" + addr.toHexString
case _ => "" case _ => ""
} }
if (other.name == f.name || usedFunctions(other.name) || usedFunctions(address)) { if (other.name == f.name || usedFunctions(other.name) || usedFunctions(address)) {
Nil // do nothing
} else { } else {
val params = other.params match { other.params match {
case NormalParamSignature(ps) => ps.map(_.name) case NormalParamSignature(ps) =>
case _ => Nil ps.foreach(p => foreignVariables += p.name)
case _ =>
} }
val locals = other.environment.things.values.flatMap{ other.environment.things.values.foreach {
case th: MemoryVariable if th.alloc == VariableAllocationMethod.Auto => Some(th.name) case th: MemoryVariable if th.alloc == VariableAllocationMethod.Auto =>
case th: MemoryVariable if th.alloc == VariableAllocationMethod.Zeropage => Some(th.name) // TODO: ??? foreignVariables += th.name
case _ => None 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)) { if (other.returnType.size > Cpu.getMaxSizeReturnableViaRegisters(optimizationContext.options.platform.cpu, optimizationContext.options)) {
other.name + ".return" :: (params ++ locals) foreignVariables += other.name + ".return"
} else {
params ++ locals
} }
} }
case _ => Nil case _ =>
}.toSet }
val stillReadOrStoredVariables = code.flatMap { val stillReadOrStoredVariables = mutable.Set[String]()
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name) code.foreach {
case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name) case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillReadOrStoredVariables += th.name
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name) case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => stillReadOrStoredVariables += th.name
case _ => None case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => stillReadOrStoredVariables += th.name
}.toSet case _ =>
val stillReadVariables = code.flatMap { }
val stillReadVariables = mutable.Set[String]()
code.foreach {
case AssemblyLine(op, am, MemoryAddressConstant(th), Elidability.Elidable, _) 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, _) case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), Elidability.Elidable, _)
if storeInstructions(op) && storeAddrModes(am) => Nil if storeInstructions(op) && storeAddrModes(am) =>
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name) case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillReadVariables += th.name
case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name) case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => stillReadVariables += th.name
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name) case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => stillReadVariables += th.name
case _ => None case _ =>
}.toSet }
val unusedForeignVariables = (foreignVariables & stillReadOrStoredVariables) -- stillReadVariables val unusedForeignVariables = (foreignVariables & stillReadOrStoredVariables) -- stillReadVariables
if (unusedForeignVariables.isEmpty) { if (unusedForeignVariables.isEmpty) {

View File

@ -30,7 +30,7 @@ object FlowInfoRequirement extends Enumeration {
} }
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match { def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required") case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
case _ => () case _ => ()
} }
} }
@ -43,20 +43,20 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
private val actualRules = rules.flatMap(_.flatten) private val actualRules = rules.flatMap(_.flatten)
actualRules.foreach(_.pattern.validate(needsFlowInfo)) actualRules.foreach(_.pattern.validate(needsFlowInfo))
private val actualRulesWithIndex = actualRules.zipWithIndex
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = { override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo) val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext) optimizeImpl(f, code, taggedCode, optimizationContext)
if (changed) optimized else code
} }
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 val log = optimizationContext.log
code match { taggedCode match {
case Nil => false -> Nil case Nil => code
case head :: tail => case head :: tail =>
for ((rule, index) <- actualRules.zipWithIndex) { for ((rule, index) <- actualRulesWithIndex) {
val ctx = new AssemblyMatchingContext( val ctx = new AssemblyMatchingContext(
optimizationContext.options, optimizationContext.options,
optimizationContext.labelMap, optimizationContext.labelMap,
@ -64,9 +64,10 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
optimizationContext.niceFunctionProperties, optimizationContext.niceFunctionProperties,
head._1.labelUseCount(_) head._1.labelUseCount(_)
) )
rule.pattern.matchTo(ctx, code) match { rule.pattern.matchTo(ctx, taggedCode) match {
case Some(rest: List[(FlowInfo, AssemblyLine)]) => 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 optimizedChunk: List[AssemblyLine] = rule.result(matchedChunkToOptimize, ctx)
val optimizedChunkWithSource = val optimizedChunkWithSource =
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk 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 (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 if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else optimizedChunk else optimizedChunk
log.debug(s"Applied $name ($index)") if (log.debugEnabled) {
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { log.debug(s"Applied $name ($index)")
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.traceEnabled) { 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)) matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
log.trace(" ↓") log.trace(" ↓")
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString)) optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
} }
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2) return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
} else { } else {
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext) return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
} }
case None => () case None => ()
} }
} }
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext) val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
(changedTail, head._2 :: optimizedTail) if (optimizedTail eq code.tail) {
code
} else {
code.head :: optimizedTail
}
} }
} }
} }

View File

@ -9,6 +9,7 @@ import millfork.env._
import millfork.error.Logger import millfork.error.Logger
import millfork.node.MosNiceFunctionProperty import millfork.node.MosNiceFunctionProperty
import scala.collection.mutable
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
import scala.util.control.TailCalls.{TailRec, done, tailcall} import scala.util.control.TailCalls.{TailRec, done, tailcall}
@ -147,24 +148,26 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
// assembly functions do not get this optimization // assembly functions do not get this optimization
return code return code
} }
val stillUsedVariables = code.flatMap { val stillUsedVariables = mutable.Set[String]()
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name) val variablesWithAddressesTaken = mutable.Set[String]()
code.foreach {
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillUsedVariables += th.name
case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => variablesWithAddressesTaken += th.name
case _ => None case _ => None
}.toSet }
val variablesWithAddressesTaken = code.flatMap { val localVariables = mutable.Set[Variable]()
case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name) val variablesWithRegisterHint = mutable.Set[String]()
case _ => None
}.toSet f.environment.getAllLocalVariables.foreach {
val localVariables = f.environment.getAllLocalVariables.filter { case v@MemoryVariable(name, typ, alloc@(VariableAllocationMethod.Auto | VariableAllocationMethod.Register)) =>
case v@MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Register) => if (typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile){
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile localVariables += v
if (alloc == VariableAllocationMethod.Register) {
variablesWithRegisterHint += v.name
}
}
case _ => false 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 => val variablesWithLifetimes = localVariables.map(v =>
v.name -> VariableLifetime.apply(v.name, code) v.name -> VariableLifetime.apply(v.name, code)
@ -187,10 +190,12 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
log = log log = log
) )
val labelsUsedOnce: Set[String] = code.flatMap { val tmpUsedLabelList = mutable.ListBuffer[String]()
case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if op != Opcode.LABEL => Some(l) code.foreach{
case _ => None case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if op != Opcode.LABEL => tmpUsedLabelList += l
}.groupBy(identity).filter(_._2.size == 1).keySet case _ =>
}
val labelsUsedOnce: Set[String] = tmpUsedLabelList.groupBy(identity).filter(_._2.size == 1).keySet
val featuresForAcc = FeaturesForAccumulator( val featuresForAcc = FeaturesForAccumulator(
cmos = options.flag(CompilationFlag.EmitCmosOpcodes), cmos = options.flag(CompilationFlag.EmitCmosOpcodes),

View File

@ -30,7 +30,7 @@ object FlowInfoRequirement extends Enumeration {
} }
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match { def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required") case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
case _ => () case _ => ()
} }
} }
@ -43,23 +43,24 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
private val actualRules = rules.flatMap(_.flatten) private val actualRules = rules.flatMap(_.flatten)
actualRules.foreach(_.pattern.validate(needsFlowInfo)) actualRules.foreach(_.pattern.validate(needsFlowInfo))
private val actualRulesWithIndex = actualRules.zipWithIndex
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = { override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo) val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext) optimizeImpl(f, code, taggedCode, optimizationContext)
if (changed) optimized else code
} }
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 val log = optimizationContext.log
code match { taggedCode match {
case Nil => (false, Nil) case Nil => code
case head :: tail => case head :: tail =>
for ((rule, index) <- actualRules.zipWithIndex) { for ((rule, index) <- actualRulesWithIndex) {
val ctx = new AssemblyMatchingContext(optimizationContext.options) val ctx = new AssemblyMatchingContext(optimizationContext.options)
rule.pattern.matchTo(ctx, code) match { rule.pattern.matchTo(ctx, taggedCode) match {
case Some(rest: List[(FlowInfo, ZLine)]) => 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 optimizedChunk: List[ZLine] = rule.result(matchedChunkToOptimize, ctx)
val optimizedChunkWithSource = val optimizedChunkWithSource =
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk 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 (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 if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else optimizedChunk else optimizedChunk
log.debug(s"Applied $name ($index)") if (log.debugEnabled) {
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { log.debug(s"Applied $name ($index)")
val before = code.head._1.statusBefore }
val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter if (log.traceEnabled) {
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"Before: $before")
log.trace(s"After: $after") log.trace(s"After: $after")
} }
}
if (log.traceEnabled) {
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString)) matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
log.trace(" ↓") log.trace(" ↓")
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString)) optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
} }
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2) return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
} else { } else {
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext) return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
} }
case None => () case None => ()
} }
} }
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext) val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
(changedTail, head._2 :: optimizedTail) if (optimizedTail eq code.tail) {
code
} else {
code.head :: optimizedTail
}
} }
} }
} }