1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-02-09 06:30:36 +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:
Karol Stasiak 2018-06-25 21:29:04 +02:00
parent 57740cc6b4
commit 2500f842e9
30 changed files with 750 additions and 371 deletions

View File

@ -53,7 +53,7 @@ and ending when the function returns.
Most automatic variables reside in memory. Most automatic variables reside in memory.
They can share their memory location with other automatic variables and parameters, They can share their memory location with other automatic variables and parameters,
to conserve memory usage. 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. 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. 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. and ending when the function returns.
They reside in memory and can share their memory location with other parameters and automatic variables, They reside in memory and can share their memory location with other parameters and automatic variables,
to conserve memory usage. 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. Parameters are not safe to use with reentrant functions, see the [relevant documentation](../lang/reentrancy.md) for more details.

View File

@ -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. * `--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). * `--size` Optimize for size, sacrificing some speed (experimental).
* `--fast` Optimize for speed, even if it increases the size a bit (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). * `--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 ## Warning options

View File

@ -45,6 +45,8 @@ You may be also interested in the following:
* `--inline` automatically inline functions for better optimization * `--inline` automatically inline functions for better optimization
* `--fipo` enable interprocedural optimization
* `-s` additionally generate assembly output * `-s` additionally generate assembly output
* `-g` additionally generate a label file, in format compatible with VICE emulator * `-g` additionally generate a label file, in format compatible with VICE emulator

View File

