From badd7ef1d862ebf789f79ade557ca6019c75eff3 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Sun, 16 Dec 2018 14:38:57 +0100 Subject: [PATCH] Compiler performance improvements --- .../assembly/mos/opt/CoarseFlowAnalyzer.scala | 7 +- .../assembly/mos/opt/JumpFollowing.scala | 10 +- .../mos/opt/ReverseFlowAnalyzer.scala | 117 ++++++------ .../opt/RuleBasedAssemblyOptimization.scala | 28 +-- .../millfork/assembly/opt/FlowCache.scala | 37 ++++ .../scala/millfork/assembly/opt/Status.scala | 19 +- .../assembly/z80/opt/CoarseFlowAnalyzer.scala | 168 ++++++++++++++---- .../millfork/assembly/z80/opt/CpuStatus.scala | 14 +- .../assembly/z80/opt/JumpFollowing.scala | 6 +- .../z80/opt/ReverseFlowAnalyzer.scala | 6 +- .../opt/RuleBasedAssemblyOptimization.scala | 30 ++-- .../millfork/output/AbstractAssembler.scala | 7 +- .../scala/millfork/output/MosAssembler.scala | 2 + .../scala/millfork/output/Z80Assembler.scala | 2 + src/main/scala/millfork/parser/MfParser.scala | 12 +- 15 files changed, 328 insertions(+), 137 deletions(-) create mode 100644 src/main/scala/millfork/assembly/opt/FlowCache.scala diff --git a/src/main/scala/millfork/assembly/mos/opt/CoarseFlowAnalyzer.scala b/src/main/scala/millfork/assembly/mos/opt/CoarseFlowAnalyzer.scala index 3f9ce313..04f11747 100644 --- a/src/main/scala/millfork/assembly/mos/opt/CoarseFlowAnalyzer.scala +++ b/src/main/scala/millfork/assembly/mos/opt/CoarseFlowAnalyzer.scala @@ -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) } } diff --git a/src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala b/src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala index c1a8d4da..043944c7 100644 --- a/src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala +++ b/src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala @@ -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))) => diff --git a/src/main/scala/millfork/assembly/mos/opt/ReverseFlowAnalyzer.scala b/src/main/scala/millfork/assembly/mos/opt/ReverseFlowAnalyzer.scala index 77e2258d..3e150add 100644 --- a/src/main/scala/millfork/assembly/mos/opt/ReverseFlowAnalyzer.scala +++ b/src/main/scala/millfork/assembly/mos/opt/ReverseFlowAnalyzer.scala @@ -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) } } diff --git a/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala index 2a39d24d..02712c3e 100644 --- a/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala @@ -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) "" 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 { diff --git a/src/main/scala/millfork/assembly/opt/FlowCache.scala b/src/main/scala/millfork/assembly/opt/FlowCache.scala new file mode 100644 index 00000000..0b52d105 --- /dev/null +++ b/src/main/scala/millfork/assembly/opt/FlowCache.scala @@ -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 + } +} diff --git a/src/main/scala/millfork/assembly/opt/Status.scala b/src/main/scala/millfork/assembly/opt/Status.scala index f6b8367f..e01684a9 100644 --- a/src/main/scala/millfork/assembly/opt/Status.scala +++ b/src/main/scala/millfork/assembly/opt/Status.scala @@ -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 { diff --git a/src/main/scala/millfork/assembly/z80/opt/CoarseFlowAnalyzer.scala b/src/main/scala/millfork/assembly/z80/opt/CoarseFlowAnalyzer.scala index c531a298..02522ab4 100644 --- a/src/main/scala/millfork/assembly/z80/opt/CoarseFlowAnalyzer.scala +++ b/src/main/scala/millfork/assembly/z80/opt/CoarseFlowAnalyzer.scala @@ -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) } } diff --git a/src/main/scala/millfork/assembly/z80/opt/CpuStatus.scala b/src/main/scala/millfork/assembly/z80/opt/CpuStatus.scala index e2919897..49b77be6 100644 --- a/src/main/scala/millfork/assembly/z80/opt/CpuStatus.scala +++ b/src/main/scala/millfork/assembly/z80/opt/CpuStatus.scala @@ -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, diff --git a/src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala b/src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala index 6c174b1e..dc2b49be 100644 --- a/src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala +++ b/src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala @@ -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))) => diff --git a/src/main/scala/millfork/assembly/z80/opt/ReverseFlowAnalyzer.scala b/src/main/scala/millfork/assembly/z80/opt/ReverseFlowAnalyzer.scala index 2b580e00..b1d08c8b 100644 --- a/src/main/scala/millfork/assembly/z80/opt/ReverseFlowAnalyzer.scala +++ b/src/main/scala/millfork/assembly/z80/opt/ReverseFlowAnalyzer.scala @@ -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) = { diff --git a/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala index bdd2b141..93909017 100644 --- a/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala @@ -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 { diff --git a/src/main/scala/millfork/output/AbstractAssembler.scala b/src/main/scala/millfork/output/AbstractAssembler.scala index 395c7b39..0572ce5c 100644 --- a/src/main/scala/millfork/output/AbstractAssembler.scala +++ b/src/main/scala/millfork/output/AbstractAssembler.scala @@ -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] diff --git a/src/main/scala/millfork/output/MosAssembler.scala b/src/main/scala/millfork/output/MosAssembler.scala index a25718f0..f4245df0 100644 --- a/src/main/scala/millfork/output/MosAssembler.scala +++ b/src/main/scala/millfork/output/MosAssembler.scala @@ -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._ diff --git a/src/main/scala/millfork/output/Z80Assembler.scala b/src/main/scala/millfork/output/Z80Assembler.scala index 5f291ddc..26e9f5e2 100644 --- a/src/main/scala/millfork/output/Z80Assembler.scala +++ b/src/main/scala/millfork/output/Z80Assembler.scala @@ -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 } diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index f661e060..6c1f8587 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -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,