mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-27 11:30:19 +00:00
More optimisations
This commit is contained in:
parent
92999ec490
commit
8b086f6c2f
@ -108,6 +108,7 @@ object OptimizationPresets {
|
|||||||
LaterOptimizations.UseYInsteadOfStack,
|
LaterOptimizations.UseYInsteadOfStack,
|
||||||
LaterOptimizations.IndexSwitchingOptimization,
|
LaterOptimizations.IndexSwitchingOptimization,
|
||||||
LaterOptimizations.LoadingBranchesOptimization,
|
LaterOptimizations.LoadingBranchesOptimization,
|
||||||
|
LaterOptimizations.IncreaseWithLimit,
|
||||||
)
|
)
|
||||||
|
|
||||||
val Good: List[AssemblyOptimization] = List[AssemblyOptimization](
|
val Good: List[AssemblyOptimization] = List[AssemblyOptimization](
|
||||||
@ -125,6 +126,7 @@ object OptimizationPresets {
|
|||||||
AlwaysGoodOptimizations.IndexSequenceOptimization,
|
AlwaysGoodOptimizations.IndexSequenceOptimization,
|
||||||
AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands,
|
AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands,
|
||||||
AlwaysGoodOptimizations.ModificationOfJustWrittenValue,
|
AlwaysGoodOptimizations.ModificationOfJustWrittenValue,
|
||||||
|
AlwaysGoodOptimizations.OperationsAroundShifting,
|
||||||
AlwaysGoodOptimizations.PoinlessFlagChange,
|
AlwaysGoodOptimizations.PoinlessFlagChange,
|
||||||
AlwaysGoodOptimizations.PointlessLoadAfterLoadOrStore,
|
AlwaysGoodOptimizations.PointlessLoadAfterLoadOrStore,
|
||||||
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
|
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
|
||||||
|
@ -14,6 +14,7 @@ import millfork.env._
|
|||||||
*
|
*
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
|
//noinspection ZeroIndexToHead
|
||||||
object AlwaysGoodOptimizations {
|
object AlwaysGoodOptimizations {
|
||||||
|
|
||||||
val counter = new AtomicInteger(30000)
|
val counter = new AtomicInteger(30000)
|
||||||
@ -851,4 +852,37 @@ object AlwaysGoodOptimizations {
|
|||||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil)
|
(Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val OperationsAroundShifting = new RuleBasedAssemblyOptimization("Operations around shifting",
|
||||||
|
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||||
|
(Elidable & HasOpcode(CLC)).? ~
|
||||||
|
(Elidable & HasOpcode(ADC) & HasClear(State.C) & HasClear(State.D) & MatchImmediate(1)) ~
|
||||||
|
HasOpcode(ASL).+.capture(2) ~
|
||||||
|
(Elidable & HasOpcode(CLC)) ~
|
||||||
|
(Elidable & HasOpcode(ADC) & HasClear(State.D) & MatchImmediate(3) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N)) ~~> {(code, ctx) =>
|
||||||
|
val shifts = ctx.get[List[AssemblyLine]](2)
|
||||||
|
val const = ctx.get[Constant](1).asl(shifts.length) + ctx.get[Constant](3)
|
||||||
|
shifts ++ List(AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, const))
|
||||||
|
},
|
||||||
|
(Elidable & HasOpcode(AND) & MatchImmediate(1)) ~
|
||||||
|
HasOpcode(ASL).+.capture(2) ~
|
||||||
|
(Elidable & HasOpcode(AND) & MatchImmediate(3) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N)) ~~> {(code, ctx) =>
|
||||||
|
val shifts = ctx.get[List[AssemblyLine]](2)
|
||||||
|
val const = CompoundConstant(MathOperator.And, ctx.get[Constant](1).asl(shifts.length), ctx.get[Constant](3)).quickSimplify
|
||||||
|
shifts :+ AssemblyLine.immediate(AND, const)
|
||||||
|
},
|
||||||
|
(Elidable & HasOpcode(EOR) & MatchImmediate(1)) ~
|
||||||
|
HasOpcode(ASL).+.capture(2) ~
|
||||||
|
(Elidable & HasOpcode(EOR) & MatchImmediate(3) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N)) ~~> {(code, ctx) =>
|
||||||
|
val shifts = ctx.get[List[AssemblyLine]](2)
|
||||||
|
val const = CompoundConstant(MathOperator.Exor, ctx.get[Constant](1).asl(shifts.length), ctx.get[Constant](3)).quickSimplify
|
||||||
|
shifts :+ AssemblyLine.immediate(EOR, const)
|
||||||
|
},
|
||||||
|
(Elidable & HasOpcode(ORA) & MatchImmediate(1)) ~
|
||||||
|
HasOpcode(ASL).+.capture(2) ~
|
||||||
|
(Elidable & HasOpcode(ORA) & MatchImmediate(3) & DoesntMatterWhatItDoesWith(State.C, State.Z, State.N)) ~~> {(code, ctx) =>
|
||||||
|
val shifts = ctx.get[List[AssemblyLine]](2)
|
||||||
|
val const = CompoundConstant(MathOperator.Or, ctx.get[Constant](1).asl(shifts.length), ctx.get[Constant](3)).quickSimplify
|
||||||
|
shifts :+ AssemblyLine.immediate(ORA, const)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import millfork.env.{Constant, NormalFunction, NumericConstant}
|
|||||||
*
|
*
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
|
//noinspection ZeroIndexToHead
|
||||||
object LaterOptimizations {
|
object LaterOptimizations {
|
||||||
|
|
||||||
|
|
||||||
@ -275,7 +276,7 @@ object LaterOptimizations {
|
|||||||
val LoadingBranchesOptimization = new RuleBasedAssemblyOptimization("Loading branches optimization",
|
val LoadingBranchesOptimization = new RuleBasedAssemblyOptimization("Loading branches optimization",
|
||||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||||
(Elidable & HasOpcode(LDA) & HasAddrModeIn(LdxAddrModes) & DoesntMatterWhatItDoesWith(State.X)) ~
|
(Elidable & HasOpcode(LDA) & HasAddrModeIn(LdxAddrModes) & DoesntMatterWhatItDoesWith(State.X)) ~
|
||||||
(Linear & Not(ConcernsX) & Not(ChangesA) & Not(HasOpcode(CMP)) & (Not(ReadsA) | Elidable & HasOpcode(STA) & HasAddrModeIn(StxAddrModes)) ).*.capture(39) ~
|
(Linear & Not(ConcernsX) & Not(ChangesA) & Not(HasOpcode(CMP)) & (Not(ReadsA) | Elidable & HasOpcode(STA) & HasAddrModeIn(StxAddrModes))).*.capture(39) ~
|
||||||
(Elidable & HasOpcode(CMP) & HasAddrModeIn(CpxyAddrModes)).?.capture(40) ~
|
(Elidable & HasOpcode(CMP) & HasAddrModeIn(CpxyAddrModes)).?.capture(40) ~
|
||||||
(Elidable & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~
|
(Elidable & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~
|
||||||
(Elidable & HasOpcode(LDA)).capture(31) ~
|
(Elidable & HasOpcode(LDA)).capture(31) ~
|
||||||
@ -284,7 +285,7 @@ object LaterOptimizations {
|
|||||||
(Elidable & HasOpcode(LDA)).capture(32) ~
|
(Elidable & HasOpcode(LDA)).capture(32) ~
|
||||||
(Elidable & HasOpcode(LABEL) & MatchParameter(21) & HasCallerCount(1) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)) ~~> { (code, ctx) =>
|
(Elidable & HasOpcode(LABEL) & MatchParameter(21) & HasCallerCount(1) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)) ~~> { (code, ctx) =>
|
||||||
val ldx = List(code.head.copy(opcode = LDX))
|
val ldx = List(code.head.copy(opcode = LDX))
|
||||||
val stx = ctx.get[List[AssemblyLine]](39).map(l => if (l.opcode == STA) l.copy(opcode = STX) else l )
|
val stx = ctx.get[List[AssemblyLine]](39).map(l => if (l.opcode == STA) l.copy(opcode = STX) else l)
|
||||||
val cpx = ctx.get[List[AssemblyLine]](40).map(_.copy(opcode = CPX))
|
val cpx = ctx.get[List[AssemblyLine]](40).map(_.copy(opcode = CPX))
|
||||||
val branch = ctx.get[List[AssemblyLine]](41)
|
val branch = ctx.get[List[AssemblyLine]](41)
|
||||||
val label = ctx.get[List[AssemblyLine]](42)
|
val label = ctx.get[List[AssemblyLine]](42)
|
||||||
@ -293,7 +294,7 @@ object LaterOptimizations {
|
|||||||
List(loadIfJumped, ldx, stx, cpx, branch, loadIfNotJumped, label).flatten
|
List(loadIfJumped, ldx, stx, cpx, branch, loadIfNotJumped, label).flatten
|
||||||
},
|
},
|
||||||
(Elidable & HasOpcode(LDA) & HasAddrModeIn(LdyAddrModes) & DoesntMatterWhatItDoesWith(State.Y)) ~
|
(Elidable & HasOpcode(LDA) & HasAddrModeIn(LdyAddrModes) & DoesntMatterWhatItDoesWith(State.Y)) ~
|
||||||
(Linear & Not(ConcernsY) & Not(ChangesA) & Not(HasOpcode(CMP)) & (Not(ReadsA) | Elidable & HasOpcode(STA) & HasAddrModeIn(StyAddrModes)) ).*.capture(39) ~
|
(Linear & Not(ConcernsY) & Not(ChangesA) & Not(HasOpcode(CMP)) & (Not(ReadsA) | Elidable & HasOpcode(STA) & HasAddrModeIn(StyAddrModes))).*.capture(39) ~
|
||||||
(Elidable & HasOpcode(CMP) & HasAddrModeIn(CpxyAddrModes)).?.capture(40) ~
|
(Elidable & HasOpcode(CMP) & HasAddrModeIn(CpxyAddrModes)).?.capture(40) ~
|
||||||
(Elidable & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~
|
(Elidable & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~
|
||||||
(Elidable & HasOpcode(LDA)).capture(31) ~
|
(Elidable & HasOpcode(LDA)).capture(31) ~
|
||||||
@ -302,7 +303,7 @@ object LaterOptimizations {
|
|||||||
(Elidable & HasOpcode(LDA)).capture(32) ~
|
(Elidable & HasOpcode(LDA)).capture(32) ~
|
||||||
(Elidable & HasOpcode(LABEL) & MatchParameter(21) & HasCallerCount(1) & DoesntMatterWhatItDoesWith(State.A, State.Y, State.N, State.Z)) ~~> { (code, ctx) =>
|
(Elidable & HasOpcode(LABEL) & MatchParameter(21) & HasCallerCount(1) & DoesntMatterWhatItDoesWith(State.A, State.Y, State.N, State.Z)) ~~> { (code, ctx) =>
|
||||||
val ldy = List(code.head.copy(opcode = LDY))
|
val ldy = List(code.head.copy(opcode = LDY))
|
||||||
val sty = ctx.get[List[AssemblyLine]](39).map(l => if (l.opcode == STA) l.copy(opcode = STY) else l )
|
val sty = ctx.get[List[AssemblyLine]](39).map(l => if (l.opcode == STA) l.copy(opcode = STY) else l)
|
||||||
val cpy = ctx.get[List[AssemblyLine]](40).map(_.copy(opcode = CPY))
|
val cpy = ctx.get[List[AssemblyLine]](40).map(_.copy(opcode = CPY))
|
||||||
val branch = ctx.get[List[AssemblyLine]](41)
|
val branch = ctx.get[List[AssemblyLine]](41)
|
||||||
val label = ctx.get[List[AssemblyLine]](42)
|
val label = ctx.get[List[AssemblyLine]](42)
|
||||||
@ -348,6 +349,42 @@ object LaterOptimizations {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val IncreaseWithLimit = new RuleBasedAssemblyOptimization("Increase with a limit",
|
||||||
|
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||||
|
(Elidable & HasOpcode(INC) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0)) ~
|
||||||
|
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0)) ~
|
||||||
|
(Elidable & HasOpcode(CMP) & HasAddrModeIn(CpxyAddrModes)) ~
|
||||||
|
(HasOpcode(BNE) & MatchParameter(14831)) ~
|
||||||
|
(Elidable & HasOpcode(LDA) & HasAddrModeIn(LdyAddrModes)) ~
|
||||||
|
(Elidable & HasOpcode(STA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0) & DoesntMatterWhatItDoesWith(State.A, State.Y)) ~
|
||||||
|
(HasOpcode(LABEL) & MatchParameter(14831)) ~~> { code =>
|
||||||
|
List(
|
||||||
|
code(1).copy(opcode = LDY),
|
||||||
|
AssemblyLine.implied(INY),
|
||||||
|
code(2).copy(opcode = CPY),
|
||||||
|
code(3),
|
||||||
|
code(4).copy(opcode = LDY),
|
||||||
|
code(6),
|
||||||
|
code(5).copy(opcode = STY))
|
||||||
|
},
|
||||||
|
(Elidable & HasOpcode(INC) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0)) ~
|
||||||
|
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0)) ~
|
||||||
|
(Elidable & HasOpcode(CMP) & HasAddrModeIn(CpxyAddrModes)) ~
|
||||||
|
(HasOpcode(BNE) & MatchParameter(14831)) ~
|
||||||
|
(Elidable & HasOpcode(LDA) & HasAddrModeIn(LdxAddrModes)) ~
|
||||||
|
(Elidable & HasOpcode(STA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0) & DoesntMatterWhatItDoesWith(State.A, State.X)) ~
|
||||||
|
(HasOpcode(LABEL) & MatchParameter(14831)) ~~> { code =>
|
||||||
|
List(
|
||||||
|
code(1).copy(opcode = LDX),
|
||||||
|
AssemblyLine.implied(INX),
|
||||||
|
code(2).copy(opcode = CPX),
|
||||||
|
code(3),
|
||||||
|
code(4).copy(opcode = LDX),
|
||||||
|
code(6),
|
||||||
|
code(5).copy(opcode = STX))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
val All = List(
|
val All = List(
|
||||||
DoubleLoadToDifferentRegisters,
|
DoubleLoadToDifferentRegisters,
|
||||||
DoubleLoadToTheSameRegister,
|
DoubleLoadToTheSameRegister,
|
||||||
|
6
src/main/scala/millfork/env/Constant.scala
vendored
6
src/main/scala/millfork/env/Constant.scala
vendored
@ -77,7 +77,11 @@ case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
|||||||
|
|
||||||
override def isLowestByteAlwaysEqual(i: Int) : Boolean = (value & 0xff) == (i&0xff)
|
override def isLowestByteAlwaysEqual(i: Int) : Boolean = (value & 0xff) == (i&0xff)
|
||||||
|
|
||||||
override def asl(i: Int) = NumericConstant(value << i, requiredSize + i / 8)
|
override def asl(i: Int): Constant = {
|
||||||
|
val newSize = requiredSize + i / 8
|
||||||
|
val mask = (1 << (8 * newSize)) - 1
|
||||||
|
NumericConstant((value << i) & mask, newSize)
|
||||||
|
}
|
||||||
|
|
||||||
override def +(that: Constant): Constant = that + value
|
override def +(that: Constant): Constant = that + value
|
||||||
|
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
package millfork.test
|
||||||
|
|
||||||
|
import millfork.assembly.opt.{AlwaysGoodOptimizations, LaterOptimizations, VariableToRegisterOptimization}
|
||||||
|
import millfork.test.emu.{EmuBenchmarkRun, EmuRun, EmuUltraBenchmarkRun}
|
||||||
|
import millfork.{Cpu, OptimizationPresets}
|
||||||
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karol Stasiak
|
||||||
|
*/
|
||||||
|
class SecondAssemblyOptimizationSuite extends FunSuite with Matchers {
|
||||||
|
|
||||||
|
test("Add-shift-add") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| void main () {
|
||||||
|
| byte a
|
||||||
|
| a = two()
|
||||||
|
| output = ((a + 3) << 2) + 9
|
||||||
|
| }
|
||||||
|
| byte two() { return 2 }
|
||||||
|
""".stripMargin) { m => m.readByte(0xc000) should equal(29) }
|
||||||
|
}
|
||||||
|
|
||||||
|
test("And-shift-and") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| void main () {
|
||||||
|
| byte a
|
||||||
|
| a = ee()
|
||||||
|
| output = ((a & $dd) << 1) & $55
|
||||||
|
| }
|
||||||
|
| byte ee() { return $ee }
|
||||||
|
""".stripMargin) { m => m.readByte(0xc000) should equal(0x10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Add with limit") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| const byte start = 5
|
||||||
|
| const byte limit = 234
|
||||||
|
| void main () {
|
||||||
|
| output += 1
|
||||||
|
| if output == limit {
|
||||||
|
| output = start
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
""".stripMargin) { m => m.readByte(0xc000) should equal(1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
test("User register instead of stack") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| array output [4] @$c000
|
||||||
|
| void main () {
|
||||||
|
| output[0] = double(2)
|
||||||
|
| }
|
||||||
|
| asm byte double(byte a) {
|
||||||
|
| ? asl
|
||||||
|
| ? pha
|
||||||
|
| lda output
|
||||||
|
| ? pla
|
||||||
|
| ? rts
|
||||||
|
| }
|
||||||
|
""".stripMargin) { m => m.readByte(0xc000) should equal(4) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user