mirror of
https://github.com/KarolS/millfork.git
synced 2025-08-07 12:25:40 +00:00
Optimization improvements
This commit is contained in:
@@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
* Fixed `#pragma` not respecting `#if`
|
* Fixed `#pragma` not respecting `#if`
|
||||||
|
|
||||||
|
* Optimization improvements.
|
||||||
|
|
||||||
## 0.3.2
|
## 0.3.2
|
||||||
|
|
||||||
* Almost complete support for the Zilog Z80, Intel 8080 and Sharp LR35902 microprocessors.
|
* Almost complete support for the Zilog Z80, Intel 8080 and Sharp LR35902 microprocessors.
|
||||||
|
@@ -505,7 +505,7 @@ object AlwaysGoodOptimizations {
|
|||||||
|
|
||||||
private def operationPairBuilder3(op1: Opcode.Value, op1extra: AssemblyLinePattern, op2: Opcode.Value, middle: AssemblyLinePattern, discardToRemove: Option[Opcode.Value]) = {
|
private def operationPairBuilder3(op1: Opcode.Value, op1extra: AssemblyLinePattern, op2: Opcode.Value, middle: AssemblyLinePattern, discardToRemove: Option[Opcode.Value]) = {
|
||||||
(HasOpcode(op1) & Elidable & op1extra) ~
|
(HasOpcode(op1) & Elidable & op1extra) ~
|
||||||
middle.*.capture(1) ~
|
(middle & IsNotALabelUsedManyTimes).*.capture(1) ~
|
||||||
Where(_.isExternallyLinearBlock(1)) ~
|
Where(_.isExternallyLinearBlock(1)) ~
|
||||||
(HasOpcode(op2) & Elidable) ~~> { (_, ctx) =>
|
(HasOpcode(op2) & Elidable) ~~> { (_, ctx) =>
|
||||||
ctx.get[List[AssemblyLine]](1).filter(l => !discardToRemove.contains(l.opcode))
|
ctx.get[List[AssemblyLine]](1).filter(l => !discardToRemove.contains(l.opcode))
|
||||||
@@ -514,7 +514,7 @@ 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(Absolute, ZeroPage, LongAbsolute) & MatchParameter(3)) ~
|
(HasOpcode(op1) & op1extra & Elidable & HasAddrModeIn(Absolute, ZeroPage, LongAbsolute) & MatchParameter(3)) ~
|
||||||
middle.*.capture(1) ~
|
(middle & IsNotALabelUsedManyTimes).*.capture(1) ~
|
||||||
Where(_.isExternallyLinearBlock(1)) ~
|
Where(_.isExternallyLinearBlock(1)) ~
|
||||||
(HasOpcode(op2) & op2extra & Elidable & HasAddrModeIn(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)
|
||||||
@@ -640,7 +640,7 @@ object AlwaysGoodOptimizations {
|
|||||||
},
|
},
|
||||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||||
(Elidable & HasOpcode(PHA)) ~
|
(Elidable & HasOpcode(PHA)) ~
|
||||||
(Not(ConcernsStack) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).*.capture(2) ~
|
(IsNotALabelUsedManyTimes & Not(ConcernsStack) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).*.capture(2) ~
|
||||||
Where(ctx => ctx.isExternallyLinearBlock(2))~
|
Where(ctx => ctx.isExternallyLinearBlock(2))~
|
||||||
(Elidable & HasOpcode(PLA)) ~~> { code =>
|
(Elidable & HasOpcode(PLA)) ~~> { code =>
|
||||||
code.head :: (code.drop(2).init :+ code.head)
|
code.head :: (code.drop(2).init :+ code.head)
|
||||||
@@ -2161,13 +2161,13 @@ object AlwaysGoodOptimizations {
|
|||||||
code.tail.init :+ code.head
|
code.tail.init :+ code.head
|
||||||
},
|
},
|
||||||
(Elidable & HasOpcodeIn(DEX, INX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
(Elidable & HasOpcodeIn(DEX, INX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||||
(Not(ConcernsX)).*.capture(1) ~
|
(IsNotALabelUsedManyTimes & Not(ConcernsX)).*.capture(1) ~
|
||||||
Where(ctx => ctx.isExternallyLinearBlock(1)) ~
|
Where(ctx => ctx.isExternallyLinearBlock(1)) ~
|
||||||
(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(DEY, INY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
(Elidable & HasOpcodeIn(DEY, INY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||||
(Not(ConcernsY)).*.capture(1) ~
|
(IsNotALabelUsedManyTimes & Not(ConcernsY)).*.capture(1) ~
|
||||||
Where(ctx => ctx.isExternallyLinearBlock(1)) ~
|
Where(ctx => ctx.isExternallyLinearBlock(1)) ~
|
||||||
(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
|
||||||
@@ -2307,9 +2307,9 @@ object AlwaysGoodOptimizations {
|
|||||||
val PointlessSignCheck: RuleBasedAssemblyOptimization = {
|
val PointlessSignCheck: RuleBasedAssemblyOptimization = {
|
||||||
def loadOldSignedVariable: AssemblyPattern = (
|
def loadOldSignedVariable: AssemblyPattern = (
|
||||||
(HasOpcodeIn(AND, ANC) & HasImmediateWhere(i => (i & 0x80) == 0)) ~
|
(HasOpcodeIn(AND, ANC) & HasImmediateWhere(i => (i & 0x80) == 0)) ~
|
||||||
(HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & MatchAddrMode(0) & MatchParameter(1)) ~
|
(HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||||
DoesNotConcernMemoryAt(0, 1).* ~
|
(IsNotALabelUsedManyTimes & DoesNotConcernMemoryAt(0, 1)).* ~
|
||||||
(HasOpcode(LDA) & HasAddrModeIn(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
|
||||||
|
@@ -241,7 +241,7 @@ object LaterOptimizations {
|
|||||||
val UseXInsteadOfStack = new RuleBasedAssemblyOptimization("Using X instead of stack",
|
val UseXInsteadOfStack = new RuleBasedAssemblyOptimization("Using X instead of stack",
|
||||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||||
(Elidable & HasOpcode(PHA) & DoesntMatterWhatItDoesWith(State.X)) ~
|
(Elidable & HasOpcode(PHA) & DoesntMatterWhatItDoesWith(State.X)) ~
|
||||||
(Not(ConcernsStack) & Not(ConcernsX)).capture(1) ~
|
(IsNotALabelUsedManyTimes & Not(ConcernsStack) & Not(ConcernsX)).capture(1) ~
|
||||||
Where(_.isExternallyLinearBlock(1)) ~
|
Where(_.isExternallyLinearBlock(1)) ~
|
||||||
(Elidable & HasOpcode(PLA)) ~~> (c =>
|
(Elidable & HasOpcode(PLA)) ~~> (c =>
|
||||||
AssemblyLine.implied(TAX) :: (c.tail.init :+ AssemblyLine.implied(TXA))
|
AssemblyLine.implied(TAX) :: (c.tail.init :+ AssemblyLine.implied(TXA))
|
||||||
@@ -251,7 +251,7 @@ object LaterOptimizations {
|
|||||||
val UseYInsteadOfStack = new RuleBasedAssemblyOptimization("Using Y instead of stack",
|
val UseYInsteadOfStack = new RuleBasedAssemblyOptimization("Using Y instead of stack",
|
||||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||||
(Elidable & HasOpcode(PHA) & DoesntMatterWhatItDoesWith(State.Y)) ~
|
(Elidable & HasOpcode(PHA) & DoesntMatterWhatItDoesWith(State.Y)) ~
|
||||||
(Not(ConcernsStack) & Not(ConcernsY)).capture(1) ~
|
(IsNotALabelUsedManyTimes & Not(ConcernsStack) & Not(ConcernsY)).capture(1) ~
|
||||||
Where(_.isExternallyLinearBlock(1)) ~
|
Where(_.isExternallyLinearBlock(1)) ~
|
||||||
(Elidable & HasOpcode(PLA)) ~~> (c =>
|
(Elidable & HasOpcode(PLA)) ~~> (c =>
|
||||||
AssemblyLine.implied(TAY) :: (c.tail.init :+ AssemblyLine.implied(TYA))
|
AssemblyLine.implied(TAY) :: (c.tail.init :+ AssemblyLine.implied(TYA))
|
||||||
|
@@ -87,7 +87,7 @@ 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(RTS, JSR, RTI, RTL)) & Not(ChangesX)).*.capture(Body) ~
|
((IsNotALabelUsedManyTimes & Elidable & Not(HasOpcodeIn(RTS, JSR, RTI, RTL)) & Not(ChangesX)).*.capture(Body) ~
|
||||||
(Elidable & HasOpcodeIn(DEX, INX)).capture(Step)
|
(Elidable & HasOpcodeIn(DEX, INX)).capture(Step)
|
||||||
).capture(BodyWithStep) ~
|
).capture(BodyWithStep) ~
|
||||||
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~
|
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~
|
||||||
|
@@ -28,6 +28,11 @@ object FlowInfoRequirement extends Enumeration {
|
|||||||
case BothFlows | BackwardFlow => ()
|
case BothFlows | BackwardFlow => ()
|
||||||
case NoRequirement | JustLabels | ForwardFlow => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
case NoRequirement | JustLabels | ForwardFlow => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
|
||||||
|
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait AssemblyRuleSet{
|
trait AssemblyRuleSet{
|
||||||
@@ -1366,3 +1371,16 @@ case object IsZeroPage extends AssemblyLinePattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case object IsNotALabelUsedManyTimes extends AssemblyLinePattern {
|
||||||
|
|
||||||
|
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = FlowInfoRequirement.assertLabels(needsFlowInfo)
|
||||||
|
|
||||||
|
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = line.opcode match {
|
||||||
|
case Opcode.LABEL => line.parameter match {
|
||||||
|
case MemoryAddressConstant(Label(l)) => flowInfo.labelUseCount(l) <= 1
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
case _ => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package millfork.assembly.z80.opt
|
package millfork.assembly.z80.opt
|
||||||
|
|
||||||
import millfork.assembly.AssemblyOptimization
|
import millfork.assembly.AssemblyOptimization
|
||||||
import millfork.assembly.z80._
|
import millfork.assembly.z80.{opt, _}
|
||||||
import millfork.assembly.z80.ZOpcode._
|
import millfork.assembly.z80.ZOpcode._
|
||||||
import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant}
|
import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant}
|
||||||
import millfork.node.ZRegister
|
import millfork.node.ZRegister
|
||||||
@@ -821,6 +821,14 @@ object AlwaysGoodI80Optimizations {
|
|||||||
List(ZLine.ldImm16(ZRegister.BC, (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8)).quickSimplify))
|
List(ZLine.ldImm16(ZRegister.BC, (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8)).quickSimplify))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
(Elidable & Is8BitLoad(A, MEM_ABS_8) & MatchParameter(1)) ~
|
||||||
|
(Not(Concerns(ZRegister.HL)) & IsNotALabelUsedManyTimes).*.capture(5) ~
|
||||||
|
Where(ctx => ctx.isExternallyLinearBlock(5)) ~
|
||||||
|
(Elidable & HasOpcode(LD_16) & opt.HasRegisters(TwoRegisters(HL, IMM_16)) & MatchParameter(1)) ~~> (code =>
|
||||||
|
code.last ::
|
||||||
|
code.head.copy(registers = TwoRegisters(A, MEM_HL), parameter = Constant.Zero) ::
|
||||||
|
code.tail.init),
|
||||||
|
|
||||||
// TODO: this is a bit controversial
|
// TODO: this is a bit controversial
|
||||||
// 41 cycles 6 bytes → 24 cycles 8 bytes
|
// 41 cycles 6 bytes → 24 cycles 8 bytes
|
||||||
MultipleAssemblyRules(Seq(BC, DE).map{ reg =>
|
MultipleAssemblyRules(Seq(BC, DE).map{ reg =>
|
||||||
@@ -1263,6 +1271,8 @@ object AlwaysGoodI80Optimizations {
|
|||||||
(Elidable & HasOpcode(SCF) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
(Elidable & HasOpcode(SCF) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
||||||
(Elidable & HasOpcode(CCF) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
(Elidable & HasOpcode(CCF) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
||||||
(Elidable & HasOpcodeIn(Set(OR, AND)) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
(Elidable & HasOpcodeIn(Set(OR, AND)) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
||||||
|
HasOpcodeIn(Set(OR, AND, XOR)) ~
|
||||||
|
(Elidable & HasOpcodeIn(Set(OR, AND)) & HasRegisterParam(ZRegister.A)) ~~> (_.init),
|
||||||
)
|
)
|
||||||
|
|
||||||
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
|
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
|
||||||
|
@@ -27,6 +27,11 @@ object FlowInfoRequirement extends Enumeration {
|
|||||||
case BothFlows | BackwardFlow => ()
|
case BothFlows | BackwardFlow => ()
|
||||||
case NoRequirement | JustLabels | ForwardFlow => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
case NoRequirement | JustLabels | ForwardFlow => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
|
||||||
|
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait AssemblyRuleSet{
|
trait AssemblyRuleSet{
|
||||||
@@ -1037,3 +1042,16 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: AssemblyLinePattern, la
|
|||||||
Some(after)
|
Some(after)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case object IsNotALabelUsedManyTimes extends AssemblyLinePattern {
|
||||||
|
|
||||||
|
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = FlowInfoRequirement.assertLabels(needsFlowInfo)
|
||||||
|
|
||||||
|
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = line.opcode match {
|
||||||
|
case ZOpcode.LABEL => line.parameter match {
|
||||||
|
case MemoryAddressConstant(Label(l)) => flowInfo.labelUseCount(l) <= 1
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
case _ => true
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user