1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-12-23 23:30:22 +00:00

Many optimization improvements and bugfixes

- fixed VariableToRegisterOptimization removing variables during superoptimization
- fixed PointlessMathFromFlow giving results that do not fit a byte
- fixed PointlessLoadBeforeReturn moving reads from before to after memory modification
- achieved and exceeded CC65 performance when doing 16-bit Eratosthenes sieve
This commit is contained in:
Karol Stasiak 2018-03-28 12:32:26 +02:00
parent cb3d848d0a
commit d785d43ae7
13 changed files with 316 additions and 47 deletions

View File

@ -8,6 +8,8 @@
* Fixed several bugs, most importantly invalid offsets for branching instructions. * Fixed several bugs, most importantly invalid offsets for branching instructions.
* Other improvements.
## 0.2.2 ## 0.2.2
* Allowed adding constant words to variable bytes without the zeropage pseudoregister. * Allowed adding constant words to variable bytes without the zeropage pseudoregister.

View File

@ -143,7 +143,9 @@ object CompilationFlag extends Enumeration {
// warning options // warning options
ExtraComparisonWarnings, ExtraComparisonWarnings,
RorWarning, RorWarning,
FatalWarnings = Value FatalWarnings,
// special options for internal compiler use
InternalCurrentlyOptimizingForMeasurement = Value
val allWarnings: Set[CompilationFlag.Value] = Set(ExtraComparisonWarnings) val allWarnings: Set[CompilationFlag.Value] = Set(ExtraComparisonWarnings)

View File

@ -76,6 +76,8 @@ object OptimizationPresets {
AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch, AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch,
AlwaysGoodOptimizations.LoopInvariantRegister, AlwaysGoodOptimizations.LoopInvariantRegister,
AlwaysGoodOptimizations.PointlessStackStashing, AlwaysGoodOptimizations.PointlessStackStashing,
AlwaysGoodOptimizations.PointlessStashingForLaterStore,
AlwaysGoodOptimizations.PointlessStashingForLaterLoad,
AlwaysGoodOptimizations.RearrangeMath, AlwaysGoodOptimizations.RearrangeMath,
EmptyMemoryStoreRemoval, EmptyMemoryStoreRemoval,
AlwaysGoodOptimizations.PointlessLoadBeforeReturn, AlwaysGoodOptimizations.PointlessLoadBeforeReturn,
@ -91,6 +93,7 @@ object OptimizationPresets {
LaterOptimizations.PointlessLoadAfterStore, LaterOptimizations.PointlessLoadAfterStore,
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad, AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
AlwaysGoodOptimizations.RearrangableLoadFromTheSameLocation, AlwaysGoodOptimizations.RearrangableLoadFromTheSameLocation,
AlwaysGoodOptimizations.ShiftingJustWrittenValue,
LaterOptimizations.LoadingAfterShifting, LaterOptimizations.LoadingAfterShifting,
EmptyMemoryStoreRemoval, EmptyMemoryStoreRemoval,
@ -173,6 +176,8 @@ object OptimizationPresets {
AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn, AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn,
AlwaysGoodOptimizations.PointlessSignCheck, AlwaysGoodOptimizations.PointlessSignCheck,
AlwaysGoodOptimizations.PointlessStackStashing, AlwaysGoodOptimizations.PointlessStackStashing,
AlwaysGoodOptimizations.PointlessStashingForLaterLoad,
AlwaysGoodOptimizations.PointlessStashingForLaterStore,
AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch, AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch,
AlwaysGoodOptimizations.PoinlessStoreBeforeStore, AlwaysGoodOptimizations.PoinlessStoreBeforeStore,
AlwaysGoodOptimizations.PointlessStoreToTheSameVariable, AlwaysGoodOptimizations.PointlessStoreToTheSameVariable,
@ -180,6 +185,7 @@ object OptimizationPresets {
AlwaysGoodOptimizations.RearrangeMath, AlwaysGoodOptimizations.RearrangeMath,
AlwaysGoodOptimizations.RemoveNops, AlwaysGoodOptimizations.RemoveNops,
AlwaysGoodOptimizations.ReverseFlowAnalysis, AlwaysGoodOptimizations.ReverseFlowAnalysis,
AlwaysGoodOptimizations.ShiftingJustWrittenValue,
AlwaysGoodOptimizations.SimplifiableBitOpsSequence, AlwaysGoodOptimizations.SimplifiableBitOpsSequence,
AlwaysGoodOptimizations.SimplifiableCondition, AlwaysGoodOptimizations.SimplifiableCondition,
AlwaysGoodOptimizations.SimplifiableIndexChanging, AlwaysGoodOptimizations.SimplifiableIndexChanging,

View File

@ -170,6 +170,9 @@ object OpcodeClasses {
val AccessesWordInMemoryAlwaysIfNotImplied = Set( val AccessesWordInMemoryAlwaysIfNotImplied = Set(
) )
val StoresByte = Set(STA, STX, STY, STZ, SAX)
val StoresWord = Set(STA_W, STX_W, STY_W, STZ_W)
val OverwritesA = Set( val OverwritesA = Set(
LDA, PLA, LDA, PLA,
LDA_W, PLA_W, LDA_W, PLA_W,

View File

@ -57,7 +57,7 @@ object AlwaysGoodOptimizations {
}, },
(Elidable & MatchA(0) & (Elidable & MatchA(0) &
HasSet(State.C) & HasOpcode(ROL) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) => HasSet(State.C) & HasOpcode(ROL) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) =>
AssemblyLine.immediate(LDA, ctx.get[Int](0) * 2 + 1) :: Nil AssemblyLine.immediate(LDA, (ctx.get[Int](0) * 2 + 1) & 0xff) :: Nil
}, },
(Elidable & MatchA(0) & (Elidable & MatchA(0) &
HasSet(State.C) & HasOpcode(ROR) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) => HasSet(State.C) & HasOpcode(ROR) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) =>
@ -67,7 +67,7 @@ object AlwaysGoodOptimizations {
MatchA(0) & MatchParameter(1) & MatchA(0) & MatchParameter(1) &
HasOpcode(ADC) & HasAddrMode(Immediate) & HasOpcode(ADC) & HasAddrMode(Immediate) &
HasClear(State.D) & HasClear(State.C) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> { (code, ctx) => HasClear(State.D) & HasClear(State.C) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> { (code, ctx) =>
AssemblyLine.immediate(LDA, ctx.get[Constant](1) + ctx.get[Int](0)) :: Nil AssemblyLine.immediate(LDA, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify.loByte) :: Nil
}, },
(Elidable & (Elidable &
MatchA(0) & MatchParameter(1) & MatchA(0) & MatchParameter(1) &
@ -77,13 +77,13 @@ object AlwaysGoodOptimizations {
case NumericConstant(x, _) => x == (x & 0xff) case NumericConstant(x, _) => x == (x & 0xff)
case _ => false case _ => false
}) ~~> { (code, ctx) => }) ~~> { (code, ctx) =>
AssemblyLine.immediate(LDA, ctx.get[Constant](1) + ctx.get[Int](0)) :: Nil AssemblyLine.immediate(LDA, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify.loByte) :: Nil
}, },
(Elidable & (Elidable &
MatchA(0) & MatchParameter(1) & MatchA(0) & MatchParameter(1) &
HasOpcode(ADC) & HasAddrMode(Immediate) & HasOpcode(ADC) & HasAddrMode(Immediate) &
HasClear(State.D) & HasSet(State.C) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> { (code, ctx) => HasClear(State.D) & HasSet(State.C) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> { (code, ctx) =>
AssemblyLine.immediate(LDA, ctx.get[Constant](1) + ((ctx.get[Int](0) + 1) & 0xff)) :: Nil AssemblyLine.immediate(LDA, (ctx.get[Constant](1) + (ctx.get[Int](0) + 1)).quickSimplify.loByte) :: Nil
}, },
(Elidable & (Elidable &
MatchA(0) & MatchParameter(1) & MatchA(0) & MatchParameter(1) &
@ -207,20 +207,117 @@ object AlwaysGoodOptimizations {
) )
val PointlessStashingForLaterStore = new RuleBasedAssemblyOptimization("Pointless stashing for later store",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
// LDA/TAX/TAY/TAZ will be cleaned by something else
(HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).*.capture(5) ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(STA) & HasAddrModeIn(Set(ZeroPage, Absolute, LongAbsolute)) & DoesNotConcernMemoryAt(0, 1)).capture(4) ~
Where(ctx => {
val sta = ctx.get[List[AssemblyLine]](4).head
val middleCode = ctx.get[List[AssemblyLine]](5)
middleCode.forall(line => HelperCheckers.memoryAccessDoesntOverlap(line, sta))
}) ~~> { code =>
code.last :: code.init
},
(HasOpcode(TAX)) ~
(Elidable & Linear & Not(ChangesX) & Not(HasOpcode(STX))).*.capture(5) ~
(Elidable & HasOpcode(STX) & HasAddrModeIn(Set(ZeroPage, Absolute, LongAbsolute))).capture(4) ~
Where(ctx => {
val sta = ctx.get[List[AssemblyLine]](4).head
val middleCode = ctx.get[List[AssemblyLine]](5)
middleCode.forall(line => HelperCheckers.memoryAccessDoesntOverlap(line, sta))
}) ~~> { code =>
code.last.copy(opcode = STA) :: code.init
},
(HasOpcode(TAY)) ~
(Elidable & Linear & Not(ChangesY) & Not(HasOpcode(STY))).*.capture(5) ~
(Elidable & HasOpcode(STY) & HasAddrModeIn(Set(ZeroPage, Absolute, LongAbsolute))).capture(4) ~
Where(ctx => {
val sta = ctx.get[List[AssemblyLine]](4).head
val middleCode = ctx.get[List[AssemblyLine]](5)
middleCode.forall(line => HelperCheckers.memoryAccessDoesntOverlap(line, sta))
}) ~~> { code =>
code.last.copy(opcode = STA) :: code.init
},
(HasOpcode(TAZ)) ~
(Elidable & Linear & Not(ChangesIZ) & Not(HasOpcode(STZ))).*.capture(5) ~
(Elidable & HasOpcode(STZ) & HasAddrModeIn(Set(ZeroPage, Absolute, LongAbsolute))).capture(4) ~
Where(ctx => {
val sta = ctx.get[List[AssemblyLine]](4).head
val middleCode = ctx.get[List[AssemblyLine]](5)
middleCode.forall(line => HelperCheckers.memoryAccessDoesntOverlap(line, sta))
}) ~~> { code =>
code.last.copy(opcode = STA) :: code.init
},
)
val PointlessStashingForLaterLoad = new RuleBasedAssemblyOptimization("Pointless stashing for later load",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(10) & MatchParameter(11) & DoesntMatterWhatItDoesWith(State.A, State.Z, State.N)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesNotConcernMemoryAt(10, 11) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeIndexingInAddrMode(10)).* ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(10) & MatchParameter(11)) ~~> { code =>
code.drop(2).init ++ code.take(2)
},
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(10) & MatchParameter(11) & DoesntMatterWhatItDoesWith(State.A, State.Z, State.N)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesNotConcernMemoryAt(10, 11) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeIndexingInAddrMode(10)).* ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(10) & MatchParameter(11)) ~~> { code =>
code.head :: (code.drop(2).init ++ List(code.head.copy(opcode = LDA), code(1)))
},
(Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ConcernsX)).* ~
(Elidable & HasOpcode(STX)) ~
(Elidable & HasOpcode(TXA)) ~~> { code =>
code.tail.init.init ++ List(code.head.copy(opcode = LDA), code.init.last.copy(opcode = STA), AssemblyLine.implied(TAX))
},
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ConcernsY)).* ~
(Elidable & HasOpcode(STY)) ~
(Elidable & HasOpcode(TYA)) ~~> { code =>
code.tail.init.init ++ List(code.head.copy(opcode = LDA), code.init.last.copy(opcode = STA), AssemblyLine.implied(TAY))
},
(Elidable & HasOpcode(LDZ) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ConcernsIZ)).* ~
(Elidable & HasOpcode(STZ)) ~
(Elidable & HasOpcode(TZA)) ~~> { code =>
code.tail.init.init ++ List(code.head.copy(opcode = LDA), code.init.last.copy(opcode = STA), AssemblyLine.implied(TAZ))
},
(Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ConcernsX)).* ~
(Elidable & HasOpcode(TXA)) ~~> { code =>
code.tail.init ++ List(code.head.copy(opcode = LDA), AssemblyLine.implied(TAX))
},
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ConcernsY)).* ~
(Elidable & HasOpcode(TYA)) ~~> { code =>
code.tail.init ++ List(code.head.copy(opcode = LDA), AssemblyLine.implied(TAY))
},
(Elidable & HasOpcode(LDZ) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ConcernsIZ)).* ~
(Elidable & HasOpcode(TZA)) ~~> { code =>
code.tail.init ++ List(code.head.copy(opcode = LDA), AssemblyLine.implied(TAZ))
},
)
val PointlessLoadBeforeReturn = new RuleBasedAssemblyOptimization("Pointless load before return", val PointlessLoadBeforeReturn = new RuleBasedAssemblyOptimization("Pointless load before return",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(Set(LDA, TXA, TYA, EOR, AND, ORA, ANC) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_AF))).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail), (Set(LDA, TXA, TYA, EOR, AND, ORA, ANC) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_AF))).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail),
(Set(LDX, TAX, TSX, INX, DEX) & Elidable) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_XF))).* ~ HasOpcode(DISCARD_XF) ~~> (_.tail), (Set(LDX, TAX, TSX, INX, DEX) & Elidable) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_XF))).* ~ HasOpcode(DISCARD_XF) ~~> (_.tail),
(Set(LDY, TAY, INY, DEY) & Elidable) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_YF))).* ~ HasOpcode(DISCARD_YF) ~~> (_.tail), (Set(LDY, TAY, INY, DEY) & Elidable) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_YF))).* ~ HasOpcode(DISCARD_YF) ~~> (_.tail),
(HasOpcode(LDX) & Elidable & MatchAddrMode(3)) ~ (HasOpcode(LDX) & Elidable & MatchAddrMode(3) & MatchParameter(4)) ~
(LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & DoesntChangeMemoryAt(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~
(HasOpcode(TXA) & Elidable) ~ (HasOpcode(TXA) & Elidable) ~
((LinearOrLabel & Not(ConcernsX) & Not(HasOpcode(DISCARD_XF))).* ~ ((LinearOrLabel & Not(ConcernsX) & Not(HasOpcode(DISCARD_XF))).* ~
HasOpcode(DISCARD_XF)).capture(2) ~~> { (c, ctx) => HasOpcode(DISCARD_XF)).capture(2) ~~> { (c, ctx) =>
ctx.get[List[AssemblyLine]](1) ++ (c.head.copy(opcode = LDA) :: ctx.get[List[AssemblyLine]](2)) ctx.get[List[AssemblyLine]](1) ++ (c.head.copy(opcode = LDA) :: ctx.get[List[AssemblyLine]](2))
}, },
(HasOpcode(LDY) & Elidable & MatchAddrMode(3)) ~ (HasOpcode(LDY) & Elidable & MatchAddrMode(3) & MatchParameter(4)) ~
(LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ) & DoesntChangeMemoryAt(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~
(HasOpcode(TYA) & Elidable) ~ (HasOpcode(TYA) & Elidable) ~
((LinearOrLabel & Not(ConcernsY) & Not(HasOpcode(DISCARD_YF))).* ~ ((LinearOrLabel & Not(ConcernsY) & Not(HasOpcode(DISCARD_YF))).* ~
HasOpcode(DISCARD_YF)).capture(2) ~~> { (c, ctx) => HasOpcode(DISCARD_YF)).capture(2) ~~> { (c, ctx) =>
@ -410,7 +507,10 @@ object AlwaysGoodOptimizations {
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(AllDirectJumps & MatchParameter(0) & Elidable) ~ (AllDirectJumps & MatchParameter(0) & Elidable) ~
HasOpcodeIn(NoopDiscardsFlags).* ~ HasOpcodeIn(NoopDiscardsFlags).* ~
(HasOpcode(LABEL) & MatchParameter(0)) ~~> (c => c.last :: Nil) (HasOpcode(LABEL) & MatchParameter(0)) ~~> (c => c.last :: Nil),
(AllDirectJumps & MatchParameter(0) & Elidable) ~
(HasOpcode(LABEL) & Not(MatchParameter(0))).* ~
(HasOpcode(LABEL) & MatchParameter(0)) ~~> (_.tail),
) )
val ImpossibleBranchRemoval = new RuleBasedAssemblyOptimization("Impossible branch", val ImpossibleBranchRemoval = new RuleBasedAssemblyOptimization("Impossible branch",
@ -1021,6 +1121,25 @@ object AlwaysGoodOptimizations {
wordShifting(5, hiFirst = true, hiFromX = false), wordShifting(5, hiFirst = true, hiFromX = false),
) )
val ShiftingJustWrittenValue = new RuleBasedAssemblyOptimization("Shifting just written value",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.A)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~
(Elidable & HasOpcodeIn(Set(ASL, LSR)) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N)) ~~> { code =>
code.last.copy(addrMode = AddrMode.Implied, parameter = Constant.Zero) :: code.init
},
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.A)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ChangesC)).* ~
(Elidable & HasOpcodeIn(Set(ASL, LSR)) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> { code =>
code.last.copy(addrMode = AddrMode.Implied, parameter = Constant.Zero) :: code.init
},
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ChangesC)).* ~
(Elidable & HasOpcodeIn(Set(ROL, ROR)) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> { code =>
code.last.copy(addrMode = AddrMode.Implied, parameter = Constant.Zero) :: code.init
},
)
val SmarterShiftingBytes = new RuleBasedAssemblyOptimization("Smarter shifting of bytes", val SmarterShiftingBytes = new RuleBasedAssemblyOptimization("Smarter shifting of bytes",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~
@ -1422,6 +1541,13 @@ object AlwaysGoodOptimizations {
(Elidable & HasOpcode(JMP) & MatchParameter(1))).capture(11) ~~> { (code, ctx) => (Elidable & HasOpcode(JMP) & MatchParameter(1))).capture(11) ~~> { (code, ctx) =>
ctx.get[List[AssemblyLine]](10) ++ ctx.get[List[AssemblyLine]](13).map(_.copy(parameter = ctx.get[Constant](1))) ++ ctx.get[List[AssemblyLine]](11) ctx.get[List[AssemblyLine]](10) ++ ctx.get[List[AssemblyLine]](13).map(_.copy(parameter = ctx.get[Constant](1))) ++ ctx.get[List[AssemblyLine]](11)
}, },
(Elidable & HasOpcode(LABEL) & MatchParameter(0)) ~
(Elidable & HasOpcode(JMP) & MatchParameter(1)) ~
Not(MatchParameter(1)).* ~
(HasOpcode(LABEL) & MatchParameter(1)) ~
(HasOpcode(JMP) & MatchParameter(1)) ~~> { (code, ctx) =>
code.head :: code.drop(2)
},
) )
val IndexComparisonOptimization = new RuleBasedAssemblyOptimization("Index comparison optimization", val IndexComparisonOptimization = new RuleBasedAssemblyOptimization("Index comparison optimization",

View File

@ -1,7 +1,7 @@
package millfork.assembly.opt package millfork.assembly.opt
import millfork.CompilationOptions import millfork.CompilationOptions
import millfork.assembly.{AssemblyLine, Opcode, OpcodeClasses, State} import millfork.assembly._
import millfork.env._ import millfork.env._
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
import millfork.node.Register import millfork.node.Register
@ -45,8 +45,10 @@ case class CpuImportance(a: Importance = UnknownImportance,
d: Importance = UnknownImportance, d: Importance = UnknownImportance,
m: Importance = UnknownImportance, m: Importance = UnknownImportance,
w: Importance = UnknownImportance, w: Importance = UnknownImportance,
r0: Importance = UnknownImportance,
r1: Importance = UnknownImportance,
) { ) {
override def toString: String = s"A=$a,B=$ah,X=$x,Y=$y,Z=$iz; Z=$z,N=$n,C=$c,V=$v,D=$d,M=$m,X=$w" override def toString: String = s"A=$a,B=$ah,X=$x,Y=$y,Z=$iz; Z=$z,N=$n,C=$c,V=$v,D=$d,M=$m,X=$w; R0=$r0,R1:$r1"
def ~(that: CpuImportance) = new CpuImportance( def ~(that: CpuImportance) = new CpuImportance(
a = this.a ~ that.a, a = this.a ~ that.a,
@ -60,6 +62,8 @@ case class CpuImportance(a: Importance = UnknownImportance,
d = this.d ~ that.d, d = this.d ~ that.d,
m = this.m ~ that.m, m = this.m ~ that.m,
w = this.w ~ that.w, w = this.w ~ that.w,
r0 = this.r0 ~ that.r0,
r1 = this.r1 ~ that.r1,
) )
def isUnimportant(state: State.Value): Boolean = state match { def isUnimportant(state: State.Value): Boolean = state match {
@ -77,11 +81,19 @@ case class CpuImportance(a: Importance = UnknownImportance,
case State.M => m != Important case State.M => m != Important
case State.W => w != Important case State.W => w != Important
} }
def isPseudoregisterUnimportant(index: Int): Boolean = index match {
case 0 => r0 != Important
case 1 => r1 != Important
case _ => false
}
} }
object ReverseFlowAnalyzer { object ReverseFlowAnalyzer {
val aluAdders = Set(Opcode.ADC, Opcode.SBC, Opcode.ISC, Opcode.DCP, Opcode.ADC_W, Opcode.SBC_W) val aluAdders = Set(Opcode.ADC, Opcode.SBC, Opcode.ISC, Opcode.DCP, Opcode.ADC_W, Opcode.SBC_W)
val actuallyRead = Set(AddrMode.IndexedZ, AddrMode.IndexedSY, AddrMode.IndexedY, AddrMode.LongIndexedY, AddrMode.LongIndexedZ, AddrMode.IndexedX, AddrMode.Indirect, AddrMode.AbsoluteIndexedX)
val absoluteLike = Set(AddrMode.ZeroPage, AddrMode.Absolute, AddrMode.LongAbsolute)
//noinspection RedundantNewCaseClass //noinspection RedundantNewCaseClass
def analyze(f: NormalFunction, code: List[AssemblyLine]): List[CpuImportance] = { def analyze(f: NormalFunction, code: List[AssemblyLine]): List[CpuImportance] = {
@ -93,7 +105,8 @@ object ReverseFlowAnalyzer {
a = Important, ah = Important, a = Important, ah = Important,
x = Important, y = Important, iz = Important, x = Important, y = Important, iz = Important,
c = Important, v = Important, d = Important, z = Important, n = Important, c = Important, v = Important, d = Important, z = Important, n = Important,
m = Important, w = Important) m = Important, w = Important,
r0 = Important, r1 = Important)
changed = true changed = true
while (changed) { while (changed) {
changed = false changed = false
@ -105,7 +118,8 @@ object ReverseFlowAnalyzer {
changed = true changed = true
importanceArray(i) = currentImportance importanceArray(i) = currentImportance
} }
codeArray(i) match { val currentLine = codeArray(i)
currentLine match {
case AssemblyLine(opcode, Relative | LongRelative, MemoryAddressConstant(Label(l)), _) if OpcodeClasses.ShortConditionalBranching(opcode) => case AssemblyLine(opcode, Relative | LongRelative, MemoryAddressConstant(Label(l)), _) if OpcodeClasses.ShortConditionalBranching(opcode) =>
val L = l val L = l
val labelIndex = codeArray.indexWhere { val labelIndex = codeArray.indexWhere {
@ -115,7 +129,7 @@ object ReverseFlowAnalyzer {
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance
case _ => case _ =>
} }
codeArray(i) match { currentLine match {
case AssemblyLine(JSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(fun: FunctionInMemory), _) => case AssemblyLine(JSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(fun: FunctionInMemory), _) =>
// this case has to be handled first, because the generic JSR importance handler is too conservative // this case has to be handled first, because the generic JSR importance handler is too conservative
@ -131,7 +145,9 @@ object ReverseFlowAnalyzer {
v = Unimportant, v = Unimportant,
d = Important, d = Important,
m = Important, m = Important,
w = Important) w = Important,
r0 = Unimportant,
r1 = Unimportant)
fun.params match { fun.params match {
case AssemblyParamSignature(params) => case AssemblyParamSignature(params) =>
params.foreach(_.variable match { params.foreach(_.variable match {
@ -153,6 +169,9 @@ object ReverseFlowAnalyzer {
}) })
case _ => case _ =>
} }
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput(fun.name)) {
result = result.copy(r0 = Important, r1 = Important)
}
currentImportance = result currentImportance = result
case AssemblyLine(ANC, _, NumericConstant(0, _), _) => case AssemblyLine(ANC, _, NumericConstant(0, _), _) =>
@ -231,6 +250,47 @@ object ReverseFlowAnalyzer {
else if (addrMode == IndexedZ /*|| addrMode == LongIndexedZ*/ ) else if (addrMode == IndexedZ /*|| addrMode == LongIndexedZ*/ )
currentImportance = currentImportance.copy(iz = Important) currentImportance = currentImportance.copy(iz = Important)
} }
if (absoluteLike(currentLine.addrMode)) {
if (OpcodeClasses.StoresByte(currentLine.opcode)) {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Unimportant)
case _ => ()
}
}
if (OpcodeClasses.StoresWord(currentLine.opcode)) {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Unimportant, r1 = Unimportant)
case _ => ()
}
}
}
if (actuallyRead(currentLine.addrMode)) {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important, r1 = Important)
case _ => ()
}
} else if (OpcodeClasses.ReadsM(currentLine.opcode) || OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(currentLine.opcode)) {
if (OpcodeClasses.AccessesWordInMemory(currentLine.opcode)) {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important, r1 = Important)
case _ => ()
}
} else {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Important)
case _ => ()
}
}
}
} }
} }
// importanceArray.zip(codeArray).foreach{ // importanceArray.zip(codeArray).foreach{

