1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-08-12 11:29:20 +00:00

Compiler performance improvements

This commit is contained in:
Karol Stasiak 2018-12-16 14:38:57 +01:00
parent 0e5d79f222
commit badd7ef1d8
15 changed files with 328 additions and 137 deletions

View File

@ -3,7 +3,7 @@ package millfork.assembly.mos.opt
import millfork.assembly.OptimizationContext
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses}
import millfork.assembly.opt.AnyStatus
import millfork.assembly.opt.{AnyStatus, FlowCache}
import millfork.env._
/**
@ -11,7 +11,10 @@ import millfork.env._
*/
object CoarseFlowAnalyzer {
val cache = new FlowCache[AssemblyLine, CpuStatus]("mos forward")
def analyze(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[CpuStatus] = {
cache.get(code).foreach(return _)
val compilationOptions = optimizationContext.options
val niceFunctionProperties = optimizationContext.niceFunctionProperties
val ceFlag = compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)
@ -124,6 +127,6 @@ object CoarseFlowAnalyzer {
// println("---------------------")
}
flagArray.toList
cache.put(code, flagArray.toList)
}
}

View File

@ -15,11 +15,11 @@ import scala.collection.mutable
object JumpFollowing {
def apply(options: CompilationOptions, code: List[AssemblyLine]): List[AssemblyLine] = {
val labelsToRts = mutable.Set[String]()
val labelsToRti = mutable.Set[String]()
val labelsToRtl = mutable.Set[String]()
val labelsToJumps = mutable.Map[String, String]()
val currentLabels = mutable.Set[String]()
val labelsToRts = new mutable.HashSet[String]()
val labelsToRti = new mutable.HashSet[String]()
val labelsToRtl = new mutable.HashSet[String]()
val labelsToJumps = new mutable.HashMap[String, String]()
val currentLabels = new mutable.HashSet[String]()
for (line <- code) {
line match {
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(label))) =>

View File

@ -1,14 +1,11 @@
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly._
import millfork.assembly.mos._
import millfork.assembly.opt.FlowCache
import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node.MosRegister
import scala.collection.immutable
/**
* @author Karol Stasiak
*/
@ -51,6 +48,22 @@ case class CpuImportance(a: Importance = UnknownImportance,
r2: Importance = UnknownImportance,
r3: Importance = UnknownImportance,
) {
def setPseudoRegister(regOffset: Int, importance: Importance): CpuImportance = regOffset match {
case 0 => this.copy(r0 = importance)
case 1 => this.copy(r1 = importance)
case 2 => this.copy(r2 = importance)
case 3 => this.copy(r3 = importance)
case _ => this
}
def setPseudoRegisterWord(regOffset: Int, importance: Importance): CpuImportance = regOffset match {
case 0 => this.copy(r0 = importance, r1 = importance)
case 1 => this.copy(r1 = importance, r2 = importance)
case 2 => this.copy(r2 = importance, r3 = importance)
case 3 => this.copy(r3 = importance)
case _ => this
}
override def toString: String = s"A=$a,B=$ah,X=$x,Y=$y,Z=$iz; Z=$z,N=$n,C=$c,V=$v,D=$d,M=$m,X=$w; R0=$r0,R1=$r1,R2=$r2,R3=$r3"
def ~(that: CpuImportance) = new CpuImportance(
@ -98,9 +111,11 @@ case class CpuImportance(a: Importance = UnknownImportance,
object ReverseFlowAnalyzer {
val cache = new FlowCache[AssemblyLine, CpuImportance]("mos reverse")
val functionsThatReadC = Set("__adc_decimal", "__sbc_decimal")
private val aluAdders = Set(Opcode.ADC, Opcode.SBC, Opcode.ISC, Opcode.DCP, Opcode.ADC_W, Opcode.SBC_W)
private val actuallyRead = Set(AddrMode.IndexedZ, AddrMode.IndexedSY, AddrMode.IndexedY, AddrMode.LongIndexedY, AddrMode.LongIndexedZ, AddrMode.IndexedX, AddrMode.Indirect, AddrMode.AbsoluteIndexedX)
private val readAsPointer = Set(AddrMode.IndexedZ, AddrMode.IndexedSY, AddrMode.IndexedY, AddrMode.LongIndexedY, AddrMode.LongIndexedZ, AddrMode.IndexedX, AddrMode.Indirect, AddrMode.AbsoluteIndexedX)
private val absoluteLike = Set(AddrMode.ZeroPage, AddrMode.Absolute, AddrMode.LongAbsolute)
private val importanceBeforeJsr: CpuImportance = CpuImportance(
a = Unimportant,
@ -128,6 +143,7 @@ object ReverseFlowAnalyzer {
//noinspection RedundantNewCaseClass
def analyze(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[CpuImportance] = {
cache.get(code).foreach(return _)
val niceFunctionProperties = optimizationContext.niceFunctionProperties
val importanceArray = Array.fill[CpuImportance](code.length)(new CpuImportance())
val codeArray = code.toArray
@ -297,65 +313,38 @@ object ReverseFlowAnalyzer {
else if (addrMode == IndexedZ /*|| addrMode == LongIndexedZ*/ )
currentImportance = currentImportance.copy(iz = Important)
}
if (absoluteLike(currentLine.addrMode)) {
if (OpcodeClasses.StoresByte(currentLine.opcode)) {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(3, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r3 = Unimportant)
case _ => ()
}
}
if (OpcodeClasses.StoresWord(currentLine.opcode)) {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Unimportant, r1 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Unimportant, r2 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Unimportant, r3 = Unimportant)
case _ => ()
}
}
}
if (actuallyRead(currentLine.addrMode)) {
currentLine.parameter match {
val isAbsoluteLike = absoluteLike(currentLine.addrMode)
val isReadAsPointer = readAsPointer(currentLine.addrMode)
if (isAbsoluteLike || isReadAsPointer) {
(currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important, r1 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Important, r2 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Important, r3 = Important)
case _ => ()
}
} else if (OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(currentLine.opcode)) {
if (OpcodeClasses.AccessesWordInMemory(currentLine.opcode)) {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important, r1 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Important, r2 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Important, r3 = Important)
case _ => ()
}
} else {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(3, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r3 = Important)
case _ => ()
}
if th.name == "__reg" =>
Some(0)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(n, _))
if th.name == "__reg" =>
Some(n.toInt)
case _ =>
None
}) match {
case None =>
case Some(regOffset) =>
if (isAbsoluteLike) {
if (OpcodeClasses.StoresByte(currentLine.opcode)) {
currentImportance = currentImportance.setPseudoRegister(regOffset, Unimportant)
} else if (OpcodeClasses.StoresWord(currentLine.opcode)) {
currentImportance = currentImportance.setPseudoRegisterWord(regOffset, Unimportant)
}
if (OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(currentLine.opcode)) {
if (OpcodeClasses.AccessesWordInMemory(currentLine.opcode)) {
currentImportance = currentImportance.setPseudoRegisterWord(regOffset, Important)
} else {
currentImportance = currentImportance.setPseudoRegister(regOffset, Important)
}
}
}
if (isReadAsPointer) {
currentImportance = currentImportance.setPseudoRegisterWord(regOffset, Important)
}
}
}
}
@ -365,6 +354,6 @@ object ReverseFlowAnalyzer {
// }
// println("---------------------")
importanceArray.toList
cache.put(code, importanceArray.toList)
}
}

View File

@ -1,6 +1,6 @@
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.assembly.mos._
import millfork.assembly.opt.SingleStatus
@ -41,15 +41,15 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
val taggedCode = FlowAnalyzer.analyze(f, effectiveCode, optimizationContext, needsFlowInfo)
optimizeImpl(f, taggedCode, optimizationContext)
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext)
if (changed) optimized else code
}
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, AssemblyLine)], optimizationContext: OptimizationContext): List[AssemblyLine] = {
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, AssemblyLine)], optimizationContext: OptimizationContext): (Boolean, List[AssemblyLine]) = {
val log = optimizationContext.log
code match {
case Nil => Nil
case Nil => false -> Nil
case head :: tail =>
for ((rule, index) <- actualRules.zipWithIndex) {
val ctx = new AssemblyMatchingContext(
@ -64,7 +64,8 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2)
val optimizedChunk: List[AssemblyLine] = rule.result(matchedChunkToOptimize, ctx)
val optimizedChunkWithSource =
if (optimizedChunk.isEmpty) optimizedChunk
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk
else if (optimizedChunk.isEmpty) optimizedChunk
else if (matchedChunkToOptimize.size == 1) optimizedChunk.map(_.pos(matchedChunkToOptimize.head.source))
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
@ -82,14 +83,15 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
}
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2)
} else {
return optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
}
case None => ()
}
}
head._2 :: optimizeImpl(f, tail, optimizationContext)
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext)
(changedTail, head._2 :: optimizedTail)
}
}
}
@ -124,7 +126,7 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
def functionReadsMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntReadMemory -> name)
private val map = mutable.Map[Int, Any]()
private val map = new mutable.HashMap[Int, Any]()
override def toString: String = if (map.isEmpty) "<empty context>" else map.mkString(", ")
@ -722,6 +724,8 @@ case class Both(l: AssemblyLinePattern, r: AssemblyLinePattern) extends Assembly
l.matchLineTo(ctx, flowInfo, line) && r.matchLineTo(ctx, flowInfo, line)
override def toString: String = l + " ∧ " + r
override def &(x: AssemblyLinePattern): AssemblyLinePattern = Both(l, Both(r, x))
}
case class EitherPattern(l: AssemblyLinePattern, r: AssemblyLinePattern) extends AssemblyLinePattern {
@ -734,6 +738,8 @@ case class EitherPattern(l: AssemblyLinePattern, r: AssemblyLinePattern) extends
l.matchLineTo(ctx, flowInfo, line) || r.matchLineTo(ctx, flowInfo, line)
override def toString: String = s"($l $r)"
override def |(x: AssemblyLinePattern): AssemblyLinePattern = EitherPattern(l, EitherPattern(r, x))
}
case object Elidable extends AssemblyLinePattern {

View File

@ -0,0 +1,37 @@
package millfork.assembly.opt
/**
* @author Karol Stasiak
*/
class FlowCache[L, F](val name: String) {
private val lastL = new Array[List[L]](1)
private val lastF = new Array[List[F]](1)
private var hits = 0L
private var misses = 0L
private def index(lines: List[L]): Int = 0
private def dumpStats(): Unit = println(s"Cache for $name: $hits hits, $misses misses")
def get(lines: List[L]): Option[List[F]] = synchronized {
val i = index(lines)
if (lastL.indices.contains(i) && (lastL(i) eq lines)) {
hits += 1
// dumpStats()
Some(lastF(i))
} else {
misses += 1
// dumpStats()
None
}
}
def put(lines: List[L], flow: List[F]): List[F] = synchronized {
val i = index(lines)
if (lastL.indices.contains(i)) {
lastL(i) = lines
lastF(i) = flow
}
flow
}
}

View File

@ -79,7 +79,7 @@ object Status {
val SingleFF: Status[Int] = SingleStatus(0xff)
@inline
private def wrapBool(b: Boolean) = if (b) SingleTrue else SingleFalse
private def wrapBool(b: Boolean): Status[Boolean] = if (b) SingleTrue else SingleFalse
def flatMap2[T, U, R](a: Status[T], b: Status[U])(f: (T, U) => Status[R]): Status[R] = (a, b) match {
case (SingleStatus(t), SingleStatus(u)) => f(t, u)
@ -205,6 +205,23 @@ object Status {
case _ => AnyStatus -> AnyStatus
}
def adc(value: Status[Int], carry: Status[Boolean]): (Status[Int], Status[Boolean]) = (inner, value, carry) match {
case (SingleStatus(x), SingleStatus(y), SingleStatus(false)) =>
SingleStatus((x + y) & 0xff) -> SingleStatus((x.&(0xff) + y.&(0xff)) > 0xff)
case (SingleStatus(x), SingleStatus(y), SingleStatus(true)) =>
SingleStatus((x + y + 1) & 0xff) -> SingleStatus((x.&(0xff) + y.&(0xff) + 1) > 0xff)
case (SingleStatus(x), SingleStatus(y), AnyStatus) => x + y match {
case 255 => AnyStatus -> AnyStatus
case z if z < 255 => AnyStatus -> SingleFalse
case z if z > 255 => AnyStatus -> SingleTrue
}
case (SingleStatus(0), AnyStatus, SingleStatus(false))
| (AnyStatus, SingleStatus(0), SingleStatus(false)) => AnyStatus -> SingleFalse
case (SingleStatus(0xff), AnyStatus, SingleStatus(true))
| (AnyStatus, SingleStatus(0xff), SingleStatus(true)) => AnyStatus -> SingleTrue
case _ => AnyStatus -> AnyStatus
}
def sbc(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): (Status[Int], Status[Boolean]) = inner match {
case SingleStatus(x) => decimal match {
case SingleStatus(false) => carry match {

View File

@ -1,6 +1,6 @@
package millfork.assembly.z80.opt
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
import millfork.assembly.z80._
import millfork.env._
import millfork.node.ZRegister
@ -11,7 +11,10 @@ import millfork.{CompilationFlag, CompilationOptions, Cpu}
*/
object CoarseFlowAnalyzer {
val cache = new FlowCache[ZLine, CpuStatus]("z80 forward")
def analyze(f: NormalFunction, code: List[ZLine], compilationOptions: CompilationOptions): List[CpuStatus] = {
cache.get(code).foreach(return _)
val initialStatus = CpuStatus()
val functionStartStatus = CpuStatus()
val emptyStatus = CpuStatus()
@ -45,45 +48,84 @@ object CoarseFlowAnalyzer {
case _ => None
}).fold(currentStatus)(_ ~ _)
case ZLine0(CALL, _, MemoryAddressConstant(fun: FunctionInMemory)) =>
case ZLine0(CALL, reg, MemoryAddressConstant(fun: FunctionInMemory)) =>
val n = fun.name
val result = initialStatus.copy(memIx = currentStatus.memIx)
currentStatus = result.copy(
b = if (preservesB(n)) currentStatus.b else result.b,
c = if (preservesC(n)) currentStatus.c else result.c,
d = if (preservesD(n)) currentStatus.d else result.d,
e = if (preservesE(n)) currentStatus.e else result.e,
h = if (preservesH(n)) currentStatus.h else result.h,
l = if (preservesL(n)) currentStatus.l else result.l
)
val mayBeCalled = reg match {
case IfFlagSet(f) => !currentStatus.getFlag(f).contains(false)
case IfFlagClear(f) => !currentStatus.getFlag(f).contains(true)
case _ => true
}
if (mayBeCalled) {
val result = initialStatus.copy(memIx = currentStatus.memIx)
currentStatus = result.copy(
b = if (preservesB(n)) currentStatus.b else result.b,
c = if (preservesC(n)) currentStatus.c else result.c,
d = if (preservesD(n)) currentStatus.d else result.d,
e = if (preservesE(n)) currentStatus.e else result.e,
h = if (preservesH(n)) currentStatus.h else result.h,
l = if (preservesL(n)) currentStatus.l else result.l
)
}
case ZLine0(CALL, _, _) =>
case ZLine0(NOP | DISCARD_A | DISCARD_BC | DISCARD_DE | DISCARD_HL | DISCARD_F, _, _) =>
()
case ZLine0(PUSH, _, _) =>
()
case ZLine0(POP, OneRegister(r), _) =>
currentStatus = currentStatus.setRegister(r, AnyStatus)
case ZLine0(JR | JP | RET, IfFlagSet(flag), _) =>
currentStatus = currentStatus.setFlag(flag, newStatus = false)
case ZLine0(JR | JP | RET, IfFlagClear(flag), _) =>
currentStatus = currentStatus.setFlag(flag, newStatus = true)
case ZLine0(JR | JP | RET, NoRegisters | OneRegister(_), _) =>
currentStatus = initialStatus.copy(memIx = currentStatus.memIx)
case ZLine0(DJNZ, _, _) =>
// TODO
currentStatus = currentStatus.copy(
b = Status.SingleZero,
zf = Status.SingleTrue,
nf = AnyStatus,
cf = AnyStatus,
hf = AnyStatus,
sf = AnyStatus)
case ZLine0(BYTE, _, _) =>
currentStatus = initialStatus
case ZLine0(ADD, OneRegister(ZRegister.IMM_8), NumericConstant(0, _)) =>
currentStatus = currentStatus.copy(
nf = Status.SingleFalse,
cf = Status.SingleFalse,
zf = currentStatus.a.map(_.&(0xff) == 0),
sf = currentStatus.a.map(_.&(0x80) == 0),
pf = if (z80) Status.SingleFalse else AnyStatus,
hf = Status.SingleFalse)
case ZLine0(SUB, OneRegister(ZRegister.IMM_8), NumericConstant(0, _)) =>
currentStatus = currentStatus.copy(
nf = Status.SingleTrue,
cf = Status.SingleFalse,
zf = currentStatus.a.map(_.&(0xff) == 0),
sf = currentStatus.a.map(_.&(0x80) == 0),
zf = currentStatus.a.z(),
sf = currentStatus.a.n(),
pf = if (z80) Status.SingleFalse else AnyStatus,
hf = Status.SingleFalse)
case l@ZLine0(ADD, OneRegister(ZRegister.IMM_8), NumericConstant(n, _)) =>
val (newA, newC) = currentStatus.a.adc(n.toInt, currentStatus.cf, Status.SingleFalse)
currentStatus = currentStatus.copy(a = newA,
nf = Status.SingleFalse, cf = newC, zf = newA.z(), sf = newA.n(), pf = AnyStatus, hf = AnyStatus)
case l@ZLine0(ADC, OneRegister(ZRegister.IMM_8), NumericConstant(n, _)) =>
val (newA, newC) = currentStatus.a.adc(n.toInt, currentStatus.cf, Status.SingleFalse)
currentStatus = currentStatus.copy(a = newA,
nf = Status.SingleFalse, cf = newC, zf = newA.z(), sf = newA.n(), pf = AnyStatus, hf = AnyStatus)
case l@ZLine0(ADD, OneRegister(s), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xff),
nf = Status.SingleFalse, cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
val (newA, newC) = currentStatus.a.adc(currentStatus.getRegister(s), Status.SingleFalse)
currentStatus = currentStatus.copy(a = newA,
nf = Status.SingleFalse, cf = newC, zf = newA.z(), sf = newA.n(), pf = AnyStatus, hf = AnyStatus)
case l@ZLine0(ADC, OneRegister(s), _) =>
val (newA, newC) = currentStatus.a.adc(currentStatus.getRegister(s), currentStatus.cf)
currentStatus = currentStatus.copy(a = newA,
nf = Status.SingleFalse, cf = newC, zf = newA.z(), sf = newA.n(), pf = AnyStatus, hf = AnyStatus)
case ZLine0(SUB, OneRegister(s), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m - n) & 0xff),
nf = Status.SingleTrue, cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine0(AND, OneRegister(s), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m & n) & 0xff),
nf = Status.SingleFalse, cf = Status.SingleFalse, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
@ -99,21 +141,50 @@ object CoarseFlowAnalyzer {
nf = Status.SingleFalse, cf = Status.SingleFalse, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine0(INC, OneRegister(r), _) =>
val newV = currentStatus.getRegister(r).map(i => i.+(1).&(0xff))
currentStatus = currentStatus.
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
setRegister(r, currentStatus.getRegister(r).map(i => i.+(1).&(0xff)))
setRegister(r, newV)
case ZLine0(DEC, OneRegister(r), _) =>
val newV = currentStatus.getRegister(r).map(i => i.-(1).&(0xff))
currentStatus = currentStatus.
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
setRegister(r, currentStatus.getRegister(r).map(i => i.-(1).&(0xff)))
setRegister(r, newV)
case ZLine0(INC, OneRegisterOffset(r, o), _) =>
val newV = currentStatus.getRegister(r, o).map(i => i.+(1).&(0xff))
currentStatus = currentStatus.
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
setRegister(r, newV, o)
case ZLine0(DEC, OneRegisterOffset(r, o), _) =>
val newV = currentStatus.getRegister(r, o).map(i => i.-(1).&(0xff))
currentStatus = currentStatus.
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
setRegister(r, newV, o)
case ZLine0(INC_16, OneRegister(r), _) =>
val newV = currentStatus.getRegister(r).map(i => i.+(1).&(0xffff))
currentStatus = currentStatus.
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
setRegister(r, newV)
case ZLine0(DEC_16, OneRegister(r), _) =>
val newV = currentStatus.getRegister(r).map(i => i.-(1).&(0xffff))
currentStatus = currentStatus.
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
setRegister(r, newV)
case ZLine0(op, OneRegister(r), _) if ZOpcodeClasses.SET(op) =>
currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i | 1.<<(ZOpcodeClasses.SET_seq.indexOf(op))))
case ZLine0(op, OneRegister(r), _) if ZOpcodeClasses.RES(op) =>
currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i & ~1.<<(ZOpcodeClasses.RES_seq.indexOf(op))))
case ZLine0(ADD, OneRegisterOffset(s, o), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m + n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case l@ZLine0(ADD, OneRegisterOffset(s, o), _) =>
val (newA, newC) = currentStatus.a.adc(currentStatus.getRegister(s, o), Status.SingleFalse)
currentStatus = currentStatus.copy(a = newA,
nf = Status.SingleFalse, cf = newC, zf = newA.z(), sf = newA.n(), pf = AnyStatus, hf = AnyStatus)
case l@ZLine0(ADC, OneRegisterOffset(s, o), _) =>
val (newA, newC) = currentStatus.a.adc(currentStatus.getRegister(s, o), currentStatus.cf)
currentStatus = currentStatus.copy(a = newA,
nf = Status.SingleFalse, cf = newC, zf = newA.z(), sf = newA.n(), pf = AnyStatus, hf = AnyStatus)
case ZLine0(SUB, OneRegisterOffset(s, o), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m - n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
@ -167,6 +238,43 @@ object CoarseFlowAnalyzer {
case ZLine0(CCF, _, _) =>
currentStatus = currentStatus.copy(cf = currentStatus.cf.negate, hf = AnyStatus, nf = AnyStatus)
case ZLine0(CPL, _, _) =>
currentStatus = currentStatus.copy(a = currentStatus.a.map(_ ^ 0xff), hf = AnyStatus, nf = Status.SingleTrue)
case ZLine0(NEG, _, _) =>
currentStatus = currentStatus.copy(
a = currentStatus.a.map(x => (-x)&0xff),
hf = AnyStatus,
zf = currentStatus.a.z(),
sf = AnyStatus,
cf = AnyStatus,
pf = AnyStatus,
nf = Status.SingleTrue)
case ZLine0(DAA, _, _) =>
// TODO: full algorithm?
currentStatus = currentStatus.copy(
a = if (currentStatus.h.contains(false)) currentStatus.a else AnyStatus,
hf = AnyStatus,
zf = AnyStatus,
sf = AnyStatus,
cf = AnyStatus,
pf = AnyStatus)
case ZLine0(BIT7, OneRegister(r), _) =>
currentStatus = currentStatus.copy(
zf = currentStatus.getRegister(r).bit7.negate,
hf = Status.SingleTrue,
sf = AnyStatus,
pf = AnyStatus,
nf = Status.SingleFalse)
case ZLine0(BIT0, OneRegister(r), _) =>
currentStatus = currentStatus.copy(
zf = currentStatus.getRegister(r).bit0.negate,
hf = Status.SingleTrue,
sf = AnyStatus,
pf = AnyStatus,
nf = Status.SingleFalse)
case ZLine0(opcode, registers, _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)
@ -187,6 +295,6 @@ object CoarseFlowAnalyzer {
// println("---------------------")
}
flagArray.toList
cache.put(code, flagArray.toList)
}
}

View File

@ -57,7 +57,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
case ZRegister.BC => this.copy(b = value.hi, c = value.lo)
case ZRegister.DE => this.copy(d = value.hi, e = value.lo)
case ZRegister.HL => this.copy(h = value.hi, l = value.lo, hl = value.map(NumericConstant(_, 2)))
case ZRegister.IX => this.copy(ixh = value.hi, ixl = value.lo)
case ZRegister.IX => this.copy(ixh = value.hi, ixl = value.lo, memIx = Map())
case ZRegister.IY => this.copy(iyh = value.hi, iyl = value.lo)
case ZRegister.AF => this.copy(a = value.hi, cf = AnyStatus, zf = AnyStatus, hf = AnyStatus, pf = AnyStatus, sf = AnyStatus)
}
@ -104,6 +104,18 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
case ZFlag.S => sf
case ZFlag.N => nf
}
def setFlag(register: ZFlag.Value, newStatus: Boolean): CpuStatus = {
val st = if (newStatus) Status.SingleTrue else Status.SingleFalse
register match {
case ZFlag.C => copy(cf = st)
case ZFlag.H => copy(hf = st)
case ZFlag.P => copy(pf = st)
case ZFlag.Z => copy(zf = st)
case ZFlag.S => copy(sf = st)
case ZFlag.N => copy(nf = st)
}
}
def ~(that: CpuStatus) = new CpuStatus(
a = this.a ~ that.a,
b = this.b ~ that.b,

View File

@ -14,9 +14,9 @@ import scala.collection.mutable
object JumpFollowing {
def apply(options: CompilationOptions, code: List[ZLine]): List[ZLine] = {
val labelsToRet = mutable.Set[String]()
val labelsToJumps = mutable.Map[String, String]()
val currentLabels = mutable.Set[String]()
val labelsToRet = new mutable.HashSet[String]()
val labelsToJumps = new mutable.HashMap[String, String]()
val currentLabels = new mutable.HashSet[String]()
for (line <- code) {
line match {
case ZLine0(LABEL, _, MemoryAddressConstant(Label(label))) =>

View File

@ -1,5 +1,6 @@
package millfork.assembly.z80.opt
import millfork.assembly.opt.FlowCache
import millfork.assembly.z80._
import millfork.env._
import millfork.node.ZRegister
@ -175,6 +176,8 @@ case class CpuImportance(a: Importance = UnknownImportance,
object ReverseFlowAnalyzer {
val cache = new FlowCache[ZLine, CpuImportance]("z80 reverse")
val readsA = Set("__mul_u8u8u8", "__mul_u16u8u16")
val readsB = Set("")
val readsC = Set("")
@ -185,6 +188,7 @@ object ReverseFlowAnalyzer {
//noinspection RedundantNewCaseClass
def analyze(f: NormalFunction, code: List[ZLine]): List[CpuImportance] = {
cache.get(code).foreach(return _)
val importanceArray = Array.fill[CpuImportance](code.length)(new CpuImportance())
val codeArray = code.toArray
@ -460,7 +464,7 @@ object ReverseFlowAnalyzer {
// }
// println("---------------------")
importanceArray.toList
cache.put(code, importanceArray.toList)
}
private def getLabelIndex(codeArray: Array[ZLine], L: String) = {

View File

@ -1,6 +1,6 @@
package millfork.assembly.z80.opt
import millfork.CompilationOptions
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.assembly.opt.{AnyStatus, SingleStatus}
import millfork.assembly.z80._
@ -39,15 +39,15 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
actualRules.foreach(_.pattern.validate(needsFlowInfo))
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
val effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
val taggedCode = FlowAnalyzer.analyze(f, effectiveCode, optimizationContext.options, needsFlowInfo)
optimizeImpl(f, taggedCode, optimizationContext)
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext.options, needsFlowInfo)
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext)
if (changed) optimized else code
}
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, ZLine)], optimizationContext: OptimizationContext): List[ZLine] = {
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, ZLine)], optimizationContext: OptimizationContext): (Boolean, List[ZLine]) = {
val log = optimizationContext.log
code match {
case Nil => Nil
case Nil => (false, Nil)
case head :: tail =>
for ((rule, index) <- actualRules.zipWithIndex) {
val ctx = new AssemblyMatchingContext(optimizationContext.options)
@ -56,8 +56,9 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
val matchedChunkToOptimize: List[ZLine] = code.take(code.length - rest.length).map(_._2)
val optimizedChunk: List[ZLine] = rule.result(matchedChunkToOptimize, ctx)
val optimizedChunkWithSource =
if (optimizedChunk.isEmpty) optimizedChunk
else if (matchedChunkToOptimize.size == 1) optimizedChunk.map(_.pos(matchedChunkToOptimize.head.source))
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk
else if (optimizedChunk.isEmpty) optimizedChunk
else if (matchedChunkToOptimize.size == 1) optimizedChunk.map(_.pos(matchedChunkToOptimize.head.source))
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else optimizedChunk
@ -76,20 +77,21 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
}
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2)
} else {
return optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
}
case None => ()
}
}
head._2 :: optimizeImpl(f, tail, optimizationContext)
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext)
(changedTail, head._2 :: optimizedTail)
}
}
}
class AssemblyMatchingContext(val compilationOptions: CompilationOptions) {
private val map = mutable.Map[Int, Any]()
private val map = new mutable.HashMap[Int, Any]()
def log: Logger = compilationOptions.log
@ -697,6 +699,8 @@ case class Both(l: AssemblyLinePattern, r: AssemblyLinePattern) extends Assembly
l.matchLineTo(ctx, flowInfo, line) && r.matchLineTo(ctx, flowInfo, line)
override def toString: String = l + " ∧ " + r
override def &(x: AssemblyLinePattern): AssemblyLinePattern = Both(l, Both(r, x))
}
case class EitherPattern(l: AssemblyLinePattern, r: AssemblyLinePattern) extends AssemblyLinePattern {
@ -709,6 +713,8 @@ case class EitherPattern(l: AssemblyLinePattern, r: AssemblyLinePattern) extends
l.matchLineTo(ctx, flowInfo, line) || r.matchLineTo(ctx, flowInfo, line)
override def toString: String = s"($l $r)"
override def |(x: AssemblyLinePattern): AssemblyLinePattern = EitherPattern(l, EitherPattern(r, x))
}
case object Elidable extends AssemblyLinePattern {

View File

@ -465,12 +465,15 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
inlinedFunctions,
options.jobContext))
unoptimizedCodeSize += unoptimized.map(_.sizeInBytes).sum
val code = optimizations.foldLeft(unoptimized) { (c, opt) =>
opt.optimize(f, c, OptimizationContext(options, labelMap, env.maybeGet[ThingInMemory]("__reg"), niceFunctionProperties))
val code = optimizations.foldLeft(quickSimplify(unoptimized)) { (c, opt) =>
val code = opt.optimize(f, c, OptimizationContext(options, labelMap, env.maybeGet[ThingInMemory]("__reg"), niceFunctionProperties))
if (code eq c) code else quickSimplify(code)
}
performFinalOptimizationPass(f, optimizations.nonEmpty, options, code)
}
def quickSimplify(code: List[T]): List[T]
def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[T]): Unit
def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[T]): List[T]

