1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-08-27 14:29:40 +00:00

If a variable is used wholly within a loop body and initialized conditionally, do not remove the last store to it

This commit is contained in:
Karol Stasiak 2018-12-29 20:07:43 +01:00
parent cbe709a9cf
commit d7b2181ef5
3 changed files with 66 additions and 9 deletions

View File

@ -17,6 +17,10 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
override def name = "Removing pointless stores to automatic variables" override def name = "Removing pointless stores to automatic variables"
private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY) private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY)
private val directStorageOpcodes = Set(
STA, STX, STY, SAX, STZ, SHX, SHY, AHX,
STA_W, STX_W, STY_W, STZ_W
)
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = { override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val paramVariables = f.params match { val paramVariables = f.params match {
@ -55,20 +59,26 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
for(v <- localVariables) { for(v <- localVariables) {
val lifetime = VariableLifetime.apply(v.name, code) val lifetime = VariableLifetime.apply(v.name, code)
val firstaccess = lifetime.head
val lastaccess = lifetime.last val lastaccess = lifetime.last
if (lastaccess >= 0) { if (firstaccess >= 0 && lastaccess >= 0) {
val firstVariableAccess = code(firstaccess)
val lastVariableAccess = code(lastaccess) val lastVariableAccess = code(lastaccess)
if (lastVariableAccess.elidable && storeAddrModes(lastVariableAccess.addrMode) && (lastVariableAccess.opcode match { if (lastVariableAccess.elidable &&
case STA | STX | STY | SAX | STZ | SHX | SHY | AHX => storeAddrModes(lastVariableAccess.addrMode) &&
storeAddrModes(firstVariableAccess.addrMode) &&
directStorageOpcodes(firstVariableAccess.opcode) &&
(lastVariableAccess.opcode match {
case op if directStorageOpcodes(op) =>
true true
case TSB | TRB => case TSB | TRB =>
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext) if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
importances(lastaccess).z != Important importances(lastaccess).z != Important
case INC | DEC => case INC | DEC | INC_W | DEC_W =>
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext) 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 | ASL_W | LSR_W | ROL_W | ROR_W =>
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext) 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 &&
@ -78,7 +88,7 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
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 SLO | SRE | RLA =>
if (importances eq null) importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext) 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 &&

View File

@ -25,11 +25,17 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[ZLine] {
val badVariables = mutable.Set[String]() val badVariables = mutable.Set[String]()
for((v, lifetime) <- vs.variablesWithLifetimes if lifetime.nonEmpty) { for((v, lifetime) <- vs.variablesWithLifetimes if lifetime.nonEmpty) {
val firstaccess = lifetime.head
val lastaccess = lifetime.last val lastaccess = lifetime.last
if (lastaccess >= 0) { if (firstaccess >= 0 && lastaccess >= 0) {
val firstVariableAccess = code(firstaccess)
val lastVariableAccess = code(lastaccess) val lastVariableAccess = code(lastaccess)
import millfork.assembly.z80.ZOpcode._ import millfork.assembly.z80.ZOpcode._
if (lastVariableAccess match { if ((firstVariableAccess match {
case ZLine(LD, TwoRegisters(MEM_HL, _), _, Elidability.Elidable, _) => true
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), _, Elidability.Elidable, _) => true
case _ => false
}) && (lastVariableAccess match {
case ZLine(LD, TwoRegisters(MEM_HL, _), _, Elidability.Elidable, _) => true case ZLine(LD, TwoRegisters(MEM_HL, _), _, Elidability.Elidable, _) => true
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), _, Elidability.Elidable, _) => true case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), _, Elidability.Elidable, _) => true
case ZLine(INC | DEC, OneRegister(MEM_HL), _, Elidability.Elidable, _) => case ZLine(INC | DEC, OneRegister(MEM_HL), _, Elidability.Elidable, _) =>
@ -39,7 +45,7 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[ZLine] {
val importances = vs.codeWithFlow(lastaccess)._1.importanceAfter val importances = vs.codeWithFlow(lastaccess)._1.importanceAfter
Seq(importances.sf, importances.zf, importances.cf).forall(_ == Unimportant) Seq(importances.sf, importances.zf, importances.cf).forall(_ == Unimportant)
case _ => false case _ => false
}) { })) {
badVariables += v.name badVariables += v.name
toRemove += lastaccess toRemove += lastaccess
} }

View File

@ -103,4 +103,45 @@ class SecondAssemblyOptimizationSuite extends FunSuite with Matchers {
m.readByte(0xc001) should equal(4) m.readByte(0xc001) should equal(4)
} }
} }
test("Conditional variable initialization") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
"""
| array output [16] @$c000
| void main () {
| byte entropy
| byte noise
| byte i
| entropy = 0
| for i,0,until,output.length {
| if entropy == 0 {
| entropy = 8
| noise = rand()
| }
| output[i] = noise & 1
| noise >>= 1
| entropy -= 1
| }
| }
| noinline byte rand() { return $42 }
""".stripMargin) { m =>
m.readByte(0xc000) should equal(0)
m.readByte(0xc001) should equal(1)
m.readByte(0xc002) should equal(0)
m.readByte(0xc003) should equal(0)
m.readByte(0xc004) should equal(0)
m.readByte(0xc005) should equal(0)
m.readByte(0xc006) should equal(1)
m.readByte(0xc007) should equal(0)
m.readByte(0xc008) should equal(0)
m.readByte(0xc009) should equal(1)
m.readByte(0xc00a) should equal(0)
m.readByte(0xc00b) should equal(0)
m.readByte(0xc00c) should equal(0)
m.readByte(0xc00d) should equal(0)
m.readByte(0xc00e) should equal(1)
m.readByte(0xc00f) should equal(0)
}
}
} }