View File

@ -118,9 +118,9 @@ object ReverseFlowAnalyzerPerOpcode {
currentImportance.copy(y = currentImportance.x, x = currentImportance.y, m = Important, w = Important) currentImportance.copy(y = currentImportance.x, x = currentImportance.y, m = Important, w = Important)
}), }),
DISCARD_XF -> (_.copy(x = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)), DISCARD_XF -> (_.copy(x = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant, r0 = Unimportant, r1 = Unimportant)),
DISCARD_YF -> (_.copy(y = Unimportant, iz = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)), DISCARD_YF -> (_.copy(y = Unimportant, iz = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant, r0 = Unimportant, r1 = Unimportant)),
DISCARD_AF -> (_.copy(a = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)), DISCARD_AF -> (_.copy(a = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant, r0 = Unimportant, r1 = Unimportant)),
LDA -> (_.copy(a = Unimportant, n = Unimportant, z = Unimportant, m = Important)), LDA -> (_.copy(a = Unimportant, n = Unimportant, z = Unimportant, m = Important)),
LDX -> (_.copy(x = Unimportant, n = Unimportant, z = Unimportant, w = Important)), LDX -> (_.copy(x = Unimportant, n = Unimportant, z = Unimportant, w = Important)),
@ -181,16 +181,16 @@ object ReverseFlowAnalyzerPerOpcode {
LDX_W -> (_.copy(x = Unimportant, n = Unimportant, z = Unimportant, w = Important)), LDX_W -> (_.copy(x = Unimportant, n = Unimportant, z = Unimportant, w = Important)),
LDY_W -> (_.copy(y = Unimportant, n = Unimportant, z = Unimportant, w = Important)), LDY_W -> (_.copy(y = Unimportant, n = Unimportant, z = Unimportant, w = Important)),
STA -> (_.copy(a = Important, m = Important)), STA -> (importance => importance.copy(a = Important, m = Important)),
STX -> (_.copy(x = Important, w = Important)), STX -> (importance => importance.copy(x = Important, w = Important)),
STY -> (_.copy(y = Important, w = Important)), STY -> (importance => importance.copy(y = Important, w = Important)),
STZ -> (_.copy(iz = Important, m = Important)), STZ -> (importance => importance.copy(iz = Important, m = Important)),
SAX -> (_.copy(a = Important, x = Important)), SAX -> (importance => importance.copy(a = Important, x = Important)),
STA_W -> (_.copy(a = Important, ah = Important, m = Important)), STA_W -> (importance => importance.copy(a = Important, ah = Important, m = Important)),
STX_W -> (_.copy(x = Important, w = Important)), STX_W -> (importance => importance.copy(x = Important, w = Important)),
STY_W -> (_.copy(y = Important, w = Important)), STY_W -> (importance => importance.copy(y = Important, w = Important)),
STZ_W -> (_.copy(iz = Important, m = Important)), STZ_W -> (importance => importance.copy(iz = Important, m = Important)),
DEX -> (_.copy(x = Important, n = Unimportant, z = Unimportant, w = Important)), DEX -> (_.copy(x = Important, n = Unimportant, z = Unimportant, w = Important)),
DEY -> (_.copy(y = Important, n = Unimportant, z = Unimportant, w = Important)), DEY -> (_.copy(y = Important, n = Unimportant, z = Unimportant, w = Important)),

View File

@ -205,6 +205,13 @@ object HelperCheckers {
val p2 = l2.parameter val p2 = l2.parameter
val w1 = OpcodeClasses.AccessesWordInMemory(l1.opcode) val w1 = OpcodeClasses.AccessesWordInMemory(l1.opcode)
val w2 = OpcodeClasses.AccessesWordInMemory(l2.opcode) val w2 = OpcodeClasses.AccessesWordInMemory(l2.opcode)
def distinctThings(a: String, b: String): Boolean = {
if (a == "__reg") return b != "__reg"
if (b == "__reg") return a != "__reg"
a.takeWhile(_ != '.') != b.takeWhile(_ != '.')
}
def handleKnownDistance(distance: Short): Boolean = { def handleKnownDistance(distance: Short): Boolean = {
// `distance` is the distance between the first byte that can be addressed by l1 (b1) and the first byte that can be addressed by l2 (b2): (b2-b1) // `distance` is the distance between the first byte that can be addressed by l1 (b1) and the first byte that can be addressed by l2 (b2): (b2-b1)
val indexingAddrModes = Set(AbsoluteIndexedX, AbsoluteX, ZeroPageX, AbsoluteY, ZeroPageY, LongAbsoluteX) val indexingAddrModes = Set(AbsoluteIndexedX, AbsoluteX, ZeroPageX, AbsoluteY, ZeroPageY, LongAbsoluteX)
@ -238,7 +245,7 @@ object HelperCheckers {
case (NumericConstant(_, _), CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _))) => case (NumericConstant(_, _), CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _))) =>
true // TODO: ??? true // TODO: ???
case (MemoryAddressConstant(a: ThingInMemory), MemoryAddressConstant(b: ThingInMemory)) => case (MemoryAddressConstant(a: ThingInMemory), MemoryAddressConstant(b: ThingInMemory)) =>
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ??? distinctThings(a.name, b.name) // TODO: ???
case (CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(a: ThingInMemory), NumericConstant(offset, _)), case (CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(a: ThingInMemory), NumericConstant(offset, _)),
MemoryAddressConstant(b: ThingInMemory)) => MemoryAddressConstant(b: ThingInMemory)) =>
if (a.name == b.name) { if (a.name == b.name) {
@ -248,7 +255,7 @@ object HelperCheckers {
handleKnownDistance(offset.toShort) handleKnownDistance(offset.toShort)
} }
} else { } else {
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ??? distinctThings(a.name, b.name) // TODO: ???
} }
case (MemoryAddressConstant(a: ThingInMemory), case (MemoryAddressConstant(a: ThingInMemory),
CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(b: ThingInMemory), NumericConstant(offset, _))) => CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(b: ThingInMemory), NumericConstant(offset, _))) =>
@ -259,7 +266,7 @@ object HelperCheckers {
handleKnownDistance(offset.toShort) handleKnownDistance(offset.toShort)
} }
} else { } else {
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ??? distinctThings(a.name, b.name) // TODO: ???
} }
case (CompoundConstant(op1@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(a: ThingInMemory), NumericConstant(o1, _)), case (CompoundConstant(op1@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(a: ThingInMemory), NumericConstant(o1, _)),
CompoundConstant(op2@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(b: ThingInMemory), NumericConstant(o2, _))) => CompoundConstant(op2@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(b: ThingInMemory), NumericConstant(o2, _))) =>
@ -268,7 +275,7 @@ object HelperCheckers {
val offset2 = if (op2==MathOperator.Plus) o2 else -o2 val offset2 = if (op2==MathOperator.Plus) o2 else -o2
handleKnownDistance((offset2 - offset1).toShort) handleKnownDistance((offset2 - offset1).toShort)
} else { } else {
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ??? distinctThings(a.name, b.name) // TODO: ???
} }
case _ => case _ =>
false false
@ -531,6 +538,16 @@ case class DoesntMatterWhatItDoesWith(states: State.Value*) extends AssemblyLine
override def toString: String = states.mkString("[¯\\_(ツ)_/¯:", ",", "]") override def toString: String = states.mkString("[¯\\_(ツ)_/¯:", ",", "]")
} }
case class DoesntMatterWhatItDoesWithReg(index: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertBackward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.importanceAfter.isPseudoregisterUnimportant(index)
override def toString: String = s"\\_(ツ)_/¯: __reg+$index]"
}
case class HasSet(state: State.Value) extends AssemblyLinePattern { case class HasSet(state: State.Value) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo) FlowInfoRequirement.assertForward(needsFlowInfo)
@ -796,15 +813,15 @@ case class HasOpcode(op: Opcode.Value) extends TrivialAssemblyLinePattern {
override def toString: String = op.toString override def toString: String = op.toString
} }
case class RefersTo(identifier: String, offset: Int) extends TrivialAssemblyLinePattern { case class RefersTo(identifier: String, offset: Int = 999) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = { override def apply(line: AssemblyLine): Boolean = {
(line.addrMode == AddrMode.ZeroPage || line.addrMode == AddrMode.Absolute || line.addrMode == AddrMode.LongAbsolute) && (line.parameter match { (line.addrMode == AddrMode.ZeroPage || line.addrMode == AddrMode.Absolute || line.addrMode == AddrMode.LongAbsolute) && (line.parameter match {
case MemoryAddressConstant(th) => case MemoryAddressConstant(th) =>
offset == 0 && th.name == identifier (offset == 999 || offset == 0) && th.name == identifier
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(nn, _)) => case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
offset == nn && th.name == identifier (offset == 999 || offset == nn) && th.name == identifier
case CompoundConstant(MathOperator.Plus, NumericConstant(nn, _), MemoryAddressConstant(th)) => case CompoundConstant(MathOperator.Plus, NumericConstant(nn, _), MemoryAddressConstant(th)) =>
offset == nn && th.name == identifier (offset == 999 || offset == nn) && th.name == identifier
case _ => false case _ => false
}) })
} }

