diff --git a/src/main/scala/millfork/OptimizationPresets.scala b/src/main/scala/millfork/OptimizationPresets.scala index a4afc381..b408d9d8 100644 --- a/src/main/scala/millfork/OptimizationPresets.scala +++ b/src/main/scala/millfork/OptimizationPresets.scala @@ -16,6 +16,7 @@ object OptimizationPresets { val AssOpt: List[AssemblyOptimization] = List[AssemblyOptimization]( UnusedLabelRemoval, AlwaysGoodOptimizations.NonetAddition, + AlwaysGoodOptimizations.NonetBitOp, AlwaysGoodOptimizations.PointlessSignCheck, AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad, AlwaysGoodOptimizations.PointlessLoadAfterLoadOrStore, @@ -154,6 +155,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands, AlwaysGoodOptimizations.ModificationOfJustWrittenValue, AlwaysGoodOptimizations.NonetAddition, + AlwaysGoodOptimizations.NonetBitOp, AlwaysGoodOptimizations.OperationsAroundShifting, AlwaysGoodOptimizations.PoinlessFlagChange, AlwaysGoodOptimizations.PointlessLoadAfterLoadOrStore, diff --git a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala index 57110934..48436f81 100644 --- a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala @@ -601,6 +601,21 @@ object AlwaysGoodOptimizations { val ConstantFlowAnalysis = new RuleBasedAssemblyOptimization("Constant flow analysis", needsFlowInfo = FlowInfoRequirement.ForwardFlow, + (MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Elidable & HasParameterWhere({ + case MemoryAddressConstant(th) => th.name == "identity$" + case _ => false + })) ~~> { (code, ctx) => + code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1))) + }, + (MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasParameterWhere({ + case MemoryAddressConstant(th) => th.name == "identity$" + case _ => false + })) ~~> { (code, ctx) => + code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1))) + }, + (MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable) ~~> { (code, ctx) => + code.map(l => l.copy(addrMode = Absolute, parameter = l.parameter + ctx.get[Int](0))) + }, (MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Elidable) ~~> { (code, ctx) => code.map(l => l.copy(addrMode = Absolute, parameter = l.parameter + ctx.get[Int](0))) }, @@ -1532,7 +1547,79 @@ object AlwaysGoodOptimizations { AssemblyLine.relative(BCC, label), code(8).copy(opcode = INC), AssemblyLine.label(label)) - } + }, + (Elidable & HasOpcode(LDX) & HasImmediate(0) & HasClear(State.D)) ~ + (Elidable & HasOpcode(BCC) & MatchParameter(14)) ~ + (Elidable & HasOpcode(INX)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(14) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(CLC)) ~ + (Elidable & HasOpcode(ADC) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~ + (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~ + (Elidable & HasOpcode(TXA)) ~ + (Elidable & HasOpcode(ADC) & MatchAddrMode(2) & MatchParameter(3) & Not(ConcernsX) & DoesNotConcernMemoryAt(0, 1)) ~ + (Elidable & HasOpcode(STA) & MatchAddrMode(2) & MatchParameter(3) & Not(ConcernsX) & DoesntMatterWhatItDoesWith(State.C, State.N, State.V, State.Z)) ~~> { (code, ctx) => + val label = getNextLabel("in") + List( + code(1), // BCC + code(8).copy(opcode = INC), + code(3), //LABEL + code(4), //CLC + code(5), //ADC + code(6), //STA + AssemblyLine.relative(BCC, label), + code(8).copy(opcode = INC), + AssemblyLine.label(label)) + }, + (Elidable & HasOpcode(LDX) & HasAddrMode(Immediate) & HasClear(State.D)) ~ + (Elidable & HasOpcode(BCC) & MatchParameter(14)) ~ + (Elidable & HasOpcode(INX)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(14) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~ + (Elidable & HasOpcode(STX) & MatchAddrMode(2) & MatchParameter(3) & DoesNotConcernMemoryAt(0, 1) & Not(HasAddrMode(ZeroPageY)) & + DoesntMatterWhatItDoesWith(State.X, State.A, State.C, State.V)) ~~> { (code, ctx) => + val label = getNextLabel("in") + List( + code(4), // STA + code.head.copy(opcode = LDA), // LDX + AssemblyLine.immediate(ADC, 0), + code(5).copy(opcode = STA)) //STX + }, + (Elidable & HasOpcode(TAX) & HasClear(State.D)) ~ + (Elidable & HasOpcode(BCC) & MatchParameter(14)) ~ + (Elidable & HasOpcode(INX)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(14) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~ + (Elidable & HasOpcode(STX) & MatchAddrMode(2) & MatchParameter(3) & DoesNotConcernMemoryAt(0, 1) & Not(HasAddrMode(ZeroPageY)) & + DoesntMatterWhatItDoesWith(State.X, State.A, State.C, State.V)) ~~> { (code, ctx) => + val label = getNextLabel("in") + List( + code(4), // STA + AssemblyLine.immediate(ADC, 0), + code(5).copy(opcode = STA)) //STX + }, + ) + + val NonetBitOp = new RuleBasedAssemblyOptimization("Nonet bit operation", + needsFlowInfo = FlowInfoRequirement.BothFlows, + (Elidable & HasOpcode(LDX) & HasImmediate(0)) ~ + (Elidable & HasOpcode(BCC) & MatchParameter(14)) ~ + (Elidable & HasOpcode(INX)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(14) & HasCallerCount(1)) ~ + (Elidable & HasOpcodeIn(Set(ORA, EOR)) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~ + (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~ + (Elidable & HasOpcode(TXA)) ~ + (Elidable & HasOpcodeIn(Set(ORA, EOR)) & MatchAddrMode(2) & MatchParameter(3) & Not(ConcernsX) & DoesNotConcernMemoryAt(0, 1)) ~ + (Elidable & HasOpcode(STA) & MatchAddrMode(2) & MatchParameter(3) & Not(ConcernsX) & DoesntMatterWhatItDoesWith(State.C, State.N, State.V, State.Z)) ~~>{ (code, ctx) => + val label = getNextLabel("in") + List( + code(4), //EOR/ORA + code(5), //STA + code(1), // BCC + AssemblyLine.immediate(LDA, 1), + code(7), //EOR/ORA + code(8), //STA + code(3)) // LABEL + } ) val CommonIndexSubexpressionElimination: RuleBasedAssemblyOptimization = { diff --git a/src/test/scala/millfork/test/NonetSuite.scala b/src/test/scala/millfork/test/NonetSuite.scala index 6e7001d2..96f72466 100644 --- a/src/test/scala/millfork/test/NonetSuite.scala +++ b/src/test/scala/millfork/test/NonetSuite.scala @@ -72,4 +72,32 @@ class NonetSuite extends FunSuite with Matchers { m.readWord(0xc006) should equal(0x180) } } + + test("Nonet OR/EXOR") { + EmuBenchmarkRun( + """ + | word output0 @$c000 + | word output1 @$c002 + | word output2 @$c004 + | word output3 @$c006 + | void main () { + | byte a + | output0 = 0 + | output1 = 0 + | output2 = $8100 + | output3 = $8100 + | a = three() + | output0 |= nonet(a << 1) + | output1 ^= nonet(a << 2) + | output2 |= nonet(a << 6) + | output3 ^= nonet(a << 7) + | } + | noinline byte three() { return 3 } + """.stripMargin) { m => + m.readWord(0xc000) should equal(0x06) + m.readWord(0xc002) should equal(0x0C) + m.readWord(0xc004) should equal(0x81C0) + m.readWord(0xc006) should equal(0x8080) + } + } }