diff --git a/src/main/scala/millfork/assembly/opt/VariableToRegisterOptimization.scala b/src/main/scala/millfork/assembly/opt/VariableToRegisterOptimization.scala index 0758f3aa..d179f72e 100644 --- a/src/main/scala/millfork/assembly/opt/VariableToRegisterOptimization.scala +++ b/src/main/scala/millfork/assembly/opt/VariableToRegisterOptimization.scala @@ -54,6 +54,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { TRB, TSB) private val opcodesCommutative = Set(AND, ORA, EOR, ADC) + private val opcodesIdentityTable = Set(AND, ORA, EOR, CMP, ADC, SBC) private val LdxAddrModes = Set(ZeroPage, Absolute, Immediate, AbsoluteY, ZeroPageY) private val LdyAddrModes = Set(ZeroPage, Absolute, Immediate, AbsoluteX, ZeroPageX) @@ -94,13 +95,15 @@ object VariableToRegisterOptimization extends AssemblyOptimization { ) val importances = ReverseFlowAnalyzer.analyze(f, code) + val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed) + val identityArray = f.environment.maybeGet[ThingInMemory]("identity$").map(MemoryAddressConstant).getOrElse(Constant.Zero) val xCandidates = variablesWithLifetimes.filter { case (vName, range) => importances(range.start).x != Important }.flatMap { case (vName, range) => - canBeInlined(Some(vName), None, code.zip(importances).slice(range.start, range.end)).map { score => + canBeInlined(Some(vName), None, blastProcessing, code.zip(importances).slice(range.start, range.end)).map { score => (vName, range, if (variablesWithRegisterHint(vName)) score + 16 else score) } } @@ -110,7 +113,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { importances(range.start).y != Important }.flatMap { case (vName, range) => - canBeInlined(None, Some(vName), code.zip(importances).slice(range.start, range.end)).map { score => + canBeInlined(None, Some(vName), blastProcessing, code.zip(importances).slice(range.start, range.end)).map { score => (vName, range, if (variablesWithRegisterHint(vName)) score + 16 else score) } } @@ -171,7 +174,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { case (v, range, _) => ErrorReporting.debug(s"Inlining $v to register X") val oldCode = code.zip(importances).slice(range.start, range.end) - val newCode = inlineVars(Some(v), None, None, oldCode) + val newCode = inlineVars(Some(v), None, None, identityArray, oldCode) reportOptimizedBlock(oldCode, newCode) output ++= newCode i = range.end @@ -182,7 +185,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { case (v, range, _) => ErrorReporting.debug(s"Inlining $v to register Y") val oldCode = code.zip(importances).slice(range.start, range.end) - val newCode = inlineVars(None, Some(v), None, oldCode) + val newCode = inlineVars(None, Some(v), None, identityArray, oldCode) reportOptimizedBlock(oldCode, newCode) output ++= newCode i = range.end @@ -194,7 +197,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { case (v, range, _) => ErrorReporting.debug(s"Inlining $v to register A") val oldCode = code.zip(importances).slice(range.start, range.end) - val newCode = inlineVars(None, None, Some(v), oldCode) + val newCode = inlineVars(None, None, Some(v), identityArray, oldCode) reportOptimizedBlock(oldCode, newCode) output ++= newCode i = range.end @@ -213,7 +216,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { } - def canBeInlined(xCandidate: Option[String], yCandidate: Option[String], lines: List[(AssemblyLine, CpuImportance)]): Option[Int] = { + def canBeInlined(xCandidate: Option[String], yCandidate: Option[String], blastProcessing: Boolean, lines: List[(AssemblyLine, CpuImportance)]): Option[Int] = { val vx = xCandidate.getOrElse("-") val vy = yCandidate.getOrElse("-") lines match { @@ -231,7 +234,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { if (th.name == vx || th.name == vy) { None } else { - canBeInlined(xCandidate, yCandidate, xs) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs) } case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs @@ -250,9 +253,9 @@ object VariableToRegisterOptimization extends AssemblyOptimization { // removing LDX saves 3 cycles if (elidable && th.name == vx) { if (imp.z == Unimportant && imp.n == Unimportant) { - canBeInlined(xCandidate, yCandidate, xs).map(_ + 3) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 3) } else { - canBeInlined(xCandidate, yCandidate, xs).map(_ + 1) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 1) } } else { None @@ -263,7 +266,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { // LAX = LDX-LDA, and since LDX simplifies to nothing and LDA simplifies to TXA, // LAX simplifies to TXA, saving two bytes if (elidable && th.name == vx) { - canBeInlined(xCandidate, yCandidate, xs).map(_ + 2) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 2) } else { None } @@ -274,9 +277,9 @@ object VariableToRegisterOptimization extends AssemblyOptimization { // sometimes that LDX has to be converted into CPX#0 if (elidable && th.name == vy) { if (imp.z == Unimportant && imp.n == Unimportant) { - canBeInlined(xCandidate, yCandidate, xs).map(_ + 3) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 3) } else { - canBeInlined(xCandidate, yCandidate, xs).map(_ + 1) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 1) } } else { None @@ -290,26 +293,33 @@ object VariableToRegisterOptimization extends AssemblyOptimization { // if a register is populated with something else than a variable, then no variable cannot be assigned to that register None + case (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs + if opcodesIdentityTable(op) => + if (th.name == vx || th.name == vy) { + if (elidable) canBeInlined(xCandidate, yCandidate, blastProcessing, xs) + else None + } else canBeInlined(xCandidate, yCandidate, blastProcessing, xs) + case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs if opcodesCommutative(op) => if (th.name == vx || th.name == vy) { - if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, xs).map(_ + 2) + if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 2) else None - } else canBeInlined(xCandidate, yCandidate, xs) + } else canBeInlined(xCandidate, yCandidate, blastProcessing, xs) case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(CLC, _, _, _),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs if opcodesCommutative(op) => if (th.name == vx || th.name == vy) { - if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, xs).map(_ + 2) + if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 2) else None - } else canBeInlined(xCandidate, yCandidate, xs) + } else canBeInlined(xCandidate, yCandidate, blastProcessing, xs) case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: (AssemblyLine(TAX, _, _, elidable2), _) :: xs if xCandidate.isDefined => // a variable cannot be inlined if there is TAX not after LDA of that variable // but LDA-TAX can be simplified to TXA if (elidable && elidable2 && th.name == vx) { - canBeInlined(xCandidate, yCandidate, xs).map(_ + 3) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 3) } else { None } @@ -319,7 +329,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { // a variable cannot be inlined if there is TAY not after LDA of that variable // but LDA-TAY can be simplified to TYA if (elidable && elidable2 && th.name == vy) { - canBeInlined(xCandidate, yCandidate, xs).map(_ + 3) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 3) } else { None } @@ -328,12 +338,12 @@ object VariableToRegisterOptimization extends AssemblyOptimization { // changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX, STZ->LDA saves 2 bytes if (th.name == vy || th.name == vx) { if (elidable) { - canBeInlined(xCandidate, yCandidate, xs).map(_ + 2) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 2) } else { None } } else { - canBeInlined(xCandidate, yCandidate, xs) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs) } case (AssemblyLine(TAX, _, _, _), _) :: xs if xCandidate.isDefined => @@ -346,7 +356,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { case (AssemblyLine(LABEL, _, _, _), _) :: xs => // labels always end the initial section - canBeInlined(xCandidate, yCandidate, xs) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs) case (x, _) :: xs => if (xCandidate.isDefined && opcodesThatAlwaysPrecludeXAllocation(x.opcode)) { @@ -354,7 +364,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization { } else if (yCandidate.isDefined && opcodesThatAlwaysPrecludeYAllocation(x.opcode)) { None } else { - canBeInlined(xCandidate, yCandidate, xs) + canBeInlined(xCandidate, yCandidate, blastProcessing, xs) } case Nil => Some(0) @@ -498,170 +508,178 @@ object VariableToRegisterOptimization extends AssemblyOptimization { } } - def inlineVars(xCandidate: Option[String], yCandidate: Option[String], aCandidate: Option[String], lines: List[(AssemblyLine, CpuImportance)]): List[AssemblyLine] = { + def inlineVars(xCandidate: Option[String], yCandidate: Option[String], aCandidate: Option[String], identityArray: Constant, lines: List[(AssemblyLine, CpuImportance)]): List[AssemblyLine] = { val vx = xCandidate.getOrElse("-") val vy = yCandidate.getOrElse("-") val va = aCandidate.getOrElse("-") lines match { case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vx => - AssemblyLine.implied(INX) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(INX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vy => - AssemblyLine.implied(INY) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(INY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vx => - AssemblyLine.implied(DEX) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(DEX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vy => - AssemblyLine.implied(DEY) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(DEY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(opcode@(DEC | INC | ROL | ROR | ASL | LSR), Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == va => - AssemblyLine.implied(opcode) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(opcode) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs if th.name == vx => if (imp.z == Unimportant && imp.n == Unimportant) { - inlineVars(xCandidate, yCandidate, aCandidate, xs) + inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) } else { - AssemblyLine.immediate(CPX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.immediate(CPX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) } case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vx => - AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) + + case (l@AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs + if opcodesIdentityTable(op) && th.name == vx => + l.copy(addrMode = AbsoluteX, parameter = identityArray) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) + + case (l@AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs + if opcodesIdentityTable(op) && th.name == vy => + l.copy(addrMode = AbsoluteY, parameter = identityArray) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if opcodesCommutative(op) && th.name == va => - l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if opcodesCommutative(op) && th.name == va => - l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if opcodesCommutative(op) && th.name == vx => - AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if opcodesCommutative(op) && th.name == vx => - AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if opcodesCommutative(op) && th.name == vy => - AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if opcodesCommutative(op) && th.name == vy => - AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs if th.name == va => if (imp.z == Unimportant && imp.n == Unimportant) { - inlineVars(xCandidate, yCandidate, aCandidate, xs) + inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) } else { - AssemblyLine.immediate(CMP, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.immediate(CMP, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) } case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == va => - AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs if th.name == vy => if (imp.z == Unimportant && imp.n == Unimportant) { - inlineVars(xCandidate, yCandidate, aCandidate, xs) + inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) } else { - AssemblyLine.immediate(CPY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.immediate(CPY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) } case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TAX, _, _, true), _) :: xs if th.name == vx => // these TXA's may get optimized away by a different optimization - AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TAY, _, _, true), _) :: xs if th.name == vy => // these TYA's may get optimized away by a different optimization - AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TXA, _, _, true), _) :: xs if th.name == va => // these TAX's may get optimized away by a different optimization - AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TYA, _, _, true), _) :: xs if th.name == va => // these TAY's may get optimized away by a different optimization - AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDA, am, param, true), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs if th.name == vx && LdxAddrModes(am) => // these TXA's may get optimized away by a different optimization - AssemblyLine(LDX, am, param) :: AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine(LDX, am, param) :: AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDA, am, param, true), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs if th.name == vy && LdyAddrModes(am) => // these TYA's may get optimized away by a different optimization - AssemblyLine(LDY, am, param) :: AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine(LDY, am, param) :: AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: (AssemblyLine(CMP, am, param, true), _) :: xs if th.name == vx && doesntUseXOrY(am) => // ditto - AssemblyLine.implied(TXA) :: AssemblyLine(CPX, am, param) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TXA) :: AssemblyLine(CPX, am, param) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: (AssemblyLine(CMP, am, param, true), _) :: xs if th.name == vy && doesntUseXOrY(am) => // ditto - AssemblyLine.implied(TYA) :: AssemblyLine(CPY, am, param) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TYA) :: AssemblyLine(CPY, am, param) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vx => - AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vy => - AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == va => - AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == va => - AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vx => - AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vy => - AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == va => - AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == va => - AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vx => - AssemblyLine.immediate(LDX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.immediate(LDX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vy => - AssemblyLine.immediate(LDY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.immediate(LDY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == va => - AssemblyLine.immediate(LDA, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.immediate(LDA, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(TAX, _, _, _), _) :: xs if xCandidate.isDefined => ErrorReporting.fatal("Unexpected TAX") @@ -670,12 +688,12 @@ object VariableToRegisterOptimization extends AssemblyOptimization { ErrorReporting.fatal("Unexpected TAY") case (AssemblyLine(TXA, _, _, _), _) :: xs if aCandidate.isDefined => - AssemblyLine.immediate(CPX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.immediate(CPX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case (AssemblyLine(TYA, _, _, _), _) :: xs if aCandidate.isDefined => - AssemblyLine.immediate(CPY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + AssemblyLine.immediate(CPY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) - case (x, _) :: xs => x :: inlineVars(xCandidate, yCandidate, aCandidate, xs) + case (x, _) :: xs => x :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs) case Nil => Nil } diff --git a/src/main/scala/millfork/compiler/ExpressionCompiler.scala b/src/main/scala/millfork/compiler/ExpressionCompiler.scala index a6a6d16a..9cd6444c 100644 --- a/src/main/scala/millfork/compiler/ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/ExpressionCompiler.scala @@ -40,6 +40,8 @@ object ExpressionCompiler { w case SumExpression(params, _) => b case FunctionCallExpression("not", params) => bool + case FunctionCallExpression("hi", params) => bool + case FunctionCallExpression("lo", params) => bool case FunctionCallExpression("*", params) => b case FunctionCallExpression("|", params) => b case FunctionCallExpression("&", params) => b @@ -376,13 +378,8 @@ object ExpressionCompiler { case RegisterVariable(Register.AX, _) => exprType.size match { case 1 => if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") AssemblyLine.variable(ctx, LDA, source) ++ List( - AssemblyLine.implied(PHA), - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label), + AssemblyLine.implied(PHA)) ++ signExtendA() ++ List( AssemblyLine.implied(TAX), AssemblyLine.implied(PLA)) } else AssemblyLine.variable(ctx, LDA, source) :+ AssemblyLine.immediate(LDX, 0) @@ -392,13 +389,8 @@ object ExpressionCompiler { case RegisterVariable(Register.AY, _) => exprType.size match { case 1 => if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") AssemblyLine.variable(ctx, LDA, source) ++ List( - AssemblyLine.implied(PHA), - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label), + AssemblyLine.implied(PHA)) ++ signExtendA() ++ List( AssemblyLine.implied(TAY), AssemblyLine.implied(PLA)) } else { @@ -410,13 +402,7 @@ object ExpressionCompiler { case RegisterVariable(Register.XA, _) => exprType.size match { case 1 => if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") - AssemblyLine.variable(ctx, LDX, source) ++ List( - AssemblyLine.implied(TXA), - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) + AssemblyLine.variable(ctx, LDX, source) ++ List(AssemblyLine.implied(TXA)) ++ signExtendA() } else AssemblyLine.variable(ctx, LDX, source) :+ AssemblyLine.immediate(LDA, 0) case 2 => @@ -425,13 +411,7 @@ object ExpressionCompiler { case RegisterVariable(Register.YA, _) => exprType.size match { case 1 => if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") - AssemblyLine.variable(ctx, LDY, source) ++ List( - AssemblyLine.implied(TYA), - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) + AssemblyLine.variable(ctx, LDY, source) ++ List(AssemblyLine.implied(TYA)) ++ signExtendA() } else AssemblyLine.variable(ctx, LDY, source) :+ AssemblyLine.immediate(LDA, 0) case 2 => @@ -444,13 +424,7 @@ object ExpressionCompiler { } else { val copy = List.tabulate(exprType.size)(i => AssemblyLine.variable(ctx, LDA, source, i) ++ AssemblyLine.variable(ctx, STA, target, i)) val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") - List( - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) ++ - List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.variable(ctx, STA, target, i + exprType.size)).flatten + signExtendA() ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.variable(ctx, STA, target, i + exprType.size)).flatten } else { AssemblyLine.immediate(LDA, 0) :: List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.variable(ctx, STA, target, i + exprType.size)).flatten @@ -464,13 +438,7 @@ object ExpressionCompiler { } else { val copy = List.tabulate(exprType.size)(i => AssemblyLine.variable(ctx, LDA, source, i) :+ AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i)) val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") - List( - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) ++ - List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size)) + signExtendA() ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size)) } else { AssemblyLine.immediate(LDA, 0) :: List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size)) @@ -486,15 +454,10 @@ object ExpressionCompiler { case RegisterVariable(Register.AX, _) => exprType.size match { case 1 => if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") List( AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset), - AssemblyLine.implied(PHA), - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label), + AssemblyLine.implied(PHA)) ++ signExtendA() ++ List( AssemblyLine.implied(TAX), AssemblyLine.implied(PLA)) } else List( @@ -550,13 +513,7 @@ object ExpressionCompiler { } else { val copy = List.tabulate(exprType.size)(i => AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset + i) :: AssemblyLine.variable(ctx, STA, target, i)) val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") - List( - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) ++ - List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.variable(ctx, STA, target, i + exprType.size)).flatten + signExtendA() ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.variable(ctx, STA, target, i + exprType.size)).flatten } else { AssemblyLine.immediate(LDA, 0) :: List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.variable(ctx, STA, target, i + exprType.size)).flatten @@ -570,13 +527,7 @@ object ExpressionCompiler { } else { val copy = List.tabulate(exprType.size)(i => List(AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset + i), AssemblyLine.absoluteX(STA, target.baseOffset + i))) val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) { - val label = MfCompiler.nextLabel("sx") - List( - AssemblyLine.immediate(ORA, 0x7F), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) ++ - List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size)) + signExtendA() ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size)) } else { AssemblyLine.immediate(LDA, 0) :: List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size)) @@ -606,13 +557,7 @@ object ExpressionCompiler { AssemblyLine.variable(ctx, STA, target) } else if (target.typ.isSigned) { - val label = MfCompiler.nextLabel("sx") - AssemblyLine.variable(ctx, STA, target) ++ - List( - AssemblyLine.immediate(ORA, 0x7f), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) ++ + AssemblyLine.variable(ctx, STA, target) ++ signExtendA() ++ List.tabulate(target.typ.size - 1)(i => AssemblyLine.variable(ctx, STA, target, i + 1)).flatten } else { AssemblyLine.variable(ctx, STA, target) ++ @@ -734,6 +679,26 @@ object ExpressionCompiler { case "not" => assertBool(ctx, params, 1) compile(ctx, params.head, exprTypeAndVariable, branches.flip) + case "hi" | "lo" => + val hi = name == "hi" + if (params.length != 1) { + ErrorReporting.error("Too many parameters for hi/lo", f.position) + Nil + } else { + val param = params.head + val typ = getExpressionType(ctx, param) + if (typ.size < 1 || typ.size > 2) { + ErrorReporting.error("Invalid parameter type for hi/lo", param.position) + compile(ctx, param, None, BranchSpec.None) + } else { + val compilation = compile(ctx, param, Some(w -> RegisterVariable(Register.AX, w)), BranchSpec.None) + if (hi) { + if (typ.size == 2) compilation :+ AssemblyLine.implied(TXA) + else if (typ.isSigned) compilation ++ signExtendA() + else List(AssemblyLine.immediate(LDA, 0)) + } else compilation + } + } case "&&" => assertBool(ctx, params, 2) val a = params.head @@ -1151,12 +1116,7 @@ object ExpressionCompiler { AssemblyLine.variable(ctx, STA, v) case s if s > 1 => if (t.isSigned) { - val label = MfCompiler.nextLabel("sx") - AssemblyLine.variable(ctx, STA, v)++List( - AssemblyLine.immediate(ORA, 0x7f), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) ++ List.tabulate(s - 1)(i => AssemblyLine.variable(ctx, STA, v, i + 1)).flatten + AssemblyLine.variable(ctx, STA, v) ++ signExtendA() ++ List.tabulate(s - 1)(i => AssemblyLine.variable(ctx, STA, v, i + 1)).flatten } else { AssemblyLine.variable(ctx, STA, v) ++ List(AssemblyLine.immediate(LDA, 0)) ++ List.tabulate(s - 1)(i => AssemblyLine.variable(ctx, STA, v, i + 1)).flatten @@ -1170,13 +1130,11 @@ object ExpressionCompiler { AssemblyLine.variable(ctx, STA, v) ++ AssemblyLine.variable(ctx, STX, v, 1) case s if s > 2 => if (t.isSigned) { - val label = MfCompiler.nextLabel("sx") - AssemblyLine.variable(ctx, STA, v) ++ AssemblyLine.variable(ctx, STX, v, 1) ++ List( - AssemblyLine.implied(TXA), - AssemblyLine.immediate(ORA, 0x7f), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) ++ List.tabulate(s - 2)(i => AssemblyLine.variable(ctx, STA, v, i + 2)).flatten + AssemblyLine.variable(ctx, STA, v) ++ + AssemblyLine.variable(ctx, STX, v, 1) ++ + List(AssemblyLine.implied(TXA)) ++ + signExtendA() ++ + List.tabulate(s - 2)(i => AssemblyLine.variable(ctx, STA, v, i + 2)).flatten } else { AssemblyLine.variable(ctx, STA, v) ++ AssemblyLine.variable(ctx, STX, v, 1) ++ List( AssemblyLine.immediate(LDA, 0)) ++ @@ -1190,14 +1148,11 @@ object ExpressionCompiler { List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset)) case s if s > 1 => if (t.isSigned) { - val label = MfCompiler.nextLabel("sx") List( AssemblyLine.implied(TSX), - AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset), - AssemblyLine.immediate(ORA, 0x7f), - AssemblyLine.relative(BMI, label), - AssemblyLine.immediate(LDA, 0), - AssemblyLine.label(label)) ++ List.tabulate(s - 1)(i => AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset + i + 1)) + AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset)) ++ + signExtendA() ++ + List.tabulate(s - 1)(i => AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset + i + 1)) } else { List( AssemblyLine.implied(TSX), @@ -1297,4 +1252,13 @@ object ExpressionCompiler { Nil } } + + private def signExtendA(): List[AssemblyLine] = { + val label = MfCompiler.nextLabel("sx") + List( + AssemblyLine.immediate(ORA, 0x7F), + AssemblyLine.relative(BMI, label), + AssemblyLine.immediate(LDA, 0), + AssemblyLine.label(label)) + } } diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index eeef7001..62162205 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -723,6 +723,9 @@ class Environment(val parent: Option[Environment], val prefix: String) { } def collectDeclarations(program: Program, options: CompilationOptions): Unit = { + if (options.flag(CompilationFlag.OptimizeForSonicSpeed)) { + addThing(InitializedArray("identity$", None, List.tabulate(256)(n => NumericConstant(n, 1))), None) + } program.declarations.foreach { case f: FunctionDeclarationStatement => registerFunction(f, options) case v: VariableDeclarationStatement => registerVariable(v, options) @@ -774,9 +777,13 @@ class Environment(val parent: Option[Environment], val prefix: String) { case SumExpression(params, _) => nameCheck(params.map(_._2)) case FunctionCallExpression(name, params) => - if (name.exists(_.isLetter) && name != "not") { + if (name.exists(_.isLetter) && !Environment.predefinedFunctions(name)) { checkName[CallableThing]("Function or type", name, node.position) } nameCheck(params) } } + +object Environment { + val predefinedFunctions = Set("not", "hi", "lo") +} diff --git a/src/test/scala/millfork/test/WordMathSuite.scala b/src/test/scala/millfork/test/WordMathSuite.scala index 83db90d8..b6e0ca6a 100644 --- a/src/test/scala/millfork/test/WordMathSuite.scala +++ b/src/test/scala/millfork/test/WordMathSuite.scala @@ -100,4 +100,36 @@ class WordMathSuite extends FunSuite with Matchers { | } """.stripMargin)(m => ()) } + + test("hi()/lo()") { + EmuBenchmarkRun(""" + | array output [7] @$c000 + | void main () { + | output[0] = lo(33) + | output[1] = hi(33) + | output[2] = hi(w($504)) + | output[3] = lo(w($209)) + | output[4] = hi(s(-2)) + | output[5] = lo(s(-2)) + | output[6] = hi(b(200) + b(200)) + | } + | word w(word w) { + | return w + | } + | byte b(byte b) { + | return b + | } + | sbyte s(sbyte s) { + | return s + | } + """.stripMargin){ m => + m.readByte(0xc000) should equal(33) + m.readByte(0xc001) should equal(0) + m.readByte(0xc002) should equal(5) + m.readByte(0xc003) should equal(9) + m.readByte(0xc004) should equal(255) + m.readByte(0xc005) should equal(254) + m.readByte(0xc006) should equal(0) + } + } } diff --git a/src/test/scala/millfork/test/emu/EmuOptimizedInlinedRun.scala b/src/test/scala/millfork/test/emu/EmuOptimizedInlinedRun.scala index eca25e65..eb666ae9 100644 --- a/src/test/scala/millfork/test/emu/EmuOptimizedInlinedRun.scala +++ b/src/test/scala/millfork/test/emu/EmuOptimizedInlinedRun.scala @@ -13,7 +13,10 @@ object EmuOptimizedInlinedRun extends EmuRun( OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ OptimizationPresets.Good, - false) + false) { + override def inline: Boolean = true + override def blastProcessing: Boolean = true +} diff --git a/src/test/scala/millfork/test/emu/EmuRun.scala b/src/test/scala/millfork/test/emu/EmuRun.scala index 4e8c1fe2..e72bda48 100644 --- a/src/test/scala/millfork/test/emu/EmuRun.scala +++ b/src/test/scala/millfork/test/emu/EmuRun.scala @@ -30,6 +30,8 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], def inline = false + def blastProcessing = false + private val timingNmos = Array[Int]( 7, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, @@ -96,6 +98,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], CompilationFlag.InlineFunctions -> this.inline, CompilationFlag.CompactReturnDispatchParams -> true, CompilationFlag.EmitCmosOpcodes -> (platform.cpu == millfork.Cpu.Cmos), + CompilationFlag.OptimizeForSonicSpeed -> blastProcessing // CompilationFlag.CheckIndexOutOfBounds -> true, )) ErrorReporting.hasErrors = false