View File

@ -102,6 +102,8 @@ class MosAssembler(program: Program,
}
}
override def quickSimplify(code: List[AssemblyLine]): List[AssemblyLine] = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
override def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[AssemblyLine]): Unit = {
import Opcode._
import AddrMode._

View File

@ -626,6 +626,8 @@ class Z80Assembler(program: Program,
override def injectLabels(labelMap: Map[String, Int], code: List[ZLine]): List[ZLine] = code // TODO
override def quickSimplify(code: List[ZLine]): List[ZLine] = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
override def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[ZLine]): Unit = {
// do nothing yet
}

View File

@ -12,6 +12,8 @@ import millfork.node._
import millfork.output.{DivisibleAlignment, MemoryAlignment, NoAlignment}
import millfork.{CompilationFlag, CompilationOptions, SeparatedList}
import scala.collection.immutable.BitSet
/**
* @author Karol Stasiak
*/
@ -90,13 +92,13 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
val textLiteralAtom: P[TextLiteralExpression] = textLiteral.map(TextLiteralExpression)
val literalAtom: P[LiteralExpression] = charAtom | binaryAtom | hexAtom | octalAtom | quaternaryAtom | decimalAtom
val literalAtom: P[LiteralExpression] = binaryAtom | hexAtom | octalAtom | quaternaryAtom | decimalAtom | charAtom
val literalAtomWithIntel: P[LiteralExpression] = charAtom | binaryAtom | hexAtom | octalAtom | quaternaryAtom | intelHexAtom | decimalAtom
val literalAtomWithIntel: P[LiteralExpression] = binaryAtom | hexAtom | octalAtom | quaternaryAtom | intelHexAtom | decimalAtom | charAtom
val atom: P[Expression] = P(position() ~ (literalAtom | variableAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)}
val atom: P[Expression] = P(position() ~ (variableAtom | literalAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)}
val atomWithIntel: P[Expression] = P(position() ~ (literalAtomWithIntel | variableAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)}
val atomWithIntel: P[Expression] = P(position() ~ (variableAtom | literalAtomWithIntel | textLiteralAtom)).map{case (p,a) => a.pos(p)}
val globalVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(true)
val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false)
@ -500,7 +502,7 @@ object MfParser {
def sign(abs: Long, minus: Boolean): Long = if (minus) -abs else abs
val invalidCharLiteralTypes: Set[Int] = Set[Int](
val invalidCharLiteralTypes: BitSet = BitSet(
Character.LINE_SEPARATOR,
Character.PARAGRAPH_SEPARATOR,
Character.CONTROL,