1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-01-12 03:30:09 +00:00

More optimizations using illegal opcodes

This commit is contained in:
Karol Stasiak 2018-02-25 00:46:21 +01:00
parent 6dd4a0c5ce
commit 7cd13332a3
2 changed files with 95 additions and 3 deletions

View File

@ -165,6 +165,13 @@ object UndocumentedOptimizations {
},
)
private def extraRmw(legal: Opcode.Value, illegal: Opcode.Value) =
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(IndexedY, AbsoluteY, IndexedY)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(legal) & HasAddrMode(Implied)) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C, State.V)) ~~> { (code, ctx) =>
code.head.copy(opcode = illegal) :: Nil
}
private def trivialSequence1(o1: Opcode.Value, o2: Opcode.Value, extra: AssemblyLinePattern, combined: Opcode.Value) =
(Elidable & HasOpcode(o1) & HasAddrMode(Absolute) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & extra).* ~
@ -193,6 +200,7 @@ object UndocumentedOptimizations {
trivialSequence1(ASL, ORA, Not(ConcernsC), SLO),
trivialSequence2(ASL, ORA, Not(ConcernsC), SLO),
trivialCommutativeSequence(ASL, ORA, SLO),
extraRmw(ASL, SLO),
(Elidable & HasOpcode(ASL) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ConcernsMemory)).* ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
@ -215,10 +223,11 @@ object UndocumentedOptimizations {
)
val UseSre = new RuleBasedAssemblyOptimization("Using undocumented instruction SRE",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
needsFlowInfo = FlowInfoRequirement.BothFlows,
trivialSequence1(LSR, EOR, Not(ConcernsC), SRE),
trivialSequence2(LSR, EOR, Not(ConcernsC), SRE),
trivialCommutativeSequence(LSR, EOR, SRE),
extraRmw(LSR, SRE),
(Elidable & HasOpcode(LSR) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ConcernsMemory)).* ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
@ -236,24 +245,32 @@ object UndocumentedOptimizations {
)
val UseRla = new RuleBasedAssemblyOptimization("Using undocumented instruction RLA",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
needsFlowInfo = FlowInfoRequirement.BothFlows,
trivialSequence1(ROL, AND, Not(ConcernsC), RLA),
trivialSequence2(ROL, AND, Not(ConcernsC), RLA),
trivialCommutativeSequence(ROL, AND, RLA),
extraRmw(ROL, RLA),
)
val UseRra = new RuleBasedAssemblyOptimization("Using undocumented instruction RRA",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
needsFlowInfo = FlowInfoRequirement.BothFlows,
// TODO: is it ok? carry flag and stuff?
trivialSequence1(ROR, ADC, Not(ConcernsC), RRA),
trivialSequence2(ROR, ADC, Not(ConcernsC), RRA),
trivialCommutativeSequence(ROR, ADC, RRA),
extraRmw(ROR, RRA),
)
val UseDcp = new RuleBasedAssemblyOptimization("Using undocumented instruction DCP",
needsFlowInfo = FlowInfoRequirement.BothFlows,
trivialSequence1(DEC, CMP, Not(ConcernsC), DCP),
trivialSequence2(DEC, CMP, Not(ConcernsC), DCP),
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(IndexedY, AbsoluteY, IndexedY)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(SEC)).? ~
(Elidable & HasOpcode(SBC) & HasImmediate(1) & HasSet(State.C) & HasClear(State.D)).? ~
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C, State.V)) ~~> { (code, ctx) =>
code.head.copy(opcode = DCP) :: Nil
},
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(IndexedX, ZeroPageX, AbsoluteX))) ~
(Elidable & HasOpcode(TAX)) ~
(Elidable & HasOpcode(DEC) & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.A, State.Y, State.X, State.C, State.Z, State.N, State.V)) ~~> { code =>
@ -265,6 +282,12 @@ object UndocumentedOptimizations {
needsFlowInfo = FlowInfoRequirement.BothFlows,
trivialSequence1(INC, SBC, Not(ReadsC), ISC),
trivialSequence2(INC, SBC, Not(ReadsC), ISC),
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(IndexedY, AbsoluteY, IndexedY)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasOpcode(ADC) & HasImmediate(1) & HasClear(State.C) & HasClear(State.D)).? ~
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C, State.V)) ~~> { (code, ctx) =>
code.head.copy(opcode = ISC) :: Nil
},
(Elidable & HasOpcode(LDA) & HasImmediate(0) & HasClear(State.D)) ~
(Elidable & HasOpcode(ADC) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(Set(IndexedX, IndexedY, AbsoluteY))) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
@ -319,6 +342,50 @@ object UndocumentedOptimizations {
}
)
val UseMultiple = new RuleBasedAssemblyOptimization("Using multiple undocumented instructions",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(LDA) & LaxAddrModeRestriction) ~
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasOpcode(ADC) & HasImmediate(2) & HasClear(State.C)) ~
(Elidable & HasOpcode(TAX)) ~~> { (code, ctx) =>
List(code.head.copy(opcode = LAX), AssemblyLine(SBX, Immediate, Constant.Zero - ctx.get[Constant](2)))
},
(Elidable & HasOpcode(LDA) & LaxAddrModeRestriction) ~
(Elidable & HasOpcode(SEC)).? ~
(Elidable & HasOpcode(SBC) & HasImmediate(2) & HasSet(State.C)) ~
(Elidable & HasOpcode(TAX)) ~~> { (code, ctx) =>
List(code.head.copy(opcode = LAX), AssemblyLine(SBX, Immediate, ctx.get[Constant](2)))
},
)
private def idempotent(illegal: Opcode.Value, pointless: Opcode.Value) =
(HasOpcode(illegal) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(pointless) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
code.take(1)
}
private def preferLegal(illegal: Opcode.Value, legal: Opcode.Value) =
(Elidable & HasOpcode(illegal) & HasAddrModeIn(Set(Absolute, AbsoluteX, ZeroPage, ZeroPageX)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C, State.V)) ~~> { (code, ctx) =>
code.map(_.copy(opcode = legal))
}
val CleaningUp = new RuleBasedAssemblyOptimization("Simplifying code that uses undocumented instructions",
needsFlowInfo = FlowInfoRequirement.BothFlows,
idempotent(SLO, ORA),
idempotent(SRE, EOR),
idempotent(ROL, AND),
idempotent(DCP, CMP),
preferLegal(SLO, ASL),
preferLegal(SRE, LSR),
preferLegal(RLA, ROL),
preferLegal(RRA, ROR),
preferLegal(DCP, DEC),
preferLegal(ISC, INC),
HasOpcodeIn(Set(TAX, TXA, LAX)) ~ (Elidable & HasOpcode(SBX) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~~> (code => List(code.head)),
HasOpcodeIn(Set(TAX, TXA, LAX)) ~ (Elidable & HasOpcode(SBX) & HasImmediate(1) & DoesntMatterWhatItDoesWith(State.C)) ~~> (code => List(code.head, AssemblyLine.implied(DEX))),
HasOpcodeIn(Set(TAX, TXA, LAX)) ~ (Elidable & HasOpcode(SBX) & HasImmediate(0xff) & DoesntMatterWhatItDoesWith(State.C)) ~~> (code => List(code.head, AssemblyLine.implied(INX))),
)
val All: List[AssemblyOptimization] = List(
UseLax,
UseSax,
@ -332,5 +399,7 @@ object UndocumentedOptimizations {
UseRra,
UseIsc,
UseDcp,
UseMultiple,
CleaningUp,
)
}

View File

@ -184,4 +184,27 @@ class IllegalSuite extends FunSuite with Matchers {
m.readByte(0xc006) should equal(8)
}
test("ISC/DCP test 2") {
val m = EmuUndocumentedRun("""
| array output [3] @$c000
| void main () {
| init()
| pointer p
| p = o()
| p[1] -= 1
| p[2] += 1
| }
| word o () {
| return output.addr
| }
| void init() {
| output[0] = 4
| output[1] = 4
| output[2] = 4
| }
""".stripMargin)
m.readByte(0xc001) should equal(3)
m.readByte(0xc002) should equal(5)
}
}