diff --git a/src/main/scala/millfork/OptimizationPresets.scala b/src/main/scala/millfork/OptimizationPresets.scala index a8616754..ed437c0b 100644 --- a/src/main/scala/millfork/OptimizationPresets.scala +++ b/src/main/scala/millfork/OptimizationPresets.scala @@ -46,6 +46,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.PointlessOperationAfterLoad, AlwaysGoodOptimizations.IdempotentDuplicateRemoval, LoopUnrolling.LoopUnrolling, + AlwaysGoodOptimizations.ConstantPointer, AlwaysGoodOptimizations.ConstantIndexPropagation, AlwaysGoodOptimizations.PointlessLoadBeforeReturn, AlwaysGoodOptimizations.PoinlessFlagChange, @@ -67,9 +68,11 @@ object OptimizationPresets { LaterOptimizations.DoubleLoadToTheSameRegister, LaterOptimizations.DoubleLoadToTwoRegistersWhenOneWillBeTrashed, EmptyMemoryStoreRemoval, + EmptyParameterStoreRemoval, AlwaysGoodOptimizations.PointlessOperationFromFlow, AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad, AlwaysGoodOptimizations.IdempotentDuplicateRemoval, + AlwaysGoodOptimizations.ConstantPointer, AlwaysGoodOptimizations.ConstantIndexPropagation, AlwaysGoodOptimizations.ConstantFlowAnalysis, AlwaysGoodOptimizations.PointlessRegisterTransfers, @@ -86,6 +89,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.RearrangeMath, AlwaysGoodOptimizations.LoadingOfJustWrittenValue, EmptyMemoryStoreRemoval, + EmptyParameterStoreRemoval, AlwaysGoodOptimizations.PointlessLoadBeforeReturn, LaterOptimizations.PointessLoadingForShifting, AlwaysGoodOptimizations.SimplifiableBitOpsSequence, @@ -95,6 +99,7 @@ object OptimizationPresets { LaterOptimizations.LoadingAfterShifting, EmptyMemoryStoreRemoval, + EmptyParameterStoreRemoval, AlwaysGoodOptimizations.PoinlessStoreBeforeStore, LaterOptimizations.PointlessLoadAfterStore, AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad, @@ -105,6 +110,7 @@ object OptimizationPresets { LaterOptimizations.LoadingAfterShifting, EmptyMemoryStoreRemoval, + EmptyParameterStoreRemoval, AlwaysGoodOptimizations.PoinlessStoreBeforeStore, LaterOptimizations.PointlessLoadAfterStore, AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad, @@ -112,7 +118,9 @@ object OptimizationPresets { LaterOptimizations.LoadingAfterShifting, AlwaysGoodOptimizations.PointlessAccumulatorShifting, EmptyMemoryStoreRemoval, + EmptyParameterStoreRemoval, AlwaysGoodOptimizations.PoinlessStoreBeforeStore, + AlwaysGoodOptimizations.ConstantPointer, LaterOptimizations.PointlessLoadAfterStore, AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad, AlwaysGoodOptimizations.TailCallOptimization, @@ -135,6 +143,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.PointlessStackStore, AlwaysGoodOptimizations.OptimizeZeroComparisons, AlwaysGoodOptimizations.SimplifiableCondition, + AlwaysGoodOptimizations.ConstantPointer, AlwaysGoodOptimizations.IncrementingIndexRegistersAfterTransfer, AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands, LaterOptimizations.UseZeropageAddressingMode, @@ -167,6 +176,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.ConstantIndexPropagation, AlwaysGoodOptimizations.DoubleJumpSimplification, EmptyMemoryStoreRemoval, + EmptyParameterStoreRemoval, AlwaysGoodOptimizations.FlagFlowAnalysis, AlwaysGoodOptimizations.IdempotentDuplicateRemoval, AlwaysGoodOptimizations.ImpossibleBranchRemoval, diff --git a/src/main/scala/millfork/assembly/mos/opt/EmptyParameterStoreRemoval.scala b/src/main/scala/millfork/assembly/mos/opt/EmptyParameterStoreRemoval.scala new file mode 100644 index 00000000..d0b4385f --- /dev/null +++ b/src/main/scala/millfork/assembly/mos/opt/EmptyParameterStoreRemoval.scala @@ -0,0 +1,80 @@ +package millfork.assembly.mos.opt + +import millfork.assembly.mos.AddrMode._ +import millfork.assembly.mos.AssemblyLine +import millfork.assembly.mos.Opcode._ +import millfork.assembly.{AssemblyOptimization, OptimizationContext} +import millfork.env._ +import millfork.error.ErrorReporting + +/** + * @author Karol Stasiak + */ +object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] { + override def name = "Removing pointless stores to foreign variables" + + private val storeInstructions = Set(STA, STX, STY, SAX, STZ, STA_W, STX_W, STY_W, STZ_W) + private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY) + + override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = { + val usedFunctions = code.flatMap { + case AssemblyLine(JSR | BSR | JMP, _, MemoryAddressConstant(th), _) => Some(th.name) + case AssemblyLine(JSR | BSR | JMP, _, NumericConstant(addr, _), _) => Some("$" + addr.toHexString) + case _ => None + }.toSet + val foreignVariables = f.environment.root.things.values.flatMap { + case other: NormalFunction => + val address = other.address match { + case Some(NumericConstant(addr, _)) => "$" + addr.toHexString + case _ => "" + } + if (other.name == f.name || usedFunctions(other.name) || usedFunctions(address)) { + Nil + } else { + val params = other.params match { + case NormalParamSignature(ps) => ps.map(_.name) + case _ => Nil + } + 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 + } + params ++ locals + } + case _ => Nil + }.toSet + val stillReadOrStoredVariables = code.flatMap { + case AssemblyLine(_, _, MemoryAddressConstant(th), _) => Some(th.name) + case AssemblyLine(_, _, CompoundConstant(_, MemoryAddressConstant(th), _), _) => Some(th.name) + case AssemblyLine(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name) + case _ => None + }.toSet + val stillReadVariables = code.flatMap { + case AssemblyLine(op, am, MemoryAddressConstant(th), true) + if storeInstructions(op) && storeAddrModes(am) => Nil + case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), true) + if storeInstructions(op) && storeAddrModes(am) => Nil + case AssemblyLine(_, _, MemoryAddressConstant(th), _) => Some(th.name) + case AssemblyLine(_, _, CompoundConstant(_, MemoryAddressConstant(th), _), _) => Some(th.name) + case AssemblyLine(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name) + case _ => None + }.toSet + + val unusedForeignVariables = (foreignVariables & stillReadOrStoredVariables) -- stillReadVariables + if (unusedForeignVariables.isEmpty) { + return code + } + + ErrorReporting.debug(s"Removing pointless store(s) to foreign variables ${unusedForeignVariables.mkString(", ")}") + code.filterNot { + case AssemblyLine(op, am, MemoryAddressConstant(th), _) + if storeInstructions(op) && storeAddrModes(am) => + unusedForeignVariables(th.name) + case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), true) + if storeInstructions(op) && storeAddrModes(am) => + unusedForeignVariables(th.name) + case _ => false + } + } +}