1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-12 22:29:33 +00:00

More optimisations

This commit is contained in:
Karol Stasiak 2018-01-06 00:21:28 +01:00
parent 92999ec490
commit 8b086f6c2f
5 changed files with 152 additions and 5 deletions

View File

@ -108,6 +108,7 @@ object OptimizationPresets {
LaterOptimizations.UseYInsteadOfStack,
LaterOptimizations.IndexSwitchingOptimization,
LaterOptimizations.LoadingBranchesOptimization,
LaterOptimizations.IncreaseWithLimit,
)
val Good: List[AssemblyOptimization] = List[AssemblyOptimization](
@ -125,6 +126,7 @@ object OptimizationPresets {
AlwaysGoodOptimizations.IndexSequenceOptimization,
AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands,
AlwaysGoodOptimizations.ModificationOfJustWrittenValue,
AlwaysGoodOptimizations.OperationsAroundShifting,
AlwaysGoodOptimizations.PoinlessFlagChange,
AlwaysGoodOptimizations.PointlessLoadAfterLoadOrStore,
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,

View File

@ -14,6 +14,7 @@ import millfork.env._
*
* @author Karol Stasiak
*/
//noinspection ZeroIndexToHead
object AlwaysGoodOptimizations {
val counter = new AtomicInteger(30000)
@ -851,4 +852,37 @@ object AlwaysGoodOptimizations {
(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)
},
)
}

View File

@ -11,6 +11,7 @@ import millfork.env.{Constant, NormalFunction, NumericConstant}
*
* @author Karol Stasiak
*/
//noinspection ZeroIndexToHead
object LaterOptimizations {
@ -275,7 +276,7 @@ object LaterOptimizations {
val LoadingBranchesOptimization = new RuleBasedAssemblyOptimization("Loading branches optimization",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(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 & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~
(Elidable & HasOpcode(LDA)).capture(31) ~
@ -284,7 +285,7 @@ object LaterOptimizations {
(Elidable & HasOpcode(LDA)).capture(32) ~
(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 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 branch = ctx.get[List[AssemblyLine]](41)
val label = ctx.get[List[AssemblyLine]](42)
@ -293,7 +294,7 @@ object LaterOptimizations {
List(loadIfJumped, ldx, stx, cpx, branch, loadIfNotJumped, label).flatten
},
(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 & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~
(Elidable & HasOpcode(LDA)).capture(31) ~
@ -302,7 +303,7 @@ object LaterOptimizations {
(Elidable & HasOpcode(LDA)).capture(32) ~
(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 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 branch = ctx.get[List[AssemblyLine]](41)
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(
DoubleLoadToDifferentRegisters,
DoubleLoadToTheSameRegister,

View File

@ -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 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

View File

@ -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) }
}
}