mirror of
https://github.com/KarolS/millfork.git
synced 2025-02-03 11:33:33 +00:00
Interprocedural optimization plus some minor fixes:
– fixed handling LDX/LDY/LDZ when inlining variables into registers – fixed CLA/CLX/CLY instruction generation – refactored optimization definitions
This commit is contained in:
parent
57740cc6b4
commit
2500f842e9
@ -53,7 +53,7 @@ and ending when the function returns.
|
||||
Most automatic variables reside in memory.
|
||||
They can share their memory location with other automatic variables and parameters,
|
||||
to conserve memory usage.
|
||||
Some small automatic variables may be inlined to index registers.
|
||||
Some small automatic variables may be inlined to registers.
|
||||
They are not automatically initialized before reading, reading them before initialization yields an undefined value.
|
||||
Automatic local variables are not safe to use with reentrant functions, see the [relevant documentation](../lang/reentrancy.md) for more details.
|
||||
|
||||
@ -66,7 +66,7 @@ of the function call to the function they're defined in
|
||||
and ending when the function returns.
|
||||
They reside in memory and can share their memory location with other parameters and automatic variables,
|
||||
to conserve memory usage.
|
||||
Unlike automatic variables, they are never inlined into index registers.
|
||||
Unlike automatic variables, they are almost never inlined into registers.
|
||||
Parameters are not safe to use with reentrant functions, see the [relevant documentation](../lang/reentrancy.md) for more details.
|
||||
|
||||
|
||||
|
@ -89,13 +89,19 @@ This may cause problems if the parameter table is stored next to a hardware regi
|
||||
|
||||
* `--inline` – Inline functions automatically (experimental). See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
|
||||
|
||||
* `--fipo`, `--fno-ipo` – Whether should perform interprocedural optimization.
|
||||
It enables certain optimization similar to what inlining would enable, but without actual inlining.
|
||||
|
||||
* `--size` – Optimize for size, sacrificing some speed (experimental).
|
||||
|
||||
* `--fast` – Optimize for speed, even if it increases the size a bit (experimental).
|
||||
|
||||
* `--blast-processing` – Optimize for speed, even if it increases the size a lot (experimental).
|
||||
Enables `--inline` automatically.
|
||||
|
||||
* `--dangerous-optimizations` – Use dangerous optimizations (experimental). Dangerous optimizations are more likely to result in broken code.
|
||||
* `--dangerous-optimizations` – Use dangerous optimizations (experimental).
|
||||
Dangerous optimizations are more likely to result in broken code.
|
||||
Enables `--fipo` automatically.
|
||||
|
||||
## Warning options
|
||||
|
||||
|
@ -45,6 +45,8 @@ You may be also interested in the following:
|
||||
|
||||
* `--inline` – automatically inline functions for better optimization
|
||||
|
||||
* `--fipo` – enable interprocedural optimization
|
||||
|
||||
* `-s` – additionally generate assembly output
|
||||
|
||||
* `-g` – additionally generate a label file, in format compatible with VICE emulator
|
||||
|
@ -206,7 +206,7 @@ object CompilationFlag extends Enumeration {
|
||||
// compilation options for Z80
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack,
|
||||
// optimization options:
|
||||
DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
||||
DangerousOptimizations, InlineFunctions, InterproceduralOptimization, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
||||
// memory allocation options
|
||||
VariableOverlap, CompactReturnDispatchParams, LUnixRelocatableCode,
|
||||
// runtime check options
|
||||
@ -232,6 +232,7 @@ object CompilationFlag extends Enumeration {
|
||||
"emit_x80" -> EmitExtended80Opcodes,
|
||||
"emit_sharp" -> EmitSharpOpcodes,
|
||||
"ix_stack" -> UseIxForStack,
|
||||
"ipo" -> InterproceduralOptimization,
|
||||
"zeropage_register" -> ZeropagePseudoregister,
|
||||
"decimal_mode" -> DecimalMode,
|
||||
"ro_arrays" -> ReadOnlyArrays,
|
||||
|
@ -302,6 +302,12 @@ object Main {
|
||||
flag("--inline").action { c =>
|
||||
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
||||
}.description("Inline functions automatically.")
|
||||
flag("--ipo").action { c =>
|
||||
c.changeFlag(CompilationFlag.InterproceduralOptimization, true)
|
||||
}.description("Interprocedural optimization.").hidden()
|
||||
boolean("--fipo", "--fno-ipo").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.InterproceduralOptimization, v)
|
||||
}.description("Interprocedural optimization.")
|
||||
flag("-Os", "--size").action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, false).
|
||||
|
@ -2,14 +2,19 @@ package millfork.assembly
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.env.NormalFunction
|
||||
import millfork.node.NiceFunctionProperty
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
case class OptimizationContext(options: CompilationOptions,
|
||||
labelMap: Map[String, Int],
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)])
|
||||
|
||||
trait AssemblyOptimization[T <: AbstractCode] {
|
||||
def name: String
|
||||
|
||||
def optimize(f: NormalFunction, code: List[T], options: CompilationOptions, labelMap: Map[String, Int]): List[T] = optimize(f, code, options)
|
||||
def optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T] = optimize(f, code, context.options)
|
||||
|
||||
def optimize(f: NormalFunction, code: List[T], options: CompilationOptions): List[T] = optimize(f, code, options, Map())
|
||||
def optimize(f: NormalFunction, code: List[T], options: CompilationOptions): List[T] = optimize(f, code, OptimizationContext(options, Map(), Set()))
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import millfork.assembly.mos.OpcodeClasses._
|
||||
import millfork.assembly.mos.{AddrMode, opt, _}
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.env._
|
||||
import org.apache.commons.lang3.builder.HashCodeExclude
|
||||
|
||||
/**
|
||||
* These optimizations should not remove opportunities for more complex optimizations to trigger.
|
||||
@ -152,7 +151,7 @@ object AlwaysGoodOptimizations {
|
||||
HasOpcode(ANC) & HasAddrMode(Immediate) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) =>
|
||||
AssemblyLine.immediate(LDA, CompoundConstant(MathOperator.And, NumericConstant(ctx.get[Int](0), 1), ctx.get[Constant](1)).quickSimplify) :: Nil
|
||||
},
|
||||
(Elidable & HasA(0) & HasOpcodeIn(Set(ORA, EOR))) ~~> (code => code.map(_.copy(opcode = LDA))),
|
||||
(Elidable & HasA(0) & HasOpcodeIn(ORA, EOR)) ~~> (code => code.map(_.copy(opcode = LDA))),
|
||||
(Elidable & HasA(0) & HasOpcode(ADC) &
|
||||
HasClear(State.D) & HasClear(State.C) &
|
||||
// C stays cleared!
|
||||
@ -171,42 +170,42 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val MathOperationOnTwoIdenticalMemoryOperands = new RuleBasedAssemblyOptimization("Math operation on two identical memory operands",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(HasOpcodeIn(Set(STA, LDA, LAX)) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(HasOpcodeIn(STA, LDA, LAX) & HasAddrModeIn(ZeroPage, Absolute) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(Linear & DoesntChangeMemoryAt(9, 0) & Not(ChangesA)).* ~
|
||||
(HasClear(State.D) & HasClear(State.C) & HasOpcode(ADC) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchParameter(0) & Elidable) ~~> (code => code.init :+ AssemblyLine.implied(ASL)),
|
||||
(HasClear(State.D) & HasClear(State.C) & HasOpcode(ADC) & HasAddrModeIn(ZeroPage, Absolute) & MatchParameter(0) & Elidable) ~~> (code => code.init :+ AssemblyLine.implied(ASL)),
|
||||
|
||||
(HasOpcodeIn(Set(STA, LDA)) & HasAddrMode(AbsoluteX) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(HasOpcodeIn(STA, LDA) & HasAddrMode(AbsoluteX) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(Linear & DoesntChangeMemoryAt(9, 0) & Not(ChangesA) & Not(ChangesX)).* ~
|
||||
(HasClear(State.D) & HasClear(State.C) & HasOpcode(ADC) & HasAddrMode(AbsoluteX) & MatchParameter(0) & Elidable) ~~> (code => code.init :+ AssemblyLine.implied(ASL)),
|
||||
|
||||
(HasOpcodeIn(Set(STA, LDA, LAX)) & HasAddrMode(AbsoluteY) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(HasOpcodeIn(STA, LDA, LAX) & HasAddrMode(AbsoluteY) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(Linear & DoesntChangeMemoryAt(9, 0) & Not(ChangesA) & Not(ChangesY)).* ~
|
||||
(HasClear(State.D) & HasClear(State.C) & HasOpcode(ADC) & HasAddrMode(AbsoluteY) & MatchParameter(0) & Elidable) ~~> (code => code.init :+ AssemblyLine.implied(ASL)),
|
||||
|
||||
(HasOpcodeIn(Set(STA, LDA, LAX)) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(HasOpcodeIn(STA, LDA, LAX) & HasAddrModeIn(ZeroPage, Absolute) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(Linear & DoesntChangeMemoryAt(9, 0) & Not(ChangesA)).* ~
|
||||
(DoesntMatterWhatItDoesWith(State.N, State.Z) & HasOpcodeIn(Set(ORA, AND)) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchParameter(0) & Elidable) ~~> (code => code.init),
|
||||
(DoesntMatterWhatItDoesWith(State.N, State.Z) & HasOpcodeIn(ORA, AND) & HasAddrModeIn(ZeroPage, Absolute) & MatchParameter(0) & Elidable) ~~> (code => code.init),
|
||||
|
||||
(HasOpcodeIn(Set(STA, LDA, LAX)) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(HasOpcodeIn(STA, LDA, LAX) & HasAddrModeIn(ZeroPage, Absolute) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(Linear & DoesntChangeMemoryAt(9, 0) & Not(ChangesA)).* ~
|
||||
(DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & HasOpcode(ANC) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchParameter(0) & Elidable) ~~> (code => code.init),
|
||||
(DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & HasOpcode(ANC) & HasAddrModeIn(ZeroPage, Absolute) & MatchParameter(0) & Elidable) ~~> (code => code.init),
|
||||
|
||||
(HasOpcodeIn(Set(STA, LDA, LAX)) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(HasOpcodeIn(STA, LDA, LAX) & HasAddrModeIn(ZeroPage, Absolute) & MatchAddrMode(9) & MatchParameter(0)) ~
|
||||
(Linear & DoesntChangeMemoryAt(9, 0) & Not(ChangesA)).* ~
|
||||
(HasOpcode(EOR) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchParameter(0) & Elidable) ~~> (code => code.init :+ AssemblyLine.immediate(LDA, 0)),
|
||||
(HasOpcode(EOR) & HasAddrModeIn(ZeroPage, Absolute) & MatchParameter(0) & Elidable) ~~> (code => code.init :+ AssemblyLine.immediate(LDA, 0)),
|
||||
)
|
||||
|
||||
val PoinlessStoreBeforeStore = new RuleBasedAssemblyOptimization("Pointless store before store",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Elidable & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(1) & MatchAddrMode(2) & Set(STA, SAX, STX, STY, STZ)) ~
|
||||
(Elidable & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, SAX, STX, STY, STZ)) ~
|
||||
(LinearOrLabel & DoesNotConcernMemoryAt(2, 1)).* ~
|
||||
(MatchParameter(1) & MatchAddrMode(2) & Set(STA, SAX, STX, STY, STZ)) ~~> (_.tail),
|
||||
(Elidable & HasAddrModeIn(Set(AbsoluteX, ZeroPageX)) & MatchParameter(1) & MatchAddrMode(2) & Set(STA, STY, STZ)) ~
|
||||
(MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, SAX, STX, STY, STZ)) ~~> (_.tail),
|
||||
(Elidable & HasAddrModeIn(AbsoluteX, ZeroPageX) & MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, STY, STZ)) ~
|
||||
(LinearOrLabel & DoesntChangeMemoryAt(2, 1) & Not(ReadsMemory) & Not(ChangesX)).* ~
|
||||
(MatchParameter(1) & MatchAddrMode(2) & Set(STA, STY, STZ)) ~~> (_.tail),
|
||||
(Elidable & HasAddrModeIn(Set(AbsoluteY, ZeroPageY)) & MatchParameter(1) & MatchAddrMode(2) & Set(STA, SAX, STX, STZ)) ~
|
||||
(MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, STY, STZ)) ~~> (_.tail),
|
||||
(Elidable & HasAddrModeIn(AbsoluteY, ZeroPageY) & MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, SAX, STX, STZ)) ~
|
||||
(LinearOrLabel & DoesntChangeMemoryAt(2, 1) & Not(ReadsMemory) & Not(ChangesY)).* ~
|
||||
(MatchParameter(1) & MatchAddrMode(2) & Set(STA, SAX, STX, STZ)) ~~> (_.tail),
|
||||
(MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, SAX, STX, STZ)) ~~> (_.tail),
|
||||
)
|
||||
|
||||
private def pointlessNonimmediateStoreToTheSameVariable(ld1: Set[Opcode.Value], st1: Opcode.Value, ld2: Opcode.Value, st2: Opcode.Value) = {
|
||||
@ -222,13 +221,13 @@ object AlwaysGoodOptimizations {
|
||||
(HasOpcode(st1) & MatchAddrMode(2) & MatchParameter(3) & match1) ~
|
||||
(Linear & DoesntChangeIndexingInAddrMode(2) & (
|
||||
DoesntChangeMemoryAt(2, 3) |
|
||||
HasAddrModeIn(Set(IndexedX, IndexedY, IndexedZ, LongIndexedY, LongIndexedZ, Indirect)) &
|
||||
HasAddrModeIn(IndexedX, IndexedY, IndexedZ, LongIndexedY, LongIndexedZ, Indirect) &
|
||||
MatchParameter(3) & HasParameterWhere({
|
||||
case MemoryAddressConstant(th) => th.name.startsWith("__")
|
||||
case _ => false
|
||||
})
|
||||
)).* ~
|
||||
(Elidable & match2 & HasOpcode(st2) & MatchAddrMode(2) & MatchParameter(3)) ~~> (code => code.init),
|
||||
(Elidable & match2 & HasOpcode(st2) & MatchAddrMode(2) & MatchParameter(3)) ~~> (code => code.init)
|
||||
}
|
||||
|
||||
val PointlessStoreToTheSameVariable = new RuleBasedAssemblyOptimization("Pointless store to the same variable",
|
||||
@ -280,7 +279,7 @@ object AlwaysGoodOptimizations {
|
||||
(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) ~
|
||||
(Elidable & HasOpcode(STA) & HasAddrModeIn(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)
|
||||
@ -290,7 +289,7 @@ object AlwaysGoodOptimizations {
|
||||
},
|
||||
(HasOpcode(TAX)) ~
|
||||
(Elidable & Linear & Not(ChangesX) & Not(HasOpcode(STX))).*.capture(5) ~
|
||||
(Elidable & HasOpcode(STX) & HasAddrModeIn(Set(ZeroPage, Absolute, LongAbsolute))).capture(4) ~
|
||||
(Elidable & HasOpcode(STX) & HasAddrModeIn(ZeroPage, Absolute, LongAbsolute)).capture(4) ~
|
||||
Where(ctx => {
|
||||
val sta = ctx.get[List[AssemblyLine]](4).head
|
||||
val middleCode = ctx.get[List[AssemblyLine]](5)
|
||||
@ -300,7 +299,7 @@ object AlwaysGoodOptimizations {
|
||||
},
|
||||
(HasOpcode(TAY)) ~
|
||||
(Elidable & Linear & Not(ChangesY) & Not(HasOpcode(STY))).*.capture(5) ~
|
||||
(Elidable & HasOpcode(STY) & HasAddrModeIn(Set(ZeroPage, Absolute, LongAbsolute))).capture(4) ~
|
||||
(Elidable & HasOpcode(STY) & HasAddrModeIn(ZeroPage, Absolute, LongAbsolute)).capture(4) ~
|
||||
Where(ctx => {
|
||||
val sta = ctx.get[List[AssemblyLine]](4).head
|
||||
val middleCode = ctx.get[List[AssemblyLine]](5)
|
||||
@ -310,7 +309,7 @@ object AlwaysGoodOptimizations {
|
||||
},
|
||||
(HasOpcode(TAZ)) ~
|
||||
(Elidable & Linear & Not(ChangesIZ) & Not(HasOpcode(STZ))).*.capture(5) ~
|
||||
(Elidable & HasOpcode(STZ) & HasAddrModeIn(Set(ZeroPage, Absolute, LongAbsolute))).capture(4) ~
|
||||
(Elidable & HasOpcode(STZ) & HasAddrModeIn(ZeroPage, Absolute, LongAbsolute)).capture(4) ~
|
||||
Where(ctx => {
|
||||
val sta = ctx.get[List[AssemblyLine]](4).head
|
||||
val middleCode = ctx.get[List[AssemblyLine]](5)
|
||||
@ -373,9 +372,9 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val PointlessLoadBeforeReturn = new RuleBasedAssemblyOptimization("Pointless load before return",
|
||||
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(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),
|
||||
(HasOpcodeIn(LDA, TXA, TYA, EOR, AND, ORA, ANC) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_AF))).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail),
|
||||
(HasOpcodeIn(LDX, TAX, TSX, INX, DEX) & Elidable) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_XF))).* ~ HasOpcode(DISCARD_XF) ~~> (_.tail),
|
||||
(HasOpcodeIn(LDY, TAY, INY, DEY) & Elidable) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_YF))).* ~ HasOpcode(DISCARD_YF) ~~> (_.tail),
|
||||
(HasOpcode(LDX) & Elidable & MatchAddrMode(3) & MatchParameter(4)) ~
|
||||
(LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & DoesntChangeMemoryAt(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~
|
||||
(HasOpcode(TXA) & Elidable) ~
|
||||
@ -395,17 +394,17 @@ object AlwaysGoodOptimizations {
|
||||
val InefficientStashingToRegister = new RuleBasedAssemblyOptimization("Inefficient stashing to register",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
|
||||
(Elidable & HasOpcodeIn(Set(LDA, STA)) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcodeIn(LDA, STA) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsX) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.X)) ~~> (code => code.head :: (code.drop(2).init :+ code.head.copy(opcode = LDA))),
|
||||
|
||||
(Elidable & HasOpcodeIn(Set(LDA, STA)) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcodeIn(LDA, STA) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsY) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.Y)) ~~> (code => code.head :: (code.drop(2).init :+ code.head.copy(opcode = LDA))),
|
||||
|
||||
(Elidable & HasOpcodeIn(Set(LDA, STA)) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcodeIn(LDA, STA) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(TAZ) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsIZ) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(TZA) & DoesntMatterWhatItDoesWith(State.IZ)) ~~> (code => code.head :: (code.drop(2).init :+ code.head.copy(opcode = LDA))),
|
||||
@ -451,10 +450,10 @@ object AlwaysGoodOptimizations {
|
||||
}
|
||||
|
||||
private def operationPairBuilder4(op1: Opcode.Value, op1extra: AssemblyLinePattern, middle: AssemblyLinePattern, op2: Opcode.Value, op2extra: AssemblyLinePattern) = {
|
||||
(HasOpcode(op1) & op1extra & Elidable & HasAddrModeIn(Set(Absolute, ZeroPage, LongAbsolute)) & MatchParameter(3)) ~
|
||||
(HasOpcode(op1) & op1extra & Elidable & HasAddrModeIn(Absolute, ZeroPage, LongAbsolute) & MatchParameter(3)) ~
|
||||
middle.*.capture(1) ~
|
||||
Where(_.isExternallyLinearBlock(1)) ~
|
||||
(HasOpcode(op2) & op2extra & Elidable & HasAddrModeIn(Set(Absolute, ZeroPage, LongAbsolute)) & MatchParameter(3)) ~~> { (_, ctx) =>
|
||||
(HasOpcode(op2) & op2extra & Elidable & HasAddrModeIn(Absolute, ZeroPage, LongAbsolute) & MatchParameter(3)) ~~> { (_, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](1)
|
||||
}
|
||||
}
|
||||
@ -493,8 +492,8 @@ object AlwaysGoodOptimizations {
|
||||
operationPairBuilder3(PHX, Anything, PLX, Not(ChangesX) & Not(ConcernsStack), Some(DISCARD_XF)),
|
||||
operationPairBuilder3(PHY, Anything, PLY, Not(ChangesY) & Not(ConcernsStack), Some(DISCARD_YF)),
|
||||
operationPairBuilder3(PHZ, Anything, PLZ, Not(ChangesIZ) & Not(ConcernsStack), Some(DISCARD_YF)),
|
||||
operationPairBuilder3(PHD, Anything, PLD, Not(ChangesDirectPageRegister), None),
|
||||
operationPairBuilder3(PHB, Anything, PLB, Not(ChangesDataBankRegister), None),
|
||||
operationPairBuilder3(PHD, Anything, PLD, Not(HasOpcodeIn(ChangesDirectPageRegister)), None),
|
||||
operationPairBuilder3(PHB, Anything, PLB, Not(HasOpcodeIn(ChangesDataBankRegister)), None),
|
||||
operationPairBuilder3(INX, DoesntMatterWhatItDoesWith(State.N, State.Z), DEX, Not(ConcernsX) & Not(ReadsNOrZ), None),
|
||||
operationPairBuilder3(DEX, DoesntMatterWhatItDoesWith(State.N, State.Z), INX, Not(ConcernsX) & Not(ReadsNOrZ), None),
|
||||
operationPairBuilder3(INY, DoesntMatterWhatItDoesWith(State.N, State.Z), DEY, Not(ConcernsX) & Not(ReadsNOrZ), None),
|
||||
@ -529,19 +528,19 @@ object AlwaysGoodOptimizations {
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate)) ~
|
||||
(Elidable & HasOpcode(PHA)) ~
|
||||
(Linear & Not(ConcernsStack) | HasOpcodeIn(Set(JSR, BSR))).* ~
|
||||
(Linear & Not(ConcernsStack) | HasOpcodeIn(JSR, BSR)).* ~
|
||||
(Elidable & HasOpcode(PLA)) ~~> { code =>
|
||||
code.head :: (code.drop(2).init :+ code.head)
|
||||
},
|
||||
(Elidable & HasOpcode(LDX) & HasAddrMode(Immediate)) ~
|
||||
(Elidable & HasOpcode(PHX)) ~
|
||||
(Linear & Not(ConcernsStack) | HasOpcodeIn(Set(JSR, BSR))).* ~
|
||||
(Linear & Not(ConcernsStack) | HasOpcodeIn(JSR, BSR)).* ~
|
||||
(Elidable & HasOpcode(PLX)) ~~> { code =>
|
||||
code.head :: (code.drop(2).init :+ code.head)
|
||||
},
|
||||
(Elidable & HasOpcode(LDY) & HasAddrMode(Immediate)) ~
|
||||
(Elidable & HasOpcode(PHY)) ~
|
||||
(Linear & Not(ConcernsStack) | HasOpcodeIn(Set(JSR, BSR))).* ~
|
||||
(Linear & Not(ConcernsStack) | HasOpcodeIn(JSR, BSR)).* ~
|
||||
(Elidable & HasOpcode(PLY)) ~~> { code =>
|
||||
code.head :: (code.drop(2).init :+ code.head)
|
||||
},
|
||||
@ -598,10 +597,10 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val BranchInPlaceRemoval = new RuleBasedAssemblyOptimization("Branch in place",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(AllDirectJumps & HasAddrModeIn(Set(Absolute, Relative, LongAbsolute, LongRelative)) & MatchParameter(0) & Elidable) ~
|
||||
HasOpcodeIn(NoopDiscardsFlags).* ~
|
||||
(HasOpcodeIn(AllDirectJumps) & HasAddrModeIn(Absolute, Relative, LongAbsolute, LongRelative) & MatchParameter(0) & Elidable) ~
|
||||
NoopDiscardsFlags.* ~
|
||||
(HasOpcode(LABEL) & MatchParameter(0)) ~~> (c => c.last :: Nil),
|
||||
(AllDirectJumps & HasAddrModeIn(Set(Absolute, Relative, LongAbsolute, LongRelative)) & MatchParameter(0) & Elidable) ~
|
||||
(HasOpcodeIn(AllDirectJumps) & HasAddrModeIn(Absolute, Relative, LongAbsolute, LongRelative) & MatchParameter(0) & Elidable) ~
|
||||
(HasOpcode(LABEL) & Not(MatchParameter(0))).* ~
|
||||
(HasOpcode(LABEL) & MatchParameter(0)) ~~> (_.tail),
|
||||
(HasOpcode(BEQ) & MatchParameter(0) & Elidable) ~ HasOpcode(BNE) ~
|
||||
@ -653,13 +652,13 @@ object AlwaysGoodOptimizations {
|
||||
(Elidable & HasOpcode(JMP) & HasAddrMode(Absolute) & MatchParameter(0)) ~
|
||||
(Not(HasOpcode(LABEL)) & Not(MatchParameter(0))).* ~
|
||||
(HasOpcode(LABEL) & MatchParameter(0)) ~
|
||||
(HasOpcode(LABEL) | HasOpcodeIn(NoopDiscardsFlags)).* ~
|
||||
(HasOpcode(LABEL) | NoopDiscardsFlags).* ~
|
||||
HasOpcode(RTS) ~~> (code => AssemblyLine.implied(RTS) :: code.tail),
|
||||
(Elidable & HasOpcodeIn(ShortBranching) & MatchParameter(0)) ~
|
||||
(HasOpcodeIn(NoopDiscardsFlags).* ~
|
||||
(Elidable & ShortBranching & MatchParameter(0)) ~
|
||||
(NoopDiscardsFlags.* ~
|
||||
(Elidable & HasOpcode(RTS))).capture(1) ~
|
||||
(HasOpcode(LABEL) & MatchParameter(0)) ~
|
||||
HasOpcodeIn(NoopDiscardsFlags).* ~
|
||||
NoopDiscardsFlags.* ~
|
||||
(Elidable & HasOpcode(RTS)) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1)),
|
||||
)
|
||||
|
||||
@ -693,10 +692,10 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val TailCallOptimization = new RuleBasedAssemblyOptimization("Tail call optimization",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Elidable & HasOpcode(JSR)) ~ HasOpcodeIn(NoopDiscardsFlags).* ~ (Elidable & HasOpcode(RTS)) ~~> (c => c.head.copy(opcode = JMP) :: Nil),
|
||||
(Elidable & HasOpcode(JSR)) ~ NoopDiscardsFlags.* ~ (Elidable & HasOpcode(RTS)) ~~> (c => c.head.copy(opcode = JMP) :: Nil),
|
||||
(Elidable & HasOpcode(JSR)) ~
|
||||
HasOpcode(LABEL).* ~
|
||||
HasOpcodeIn(NoopDiscardsFlags).*.capture(0) ~
|
||||
NoopDiscardsFlags.*.capture(0) ~
|
||||
HasOpcode(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](0) ++ (code.head.copy(opcode = JMP) :: code.tail)),
|
||||
)
|
||||
|
||||
@ -707,10 +706,10 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val PoinlessFlagChange = new RuleBasedAssemblyOptimization("Pointless flag change",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(HasOpcodeIn(Set(CMP, CPX, CPY)) & Elidable) ~ NoopDiscardsFlags ~~> (_.tail),
|
||||
(OverwritesC & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsC) & Not(DiscardsC)).* ~ DiscardsC ~~> (_.tail),
|
||||
(OverwritesD & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsD) & Not(DiscardsD)).* ~ DiscardsD ~~> (_.tail),
|
||||
(OverwritesV & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsV) & Not(DiscardsV)).* ~ DiscardsV ~~> (_.tail)
|
||||
(HasOpcodeIn(CMP, CPX, CPY) & Elidable) ~ NoopDiscardsFlags ~~> (_.tail),
|
||||
(HasOpcodeIn(OverwritesC) & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsC) & Not(HasOpcodeIn(DiscardsC))).* ~ HasOpcodeIn(DiscardsC) ~~> (_.tail),
|
||||
(HasOpcodeIn(OverwritesD) & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsD) & Not(HasOpcodeIn(DiscardsD))).* ~ HasOpcodeIn(DiscardsD) ~~> (_.tail),
|
||||
(HasOpcodeIn(OverwritesV) & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsV) & Not(HasOpcodeIn(DiscardsV))).* ~ HasOpcodeIn(DiscardsV) ~~> (_.tail)
|
||||
)
|
||||
|
||||
// Optimizing Bxx to JMP is generally bad, but it may allow for better optimizations later
|
||||
@ -733,18 +732,18 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val ReverseFlowAnalysis = new RuleBasedAssemblyOptimization("Reverse flow analysis",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcodeIn(Set(TXA, TYA, LDA, EOR, ORA, AND)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(TXA, TYA, LDA, EOR, ORA, AND) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcode(ANC) & DoesntMatterWhatItDoesWith(State.A, State.C, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(TAX, TSX, LDX, INX, DEX)) & DoesntMatterWhatItDoesWith(State.X, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(TAY, LDY, DEY, INY)) & DoesntMatterWhatItDoesWith(State.Y, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(LAX)) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(SEC, CLC)) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(CLD, SED)) & DoesntMatterWhatItDoesWith(State.D)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(TAX, TSX, LDX, INX, DEX) & DoesntMatterWhatItDoesWith(State.X, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(TAY, LDY, DEY, INY) & DoesntMatterWhatItDoesWith(State.Y, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(LAX) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(SEC, CLC) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(CLD, SED) & DoesntMatterWhatItDoesWith(State.D)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcode(CLV) & DoesntMatterWhatItDoesWith(State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(CMP, CPX, CPY)) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(BIT)) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z, State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(ASL, LSR, ROL, ROR)) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.A, State.C, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(ADC, SBC)) & DoesntMatterWhatItDoesWith(State.A, State.C, State.V, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(CMP, CPX, CPY) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(BIT) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z, State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(ASL, LSR, ROL, ROR) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.A, State.C, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(ADC, SBC) & DoesntMatterWhatItDoesWith(State.A, State.C, State.V, State.N, State.Z)) ~~> (_ => Nil),
|
||||
)
|
||||
|
||||
private def modificationOfJustWrittenValue(store: Opcode.Value,
|
||||
@ -892,14 +891,14 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val IdempotentDuplicateRemoval = new RuleBasedAssemblyOptimization("Idempotent duplicate operation",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
HasOpcode(RTS) ~ HasOpcodeIn(NoopDiscardsFlags).* ~ (HasOpcode(RTS) ~ Elidable) ~~> (_.take(1)) ::
|
||||
HasOpcode(RTI) ~ HasOpcodeIn(NoopDiscardsFlags).* ~ (HasOpcode(RTI) ~ Elidable) ~~> (_.take(1)) ::
|
||||
HasOpcode(DISCARD_XF) ~ (Not(HasOpcode(DISCARD_XF)) & HasOpcodeIn(NoopDiscardsFlags + LABEL)).* ~ HasOpcode(DISCARD_XF) ~~> (_.tail) ::
|
||||
HasOpcode(DISCARD_AF) ~ (Not(HasOpcode(DISCARD_AF)) & HasOpcodeIn(NoopDiscardsFlags + LABEL)).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail) ::
|
||||
HasOpcode(DISCARD_YF) ~ (Not(HasOpcode(DISCARD_YF)) & HasOpcodeIn(NoopDiscardsFlags + LABEL)).* ~ HasOpcode(DISCARD_YF) ~~> (_.tail) ::
|
||||
HasOpcode(RTS) ~ NoopDiscardsFlags.* ~ (HasOpcode(RTS) ~ Elidable) ~~> (_.take(1)) ::
|
||||
HasOpcode(RTI) ~ NoopDiscardsFlags.* ~ (HasOpcode(RTI) ~ Elidable) ~~> (_.take(1)) ::
|
||||
HasOpcode(DISCARD_XF) ~ (Not(HasOpcode(DISCARD_XF)) & NoopDiscardsFlagsOrLabel).* ~ HasOpcode(DISCARD_XF) ~~> (_.tail) ::
|
||||
HasOpcode(DISCARD_AF) ~ (Not(HasOpcode(DISCARD_AF)) & NoopDiscardsFlagsOrLabel).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail) ::
|
||||
HasOpcode(DISCARD_YF) ~ (Not(HasOpcode(DISCARD_YF)) & NoopDiscardsFlagsOrLabel).* ~ HasOpcode(DISCARD_YF) ~~> (_.tail) ::
|
||||
List(RTS, RTI, SEC, CLC, CLV, CLD, SED, SEI, CLI, TAX, TXA, TYA, TAY, TXS, TSX).flatMap { opcode =>
|
||||
Seq(
|
||||
(HasOpcode(opcode) & Elidable) ~ (HasOpcodeIn(NoopDiscardsFlags) | HasOpcode(LABEL)).* ~ HasOpcode(opcode) ~~> (_.tail),
|
||||
(HasOpcode(opcode) & Elidable) ~ (NoopDiscardsFlags | HasOpcode(LABEL)).* ~ HasOpcode(opcode) ~~> (_.tail),
|
||||
HasOpcode(opcode) ~ (HasOpcode(opcode) ~ Elidable) ~~> (_.init),
|
||||
)
|
||||
}: _*
|
||||
@ -907,30 +906,30 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val PointlessRegisterTransfers = new RuleBasedAssemblyOptimization("Pointless register transfers",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
HasOpcode(TYA) ~ (Elidable & Set(TYA, TAY)) ~~> (_.init),
|
||||
HasOpcode(TXA) ~ (Elidable & Set(TXA, TAX)) ~~> (_.init),
|
||||
HasOpcode(TAY) ~ (Elidable & Set(TYA, TAY)) ~~> (_.init),
|
||||
HasOpcode(TAX) ~ (Elidable & Set(TXA, TAX)) ~~> (_.init),
|
||||
HasOpcode(TSX) ~ (Elidable & Set(TXS, TSX)) ~~> (_.init),
|
||||
HasOpcode(TXS) ~ (Elidable & Set(TXS, TSX)) ~~> (_.init),
|
||||
HasOpcodeIn(Set(TXA, TAX, LAX, LXA)) ~
|
||||
HasOpcode(TYA) ~ (Elidable & HasOpcodeIn(TYA, TAY)) ~~> (_.init),
|
||||
HasOpcode(TXA) ~ (Elidable & HasOpcodeIn(TXA, TAX)) ~~> (_.init),
|
||||
HasOpcode(TAY) ~ (Elidable & HasOpcodeIn(TYA, TAY)) ~~> (_.init),
|
||||
HasOpcode(TAX) ~ (Elidable & HasOpcodeIn(TXA, TAX)) ~~> (_.init),
|
||||
HasOpcode(TSX) ~ (Elidable & HasOpcodeIn(TXS, TSX)) ~~> (_.init),
|
||||
HasOpcode(TXS) ~ (Elidable & HasOpcodeIn(TXS, TSX)) ~~> (_.init),
|
||||
HasOpcodeIn(TXA, TAX, LAX, LXA) ~
|
||||
(Linear & Not(ChangesNAndZ) & Not(ChangesA) & Not(ChangesX)).* ~
|
||||
(Elidable & HasOpcodeIn(Set(TXA, TAX))) ~~> (_.init),
|
||||
HasOpcodeIn(Set(TYA, TAY)) ~
|
||||
(Elidable & HasOpcodeIn(TXA, TAX)) ~~> (_.init),
|
||||
HasOpcodeIn(TYA, TAY) ~
|
||||
(Linear & Not(ChangesNAndZ) & Not(ChangesA) & Not(ChangesX)).* ~
|
||||
(Elidable & HasOpcodeIn(Set(TYA, TAY))) ~~> (_.init),
|
||||
HasOpcode(TSX) ~ (Not(ChangesX) & Not(ChangesS) & Linear).* ~ (Elidable & Set(TXS, TSX)) ~~> (_.init),
|
||||
HasOpcode(TXS) ~ (Not(ChangesX) & Not(ChangesS) & Linear).* ~ (Elidable & Set(TXS, TSX)) ~~> (_.init),
|
||||
(Elidable & HasOpcodeIn(TYA, TAY)) ~~> (_.init),
|
||||
HasOpcode(TSX) ~ (Not(ChangesX) & Not(ChangesS) & Linear).* ~ (Elidable & HasOpcodeIn(TXS, TSX)) ~~> (_.init),
|
||||
HasOpcode(TXS) ~ (Not(ChangesX) & Not(ChangesS) & Linear).* ~ (Elidable & HasOpcodeIn(TXS, TSX)) ~~> (_.init),
|
||||
)
|
||||
|
||||
val PointlessRegisterTransfersBeforeStore = new RuleBasedAssemblyOptimization("Pointless register transfers before store",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcode(TXA)) ~
|
||||
(Linear & Not(ConcernsA) & Not(ConcernsX)).* ~
|
||||
(Elidable & HasOpcode(STA) & HasAddrModeIn(Set(ZeroPage, ZeroPageY, Absolute)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (code => code.tail.init :+ code.last.copy(opcode = STX)),
|
||||
(Elidable & HasOpcode(STA) & HasAddrModeIn(ZeroPage, ZeroPageY, Absolute) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (code => code.tail.init :+ code.last.copy(opcode = STX)),
|
||||
(Elidable & HasOpcode(TYA)) ~
|
||||
(Linear & Not(ConcernsA) & Not(ConcernsY)).* ~
|
||||
(Elidable & HasOpcode(STA) & HasAddrModeIn(Set(ZeroPage, ZeroPageX, Absolute)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (code => code.tail.init :+ code.last.copy(opcode = STY)),
|
||||
(Elidable & HasOpcode(STA) & HasAddrModeIn(ZeroPage, ZeroPageX, Absolute) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (code => code.tail.init :+ code.last.copy(opcode = STY)),
|
||||
)
|
||||
|
||||
|
||||
@ -939,41 +938,41 @@ object AlwaysGoodOptimizations {
|
||||
(HasOpcode(TAX) & Elidable) ~
|
||||
HasOpcode(LABEL).* ~
|
||||
HasOpcode(TXA).? ~
|
||||
ManyWhereAtLeastOne(HasOpcodeIn(NoopDiscardsFlags), HasOpcode(DISCARD_XF)).capture(1) ~
|
||||
ManyWhereAtLeastOne(NoopDiscardsFlags, HasOpcode(DISCARD_XF)).capture(1) ~
|
||||
HasOpcode(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
|
||||
(HasOpcode(TSX) & Elidable) ~
|
||||
HasOpcode(LABEL).* ~
|
||||
HasOpcode(TSX).? ~
|
||||
ManyWhereAtLeastOne(HasOpcodeIn(NoopDiscardsFlags), HasOpcode(DISCARD_XF)).capture(1) ~
|
||||
ManyWhereAtLeastOne(NoopDiscardsFlags, HasOpcode(DISCARD_XF)).capture(1) ~
|
||||
HasOpcode(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
|
||||
(HasOpcode(TXA) & Elidable) ~
|
||||
HasOpcode(LABEL).* ~
|
||||
HasOpcode(TAX).? ~
|
||||
ManyWhereAtLeastOne(HasOpcodeIn(NoopDiscardsFlags), HasOpcode(DISCARD_AF)).capture(1) ~
|
||||
ManyWhereAtLeastOne(NoopDiscardsFlags, HasOpcode(DISCARD_AF)).capture(1) ~
|
||||
HasOpcode(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
|
||||
(HasOpcode(TAY) & Elidable) ~
|
||||
HasOpcode(LABEL).* ~
|
||||
HasOpcode(TYA).? ~
|
||||
ManyWhereAtLeastOne(HasOpcodeIn(NoopDiscardsFlags), HasOpcode(DISCARD_YF)).capture(1) ~
|
||||
ManyWhereAtLeastOne(NoopDiscardsFlags, HasOpcode(DISCARD_YF)).capture(1) ~
|
||||
HasOpcode(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
|
||||
(HasOpcode(TYA) & Elidable) ~
|
||||
HasOpcode(LABEL).* ~
|
||||
HasOpcode(TAY).? ~
|
||||
ManyWhereAtLeastOne(HasOpcodeIn(NoopDiscardsFlags), HasOpcode(DISCARD_AF)).capture(1) ~
|
||||
ManyWhereAtLeastOne(NoopDiscardsFlags, HasOpcode(DISCARD_AF)).capture(1) ~
|
||||
HasOpcode(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
|
||||
)
|
||||
|
||||
val PointlessRegisterTransfersBeforeCompare = new RuleBasedAssemblyOptimization("Pointless register transfers and loads before compare",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
HasOpcodeIn(Set(DEX, INX, LDX, LAX)) ~
|
||||
HasOpcodeIn(DEX, INX, LDX, LAX) ~
|
||||
(HasOpcode(TXA) & Elidable & DoesntMatterWhatItDoesWith(State.A)) ~~> (code => code.init),
|
||||
HasOpcodeIn(Set(DEY, INY, LDY)) ~
|
||||
HasOpcodeIn(DEY, INY, LDY) ~
|
||||
(HasOpcode(TYA) & Elidable & DoesntMatterWhatItDoesWith(State.A)) ~~> (code => code.init),
|
||||
(HasOpcodeIn(Set(DEC, INC, ASL, ROL, ROR, LSR, SLO, SRE, RLA, RRA, ISC, DCP)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(DEC, INC, ASL, ROL, ROR, LSR, SLO, SRE, RLA, RRA, ISC, DCP) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcode(LDA) & Elidable & DoesntMatterWhatItDoesWith(State.A) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init),
|
||||
(HasOpcodeIn(Set(DEC, INC, ASL, ROL, ROR, LSR, SLO, SRE, RLA, RRA, ISC, DCP)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(DEC, INC, ASL, ROL, ROR, LSR, SLO, SRE, RLA, RRA, ISC, DCP) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcode(LDX) & Elidable & DoesntMatterWhatItDoesWith(State.X) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init),
|
||||
(HasOpcodeIn(Set(DEC, INC, ASL, ROL, ROR, LSR, SLO, SRE, RLA, RRA, ISC, DCP)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(DEC, INC, ASL, ROL, ROR, LSR, SLO, SRE, RLA, RRA, ISC, DCP) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcode(LDY) & Elidable & DoesntMatterWhatItDoesWith(State.Y) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init),
|
||||
)
|
||||
|
||||
@ -989,9 +988,9 @@ object AlwaysGoodOptimizations {
|
||||
((ShortBranching -- ReadsNOrZ) & MatchParameter(0))
|
||||
}
|
||||
val inner: AssemblyPattern = if (withRts) {
|
||||
(Linear & Not(readsI) & Not(ReadsNOrZ ++ NoopDiscardsFlags)).* ~
|
||||
ManyWhereAtLeastOne(HasOpcodeIn(NoopDiscardsFlags), HasOpcode(discardIF)) ~
|
||||
HasOpcodeIn(Set(RTS, RTI)) ~
|
||||
(Linear & Not(readsI) & Not(ReadsNOrZ | NoopDiscardsFlags)).* ~
|
||||
ManyWhereAtLeastOne(NoopDiscardsFlags, HasOpcode(discardIF)) ~
|
||||
HasOpcodeIn(RTS, RTI) ~
|
||||
Not(HasOpcode(LABEL)).*
|
||||
} else {
|
||||
(Linear & Not(concernsI) & Not(ChangesA) & Not(ReadsNOrZ)).*
|
||||
@ -1072,36 +1071,36 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val PoinlessLoadBeforeAnotherLoad = new RuleBasedAssemblyOptimization("Pointless load before another load",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Set(LDA, TXA, TYA) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ)).* ~ OverwritesA ~~> (_.tail),
|
||||
(Set(LDX, TAX, TSX) & Elidable) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ)).* ~ OverwritesX ~~> (_.tail),
|
||||
(Set(LDY, TAY) & Elidable) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ)).* ~ OverwritesY ~~> (_.tail),
|
||||
(HasOpcodeIn(LDA, TXA, TYA) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ)).* ~ OverwritesA ~~> (_.tail),
|
||||
(HasOpcodeIn(LDX, TAX, TSX) & Elidable) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ)).* ~ OverwritesX ~~> (_.tail),
|
||||
(HasOpcodeIn(LDY, TAY) & Elidable) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ)).* ~ OverwritesY ~~> (_.tail),
|
||||
)
|
||||
|
||||
// TODO: better proofs that memory doesn't change
|
||||
val PointlessLoadAfterLoadOrStore = new RuleBasedAssemblyOptimization("Pointless load after load or store",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
|
||||
(HasOpcodeIn(Set(LDA, STA)) & HasAddrMode(Immediate) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDA, STA) & HasAddrMode(Immediate) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF))).* ~
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDX, STX)) & HasAddrMode(Immediate) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDX, STX) & HasAddrMode(Immediate) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF))).* ~
|
||||
(Elidable & HasOpcode(LDX) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDY, STY)) & HasAddrMode(Immediate) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDY, STY) & HasAddrMode(Immediate) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF))).* ~
|
||||
(Elidable & HasOpcode(LDY) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDA, STA)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDX, STX)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDX, STX) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDY, STY)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDY, STY) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
@ -1129,49 +1128,49 @@ object AlwaysGoodOptimizations {
|
||||
(Linear & Not(ChangesY) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDA, STA)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(ShortConditionalBranching & MatchParameter(2)) ~
|
||||
(Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(HasOpcode(LABEL) & MatchParameter(2)) ~
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDX, STX)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDX, STX) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(ShortConditionalBranching & MatchParameter(2)) ~
|
||||
(Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(HasOpcode(LABEL) & MatchParameter(2)) ~
|
||||
(Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDY, STY)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDY, STY) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(ShortConditionalBranching & MatchParameter(2)) ~
|
||||
(Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(HasOpcode(LABEL) & MatchParameter(2)) ~
|
||||
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDA, STA)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(ShortBranching) & MatchParameter(3)) ~
|
||||
(HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(ShortBranching & MatchParameter(3)) ~
|
||||
(Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(HasOpcode(LABEL) & MatchParameter(3) & HasCallerCount(1)) ~
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
HasOpcodeIn(Set(TXA, TAX, LAX, LXA)) ~
|
||||
(Not(Set(TXA, TAX)) & Linear & Not(ChangesA) & Not(ChangesX)).* ~
|
||||
(Elidable & HasOpcodeIn(Set(TXA, TAX)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
HasOpcodeIn(TXA, TAX, LAX, LXA) ~
|
||||
(Not(HasOpcodeIn(TXA, TAX)) & Linear & Not(ChangesA) & Not(ChangesX)).* ~
|
||||
(Elidable & HasOpcodeIn(TXA, TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
HasOpcodeIn(Set(TYA, TAY)) ~
|
||||
(Not(Set(TYA, TAY)) & Linear & Not(ChangesA) & Not(ChangesY)).* ~
|
||||
(Elidable & HasOpcodeIn(Set(TYA, TAY)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
HasOpcodeIn(TYA, TAY) ~
|
||||
(Not(HasOpcodeIn(TYA, TAY)) & Linear & Not(ChangesA) & Not(ChangesY)).* ~
|
||||
(Elidable & HasOpcodeIn(TYA, TAY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(STA, LDA)) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(STA, LDA) & HasAddrModeIn(ZeroPage, Absolute) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(HasOpcode(TAX)) & Not(ChangesA) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
HasOpcode(TAX) ~
|
||||
(LinearOrBranch & Not(ChangesX) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDX) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
(Elidable & HasOpcode(LDX) & HasAddrModeIn(ZeroPage, Absolute) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(STA, LDA)) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(STA, LDA) & HasAddrModeIn(ZeroPage, Absolute) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(HasOpcode(TAY)) & Not(ChangesA) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
HasOpcode(TAY) ~
|
||||
(LinearOrBranch & Not(ChangesY) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDY) & HasAddrModeIn(Set(ZeroPage, Absolute)) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
(Elidable & HasOpcode(LDY) & HasAddrModeIn(ZeroPage, Absolute) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
|
||||
)
|
||||
|
||||
val RearrangableLoadFromTheSameLocation = new RuleBasedAssemblyOptimization("Rearrangable load from the same location",
|
||||
@ -1223,19 +1222,19 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val PointlessOperationFromFlow = new RuleBasedAssemblyOptimization("Pointless operation from flow",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcodeIn(Set(LDA, TXA, TYA, AND, EOR, ORA, XAA)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(LDX, TSX, TAX, SBX, INX, DEX)) & DoesntMatterWhatItDoesWith(State.X, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(LDY, TAY, INY, DEY)) & DoesntMatterWhatItDoesWith(State.Y, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(LAX, LXA)) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(ANC, ALR)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(ADC, SBC, ARR)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C, State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(CMP, CPY, CPX, BIT)) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(ROL, ROR, ASL, LSR)) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(INC, DEC)) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(CLC, SEC)) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(CLD, SED)) & DoesntMatterWhatItDoesWith(State.D)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(CLV)) & DoesntMatterWhatItDoesWith(State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(LDA, TXA, TYA, AND, EOR, ORA, XAA) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(LDX, TSX, TAX, SBX, INX, DEX) & DoesntMatterWhatItDoesWith(State.X, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(LDY, TAY, INY, DEY) & DoesntMatterWhatItDoesWith(State.Y, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(LAX, LXA) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(ANC, ALR) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(ADC, SBC, ARR) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C, State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(CMP, CPY, CPX, BIT) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(ROL, ROR, ASL, LSR) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z, State.C)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(INC, DEC) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(CLC, SEC) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(CLD, SED) & DoesntMatterWhatItDoesWith(State.D)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(CLV) & DoesntMatterWhatItDoesWith(State.V)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(ORA, EOR) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcode(AND) & HasImmediate(0xff) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcode(ANC) & HasImmediate(0xff) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C)) ~~> (_ => Nil),
|
||||
)
|
||||
@ -1311,12 +1310,12 @@ object AlwaysGoodOptimizations {
|
||||
val RearrangeMath = new RuleBasedAssemblyOptimization("Rearranging math",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate)) ~
|
||||
(Elidable & HasOpcodeIn(Set(CLC, SEC))) ~
|
||||
(Elidable & HasOpcodeIn(CLC, SEC)) ~
|
||||
(Elidable & HasOpcode(ADC) & Not(HasAddrMode(Immediate))) ~~> { c =>
|
||||
c.last.copy(opcode = LDA) :: c(1) :: c.head.copy(opcode = ADC) :: Nil
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate)) ~
|
||||
(Elidable & HasOpcodeIn(Set(ADC, EOR, ORA, AND)) & Not(HasAddrMode(Immediate))) ~~> { c =>
|
||||
(Elidable & HasOpcodeIn(ADC, EOR, ORA, AND) & Not(HasAddrMode(Immediate))) ~~> { c =>
|
||||
c.last.copy(opcode = LDA) :: c.head.copy(opcode = c.last.opcode) :: Nil
|
||||
},
|
||||
)
|
||||
@ -1433,17 +1432,17 @@ object AlwaysGoodOptimizations {
|
||||
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 =>
|
||||
(Elidable & HasOpcodeIn(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 =>
|
||||
(Elidable & HasOpcodeIn(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 =>
|
||||
(Elidable & HasOpcodeIn(ROL, ROR) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> { code =>
|
||||
code.last.copy(addrMode = AddrMode.Implied, parameter = Constant.Zero) :: code.init
|
||||
},
|
||||
)
|
||||
@ -1513,8 +1512,8 @@ object AlwaysGoodOptimizations {
|
||||
val jump = Elidable & HasOpcodeIn(Set(JMP, if (firstSet) BCS else BCC, if (zeroIfSet) BEQ else BNE)) & MatchParameter(1)
|
||||
val elseLabel = Elidable & HasOpcode(LABEL) & MatchParameter(0)
|
||||
val afterLabel = Elidable & HasOpcode(LABEL) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.N, State.V, State.Z)
|
||||
val store = Elidable & (Not(ReadsC) & Linear | HasOpcodeIn(Set(RTS, JSR, RTI, RTL, BSR)))
|
||||
val secondReturn = (Elidable & HasOpcodeIn(Set(RTS, RTI) | NoopDiscardsFlags)).*.capture(6)
|
||||
val store = Elidable & (Not(ReadsC) & Linear | HasOpcodeIn(RTS, JSR, RTI, RTL, BSR))
|
||||
val secondReturn = (Elidable & (HasOpcodeIn(RTS, RTI) | NoopDiscardsFlags)).*.capture(6)
|
||||
val where = Where { ctx =>
|
||||
ctx.get[List[AssemblyLine]](4) == ctx.get[List[AssemblyLine]](5) ||
|
||||
ctx.get[List[AssemblyLine]](4) == ctx.get[List[AssemblyLine]](5) ++ ctx.get[List[AssemblyLine]](6)
|
||||
@ -1562,7 +1561,7 @@ object AlwaysGoodOptimizations {
|
||||
val Adc0Optimization = new RuleBasedAssemblyOptimization("ADC #0/#1 optimization",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(Elidable & HasOpcode(LDA) & HasImmediate(0) & HasClear(State.D)) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(Set(ZeroPage, ZeroPageX, Absolute, AbsoluteX))) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(ZeroPage, ZeroPageX, Absolute, AbsoluteX)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
|
||||
val label = getNextLabel("ah")
|
||||
List(
|
||||
@ -1570,7 +1569,7 @@ object AlwaysGoodOptimizations {
|
||||
code.last.copy(opcode = INC),
|
||||
AssemblyLine.label(label))
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(Set(ZeroPage, ZeroPageX, Absolute, AbsoluteX))) ~
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(ZeroPage, ZeroPageX, Absolute, AbsoluteX)) ~
|
||||
(Elidable & HasOpcode(ADC) & HasImmediate(0) & HasClear(State.D)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
|
||||
val label = getNextLabel("ah")
|
||||
@ -1580,11 +1579,11 @@ object AlwaysGoodOptimizations {
|
||||
AssemblyLine.label(label))
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & HasImmediate(1) & HasClear(State.D) & HasClear(State.C)) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(Set(ZeroPage, ZeroPageX, Absolute, AbsoluteX))) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(ZeroPage, ZeroPageX, Absolute, AbsoluteX)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
|
||||
List(code.last.copy(opcode = INC))
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(1) & HasClear(State.C) & MatchParameter(2) & HasAddrModeIn(Set(ZeroPage, ZeroPageX, Absolute, AbsoluteX))) ~
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(1) & HasClear(State.C) & MatchParameter(2) & HasAddrModeIn(ZeroPage, ZeroPageX, Absolute, AbsoluteX)) ~
|
||||
(Elidable & HasOpcode(ADC) & HasImmediate(1) & HasClear(State.D)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
|
||||
List(code.last.copy(opcode = INC))
|
||||
@ -1633,7 +1632,7 @@ object AlwaysGoodOptimizations {
|
||||
(HasOpcode(AND) & HasImmediate(1)) ~
|
||||
(Linear & Not(ChangesNAndZ) & Not(HasOpcode(CLC)) & Not(ChangesA)).* ~
|
||||
(Elidable & HasOpcode(CLC) & HasClear(State.D)) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(0) & MatchParameter(1) & HasAddrModeIn(Set(ZeroPage, ZeroPageX, Absolute, AbsoluteX))) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(0) & MatchParameter(1) & HasAddrModeIn(ZeroPage, ZeroPageX, Absolute, AbsoluteX)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.V, State.Z, State.N, State.A)) ~~> { code =>
|
||||
val label = getNextLabel("in")
|
||||
code.take(code.length - 3) ++ List(
|
||||
@ -1644,7 +1643,7 @@ object AlwaysGoodOptimizations {
|
||||
},
|
||||
(HasOpcode(ANC) & HasImmediate(1)) ~
|
||||
(Linear & Not(ChangesNAndZ) & Not(ChangesA) & (Not(ChangesC) | HasOpcode(CLC))).* ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(0) & MatchParameter(1) & HasClear(State.D) & HasAddrModeIn(Set(ZeroPage, ZeroPageX, Absolute, AbsoluteX))) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(0) & MatchParameter(1) & HasClear(State.D) & HasAddrModeIn(ZeroPage, ZeroPageX, Absolute, AbsoluteX)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.V, State.Z, State.N, State.A)) ~~> { code =>
|
||||
val label = getNextLabel("in")
|
||||
code.head.copy(opcode = AND) :: code.take(code.length - 2).tail ++ List(
|
||||
@ -1758,8 +1757,8 @@ object AlwaysGoodOptimizations {
|
||||
(Elidable & HasOpcode(AND) & HasImmediate(1)) ~
|
||||
((Elidable & Linear & Not(ChangesMemory) & DoesNotConcernMemoryAt(0, 1) & Not(ChangesA)).* ~
|
||||
(Elidable & HasOpcode(STA) & DoesNotConcernMemoryAt(0, 1))).capture(3) ~
|
||||
((Elidable & HasOpcodeIn(Set(LSR, ROR)) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).* ~
|
||||
(Elidable & HasOpcodeIn(Set(LSR, ROR)) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.C, State.N))).capture(2) ~~> { (code, ctx) =>
|
||||
((Elidable & HasOpcodeIn(LSR, ROR) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).* ~
|
||||
(Elidable & HasOpcodeIn(LSR, ROR) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.C, State.N))).capture(2) ~~> { (code, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](2) ++
|
||||
List(AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(ROL)) ++
|
||||
ctx.get[List[AssemblyLine]](3)
|
||||
@ -1768,8 +1767,8 @@ object AlwaysGoodOptimizations {
|
||||
(Elidable & HasOpcode(AND) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
((Elidable & Linear & Not(ChangesMemory) & DoesNotConcernMemoryAt(0, 1) & Not(ChangesA)).* ~
|
||||
(Elidable & HasOpcode(STA) & DoesNotConcernMemoryAt(0, 1))).capture(3) ~
|
||||
((Elidable & HasOpcodeIn(Set(LSR, ROR)) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).* ~
|
||||
(Elidable & HasOpcodeIn(Set(LSR, ROR)) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.C, State.N))).capture(2) ~~> { (code, ctx) =>
|
||||
((Elidable & HasOpcodeIn(LSR, ROR) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).* ~
|
||||
(Elidable & HasOpcodeIn(LSR, ROR) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.C, State.N))).capture(2) ~~> { (code, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](2) ++
|
||||
List(AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(ROL)) ++
|
||||
ctx.get[List[AssemblyLine]](3)
|
||||
@ -1777,7 +1776,7 @@ object AlwaysGoodOptimizations {
|
||||
(Elidable & (HasOpcode(ASL) | HasOpcode(ROL) & HasClear(State.C)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(ROL) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).*.capture(2) ~
|
||||
(Elidable & HasOpcode(CLC)).? ~
|
||||
(Elidable & HasOpcodeIn(Set(LDA, TYA, TXA, PLA))).capture(3) ~
|
||||
(Elidable & HasOpcodeIn(LDA, TYA, TXA, PLA)).capture(3) ~
|
||||
(Elidable & HasOpcode(AND) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CLC)).? ~
|
||||
(Elidable & (HasOpcode(ORA) | HasOpcode(ADC) & HasClear(State.C) & HasClear(State.D)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
@ -1787,19 +1786,19 @@ object AlwaysGoodOptimizations {
|
||||
ctx.get[List[AssemblyLine]](2)
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcodeIn(Set(ROL, ASL)) & DoesntMatterWhatItDoesWith(State.A)) ~
|
||||
(Elidable & HasOpcodeIn(ROL, ASL) & DoesntMatterWhatItDoesWith(State.A)) ~
|
||||
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~
|
||||
(Elidable & HasOpcodeIn(Set(ROL, ASL)) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
|
||||
(Elidable & HasOpcodeIn(ROL, ASL) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
|
||||
code.last :: code.drop(2).init
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcodeIn(Set(ROR, LSR)) & DoesntMatterWhatItDoesWith(State.A)) ~
|
||||
(Elidable & HasOpcodeIn(ROR, LSR) & DoesntMatterWhatItDoesWith(State.A)) ~
|
||||
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~
|
||||
(Elidable & HasOpcode(LSR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
|
||||
code.last :: code.drop(2).init
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcodeIn(Set(ROR, LSR)) & DoesntMatterWhatItDoesWith(State.A)) ~
|
||||
(Elidable & HasOpcodeIn(ROR, LSR) & DoesntMatterWhatItDoesWith(State.A)) ~
|
||||
(Linear & Not(HasOpcode(LSR)) & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~
|
||||
(Elidable & HasOpcode(LSR) & DoesNotConcernMemoryAt(0, 1)) ~
|
||||
(Elidable & HasOpcode(ROR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
|
||||
@ -1815,7 +1814,7 @@ object AlwaysGoodOptimizations {
|
||||
List(AssemblyLine.implied(SEC), code.head.copy(opcode = ROL))
|
||||
},
|
||||
(Elidable & HasOpcode(ASL) & HasAddrMode(AddrMode.Implied)) ~
|
||||
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediate(1)) ~~> { code =>
|
||||
(Elidable & HasOpcodeIn(ORA, EOR) & HasImmediate(1)) ~~> { code =>
|
||||
List(AssemblyLine.implied(SEC), code.head.copy(opcode = ROL))
|
||||
},
|
||||
)
|
||||
@ -1823,9 +1822,9 @@ object AlwaysGoodOptimizations {
|
||||
private def blockIsIdempotentWhenItComesToIndexRegisters(i: Int) = Where(ctx => {
|
||||
val code = ctx.get[List[AssemblyLine]](i)
|
||||
val rx = code.indexWhere(ReadsX)
|
||||
val wx = code.indexWhere(l => ChangesX(l.opcode))
|
||||
val wx = code.indexWhere(l => OpcodeClasses.ChangesX(l.opcode))
|
||||
val ry = code.indexWhere(ReadsY)
|
||||
val wy = code.indexWhere(l => ChangesY(l.opcode))
|
||||
val wy = code.indexWhere(l => OpcodeClasses.ChangesY(l.opcode))
|
||||
val xOk = rx < 0 || wx < 0 || rx >= wx
|
||||
val yOk = ry < 0 || wy < 0 || ry >= wy
|
||||
xOk && yOk
|
||||
@ -1834,25 +1833,25 @@ object AlwaysGoodOptimizations {
|
||||
val CommonExpressionInConditional = new RuleBasedAssemblyOptimization("Common expression in conditional",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(
|
||||
(HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
HasOpcodeIn(Set(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP)).*
|
||||
(HasOpcodeIn(LDA, LAX) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
HasOpcodeIn(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP).*
|
||||
).capture(7) ~
|
||||
blockIsIdempotentWhenItComesToIndexRegisters(7) ~
|
||||
HasOpcodeIn(ShortConditionalBranching) ~
|
||||
ShortConditionalBranching ~
|
||||
MatchElidableCopyOf(7, Anything, DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.V)) ~~> { code =>
|
||||
code.take(code.length / 2 + 1)
|
||||
},
|
||||
|
||||
(Elidable & HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(AND) & HasAddrModeIn(Set(Absolute, ZeroPage)) & DoesntMatterWhatItDoesWith(State.C, State.V, State.A)) ~
|
||||
HasOpcodeIn(Set(BEQ, BNE)) ~
|
||||
(HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
|
||||
(Elidable & HasOpcodeIn(LDA, LAX) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(AND) & HasAddrModeIn(Absolute, ZeroPage) & DoesntMatterWhatItDoesWith(State.C, State.V, State.A)) ~
|
||||
HasOpcodeIn(BEQ, BNE) ~
|
||||
(HasOpcodeIn(LDA, LAX) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
|
||||
List(code(0), code(1).copy(opcode = BIT), code(2))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage))) ~
|
||||
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Absolute, ZeroPage)) ~
|
||||
(Elidable & HasOpcode(AND) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
HasOpcodeIn(Set(BEQ, BNE)) ~
|
||||
HasOpcodeIn(BEQ, BNE) ~
|
||||
(HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
|
||||
List(code(1).copy(opcode = LDA), code(0).copy(opcode = BIT), code(2))
|
||||
},
|
||||
@ -1874,14 +1873,14 @@ object AlwaysGoodOptimizations {
|
||||
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
||||
((Elidable & HasOpcode(LABEL) & MatchParameter(1)) ~ LinearOrLabel.*).capture(10) ~
|
||||
Where(ctx => ctx.get[List[AssemblyLine]](10).map(_.sizeInBytes).sum < 100) ~
|
||||
(Elidable & HasOpcodeIn(ShortConditionalBranching) & MatchParameter(0)) ~
|
||||
(Elidable & ShortConditionalBranching & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(JMP) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(0)) ~~> { (code, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](10) ++ List(negate(code(code.length - 3).copy(parameter = ctx.get[Constant](1))), code.last)
|
||||
},
|
||||
((Elidable & HasOpcode(LABEL) & MatchParameter(1)) ~ LinearOrLabel.*).capture(10) ~
|
||||
Where(ctx => ctx.get[List[AssemblyLine]](10).map(_.sizeInBytes).sum < 100) ~
|
||||
(Elidable & HasOpcodeIn(ShortConditionalBranching) & MatchParameter(0)).capture(13) ~
|
||||
(Elidable & ShortConditionalBranching & MatchParameter(0)).capture(13) ~
|
||||
((Elidable & Not(MatchParameter(0))).* ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(JMP) & MatchParameter(1))).capture(11) ~~> { (code, ctx) =>
|
||||
@ -1898,17 +1897,17 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val IndexComparisonOptimization = new RuleBasedAssemblyOptimization("Index comparison optimization",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcodeIn(Set(DEX, INX)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasOpcodeIn(DEX, INX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsX)).* ~
|
||||
(Elidable & (HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPX) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code =>
|
||||
code.tail.init :+ code.head
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(DEY, INY)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasOpcodeIn(DEY, INY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsY)).* ~
|
||||
(Elidable & (HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPY) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code =>
|
||||
code.tail.init :+ code.head
|
||||
},
|
||||
(Elidable & HasAddrMode(Implied) & HasOpcodeIn(Set(DEC, INC)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(Implied) & HasOpcodeIn(DEC, INC) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsA)).* ~
|
||||
(Elidable & (
|
||||
HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.Y)
|
||||
@ -1999,22 +1998,22 @@ object AlwaysGoodOptimizations {
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
HasOpcode(LDA) ~
|
||||
(Elidable & HasOpcode(AND) & HasImmediate(0x80)) ~
|
||||
(Elidable & HasOpcodeIn(Set(BNE, BEQ)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> {code =>
|
||||
(Elidable & HasOpcodeIn(BNE, BEQ) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> {code =>
|
||||
List(code(0), remapZ2N(code(2)))
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & HasImmediate(0x80)) ~
|
||||
(Elidable & HasOpcode(AND)) ~
|
||||
(Elidable & HasOpcodeIn(Set(BNE, BEQ)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> {code =>
|
||||
(Elidable & HasOpcodeIn(BNE, BEQ) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> {code =>
|
||||
List(code(1).copy(opcode = LDA), remapZ2N(code(2)))
|
||||
},
|
||||
)
|
||||
|
||||
val PointlessSignCheck: RuleBasedAssemblyOptimization = {
|
||||
def loadOldSignedVariable: AssemblyPattern = (
|
||||
(HasOpcodeIn(Set(AND, ANC)) & HasImmediateWhere(i => (i & 0x80) == 0)) ~
|
||||
(HasOpcode(STA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(AND, ANC) & HasImmediateWhere(i => (i & 0x80) == 0)) ~
|
||||
(HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
DoesNotConcernMemoryAt(0, 1).* ~
|
||||
(HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(1))
|
||||
(HasOpcode(LDA) & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(1))
|
||||
).capture(10) ~ Where(_.isExternallyLinearBlock(10))
|
||||
|
||||
val isNonnegative: Int => Boolean = i => (i & 0x80) == 0
|
||||
@ -2026,21 +2025,21 @@ object AlwaysGoodOptimizations {
|
||||
loadOldSignedVariable ~
|
||||
(Elidable & HasOpcode(BMI)) ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) },
|
||||
loadOldSignedVariable ~
|
||||
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)) ~
|
||||
(Elidable & HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)) ~
|
||||
(Elidable & HasOpcode(BMI)) ~
|
||||
OverwritesA ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) :+ code.last },
|
||||
loadOldSignedVariable ~
|
||||
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)) ~
|
||||
(Elidable & HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)) ~
|
||||
(Elidable & HasOpcode(BMI)) ~~> { code => code.init },
|
||||
loadOldSignedVariable ~
|
||||
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)).? ~
|
||||
(Elidable & HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)).? ~
|
||||
(Elidable & HasOpcode(BPL)) ~~> { code => code.init :+ code.last.copy(opcode = JMP, addrMode = Absolute) },
|
||||
loadOldSignedVariable ~
|
||||
((Linear & Not(ConcernsX) & Not(ChangesA)).* ~
|
||||
HasOpcode(TAX) ~
|
||||
(Linear & Not(ConcernsX)).*).capture(11) ~
|
||||
(Elidable & HasOpcode(TXA)) ~
|
||||
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)).? ~
|
||||
(Elidable & HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)).? ~
|
||||
(Elidable & HasOpcode(BMI)) ~
|
||||
OverwritesA ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) ++ ctx.get[List[AssemblyLine]](11) :+ code.last },
|
||||
loadOldSignedVariable ~
|
||||
@ -2048,14 +2047,14 @@ object AlwaysGoodOptimizations {
|
||||
HasOpcode(TAX) ~
|
||||
(Linear & Not(ConcernsX)).* ~
|
||||
HasOpcode(TXA) ~
|
||||
(HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)).? ~
|
||||
(HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)).? ~
|
||||
(Elidable & HasOpcode(BMI)) ~~> { code => code.init },
|
||||
loadOldSignedVariable ~
|
||||
(Linear & Not(ConcernsX) & Not(ChangesA)).* ~
|
||||
HasOpcode(TAX) ~
|
||||
(Linear & Not(ConcernsX)).* ~
|
||||
HasOpcode(TXA) ~
|
||||
(HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)).? ~
|
||||
(HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)).? ~
|
||||
(Elidable & HasOpcode(BPL)) ~~> { code => code.init :+ code.last.copy(opcode = JMP, addrMode = Absolute) },
|
||||
)
|
||||
}
|
||||
@ -2141,10 +2140,10 @@ object AlwaysGoodOptimizations {
|
||||
(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 & HasOpcodeIn(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 & HasOpcodeIn(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(
|
||||
@ -2161,12 +2160,12 @@ object AlwaysGoodOptimizations {
|
||||
val CommonIndexSubexpressionElimination: RuleBasedAssemblyOptimization = {
|
||||
def eliminate(firstLda: Boolean, targetY: Boolean): AssemblyRule = {
|
||||
val firstLoad = HasClear(State.D) & (
|
||||
if (firstLda) HasOpcodeIn(Set(LDA, STA, LAX)) & HasAddrModeIn(Set(Absolute, ZeroPage, Immediate)) & MatchParameter(1)
|
||||
else if (targetY) HasOpcodeIn(Set(TXA, TAX))
|
||||
else HasOpcodeIn(Set(TAY, TYA))
|
||||
if (firstLda) HasOpcodeIn(LDA, STA, LAX) & HasAddrModeIn(Absolute, ZeroPage, Immediate) & MatchParameter(1)
|
||||
else if (targetY) HasOpcodeIn(TXA, TAX)
|
||||
else HasOpcodeIn(TAY, TYA)
|
||||
)
|
||||
val secondLoad = Elidable & (
|
||||
if (firstLda) HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage, Immediate)) & MatchParameter(1)
|
||||
if (firstLda) HasOpcode(LDA) & HasAddrModeIn(Absolute, ZeroPage, Immediate) & MatchParameter(1)
|
||||
else if (targetY) HasOpcode(TXA)
|
||||
else HasOpcode(TYA)
|
||||
)
|
||||
@ -2176,8 +2175,8 @@ object AlwaysGoodOptimizations {
|
||||
else HasOpcode(TAX)
|
||||
) & DoesntMatterWhatItDoesWith(State.A, State.Z, State.N, State.C)
|
||||
val fillerLine =
|
||||
HasAddrMode(Implied) & HasOpcodeIn(Set(ASL, CLC, CLD, SEC, SED, LSR, INC, DEC)) |
|
||||
HasOpcodeIn(Set(ADC, ORA, EOR, AND, SBC)) & HasAddrModeIn(Set(Absolute, ZeroPage, Immediate))
|
||||
HasAddrMode(Implied) & HasOpcodeIn(ASL, CLC, CLD, SEC, SED, LSR, INC, DEC) |
|
||||
HasOpcodeIn(ADC, ORA, EOR, AND, SBC) & HasAddrModeIn(Absolute, ZeroPage, Immediate)
|
||||
val firstFiller = fillerLine.*
|
||||
val secondFiller = (Elidable & fillerLine).*
|
||||
val betweenLines = (Linear & Not(secondLoad) & Not(if (targetY) ChangesY else ChangesX)).+
|
||||
@ -2224,7 +2223,7 @@ object AlwaysGoodOptimizations {
|
||||
val ConstantPointer = new RuleBasedAssemblyOptimization("Constant pointer optimization",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
|
||||
(HasOpcode(STA) & MatchA(0) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(4)) ~
|
||||
(HasOpcode(STA) & MatchA(0) & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(4)) ~
|
||||
Where(ctx => {
|
||||
val lo = ctx.get[Constant](4)
|
||||
ctx.addObject(5, lo + 1)
|
||||
@ -2232,7 +2231,7 @@ object AlwaysGoodOptimizations {
|
||||
true
|
||||
}) ~
|
||||
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~
|
||||
(HasOpcode(STA) & MatchA(1) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(5)) ~
|
||||
(HasOpcode(STA) & MatchA(1) & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(5)) ~
|
||||
Where(ctx => {
|
||||
val lo = ctx.get[Int](0) & 0xff
|
||||
val hi = ctx.get[Int](1) & 0xff
|
||||
@ -2240,13 +2239,13 @@ object AlwaysGoodOptimizations {
|
||||
true
|
||||
}) ~
|
||||
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~
|
||||
(Elidable & MatchParameter(6) & HasAddrModeIn(Set(IndexedZ, IndexedY))) ~~> { (code, ctx) =>
|
||||
(Elidable & MatchParameter(6) & HasAddrModeIn(IndexedZ, IndexedY)) ~~> { (code, ctx) =>
|
||||
val addr = ctx.get[Int](2)
|
||||
val last = code.last
|
||||
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY)
|
||||
},
|
||||
|
||||
(HasOpcode(STA) & MatchA(0) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(4)) ~
|
||||
(HasOpcode(STA) & MatchA(0) & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(4)) ~
|
||||
Where(ctx => {
|
||||
val lo = ctx.get[Constant](4)
|
||||
ctx.addObject(5, lo + 1)
|
||||
@ -2254,7 +2253,7 @@ object AlwaysGoodOptimizations {
|
||||
true
|
||||
}) ~
|
||||
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~
|
||||
(HasOpcode(STX) & MatchX(1) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(5)) ~
|
||||
(HasOpcode(STX) & MatchX(1) & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(5)) ~
|
||||
Where(ctx => {
|
||||
val lo = ctx.get[Int](0) & 0xff
|
||||
val hi = ctx.get[Int](1) & 0xff
|
||||
@ -2262,7 +2261,7 @@ object AlwaysGoodOptimizations {
|
||||
true
|
||||
}) ~
|
||||
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~
|
||||
(Elidable & MatchParameter(6) & HasAddrModeIn(Set(IndexedZ, IndexedY))) ~~> { (code, ctx) =>
|
||||
(Elidable & MatchParameter(6) & HasAddrModeIn(IndexedZ, IndexedY)) ~~> { (code, ctx) =>
|
||||
val addr = ctx.get[Int](2)
|
||||
val last = code.last
|
||||
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.OptimizationContext
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.assembly.mos.OpcodeClasses
|
||||
@ -11,7 +12,9 @@ import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConsta
|
||||
*/
|
||||
object CoarseFlowAnalyzer {
|
||||
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine], compilationOptions: CompilationOptions): List[CpuStatus] = {
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[CpuStatus] = {
|
||||
val compilationOptions = optimizationContext.options
|
||||
val niceFunctionProperties = optimizationContext.niceFunctionProperties
|
||||
val ceFlag = compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)
|
||||
val cmosFlag = compilationOptions.flag(CompilationFlag.EmitCmosOpcodes)
|
||||
val initialStatus =
|
||||
@ -36,6 +39,7 @@ object CoarseFlowAnalyzer {
|
||||
for (i <- codeArray.indices) {
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.node.MosNiceFunctionProperty._
|
||||
if (flagArray(i) != currentStatus) {
|
||||
changed = true
|
||||
flagArray(i) = currentStatus
|
||||
@ -48,6 +52,17 @@ object CoarseFlowAnalyzer {
|
||||
case _ => None
|
||||
}).fold(currentStatus)(_ ~ _)
|
||||
|
||||
case AssemblyLine(JSR, _, MemoryAddressConstant(th), _) =>
|
||||
currentStatus = initialStatus.copy(
|
||||
a = if (niceFunctionProperties(DoesntChangeA -> th.name)) currentStatus.a else AnyStatus,
|
||||
ah = if (niceFunctionProperties(DoesntChangeAH -> th.name)) currentStatus.ah else AnyStatus,
|
||||
x = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.x else AnyStatus,
|
||||
eqSX = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.eqSX else false,
|
||||
y = if (niceFunctionProperties(DoesntChangeY -> th.name)) currentStatus.y else AnyStatus,
|
||||
iz = if (niceFunctionProperties(DoesntChangeIZ -> th.name)) currentStatus.iz else AnyStatus,
|
||||
c = if (niceFunctionProperties(DoesntChangeC -> th.name)) currentStatus.c else AnyStatus
|
||||
)
|
||||
|
||||
case AssemblyLine(JSR | BYTE, _, _, _) =>
|
||||
currentStatus = initialStatus
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.env._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
@ -18,7 +18,7 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
|
||||
|
||||
private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY)
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val paramVariables = f.params match {
|
||||
case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>
|
||||
Set[String]()
|
||||
@ -62,30 +62,30 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
|
||||
case STA | STX | STY | SAX | STZ | SHX | SHY | AHX =>
|
||||
true
|
||||
case TSB | TRB =>
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code)
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
importances(lastaccess).z != Important
|
||||
case INC | DEC =>
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code)
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
importances(lastaccess).z != Important &&
|
||||
importances(lastaccess).n != Important
|
||||
case ASL | LSR | ROL | ROR | DCP =>
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code)
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
importances(lastaccess).z != Important &&
|
||||
importances(lastaccess).n != Important &&
|
||||
importances(lastaccess).c != Important
|
||||
case ISC =>
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code)
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
importances(lastaccess).z != Important &&
|
||||
importances(lastaccess).n != Important &&
|
||||
importances(lastaccess).a != Important
|
||||
case DCP | SLO | SRE | RLA =>
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code)
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
importances(lastaccess).z != Important &&
|
||||
importances(lastaccess).n != Important &&
|
||||
importances(lastaccess).c != Important &&
|
||||
importances(lastaccess).a != Important
|
||||
case RRA =>
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code)
|
||||
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
importances(lastaccess).z != Important &&
|
||||
importances(lastaccess).n != Important &&
|
||||
importances(lastaccess).c != Important &&
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.OptimizationContext
|
||||
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
|
||||
|
||||
@ -33,20 +34,20 @@ object FlowAnalyzer {
|
||||
private val EmptyCpuStatus = CpuStatus()
|
||||
private val EmptyCpuImportance = CpuImportance()
|
||||
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions, req: FlowInfoRequirement.Value): List[(FlowInfo, AssemblyLine)] = {
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext, req: FlowInfoRequirement.Value): List[(FlowInfo, AssemblyLine)] = {
|
||||
val forwardFlow = req match {
|
||||
case FlowInfoRequirement.BothFlows | FlowInfoRequirement.ForwardFlow =>
|
||||
() => CoarseFlowAnalyzer.analyze(f, code, options)
|
||||
() => CoarseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
case FlowInfoRequirement.BackwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
|
||||
() => List.fill(code.size)(EmptyCpuStatus)
|
||||
}
|
||||
val reverseFlow = req match {
|
||||
case FlowInfoRequirement.BothFlows | FlowInfoRequirement.BackwardFlow =>
|
||||
() => ReverseFlowAnalyzer.analyze(f, code)
|
||||
() => ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
case FlowInfoRequirement.ForwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
|
||||
() => List.fill(code.size)(EmptyCpuImportance)
|
||||
}
|
||||
val labelMap: (() => Option[Map[String, Int]]) = () => req match {
|
||||
val labelMap: () => Option[Map[String, Int]] = () => req match {
|
||||
case FlowInfoRequirement.NoRequirement => None
|
||||
case _ => Some(code.flatMap {
|
||||
case AssemblyLine(op, _, MemoryAddressConstant(Label(l)), _) if op != Opcode.LABEL => Some(l)
|
||||
|
@ -32,6 +32,9 @@ object FlowAnalyzerForImplied {
|
||||
CLC -> (_.copy(c = Status.SingleFalse)),
|
||||
SEC -> (_.copy(c = Status.SingleTrue)),
|
||||
CLV -> (_.copy(v = Status.SingleFalse)),
|
||||
CLA -> (currentStatus => currentStatus.copy(a = Status.SingleZero, src = currentStatus.src.butNotA)),
|
||||
CLX -> (currentStatus => currentStatus.copy(x = Status.SingleZero, src = currentStatus.src.butNotX)),
|
||||
CLY -> (currentStatus => currentStatus.copy(y = Status.SingleZero, src = currentStatus.src.butNotY)),
|
||||
XCE -> (_.copy(c = AnyStatus, m = AnyStatus, w = AnyStatus, x = AnyStatus, y = AnyStatus, eqSX = false)),
|
||||
PLA -> (_.copy(src = SourceOfNZ.A, a = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),
|
||||
PLX -> (_.copy(src = SourceOfNZ.X, x = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),
|
||||
|
@ -1,10 +1,11 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.env.NumericConstant
|
||||
import millfork.env.{NormalFunction, NumericConstant}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -13,10 +14,13 @@ object HudsonOptimizations {
|
||||
|
||||
val All: List[AssemblyOptimization[AssemblyLine]] = List()
|
||||
|
||||
def removeLoadZero(code: List[AssemblyLine]): List[AssemblyLine] = code.map{
|
||||
case AssemblyLine(LDA, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLA)
|
||||
case AssemblyLine(LDX, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLX)
|
||||
case AssemblyLine(LDY, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLY)
|
||||
case l => l
|
||||
def removeLoadZero(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
ReverseFlowAnalyzer.analyze(f, code, optimizationContext).zip(code).map {
|
||||
case (i, l) if i.n != Unimportant || i.z != Unimportant => l
|
||||
case (i, AssemblyLine(LDA, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLA)
|
||||
case (_, AssemblyLine(LDX, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLX)
|
||||
case (_, AssemblyLine(LDY, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLY)
|
||||
case (_, l) => l
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.OptimizationContext
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine}
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.mos.Opcode._
|
||||
@ -16,7 +17,8 @@ object JumpShortening {
|
||||
distance.toByte == distance
|
||||
}
|
||||
|
||||
def apply(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
|
||||
def apply(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val options = optimizationContext.options
|
||||
val offsets = new Array[Int](code.length)
|
||||
var o = 0
|
||||
code.zipWithIndex.foreach{
|
||||
@ -46,7 +48,7 @@ object JumpShortening {
|
||||
case (line, _) => line
|
||||
}
|
||||
} else {
|
||||
FlowAnalyzer.analyze(f, code, options, FlowInfoRequirement.ForwardFlow).zipWithIndex.map {
|
||||
FlowAnalyzer.analyze(f, code, optimizationContext, FlowInfoRequirement.ForwardFlow).zipWithIndex.map {
|
||||
case ((info, line@AssemblyLine(JMP, AddrMode.Absolute, MemoryAddressConstant(Label(label)), _)), ix) =>
|
||||
labelOffsets.get(label).fold(line) { labelOffset =>
|
||||
val thisOffset = offsets(ix)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
|
||||
@ -16,7 +16,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
|
||||
override def name: String = "Local variable read optimization"
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
|
||||
val stillUsedVariables = code.flatMap {
|
||||
case AssemblyLine(_, _, MemoryAddressConstant(th: MemoryVariable), _) => th match {
|
||||
@ -36,7 +36,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
return code
|
||||
}
|
||||
|
||||
val statuses = CoarseFlowAnalyzer.analyze(f, code, options)
|
||||
val statuses = CoarseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
val (optimized, result) = optimizeImpl(code.zip(statuses), eligibleVariables, Map())
|
||||
if (optimized) {
|
||||
ErrorReporting.debug("Optimized local variable reads")
|
||||
|
@ -87,8 +87,8 @@ object LoopUnrolling {
|
||||
(Elidable & HasOpcode(LDX) & MatchNumericImmediate(Start) & Not(HasImmediate(0))).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesX)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step)
|
||||
((Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesX)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(DEX, INX)).capture(Step)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~
|
||||
@ -101,8 +101,8 @@ object LoopUnrolling {
|
||||
},
|
||||
(Elidable & HasOpcode(LDX) & MatchNumericImmediate(Start)).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesX)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step)
|
||||
((Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesX)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(DEX, INX)).capture(Step)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~
|
||||
@ -114,8 +114,8 @@ object LoopUnrolling {
|
||||
},
|
||||
(Elidable & HasOpcode(LDX) & MatchNumericImmediate(Start)).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step) ~
|
||||
(Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL, BNE, CPX, TXA))) & Not(ChangesX)).*.capture(Body)
|
||||
((Elidable & HasOpcodeIn(DEX, INX)).capture(Step) ~
|
||||
(Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL, BNE, CPX, TXA)) & Not(ChangesX)).*.capture(Body)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End) | Elidable & HasOpcode(TXA)) ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(Back)) ~
|
||||
@ -128,8 +128,8 @@ object LoopUnrolling {
|
||||
(Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start) & Not(HasImmediate(0))).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesY)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step)
|
||||
((Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesY)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(DEY, INY)).capture(Step)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPY) & MatchNumericImmediate(End)).? ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~
|
||||
@ -142,8 +142,8 @@ object LoopUnrolling {
|
||||
},
|
||||
(Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start)).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesY)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step)
|
||||
((Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesY)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(DEY, INY)).capture(Step)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPY) & MatchNumericImmediate(End)).? ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~
|
||||
@ -155,8 +155,8 @@ object LoopUnrolling {
|
||||
},
|
||||
(Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start)).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step) ~
|
||||
(Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL, BNE, CPY, TYA))) & Not(ChangesY)).*.capture(Body)
|
||||
((Elidable & HasOpcodeIn(DEY, INY)).capture(Step) ~
|
||||
(Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL, BNE, CPY, TYA)) & Not(ChangesY)).*.capture(Body)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPY) & MatchNumericImmediate(End) | Elidable & HasOpcode(TYA)) ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~
|
||||
|
@ -118,7 +118,8 @@ object ReverseFlowAnalyzer {
|
||||
r0 = Important, r1 = Important)
|
||||
|
||||
//noinspection RedundantNewCaseClass
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine]): List[CpuImportance] = {
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[CpuImportance] = {
|
||||
val niceFunctionProperties = optimizationContext.niceFunctionProperties
|
||||
val importanceArray = Array.fill[CpuImportance](code.length)(new CpuImportance())
|
||||
val codeArray = code.toArray
|
||||
|
||||
@ -130,6 +131,7 @@ object ReverseFlowAnalyzer {
|
||||
for (i <- codeArray.indices.reverse) {
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import AddrMode._
|
||||
import millfork.node.MosNiceFunctionProperty._
|
||||
if (importanceArray(i) != currentImportance) {
|
||||
changed = true
|
||||
importanceArray(i) = currentImportance
|
||||
@ -173,10 +175,27 @@ object ReverseFlowAnalyzer {
|
||||
result = result.copy(a = Important)
|
||||
case _ =>
|
||||
}
|
||||
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput(fun.name)) {
|
||||
result = result.copy(r0 = Important, r1 = Important)
|
||||
}
|
||||
currentImportance = result
|
||||
currentImportance = result.copy(
|
||||
a = if (niceFunctionProperties(DoesntChangeA -> fun.name)) currentImportance.a ~ result.a else result.a,
|
||||
ah = if (niceFunctionProperties(DoesntChangeAH -> fun.name)) currentImportance.ah ~ result.ah else result.ah,
|
||||
x = if (niceFunctionProperties(DoesntChangeX -> fun.name)) currentImportance.x ~ result.x else result.x,
|
||||
y = if (niceFunctionProperties(DoesntChangeY -> fun.name)) currentImportance.y ~ result.y else result.y,
|
||||
iz = if (niceFunctionProperties(DoesntChangeIZ -> fun.name)) currentImportance.iz ~ result.iz else result.iz,
|
||||
c = if (niceFunctionProperties(DoesntChangeC -> fun.name)) currentImportance.c ~ result.c else result.c,
|
||||
d = if (niceFunctionProperties(DoesntConcernD -> fun.name)) currentImportance.d else result.d,
|
||||
r0 =
|
||||
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput(fun.name))
|
||||
Important
|
||||
else if (niceFunctionProperties(DoesntChangeZpRegister -> fun.name))
|
||||
currentImportance.r0 ~ result.r0
|
||||
else result.r0,
|
||||
r1 =
|
||||
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput(fun.name))
|
||||
Important
|
||||
else if (niceFunctionProperties(DoesntChangeZpRegister -> fun.name))
|
||||
currentImportance.r1 ~ result.r1
|
||||
else result.r1
|
||||
)
|
||||
|
||||
case AssemblyLine(ANC, _, NumericConstant(0, _), _) =>
|
||||
currentImportance = currentImportance.copy(c = Unimportant, n = Unimportant, z = Unimportant, a = Unimportant)
|
||||
|
@ -174,6 +174,10 @@ object ReverseFlowAnalyzerPerImpiedOpcode {
|
||||
SED -> (_.copy(d = Unimportant)),
|
||||
CLV -> (_.copy(v = Unimportant)),
|
||||
|
||||
CLA -> (_.copy(a = Unimportant)),
|
||||
CLX -> (_.copy(x = Unimportant)),
|
||||
CLY -> (_.copy(y = Unimportant)),
|
||||
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: Opcode.Value): Boolean = map.contains(opcode)
|
||||
|
@ -1,11 +1,12 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.opt.SingleStatus
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.{MosNiceFunctionProperty, NiceFunctionProperty}
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
@ -32,18 +33,18 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
||||
|
||||
rules.foreach(_.pattern.validate(needsFlowInfo))
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions, labelMap: Map[String, Int]): List[AssemblyLine] = {
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
|
||||
val taggedCode = FlowAnalyzer.analyze(f, effectiveCode, options, needsFlowInfo)
|
||||
optimizeImpl(f, taggedCode, options, labelMap)
|
||||
val taggedCode = FlowAnalyzer.analyze(f, effectiveCode, optimizationContext, needsFlowInfo)
|
||||
optimizeImpl(f, taggedCode, optimizationContext)
|
||||
}
|
||||
|
||||
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, AssemblyLine)], options: CompilationOptions, labelMap: Map[String, Int]): List[AssemblyLine] = {
|
||||
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, AssemblyLine)], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
code match {
|
||||
case Nil => Nil
|
||||
case head :: tail =>
|
||||
for ((rule, index) <- rules.zipWithIndex) {
|
||||
val ctx = new AssemblyMatchingContext(options, labelMap)
|
||||
val ctx = new AssemblyMatchingContext(optimizationContext.options, optimizationContext.labelMap, optimizationContext.niceFunctionProperties)
|
||||
rule.pattern.matchTo(ctx, code) match {
|
||||
case Some(rest: List[(FlowInfo, AssemblyLine)]) =>
|
||||
val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2)
|
||||
@ -59,19 +60,40 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
||||
ErrorReporting.trace(" ↓")
|
||||
optimizedChunk.filter(_.isPrintable).foreach(l => ErrorReporting.trace(l.toString))
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
return optimizedChunk ++ optimizeImpl(f, rest, options, labelMap)
|
||||
return optimizedChunk ++ optimizeImpl(f, rest, optimizationContext)
|
||||
} else {
|
||||
return optimize(f, optimizedChunk ++ rest.map(_._2), options)
|
||||
return optimize(f, optimizedChunk ++ rest.map(_._2), optimizationContext)
|
||||
}
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
head._2 :: optimizeImpl(f, tail, options, labelMap)
|
||||
head._2 :: optimizeImpl(f, tail, optimizationContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions, val labelMap: Map[String, Int]) {
|
||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||
val labelMap: Map[String, Int],
|
||||
val niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
||||
|
||||
def functionChangesA(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeA -> name)
|
||||
|
||||
def functionChangesX(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeX -> name)
|
||||
|
||||
def functionChangesY(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeY -> name)
|
||||
|
||||
def functionChangesIZ(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeIZ -> name)
|
||||
|
||||
def functionChangesAH(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeAH -> name)
|
||||
|
||||
def functionChangesC(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeC -> name)
|
||||
|
||||
def functionReadsD(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntConcernD -> name)
|
||||
|
||||
def functionChangesMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntWriteMemory -> name)
|
||||
|
||||
def functionReadsMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntReadMemory -> name)
|
||||
|
||||
private val map = mutable.Map[Int, Any]()
|
||||
|
||||
override def toString: String = map.mkString(", ")
|
||||
@ -310,7 +332,7 @@ case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPatte
|
||||
}
|
||||
|
||||
|
||||
case class Where(predicate: (AssemblyMatchingContext => Boolean)) extends AssemblyPattern {
|
||||
case class Where(predicate: AssemblyMatchingContext => Boolean) extends AssemblyPattern {
|
||||
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
|
||||
if (predicate(ctx)) Some(code) else None
|
||||
}
|
||||
@ -442,11 +464,12 @@ trait AssemblyLinePattern extends AssemblyPattern {
|
||||
def &(x: AssemblyLinePattern): AssemblyLinePattern = Both(this, x)
|
||||
}
|
||||
|
||||
//noinspection ScalaUnnecessaryParentheses
|
||||
trait TrivialAssemblyLinePattern extends AssemblyLinePattern with (AssemblyLine => Boolean) {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = this (line)
|
||||
}
|
||||
|
||||
case class Match(predicate: (AssemblyMatchingContext => Boolean)) extends AssemblyLinePattern {
|
||||
case class Match(predicate: AssemblyMatchingContext => Boolean) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = predicate(ctx)
|
||||
|
||||
override def toString: String = "Match(...)"
|
||||
@ -460,11 +483,6 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection LanguageFeature
|
||||
object AssemblyLinePattern {
|
||||
implicit def __implicitOpcodeIn(ops: Set[Opcode.Value]): AssemblyLinePattern = HasOpcodeIn(ops)
|
||||
}
|
||||
|
||||
case class MatchA(i: Int) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
@ -777,14 +795,108 @@ case object ConcernsIZ extends TrivialAssemblyLinePattern {
|
||||
OpcodeClasses.ConcernsIZAlways(line.opcode) || IZAddrModes(line.addrMode)
|
||||
}
|
||||
|
||||
case object ChangesA extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ChangesAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ChangesAIfImplied(line.opcode)
|
||||
case object ChangesA extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
import Opcode._
|
||||
import AddrMode._
|
||||
line match {
|
||||
case AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesA(th.name)
|
||||
case AssemblyLine(_, Implied, _, _) => OpcodeClasses.ChangesAIfImplied(line.opcode) || OpcodeClasses.ChangesAAlways(line.opcode)
|
||||
case _ => OpcodeClasses.ChangesAAlways(line.opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ChangesAH extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ChangesAHAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ChangesAHIfImplied(line.opcode)
|
||||
case object ChangesX extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
import Opcode._
|
||||
import AddrMode._
|
||||
line match {
|
||||
case AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesX(th.name)
|
||||
case _ => OpcodeClasses.ChangesX(line.opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ChangesY extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
import Opcode._
|
||||
import AddrMode._
|
||||
line match {
|
||||
case AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesY(th.name)
|
||||
case _ => OpcodeClasses.ChangesY(line.opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ReadsNOrZ extends HasOpcodeIn(OpcodeClasses.ReadsNOrZ)
|
||||
|
||||
case object ReadsC extends HasOpcodeIn(OpcodeClasses.ReadsC)
|
||||
|
||||
case object ReadsD extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
import Opcode._
|
||||
import AddrMode._
|
||||
line match {
|
||||
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionReadsD(th.name)
|
||||
case _ => OpcodeClasses.ReadsD(line.opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ReadsV extends HasOpcodeIn(OpcodeClasses.ReadsV)
|
||||
|
||||
case object ChangesNAndZ extends HasOpcodeIn(OpcodeClasses.ChangesNAndZ)
|
||||
|
||||
case object ChangesC extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
import Opcode._
|
||||
import AddrMode._
|
||||
line match {
|
||||
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesC(th.name)
|
||||
case _ => OpcodeClasses.ChangesC(line.opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ChangesV extends HasOpcodeIn(OpcodeClasses.ChangesV)
|
||||
|
||||
case object ChangesStack extends HasOpcodeIn(OpcodeClasses.ChangesStack)
|
||||
|
||||
case object ChangesIZ extends HasOpcodeIn(OpcodeClasses.ChangesIZ)
|
||||
|
||||
case object ChangesS extends HasOpcodeIn(OpcodeClasses.ChangesS)
|
||||
|
||||
case object SupportsAbsolute extends HasOpcodeIn(OpcodeClasses.SupportsAbsolute)
|
||||
|
||||
case object SupportsAbsoluteX extends HasOpcodeIn(OpcodeClasses.SupportsAbsoluteX)
|
||||
|
||||
case object SupportsAbsoluteY extends HasOpcodeIn(OpcodeClasses.SupportsAbsoluteY)
|
||||
|
||||
case object ShortConditionalBranching extends HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)
|
||||
|
||||
case object ShortBranching extends HasOpcodeIn(OpcodeClasses.ShortBranching)
|
||||
|
||||
case object OverwritesA extends HasOpcodeIn(OpcodeClasses.OverwritesA)
|
||||
|
||||
case object OverwritesX extends HasOpcodeIn(OpcodeClasses.OverwritesX)
|
||||
|
||||
case object OverwritesY extends HasOpcodeIn(OpcodeClasses.OverwritesY)
|
||||
|
||||
case object NoopDiscardsFlags extends HasOpcodeIn(OpcodeClasses.NoopDiscardsFlags)
|
||||
|
||||
case object NoopDiscardsFlagsOrLabel extends HasOpcodeIn(OpcodeClasses.NoopDiscardsFlags + Opcode.LABEL)
|
||||
|
||||
case object ChangesAH extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
import Opcode._
|
||||
import AddrMode._
|
||||
line match {
|
||||
case AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesAH(th.name)
|
||||
case AssemblyLine(_, Implied, _, _) => OpcodeClasses.ChangesAHIfImplied(line.opcode) || OpcodeClasses.ChangesAHAlways(line.opcode)
|
||||
case _ => OpcodeClasses.ChangesAHAlways(line.opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ChangesM extends TrivialAssemblyLinePattern {
|
||||
@ -801,34 +913,57 @@ case object ChangesW extends TrivialAssemblyLinePattern {
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
case object ChangesMemory extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
|
||||
|
||||
case object ChangesMemory extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
import AddrMode._
|
||||
import Opcode._
|
||||
line match {
|
||||
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesMemory(th.name)
|
||||
case AssemblyLine(op, Implied, _, _) => OpcodeClasses.ChangesMemoryAlways(op)
|
||||
case AssemblyLine(op, _, _, _) => OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int, opcode: Opcode.Value = Opcode.NOP) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
val p1 = ctx.get[Constant](param1)
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
|
||||
// TODO: NOP
|
||||
// this will break if the actual instruction was 16-bit
|
||||
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(opcode, a1, p1), line)
|
||||
import AddrMode._
|
||||
import Opcode._
|
||||
line match {
|
||||
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => !ctx.functionChangesMemory(th.name)
|
||||
case _ =>
|
||||
val p1 = ctx.get[Constant](param1)
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
|
||||
// TODO: NOP
|
||||
// this will break if the actual instruction was 16-bit
|
||||
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(opcode, a1, p1), line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ConcernsMemory extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
ReadsMemory(line) || ChangesMemory(line)
|
||||
case object ConcernsMemory extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
ReadsMemory.matchLineTo(ctx, flowInfo, line) || ChangesMemory.matchLineTo(ctx, flowInfo, line)
|
||||
}
|
||||
|
||||
case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
val p1 = ctx.get[Constant](param1)
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
// TODO: NOP
|
||||
// this will break if the actual instruction was 16-bit
|
||||
HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(Opcode.NOP, a1, p1), line)
|
||||
{
|
||||
import AddrMode._
|
||||
import Opcode._
|
||||
line match {
|
||||
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => !ctx.functionReadsMemory(th.name) && !ctx.functionChangesMemory(th.name)
|
||||
case _ =>
|
||||
val p1 = ctx.get[Constant](param1)
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
// TODO: NOP
|
||||
// this will break if the actual instruction was 16-bit
|
||||
HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(Opcode.NOP, a1, p1), line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -931,11 +1066,20 @@ case class CallsAnyExcept(identifiers: Set[String]) extends TrivialAssemblyLineP
|
||||
override def toString: String = identifiers.mkString("(JSR ¬{", ",", "})")
|
||||
}
|
||||
|
||||
case class HasOpcodeIn(ops: Set[Opcode.Value]) extends TrivialAssemblyLinePattern {
|
||||
class HasOpcodeIn(val ops: Set[Opcode.Value]) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
ops(line.opcode)
|
||||
|
||||
override def toString: String = ops.mkString("{", ",", "}")
|
||||
|
||||
def |(that: HasOpcodeIn): HasOpcodeIn = new HasOpcodeIn(ops ++ that.ops)
|
||||
|
||||
def --(that: HasOpcodeIn): HasOpcodeIn = new HasOpcodeIn(ops -- that.ops)
|
||||
}
|
||||
|
||||
object HasOpcodeIn {
|
||||
def apply(ops: Opcode.Value*): HasOpcodeIn = new HasOpcodeIn(ops.toSet)
|
||||
def apply(ops: Set[Opcode.Value]): HasOpcodeIn = new HasOpcodeIn(ops)
|
||||
}
|
||||
|
||||
case class HasAddrMode(am: AddrMode.Value) extends TrivialAssemblyLinePattern {
|
||||
@ -950,6 +1094,13 @@ case class HasAddrModeIn(ams: Set[AddrMode.Value]) extends TrivialAssemblyLinePa
|
||||
ams(line.addrMode)
|
||||
|
||||
override def toString: String = ams.mkString("{", ",", "}")
|
||||
|
||||
def |(that: HasAddrModeIn): HasAddrModeIn = HasAddrModeIn(ams ++ that.ams)
|
||||
|
||||
def --(that: HasAddrModeIn): HasAddrModeIn = HasAddrModeIn(ams -- that.ams)
|
||||
}
|
||||
object HasAddrModeIn {
|
||||
def apply(ams: AddrMode.Value*): HasAddrModeIn = HasAddrModeIn(ams.toSet)
|
||||
}
|
||||
|
||||
case class HasImmediate(i: Int) extends TrivialAssemblyLinePattern {
|
||||
@ -1034,11 +1185,11 @@ case class MatchNumericImmediate(i: Int) extends AssemblyLinePattern {
|
||||
case class DoesntChangeIndexingInAddrMode(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
ctx.get[AddrMode.Value](i) match {
|
||||
case AddrMode.ZeroPageX | AddrMode.AbsoluteX | AddrMode.LongAbsoluteX | AddrMode.IndexedX | AddrMode.AbsoluteIndexedX => !OpcodeClasses.ChangesX.contains(line.opcode)
|
||||
case AddrMode.ZeroPageY | AddrMode.AbsoluteY | AddrMode.IndexedY | AddrMode.LongIndexedY => !OpcodeClasses.ChangesY.contains(line.opcode)
|
||||
case AddrMode.IndexedZ | AddrMode.LongIndexedZ => !OpcodeClasses.ChangesIZ.contains(line.opcode)
|
||||
case AddrMode.ZeroPageX | AddrMode.AbsoluteX | AddrMode.LongAbsoluteX | AddrMode.IndexedX | AddrMode.AbsoluteIndexedX => !ChangesX.matchLineTo(ctx, flowInfo, line)
|
||||
case AddrMode.ZeroPageY | AddrMode.AbsoluteY | AddrMode.IndexedY | AddrMode.LongIndexedY => !ChangesY.matchLineTo(ctx, flowInfo, line)
|
||||
case AddrMode.IndexedZ | AddrMode.LongIndexedZ => !ChangesIZ.matchLineTo(ctx, flowInfo, line)
|
||||
case AddrMode.Stack => !OpcodeClasses.ChangesS.contains(line.opcode)
|
||||
case AddrMode.IndexedSY => !OpcodeClasses.ChangesS.contains(line.opcode) && !OpcodeClasses.ChangesY.contains(line.opcode)
|
||||
case AddrMode.IndexedSY => !OpcodeClasses.ChangesS.contains(line.opcode) && !ChangesY.matchLineTo(ctx, flowInfo, line)
|
||||
case _ => true
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ object SingleAssignmentVariableOptimization extends AssemblyOptimization[Assembl
|
||||
override def name = "Single assignment variable optimization"
|
||||
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val paramVariables = f.params match {
|
||||
case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>
|
||||
Set[String]()
|
||||
@ -65,7 +65,7 @@ object SingleAssignmentVariableOptimization extends AssemblyOptimization[Assembl
|
||||
val slice = code.slice(lifetime.start, lifetime.end)
|
||||
slice.forall(l => GoodOpcodes.contains(l.opcode) || !BadOpcodes.contains(l.opcode) && source.forall(s => HelperCheckers.memoryAccessDoesntOverlap(s,l)))
|
||||
}.flatMap{case (v,source) =>
|
||||
replaceVariable(v.name, source, code.zip(ReverseFlowAnalyzer.analyze(f, code))).map(v -> _)
|
||||
replaceVariable(v.name, source, code.zip(ReverseFlowAnalyzer.analyze(f, code, optimizationContext))).map(v -> _)
|
||||
}
|
||||
|
||||
if (finalGoodVariables.isEmpty) return code
|
||||
|
@ -33,52 +33,52 @@ object SixteenOptimizations {
|
||||
val RepSepWeakening = new RuleBasedAssemblyOptimization("REP/SEP weakening",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & HasImmediate(0)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(SEP, REP) & HasImmediate(0)) ~~> (_ => Nil),
|
||||
|
||||
(HasOpcode(SEP) & HasImmediate(0x20)) ~
|
||||
(Linear & Not(HasOpcodeIn(Set(SEP, REP, PLP)))).* ~
|
||||
(Linear & Not(HasOpcodeIn(SEP, REP, PLP))).* ~
|
||||
(Elidable & HasOpcode(SEP) & HasImmediate(0x20)) ~~> (_.init),
|
||||
(HasOpcode(REP) & HasImmediate(0x20)) ~
|
||||
(Linear & Not(HasOpcodeIn(Set(SEP, REP, PLP)))).* ~
|
||||
(Linear & Not(HasOpcodeIn(SEP, REP, PLP))).* ~
|
||||
(Elidable & HasOpcode(REP) & HasImmediate(0x20)) ~~> (_.init),
|
||||
(HasOpcode(SEP) & HasImmediate(0x10)) ~
|
||||
(Linear & Not(HasOpcodeIn(Set(SEP, REP, PLP)))).* ~
|
||||
(Linear & Not(HasOpcodeIn(SEP, REP, PLP))).* ~
|
||||
(Elidable & HasOpcode(SEP) & HasImmediate(0x10)) ~~> (_.init),
|
||||
(HasOpcode(REP) & HasImmediate(0x10)) ~
|
||||
(Linear & Not(HasOpcodeIn(Set(SEP, REP, PLP)))).* ~
|
||||
(Linear & Not(HasOpcodeIn(SEP, REP, PLP))).* ~
|
||||
(Elidable & HasOpcode(REP) & HasImmediate(0x10)) ~~> (_.init),
|
||||
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.C)) ~
|
||||
(Elidable & HasOpcodeIn(SEP, REP) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.C)) ~
|
||||
Where(c => c.get[Int](0).&(0x1).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xFE
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.Z)) ~
|
||||
(Elidable & HasOpcodeIn(SEP, REP) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.Z)) ~
|
||||
Where(c => c.get[Int](0).&(0x2).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xFD
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.D)) ~
|
||||
(Elidable & HasOpcodeIn(SEP, REP) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.D)) ~
|
||||
Where(c => c.get[Int](0).&(0x8).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xF7
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.W)) ~
|
||||
(Elidable & HasOpcodeIn(SEP, REP) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.W)) ~
|
||||
Where(c => c.get[Int](0).&(0x10).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xEF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.M)) ~
|
||||
(Elidable & HasOpcodeIn(SEP, REP) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.M)) ~
|
||||
Where(c => c.get[Int](0).&(0x20).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xDF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.V)) ~
|
||||
(Elidable & HasOpcodeIn(SEP, REP) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.V)) ~
|
||||
Where(c => c.get[Int](0).&(0x40).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xBF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.N)) ~
|
||||
(Elidable & HasOpcodeIn(SEP, REP) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.N)) ~
|
||||
Where(c => c.get[Int](0).&(0x80).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0x7F
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
@ -122,11 +122,11 @@ object SixteenOptimizations {
|
||||
val PointlessLoadAfterLoadOrStore = new RuleBasedAssemblyOptimization("Pointless 16-bit load after load or store",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
|
||||
(HasOpcodeIn(Set(LDA_W, STA_W)) & HasAddrMode(WordImmediate) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDA_W, STA_W) & HasAddrMode(WordImmediate) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(ChangesAH)).* ~
|
||||
(Elidable & HasOpcode(LDA_W) & HasAddrMode(WordImmediate) & MatchParameter(1)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDA_W, STA_W)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcodeIn(LDA_W, STA_W) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(ChangesAH) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1, LDA_W)).* ~
|
||||
(Elidable & HasOpcode(LDA_W) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.init),
|
||||
)
|
||||
@ -136,7 +136,7 @@ object SixteenOptimizations {
|
||||
(Elidable & HasY(0) /*& HasZ(0)*/ & HasIndex8 & HasAddrMode(LongIndexedY) & HasOpcodeIn(SupportsLongIndexedZ)) ~~> (code => code.map(_.copy(addrMode = LongIndexedZ))),
|
||||
)
|
||||
|
||||
private val SupportsStackAddressing = Set(
|
||||
private val SupportsStackAddressing = HasOpcodeIn(
|
||||
ADC, AND, EOR, ORA, LDA, STA, SBC, CMP,
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions, OptimizationPresets}
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
|
||||
import millfork.env.NormalFunction
|
||||
import millfork.error.ErrorReporting
|
||||
@ -13,7 +13,8 @@ import scala.collection.mutable
|
||||
*/
|
||||
object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
|
||||
|
||||
override def optimize(m: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
|
||||
override def optimize(m: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val options = optimizationContext.options
|
||||
val oldVerbosity = ErrorReporting.verbosity
|
||||
ErrorReporting.verbosity = -1
|
||||
var allOptimizers = OptimizationPresets.Good ++ LaterOptimizations.All
|
||||
@ -55,6 +56,7 @@ object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
|
||||
AlwaysGoodOptimizations.UnusedCodeRemoval
|
||||
)
|
||||
val optionsForMeasurements = options.copy(commandLineFlags = options.commandLineFlags + (CompilationFlag.InternalCurrentlyOptimizingForMeasurement -> true))
|
||||
val optimizationContextForMeasurements = optimizationContext.copy(options = optionsForMeasurements)
|
||||
val quicklyCleanedCode = quickScrub.foldLeft(code)((c, o) => o.optimize(m, c, optionsForMeasurements))
|
||||
seenSoFar += viewCode(quicklyCleanedCode)
|
||||
queue.enqueue(quickScrub.reverse -> quicklyCleanedCode)
|
||||
@ -64,7 +66,7 @@ object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
|
||||
var isLeaf = true
|
||||
(if (optionsForMeasurements.flag(CompilationFlag.SingleThreaded)) allOptimizers
|
||||
else allOptimizers.par).foreach { o =>
|
||||
val optimized = o.optimize(m, codeSoFar, optionsForMeasurements)
|
||||
val optimized = o.optimize(m, codeSoFar, optimizationContextForMeasurements)
|
||||
val view = viewCode(optimized)
|
||||
seenSoFar.synchronized{
|
||||
if (!seenSoFar(view)) {
|
||||
|
@ -1,12 +1,13 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions, NonOverlappingIntervals}
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.{CompilationFlag, NonOverlappingIntervals}
|
||||
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import AddrMode._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.MosNiceFunctionProperty
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
@ -22,12 +23,19 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
def +(that: CyclesAndBytes) = CyclesAndBytes(this.bytes + that.bytes, this.cycles + that.cycles)
|
||||
}
|
||||
|
||||
case class Features(
|
||||
case class FeaturesForIndexRegisters(
|
||||
blastProcessing: Boolean,
|
||||
izIsAlwaysZero: Boolean,
|
||||
indexRegisterTransfers: Boolean,
|
||||
functionsSafeForX: Set[String],
|
||||
functionsSafeForY: Set[String],
|
||||
functionsSafeForZ: Set[String],
|
||||
identityArray: Constant)
|
||||
|
||||
case class FeaturesForAccumulator(
|
||||
cmos: Boolean,
|
||||
safeFunctions: Set[String])
|
||||
|
||||
// If any of these opcodes is present within a method,
|
||||
// then it's too hard to assign any variable to a register.
|
||||
private val opcodesThatAlwaysPrecludeXAllocation = Set(
|
||||
@ -103,7 +111,8 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
override def name = "Allocating variables to index registers"
|
||||
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val options = optimizationContext.options
|
||||
val paramVariables = f.params match {
|
||||
case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>
|
||||
Set[String]()
|
||||
@ -138,23 +147,30 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
|
||||
val removeVariablesForReal = !options.flag(CompilationFlag.InternalCurrentlyOptimizingForMeasurement)
|
||||
val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes
|
||||
val importances = ReverseFlowAnalyzer.analyze(f, code)
|
||||
val importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed)
|
||||
val identityArray = f.environment.maybeGet[ThingInMemory]("identity$").map(MemoryAddressConstant).getOrElse(Constant.Zero)
|
||||
val izIsAlwaysZero = !options.flag(CompilationFlag.Emit65CE02Opcodes)
|
||||
val features = Features(
|
||||
val featuresForIndices = FeaturesForIndexRegisters(
|
||||
blastProcessing = blastProcessing,
|
||||
izIsAlwaysZero = izIsAlwaysZero,
|
||||
indexRegisterTransfers = options.flag(CompilationFlag.EmitEmulation65816Opcodes),
|
||||
functionsSafeForX = optimizationContext.niceFunctionProperties.filter(x => x._1 == MosNiceFunctionProperty.DoesntChangeX).map(_._2),
|
||||
functionsSafeForY = optimizationContext.niceFunctionProperties.filter(x => x._1 == MosNiceFunctionProperty.DoesntChangeY).map(_._2),
|
||||
functionsSafeForZ = optimizationContext.niceFunctionProperties.filter(x => x._1 == MosNiceFunctionProperty.DoesntChangeIZ).map(_._2),
|
||||
identityArray = identityArray
|
||||
)
|
||||
val featuresForAcc = FeaturesForAccumulator(
|
||||
cmos = options.flag(CompilationFlag.EmitCmosOpcodes),
|
||||
safeFunctions = optimizationContext.niceFunctionProperties.filter(x => x._1 == MosNiceFunctionProperty.DoesntChangeA).map(_._2)
|
||||
)
|
||||
|
||||
val xCandidates = variablesWithLifetimes.filter {
|
||||
case (vName, range) =>
|
||||
importances(range.start).x != Important
|
||||
}.flatMap {
|
||||
case (vName, range) =>
|
||||
canBeInlined(Some(vName), None, None, features, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
canBeInlined(Some(vName), None, None, featuresForIndices, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
(vName, range, if (variablesWithRegisterHint(vName)) score + CyclesAndBytes(16, 16) else score)
|
||||
}
|
||||
}
|
||||
@ -164,7 +180,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
importances(range.start).y != Important
|
||||
}.flatMap {
|
||||
case (vName, range) =>
|
||||
canBeInlined(None, Some(vName), None, features, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
canBeInlined(None, Some(vName), None, featuresForIndices, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
(vName, range, if (variablesWithRegisterHint(vName)) score + CyclesAndBytes(16, 16) else score)
|
||||
}
|
||||
}
|
||||
@ -174,7 +190,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
importances(range.start).iz != Important
|
||||
}.flatMap {
|
||||
case (vName, range) =>
|
||||
canBeInlined(None, None, Some(vName), features, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
canBeInlined(None, None, Some(vName), featuresForIndices, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
(vName, range, if (variablesWithRegisterHint(vName)) score + CyclesAndBytes(16, 16) else score)
|
||||
}
|
||||
}
|
||||
@ -185,7 +201,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}.flatMap {
|
||||
case (vName, range) =>
|
||||
canBeInlinedToAccumulator(
|
||||
options,
|
||||
featuresForAcc,
|
||||
start = true,
|
||||
synced = false,
|
||||
vName,
|
||||
@ -251,7 +267,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case (v, range, _) =>
|
||||
ErrorReporting.debug(s"Inlining $v to register X")
|
||||
val oldCode = code.zip(importances).slice(range.start, range.end)
|
||||
val newCode = inlineVars(Some(v), None, None, None, features, oldCode)
|
||||
val newCode = inlineVars(Some(v), None, None, None, featuresForIndices, oldCode)
|
||||
reportOptimizedBlock(oldCode, newCode)
|
||||
output ++= newCode
|
||||
i = range.end
|
||||
@ -265,7 +281,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case (v, range, _) =>
|
||||
ErrorReporting.debug(s"Inlining $v to register Y")
|
||||
val oldCode = code.zip(importances).slice(range.start, range.end)
|
||||
val newCode = inlineVars(None, Some(v), None, None, features, oldCode)
|
||||
val newCode = inlineVars(None, Some(v), None, None, featuresForIndices, oldCode)
|
||||
reportOptimizedBlock(oldCode, newCode)
|
||||
output ++= newCode
|
||||
i = range.end
|
||||
@ -280,7 +296,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case (v, range, _) =>
|
||||
ErrorReporting.debug(s"Inlining $v to register Z")
|
||||
val oldCode = code.zip(importances).slice(range.start, range.end)
|
||||
val newCode = inlineVars(None, None, Some(v), None, features, oldCode)
|
||||
val newCode = inlineVars(None, None, Some(v), None, featuresForIndices, oldCode)
|
||||
reportOptimizedBlock(oldCode, newCode)
|
||||
output ++= newCode
|
||||
i = range.end
|
||||
@ -295,7 +311,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case (v, range, _) =>
|
||||
ErrorReporting.debug(s"Inlining $v to register A")
|
||||
val oldCode = code.zip(importances).slice(range.start, range.end)
|
||||
val newCode = inlineVars(None, None, None, Some(v), features, oldCode)
|
||||
val newCode = inlineVars(None, None, None, Some(v), featuresForIndices, oldCode)
|
||||
reportOptimizedBlock(oldCode, newCode)
|
||||
output ++= newCode
|
||||
i = range.end
|
||||
@ -321,7 +337,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
|
||||
// TODO: STA has different flag behaviour than TAX, keep it in mind!
|
||||
def canBeInlined(xCandidate: Option[String], yCandidate: Option[String], zCandidate: Option[String], features: Features, lines: List[(AssemblyLine, CpuImportance)]): Option[CyclesAndBytes] = {
|
||||
def canBeInlined(xCandidate: Option[String], yCandidate: Option[String], zCandidate: Option[String], features: FeaturesForIndexRegisters, lines: List[(AssemblyLine, CpuImportance)]): Option[CyclesAndBytes] = {
|
||||
val vx = xCandidate.getOrElse("-")
|
||||
val vy = yCandidate.getOrElse("-")
|
||||
val vz = zCandidate.getOrElse("-")
|
||||
@ -369,17 +385,17 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
|
||||
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx && (opcode == LDY || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if th.name == vx && (opcode == LDY || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vy && (opcode == LDX || opcode == LAX || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if th.name == vy && (opcode == LDX || opcode == LAX || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vz && (opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if th.name == vz && (opcode == LDX || opcode == LDY || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
@ -547,6 +563,14 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
// labels always end the initial section
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _), _) :: xs =>
|
||||
if (
|
||||
xCandidate.isDefined && features.functionsSafeForX(th.name) ||
|
||||
yCandidate.isDefined && features.functionsSafeForY(th.name) ||
|
||||
zCandidate.isDefined && features.functionsSafeForZ(th.name)
|
||||
) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
else None
|
||||
|
||||
case (x, _) :: xs =>
|
||||
if (xCandidate.isDefined && opcodesThatAlwaysPrecludeXAllocation(x.opcode)) {
|
||||
None
|
||||
@ -562,13 +586,12 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
}
|
||||
|
||||
def canBeInlinedToAccumulator(options: CompilationOptions, start: Boolean, synced: Boolean, candidate: String, lines: List[(AssemblyLine, CpuImportance)]): Option[CyclesAndBytes] = {
|
||||
val cmos = options.flags(CompilationFlag.EmitCmosOpcodes)
|
||||
def canBeInlinedToAccumulator(features: FeaturesForAccumulator, start: Boolean, synced: Boolean, candidate: String, lines: List[(AssemblyLine, CpuImportance)]): Option[CyclesAndBytes] = {
|
||||
lines match {
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true),_) :: xs
|
||||
if th.name == candidate && start || synced =>
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
|
||||
|
||||
case (AssemblyLine(op, _, _, _),_) :: xs if opcodesThatAlwaysPrecludeAAllocation(op) =>
|
||||
None
|
||||
@ -589,35 +612,35 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
None
|
||||
|
||||
case (AssemblyLine(SEP | REP, Immediate, NumericConstant(nn, _), _), _) :: xs =>
|
||||
if ((nn & 0x20) == 0) canBeInlinedToAccumulator(options, start = false, synced = synced, candidate, xs)
|
||||
if ((nn & 0x20) == 0) canBeInlinedToAccumulator(features, start = false, synced = synced, candidate, xs)
|
||||
else None
|
||||
|
||||
case (AssemblyLine(SEP | REP, _, _, _), _) :: xs => None
|
||||
|
||||
case (AssemblyLine(STA, _, MemoryAddressConstant(th), elidable) ,_):: xs if th.name == candidate =>
|
||||
if (synced && elidable) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
case (AssemblyLine(DCP, Absolute | ZeroPage, MemoryAddressConstant(th), _) ,_):: xs if th.name == candidate =>
|
||||
if (synced) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs)
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
case (AssemblyLine(STA | SAX, _, MemoryAddressConstant(th), elidable) ,_):: xs if th.name != candidate =>
|
||||
if (synced) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs)
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
case (AssemblyLine(STA | SAX, _, NumericConstant(_, _), _) ,_):: xs =>
|
||||
if (synced) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs)
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -629,7 +652,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
|
||||
case (AssemblyLine(TAX | TAY, _, _, _),_) :: xs =>
|
||||
if (synced) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs)
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -638,30 +661,30 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
if th.name == candidate =>
|
||||
// removing LDA saves 3 bytes
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 1, cycles = 2))
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 1, cycles = 2))
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
|
||||
if opcodesCommutative(op) =>
|
||||
if (th.name == candidate) {
|
||||
if (elidable && elidable2) canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
if (elidable && elidable2) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
else None
|
||||
} else canBeInlinedToAccumulator(options, start = false, synced = synced, candidate, xs)
|
||||
} else canBeInlinedToAccumulator(features, start = false, synced = synced, candidate, xs)
|
||||
|
||||
case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(CLC, _, _, _),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
|
||||
if opcodesCommutative(op) =>
|
||||
if (th.name == candidate) {
|
||||
if (elidable && elidable2) canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
if (elidable && elidable2) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
else None
|
||||
} else canBeInlinedToAccumulator(options, start = false, synced = synced, candidate, xs)
|
||||
} else canBeInlinedToAccumulator(features, start = false, synced = synced, candidate, xs)
|
||||
|
||||
case (AssemblyLine(LDX | LDY | LAX, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
|
||||
if th.name == candidate =>
|
||||
// converting a load into a transfer saves 2 bytes
|
||||
if (elidable) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -673,15 +696,15 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case (AssemblyLine(ASL | LSR | ROR | ROL, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
|
||||
if th.name == candidate =>
|
||||
if (elidable) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
|
||||
canBeInlinedToAccumulator(features, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
|
||||
if th.name == candidate =>
|
||||
if (cmos && elidable) {
|
||||
canBeInlinedToAccumulator(options, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
|
||||
if (features.cmos && elidable) {
|
||||
canBeInlinedToAccumulator(features, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -689,12 +712,16 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case (AssemblyLine(TXA | TYA, _, _, elidable), imp) :: xs =>
|
||||
if (imp.a == Unimportant && imp.c == Unimportant && imp.v == Unimportant && elidable) {
|
||||
// TYA/TXA has to be converted to CPY#0/CPX#0
|
||||
canBeInlinedToAccumulator(options, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = -1, cycles = 0))
|
||||
canBeInlinedToAccumulator(features, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = -1, cycles = 0))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
case (x, _) :: xs => canBeInlinedToAccumulator(options, start = false, synced = synced && OpcodeClasses.AllLinear(x.opcode), candidate, xs)
|
||||
case (AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _), _) :: xs =>
|
||||
if (features.safeFunctions(th.name)) canBeInlinedToAccumulator(features, start = false, synced = synced, candidate, xs)
|
||||
else None
|
||||
|
||||
case (x, _) :: xs => canBeInlinedToAccumulator(features, start = false, synced = synced && OpcodeClasses.AllLinear(x.opcode), candidate, xs)
|
||||
|
||||
case Nil => Some(CyclesAndBytes.Zero)
|
||||
}
|
||||
@ -706,7 +733,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case _ => true
|
||||
}
|
||||
|
||||
def inlineVars(xCandidate: Option[String], yCandidate: Option[String], zCandidate: Option[String], aCandidate: Option[String], features: Features, lines: List[(AssemblyLine, CpuImportance)]): List[AssemblyLine] = {
|
||||
def inlineVars(xCandidate: Option[String], yCandidate: Option[String], zCandidate: Option[String], aCandidate: Option[String], features: FeaturesForIndexRegisters, lines: List[(AssemblyLine, CpuImportance)]): List[AssemblyLine] = {
|
||||
val vx = xCandidate.getOrElse("-")
|
||||
val vy = yCandidate.getOrElse("-")
|
||||
val vz = zCandidate.getOrElse("-")
|
||||
|
@ -124,6 +124,18 @@ object Status {
|
||||
case SingleStatus(v) => v.iz
|
||||
case _ => false
|
||||
}
|
||||
def butNotA: Status[SourceOfNZ] = inner match {
|
||||
case SingleStatus(v) => if (v.a || v.aw) AnyStatus else inner
|
||||
case _ => inner
|
||||
}
|
||||
def butNotX: Status[SourceOfNZ] = inner match {
|
||||
case SingleStatus(v) => if (v.x) AnyStatus else inner
|
||||
case _ => inner
|
||||
}
|
||||
def butNotY: Status[SourceOfNZ] = inner match {
|
||||
case SingleStatus(v) => if (v.y) AnyStatus else inner
|
||||
case _ => inner
|
||||
}
|
||||
}
|
||||
implicit class IntStatusOps(val inner: Status[Int]) extends AnyVal {
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.compiler
|
||||
|
||||
import millfork.env.{Environment, Label, NormalFunction}
|
||||
import millfork.node.NiceFunctionProperty
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
|
||||
/**
|
||||
@ -10,6 +11,7 @@ case class CompilationContext(env: Environment,
|
||||
function: NormalFunction,
|
||||
extraStackOffset: Int,
|
||||
options: CompilationOptions,
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
||||
breakLabels: Map[String, Label] = Map(),
|
||||
continueLabels: Map[String, Label] = Map()){
|
||||
def withInlinedEnv(environment: Environment, newLabel: String): CompilationContext = {
|
||||
|
@ -64,6 +64,24 @@ case class HalfWordExpression(expression: Expression, hiByte: Boolean) extends E
|
||||
HalfWordExpression(expression.replaceVariable(variable, actualParam), hiByte)
|
||||
}
|
||||
|
||||
sealed class NiceFunctionProperty(override val toString: String)
|
||||
|
||||
object NiceFunctionProperty {
|
||||
case object DoesntReadMemory extends NiceFunctionProperty("MR")
|
||||
case object DoesntWriteMemory extends NiceFunctionProperty("MW")
|
||||
}
|
||||
|
||||
object MosNiceFunctionProperty {
|
||||
case object DoesntChangeA extends NiceFunctionProperty("A")
|
||||
case object DoesntChangeX extends NiceFunctionProperty("X")
|
||||
case object DoesntChangeY extends NiceFunctionProperty("Y")
|
||||
case object DoesntChangeIZ extends NiceFunctionProperty("Z")
|
||||
case object DoesntChangeAH extends NiceFunctionProperty("AH")
|
||||
case object DoesntChangeC extends NiceFunctionProperty("C")
|
||||
case object DoesntConcernD extends NiceFunctionProperty("D")
|
||||
case object DoesntChangeZpRegister extends NiceFunctionProperty("reg")
|
||||
}
|
||||
|
||||
object MosRegister extends Enumeration {
|
||||
val A, X, Y, AX, AY, YA, XA, XY, YX, AW = Value
|
||||
}
|
||||
@ -72,7 +90,7 @@ object ZRegister extends Enumeration {
|
||||
|
||||
val A, B, C, D, E, H, L, AF, BC, HL, DE, SP, IXH, IXL, IYH, IYL, IX, IY, R, I, MEM_HL, MEM_BC, MEM_DE, MEM_IX_D, MEM_IY_D, MEM_ABS_8, MEM_ABS_16, IMM_8, IMM_16 = Value
|
||||
|
||||
def size(reg: Value) = reg match {
|
||||
def size(reg: Value): Int = reg match {
|
||||
case AF | BC | DE | HL | IX | IY | IMM_16 => 2
|
||||
case A | B | C | D | E | H | L | IXH | IXL | IYH | IYL | R | I | IMM_8 => 1
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import millfork.assembly._
|
||||
import millfork.compiler.{AbstractCompiler, CompilationContext}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.{CallGraph, Program}
|
||||
import millfork.node.{CallGraph, NiceFunctionProperty, Program}
|
||||
import millfork._
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -97,7 +97,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
???
|
||||
}
|
||||
} catch {
|
||||
case e: StackOverflowError =>
|
||||
case _: StackOverflowError =>
|
||||
ErrorReporting.fatal("Stack overflow " + c)
|
||||
}
|
||||
case UnexpandedConstant(name, _) =>
|
||||
@ -200,9 +200,10 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
var inlinedFunctions = Map[String, List[T]]()
|
||||
val compiledFunctions = mutable.Map[String, List[T]]()
|
||||
val recommendedCompilationOrder = callGraph.recommendedCompilationOrder
|
||||
val niceFunctionProperties = mutable.Set[(NiceFunctionProperty, String)]()
|
||||
recommendedCompilationOrder.foreach { f =>
|
||||
env.maybeGet[NormalFunction](f).foreach { function =>
|
||||
val code = compileFunction(function, optimizations, options, inlinedFunctions, labelMap.toMap)
|
||||
val code = compileFunction(function, optimizations, options, inlinedFunctions, labelMap.toMap, niceFunctionProperties.toSet)
|
||||
val strippedCodeForInlining = for {
|
||||
limit <- potentiallyInlineable.get(f)
|
||||
if code.map(_.sizeInBytes).sum <= limit
|
||||
@ -217,10 +218,18 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
nonInlineableFunctions += function.name
|
||||
compiledFunctions(f) = code
|
||||
optimizedCodeSize += code.map(_.sizeInBytes).sum
|
||||
if (options.flag(CompilationFlag.InterproceduralOptimization)) {
|
||||
gatherNiceFunctionProperties(niceFunctionProperties, f, code)
|
||||
}
|
||||
}
|
||||
function.environment.removedThings.foreach(env.removeVariable)
|
||||
}
|
||||
}
|
||||
if (ErrorReporting.traceEnabled) {
|
||||
niceFunctionProperties.toList.groupBy(_._2).mapValues(_.map(_._1).sortBy(_.toString)).toList.sortBy(_._1).foreach{ case (fname, properties) =>
|
||||
ErrorReporting.trace(fname.padTo(30, ' ') + properties.mkString(" "))
|
||||
}
|
||||
}
|
||||
|
||||
rootEnv.things.foreach{case (name, thing) =>
|
||||
if (!env.things.contains(name)) {
|
||||
@ -442,20 +451,27 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
|
||||
def injectLabels(labelMap: Map[String, Int], code: List[T]): List[T]
|
||||
|
||||
private def compileFunction(f: NormalFunction, optimizations: Seq[AssemblyOptimization[T]], options: CompilationOptions, inlinedFunctions: Map[String, List[T]], labelMap: Map[String, Int]): List[T] = {
|
||||
private def compileFunction(f: NormalFunction,
|
||||
optimizations: Seq[AssemblyOptimization[T]],
|
||||
options: CompilationOptions,
|
||||
inlinedFunctions: Map[String, List[T]],
|
||||
labelMap: Map[String, Int],
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]): List[T] = {
|
||||
ErrorReporting.debug("Compiling: " + f.name, f.position)
|
||||
val unoptimized: List[T] =
|
||||
injectLabels(labelMap, inliningCalculator.inline(
|
||||
compiler.compile(CompilationContext(env = f.environment, function = f, extraStackOffset = 0, options = options)),
|
||||
compiler.compile(CompilationContext(env = f.environment, function = f, extraStackOffset = 0, options = options, niceFunctionProperties = niceFunctionProperties)),
|
||||
inlinedFunctions,
|
||||
compiler))
|
||||
unoptimizedCodeSize += unoptimized.map(_.sizeInBytes).sum
|
||||
val code = optimizations.foldLeft(unoptimized) { (c, opt) =>
|
||||
opt.optimize(f, c, options, labelMap)
|
||||
opt.optimize(f, c, OptimizationContext(options, labelMap, niceFunctionProperties))
|
||||
}
|
||||
performFinalOptimizationPass(f, optimizations.nonEmpty, options, code)
|
||||
}
|
||||
|
||||
def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[T]): Unit
|
||||
|
||||
def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[T]): List[T]
|
||||
|
||||
private def outputFunction(bank: String, code: List[T], startFrom: Int, assOut: mutable.ArrayBuffer[String], options: CompilationOptions): Int = {
|
||||
|
@ -4,11 +4,12 @@ import millfork.assembly.mos.opt.{HudsonOptimizations, JumpFixing, JumpShortenin
|
||||
import millfork.assembly._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.Program
|
||||
import millfork.node.{MosNiceFunctionProperty, NiceFunctionProperty, Program}
|
||||
import millfork._
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode, OpcodeClasses}
|
||||
import millfork.compiler.mos.MosCompiler
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
@ -20,9 +21,10 @@ class MosAssembler(program: Program,
|
||||
|
||||
|
||||
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[AssemblyLine]):List[AssemblyLine] = {
|
||||
val optimizationContext = OptimizationContext(options, Map(), Set())
|
||||
if (actuallyOptimize) {
|
||||
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(code) else code
|
||||
JumpShortening(f, JumpShortening(f, JumpFixing(f, finalCode, options), options), options)
|
||||
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(f, code, optimizationContext) else code
|
||||
JumpShortening(f, JumpShortening(f, JumpFixing(f, finalCode, options), optimizationContext), optimizationContext)
|
||||
}
|
||||
else JumpFixing(f, code, options)
|
||||
}
|
||||
@ -34,8 +36,8 @@ class MosAssembler(program: Program,
|
||||
case AssemblyLine(BYTE, RawByte, c, _) =>
|
||||
writeByte(bank, index, c)
|
||||
index + 1
|
||||
case AssemblyLine(BYTE, _, _, _) => ???
|
||||
case AssemblyLine(_, RawByte, _, _) => ???
|
||||
case AssemblyLine(BYTE, _, _, _) => ErrorReporting.fatal("BYTE opcode failure")
|
||||
case AssemblyLine(_, RawByte, _, _) => ErrorReporting.fatal("BYTE opcode failure")
|
||||
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(labelName)), _) =>
|
||||
labelMap(labelName) = index
|
||||
index
|
||||
@ -87,18 +89,93 @@ class MosAssembler(program: Program,
|
||||
case l => l
|
||||
}
|
||||
}
|
||||
|
||||
@tailrec
|
||||
private def isNaughty(code: List[AssemblyLine]): Boolean = {
|
||||
import Opcode._
|
||||
import AddrMode._
|
||||
code match {
|
||||
case AssemblyLine(JMP | JSR | BSR, Indirect | LongIndirect | AbsoluteIndexedX, _, _) :: _ => true
|
||||
case AssemblyLine(PHA, _, _, _) :: AssemblyLine(RTS | RTL, _, _, _) :: _ => true
|
||||
case _ :: xs => isNaughty(xs)
|
||||
case Nil => false
|
||||
}
|
||||
}
|
||||
|
||||
override def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[AssemblyLine]): Unit = {
|
||||
import Opcode._
|
||||
import AddrMode._
|
||||
import MosNiceFunctionProperty._
|
||||
import NiceFunctionProperty._
|
||||
if (isNaughty(code)) return
|
||||
val localLabels = code.flatMap {
|
||||
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) => Some(l)
|
||||
case _ => None
|
||||
}.toSet
|
||||
def genericPropertyScan(niceFunctionProperty: NiceFunctionProperty)(predicate: AssemblyLine => Boolean): Unit = {
|
||||
val preserved = code.forall {
|
||||
case AssemblyLine(JSR | BSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => niceFunctionProperties(niceFunctionProperty -> th.name)
|
||||
case AssemblyLine(JSR | BSR, _, _, _) => false
|
||||
case AssemblyLine(op, _, MemoryAddressConstant(Label(label)), _) if OpcodeClasses.AllDirectJumps(op) => localLabels(label)
|
||||
case AssemblyLine(op, _, _, _) if OpcodeClasses.AllDirectJumps(op) => false
|
||||
case l => predicate(l)
|
||||
}
|
||||
if (preserved) {
|
||||
niceFunctionProperties += (niceFunctionProperty -> functionName)
|
||||
}
|
||||
}
|
||||
genericPropertyScan(DoesntChangeX) {
|
||||
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ChangesX(op)
|
||||
}
|
||||
genericPropertyScan(DoesntChangeY) {
|
||||
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ChangesY(op)
|
||||
}
|
||||
genericPropertyScan(DoesntChangeA) {
|
||||
case AssemblyLine(op, _, _, _) if OpcodeClasses.ChangesAAlways(op) => false
|
||||
case AssemblyLine(op, _, Implied, _) if OpcodeClasses.ChangesAIfImplied(op) => false
|
||||
case _ => true
|
||||
}
|
||||
genericPropertyScan(DoesntChangeIZ) {
|
||||
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ChangesIZ(op)
|
||||
}
|
||||
genericPropertyScan(DoesntChangeAH) {
|
||||
case AssemblyLine(op, _, _, _) if OpcodeClasses.ChangesAHAlways(op) => false
|
||||
case AssemblyLine(op, _, Implied, _) if OpcodeClasses.ChangesAHIfImplied(op) => false
|
||||
case _ => true
|
||||
}
|
||||
genericPropertyScan(DoesntChangeC) {
|
||||
case AssemblyLine(SEP | REP, Immediate, NumericConstant(imm, _), _) => (imm & 1) == 0
|
||||
case AssemblyLine(SEP | REP, _, _, _) => false
|
||||
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ChangesC(op)
|
||||
}
|
||||
genericPropertyScan(DoesntConcernD) {
|
||||
case AssemblyLine(SEP | REP, Immediate, NumericConstant(imm, _), _) => (imm & 8) == 0
|
||||
case AssemblyLine(SEP | REP, _, _, _) => false
|
||||
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ReadsD(op) && !OpcodeClasses.OverwritesD(op)
|
||||
}
|
||||
genericPropertyScan(DoesntReadMemory) {
|
||||
case AssemblyLine(op, _, Implied | Immediate | WordImmediate, _) => true
|
||||
case AssemblyLine(op, _, _, _) if OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(op) => false
|
||||
case _ => true
|
||||
}
|
||||
genericPropertyScan(DoesntWriteMemory) {
|
||||
case AssemblyLine(op, _, Implied | Immediate | WordImmediate, _) => true
|
||||
case AssemblyLine(op, _, _, _) if OpcodeClasses.ChangesMemoryIfNotImplied(op) || OpcodeClasses.ChangesMemoryAlways(op) => false
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object MosAssembler {
|
||||
val opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val illegalOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val cmosOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val cmosNopOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val ce02Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val hudsonOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val emulation65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val native65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
private val opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
private val illegalOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
private val cmosOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
private val cmosNopOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
private val ce02Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
private val hudsonOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
private val emulation65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
private val native65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
|
||||
def opcodeFor(opcode: Opcode.Value, addrMode: AddrMode.Value, options: CompilationOptions): Byte = {
|
||||
val key = opcode -> addrMode
|
||||
|
@ -4,7 +4,9 @@ import millfork.{CompilationOptions, Platform}
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler.z80.Z80Compiler
|
||||
import millfork.env.{Environment, NormalFunction}
|
||||
import millfork.node.Program
|
||||
import millfork.node.{NiceFunctionProperty, Program}
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -20,6 +22,10 @@ class Z80Assembler(program: Program,
|
||||
}
|
||||
|
||||
override def injectLabels(labelMap: Map[String, Int], code: List[ZLine]): List[ZLine] = code // TODO
|
||||
|
||||
override def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[ZLine]): Unit = {
|
||||
// do nothing yet
|
||||
}
|
||||
}
|
||||
object Z80Assembler {
|
||||
|
||||
|
@ -102,6 +102,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
val options = CompilationOptions(platform, Map(
|
||||
CompilationFlag.EmitIllegals -> this.emitIllegals,
|
||||
CompilationFlag.InlineFunctions -> this.inline,
|
||||
CompilationFlag.InterproceduralOptimization -> true,
|
||||
CompilationFlag.CompactReturnDispatchParams -> true,
|
||||
CompilationFlag.ZeropagePseudoregister -> true,
|
||||
CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
|
||||
@ -136,7 +137,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
// print unoptimized asm
|
||||
env.allPreallocatables.foreach {
|
||||
case f: NormalFunction =>
|
||||
val unoptimized = MosCompiler.compile(CompilationContext(f.environment, f, 0, options))
|
||||
val unoptimized = MosCompiler.compile(CompilationContext(f.environment, f, 0, options, Set()))
|
||||
unoptimizedSize += unoptimized.map(_.sizeInBytes).sum
|
||||
case d: InitializedArray =>
|
||||
unoptimizedSize += d.contents.length
|
||||
|
@ -51,7 +51,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
// print unoptimized asm
|
||||
env.allPreallocatables.foreach {
|
||||
case f: NormalFunction =>
|
||||
val unoptimized = Z80Compiler.compile(CompilationContext(f.environment, f, 0, options))
|
||||
val unoptimized = Z80Compiler.compile(CompilationContext(f.environment, f, 0, options, Set()))
|
||||
unoptimizedSize += unoptimized.map(_.sizeInBytes).sum
|
||||
case d: InitializedArray =>
|
||||
unoptimizedSize += d.contents.length
|
||||
|
Loading…
x
Reference in New Issue
Block a user