1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-07-05 09:28:54 +00:00

Optimizing addition by replacing it with bit ops (NMOS) or increments (CMOS)

This commit is contained in:
Karol Stasiak 2018-06-18 19:48:47 +02:00
parent 7510b44412
commit 9b7d58cf65
5 changed files with 89 additions and 2 deletions

View File

@ -116,6 +116,7 @@ object OptimizationPresets {
AlwaysGoodOptimizations.PointlessMathFromFlow,
AlwaysGoodOptimizations.PointlessMathFromFlow,
AlwaysGoodOptimizations.PointlessMathFromFlow,
AlwaysGoodOptimizations.ReplacingArithmeticsWithBitOps,
AlwaysGoodOptimizations.PointlessMathFromFlow,
AlwaysGoodOptimizations.PointlessMathFromFlow,
AlwaysGoodOptimizations.PointlessMathFromFlow,
@ -192,6 +193,7 @@ object OptimizationPresets {
AlwaysGoodOptimizations.RearrangableLoadFromTheSameLocation,
AlwaysGoodOptimizations.RearrangeMath,
AlwaysGoodOptimizations.RemoveNops,
AlwaysGoodOptimizations.ReplacingArithmeticsWithBitOps,
AlwaysGoodOptimizations.ReverseFlowAnalysis,
AlwaysGoodOptimizations.ShiftingJustWrittenValue,
AlwaysGoodOptimizations.SimplifiableBitOpsSequence,

View File

@ -2076,4 +2076,41 @@ object AlwaysGoodOptimizations {
},
)
val ReplacingArithmeticsWithBitOps = new RuleBasedAssemblyOptimization("Replacing arithmetics with bit ops",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasOpcode(ADC) & HasImmediate(0x80) & HasClear(State.C) & HasClear(State.D) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~>
(_ => List(AssemblyLine.immediate(EOR, 0x80))),
(Elidable & HasOpcode(SEC)).? ~
(Elidable & HasOpcode(SBC) & HasImmediate(0x80) & HasSet(State.C) & HasClear(State.D) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~>
(_ => List(AssemblyLine.immediate(EOR, 0x80))),
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasOpcode(ADC) & HasImmediate(1) & HasClearBitA0 & HasClear(State.C) & HasClear(State.D) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~>
(_ => List(AssemblyLine.immediate(ORA, 1))),
(HasOpcode(AND) & MatchImmediate(1)) ~
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasOpcode(ADC) & MatchImmediate(2) & HasClear(State.C) & HasClear(State.D) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~
Where(ctx => (ctx.get[Constant](1), ctx.get[Constant](2)) match {
case (NumericConstant(a1, _), NumericConstant(a2, _)) => (a1 & a2) == 0
case _ => false
}) ~~> ((code,ctx) => List(code.head, AssemblyLine.immediate(ORA, ctx.get[Constant](2)))),
(Elidable & HasOpcode(ANC) & MatchImmediate(1)) ~
(Elidable & HasOpcode(ADC) & MatchImmediate(2) & HasClear(State.C) & HasClear(State.D) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~
Where(ctx => (ctx.get[Constant](1), ctx.get[Constant](2)) match {
case (NumericConstant(a1, _), NumericConstant(a2, _)) => (a1 & 0x80) == 0 && (a1 & a2) == 0 && (a2 & 0xff) > 1
case _ => false
}) ~~> ((code,ctx) => List(code.head.copy(opcode = AND), AssemblyLine.immediate(ORA, ctx.get[Constant](2)))),
(Elidable & HasOpcode(STA) & HasClearBitA0 & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~
(Elidable & HasOpcode(INC) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> { code =>
AssemblyLine.immediate(ORA, 1) :: code.init
},
)
}

View File

@ -1,7 +1,7 @@
package millfork.assembly.mos.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode, State}
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.OpcodeClasses._
@ -50,5 +50,23 @@ object CmosOptimizations {
(Elidable & HasX(0) & HasZ(0) & HasAddrMode(AbsoluteIndexedX) & HasOpcode(JMP)) ~~> (code => code.map(_.copy(addrMode = Indirect))),
)
val All: List[AssemblyOptimization[AssemblyLine]] = List(OptimizeZeroIndex, SimplerBitFlipping, ZeroStoreAsStz)
val OptimizeIncrement = new RuleBasedAssemblyOptimization("Optimizing increment/decrement",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasOpcode(ADC) & HasImmediate(1) & HasClear(State.C) & HasClear(State.D) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> (code => List(AssemblyLine.implied(INC))),
(Elidable & HasOpcode(SEC)).? ~
(Elidable & HasOpcode(SBC) & HasImmediate(1) & HasSet(State.C) & HasClear(State.D) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> (code => List(AssemblyLine.implied(DEC))),
(Elidable & HasClearBitA0 & HasOpcodeIn(Set(ORA, EOR)) & HasImmediate(1)) ~~> (code => List(AssemblyLine.implied(INC))),
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~
(Elidable & HasOpcodeIn(Set(INC, DEC)) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> { code =>
code.last.copy(addrMode = Implied, parameter = Constant.Zero) :: code.init
},
)
val All: List[AssemblyOptimization[AssemblyLine]] = List(OptimizeZeroIndex, SimplerBitFlipping, ZeroStoreAsStz, OptimizeIncrement)
}

View File

@ -606,6 +606,14 @@ case class HasClear(state: State.Value) extends AssemblyLinePattern {
flowInfo.hasClear(state)
}
case object HasClearBitA0 extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.a0.contains(false)
}
case object Anything extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = true
}

View File

@ -489,6 +489,28 @@ class AssemblyOptimizationSuite extends FunSuite with Matchers {
}
}
test("Low bit 4") {
EmuBenchmarkRun(
"""
| byte output @$c000
| void main() {
| g(1)
| }
| void g(byte x) {
| f()
| output &= $F0
| output += 1
| }
| noinline byte f () {
| output = 33
| return 3
| }
""".stripMargin
){m =>
m.readByte(0xc000) should equal(33)
}
}
test("Identity page") {
EmuUltraBenchmarkRun(
"""