1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-09 16:29:34 +00:00

6809: various optimizations

This commit is contained in:
Karol Stasiak 2020-09-15 17:08:38 +02:00
parent 947a84833a
commit ca5fe5cdb0
5 changed files with 225 additions and 3 deletions

View File

@ -40,6 +40,8 @@ object MOpcode extends Enumeration {
val PrefixedBy10: Set[MOpcode.Value] = Set(CMPD, CMPY, LDS, LDY, SWI2, STS, STY) // TODO: branches
val PrefixedBy11: Set[MOpcode.Value] = Set(CMPS, CMPU, SWI3)
val Prefixed: Set[MOpcode.Value] = PrefixedBy10 ++ PrefixedBy11
val CanHaveImmediateAndIndexedByte: Set[MOpcode.Value] = Set(ADCA, ADCB, ADDA, ADDB, ANDA, ANDB, BITA, BITB, CMPA, CMPB, EORA, EORB, LDA, LDB, ORA, ORB, SBCA, SBCB, SUBA, SUBB)
val CanHaveImmediateAndIndexedWord: Set[MOpcode.Value] = Set(ADDD, CMPD, CMPS, CMPU, CMPX, CMPY, LDD, LDS, LDU, LDX, LDY, SUBD)
val CanHaveInherentAccumulator: Set[MOpcode.Value] = Set(ASL, ASR, CLR, COM, DEC, INC, LSR, NEG, ROL, ROR, TST)
val Branching: Set[MOpcode.Value] = Set(BRA, BRN, BHI, BLS, BCC, BCS, BNE, BEQ, BVC, BVS, BPL, BMI, BGE, BLT, BGT, BLE)
val ConditionalBranching: Set[MOpcode.Value] = Set(BHI, BLS, BCC, BCS, BNE, BEQ, BVC, BVS, BPL, BMI, BGE, BLT, BGT, BLE)
@ -146,4 +148,26 @@ object MOpcode extends Enumeration {
NOP -> None
}
}
def invertBranching(opcode: Value): Value = {
opcode match {
case BCC => BCS
case BCS => BCC
case BEQ => BNE
case BNE => BEQ
case BGE => BLT
case BLT => BGE
case BGT => BLE
case BLE => BGT
case BHI => BLS
case BLS => BHI
case BPL => BMI
case BMI => BPL
case BVC => BVS
case BVS => BVC
case BRA => BRN
case BRN => BRA
case _ => opcode
}
}
}

View File