@ -206,7 +206,7 @@ object CompilationFlag extends Enumeration {
// compilation options for Z80 // compilation options for Z80
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack, EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack,
// optimization options: // optimization options:
DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed, DangerousOptimizations, InlineFunctions, InterproceduralOptimization, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
// memory allocation options // memory allocation options
VariableOverlap, CompactReturnDispatchParams, LUnixRelocatableCode, VariableOverlap, CompactReturnDispatchParams, LUnixRelocatableCode,
// runtime check options // runtime check options
@ -232,6 +232,7 @@ object CompilationFlag extends Enumeration {
"emit_x80" -> EmitExtended80Opcodes, "emit_x80" -> EmitExtended80Opcodes,
"emit_sharp" -> EmitSharpOpcodes, "emit_sharp" -> EmitSharpOpcodes,
"ix_stack" -> UseIxForStack, "ix_stack" -> UseIxForStack,
"ipo" -> InterproceduralOptimization,
"zeropage_register" -> ZeropagePseudoregister, "zeropage_register" -> ZeropagePseudoregister,
"decimal_mode" -> DecimalMode, "decimal_mode" -> DecimalMode,
"ro_arrays" -> ReadOnlyArrays, "ro_arrays" -> ReadOnlyArrays,

View File

@ -302,6 +302,12 @@ object Main {
flag("--inline").action { c => flag("--inline").action { c =>
c.changeFlag(CompilationFlag.InlineFunctions, true) c.changeFlag(CompilationFlag.InlineFunctions, true)
}.description("Inline functions automatically.") }.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 => flag("-Os", "--size").action { c =>
c.changeFlag(CompilationFlag.OptimizeForSize, true). c.changeFlag(CompilationFlag.OptimizeForSize, true).
changeFlag(CompilationFlag.OptimizeForSpeed, false). changeFlag(CompilationFlag.OptimizeForSpeed, false).

View File

@ -2,14 +2,19 @@ package millfork.assembly
import millfork.CompilationOptions import millfork.CompilationOptions
import millfork.env.NormalFunction import millfork.env.NormalFunction
import millfork.node.NiceFunctionProperty
/** /**
* @author Karol Stasiak * @author Karol Stasiak
*/ */
case class OptimizationContext(options: CompilationOptions,
labelMap: Map[String, Int],
niceFunctionProperties: Set[(NiceFunctionProperty, String)])
trait AssemblyOptimization[T <: AbstractCode] { trait AssemblyOptimization[T <: AbstractCode] {
def name: String 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()))
} }

View File

@ -7,7 +7,6 @@ import millfork.assembly.mos.OpcodeClasses._
import millfork.assembly.mos.{AddrMode, opt, _} import millfork.assembly.mos.{AddrMode, opt, _}
import millfork.assembly.mos.AddrMode._ import millfork.assembly.mos.AddrMode._
import millfork.env._ import millfork.env._
import org.apache.commons.lang3.builder.HashCodeExclude
/** /**
* These optimizations should not remove opportunities for more complex optimizations to trigger. * 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) => 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 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) & (Elidable & HasA(0) & HasOpcode(ADC) &
HasClear(State.D) & HasClear(State.C) & HasClear(State.D) & HasClear(State.C) &
// C stays cleared! // C stays cleared!
@ -171,42 +170,42 @@ object AlwaysGoodOptimizations {
val MathOperationOnTwoIdenticalMemoryOperands = new RuleBasedAssemblyOptimization("Math operation on two identical memory operands", val MathOperationOnTwoIdenticalMemoryOperands = new RuleBasedAssemblyOptimization("Math operation on two identical memory operands",
needsFlowInfo = FlowInfoRequirement.BothFlows, 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)).* ~ (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)).* ~ (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)), (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)).* ~ (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)), (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)).* ~ (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)).* ~ (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)).* ~ (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", val PoinlessStoreBeforeStore = new RuleBasedAssemblyOptimization("Pointless store before store",
needsFlowInfo = FlowInfoRequirement.NoRequirement, 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)).* ~ (LinearOrLabel & DoesNotConcernMemoryAt(2, 1)).* ~
(MatchParameter(1) & MatchAddrMode(2) & Set(STA, SAX, STX, STY, STZ)) ~~> (_.tail), (MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, SAX, STX, STY, STZ)) ~~> (_.tail),
(Elidable & HasAddrModeIn(Set(AbsoluteX, ZeroPageX)) & MatchParameter(1) & MatchAddrMode(2) & Set(STA, STY, STZ)) ~ (Elidable & HasAddrModeIn(AbsoluteX, ZeroPageX) & MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, STY, STZ)) ~
(LinearOrLabel & DoesntChangeMemoryAt(2, 1) & Not(ReadsMemory) & Not(ChangesX)).* ~ (LinearOrLabel & DoesntChangeMemoryAt(2, 1) & Not(ReadsMemory) & Not(ChangesX)).* ~
(MatchParameter(1) & MatchAddrMode(2) & Set(STA, STY, STZ)) ~~> (_.tail), (MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, STY, STZ)) ~~> (_.tail),
(Elidable & HasAddrModeIn(Set(AbsoluteY, ZeroPageY)) & MatchParameter(1) & MatchAddrMode(2) & Set(STA, SAX, STX, STZ)) ~ (Elidable & HasAddrModeIn(AbsoluteY, ZeroPageY) & MatchParameter(1) & MatchAddrMode(2) & HasOpcodeIn(STA, SAX, STX, STZ)) ~
(LinearOrLabel & DoesntChangeMemoryAt(2, 1) & Not(ReadsMemory) & Not(ChangesY)).* ~ (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) = { 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) ~ (HasOpcode(st1) & MatchAddrMode(2) & MatchParameter(3) & match1) ~
(Linear & DoesntChangeIndexingInAddrMode(2) & ( (Linear & DoesntChangeIndexingInAddrMode(2) & (
DoesntChangeMemoryAt(2, 3) | DoesntChangeMemoryAt(2, 3) |
HasAddrModeIn(Set(IndexedX, IndexedY, IndexedZ, LongIndexedY, LongIndexedZ, Indirect)) & HasAddrModeIn(IndexedX, IndexedY, IndexedZ, LongIndexedY, LongIndexedZ, Indirect) &
MatchParameter(3) & HasParameterWhere({ MatchParameter(3) & HasParameterWhere({
case MemoryAddressConstant(th) => th.name.startsWith("__") case MemoryAddressConstant(th) => th.name.startsWith("__")
case _ => false 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", val PointlessStoreToTheSameVariable = new RuleBasedAssemblyOptimization("Pointless store to the same variable",
@ -280,7 +279,7 @@ object AlwaysGoodOptimizations {
(HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~ (HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).*.capture(5) ~ (Elidable & Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).*.capture(5) ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ (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 => { Where(ctx => {
val sta = ctx.get[List[AssemblyLine]](4).head val sta = ctx.get[List[AssemblyLine]](4).head
val middleCode = ctx.get[List[AssemblyLine]](5) val middleCode = ctx.get[List[AssemblyLine]](5)
@ -290,7 +289,7 @@ object AlwaysGoodOptimizations {
}, },
(HasOpcode(TAX)) ~ (HasOpcode(TAX)) ~
(Elidable & Linear & Not(ChangesX) & Not(HasOpcode(STX))).*.capture(5) ~ (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 => { Where(ctx => {
val sta = ctx.get[List[AssemblyLine]](4).head val sta = ctx.get[List[AssemblyLine]](4).head
val middleCode = ctx.get[List[AssemblyLine]](5) val middleCode = ctx.get[List[AssemblyLine]](5)
@ -300,7 +299,7 @@ object AlwaysGoodOptimizations {
}, },
(HasOpcode(TAY)) ~ (HasOpcode(TAY)) ~
(Elidable & Linear & Not(ChangesY) & Not(HasOpcode(STY))).*.capture(5) ~ (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 => { Where(ctx => {
val sta = ctx.get[List[AssemblyLine]](4).head val sta = ctx.get[List[AssemblyLine]](4).head
val middleCode = ctx.get[List[AssemblyLine]](5) val middleCode = ctx.get[List[AssemblyLine]](5)
@ -310,7 +309,7 @@ object AlwaysGoodOptimizations {
}, },
(HasOpcode(TAZ)) ~ (HasOpcode(TAZ)) ~
(Elidable & Linear & Not(ChangesIZ) & Not(HasOpcode(STZ))).*.capture(5) ~ (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 => { Where(ctx => {
val sta = ctx.get[List[AssemblyLine]](4).head val sta = ctx.get[List[AssemblyLine]](4).head
val middleCode = ctx.get[List[AssemblyLine]](5) val middleCode = ctx.get[List[AssemblyLine]](5)
@ -373,9 +372,9 @@ object AlwaysGoodOptimizations {
val PointlessLoadBeforeReturn = new RuleBasedAssemblyOptimization("Pointless load before return", val PointlessLoadBeforeReturn = new RuleBasedAssemblyOptimization("Pointless load before return",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(Set(LDA, TXA, TYA, EOR, AND, ORA, ANC) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_AF))).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail), (HasOpcodeIn(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), (HasOpcodeIn(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(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)) ~ (HasOpcode(LDX) & Elidable & MatchAddrMode(3) & MatchParameter(4)) ~
(LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & DoesntChangeMemoryAt(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & DoesntChangeMemoryAt(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~
(HasOpcode(TXA) & Elidable) ~ (HasOpcode(TXA) & Elidable) ~
@ -395,17 +394,17 @@ object AlwaysGoodOptimizations {
val InefficientStashingToRegister = new RuleBasedAssemblyOptimization("Inefficient stashing to register", val InefficientStashingToRegister = new RuleBasedAssemblyOptimization("Inefficient stashing to register",
needsFlowInfo = FlowInfoRequirement.BackwardFlow, 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)) ~ (Elidable & HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
(Linear & Not(ConcernsX) & DoesntChangeMemoryAt(0, 1)).* ~ (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 & 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)) ~ (Elidable & HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
(Linear & Not(ConcernsY) & DoesntChangeMemoryAt(0, 1)).* ~ (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 & 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)) ~ (Elidable & HasOpcode(TAZ) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
(Linear & Not(ConcernsIZ) & DoesntChangeMemoryAt(0, 1)).* ~ (Linear & Not(ConcernsIZ) & DoesntChangeMemoryAt(0, 1)).* ~
(Elidable & HasOpcode(TZA) & DoesntMatterWhatItDoesWith(State.IZ)) ~~> (code => code.head :: (code.drop(2).init :+ code.head.copy(opcode = LDA))), (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) = { 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) ~ middle.*.capture(1) ~
Where(_.isExternallyLinearBlock(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) ctx.get[List[AssemblyLine]](1)
} }
} }
@ -493,8 +492,8 @@ object AlwaysGoodOptimizations {
operationPairBuilder3(PHX, Anything, PLX, Not(ChangesX) & Not(ConcernsStack), Some(DISCARD_XF)), operationPairBuilder3(PHX, Anything, PLX, Not(ChangesX) & Not(ConcernsStack), Some(DISCARD_XF)),
operationPairBuilder3(PHY, Anything, PLY, Not(ChangesY) & Not(ConcernsStack), Some(DISCARD_YF)), operationPairBuilder3(PHY, Anything, PLY, Not(ChangesY) & Not(ConcernsStack), Some(DISCARD_YF)),
operationPairBuilder3(PHZ, Anything, PLZ, Not(ChangesIZ) & Not(ConcernsStack), Some(DISCARD_YF)), operationPairBuilder3(PHZ, Anything, PLZ, Not(ChangesIZ) & Not(ConcernsStack), Some(DISCARD_YF)),
operationPairBuilder3(PHD, Anything, PLD, Not(ChangesDirectPageRegister), None), operationPairBuilder3(PHD, Anything, PLD, Not(HasOpcodeIn(ChangesDirectPageRegister)), None),
operationPairBuilder3(PHB, Anything, PLB, Not(ChangesDataBankRegister), None), operationPairBuilder3(PHB, Anything, PLB, Not(HasOpcodeIn(ChangesDataBankRegister)), None),
operationPairBuilder3(INX, DoesntMatterWhatItDoesWith(State.N, State.Z), DEX, Not(ConcernsX) & Not(ReadsNOrZ), 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(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), operationPairBuilder3(INY, DoesntMatterWhatItDoesWith(State.N, State.Z), DEY, Not(ConcernsX) & Not(ReadsNOrZ), None),
@ -529,19 +528,19 @@ object AlwaysGoodOptimizations {
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate)) ~ (Elidable & HasOpcode(LDA) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PHA)) ~ (Elidable & HasOpcode(PHA)) ~
(Linear & Not(ConcernsStack) | HasOpcodeIn(Set(JSR, BSR))).* ~ (Linear & Not(ConcernsStack) | HasOpcodeIn(JSR, BSR)).* ~
(Elidable & HasOpcode(PLA)) ~~> { code => (Elidable & HasOpcode(PLA)) ~~> { code =>
code.head :: (code.drop(2).init :+ code.head) code.head :: (code.drop(2).init :+ code.head)
}, },
(Elidable & HasOpcode(LDX) & HasAddrMode(Immediate)) ~ (Elidable & HasOpcode(LDX) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PHX)) ~ (Elidable & HasOpcode(PHX)) ~
(Linear & Not(ConcernsStack) | HasOpcodeIn(Set(JSR, BSR))).* ~ (Linear & Not(ConcernsStack) | HasOpcodeIn(JSR, BSR)).* ~
(Elidable & HasOpcode(PLX)) ~~> { code => (Elidable & HasOpcode(PLX)) ~~> { code =>
code.head :: (code.drop(2).init :+ code.head) code.head :: (code.drop(2).init :+ code.head)
}, },
(Elidable & HasOpcode(LDY) & HasAddrMode(Immediate)) ~ (Elidable & HasOpcode(LDY) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PHY)) ~ (Elidable & HasOpcode(PHY)) ~
(Linear & Not(ConcernsStack) | HasOpcodeIn(Set(JSR, BSR))).* ~ (Linear & Not(ConcernsStack) | HasOpcodeIn(JSR, BSR)).* ~
(Elidable & HasOpcode(PLY)) ~~> { code => (Elidable & HasOpcode(PLY)) ~~> { code =>
code.head :: (code.drop(2).init :+ code.head) code.head :: (code.drop(2).init :+ code.head)
}, },
@ -598,10 +597,10 @@ object AlwaysGoodOptimizations {
val BranchInPlaceRemoval = new RuleBasedAssemblyOptimization("Branch in place", val BranchInPlaceRemoval = new RuleBasedAssemblyOptimization("Branch in place",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(AllDirectJumps & HasAddrModeIn(Set(Absolute, Relative, LongAbsolute, LongRelative)) & MatchParameter(0) & Elidable) ~ (HasOpcodeIn(AllDirectJumps) & HasAddrModeIn(Absolute, Relative, LongAbsolute, LongRelative) & MatchParameter(0) & Elidable) ~
HasOpcodeIn(NoopDiscardsFlags).* ~ NoopDiscardsFlags.* ~
(HasOpcode(LABEL) & MatchParameter(0)) ~~> (c => c.last :: Nil), (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) & Not(MatchParameter(0))).* ~
(HasOpcode(LABEL) & MatchParameter(0)) ~~> (_.tail), (HasOpcode(LABEL) & MatchParameter(0)) ~~> (_.tail),
(HasOpcode(BEQ) & MatchParameter(0) & Elidable) ~ HasOpcode(BNE) ~ (HasOpcode(BEQ) & MatchParameter(0) & Elidable) ~ HasOpcode(BNE) ~
@ -653,13 +652,13 @@ object AlwaysGoodOptimizations {
(Elidable & HasOpcode(JMP) & HasAddrMode(Absolute) & MatchParameter(0)) ~ (Elidable & HasOpcode(JMP) & HasAddrMode(Absolute) & MatchParameter(0)) ~
(Not(HasOpcode(LABEL)) & Not(MatchParameter(0))).* ~ (Not(HasOpcode(LABEL)) & Not(MatchParameter(0))).* ~
(HasOpcode(LABEL) & MatchParameter(0)) ~ (HasOpcode(LABEL) & MatchParameter(0)) ~
(HasOpcode(LABEL) | HasOpcodeIn(NoopDiscardsFlags)).* ~ (HasOpcode(LABEL) | NoopDiscardsFlags).* ~
HasOpcode(RTS) ~~> (code => AssemblyLine.implied(RTS) :: code.tail), HasOpcode(RTS) ~~> (code => AssemblyLine.implied(RTS) :: code.tail),
(Elidable & HasOpcodeIn(ShortBranching) & MatchParameter(0)) ~ (Elidable & ShortBranching & MatchParameter(0)) ~
(HasOpcodeIn(NoopDiscardsFlags).* ~ (NoopDiscardsFlags.* ~
(Elidable & HasOpcode(RTS))).capture(1) ~ (Elidable & HasOpcode(RTS))).capture(1) ~
(HasOpcode(LABEL) & MatchParameter(0)) ~ (HasOpcode(LABEL) & MatchParameter(0)) ~
HasOpcodeIn(NoopDiscardsFlags).* ~ NoopDiscardsFlags.* ~
(Elidable & HasOpcode(RTS)) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1)), (Elidable & HasOpcode(RTS)) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1)),
) )
@ -693,10 +692,10 @@ object AlwaysGoodOptimizations {
val TailCallOptimization = new RuleBasedAssemblyOptimization("Tail call optimization", val TailCallOptimization = new RuleBasedAssemblyOptimization("Tail call optimization",
needsFlowInfo = FlowInfoRequirement.NoRequirement, 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)) ~ (Elidable & HasOpcode(JSR)) ~
HasOpcode(LABEL).* ~ 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)), 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", val PoinlessFlagChange = new RuleBasedAssemblyOptimization("Pointless flag change",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(HasOpcodeIn(Set(CMP, CPX, CPY)) & Elidable) ~ NoopDiscardsFlags ~~> (_.tail), (HasOpcodeIn(CMP, CPX, CPY) & Elidable) ~ NoopDiscardsFlags ~~> (_.tail),
(OverwritesC & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsC) & Not(DiscardsC)).* ~ DiscardsC ~~> (_.tail), (HasOpcodeIn(OverwritesC) & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsC) & Not(HasOpcodeIn(DiscardsC))).* ~ HasOpcodeIn(DiscardsC) ~~> (_.tail),
(OverwritesD & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsD) & Not(DiscardsD)).* ~ DiscardsD ~~> (_.tail), (HasOpcodeIn(OverwritesD) & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsD) & Not(HasOpcodeIn(DiscardsD))).* ~ HasOpcodeIn(DiscardsD) ~~> (_.tail),
(OverwritesV & Elidable & Not(ChangesStack)) ~ (LinearOrLabel & Not(ReadsV) & Not(DiscardsV)).* ~ DiscardsV ~~> (_.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 // 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", val ReverseFlowAnalysis = new RuleBasedAssemblyOptimization("Reverse flow analysis",
needsFlowInfo = FlowInfoRequirement.BackwardFlow, 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 & 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(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(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(LAX) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(Set(SEC, CLC)) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_ => Nil), (Elidable & HasOpcodeIn(SEC, CLC) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(Set(CLD, SED)) & DoesntMatterWhatItDoesWith(State.D)) ~~> (_ => Nil), (Elidable & HasOpcodeIn(CLD, SED) & DoesntMatterWhatItDoesWith(State.D)) ~~> (_ => Nil),
(Elidable & HasOpcode(CLV) & DoesntMatterWhatItDoesWith(State.V)) ~~> (_ => Nil), (Elidable & HasOpcode(CLV) & DoesntMatterWhatItDoesWith(State.V)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(Set(CMP, CPX, CPY)) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~~> (_ => Nil), (Elidable & HasOpcodeIn(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(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(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(ADC, SBC) & DoesntMatterWhatItDoesWith(State.A, State.C, State.V, State.N, State.Z)) ~~> (_ => Nil),
) )
private def modificationOfJustWrittenValue(store: Opcode.Value, private def modificationOfJustWrittenValue(store: Opcode.Value,
@ -892,14 +891,14 @@ object AlwaysGoodOptimizations {
val IdempotentDuplicateRemoval = new RuleBasedAssemblyOptimization("Idempotent duplicate operation", val IdempotentDuplicateRemoval = new RuleBasedAssemblyOptimization("Idempotent duplicate operation",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
HasOpcode(RTS) ~ HasOpcodeIn(NoopDiscardsFlags).* ~ (HasOpcode(RTS) ~ Elidable) ~~> (_.take(1)) :: HasOpcode(RTS) ~ NoopDiscardsFlags.* ~ (HasOpcode(RTS) ~ Elidable) ~~> (_.take(1)) ::
HasOpcode(RTI) ~ HasOpcodeIn(NoopDiscardsFlags).* ~ (HasOpcode(RTI) ~ Elidable) ~~> (_.take(1)) :: HasOpcode(RTI) ~ NoopDiscardsFlags.* ~ (HasOpcode(RTI) ~ Elidable) ~~> (_.take(1)) ::
HasOpcode(DISCARD_XF) ~ (Not(HasOpcode(DISCARD_XF)) & HasOpcodeIn(NoopDiscardsFlags + LABEL)).* ~ HasOpcode(DISCARD_XF) ~~> (_.tail) :: HasOpcode(DISCARD_XF) ~ (Not(HasOpcode(DISCARD_XF)) & NoopDiscardsFlagsOrLabel).* ~ HasOpcode(DISCARD_XF) ~~> (_.tail) ::
HasOpcode(DISCARD_AF) ~ (Not(HasOpcode(DISCARD_AF)) & HasOpcodeIn(NoopDiscardsFlags + LABEL)).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail) :: HasOpcode(DISCARD_AF) ~ (Not(HasOpcode(DISCARD_AF)) & NoopDiscardsFlagsOrLabel).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail) ::
HasOpcode(DISCARD_YF) ~ (Not(HasOpcode(DISCARD_YF)) & HasOpcodeIn(NoopDiscardsFlags + LABEL)).* ~ HasOpcode(DISCARD_YF) ~~> (_.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 => List(RTS, RTI, SEC, CLC, CLV, CLD, SED, SEI, CLI, TAX, TXA, TYA, TAY, TXS, TSX).flatMap { opcode =>
Seq( 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), HasOpcode(opcode) ~ (HasOpcode(opcode) ~ Elidable) ~~> (_.init),
) )
}: _* }: _*
@ -907,30 +906,30 @@ object AlwaysGoodOptimizations {
val PointlessRegisterTransfers = new RuleBasedAssemblyOptimization("Pointless register transfers", val PointlessRegisterTransfers = new RuleBasedAssemblyOptimization("Pointless register transfers",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
HasOpcode(TYA) ~ (Elidable & Set(TYA, TAY)) ~~> (_.init), HasOpcode(TYA) ~ (Elidable & HasOpcodeIn(TYA, TAY)) ~~> (_.init),
HasOpcode(TXA) ~ (Elidable & Set(TXA, TAX)) ~~> (_.init), HasOpcode(TXA) ~ (Elidable & HasOpcodeIn(TXA, TAX)) ~~> (_.init),
HasOpcode(TAY) ~ (Elidable & Set(TYA, TAY)) ~~> (_.init), HasOpcode(TAY) ~ (Elidable & HasOpcodeIn(TYA, TAY)) ~~> (_.init),
HasOpcode(TAX) ~ (Elidable & Set(TXA, TAX)) ~~> (_.init), HasOpcode(TAX) ~ (Elidable & HasOpcodeIn(TXA, TAX)) ~~> (_.init),
HasOpcode(TSX) ~ (Elidable & Set(TXS, TSX)) ~~> (_.init), HasOpcode(TSX) ~ (Elidable & HasOpcodeIn(TXS, TSX)) ~~> (_.init),
HasOpcode(TXS) ~ (Elidable & Set(TXS, TSX)) ~~> (_.init), HasOpcode(TXS) ~ (Elidable & HasOpcodeIn(TXS, TSX)) ~~> (_.init),
HasOpcodeIn(Set(TXA, TAX, LAX, LXA)) ~ HasOpcodeIn(TXA, TAX, LAX, LXA) ~
(Linear & Not(ChangesNAndZ) & Not(ChangesA) & Not(ChangesX)).* ~ (Linear & Not(ChangesNAndZ) & Not(ChangesA) & Not(ChangesX)).* ~
(Elidable & HasOpcodeIn(Set(TXA, TAX))) ~~> (_.init), (Elidable & HasOpcodeIn(TXA, TAX)) ~~> (_.init),
HasOpcodeIn(Set(TYA, TAY)) ~ HasOpcodeIn(TYA, TAY) ~
(Linear & Not(ChangesNAndZ) & Not(ChangesA) & Not(ChangesX)).* ~ (Linear & Not(ChangesNAndZ) & Not(ChangesA) & Not(ChangesX)).* ~
(Elidable & HasOpcodeIn(Set(TYA, TAY))) ~~> (_.init), (Elidable & HasOpcodeIn(TYA, TAY)) ~~> (_.init),
HasOpcode(TSX) ~ (Not(ChangesX) & Not(ChangesS) & Linear).* ~ (Elidable & Set(TXS, TSX)) ~~> (_.init), HasOpcode(TSX) ~ (Not(ChangesX) & Not(ChangesS) & Linear).* ~ (Elidable & HasOpcodeIn(TXS, TSX)) ~~> (_.init),
HasOpcode(TXS) ~ (Not(ChangesX) & Not(ChangesS) & Linear).* ~ (Elidable & Set(TXS, TSX)) ~~> (_.init), HasOpcode(TXS) ~ (Not(ChangesX) & Not(ChangesS) & Linear).* ~ (Elidable & HasOpcodeIn(TXS, TSX)) ~~> (_.init),
) )
val PointlessRegisterTransfersBeforeStore = new RuleBasedAssemblyOptimization("Pointless register transfers before store", val PointlessRegisterTransfersBeforeStore = new RuleBasedAssemblyOptimization("Pointless register transfers before store",
needsFlowInfo = FlowInfoRequirement.BackwardFlow, needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(TXA)) ~ (Elidable & HasOpcode(TXA)) ~
(Linear & Not(ConcernsA) & Not(ConcernsX)).* ~ (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)) ~ (Elidable & HasOpcode(TYA)) ~
(Linear & Not(ConcernsA) & Not(ConcernsY)).* ~ (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(TAX) & Elidable) ~
HasOpcode(LABEL).* ~ HasOpcode(LABEL).* ~
HasOpcode(TXA).? ~ 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(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
(HasOpcode(TSX) & Elidable) ~ (HasOpcode(TSX) & Elidable) ~
HasOpcode(LABEL).* ~ HasOpcode(LABEL).* ~
HasOpcode(TSX).? ~ 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(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
(HasOpcode(TXA) & Elidable) ~ (HasOpcode(TXA) & Elidable) ~
HasOpcode(LABEL).* ~ HasOpcode(LABEL).* ~
HasOpcode(TAX).? ~ 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(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
(HasOpcode(TAY) & Elidable) ~ (HasOpcode(TAY) & Elidable) ~
HasOpcode(LABEL).* ~ HasOpcode(LABEL).* ~
HasOpcode(TYA).? ~ 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(RTS) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](1) ++ (AssemblyLine.implied(RTS) :: code.tail)),
(HasOpcode(TYA) & Elidable) ~ (HasOpcode(TYA) & Elidable) ~
HasOpcode(LABEL).* ~ HasOpcode(LABEL).* ~
HasOpcode(TAY).? ~ 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)), 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", val PointlessRegisterTransfersBeforeCompare = new RuleBasedAssemblyOptimization("Pointless register transfers and loads before compare",
needsFlowInfo = FlowInfoRequirement.BackwardFlow, needsFlowInfo = FlowInfoRequirement.BackwardFlow,
HasOpcodeIn(Set(DEX, INX, LDX, LAX)) ~ HasOpcodeIn(DEX, INX, LDX, LAX) ~
(HasOpcode(TXA) & Elidable & DoesntMatterWhatItDoesWith(State.A)) ~~> (code => code.init), (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), (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), (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), (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), (HasOpcode(LDY) & Elidable & DoesntMatterWhatItDoesWith(State.Y) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init),
) )
@ -989,9 +988,9 @@ object AlwaysGoodOptimizations {
((ShortBranching -- ReadsNOrZ) & MatchParameter(0)) ((ShortBranching -- ReadsNOrZ) & MatchParameter(0))
} }
val inner: AssemblyPattern = if (withRts) { val inner: AssemblyPattern = if (withRts) {
(Linear & Not(readsI) & Not(ReadsNOrZ ++ NoopDiscardsFlags)).* ~ (Linear & Not(readsI) & Not(ReadsNOrZ | NoopDiscardsFlags)).* ~
ManyWhereAtLeastOne(HasOpcodeIn(NoopDiscardsFlags), HasOpcode(discardIF)) ~ ManyWhereAtLeastOne(NoopDiscardsFlags, HasOpcode(discardIF)) ~
HasOpcodeIn(Set(RTS, RTI)) ~ HasOpcodeIn(RTS, RTI) ~
Not(HasOpcode(LABEL)).* Not(HasOpcode(LABEL)).*
} else { } else {
(Linear & Not(concernsI) & Not(ChangesA) & Not(ReadsNOrZ)).* (Linear & Not(concernsI) & Not(ChangesA) & Not(ReadsNOrZ)).*
@ -1072,36 +1071,36 @@ object AlwaysGoodOptimizations {
val PoinlessLoadBeforeAnotherLoad = new RuleBasedAssemblyOptimization("Pointless load before another load", val PoinlessLoadBeforeAnotherLoad = new RuleBasedAssemblyOptimization("Pointless load before another load",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(Set(LDA, TXA, TYA) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ)).* ~ OverwritesA ~~> (_.tail), (HasOpcodeIn(LDA, TXA, TYA) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ)).* ~ OverwritesA ~~> (_.tail),
(Set(LDX, TAX, TSX) & Elidable) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ)).* ~ OverwritesX ~~> (_.tail), (HasOpcodeIn(LDX, TAX, TSX) & Elidable) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ)).* ~ OverwritesX ~~> (_.tail),
(Set(LDY, TAY) & Elidable) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ)).* ~ OverwritesY ~~> (_.tail), (HasOpcodeIn(LDY, TAY) & Elidable) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ)).* ~ OverwritesY ~~> (_.tail),
) )
// TODO: better proofs that memory doesn't change // TODO: better proofs that memory doesn't change
val PointlessLoadAfterLoadOrStore = new RuleBasedAssemblyOptimization("Pointless load after load or store", val PointlessLoadAfterLoadOrStore = new RuleBasedAssemblyOptimization("Pointless load after load or store",
needsFlowInfo = FlowInfoRequirement.BackwardFlow, 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))).* ~ (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF))).* ~
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (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))).* ~ (Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF))).* ~
(Elidable & HasOpcode(LDX) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (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))).* ~ (Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF))).* ~
(Elidable & HasOpcode(LDY) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (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)).* ~ (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), (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)).* ~ (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), (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)).* ~ (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), (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)).* ~ (Linear & Not(ChangesY) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.init), (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)) ~ (ShortConditionalBranching & MatchParameter(2)) ~
(Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(LABEL) & MatchParameter(2)) ~ (HasOpcode(LABEL) & MatchParameter(2)) ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (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)) ~ (ShortConditionalBranching & MatchParameter(2)) ~
(Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ (Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(LABEL) & MatchParameter(2)) ~ (HasOpcode(LABEL) & MatchParameter(2)) ~
(Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (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)) ~ (ShortConditionalBranching & MatchParameter(2)) ~
(Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ (Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(LABEL) & MatchParameter(2)) ~ (HasOpcode(LABEL) & MatchParameter(2)) ~
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
(HasOpcodeIn(Set(LDA, STA)) & MatchAddrMode(0) & MatchParameter(1)) ~ (HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~
(HasOpcodeIn(ShortBranching) & MatchParameter(3)) ~ (ShortBranching & MatchParameter(3)) ~
(Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(LABEL) & MatchParameter(3) & HasCallerCount(1)) ~ (HasOpcode(LABEL) & MatchParameter(3) & HasCallerCount(1)) ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
HasOpcodeIn(Set(TXA, TAX, LAX, LXA)) ~ HasOpcodeIn(TXA, TAX, LAX, LXA) ~
(Not(Set(TXA, TAX)) & Linear & Not(ChangesA) & Not(ChangesX)).* ~ (Not(HasOpcodeIn(TXA, TAX)) & Linear & Not(ChangesA) & Not(ChangesX)).* ~
(Elidable & HasOpcodeIn(Set(TXA, TAX)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (Elidable & HasOpcodeIn(TXA, TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init),
HasOpcodeIn(Set(TYA, TAY)) ~ HasOpcodeIn(TYA, TAY) ~
(Not(Set(TYA, TAY)) & Linear & Not(ChangesA) & Not(ChangesY)).* ~ (Not(HasOpcodeIn(TYA, TAY)) & Linear & Not(ChangesA) & Not(ChangesY)).* ~
(Elidable & HasOpcodeIn(Set(TYA, TAY)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (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)).* ~ (Linear & Not(HasOpcode(TAX)) & Not(ChangesA) & DoesntChangeMemoryAt(0, 1)).* ~
HasOpcode(TAX) ~ HasOpcode(TAX) ~
(LinearOrBranch & Not(ChangesX) & DoesntChangeMemoryAt(0, 1)).* ~ (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)).* ~ (Linear & Not(HasOpcode(TAY)) & Not(ChangesA) & DoesntChangeMemoryAt(0, 1)).* ~
HasOpcode(TAY) ~ HasOpcode(TAY) ~
(LinearOrBranch & Not(ChangesY) & DoesntChangeMemoryAt(0, 1)).* ~ (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", val RearrangableLoadFromTheSameLocation = new RuleBasedAssemblyOptimization("Rearrangable load from the same location",
@ -1223,19 +1222,19 @@ object AlwaysGoodOptimizations {
val PointlessOperationFromFlow = new RuleBasedAssemblyOptimization("Pointless operation from flow", val PointlessOperationFromFlow = new RuleBasedAssemblyOptimization("Pointless operation from flow",
needsFlowInfo = FlowInfoRequirement.BackwardFlow, needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcodeIn(Set(LDA, TXA, TYA, AND, EOR, ORA, XAA)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (_ => Nil), (Elidable & HasOpcodeIn(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(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(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(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(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(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(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(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(INC, DEC) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(Set(CLC, SEC)) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_ => Nil), (Elidable & HasOpcodeIn(CLC, SEC) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(Set(CLD, SED)) & DoesntMatterWhatItDoesWith(State.D)) ~~> (_ => Nil), (Elidable & HasOpcodeIn(CLD, SED) & DoesntMatterWhatItDoesWith(State.D)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(Set(CLV)) & DoesntMatterWhatItDoesWith(State.V)) ~~> (_ => Nil), (Elidable & HasOpcodeIn(CLV) & DoesntMatterWhatItDoesWith(State.V)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_ => 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(AND) & HasImmediate(0xff) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_ => Nil),
(Elidable & HasOpcode(ANC) & HasImmediate(0xff) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C)) ~~> (_ => 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", val RearrangeMath = new RuleBasedAssemblyOptimization("Rearranging math",
needsFlowInfo = FlowInfoRequirement.NoRequirement, needsFlowInfo = FlowInfoRequirement.NoRequirement,
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate)) ~ (Elidable & HasOpcode(LDA) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcodeIn(Set(CLC, SEC))) ~ (Elidable & HasOpcodeIn(CLC, SEC)) ~
(Elidable & HasOpcode(ADC) & Not(HasAddrMode(Immediate))) ~~> { c => (Elidable & HasOpcode(ADC) & Not(HasAddrMode(Immediate))) ~~> { c =>
c.last.copy(opcode = LDA) :: c(1) :: c.head.copy(opcode = ADC) :: Nil c.last.copy(opcode = LDA) :: c(1) :: c.head.copy(opcode = ADC) :: Nil
}, },
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate)) ~ (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 c.last.copy(opcode = LDA) :: c.head.copy(opcode = c.last.opcode) :: Nil
}, },
) )
@ -1433,17 +1432,17 @@ object AlwaysGoodOptimizations {
needsFlowInfo = FlowInfoRequirement.BackwardFlow, needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.A)) ~ (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.A)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~ (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 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)) ~ (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.A)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ChangesC)).* ~ (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 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)) ~ (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0) & Not(ChangesC)).* ~ (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 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 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 elseLabel = Elidable & HasOpcode(LABEL) & MatchParameter(0)
val afterLabel = Elidable & HasOpcode(LABEL) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.N, State.V, State.Z) 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 store = Elidable & (Not(ReadsC) & Linear | HasOpcodeIn(RTS, JSR, RTI, RTL, BSR))
val secondReturn = (Elidable & HasOpcodeIn(Set(RTS, RTI) | NoopDiscardsFlags)).*.capture(6) val secondReturn = (Elidable & (HasOpcodeIn(RTS, RTI) | NoopDiscardsFlags)).*.capture(6)
val where = Where { ctx => 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]](4) == ctx.get[List[AssemblyLine]](5) ++ ctx.get[List[AssemblyLine]](6) 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", val Adc0Optimization = new RuleBasedAssemblyOptimization("ADC #0/#1 optimization",
needsFlowInfo = FlowInfoRequirement.BothFlows, needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(LDA) & HasImmediate(0) & HasClear(State.D)) ~ (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 => (Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
val label = getNextLabel("ah") val label = getNextLabel("ah")
List( List(
@ -1570,7 +1569,7 @@ object AlwaysGoodOptimizations {
code.last.copy(opcode = INC), code.last.copy(opcode = INC),
AssemblyLine.label(label)) 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(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 => (Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
val label = getNextLabel("ah") val label = getNextLabel("ah")
@ -1580,11 +1579,11 @@ object AlwaysGoodOptimizations {
AssemblyLine.label(label)) AssemblyLine.label(label))
}, },
(Elidable & HasOpcode(LDA) & HasImmediate(1) & HasClear(State.D) & HasClear(State.C)) ~ (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 => (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)) 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(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 => (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)) List(code.last.copy(opcode = INC))
@ -1633,7 +1632,7 @@ object AlwaysGoodOptimizations {
(HasOpcode(AND) & HasImmediate(1)) ~ (HasOpcode(AND) & HasImmediate(1)) ~
(Linear & Not(ChangesNAndZ) & Not(HasOpcode(CLC)) & Not(ChangesA)).* ~ (Linear & Not(ChangesNAndZ) & Not(HasOpcode(CLC)) & Not(ChangesA)).* ~
(Elidable & HasOpcode(CLC) & HasClear(State.D)) ~ (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 => (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.V, State.Z, State.N, State.A)) ~~> { code =>
val label = getNextLabel("in") val label = getNextLabel("in")
code.take(code.length - 3) ++ List( code.take(code.length - 3) ++ List(
@ -1644,7 +1643,7 @@ object AlwaysGoodOptimizations {
}, },
(HasOpcode(ANC) & HasImmediate(1)) ~ (HasOpcode(ANC) & HasImmediate(1)) ~
(Linear & Not(ChangesNAndZ) & Not(ChangesA) & (Not(ChangesC) | HasOpcode(CLC))).* ~ (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 => (Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.V, State.Z, State.N, State.A)) ~~> { code =>
val label = getNextLabel("in") val label = getNextLabel("in")
code.head.copy(opcode = AND) :: code.take(code.length - 2).tail ++ List( code.head.copy(opcode = AND) :: code.take(code.length - 2).tail ++ List(
@ -1758,8 +1757,8 @@ object AlwaysGoodOptimizations {
(Elidable & HasOpcode(AND) & HasImmediate(1)) ~ (Elidable & HasOpcode(AND) & HasImmediate(1)) ~
((Elidable & Linear & Not(ChangesMemory) & DoesNotConcernMemoryAt(0, 1) & Not(ChangesA)).* ~ ((Elidable & Linear & Not(ChangesMemory) & DoesNotConcernMemoryAt(0, 1) & Not(ChangesA)).* ~
(Elidable & HasOpcode(STA) & DoesNotConcernMemoryAt(0, 1))).capture(3) ~ (Elidable & HasOpcode(STA) & DoesNotConcernMemoryAt(0, 1))).capture(3) ~
((Elidable & HasOpcodeIn(Set(LSR, ROR)) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).* ~ ((Elidable & HasOpcodeIn(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) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.C, State.N))).capture(2) ~~> { (code, ctx) =>
ctx.get[List[AssemblyLine]](2) ++ ctx.get[List[AssemblyLine]](2) ++
List(AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(ROL)) ++ List(AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(ROL)) ++
ctx.get[List[AssemblyLine]](3) ctx.get[List[AssemblyLine]](3)
@ -1768,8 +1767,8 @@ object AlwaysGoodOptimizations {
(Elidable & HasOpcode(AND) & MatchAddrMode(0) & MatchParameter(1)) ~ (Elidable & HasOpcode(AND) & MatchAddrMode(0) & MatchParameter(1)) ~
((Elidable & Linear & Not(ChangesMemory) & DoesNotConcernMemoryAt(0, 1) & Not(ChangesA)).* ~ ((Elidable & Linear & Not(ChangesMemory) & DoesNotConcernMemoryAt(0, 1) & Not(ChangesA)).* ~
(Elidable & HasOpcode(STA) & DoesNotConcernMemoryAt(0, 1))).capture(3) ~ (Elidable & HasOpcode(STA) & DoesNotConcernMemoryAt(0, 1))).capture(3) ~
((Elidable & HasOpcodeIn(Set(LSR, ROR)) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).* ~ ((Elidable & HasOpcodeIn(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) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.Z, State.C, State.N))).capture(2) ~~> { (code, ctx) =>
ctx.get[List[AssemblyLine]](2) ++ ctx.get[List[AssemblyLine]](2) ++
List(AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(ROL)) ++ List(AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(ROL)) ++
ctx.get[List[AssemblyLine]](3) 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(ASL) | HasOpcode(ROL) & HasClear(State.C)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(ROL) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).*.capture(2) ~ (Elidable & HasOpcode(ROL) & Not(ChangesA) & MatchAddrMode(0) & Not(MatchParameter(1))).*.capture(2) ~
(Elidable & HasOpcode(CLC)).? ~ (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(AND) & HasImmediate(1)) ~
(Elidable & HasOpcode(CLC)).? ~ (Elidable & HasOpcode(CLC)).? ~
(Elidable & (HasOpcode(ORA) | HasOpcode(ADC) & HasClear(State.C) & HasClear(State.D)) & MatchAddrMode(0) & MatchParameter(1)) ~ (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) ctx.get[List[AssemblyLine]](2)
}, },
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ (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)).* ~ (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 code.last :: code.drop(2).init
}, },
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ (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)).* ~ (Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~
(Elidable & HasOpcode(LSR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code => (Elidable & HasOpcode(LSR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
code.last :: code.drop(2).init code.last :: code.drop(2).init
}, },
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ (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)).* ~ (Linear & Not(HasOpcode(LSR)) & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~
(Elidable & HasOpcode(LSR) & DoesNotConcernMemoryAt(0, 1)) ~ (Elidable & HasOpcode(LSR) & DoesNotConcernMemoryAt(0, 1)) ~
(Elidable & HasOpcode(ROR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code => (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)) List(AssemblyLine.implied(SEC), code.head.copy(opcode = ROL))
}, },
(Elidable & HasOpcode(ASL) & HasAddrMode(AddrMode.Implied)) ~ (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)) List(AssemblyLine.implied(SEC), code.head.copy(opcode = ROL))
}, },
) )
@ -1823,9 +1822,9 @@ object AlwaysGoodOptimizations {
private def blockIsIdempotentWhenItComesToIndexRegisters(i: Int) = Where(ctx => { private def blockIsIdempotentWhenItComesToIndexRegisters(i: Int) = Where(ctx => {
val code = ctx.get[List[AssemblyLine]](i) val code = ctx.get[List[AssemblyLine]](i)
val rx = code.indexWhere(ReadsX) 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 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 xOk = rx < 0 || wx < 0 || rx >= wx
val yOk = ry < 0 || wy < 0 || ry >= wy val yOk = ry < 0 || wy < 0 || ry >= wy
xOk && yOk xOk && yOk
@ -1834,25 +1833,25 @@ object AlwaysGoodOptimizations {
val CommonExpressionInConditional = new RuleBasedAssemblyOptimization("Common expression in conditional", val CommonExpressionInConditional = new RuleBasedAssemblyOptimization("Common expression in conditional",
needsFlowInfo = FlowInfoRequirement.BackwardFlow, needsFlowInfo = FlowInfoRequirement.BackwardFlow,
( (
(HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~ (HasOpcodeIn(LDA, LAX) & MatchAddrMode(0) & MatchParameter(1)) ~
HasOpcodeIn(Set(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP)).* HasOpcodeIn(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP).*
).capture(7) ~ ).capture(7) ~
blockIsIdempotentWhenItComesToIndexRegisters(7) ~ blockIsIdempotentWhenItComesToIndexRegisters(7) ~
HasOpcodeIn(ShortConditionalBranching) ~ ShortConditionalBranching ~
MatchElidableCopyOf(7, Anything, DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.V)) ~~> { code => MatchElidableCopyOf(7, Anything, DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.V)) ~~> { code =>
code.take(code.length / 2 + 1) code.take(code.length / 2 + 1)
}, },
(Elidable & HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~ (Elidable & HasOpcodeIn(LDA, LAX) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(AND) & HasAddrModeIn(Set(Absolute, ZeroPage)) & DoesntMatterWhatItDoesWith(State.C, State.V, State.A)) ~ (Elidable & HasOpcode(AND) & HasAddrModeIn(Absolute, ZeroPage) & DoesntMatterWhatItDoesWith(State.C, State.V, State.A)) ~
HasOpcodeIn(Set(BEQ, BNE)) ~ HasOpcodeIn(BEQ, BNE) ~
(HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code => (HasOpcodeIn(LDA, LAX) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
List(code(0), code(1).copy(opcode = BIT), code(2)) 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)) ~ (Elidable & HasOpcode(AND) & MatchAddrMode(0) & MatchParameter(1)) ~
HasOpcodeIn(Set(BEQ, BNE)) ~ HasOpcodeIn(BEQ, BNE) ~
(HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code => (HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code =>
List(code(1).copy(opcode = LDA), code(0).copy(opcode = BIT), code(2)) List(code(1).copy(opcode = LDA), code(0).copy(opcode = BIT), code(2))
}, },
@ -1874,14 +1873,14 @@ object AlwaysGoodOptimizations {
needsFlowInfo = FlowInfoRequirement.JustLabels, needsFlowInfo = FlowInfoRequirement.JustLabels,
((Elidable & HasOpcode(LABEL) & MatchParameter(1)) ~ LinearOrLabel.*).capture(10) ~ ((Elidable & HasOpcode(LABEL) & MatchParameter(1)) ~ LinearOrLabel.*).capture(10) ~
Where(ctx => ctx.get[List[AssemblyLine]](10).map(_.sizeInBytes).sum < 100) ~ 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(JMP) & MatchParameter(1)) ~
(Elidable & HasOpcode(LABEL) & MatchParameter(0)) ~~> { (code, ctx) => (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) 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) ~ ((Elidable & HasOpcode(LABEL) & MatchParameter(1)) ~ LinearOrLabel.*).capture(10) ~
Where(ctx => ctx.get[List[AssemblyLine]](10).map(_.sizeInBytes).sum < 100) ~ 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 & Not(MatchParameter(0))).* ~
(Elidable & HasOpcode(LABEL) & MatchParameter(0)) ~ (Elidable & HasOpcode(LABEL) & MatchParameter(0)) ~
(Elidable & HasOpcode(JMP) & MatchParameter(1))).capture(11) ~~> { (code, ctx) => (Elidable & HasOpcode(JMP) & MatchParameter(1))).capture(11) ~~> { (code, ctx) =>
@ -1898,17 +1897,17 @@ object AlwaysGoodOptimizations {
val IndexComparisonOptimization = new RuleBasedAssemblyOptimization("Index comparison optimization", val IndexComparisonOptimization = new RuleBasedAssemblyOptimization("Index comparison optimization",
needsFlowInfo = FlowInfoRequirement.BackwardFlow, 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)).* ~ (Linear & Not(ConcernsX)).* ~
(Elidable & (HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPX) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code => (Elidable & (HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPX) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code =>
code.tail.init :+ code.head 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)).* ~ (Linear & Not(ConcernsY)).* ~
(Elidable & (HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPY) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code => (Elidable & (HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPY) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code =>
code.tail.init :+ code.head 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)).* ~ (Linear & Not(ConcernsA)).* ~
(Elidable & ( (Elidable & (
HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.Y) HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.Y)
@ -1999,22 +1998,22 @@ object AlwaysGoodOptimizations {
needsFlowInfo = FlowInfoRequirement.BackwardFlow, needsFlowInfo = FlowInfoRequirement.BackwardFlow,
HasOpcode(LDA) ~ HasOpcode(LDA) ~
(Elidable & HasOpcode(AND) & HasImmediate(0x80)) ~ (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))) List(code(0), remapZ2N(code(2)))
}, },
(Elidable & HasOpcode(LDA) & HasImmediate(0x80)) ~ (Elidable & HasOpcode(LDA) & HasImmediate(0x80)) ~
(Elidable & HasOpcode(AND)) ~ (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))) List(code(1).copy(opcode = LDA), remapZ2N(code(2)))
}, },
) )
val PointlessSignCheck: RuleBasedAssemblyOptimization = { val PointlessSignCheck: RuleBasedAssemblyOptimization = {
def loadOldSignedVariable: AssemblyPattern = ( def loadOldSignedVariable: AssemblyPattern = (
(HasOpcodeIn(Set(AND, ANC)) & HasImmediateWhere(i => (i & 0x80) == 0)) ~ (HasOpcodeIn(AND, ANC) & HasImmediateWhere(i => (i & 0x80) == 0)) ~
(HasOpcode(STA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchAddrMode(0) & MatchParameter(1)) ~ (HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & MatchAddrMode(0) & MatchParameter(1)) ~
DoesNotConcernMemoryAt(0, 1).* ~ DoesNotConcernMemoryAt(0, 1).* ~
(HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(1)) (HasOpcode(LDA) & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(1))
).capture(10) ~ Where(_.isExternallyLinearBlock(10)) ).capture(10) ~ Where(_.isExternallyLinearBlock(10))
val isNonnegative: Int => Boolean = i => (i & 0x80) == 0 val isNonnegative: Int => Boolean = i => (i & 0x80) == 0
@ -2026,21 +2025,21 @@ object AlwaysGoodOptimizations {
loadOldSignedVariable ~ loadOldSignedVariable ~
(Elidable & HasOpcode(BMI)) ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) }, (Elidable & HasOpcode(BMI)) ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) },
loadOldSignedVariable ~ loadOldSignedVariable ~
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)) ~ (Elidable & HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)) ~
(Elidable & HasOpcode(BMI)) ~ (Elidable & HasOpcode(BMI)) ~
OverwritesA ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) :+ code.last }, OverwritesA ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) :+ code.last },
loadOldSignedVariable ~ loadOldSignedVariable ~
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)) ~ (Elidable & HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)) ~
(Elidable & HasOpcode(BMI)) ~~> { code => code.init }, (Elidable & HasOpcode(BMI)) ~~> { code => code.init },
loadOldSignedVariable ~ 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) }, (Elidable & HasOpcode(BPL)) ~~> { code => code.init :+ code.last.copy(opcode = JMP, addrMode = Absolute) },
loadOldSignedVariable ~ loadOldSignedVariable ~
((Linear & Not(ConcernsX) & Not(ChangesA)).* ~ ((Linear & Not(ConcernsX) & Not(ChangesA)).* ~
HasOpcode(TAX) ~ HasOpcode(TAX) ~
(Linear & Not(ConcernsX)).*).capture(11) ~ (Linear & Not(ConcernsX)).*).capture(11) ~
(Elidable & HasOpcode(TXA)) ~ (Elidable & HasOpcode(TXA)) ~
(Elidable & HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)).? ~ (Elidable & HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)).? ~
(Elidable & HasOpcode(BMI)) ~ (Elidable & HasOpcode(BMI)) ~
OverwritesA ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) ++ ctx.get[List[AssemblyLine]](11) :+ code.last }, OverwritesA ~~> { (code, ctx) => ctx.get[List[AssemblyLine]](10) ++ ctx.get[List[AssemblyLine]](11) :+ code.last },
loadOldSignedVariable ~ loadOldSignedVariable ~
@ -2048,14 +2047,14 @@ object AlwaysGoodOptimizations {
HasOpcode(TAX) ~ HasOpcode(TAX) ~
(Linear & Not(ConcernsX)).* ~ (Linear & Not(ConcernsX)).* ~
HasOpcode(TXA) ~ HasOpcode(TXA) ~
(HasOpcodeIn(Set(ORA, EOR)) & HasImmediateWhere(isNonnegative)).? ~ (HasOpcodeIn(ORA, EOR) & HasImmediateWhere(isNonnegative)).? ~
(Elidable & HasOpcode(BMI)) ~~> { code => code.init }, (Elidable & HasOpcode(BMI)) ~~> { code => code.init },
loadOldSignedVariable ~ loadOldSignedVariable ~
(Linear & Not(ConcernsX) & Not(ChangesA)).* ~ (Linear & Not(ConcernsX) & Not(ChangesA)).* ~
HasOpcode(TAX) ~ HasOpcode(TAX) ~
(Linear & Not(ConcernsX)).* ~ (Linear & Not(ConcernsX)).* ~
HasOpcode(TXA) ~ 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) }, (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(BCC) & MatchParameter(14)) ~
(Elidable & HasOpcode(INX)) ~ (Elidable & HasOpcode(INX)) ~
(Elidable & HasOpcode(LABEL) & MatchParameter(14) & HasCallerCount(1)) ~ (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(STA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~
(Elidable & HasOpcode(TXA)) ~ (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) => (Elidable & HasOpcode(STA) & MatchAddrMode(2) & MatchParameter(3) & Not(ConcernsX) & DoesntMatterWhatItDoesWith(State.C, State.N, State.V, State.Z)) ~~>{ (code, ctx) =>
val label = getNextLabel("in") val label = getNextLabel("in")
List( List(
@ -2161,12 +2160,12 @@ object AlwaysGoodOptimizations {
val CommonIndexSubexpressionElimination: RuleBasedAssemblyOptimization = { val CommonIndexSubexpressionElimination: RuleBasedAssemblyOptimization = {
def eliminate(firstLda: Boolean, targetY: Boolean): AssemblyRule = { def eliminate(firstLda: Boolean, targetY: Boolean): AssemblyRule = {
val firstLoad = HasClear(State.D) & ( val firstLoad = HasClear(State.D) & (
if (firstLda) HasOpcodeIn(Set(LDA, STA, LAX)) & HasAddrModeIn(Set(Absolute, ZeroPage, Immediate)) & MatchParameter(1) if (firstLda) HasOpcodeIn(LDA, STA, LAX) & HasAddrModeIn(Absolute, ZeroPage, Immediate) & MatchParameter(1)
else if (targetY) HasOpcodeIn(Set(TXA, TAX)) else if (targetY) HasOpcodeIn(TXA, TAX)
else HasOpcodeIn(Set(TAY, TYA)) else HasOpcodeIn(TAY, TYA)
) )
val secondLoad = Elidable & ( 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 if (targetY) HasOpcode(TXA)
else HasOpcode(TYA) else HasOpcode(TYA)
) )
@ -2176,8 +2175,8 @@ object AlwaysGoodOptimizations {
else HasOpcode(TAX) else HasOpcode(TAX)
) & DoesntMatterWhatItDoesWith(State.A, State.Z, State.N, State.C) ) & DoesntMatterWhatItDoesWith(State.A, State.Z, State.N, State.C)
val fillerLine = val fillerLine =
HasAddrMode(Implied) & HasOpcodeIn(Set(ASL, CLC, CLD, SEC, SED, LSR, INC, DEC)) | HasAddrMode(Implied) & HasOpcodeIn(ASL, CLC, CLD, SEC, SED, LSR, INC, DEC) |
HasOpcodeIn(Set(ADC, ORA, EOR, AND, SBC)) & HasAddrModeIn(Set(Absolute, ZeroPage, Immediate)) HasOpcodeIn(ADC, ORA, EOR, AND, SBC) & HasAddrModeIn(Absolute, ZeroPage, Immediate)
val firstFiller = fillerLine.* val firstFiller = fillerLine.*
val secondFiller = (Elidable & fillerLine).* val secondFiller = (Elidable & fillerLine).*
val betweenLines = (Linear & Not(secondLoad) & Not(if (targetY) ChangesY else ChangesX)).+ val betweenLines = (Linear & Not(secondLoad) & Not(if (targetY) ChangesY else ChangesX)).+
@ -2224,7 +2223,7 @@ object AlwaysGoodOptimizations {
val ConstantPointer = new RuleBasedAssemblyOptimization("Constant pointer optimization", val ConstantPointer = new RuleBasedAssemblyOptimization("Constant pointer optimization",
needsFlowInfo = FlowInfoRequirement.ForwardFlow, needsFlowInfo = FlowInfoRequirement.ForwardFlow,
(HasOpcode(STA) & MatchA(0) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(4)) ~ (HasOpcode(STA) & MatchA(0) & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(4)) ~
Where(ctx => { Where(ctx => {
val lo = ctx.get[Constant](4) val lo = ctx.get[Constant](4)
ctx.addObject(5, lo + 1) ctx.addObject(5, lo + 1)
@ -2232,7 +2231,7 @@ object AlwaysGoodOptimizations {
true true
}) ~ }) ~
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~ (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 => { Where(ctx => {
val lo = ctx.get[Int](0) & 0xff val lo = ctx.get[Int](0) & 0xff
val hi = ctx.get[Int](1) & 0xff val hi = ctx.get[Int](1) & 0xff
@ -2240,13 +2239,13 @@ object AlwaysGoodOptimizations {
true true
}) ~ }) ~
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~ (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 addr = ctx.get[Int](2)
val last = code.last val last = code.last
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY) 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 => { Where(ctx => {
val lo = ctx.get[Constant](4) val lo = ctx.get[Constant](4)
ctx.addObject(5, lo + 1) ctx.addObject(5, lo + 1)
@ -2254,7 +2253,7 @@ object AlwaysGoodOptimizations {
true true
}) ~ }) ~
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~ (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 => { Where(ctx => {
val lo = ctx.get[Int](0) & 0xff val lo = ctx.get[Int](0) & 0xff
val hi = ctx.get[Int](1) & 0xff val hi = ctx.get[Int](1) & 0xff
@ -2262,7 +2261,7 @@ object AlwaysGoodOptimizations {
true true
}) ~ }) ~
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~ (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 addr = ctx.get[Int](2)
val last = code.last val last = code.last
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY) code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY)

View File

@ -1,5 +1,6 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.assembly.OptimizationContext
import millfork.{CompilationFlag, CompilationOptions} import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.mos.AssemblyLine import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.OpcodeClasses import millfork.assembly.mos.OpcodeClasses
@ -11,7 +12,9 @@ import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConsta
*/ */
object CoarseFlowAnalyzer { 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 ceFlag = compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)
val cmosFlag = compilationOptions.flag(CompilationFlag.EmitCmosOpcodes) val cmosFlag = compilationOptions.flag(CompilationFlag.EmitCmosOpcodes)
val initialStatus = val initialStatus =
@ -36,6 +39,7 @@ object CoarseFlowAnalyzer {
for (i <- codeArray.indices) { for (i <- codeArray.indices) {
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._ import millfork.assembly.mos.AddrMode._
import millfork.node.MosNiceFunctionProperty._
if (flagArray(i) != currentStatus) { if (flagArray(i) != currentStatus) {
changed = true changed = true
flagArray(i) = currentStatus flagArray(i) = currentStatus
@ -48,6 +52,17 @@ object CoarseFlowAnalyzer {
case _ => None case _ => None
}).fold(currentStatus)(_ ~ _) }).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, _, _, _) => case AssemblyLine(JSR | BYTE, _, _, _) =>
currentStatus = initialStatus currentStatus = initialStatus

View File

@ -1,7 +1,7 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.CompilationOptions import millfork.CompilationOptions
import millfork.assembly.AssemblyOptimization import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.mos.AssemblyLine import millfork.assembly.mos.AssemblyLine
import millfork.env._ import millfork.env._
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
@ -18,7 +18,7 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY) 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 { val paramVariables = f.params match {
case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 => case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>
Set[String]() Set[String]()
@ -62,30 +62,30 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
case STA | STX | STY | SAX | STZ | SHX | SHY | AHX => case STA | STX | STY | SAX | STZ | SHX | SHY | AHX =>
true true
case TSB | TRB => 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 importances(lastaccess).z != Important
case INC | DEC => 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).z != Important &&
importances(lastaccess).n != Important importances(lastaccess).n != Important
case ASL | LSR | ROL | ROR | DCP => 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).z != Important &&
importances(lastaccess).n != Important && importances(lastaccess).n != Important &&
importances(lastaccess).c != Important importances(lastaccess).c != Important
case ISC => 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).z != Important &&
importances(lastaccess).n != Important && importances(lastaccess).n != Important &&
importances(lastaccess).a != Important importances(lastaccess).a != Important
case DCP | SLO | SRE | RLA => 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).z != Important &&
importances(lastaccess).n != Important && importances(lastaccess).n != Important &&
importances(lastaccess).c != Important && importances(lastaccess).c != Important &&
importances(lastaccess).a != Important importances(lastaccess).a != Important
case RRA => 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).z != Important &&
importances(lastaccess).n != Important && importances(lastaccess).n != Important &&
importances(lastaccess).c != Important && importances(lastaccess).c != Important &&