View File

@ -49,18 +49,21 @@ object SuperOptimizer extends AssemblyOptimization {
val quickScrub = List( val quickScrub = List(
UnusedLabelRemoval, UnusedLabelRemoval,
AlwaysGoodOptimizations.BranchInPlaceRemoval,
AlwaysGoodOptimizations.PointlessLoadBeforeReturn,
AlwaysGoodOptimizations.UnusedCodeRemoval AlwaysGoodOptimizations.UnusedCodeRemoval
) )
val quicklyCleanedCode = quickScrub.foldLeft(code)((c, o) => o.optimize(m, c, options)) val optionsForMeasurements = options.copy(commandLineFlags = options.commandLineFlags + (CompilationFlag.InternalCurrentlyOptimizingForMeasurement -> true))
val quicklyCleanedCode = quickScrub.foldLeft(code)((c, o) => o.optimize(m, c, optionsForMeasurements))
seenSoFar += viewCode(quicklyCleanedCode) seenSoFar += viewCode(quicklyCleanedCode)
queue.enqueue(quickScrub.reverse -> quicklyCleanedCode) queue.enqueue(quickScrub.reverse -> quicklyCleanedCode)
while(queue.nonEmpty) { while(queue.nonEmpty) {
val (optsSoFar, codeSoFar) = queue.dequeue() val (optsSoFar, codeSoFar) = queue.dequeue()
var isLeaf = true var isLeaf = true
(if (options.flag(CompilationFlag.SingleThreaded)) allOptimizers (if (optionsForMeasurements.flag(CompilationFlag.SingleThreaded)) allOptimizers
else allOptimizers.par).foreach { o => else allOptimizers.par).foreach { o =>
val optimized = o.optimize(m, codeSoFar, options) val optimized = o.optimize(m, codeSoFar, optionsForMeasurements)
val view = viewCode(optimized) val view = viewCode(optimized)
seenSoFar.synchronized{ seenSoFar.synchronized{
if (!seenSoFar(view)) { if (!seenSoFar(view)) {

View File

@ -134,6 +134,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
v.name -> VariableLifetime.apply(v.name, code) v.name -> VariableLifetime.apply(v.name, code)
).toMap ).toMap
val removeVariablesForReal = !options.flag(CompilationFlag.InternalCurrentlyOptimizingForMeasurement)
val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes
val importances = ReverseFlowAnalyzer.analyze(f, code) val importances = ReverseFlowAnalyzer.analyze(f, code)
val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed) val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed)
@ -252,7 +253,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
reportOptimizedBlock(oldCode, newCode) reportOptimizedBlock(oldCode, newCode)
output ++= newCode output ++= newCode
i = range.end i = range.end
if (contains(range, variablesWithLifetimes(v))) { if (removeVariablesForReal && contains(range, variablesWithLifetimes(v))) {
f.environment.removeVariable(v) f.environment.removeVariable(v)
} }
done = true done = true
@ -266,7 +267,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
reportOptimizedBlock(oldCode, newCode) reportOptimizedBlock(oldCode, newCode)
output ++= newCode output ++= newCode
i = range.end i = range.end
if (contains(range, variablesWithLifetimes(v))) { if (removeVariablesForReal && contains(range, variablesWithLifetimes(v))) {
f.environment.removeVariable(v) f.environment.removeVariable(v)
} }
done = true done = true
@ -281,7 +282,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
reportOptimizedBlock(oldCode, newCode) reportOptimizedBlock(oldCode, newCode)
output ++= newCode output ++= newCode
i = range.end i = range.end
if (contains(range, variablesWithLifetimes(v))) { if (removeVariablesForReal && contains(range, variablesWithLifetimes(v))) {
f.environment.removeVariable(v) f.environment.removeVariable(v)
} }
done = true done = true
@ -296,7 +297,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
reportOptimizedBlock(oldCode, newCode) reportOptimizedBlock(oldCode, newCode)
output ++= newCode output ++= newCode
i = range.end i = range.end
if (contains(range, variablesWithLifetimes(v))) { if (removeVariablesForReal && contains(range, variablesWithLifetimes(v))) {
f.environment.removeVariable(v) f.environment.removeVariable(v)
} }
done = true done = true

View File

@ -10,7 +10,7 @@ import millfork.env.{CompoundConstant, Constant, MathOperator}
*/ */
object ZeropageRegisterOptimizations { object ZeropageRegisterOptimizations {
private val functionsThatUsePseudoregisterAsInput = Set("__mul_u8u8u8") val functionsThatUsePseudoregisterAsInput = Set("__mul_u8u8u8")
val ConstantMultiplication = new RuleBasedAssemblyOptimization("Constant multiplication", val ConstantMultiplication = new RuleBasedAssemblyOptimization("Constant multiplication",
needsFlowInfo = FlowInfoRequirement.ForwardFlow, needsFlowInfo = FlowInfoRequirement.ForwardFlow,
@ -22,6 +22,8 @@ object ZeropageRegisterOptimizations {
code.init :+ AssemblyLine.immediate(LDA, product & 0xff) code.init :+ AssemblyLine.immediate(LDA, product & 0xff)
}, },
// TODO: constants other than power of 2:
(Elidable & HasOpcode(STA) & RefersTo("__reg", 0) & MatchAddrMode(0) & MatchParameter(1)) ~ (Elidable & HasOpcode(STA) & RefersTo("__reg", 0) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(RefersTo("__reg", 1)) & DoesntChangeMemoryAt(0, 1)).* ~ (Linear & Not(RefersTo("__reg", 1)) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(STA) & RefersTo("__reg", 1) & MatchA(4)) ~ (HasOpcode(STA) & RefersTo("__reg", 1) & MatchA(4)) ~
@ -66,9 +68,16 @@ object ZeropageRegisterOptimizations {
(HasOpcodeIn(Set(RTS, RTL)) | CallsAnyExcept(functionsThatUsePseudoregisterAsInput)) ~~> (_.tail), (HasOpcodeIn(Set(RTS, RTL)) | CallsAnyExcept(functionsThatUsePseudoregisterAsInput)) ~~> (_.tail),
) )
val DeadRegStoreFromFlow = new RuleBasedAssemblyOptimization("Dead zeropage register store from flow",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(STA) & RefersTo("__reg", 0) & DoesntMatterWhatItDoesWithReg(0)) ~~> (_.tail),
(Elidable & HasOpcode(STA) & RefersTo("__reg", 1) & DoesntMatterWhatItDoesWithReg(1)) ~~> (_.tail),
)
val All: List[AssemblyOptimization] = List( val All: List[AssemblyOptimization] = List(
ConstantMultiplication, ConstantMultiplication,
DeadRegStore, DeadRegStore,
DeadRegStoreFromFlow,
) )
} }

View File

@ -326,15 +326,17 @@ object StatementCompiler {
List(labelChunk(start), conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten List(labelChunk(start), conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
} else { } else {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching) val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
List(labelChunk(start), conditionBlock, branchChunk(jumpIfFalse, end), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten List(jmpChunk(middle), labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, labelChunk(middle), conditionBlock, branchChunk(jumpIfTrue, start), labelChunk(end)).flatten
// List(labelChunk(start), conditionBlock, branchChunk(jumpIfFalse, end), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
} }
case BuiltInBooleanType => case BuiltInBooleanType =>
if (largeBodyBlock) { if (largeBodyBlock) {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle)) val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
List(labelChunk(start), conditionBlock, jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten List(labelChunk(start), conditionBlock, jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
} else { } else {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end)) val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
List(labelChunk(start), conditionBlock, bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten List(jmpChunk(middle), labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, labelChunk(middle), conditionBlock, labelChunk(end)).flatten
// List(labelChunk(start), conditionBlock, bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
} }
case _ => case _ =>
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position) ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)

View File

@ -165,6 +165,26 @@ class WordMathSuite extends FunSuite with Matchers {
} }
} }
test("Word addition 4") {
EmuBenchmarkRun("""
| word output @$c000
| void main () {
| word v
| word u
| v = w($308)
| u = w($601)
| barrier()
| output = u + v
| }
| noinline void barrier() { }
| noinline word w(word w) {
| return w
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(0x909)
}
}
test("Word bit ops 2") { test("Word bit ops 2") {
EmuBenchmarkRun(""" EmuBenchmarkRun("""
| word output @$c000 | word output @$c000
@ -180,4 +200,22 @@ class WordMathSuite extends FunSuite with Matchers {
m.readWord(0xc000) should equal(0x483) m.readWord(0xc000) should equal(0x483)
} }
} }
test("Word shift") {
EmuBenchmarkRun("""
| word output @$c000
| void main () {
| word v
| v = w($123)
| barrier()
| output = v << 1
| }
| noinline void barrier() { }
| noinline word w(word w) {
| return w
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(0x246)
}
}
} }