@ -3,7 +3,7 @@ package millfork.assembly.m6809.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.m6809.MOpcode._
import millfork.assembly.m6809.{MLine, MState}
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
import millfork.node.M6809Register
/**
@ -18,6 +18,14 @@ object AlwaysGoodMOptimizations {
(Elidable & HasOpcode(LDD) & DoesntMatterWhatItDoesWith(MState.A, MState.B, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(LDX, LEAX) & DoesntMatterWhatItDoesWith(MState.X, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(LDY, LEAY) & DoesntMatterWhatItDoesWith(MState.Y, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
(Elidable & HasOpcode(STB) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0)).* ~
(Elidable & HasOpcode(LDB) & MatchAddrMode(0) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
(Elidable & HasOpcode(STD) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0)).* ~
(Elidable & HasOpcode(LDD) & MatchAddrMode(0) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
)
val SimplifiableZeroStore = new RuleBasedAssemblyOptimization("Simplifiable zero store",
@ -54,9 +62,96 @@ object AlwaysGoodMOptimizations {
(Elidable & IsTfrTo(M6809Register.X) & DoesntMatterWhatItDoesWith(MState.X)) ~~> (_ => Nil),
)
private val PullByte: MAddrMode = PostIncremented(M6809Register.S, 1, indirect = false)
private val PullWord: MAddrMode = PostIncremented(M6809Register.S, 2, indirect = false)
import M6809Register._
val PointlessStashing = new RuleBasedAssemblyOptimization("Pointless stashing",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(LDB) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PSHS) & HasAddrMode(RegisterSet(Set(B))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF, MState.B)) ~
(Linear & Not(ConcernsS)).* ~
(Elidable & HasOpcodeIn(CanHaveImmediateAndIndexedByte) & HasAddrMode(PullByte)) ~~> { code =>
code.drop(2).init :+ code.last.copy(addrMode = Immediate, parameter = code.head.parameter)
},
(Elidable & HasOpcode(LDD) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PSHS) & HasAddrMode(RegisterSet(Set(D))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF, MState.B, MState.A)) ~
(Linear & Not(ConcernsS)).* ~
(Elidable & HasOpcodeIn(CanHaveImmediateAndIndexedWord) & HasAddrMode(PullWord)) ~~> { code =>
code.drop(2).init :+ code.last.copy(addrMode = Immediate, parameter = code.head.parameter)
},
(Elidable & HasOpcode(LDB) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PSHS) & HasAddrMode(RegisterSet(Set(B))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF, MState.B)) ~
(Linear & Not(ConcernsS)).* ~
(Elidable & HasOpcode(PULS) & HasAddrMode(RegisterSet(Set(B))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> { code =>
code.drop(2).init :+ code.head
},
(Elidable & HasOpcode(LDB) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PSHS) & HasAddrMode(RegisterSet(Set(B))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF, MState.B)) ~
(Linear & Not(ConcernsS)).* ~
(Elidable & HasOpcode(PULS) & HasAddrMode(RegisterSet(Set(A))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> { code =>
code.drop(2).init :+ code.head.copy(opcode = LDA)
},
(Elidable & HasOpcode(LDD) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PSHS) & HasAddrMode(RegisterSet(Set(D))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF, MState.B, MState.A)) ~
(Linear & Not(ConcernsS)).* ~
(Elidable & HasOpcode(PULS) & HasAddrMode(RegisterSet(Set(D))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> { code =>
code.drop(2).init :+ code.head
},
(Elidable & HasOpcode(LDD) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(PSHS) & HasAddrMode(RegisterSet(Set(D))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF, MState.B, MState.A)) ~
(Linear & Not(ConcernsS)).* ~
(Elidable & HasOpcode(PULS) & HasAddrMode(RegisterSet(Set(X))) & DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> { code =>
code.drop(2).init :+ code.head.copy(opcode = LDX)
},
)
val SimplifiableJumps = new RuleBasedAssemblyOptimization("Simplifiable jumps",
needsFlowInfo = FlowInfoRequirement.JustLabels,
(Elidable & HasOpcodeIn(ConditionalBranching) & MatchParameter(0)) ~
(Elidable & HasOpcodeIn(BRA, JMP)) ~
(Elidable & HasOpcodeIn(LABEL) & MatchParameter(0) & IsNotALabelUsedManyTimes) ~~> {code =>
List(code(1).copy(opcode = invertBranching(code.head.opcode), addrMode = LongRelative))
},
(Elidable & HasOpcodeIn(ConditionalBranching) & MatchParameter(0)) ~
(Elidable & HasOpcodeIn(BRA, JMP)) ~
(Elidable & HasOpcodeIn(LABEL) & MatchParameter(0)) ~~> {code =>
List(code(1).copy(opcode = invertBranching(code.head.opcode), addrMode = LongRelative), code(2))
}
)
val SimplifiableArithmetics = new RuleBasedAssemblyOptimization("Simplifiable arithmetics",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(LEAX)
& HasAddrMode(DAccumulatorIndexed(X, indirect = false))
& HasA(0)
& DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF)) ~~>
(_ => List(MLine.inherent(ABX))),
(NotFixed & HasB(0) & HasOpcodeIn(ORB, EORB, ADDB) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
(_.map(_.copy(opcode = LDB))),
(NotFixed & HasB(0xff) & HasOpcode(ANDB) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
(_.map(_.copy(opcode = LDB))),
(NotFixed & HasA(0) & HasOpcodeIn(ORA, EORA, ADDA) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
(_.map(_.copy(opcode = LDA))),
(NotFixed & HasA(0xff) & HasOpcode(ANDA) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
(_.map(_.copy(opcode = LDA))),
(NotFixed & HasA(0) & HasB(0) & HasOpcodeIn(ADDD) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
(_.map(_.copy(opcode = LDD))),
(Elidable & HasImmediate(0) & HasOpcodeIn(ORB, EORB, ADDB, ORA, EORA, ADDA, ADDD) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
(_ => Nil),
)
val All: Seq[AssemblyOptimization[MLine]] = Seq(
PointlessLoad,
PointlessRegisterTransfers,
SimplifiableArithmetics,
SimplifiableJumps,
SimplifiableZeroStore
)
}

View File

@ -0,0 +1,81 @@
package millfork.assembly.m6809.opt
import millfork.CompilationOptions
import millfork.assembly.m6809.{LongRelative, MLine, MLine0, Relative}
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, ThingInMemory}
import millfork.assembly.m6809.MOpcode._
/**
* @author Karol Stasiak
*/
object JumpFixing {
def validShortJump(thisOffset: Int, labelOffset: Int): Boolean = {
// TODO
val distance = labelOffset - (thisOffset + 2)
distance.toByte == distance
}
def apply(f: NormalFunction, code: List[MLine], options: CompilationOptions): List[MLine] = {
val offsets = new Array[Int](code.length)
var o = 0
code.zipWithIndex.foreach{
case (line, ix) =>
offsets(ix) = o
o += line.sizeInBytes
}
val labelOffsets = code.zipWithIndex.flatMap {
case (MLine0(LABEL, _, MemoryAddressConstant(Label(label))), ix) => Some(label -> offsets(ix))
case _ => None
}.toMap
var changed = false
def makeLong(line: MLine): MLine = {
changed = true
val newLine = line.copy(addrMode = LongRelative)
options.log.debug("Changing branch from short to long")
if (options.log.traceEnabled) {
options.log.trace(line.toString)
options.log.trace(" ↓")
options.log.trace(newLine.toString)
}
newLine
}
def makeShort(line: MLine): MLine = {
changed = true
val newLine = line.copy(addrMode = Relative)
options.log.debug("Changing branch from long to short")
if (options.log.traceEnabled) {
options.log.trace(line.toString)
options.log.trace(" ↓")
options.log.trace(newLine.toString)
}
newLine
}
import millfork.assembly.Elidability.Elidable
import millfork.assembly.Elidability.Volatile
val result = code.zipWithIndex.map {
case (line@MLine(_, Relative, MemoryAddressConstant(th: ThingInMemory), Elidable | Volatile, _), ix) =>
labelOffsets.get(th.name) match {
case None =>
changed = true
line.copy(addrMode = LongRelative)
case Some(labelOffset) =>
val thisOffset = offsets(ix)
if (!validShortJump(thisOffset, labelOffset)) makeLong(line) else line
}
case (line@MLine(_, Relative, _, Elidable | Volatile, _), _) =>
makeLong(line)
case (line@MLine(_, LongRelative, MemoryAddressConstant(th: ThingInMemory), Elidable | Volatile, _), ix) =>
labelOffsets.get(th.name) match {
case None => line
case Some(labelOffset) =>
val thisOffset = offsets(ix)
if (validShortJump(thisOffset, labelOffset)) makeShort(line) else line
}
case (line, _) => line
}
if (changed) apply(f, result, options) else result
}
}

