1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-30 21:29:36 +00:00

hi()/lo() builtins; identity table for using index registers as operands

This commit is contained in:
Karol Stasiak 2018-02-28 01:13:05 +01:00
parent f31086e686
commit 3dc526bcb7
6 changed files with 179 additions and 152 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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