mirror of
https://github.com/KarolS/millfork.git
synced 2025-03-24 10:33:53 +00:00
Removal of detailed flow analysis; it was slow and not worth it
This commit is contained in:
parent
15dbaad6d1
commit
05e147b880
@ -15,6 +15,9 @@
|
||||
* Added optimizer hints: `inline`, `noinline`, `register`.
|
||||
|
||||
* Added command line flags `--size`, `--fast`, `--blast-processing`.
|
||||
|
||||
* Removed command line flag `--detailed-flow`.
|
||||
Detailed flow analysis was slow, broken, hard to maintain, and didn't even help that much.
|
||||
|
||||
* Added `*'=` and `<<<<` operators.
|
||||
|
||||
|
@ -93,8 +93,6 @@ This may cause problems if the parameter table is stored next to a hardware regi
|
||||
|
||||
* `--blast-processing` – Optimize for speed, even if it increases the size a lot (experimental).
|
||||
|
||||
* `--detailed-flow` – Use detailed flow analysis (experimental). Very computationally expensive and not that great.
|
||||
|
||||
* `--dangerous-optimizations` – Use dangerous optimizations (experimental). Dangerous optimizations are more likely to result in broken code.
|
||||
|
||||
## Warning options
|
||||
|
@ -7,7 +7,7 @@ arch=nmos
|
||||
; modules to load
|
||||
modules=c64_hardware,loader_0801,c64_kernal,c64_panic,stdlib
|
||||
; optionally: default flags
|
||||
emit_illegals=true
|
||||
emit_illegals=false
|
||||
|
||||
|
||||
[allocation]
|
||||
|
@ -121,7 +121,7 @@ object CompilationFlag extends Enumeration {
|
||||
EmitIllegals, EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
ZeropagePseudoregister, DecimalMode, ReadOnlyArrays, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
|
||||
// optimization options:
|
||||
DetailedFlowAnalysis, DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
||||
DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
||||
// memory allocation options
|
||||
VariableOverlap, CompactReturnDispatchParams,
|
||||
// runtime check options
|
||||
|
@ -285,9 +285,6 @@ object Main {
|
||||
c.changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
|
||||
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
||||
}.description("Prefer faster code even if it is much bigger (experimental). Implies --inline.")
|
||||
flag("--detailed-flow").action { c =>
|
||||
c.changeFlag(CompilationFlag.DetailedFlowAnalysis, true)
|
||||
}.description("Use detailed flow analysis (experimental).")
|
||||
flag("--dangerous-optimizations").action { c =>
|
||||
c.changeFlag(CompilationFlag.DangerousOptimizations, true)
|
||||
}.description("Use dangerous optimizations (experimental).")
|
||||
|
@ -36,11 +36,7 @@ object FlowAnalyzer {
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions, req: FlowInfoRequirement.Value): List[(FlowInfo, AssemblyLine)] = {
|
||||
val forwardFlow = req match {
|
||||
case FlowInfoRequirement.BothFlows | FlowInfoRequirement.ForwardFlow =>
|
||||
if (options.flag(CompilationFlag.DetailedFlowAnalysis)) {
|
||||
() => QuantumFlowAnalyzer.analyze(f, code).map(_.collapse)
|
||||
} else {
|
||||
() => CoarseFlowAnalyzer.analyze(f, code, options)
|
||||
}
|
||||
() => CoarseFlowAnalyzer.analyze(f, code, options)
|
||||
case FlowInfoRequirement.BackwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
|
||||
() => List.fill(code.size)(EmptyCpuStatus)
|
||||
}
|
||||
|
@ -1,429 +0,0 @@
|
||||
package millfork.assembly.opt
|
||||
|
||||
import millfork.assembly.{AssemblyLine, OpcodeClasses}
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
||||
|
||||
import scala.collection.immutable.BitSet
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object QCpuStatus {
|
||||
val InitialStatus = QCpuStatus((for {
|
||||
c <- Seq(true, false)
|
||||
v <- Seq(true, false)
|
||||
n <- Seq(true, false)
|
||||
z <- Seq(true, false)
|
||||
} yield QFlagStatus(c = c, d = false, v = v, n = n, z = z) -> QRegStatus(a = QRegStatus.AllValues, x = QRegStatus.AllValues, y = QRegStatus.AllValues, equal = RegEquality.NoEquality)).toMap)
|
||||
|
||||
val UnknownStatus = QCpuStatus((for {
|
||||
c <- Seq(true, false)
|
||||
v <- Seq(true, false)
|
||||
n <- Seq(true, false)
|
||||
z <- Seq(true, false)
|
||||
} yield QFlagStatus(c = c, d = false, v = v, n = n, z = z) -> QRegStatus(a = QRegStatus.AllValues, x = QRegStatus.AllValues, y = QRegStatus.AllValues, equal = RegEquality.UnknownEquality)).toMap)
|
||||
|
||||
def gather(l: List[(QFlagStatus, QRegStatus)]) =
|
||||
QCpuStatus(l.groupBy(_._1).
|
||||
map { case (k, vs) => k -> vs.map(_._2).reduce(_ ++ _) }.
|
||||
filterNot(_._2.isEmpty))
|
||||
}
|
||||
|
||||
case class QCpuStatus(data: Map[QFlagStatus, QRegStatus]) {
|
||||
def collapse: CpuStatus = {
|
||||
val registers = data.values.reduce(_ ++ _)
|
||||
|
||||
def bitset(b: BitSet): Status[Int] = if (b.size == 1) SingleStatus(b.head) else AnyStatus()
|
||||
|
||||
def flag(f: QFlagStatus => Boolean): Status[Boolean] =
|
||||
if (data.keys.forall(k => f(k))) SingleStatus(true)
|
||||
else if (data.keys.forall(k => !f(k))) SingleStatus(false)
|
||||
else AnyStatus()
|
||||
|
||||
CpuStatus(
|
||||
a = bitset(registers.a),
|
||||
x = bitset(registers.x),
|
||||
y = bitset(registers.y),
|
||||
c = flag(_.c),
|
||||
d = flag(_.d),
|
||||
v = flag(_.v),
|
||||
z = flag(_.z),
|
||||
n = flag(_.n),
|
||||
)
|
||||
}
|
||||
|
||||
def changeFlagUnconditionally(f: QFlagStatus => QFlagStatus): QCpuStatus = {
|
||||
QCpuStatus.gather(data.toList.map { case (k, v) => f(k) -> v })
|
||||
}
|
||||
|
||||
def changeFlagsInAnUnknownWay(f: QFlagStatus => QFlagStatus, g: QFlagStatus => QFlagStatus): QCpuStatus = {
|
||||
QCpuStatus.gather(data.toList.flatMap { case (k, v) => List(f(k) -> v, g(k) -> v) })
|
||||
}
|
||||
|
||||
def changeFlagsInAnUnknownWay(f: QFlagStatus => QFlagStatus, g: QFlagStatus => QFlagStatus, h: QFlagStatus => QFlagStatus): QCpuStatus = {
|
||||
QCpuStatus.gather(data.toList.flatMap { case (k, v) => List(f(k) -> v, g(k) -> v, h(k) -> v) })
|
||||
}
|
||||
|
||||
def mapRegisters(f: QRegStatus => QRegStatus): QCpuStatus = {
|
||||
QCpuStatus(data.map { case (k, v) => k -> f(v) })
|
||||
}
|
||||
|
||||
def mapRegisters(f: (QFlagStatus, QRegStatus) => QRegStatus): QCpuStatus = {
|
||||
QCpuStatus(data.map { case (k, v) => k -> f(k, v) })
|
||||
}
|
||||
|
||||
def flatMap(f: (QFlagStatus, QRegStatus) => List[(QFlagStatus, QRegStatus)]): QCpuStatus = {
|
||||
QCpuStatus.gather(data.toList.flatMap { case (k, v) => f(k, v) })
|
||||
}
|
||||
|
||||
def changeNZFromA: QCpuStatus = {
|
||||
QCpuStatus.gather(data.toList.flatMap { case (k, v) =>
|
||||
List(
|
||||
k.copy(n = false, z = false) -> v.whereA(i => i.toByte > 0),
|
||||
k.copy(n = true, z = false) -> v.whereA(i => i.toByte < 0),
|
||||
k.copy(n = false, z = true) -> v.whereA(i => i.toByte == 0))
|
||||
})
|
||||
}
|
||||
|
||||
def changeNZFromX: QCpuStatus = {
|
||||
QCpuStatus.gather(data.toList.flatMap { case (k, v) =>
|
||||
List(
|
||||
k.copy(n = false, z = false) -> v.whereX(i => i.toByte > 0),
|
||||
k.copy(n = true, z = false) -> v.whereX(i => i.toByte < 0),
|
||||
k.copy(n = false, z = true) -> v.whereX(i => i.toByte == 0))
|
||||
})
|
||||
}
|
||||
|
||||
def changeNZFromY: QCpuStatus = {
|
||||
QCpuStatus.gather(data.toList.flatMap { case (k, v) =>
|
||||
List(
|
||||
k.copy(n = false, z = false) -> v.whereY(i => i.toByte > 0),
|
||||
k.copy(n = true, z = false) -> v.whereY(i => i.toByte < 0),
|
||||
k.copy(n = false, z = true) -> v.whereY(i => i.toByte == 0))
|
||||
})
|
||||
}
|
||||
|
||||
def ~(that: QCpuStatus): QCpuStatus = QCpuStatus.gather(this.data.toList ++ that.data.toList)
|
||||
}
|
||||
|
||||
object QRegStatus {
|
||||
val NoValues: BitSet = BitSet.empty
|
||||
val AllValues: BitSet = BitSet.fromBitMask(Array(-1L, -1L, -1L, -1L))
|
||||
|
||||
}
|
||||
|
||||
object RegEquality extends Enumeration {
|
||||
val NoEquality, AX, AY, XY, AXY, UnknownEquality = Value
|
||||
|
||||
def or(a: Value, b: Value) = {
|
||||
(a, b) match {
|
||||
case (UnknownEquality, _) => b
|
||||
case (_, UnknownEquality) => a
|
||||
case (NoEquality, _) => NoEquality
|
||||
case (_, NoEquality) => NoEquality
|
||||
case (_, _) if a == b => a
|
||||
case (AXY, _) => b
|
||||
case (_, AXY) => a
|
||||
case _ => NoEquality
|
||||
}
|
||||
}
|
||||
|
||||
def afterTransfer(a: Value, b: Value) = {
|
||||
(a, b) match {
|
||||
case (UnknownEquality, _) => b
|
||||
case (_, UnknownEquality) => a
|
||||
case (NoEquality, _) => b
|
||||
case (_, NoEquality) => a
|
||||
case (_, _) if a == b => a
|
||||
case _ => AXY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class QRegStatus(a: BitSet, x: BitSet, y: BitSet, equal: RegEquality.Value) {
|
||||
def isEmpty: Boolean = a.isEmpty || x.isEmpty || y.isEmpty
|
||||
|
||||
def ++(that: QRegStatus) = QRegStatus(
|
||||
a = a ++ that.a,
|
||||
x = x ++ that.x,
|
||||
y = y ++ that.y,
|
||||
equal = RegEquality.or(equal, that.equal))
|
||||
|
||||
def afterTransfer(transfer: RegEquality.Value): QRegStatus =
|
||||
copy(equal = RegEquality.afterTransfer(equal, transfer))
|
||||
|
||||
def changeA(f: Int => Long): QRegStatus = {
|
||||
val newA = a.map(i => f(i).toInt & 0xff)
|
||||
val newEqual = equal match {
|
||||
case RegEquality.XY => RegEquality.XY
|
||||
case RegEquality.AXY => RegEquality.XY
|
||||
case _ => RegEquality.NoEquality
|
||||
}
|
||||
QRegStatus(newA, x, y, newEqual)
|
||||
}
|
||||
|
||||
def changeX(f: Int => Long): QRegStatus = {
|
||||
val newA = a.map(i => f(i).toInt & 0xff)
|
||||
val newEqual = equal match {
|
||||
case RegEquality.XY => RegEquality.XY
|
||||
case RegEquality.AXY => RegEquality.XY
|
||||
case _ => RegEquality.NoEquality
|
||||
}
|
||||
QRegStatus(newA, x, y, newEqual)
|
||||
}
|
||||
|
||||
def changeY(f: Int => Long): QRegStatus = {
|
||||
val newA = a.map(i => f(i).toInt & 0xff)
|
||||
val newEqual = equal match {
|
||||
case RegEquality.XY => RegEquality.XY
|
||||
case RegEquality.AXY => RegEquality.XY
|
||||
case _ => RegEquality.NoEquality
|
||||
}
|
||||
QRegStatus(newA, x, y, newEqual)
|
||||
}
|
||||
|
||||
def whereA(f: Int => Boolean): QRegStatus =
|
||||
equal match {
|
||||
case RegEquality.AXY =>
|
||||
copy(a = a.filter(f), x = x.filter(f), y = y.filter(f))
|
||||
case RegEquality.AY =>
|
||||
copy(a = a.filter(f), y = y.filter(f))
|
||||
case RegEquality.AX =>
|
||||
copy(a = a.filter(f), x = x.filter(f))
|
||||
case _ =>
|
||||
copy(a = a.filter(f))
|
||||
}
|
||||
|
||||
def whereX(f: Int => Boolean): QRegStatus =
|
||||
equal match {
|
||||
case RegEquality.AXY =>
|
||||
copy(a = a.filter(f), x = x.filter(f), y = y.filter(f))
|
||||
case RegEquality.XY =>
|
||||
copy(x = x.filter(f), y = y.filter(f))
|
||||
case RegEquality.AX =>
|
||||
copy(a = a.filter(f), x = x.filter(f))
|
||||
case _ =>
|
||||
copy(x = x.filter(f))
|
||||
}
|
||||
|
||||
def whereY(f: Int => Boolean): QRegStatus =
|
||||
equal match {
|
||||
case RegEquality.AXY =>
|
||||
copy(a = a.filter(f), x = x.filter(f), y = y.filter(f))
|
||||
case RegEquality.AY =>
|
||||
copy(a = a.filter(f), y = y.filter(f))
|
||||
case RegEquality.XY =>
|
||||
copy(x = x.filter(f), y = y.filter(f))
|
||||
case _ =>
|
||||
copy(y = y.filter(f))
|
||||
}
|
||||
}
|
||||
|
||||
case class QFlagStatus(c: Boolean, d: Boolean, v: Boolean, z: Boolean, n: Boolean)
|
||||
|
||||
object QuantumFlowAnalyzer {
|
||||
private def loBit(b: Boolean) = if (b) 1 else 0
|
||||
|
||||
private def hiBit(b: Boolean) = if (b) 0x80 else 0
|
||||
|
||||
//noinspection RedundantNewCaseClass
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine]): List[QCpuStatus] = {
|
||||
val flagArray = Array.fill[QCpuStatus](code.length)(QCpuStatus.UnknownStatus)
|
||||
val codeArray = code.toArray
|
||||
|
||||
var changed = true
|
||||
while (changed) {
|
||||
changed = false
|
||||
var currentStatus: QCpuStatus = if (f.interrupt) QCpuStatus.UnknownStatus else QCpuStatus.UnknownStatus
|
||||
for (i <- codeArray.indices) {
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
if (flagArray(i) != currentStatus) {
|
||||
changed = true
|
||||
flagArray(i) = currentStatus
|
||||
}
|
||||
codeArray(i) match {
|
||||
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) =>
|
||||
val L = l
|
||||
currentStatus = codeArray.indices.flatMap(j => codeArray(j) match {
|
||||
case AssemblyLine(_, _, MemoryAddressConstant(Label(L)), _) => Some(flagArray(j))
|
||||
case _ => None
|
||||
}).fold(QCpuStatus.UnknownStatus)(_ ~ _)
|
||||
|
||||
case AssemblyLine(BCC, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(c = true))
|
||||
case AssemblyLine(BCS, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(c = false))
|
||||
case AssemblyLine(BVS, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(v = false))
|
||||
case AssemblyLine(BVC, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(v = true))
|
||||
case AssemblyLine(BMI, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(n = false))
|
||||
case AssemblyLine(BPL, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(n = true))
|
||||
case AssemblyLine(BEQ, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(z = false))
|
||||
case AssemblyLine(BNE, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(z = true))
|
||||
|
||||
case AssemblyLine(SED, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(d = true))
|
||||
case AssemblyLine(SEC, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(c = true))
|
||||
case AssemblyLine(CLD, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(d = false))
|
||||
case AssemblyLine(CLC, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(c = false))
|
||||
case AssemblyLine(CLV, _, _, _) =>
|
||||
currentStatus = currentStatus.changeFlagUnconditionally(f => f.copy(v = false))
|
||||
|
||||
case AssemblyLine(JSR, _, _, _) =>
|
||||
currentStatus = QCpuStatus.InitialStatus
|
||||
|
||||
case AssemblyLine(LDX, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeX(_ => n)).changeNZFromX
|
||||
case AssemblyLine(LDY, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeY(_ => n)).changeNZFromY
|
||||
case AssemblyLine(LDA, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeA(_ => n)).changeNZFromA
|
||||
case AssemblyLine(LAX, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeA(_ => n).changeX(_ => n).afterTransfer(RegEquality.AX)).changeNZFromA
|
||||
|
||||
case AssemblyLine(EOR, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeA(_ ^ n)).changeNZFromA
|
||||
case AssemblyLine(AND, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeA(_ & n)).changeNZFromA
|
||||
case AssemblyLine(ANC, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeA(_ & n)).changeNZFromA.changeFlagUnconditionally(f => f.copy(c = f.z))
|
||||
case AssemblyLine(ORA, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeA(_ | n)).changeNZFromA
|
||||
|
||||
case AssemblyLine(INX, Implied, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeX(_ + 1)).changeNZFromX
|
||||
case AssemblyLine(DEX, Implied, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeX(_ - 1)).changeNZFromX
|
||||
case AssemblyLine(INY, Implied, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeY(_ - 1)).changeNZFromY
|
||||
case AssemblyLine(DEY, Implied, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeY(_ - 1)).changeNZFromY
|
||||
case AssemblyLine(INC, Implied, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeA(_ - 1)).changeNZFromA
|
||||
case AssemblyLine(DEC, Implied, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.changeA(_ - 1)).changeNZFromA
|
||||
case AssemblyLine(TAX, _, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.copy(x = r.a).afterTransfer(RegEquality.AX)).changeNZFromX
|
||||
case AssemblyLine(TXA, _, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.copy(a = r.x).afterTransfer(RegEquality.AX)).changeNZFromA
|
||||
case AssemblyLine(TAY, _, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.copy(y = r.a).afterTransfer(RegEquality.AY)).changeNZFromY
|
||||
case AssemblyLine(TYA, _, _, _) =>
|
||||
currentStatus = currentStatus.mapRegisters(r => r.copy(a = r.y).afterTransfer(RegEquality.AY)).changeNZFromA
|
||||
|
||||
case AssemblyLine(ROL, Implied, _, _) =>
|
||||
currentStatus = currentStatus.flatMap((f, r) => List(
|
||||
f.copy(c = true) -> r.whereA(a => (a & 0x80) != 0).changeA(a => a * 2 + loBit(f.c)),
|
||||
f.copy(c = false) -> r.whereA(a => (a & 0x80) == 0).changeA(a => a * 2 + loBit(f.c)),
|
||||
)).changeNZFromA
|
||||
case AssemblyLine(ROR, Implied, _, _) =>
|
||||
currentStatus = currentStatus.flatMap((f, r) => List(
|
||||
f.copy(c = true) -> r.whereA(a => (a & 1) != 0).changeA(a => (a >>> 2) & 0x7f | hiBit(f.c)),
|
||||
f.copy(c = false) -> r.whereA(a => (a & 1) == 0).changeA(a => (a >>> 2) & 0x7f | hiBit(f.c)),
|
||||
)).changeNZFromA
|
||||
case AssemblyLine(ASL, Implied, _, _) =>
|
||||
currentStatus = currentStatus.flatMap((f, r) => List(
|
||||
f.copy(c = true) -> r.whereA(a => (a & 0x80) != 0).changeA(a => a * 2),
|
||||
f.copy(c = false) -> r.whereA(a => (a & 0x80) == 0).changeA(a => a * 2),
|
||||
)).changeNZFromA
|
||||
case AssemblyLine(LSR, Implied, _, _) =>
|
||||
currentStatus = currentStatus.flatMap((f, r) => List(
|
||||
f.copy(c = true) -> r.whereA(a => (a & 1) != 0).changeA(a => (a >>> 1) & 0x7f),
|
||||
f.copy(c = false) -> r.whereA(a => (a & 1) == 0).changeA(a => (a >>> 1) & 0x7f),
|
||||
)).changeNZFromA
|
||||
case AssemblyLine(ALR, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.flatMap((f, r) => List(
|
||||
f.copy(c = true) -> r.whereA(a => (a & n & 1) != 0).changeA(a => ((a & n) >>> 2) & 0x7f),
|
||||
f.copy(c = false) -> r.whereA(a => (a & n & 1) == 0).changeA(a => ((a & n) >>> 2) & 0x7f),
|
||||
)).changeNZFromA
|
||||
case AssemblyLine(ADC, Immediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn & 0xff
|
||||
currentStatus = currentStatus.flatMap((f, r) =>
|
||||
if (f.d) {
|
||||
val regs = r.copy(a = QRegStatus.AllValues).changeA(_.toLong)
|
||||
List(
|
||||
f.copy(c = false, v = false) -> regs,
|
||||
f.copy(c = true, v = false) -> regs,
|
||||
f.copy(c = false, v = true) -> regs,
|
||||
f.copy(c = true, v = true) -> regs,
|
||||
)
|
||||
} else {
|
||||
if (f.c) {
|
||||
val regs = r.changeA(_ + n + 1)
|
||||
List(
|
||||
f.copy(c = false, v = false) -> regs.whereA(_ >= n),
|
||||
f.copy(c = true, v = false) -> regs.whereA(_ < n),
|
||||
f.copy(c = false, v = true) -> regs.whereA(_ >= n),
|
||||
f.copy(c = true, v = true) -> regs.whereA(_ < n),
|
||||
)
|
||||
} else {
|
||||
val regs = r.changeA(_ + n)
|
||||
List(
|
||||
f.copy(c = false, v = false) -> regs.whereA(_ > n),
|
||||
f.copy(c = true, v = false) -> regs.whereA(_ <= n),
|
||||
f.copy(c = false, v = true) -> regs.whereA(_ > n),
|
||||
f.copy(c = true, v = true) -> regs.whereA(_ <= n),
|
||||
)
|
||||
}
|
||||
}
|
||||
).changeNZFromA
|
||||
case AssemblyLine(SBC, Immediate, NumericConstant(n, _), _) =>
|
||||
currentStatus = currentStatus.flatMap((f, r) =>
|
||||
if (f.d) {
|
||||
val regs = r.copy(a = QRegStatus.AllValues).changeA(_.toLong)
|
||||
// TODO: guess the carry flag correctly
|
||||
List(
|
||||
f.copy(c = false, v = false) -> regs,
|
||||
f.copy(c = true, v = false) -> regs,
|
||||
f.copy(c = false, v = true) -> regs,
|
||||
f.copy(c = true, v = true) -> regs,
|
||||
)
|
||||
} else {
|
||||
val regs = if (f.c) r.changeA(_ - n) else r.changeA(_ - n - 1)
|
||||
List(
|
||||
f.copy(c = false, v = false) -> regs,
|
||||
f.copy(c = true, v = false) -> regs,
|
||||
f.copy(c = false, v = true) -> regs,
|
||||
f.copy(c = true, v = true) -> regs,
|
||||
)
|
||||
}
|
||||
).changeNZFromA
|
||||
|
||||
case AssemblyLine(opcode, addrMode, parameter, _) =>
|
||||
if (OpcodeClasses.ChangesX(opcode)) currentStatus = currentStatus.mapRegisters(r => r.copy(x = QRegStatus.AllValues))
|
||||
if (OpcodeClasses.ChangesY(opcode)) currentStatus = currentStatus.mapRegisters(r => r.copy(y = QRegStatus.AllValues))
|
||||
if (OpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.mapRegisters(r => r.copy(a = QRegStatus.AllValues))
|
||||
if (addrMode == Implied && OpcodeClasses.ChangesAIfImplied(opcode)) currentStatus = currentStatus.mapRegisters(r => r.copy(a = QRegStatus.AllValues))
|
||||
if (OpcodeClasses.ChangesNAndZ(opcode)) currentStatus = currentStatus.changeFlagsInAnUnknownWay(
|
||||
_.copy(n = false, z = false),
|
||||
_.copy(n = true, z = false),
|
||||
_.copy(n = false, z = true))
|
||||
if (OpcodeClasses.ChangesC(opcode)) currentStatus = currentStatus.changeFlagsInAnUnknownWay(_.copy(c = false), _.copy(c = true))
|
||||
if (OpcodeClasses.ChangesV(opcode)) currentStatus = currentStatus.changeFlagsInAnUnknownWay(_.copy(v = false), _.copy(v = true))
|
||||
if (opcode == CMP || opcode == CPX || opcode == CPY) {
|
||||
if (addrMode == Immediate) parameter match {
|
||||
case NumericConstant(0, _) => currentStatus = currentStatus.changeFlagUnconditionally(_.copy(c = true))
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// flagArray.zip(codeArray).foreach{
|
||||
// case (fl, y) => if (y.isPrintable) println(f"$fl%-32s $y%-32s")
|
||||
// }
|
||||
// println("---------------------")
|
||||
}
|
||||
|
||||
flagArray.toList
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Array assignment with offset 1") {
|
||||
val m = new EmuRun(Cpu.StrictMos, Nil, DangerousOptimizations.All ++ OptimizationPresets.Good, true)(
|
||||
val m = new EmuRun(Cpu.StrictMos, Nil, DangerousOptimizations.All ++ OptimizationPresets.Good)(
|
||||
"""
|
||||
| array output [8] @$c000
|
||||
| void main () {
|
||||
|
@ -112,7 +112,7 @@ class AssemblyOptimizationSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("LDA-TAY elimination") {
|
||||
new EmuRun(Cpu.StrictMos, OptimizationPresets.NodeOpt, List(VariableToRegisterOptimization, AlwaysGoodOptimizations.YYY), false)(
|
||||
new EmuRun(Cpu.StrictMos, OptimizationPresets.NodeOpt, List(VariableToRegisterOptimization, AlwaysGoodOptimizations.YYY))(
|
||||
"""
|
||||
| array mouse_pointer[64]
|
||||
| array arrow[64]
|
||||
@ -204,7 +204,7 @@ class AssemblyOptimizationSuite extends FunSuite with Matchers {
|
||||
AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn,
|
||||
AlwaysGoodOptimizations.IdempotentDuplicateRemoval,
|
||||
AlwaysGoodOptimizations.IdempotentDuplicateRemoval,
|
||||
AlwaysGoodOptimizations.IdempotentDuplicateRemoval), false)(
|
||||
AlwaysGoodOptimizations.IdempotentDuplicateRemoval))(
|
||||
"""
|
||||
| byte output @$C000
|
||||
| void main(){ delta() }
|
||||
@ -385,7 +385,7 @@ class AssemblyOptimizationSuite extends FunSuite with Matchers {
|
||||
AlwaysGoodOptimizations.IdempotentDuplicateRemoval,
|
||||
AlwaysGoodOptimizations.IdempotentDuplicateRemoval,
|
||||
AlwaysGoodOptimizations.IdempotentDuplicateRemoval,
|
||||
AlwaysGoodOptimizations.CommonExpressionInConditional), false)(
|
||||
AlwaysGoodOptimizations.CommonExpressionInConditional))(
|
||||
"""
|
||||
| byte output @$C000
|
||||
| void main(){
|
||||
|
@ -11,5 +11,4 @@ object EmuNodeOptimizedRun extends EmuRun(
|
||||
List(
|
||||
UnreachableCode,
|
||||
UnusedLocalVariables),
|
||||
Nil,
|
||||
false)
|
||||
Nil)
|
@ -14,8 +14,7 @@ object EmuOptimized65816Run extends EmuRun(
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good,
|
||||
false)
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good)
|
||||
|
||||
|
||||
|
||||
|
@ -12,8 +12,7 @@ object EmuOptimized65CE02Run extends EmuRun(
|
||||
OptimizationPresets.AssOpt ++
|
||||
CmosOptimizations.All ++ CE02Optimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ CE02Optimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ CE02Optimizations.All ++ OptimizationPresets.Good,
|
||||
false)
|
||||
CmosOptimizations.All ++ CE02Optimizations.All ++ OptimizationPresets.Good)
|
||||
|
||||
|
||||
|
||||
|
@ -14,8 +14,7 @@ object EmuOptimizedCmosRun extends EmuRun(
|
||||
CmosOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ OptimizationPresets.Good ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
CmosOptimizations.All ++ OptimizationPresets.Good,
|
||||
false)
|
||||
CmosOptimizations.All ++ OptimizationPresets.Good)
|
||||
|
||||
|
||||
|
||||
|
@ -12,8 +12,7 @@ object EmuOptimizedHudsonRun extends EmuRun(
|
||||
OptimizationPresets.AssOpt ++
|
||||
CmosOptimizations.All ++ HudsonOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ HudsonOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ HudsonOptimizations.All ++ OptimizationPresets.Good,
|
||||
false)
|
||||
CmosOptimizations.All ++ HudsonOptimizations.All ++ OptimizationPresets.Good)
|
||||
|
||||
|
||||
|
||||
|
@ -14,8 +14,7 @@ object EmuOptimizedInlinedRun extends EmuRun(
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good,
|
||||
false) {
|
||||
OptimizationPresets.Good) {
|
||||
override def inline: Boolean = true
|
||||
override def blastProcessing: Boolean = false
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ object EmuOptimizedRun extends EmuRun(
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good,
|
||||
false)
|
||||
OptimizationPresets.Good)
|
||||
|
||||
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.assembly.opt.{LaterOptimizations, ZeropageRegisterOptimizations}
|
||||
import millfork.{Cpu, OptimizationPresets}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuQuantumOptimizedRun extends EmuRun(
|
||||
Cpu.StrictMos,
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good,
|
||||
true)
|
||||
|
||||
|
||||
|
@ -24,7 +24,7 @@ import scala.collection.JavaConverters._
|
||||
*/
|
||||
case class Timings(nmos: Long, cmos: Long)
|
||||
|
||||
class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization], quantum: Boolean) extends Matchers {
|
||||
class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization]) extends Matchers {
|
||||
|
||||
def apply(source: String): MemoryBank = {
|
||||
apply2(source)._2
|
||||
@ -98,7 +98,6 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
val platform = EmuPlatform.get(cpu)
|
||||
val options = CompilationOptions(platform, Map(
|
||||
CompilationFlag.EmitIllegals -> this.emitIllegals,
|
||||
CompilationFlag.DetailedFlowAnalysis -> quantum,
|
||||
CompilationFlag.InlineFunctions -> this.inline,
|
||||
CompilationFlag.CompactReturnDispatchParams -> true,
|
||||
CompilationFlag.ZeropagePseudoregister -> true,
|
||||
|
@ -6,11 +6,10 @@ import millfork.{Cpu, OptimizationPresets}
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuSuperQuantumOptimizedInliningRun extends EmuRun(
|
||||
object EmuSuperOptimizedInliningRun extends EmuRun(
|
||||
Cpu.StrictMos,
|
||||
OptimizationPresets.NodeOpt,
|
||||
List(SuperOptimizer),
|
||||
true) {
|
||||
List(SuperOptimizer)) {
|
||||
override def inline = true
|
||||
}
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.assembly.opt.SuperOptimizer
|
||||
import millfork.{Cpu, OptimizationPresets}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
// TODO : it doesn't work
|
||||
object EmuSuperQuantumOptimizedRun extends EmuRun(
|
||||
Cpu.StrictMos,
|
||||
OptimizationPresets.NodeOpt,
|
||||
List(SuperOptimizer),
|
||||
true)
|
||||
|
||||
|
||||
|
@ -9,8 +9,7 @@ import millfork.{Cpu, OptimizationPresets}
|
||||
object EmuSuperOptimizedRun extends EmuRun(
|
||||
Cpu.StrictMos,
|
||||
OptimizationPresets.NodeOpt,
|
||||
List(SuperOptimizer),
|
||||
false)
|
||||
List(SuperOptimizer))
|
||||
|
||||
|
||||
|
||||
|
@ -11,22 +11,16 @@ object EmuUltraBenchmarkRun {
|
||||
val (Timings(t1, _), m1) = EmuOptimizedRun.apply2(source)
|
||||
val (Timings(ti, _), mi) = EmuOptimizedInlinedRun.apply2(source)
|
||||
val (Timings(t2, _), m2) = EmuSuperOptimizedRun.apply2(source)
|
||||
val (Timings(t3, _), m3) = EmuQuantumOptimizedRun.apply2(source)
|
||||
val (Timings(t4, _), m4) = EmuSuperQuantumOptimizedRun.apply2(source)
|
||||
val (Timings(t5, _), m5) = EmuSuperQuantumOptimizedInliningRun.apply2(source)
|
||||
val (Timings(t5, _), m5) = EmuSuperOptimizedInliningRun.apply2(source)
|
||||
println(f"Before optimization: $t0%7d")
|
||||
println(f"After optimization: $t1%7d")
|
||||
println(f"After inlining: $ti%7d")
|
||||
println(f"After superopt.: $t2%7d")
|
||||
println(f"After quantum: $t3%7d")
|
||||
println(f"After superquantum: $t4%7d")
|
||||
println(f"After SQ+inlining: $t5%7d")
|
||||
println(f"After super. + inl.: $t5%7d")
|
||||
println(f"Gain: ${(100L*(t0-t1)/t0.toDouble).round}%7d%%")
|
||||
println(f"Inlining gain: ${(100L*(t0-ti)/t0.toDouble).round}%7d%%")
|
||||
println(f"Superopt. gain: ${(100L*(t0-t2)/t0.toDouble).round}%7d%%")
|
||||
println(f"Quantum gain: ${(100L*(t0-t3)/t0.toDouble).round}%7d%%")
|
||||
println(f"Super quantum gain: ${(100L*(t0-t4)/t0.toDouble).round}%7d%%")
|
||||
println(f"SQ+inlining gain: ${(100L*(t0-t5)/t0.toDouble).round}%7d%%")
|
||||
println(f"Super. + inl. gain: ${(100L*(t0-t5)/t0.toDouble).round}%7d%%")
|
||||
println(f"Running unoptimized")
|
||||
verifier(m0)
|
||||
println(f"Running optimized")
|
||||
@ -35,11 +29,7 @@ object EmuUltraBenchmarkRun {
|
||||
verifier(mi)
|
||||
println(f"Running superoptimized")
|
||||
verifier(m2)
|
||||
println(f"Running quantum optimized")
|
||||
verifier(m3)
|
||||
println(f"Running superquantum optimized")
|
||||
verifier(m4)
|
||||
println(f"Running superquantum optimized inlined")
|
||||
println(f"Running superoptimized inlined")
|
||||
verifier(m5)
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,7 @@ object EmuUndocumentedRun extends EmuRun(
|
||||
UndocumentedOptimizations.All ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
UndocumentedOptimizations.All ++
|
||||
OptimizationPresets.Good,
|
||||
false) {
|
||||
OptimizationPresets.Good) {
|
||||
|
||||
override def emitIllegals = true
|
||||
}
|
||||
|
@ -6,4 +6,4 @@ import millfork.Cpu
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuUnoptimizedRun extends EmuRun(Cpu.StrictMos, Nil, Nil, false)
|
||||
object EmuUnoptimizedRun extends EmuRun(Cpu.StrictMos, Nil, Nil)
|
Loading…
x
Reference in New Issue
Block a user