View File

@ -2,7 +2,7 @@ package millfork.assembly.m6809.opt
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.assembly.m6809.{Absolute, Immediate, Inherent, InherentA, InherentB, LongRelative, MAddrMode, MLine, MLine0, MOpcode, MState, Relative, TwoRegisters}
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, Indexed, Inherent, InherentA, InherentB, LongRelative, MAddrMode, MLine, MLine0, MOpcode, MState, NonExistent, PostIncremented, PreDecremented, Relative, TwoRegisters}
import millfork.assembly.opt.SingleStatus
import millfork.compiler.LabelGenerator
import millfork.env._
@ -989,6 +989,25 @@ case class DoesntChangeMemoryAtAssumingNonchangingIndices(addrMode1: Int, param1
override def hitRate: Double = 0.973
}
case class DoesntChangeIndexingInAddrMode(i: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import M6809Register._
ctx.get[MAddrMode](i) match {
case Indexed(S, false) => !ConcernsS.matchLineTo(ctx, flowInfo, line)
case Indexed(X, false) => !ChangesX.matchLineTo(ctx, flowInfo, line)
case Indexed(Y, false) => !ChangesY.matchLineTo(ctx, flowInfo, line)
case Absolute(true) => !ChangesMemory.matchLineTo(ctx, flowInfo, line)
case Absolute(false) | Inherent | InherentA | InherentB | Immediate => true
case _ => false // let's ignore rarer addressing modes
}
}
override def toString: String = s"¬(?<$i>AddrMode)"
override def hitRate: Double = 0.99
}
case object ConcernsMemory extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
ReadsMemory.matchLineTo(ctx, flowInfo, line) || ChangesMemory.matchLineTo(ctx, flowInfo, line)

View File

@ -1,6 +1,7 @@
package millfork.output
import millfork.assembly.SourceLine
import millfork.assembly.m6809.opt.JumpFixing
import millfork.{CompilationOptions, Platform}
import millfork.assembly.m6809.{MOpcode, _}
import millfork.compiler.m6809.M6809Compiler
@ -25,7 +26,9 @@ class M6809Assembler(program: Program,
override def gatherNiceFunctionProperties(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[MLine]): Unit = ()
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[MLine]): List[MLine] = code
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[MLine]): List[MLine] = {
JumpFixing(f, code, options)
}
private def registerCode(register: M6809Register.Value): Int = {
import M6809Register._