View File

@ -1,6 +1,7 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.CompilationOptions import millfork.CompilationOptions
import millfork.assembly.OptimizationContext
import millfork.assembly.mos.{AssemblyLine, Opcode, State} import millfork.assembly.mos.{AssemblyLine, Opcode, State}
import millfork.env.{Label, MemoryAddressConstant, NormalFunction} import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
@ -33,20 +34,20 @@ object FlowAnalyzer {
private val EmptyCpuStatus = CpuStatus() private val EmptyCpuStatus = CpuStatus()
private val EmptyCpuImportance = CpuImportance() 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 { val forwardFlow = req match {
case FlowInfoRequirement.BothFlows | FlowInfoRequirement.ForwardFlow => case FlowInfoRequirement.BothFlows | FlowInfoRequirement.ForwardFlow =>
() => CoarseFlowAnalyzer.analyze(f, code, options) () => CoarseFlowAnalyzer.analyze(f, code, optimizationContext)
case FlowInfoRequirement.BackwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement => case FlowInfoRequirement.BackwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
() => List.fill(code.size)(EmptyCpuStatus) () => List.fill(code.size)(EmptyCpuStatus)
} }
val reverseFlow = req match { val reverseFlow = req match {
case FlowInfoRequirement.BothFlows | FlowInfoRequirement.BackwardFlow => case FlowInfoRequirement.BothFlows | FlowInfoRequirement.BackwardFlow =>
() => ReverseFlowAnalyzer.analyze(f, code) () => ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
case FlowInfoRequirement.ForwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement => case FlowInfoRequirement.ForwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
() => List.fill(code.size)(EmptyCpuImportance) () => 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 FlowInfoRequirement.NoRequirement => None
case _ => Some(code.flatMap { case _ => Some(code.flatMap {
case AssemblyLine(op, _, MemoryAddressConstant(Label(l)), _) if op != Opcode.LABEL => Some(l) case AssemblyLine(op, _, MemoryAddressConstant(Label(l)), _) if op != Opcode.LABEL => Some(l)

View File

@ -32,6 +32,9 @@ object FlowAnalyzerForImplied {
CLC -> (_.copy(c = Status.SingleFalse)), CLC -> (_.copy(c = Status.SingleFalse)),
SEC -> (_.copy(c = Status.SingleTrue)), SEC -> (_.copy(c = Status.SingleTrue)),
CLV -> (_.copy(v = Status.SingleFalse)), 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)), 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)), 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)), PLX -> (_.copy(src = SourceOfNZ.X, x = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),

View File

@ -1,10 +1,11 @@
package millfork.assembly.mos.opt 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.AssemblyLine
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._ import millfork.assembly.mos.AddrMode._
import millfork.env.NumericConstant import millfork.env.{NormalFunction, NumericConstant}
/** /**
* @author Karol Stasiak * @author Karol Stasiak
@ -13,10 +14,13 @@ object HudsonOptimizations {
val All: List[AssemblyOptimization[AssemblyLine]] = List() val All: List[AssemblyOptimization[AssemblyLine]] = List()
def removeLoadZero(code: List[AssemblyLine]): List[AssemblyLine] = code.map{ def removeLoadZero(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
case AssemblyLine(LDA, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLA) ReverseFlowAnalyzer.analyze(f, code, optimizationContext).zip(code).map {
case AssemblyLine(LDX, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLX) case (i, l) if i.n != Unimportant || i.z != Unimportant => l
case AssemblyLine(LDY, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLY) case (i, AssemblyLine(LDA, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLA)
case l => l case (_, AssemblyLine(LDX, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLX)
case (_, AssemblyLine(LDY, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLY)
case (_, l) => l
}
} }
} }

View File

@ -1,5 +1,6 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.assembly.OptimizationContext
import millfork.assembly.mos.{AddrMode, AssemblyLine} import millfork.assembly.mos.{AddrMode, AssemblyLine}
import millfork.{CompilationFlag, CompilationOptions} import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
@ -16,7 +17,8 @@ object JumpShortening {
distance.toByte == distance 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) val offsets = new Array[Int](code.length)
var o = 0 var o = 0
code.zipWithIndex.foreach{ code.zipWithIndex.foreach{
@ -46,7 +48,7 @@ object JumpShortening {
case (line, _) => line case (line, _) => line
} }
} else { } 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) => case ((info, line@AssemblyLine(JMP, AddrMode.Absolute, MemoryAddressConstant(Label(label)), _)), ix) =>
labelOffsets.get(label).fold(line) { labelOffset => labelOffsets.get(label).fold(line) { labelOffset =>
val thisOffset = offsets(ix) val thisOffset = offsets(ix)

View File

@ -1,7 +1,7 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.CompilationOptions import millfork.CompilationOptions
import millfork.assembly.AssemblyOptimization import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._ import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses} 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 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 { val stillUsedVariables = code.flatMap {
case AssemblyLine(_, _, MemoryAddressConstant(th: MemoryVariable), _) => th match { case AssemblyLine(_, _, MemoryAddressConstant(th: MemoryVariable), _) => th match {
@ -36,7 +36,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
return code 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()) val (optimized, result) = optimizeImpl(code.zip(statuses), eligibleVariables, Map())
if (optimized) { if (optimized) {
ErrorReporting.debug("Optimized local variable reads") ErrorReporting.debug("Optimized local variable reads")

View File

@ -87,8 +87,8 @@ object LoopUnrolling {
(Elidable & HasOpcode(LDX) & MatchNumericImmediate(Start) & Not(HasImmediate(0))).capture(Initialization) ~ (Elidable & HasOpcode(LDX) & MatchNumericImmediate(Start) & Not(HasImmediate(0))).capture(Initialization) ~
(Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~ (Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesX)).*.capture(Body) ~ ((Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesX)).*.capture(Body) ~
(Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step) (Elidable & HasOpcodeIn(DEX, INX)).capture(Step)
).capture(BodyWithStep) ~ ).capture(BodyWithStep) ~
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~ (Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~ (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(LDX) & MatchNumericImmediate(Start)).capture(Initialization) ~
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesX)).*.capture(Body) ~ ((Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesX)).*.capture(Body) ~
(Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step) (Elidable & HasOpcodeIn(DEX, INX)).capture(Step)
).capture(BodyWithStep) ~ ).capture(BodyWithStep) ~
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~ (Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~ (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(LDX) & MatchNumericImmediate(Start)).capture(Initialization) ~
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
((Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step) ~ ((Elidable & HasOpcodeIn(DEX, INX)).capture(Step) ~
(Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL, BNE, CPX, TXA))) & Not(ChangesX)).*.capture(Body) (Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL, BNE, CPX, TXA)) & Not(ChangesX)).*.capture(Body)
).capture(BodyWithStep) ~ ).capture(BodyWithStep) ~
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End) | Elidable & HasOpcode(TXA)) ~ (Elidable & HasOpcode(CPX) & MatchNumericImmediate(End) | Elidable & HasOpcode(TXA)) ~
(Elidable & HasOpcode(BNE) & MatchParameter(Back)) ~ (Elidable & HasOpcode(BNE) & MatchParameter(Back)) ~
@ -128,8 +128,8 @@ object LoopUnrolling {
(Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start) & Not(HasImmediate(0))).capture(Initialization) ~ (Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start) & Not(HasImmediate(0))).capture(Initialization) ~
(Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~ (Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesY)).*.capture(Body) ~ ((Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesY)).*.capture(Body) ~
(Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step) (Elidable & HasOpcodeIn(DEY, INY)).capture(Step)
).capture(BodyWithStep) ~ ).capture(BodyWithStep) ~
(Elidable & HasOpcode(CPY) & MatchNumericImmediate(End)).? ~ (Elidable & HasOpcode(CPY) & MatchNumericImmediate(End)).? ~
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~ (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(LDY) & MatchNumericImmediate(Start)).capture(Initialization) ~
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesY)).*.capture(Body) ~ ((Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesY)).*.capture(Body) ~
(Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step) (Elidable & HasOpcodeIn(DEY, INY)).capture(Step)
).capture(BodyWithStep) ~ ).capture(BodyWithStep) ~
(Elidable & HasOpcode(CPY) & MatchNumericImmediate(End)).? ~ (Elidable & HasOpcode(CPY) & MatchNumericImmediate(End)).? ~
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~ (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(LDY) & MatchNumericImmediate(Start)).capture(Initialization) ~
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
((Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step) ~ ((Elidable & HasOpcodeIn(DEY, INY)).capture(Step) ~
(Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL, BNE, CPY, TYA))) & Not(ChangesY)).*.capture(Body) (Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL, BNE, CPY, TYA)) & Not(ChangesY)).*.capture(Body)
).capture(BodyWithStep) ~ ).capture(BodyWithStep) ~
(Elidable & HasOpcode(CPY) & MatchNumericImmediate(End) | Elidable & HasOpcode(TYA)) ~ (Elidable & HasOpcode(CPY) & MatchNumericImmediate(End) | Elidable & HasOpcode(TYA)) ~
(Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~ (Elidable & HasOpcode(BNE) & MatchParameter(Back) & DoesntMatterWhatItDoesWith(State.C, State.N, State.Z)) ~

View File

@ -118,7 +118,8 @@ object ReverseFlowAnalyzer {
r0 = Important, r1 = Important) r0 = Important, r1 = Important)
//noinspection RedundantNewCaseClass //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 importanceArray = Array.fill[CpuImportance](code.length)(new CpuImportance())
val codeArray = code.toArray val codeArray = code.toArray
@ -130,6 +131,7 @@ object ReverseFlowAnalyzer {
for (i <- codeArray.indices.reverse) { for (i <- codeArray.indices.reverse) {
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import AddrMode._ import AddrMode._
import millfork.node.MosNiceFunctionProperty._
if (importanceArray(i) != currentImportance) { if (importanceArray(i) != currentImportance) {
changed = true changed = true
importanceArray(i) = currentImportance importanceArray(i) = currentImportance
@ -173,10 +175,27 @@ object ReverseFlowAnalyzer {
result = result.copy(a = Important) result = result.copy(a = Important)
case _ => case _ =>
} }
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput(fun.name)) { currentImportance = result.copy(
result = result.copy(r0 = Important, r1 = Important) 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,
currentImportance = result 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, _), _) => case AssemblyLine(ANC, _, NumericConstant(0, _), _) =>
currentImportance = currentImportance.copy(c = Unimportant, n = Unimportant, z = Unimportant, a = Unimportant) currentImportance = currentImportance.copy(c = Unimportant, n = Unimportant, z = Unimportant, a = Unimportant)

View File

@ -174,6 +174,10 @@ object ReverseFlowAnalyzerPerImpiedOpcode {
SED -> (_.copy(d = Unimportant)), SED -> (_.copy(d = Unimportant)),
CLV -> (_.copy(v = 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) def hasDefinition(opcode: Opcode.Value): Boolean = map.contains(opcode)

View File

@ -1,11 +1,12 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions} import millfork.CompilationOptions
import millfork.assembly._ import millfork.assembly._
import millfork.assembly.mos._ import millfork.assembly.mos._
import millfork.assembly.opt.SingleStatus import millfork.assembly.opt.SingleStatus
import millfork.env._ import millfork.env._
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
import millfork.node.{MosNiceFunctionProperty, NiceFunctionProperty}
import scala.collection.mutable import scala.collection.mutable
@ -32,18 +33,18 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
rules.foreach(_.pattern.validate(needsFlowInfo)) 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 effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
val taggedCode = FlowAnalyzer.analyze(f, effectiveCode, options, needsFlowInfo) val taggedCode = FlowAnalyzer.analyze(f, effectiveCode, optimizationContext, needsFlowInfo)
optimizeImpl(f, taggedCode, options, labelMap) 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 { code match {
case Nil => Nil case Nil => Nil
case head :: tail => case head :: tail =>
for ((rule, index) <- rules.zipWithIndex) { 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 { rule.pattern.matchTo(ctx, code) match {
case Some(rest: List[(FlowInfo, AssemblyLine)]) => case Some(rest: List[(FlowInfo, AssemblyLine)]) =>
val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2) 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(" ↓") ErrorReporting.trace(" ↓")
optimizedChunk.filter(_.isPrintable).foreach(l => ErrorReporting.trace(l.toString)) optimizedChunk.filter(_.isPrintable).foreach(l => ErrorReporting.trace(l.toString))
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) { if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return optimizedChunk ++ optimizeImpl(f, rest, options, labelMap) return optimizedChunk ++ optimizeImpl(f, rest, optimizationContext)
} else { } else {
return optimize(f, optimizedChunk ++ rest.map(_._2), options) return optimize(f, optimizedChunk ++ rest.map(_._2), optimizationContext)
} }
case None => () 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]() private val map = mutable.Map[Int, Any]()
override def toString: String = map.mkString(", ") 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)]] = { def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
if (predicate(ctx)) Some(code) else None if (predicate(ctx)) Some(code) else None
} }
@ -442,11 +464,12 @@ trait AssemblyLinePattern extends AssemblyPattern {
def &(x: AssemblyLinePattern): AssemblyLinePattern = Both(this, x) def &(x: AssemblyLinePattern): AssemblyLinePattern = Both(this, x)
} }
//noinspection ScalaUnnecessaryParentheses
trait TrivialAssemblyLinePattern extends AssemblyLinePattern with (AssemblyLine => Boolean) { trait TrivialAssemblyLinePattern extends AssemblyLinePattern with (AssemblyLine => Boolean) {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = this (line) 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 matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = predicate(ctx)
override def toString: String = "Match(...)" 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 { case class MatchA(i: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo) FlowInfoRequirement.assertForward(needsFlowInfo)
@ -777,14 +795,108 @@ case object ConcernsIZ extends TrivialAssemblyLinePattern {
OpcodeClasses.ConcernsIZAlways(line.opcode) || IZAddrModes(line.addrMode) OpcodeClasses.ConcernsIZAlways(line.opcode) || IZAddrModes(line.addrMode)
} }
case object ChangesA extends TrivialAssemblyLinePattern { case object ChangesA extends AssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
OpcodeClasses.ChangesAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ChangesAIfImplied(line.opcode) 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 { case object ChangesX extends AssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
OpcodeClasses.ChangesAHAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ChangesAHIfImplied(line.opcode) 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 { case object ChangesM extends TrivialAssemblyLinePattern {
@ -801,34 +913,57 @@ case object ChangesW extends TrivialAssemblyLinePattern {
case _ => false case _ => false
} }
} }
case object ChangesMemory extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = case object ChangesMemory extends AssemblyLinePattern {
OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode) 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 { case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int, opcode: Opcode.Value = Opcode.NOP) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = { override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
val p1 = ctx.get[Constant](param1) import AddrMode._
val a1 = ctx.get[AddrMode.Value](addrMode1) import Opcode._
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode) line match {
// TODO: NOP case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => !ctx.functionChangesMemory(th.name)
// this will break if the actual instruction was 16-bit case _ =>
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(opcode, a1, p1), line) 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 { case object ConcernsMemory extends AssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ReadsMemory(line) || ChangesMemory(line) ReadsMemory.matchLineTo(ctx, flowInfo, line) || ChangesMemory.matchLineTo(ctx, flowInfo, line)
} }
case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern { case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = { override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
val p1 = ctx.get[Constant](param1) {
val a1 = ctx.get[AddrMode.Value](addrMode1) import AddrMode._
// TODO: NOP import Opcode._
// this will break if the actual instruction was 16-bit line match {
HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(Opcode.NOP, a1, p1), line) 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 ¬{", ",", "})") 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 = override def apply(line: AssemblyLine): Boolean =
ops(line.opcode) ops(line.opcode)
override def toString: String = ops.mkString("{", ",", "}") 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 { case class HasAddrMode(am: AddrMode.Value) extends TrivialAssemblyLinePattern {
@ -950,6 +1094,13 @@ case class HasAddrModeIn(ams: Set[AddrMode.Value]) extends TrivialAssemblyLinePa
ams(line.addrMode) ams(line.addrMode)
override def toString: String = ams.mkString("{", ",", "}") 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 { 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 { case class DoesntChangeIndexingInAddrMode(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ctx.get[AddrMode.Value](i) match { ctx.get[AddrMode.Value](i) match {
case AddrMode.ZeroPageX | AddrMode.AbsoluteX | AddrMode.LongAbsoluteX | AddrMode.IndexedX | AddrMode.AbsoluteIndexedX => !OpcodeClasses.ChangesX.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 => !OpcodeClasses.ChangesY.contains(line.opcode) case AddrMode.ZeroPageY | AddrMode.AbsoluteY | AddrMode.IndexedY | AddrMode.LongIndexedY => !ChangesY.matchLineTo(ctx, flowInfo, line)
case AddrMode.IndexedZ | AddrMode.LongIndexedZ => !OpcodeClasses.ChangesIZ.contains(line.opcode) case AddrMode.IndexedZ | AddrMode.LongIndexedZ => !ChangesIZ.matchLineTo(ctx, flowInfo, line)
case AddrMode.Stack => !OpcodeClasses.ChangesS.contains(line.opcode) 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 case _ => true
} }

View File

@ -33,7 +33,7 @@ object SingleAssignmentVariableOptimization extends AssemblyOptimization[Assembl
override def name = "Single assignment variable optimization" 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 { val paramVariables = f.params match {
case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 => case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>
Set[String]() Set[String]()
@ -65,7 +65,7 @@ object SingleAssignmentVariableOptimization extends AssemblyOptimization[Assembl
val slice = code.slice(lifetime.start, lifetime.end) 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))) slice.forall(l => GoodOpcodes.contains(l.opcode) || !BadOpcodes.contains(l.opcode) && source.forall(s => HelperCheckers.memoryAccessDoesntOverlap(s,l)))
}.flatMap{case (v,source) => }.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 if (finalGoodVariables.isEmpty) return code

View File

@ -33,52 +33,52 @@ object SixteenOptimizations {
val RepSepWeakening = new RuleBasedAssemblyOptimization("REP/SEP weakening", val RepSepWeakening = new RuleBasedAssemblyOptimization("REP/SEP weakening",
needsFlowInfo = FlowInfoRequirement.BothFlows, needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcodeIn(Set(SEP, REP)) & HasImmediate(0)) ~~> (_ => Nil), (Elidable & HasOpcodeIn(SEP, REP) & HasImmediate(0)) ~~> (_ => Nil),
(HasOpcode(SEP) & HasImmediate(0x20)) ~ (HasOpcode(SEP) & HasImmediate(0x20)) ~
(Linear & Not(HasOpcodeIn(Set(SEP, REP, PLP)))).* ~ (Linear & Not(HasOpcodeIn(SEP, REP, PLP))).* ~
(Elidable & HasOpcode(SEP) & HasImmediate(0x20)) ~~> (_.init), (Elidable & HasOpcode(SEP) & HasImmediate(0x20)) ~~> (_.init),
(HasOpcode(REP) & HasImmediate(0x20)) ~ (HasOpcode(REP) & HasImmediate(0x20)) ~
(Linear & Not(HasOpcodeIn(Set(SEP, REP, PLP)))).* ~ (Linear & Not(HasOpcodeIn(SEP, REP, PLP))).* ~
(Elidable & HasOpcode(REP) & HasImmediate(0x20)) ~~> (_.init), (Elidable & HasOpcode(REP) & HasImmediate(0x20)) ~~> (_.init),
(HasOpcode(SEP) & HasImmediate(0x10)) ~ (HasOpcode(SEP) & HasImmediate(0x10)) ~
(Linear & Not(HasOpcodeIn(Set(SEP, REP, PLP)))).* ~ (Linear & Not(HasOpcodeIn(SEP, REP, PLP))).* ~
(Elidable & HasOpcode(SEP) & HasImmediate(0x10)) ~~> (_.init), (Elidable & HasOpcode(SEP) & HasImmediate(0x10)) ~~> (_.init),
(HasOpcode(REP) & HasImmediate(0x10)) ~ (HasOpcode(REP) & HasImmediate(0x10)) ~
(Linear & Not(HasOpcodeIn(Set(SEP, REP, PLP)))).* ~ (Linear & Not(HasOpcodeIn(SEP, REP, PLP))).* ~
(Elidable & HasOpcode(REP) & HasImmediate(0x10)) ~~> (_.init), (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) => Where(c => c.get[Int](0).&(0x1).!=(0)) ~~> { (code, ctx) =>
val i = ctx.get[Int](0) & 0xFE val i = ctx.get[Int](0) & 0xFE
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i)) 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) => Where(c => c.get[Int](0).&(0x2).!=(0)) ~~> { (code, ctx) =>
val i = ctx.get[Int](0) & 0xFD val i = ctx.get[Int](0) & 0xFD
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i)) 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) => Where(c => c.get[Int](0).&(0x8).!=(0)) ~~> { (code, ctx) =>
val i = ctx.get[Int](0) & 0xF7 val i = ctx.get[Int](0) & 0xF7
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i)) 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) => Where(c => c.get[Int](0).&(0x10).!=(0)) ~~> { (code, ctx) =>
val i = ctx.get[Int](0) & 0xEF val i = ctx.get[Int](0) & 0xEF
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i)) 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) => Where(c => c.get[Int](0).&(0x20).!=(0)) ~~> { (code, ctx) =>
val i = ctx.get[Int](0) & 0xDF val i = ctx.get[Int](0) & 0xDF
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i)) 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) => Where(c => c.get[Int](0).&(0x40).!=(0)) ~~> { (code, ctx) =>
val i = ctx.get[Int](0) & 0xBF val i = ctx.get[Int](0) & 0xBF
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i)) 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) => Where(c => c.get[Int](0).&(0x80).!=(0)) ~~> { (code, ctx) =>
val i = ctx.get[Int](0) & 0x7F val i = ctx.get[Int](0) & 0x7F
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i)) 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", val PointlessLoadAfterLoadOrStore = new RuleBasedAssemblyOptimization("Pointless 16-bit load after load or store",
needsFlowInfo = FlowInfoRequirement.NoRequirement, 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)).* ~ (Linear & Not(ChangesA) & Not(ChangesAH)).* ~
(Elidable & HasOpcode(LDA_W) & HasAddrMode(WordImmediate) & MatchParameter(1)) ~~> (_.init), (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)).* ~ (Linear & Not(ChangesA) & Not(ChangesAH) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1, LDA_W)).* ~
(Elidable & HasOpcode(LDA_W) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.init), (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))), (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, ADC, AND, EOR, ORA, LDA, STA, SBC, CMP,
) )

View File

@ -1,7 +1,7 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions, OptimizationPresets} import millfork.{CompilationFlag, CompilationOptions, OptimizationPresets}
import millfork.assembly.AssemblyOptimization import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode} import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
import millfork.env.NormalFunction import millfork.env.NormalFunction
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
@ -13,7 +13,8 @@ import scala.collection.mutable
*/ */
object SuperOptimizer extends AssemblyOptimization[AssemblyLine] { 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 val oldVerbosity = ErrorReporting.verbosity
ErrorReporting.verbosity = -1 ErrorReporting.verbosity = -1
var allOptimizers = OptimizationPresets.Good ++ LaterOptimizations.All var allOptimizers = OptimizationPresets.Good ++ LaterOptimizations.All
@ -55,6 +56,7 @@ object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
AlwaysGoodOptimizations.UnusedCodeRemoval AlwaysGoodOptimizations.UnusedCodeRemoval
) )
val optionsForMeasurements = options.copy(commandLineFlags = options.commandLineFlags + (CompilationFlag.InternalCurrentlyOptimizingForMeasurement -> true)) 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)) val quicklyCleanedCode = quickScrub.foldLeft(code)((c, o) => o.optimize(m, c, optionsForMeasurements))
seenSoFar += viewCode(quicklyCleanedCode) seenSoFar += viewCode(quicklyCleanedCode)
queue.enqueue(quickScrub.reverse -> quicklyCleanedCode) queue.enqueue(quickScrub.reverse -> quicklyCleanedCode)
@ -64,7 +66,7 @@ object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
var isLeaf = true var isLeaf = true
(if (optionsForMeasurements.flag(CompilationFlag.SingleThreaded)) allOptimizers (if (optionsForMeasurements.flag(CompilationFlag.SingleThreaded)) allOptimizers
else allOptimizers.par).foreach { o => else allOptimizers.par).foreach { o =>
val optimized = o.optimize(m, codeSoFar, optionsForMeasurements) val optimized = o.optimize(m, codeSoFar, optimizationContextForMeasurements)
val view = viewCode(optimized) val view = viewCode(optimized)
seenSoFar.synchronized{ seenSoFar.synchronized{
if (!seenSoFar(view)) { if (!seenSoFar(view)) {

View File

@ -1,12 +1,13 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions, NonOverlappingIntervals} import millfork.{CompilationFlag, NonOverlappingIntervals}
import millfork.assembly.AssemblyOptimization import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.mos._ import millfork.assembly.mos._
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import AddrMode._ import AddrMode._
import millfork.env._ import millfork.env._
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
import millfork.node.MosNiceFunctionProperty
import scala.collection.mutable.ListBuffer 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) def +(that: CyclesAndBytes) = CyclesAndBytes(this.bytes + that.bytes, this.cycles + that.cycles)
} }
case class Features( case class FeaturesForIndexRegisters(
blastProcessing: Boolean, blastProcessing: Boolean,
izIsAlwaysZero: Boolean, izIsAlwaysZero: Boolean,
indexRegisterTransfers: Boolean, indexRegisterTransfers: Boolean,
functionsSafeForX: Set[String],
functionsSafeForY: Set[String],
functionsSafeForZ: Set[String],
identityArray: Constant) identityArray: Constant)
case class FeaturesForAccumulator(
cmos: Boolean,
safeFunctions: Set[String])
// If any of these opcodes is present within a method, // If any of these opcodes is present within a method,
// then it's too hard to assign any variable to a register. // then it's too hard to assign any variable to a register.
private val opcodesThatAlwaysPrecludeXAllocation = Set( private val opcodesThatAlwaysPrecludeXAllocation = Set(
@ -103,7 +111,8 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
override def name = "Allocating variables to index registers" 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 { val paramVariables = f.params match {
case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 => case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>
Set[String]() Set[String]()
@ -138,23 +147,30 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
val removeVariablesForReal = !options.flag(CompilationFlag.InternalCurrentlyOptimizingForMeasurement) val removeVariablesForReal = !options.flag(CompilationFlag.InternalCurrentlyOptimizingForMeasurement)
val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes
val importances = ReverseFlowAnalyzer.analyze(f, code) val importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed) val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed)
val identityArray = f.environment.maybeGet[ThingInMemory]("identity$").map(MemoryAddressConstant).getOrElse(Constant.Zero) val identityArray = f.environment.maybeGet[ThingInMemory]("identity$").map(MemoryAddressConstant).getOrElse(Constant.Zero)
val izIsAlwaysZero = !options.flag(CompilationFlag.Emit65CE02Opcodes) val izIsAlwaysZero = !options.flag(CompilationFlag.Emit65CE02Opcodes)
val features = Features( val featuresForIndices = FeaturesForIndexRegisters(
blastProcessing = blastProcessing, blastProcessing = blastProcessing,
izIsAlwaysZero = izIsAlwaysZero, izIsAlwaysZero = izIsAlwaysZero,
indexRegisterTransfers = options.flag(CompilationFlag.EmitEmulation65816Opcodes), 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 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 { val xCandidates = variablesWithLifetimes.filter {
case (vName, range) => case (vName, range) =>
importances(range.start).x != Important importances(range.start).x != Important
}.flatMap { }.flatMap {
case (vName, range) => 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) (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 importances(range.start).y != Important
}.flatMap { }.flatMap {
case (vName, range) => 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) (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 importances(range.start).iz != Important
}.flatMap { }.flatMap {
case (vName, range) => 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) (vName, range, if (variablesWithRegisterHint(vName)) score + CyclesAndBytes(16, 16) else score)
} }
} }
@ -185,7 +201,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
}.flatMap { }.flatMap {
case (vName, range) => case (vName, range) =>
canBeInlinedToAccumulator( canBeInlinedToAccumulator(
options, featuresForAcc,
start = true, start = true,
synced = false, synced = false,
vName, vName,
@ -251,7 +267,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
case (v, range, _) => case (v, range, _) =>
ErrorReporting.debug(s"Inlining $v to register X") ErrorReporting.debug(s"Inlining $v to register X")
val oldCode = code.zip(importances).slice(range.start, range.end) 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) reportOptimizedBlock(oldCode, newCode)
output ++= newCode output ++= newCode
i = range.end i = range.end
@ -265,7 +281,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
case (v, range, _) => case (v, range, _) =>
ErrorReporting.debug(s"Inlining $v to register Y") ErrorReporting.debug(s"Inlining $v to register Y")
val oldCode = code.zip(importances).slice(range.start, range.end) 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) reportOptimizedBlock(oldCode, newCode)
output ++= newCode output ++= newCode
i = range.end i = range.end
@ -280,7 +296,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
case (v, range, _) => case (v, range, _) =>
ErrorReporting.debug(s"Inlining $v to register Z") ErrorReporting.debug(s"Inlining $v to register Z")
val oldCode = code.zip(importances).slice(range.start, range.end) 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) reportOptimizedBlock(oldCode, newCode)
output ++= newCode output ++= newCode
i = range.end i = range.end
@ -295,7 +311,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
case (v, range, _) => case (v, range, _) =>
ErrorReporting.debug(s"Inlining $v to register A") ErrorReporting.debug(s"Inlining $v to register A")
val oldCode = code.zip(importances).slice(range.start, range.end) 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) reportOptimizedBlock(oldCode, newCode)
output ++= newCode output ++= newCode
i = range.end i = range.end
@ -321,7 +337,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
} }
// TODO: STA has different flag behaviour than TAX, keep it in mind! // 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 vx = xCandidate.getOrElse("-")
val vy = yCandidate.getOrElse("-") val vy = yCandidate.getOrElse("-")
val vz = zCandidate.getOrElse("-") val vz = zCandidate.getOrElse("-")
@ -369,17 +385,17 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
} }
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs 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 // if a variable is used by some opcodes, then it cannot be assigned to a register
None None
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs 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 // if a variable is used by some opcodes, then it cannot be assigned to a register
None None
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs 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 // if a variable is used by some opcodes, then it cannot be assigned to a register
None None
@ -547,6 +563,14 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
// labels always end the initial section // labels always end the initial section
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs) 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 => case (x, _) :: xs =>
if (xCandidate.isDefined && opcodesThatAlwaysPrecludeXAllocation(x.opcode)) { if (xCandidate.isDefined && opcodesThatAlwaysPrecludeXAllocation(x.opcode)) {
None 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] = { def canBeInlinedToAccumulator(features: FeaturesForAccumulator, start: Boolean, synced: Boolean, candidate: String, lines: List[(AssemblyLine, CpuImportance)]): Option[CyclesAndBytes] = {
val cmos = options.flags(CompilationFlag.EmitCmosOpcodes)
lines match { lines match {
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true),_) :: xs case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true),_) :: xs
if th.name == candidate && start || synced => 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) => case (AssemblyLine(op, _, _, _),_) :: xs if opcodesThatAlwaysPrecludeAAllocation(op) =>
None None
@ -589,35 +612,35 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
None None
case (AssemblyLine(SEP | REP, Immediate, NumericConstant(nn, _), _), _) :: xs => 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 else None
case (AssemblyLine(SEP | REP, _, _, _), _) :: xs => None case (AssemblyLine(SEP | REP, _, _, _), _) :: xs => None
case (AssemblyLine(STA, _, MemoryAddressConstant(th), elidable) ,_):: xs if th.name == candidate => case (AssemblyLine(STA, _, MemoryAddressConstant(th), elidable) ,_):: xs if th.name == candidate =>
if (synced && elidable) { 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 { } else {
None None
} }
case (AssemblyLine(DCP, Absolute | ZeroPage, MemoryAddressConstant(th), _) ,_):: xs if th.name == candidate => case (AssemblyLine(DCP, Absolute | ZeroPage, MemoryAddressConstant(th), _) ,_):: xs if th.name == candidate =>
if (synced) { if (synced) {
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
} else { } else {
None None
} }
case (AssemblyLine(STA | SAX, _, MemoryAddressConstant(th), elidable) ,_):: xs if th.name != candidate => case (AssemblyLine(STA | SAX, _, MemoryAddressConstant(th), elidable) ,_):: xs if th.name != candidate =>
if (synced) { if (synced) {
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
} else { } else {
None None
} }
case (AssemblyLine(STA | SAX, _, NumericConstant(_, _), _) ,_):: xs => case (AssemblyLine(STA | SAX, _, NumericConstant(_, _), _) ,_):: xs =>
if (synced) { if (synced) {
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
} else { } else {
None None
} }
@ -629,7 +652,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
case (AssemblyLine(TAX | TAY, _, _, _),_) :: xs => case (AssemblyLine(TAX | TAY, _, _, _),_) :: xs =>
if (synced) { if (synced) {
canBeInlinedToAccumulator(options, start = false, synced = true, candidate, xs) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
} else { } else {
None None
} }
@ -638,30 +661,30 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
if th.name == candidate => if th.name == candidate =>
// removing LDA saves 3 bytes // removing LDA saves 3 bytes
if (imp.z == Unimportant && imp.n == Unimportant) { 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 { } 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 case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
if opcodesCommutative(op) => if opcodesCommutative(op) =>
if (th.name == candidate) { 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 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 case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(CLC, _, _, _),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
if opcodesCommutative(op) => if opcodesCommutative(op) =>
if (th.name == candidate) { 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 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 case (AssemblyLine(LDX | LDY | LAX, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
if th.name == candidate => if th.name == candidate =>
// converting a load into a transfer saves 2 bytes // converting a load into a transfer saves 2 bytes
if (elidable) { 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 { } else {
None None
} }
@ -673,15 +696,15 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
case (AssemblyLine(ASL | LSR | ROR | ROL, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs case (AssemblyLine(ASL | LSR | ROR | ROL, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
if th.name == candidate => if th.name == candidate =>
if (elidable) { 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 { } else {
None None
} }
case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
if th.name == candidate => if th.name == candidate =>
if (cmos && elidable) { if (features.cmos && 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 { } else {
None None
} }
@ -689,12 +712,16 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
case (AssemblyLine(TXA | TYA, _, _, elidable), imp) :: xs => case (AssemblyLine(TXA | TYA, _, _, elidable), imp) :: xs =>
if (imp.a == Unimportant && imp.c == Unimportant && imp.v == Unimportant && elidable) { if (imp.a == Unimportant && imp.c == Unimportant && imp.v == Unimportant && elidable) {
// TYA/TXA has to be converted to CPY#0/CPX#0 // 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 { } else {
None 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) case Nil => Some(CyclesAndBytes.Zero)
} }
@ -706,7 +733,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
case _ => true 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 vx = xCandidate.getOrElse("-")
val vy = yCandidate.getOrElse("-") val vy = yCandidate.getOrElse("-")
val vz = zCandidate.getOrElse("-") val vz = zCandidate.getOrElse("-")

View File

@ -124,6 +124,18 @@ object Status {
case SingleStatus(v) => v.iz case SingleStatus(v) => v.iz
case _ => false 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 { implicit class IntStatusOps(val inner: Status[Int]) extends AnyVal {

View File

@ -1,6 +1,7 @@
package millfork.compiler package millfork.compiler
import millfork.env.{Environment, Label, NormalFunction} import millfork.env.{Environment, Label, NormalFunction}
import millfork.node.NiceFunctionProperty
import millfork.{CompilationFlag, CompilationOptions} import millfork.{CompilationFlag, CompilationOptions}
/** /**
@ -10,6 +11,7 @@ case class CompilationContext(env: Environment,
function: NormalFunction, function: NormalFunction,
extraStackOffset: Int, extraStackOffset: Int,
options: CompilationOptions, options: CompilationOptions,
niceFunctionProperties: Set[(NiceFunctionProperty, String)],
breakLabels: Map[String, Label] = Map(), breakLabels: Map[String, Label] = Map(),
continueLabels: Map[String, Label] = Map()){ continueLabels: Map[String, Label] = Map()){
def withInlinedEnv(environment: Environment, newLabel: String): CompilationContext = { def withInlinedEnv(environment: Environment, newLabel: String): CompilationContext = {

View File

@ -64,6 +64,24 @@ case class HalfWordExpression(expression: Expression, hiByte: Boolean) extends E
HalfWordExpression(expression.replaceVariable(variable, actualParam), hiByte) 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 { object MosRegister extends Enumeration {
val A, X, Y, AX, AY, YA, XA, XY, YX, AW = Value 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 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 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 case A | B | C | D | E | H | L | IXH | IXL | IYH | IYL | R | I | IMM_8 => 1
} }

View File

@ -4,7 +4,7 @@ import millfork.assembly._
import millfork.compiler.{AbstractCompiler, CompilationContext} import millfork.compiler.{AbstractCompiler, CompilationContext}
import millfork.env._ import millfork.env._
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
import millfork.node.{CallGraph, Program} import millfork.node.{CallGraph, NiceFunctionProperty, Program}
import millfork._ import millfork._
import scala.collection.mutable import scala.collection.mutable
@ -97,7 +97,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
??? ???
} }
} catch { } catch {
case e: StackOverflowError => case _: StackOverflowError =>
ErrorReporting.fatal("Stack overflow " + c) ErrorReporting.fatal("Stack overflow " + c)
} }
case UnexpandedConstant(name, _) => case UnexpandedConstant(name, _) =>
@ -200,9 +200,10 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
var inlinedFunctions = Map[String, List[T]]() var inlinedFunctions = Map[String, List[T]]()
val compiledFunctions = mutable.Map[String, List[T]]() val compiledFunctions = mutable.Map[String, List[T]]()
val recommendedCompilationOrder = callGraph.recommendedCompilationOrder val recommendedCompilationOrder = callGraph.recommendedCompilationOrder
val niceFunctionProperties = mutable.Set[(NiceFunctionProperty, String)]()
recommendedCompilationOrder.foreach { f => recommendedCompilationOrder.foreach { f =>
env.maybeGet[NormalFunction](f).foreach { function => 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 { val strippedCodeForInlining = for {
limit <- potentiallyInlineable.get(f) limit <- potentiallyInlineable.get(f)
if code.map(_.sizeInBytes).sum <= limit if code.map(_.sizeInBytes).sum <= limit
@ -217,10 +218,18 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
nonInlineableFunctions += function.name nonInlineableFunctions += function.name
compiledFunctions(f) = code compiledFunctions(f) = code
optimizedCodeSize += code.map(_.sizeInBytes).sum optimizedCodeSize += code.map(_.sizeInBytes).sum
if (options.flag(CompilationFlag.InterproceduralOptimization)) {
gatherNiceFunctionProperties(niceFunctionProperties, f, code)
}
} }
function.environment.removedThings.foreach(env.removeVariable) 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) => rootEnv.things.foreach{case (name, thing) =>
if (!env.things.contains(name)) { 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] 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) ErrorReporting.debug("Compiling: " + f.name, f.position)
val unoptimized: List[T] = val unoptimized: List[T] =
injectLabels(labelMap, inliningCalculator.inline( 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, inlinedFunctions,
compiler)) compiler))
unoptimizedCodeSize += unoptimized.map(_.sizeInBytes).sum unoptimizedCodeSize += unoptimized.map(_.sizeInBytes).sum
val code = optimizations.foldLeft(unoptimized) { (c, opt) => 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) 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] 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 = { private def outputFunction(bank: String, code: List[T], startFrom: Int, assOut: mutable.ArrayBuffer[String], options: CompilationOptions): Int = {

View File

@ -4,11 +4,12 @@ import millfork.assembly.mos.opt.{HudsonOptimizations, JumpFixing, JumpShortenin
import millfork.assembly._ import millfork.assembly._
import millfork.env._ import millfork.env._
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
import millfork.node.Program import millfork.node.{MosNiceFunctionProperty, NiceFunctionProperty, Program}
import millfork._ import millfork._
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode} import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode, OpcodeClasses}
import millfork.compiler.mos.MosCompiler import millfork.compiler.mos.MosCompiler
import scala.annotation.tailrec
import scala.collection.mutable 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] = { override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[AssemblyLine]):List[AssemblyLine] = {
val optimizationContext = OptimizationContext(options, Map(), Set())
if (actuallyOptimize) { if (actuallyOptimize) {
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(code) else code val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(f, code, optimizationContext) else code
JumpShortening(f, JumpShortening(f, JumpFixing(f, finalCode, options), options), options) JumpShortening(f, JumpShortening(f, JumpFixing(f, finalCode, options), optimizationContext), optimizationContext)
} }
else JumpFixing(f, code, options) else JumpFixing(f, code, options)
} }
@ -34,8 +36,8 @@ class MosAssembler(program: Program,
case AssemblyLine(BYTE, RawByte, c, _) => case AssemblyLine(BYTE, RawByte, c, _) =>
writeByte(bank, index, c) writeByte(bank, index, c)
index + 1 index + 1
case AssemblyLine(BYTE, _, _, _) => ??? case AssemblyLine(BYTE, _, _, _) => ErrorReporting.fatal("BYTE opcode failure")
case AssemblyLine(_, RawByte, _, _) => ??? case AssemblyLine(_, RawByte, _, _) => ErrorReporting.fatal("BYTE opcode failure")
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(labelName)), _) => case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(labelName)), _) =>
labelMap(labelName) = index labelMap(labelName) = index
index index
@ -87,18 +89,93 @@ class MosAssembler(program: Program,
case l => l 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 { object MosAssembler {
val opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]() private val opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
val illegalOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]() private val illegalOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
val cmosOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]() private val cmosOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
val cmosNopOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]() private val cmosNopOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
val ce02Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]() private val ce02Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
val hudsonOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]() private val hudsonOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
val emulation65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]() private val emulation65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
val native65816Opcodes = 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 = { def opcodeFor(opcode: Opcode.Value, addrMode: AddrMode.Value, options: CompilationOptions): Byte = {
val key = opcode -> addrMode val key = opcode -> addrMode

View File

@ -4,7 +4,9 @@ import millfork.{CompilationOptions, Platform}
import millfork.assembly.z80.ZLine import millfork.assembly.z80.ZLine
import millfork.compiler.z80.Z80Compiler import millfork.compiler.z80.Z80Compiler
import millfork.env.{Environment, NormalFunction} import millfork.env.{Environment, NormalFunction}
import millfork.node.Program import millfork.node.{NiceFunctionProperty, Program}
import scala.collection.mutable
/** /**
* @author Karol Stasiak * @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 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 { object Z80Assembler {

View File

@ -102,6 +102,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
val options = CompilationOptions(platform, Map( val options = CompilationOptions(platform, Map(
CompilationFlag.EmitIllegals -> this.emitIllegals, CompilationFlag.EmitIllegals -> this.emitIllegals,
CompilationFlag.InlineFunctions -> this.inline, CompilationFlag.InlineFunctions -> this.inline,
CompilationFlag.InterproceduralOptimization -> true,
CompilationFlag.CompactReturnDispatchParams -> true, CompilationFlag.CompactReturnDispatchParams -> true,
CompilationFlag.ZeropagePseudoregister -> true, CompilationFlag.ZeropagePseudoregister -> true,
CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu), CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
@ -136,7 +137,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
// print unoptimized asm // print unoptimized asm
env.allPreallocatables.foreach { env.allPreallocatables.foreach {
case f: NormalFunction => 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 unoptimizedSize += unoptimized.map(_.sizeInBytes).sum
case d: InitializedArray => case d: InitializedArray =>
unoptimizedSize += d.contents.length unoptimizedSize += d.contents.length

View File

@ -51,7 +51,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
// print unoptimized asm // print unoptimized asm
env.allPreallocatables.foreach { env.allPreallocatables.foreach {
case f: NormalFunction => 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 unoptimizedSize += unoptimized.map(_.sizeInBytes).sum
case d: InitializedArray => case d: InitializedArray =>
unoptimizedSize += d.contents.length unoptimizedSize += d.contents.length