diff --git a/src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala b/src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala new file mode 100644 index 00000000..21d56d06 --- /dev/null +++ b/src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala @@ -0,0 +1,62 @@ +package millfork.assembly.mos.opt + +import millfork.CompilationOptions +import millfork.assembly.mos.{AssemblyLine, OpcodeClasses} +import millfork.env.{Label, MemoryAddressConstant} +import millfork.assembly.mos.Opcode._ +import millfork.assembly.mos.AddrMode._ + +import scala.collection.mutable + +/** + * @author Karol Stasiak + */ +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]() + for (line <- code) { + line match { + case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(label)), _) => + currentLabels += label + case AssemblyLine(op, _, _, _) if OpcodeClasses.NoopDiscardsFlags(op) => + case AssemblyLine(LABEL, _, _, _) => + case AssemblyLine(RTS, _, _, _) => + labelsToRts ++= currentLabels + currentLabels.clear() + case AssemblyLine(RTI, _, _, _) => + labelsToRti ++= currentLabels + currentLabels.clear() + case AssemblyLine(RTL, _, _, _) => + labelsToRtl ++= currentLabels + currentLabels.clear() + case AssemblyLine(JMP | BRA, Absolute | Relative, MemoryAddressConstant(Label(label)), _) => + labelsToJumps ++= currentLabels.map(_ -> label) + currentLabels.clear() + case _ => + currentLabels.clear() + } + } + code.map { + case jump@AssemblyLine(JMP | BRA, Absolute | Relative | LongRelative | LongAbsolute, MemoryAddressConstant(Label(label)), true) => + if (labelsToRts(label)) { + options.log.debug(s"Optimizing ${jump.opcode} straight into RTS") + AssemblyLine.implied(RTS) + } else if (labelsToRti(label)) { + options.log.debug(s"Optimizing ${jump.opcode} straight into RTI") + AssemblyLine.implied(RTI) + } else if (labelsToRtl(label)) { + options.log.debug(s"Optimizing ${jump.opcode} straight into RTL") + AssemblyLine.implied(RTL) + } else if (labelsToJumps.contains(label)) { + options.log.debug(s"Optimizing ${jump.opcode} straight into a jump") + AssemblyLine.absolute(JMP, Label(labelsToJumps(label))) + } else jump + case x => x + } + } +} diff --git a/src/main/scala/millfork/assembly/z80/ZLine.scala b/src/main/scala/millfork/assembly/z80/ZLine.scala index 1e729e4e..45e0d38f 100644 --- a/src/main/scala/millfork/assembly/z80/ZLine.scala +++ b/src/main/scala/millfork/assembly/z80/ZLine.scala @@ -17,13 +17,19 @@ object ZFlag extends Enumeration { val AllButZ: Seq[Value] = Seq(P, C, H, N, S) } -sealed trait ZRegisters +sealed trait ZRegisters { + def negate: ZRegisters = this +} case object NoRegisters extends ZRegisters -case class IfFlagSet(flag: ZFlag.Value) extends ZRegisters +case class IfFlagSet(flag: ZFlag.Value) extends ZRegisters { + override def negate: ZRegisters = IfFlagClear(flag) +} -case class IfFlagClear(flag: ZFlag.Value) extends ZRegisters +case class IfFlagClear(flag: ZFlag.Value) extends ZRegisters { + override def negate: ZRegisters = IfFlagSet(flag) +} case class OneRegister(register: ZRegister.Value) extends ZRegisters { // if (register == ZRegister.MEM_IY_D || register == ZRegister.MEM_IX_D) ??? diff --git a/src/main/scala/millfork/assembly/z80/opt/ConditionalInstructions.scala b/src/main/scala/millfork/assembly/z80/opt/ConditionalInstructions.scala new file mode 100644 index 00000000..8f645ea9 --- /dev/null +++ b/src/main/scala/millfork/assembly/z80/opt/ConditionalInstructions.scala @@ -0,0 +1,52 @@ +package millfork.assembly.z80.opt + +import millfork.assembly.z80.ZOpcode._ +import millfork.assembly.z80._ +import millfork.env.{Constant, Label, MemoryAddressConstant, NormalFunction} +import millfork.parser.Preprocessor.IfContext +import millfork.{CompilationFlag, CompilationOptions} + +/** + * @author Karol Stasiak + */ +object ConditionalInstructions { + + def apply(options: CompilationOptions, code: List[ZLine]): List[ZLine] = code match { + case (jump@ZLine(JR | JP, IfFlagSet(_) | IfFlagClear(_), MemoryAddressConstant(Label(label)), true)) :: + (call@ZLine(CALL, NoRegisters, _, _)) :: + xs => + if (startsWithLabel(label, xs)) { + val condCall = call.copy(registers = jump.registers.negate) + options.log.debug(s"Replacing ${jump.toString.trim} ${call.toString.trim} with ${condCall.toString.trim}") + condCall :: apply(options, xs) + }else jump :: call :: apply(options, xs) + case (jump@ZLine(JR | JP, IfFlagSet(_) | IfFlagClear(_), MemoryAddressConstant(Label(label)), true)) :: xs => + retPrecededByDiscards(xs) match { + case Some(rest) if startsWithLabel(label, rest) => + val condRet = ZLine(RET, jump.registers.negate, Constant.Zero) + options.log.debug(s"Replacing ${jump.toString.trim} RET with ${condRet.toString.trim}") + condRet :: apply(options, rest) + + case _ => jump :: apply(options, xs) + } + case x :: xs => x :: apply(options, xs) + case Nil => Nil + } + + private def retPrecededByDiscards(code: List[ZLine]): Option[List[ZLine]] = { + code match { + case ZLine(op, _, _, _) :: xs if ZOpcodeClasses.NoopDiscards(op) => retPrecededByDiscards(xs) + case ZLine(RET, NoRegisters, _, _) :: xs => Some(xs) + case _ => None + } + } + + private def startsWithLabel(LabelName: String, code: List[ZLine]): Boolean = { + code match { + case ZLine(LABEL, _, MemoryAddressConstant(Label(LabelName)), _) :: xs => true + case ZLine(LABEL, _, _, _) :: xs => startsWithLabel(LabelName, xs) + case ZLine(op, _, _, _) :: xs if ZOpcodeClasses.NoopDiscards(op) => startsWithLabel(LabelName, xs) + case _ => false + } + } +} diff --git a/src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala b/src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala new file mode 100644 index 00000000..59b02877 --- /dev/null +++ b/src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala @@ -0,0 +1,47 @@ +package millfork.assembly.z80.opt + +import millfork.assembly.z80.ZOpcode._ +import millfork.assembly.z80._ +import millfork.env.{Constant, Label, MemoryAddressConstant} +import millfork.{CompilationOptions} + +import scala.collection.mutable + +/** + * @author Karol Stasiak + */ +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]() + for (line <- code) { + line match { + case ZLine(LABEL, _, MemoryAddressConstant(Label(label)), _) => + currentLabels += label + case ZLine(op, _, _, _) if ZOpcodeClasses.NoopDiscards(op) => + case ZLine(LABEL, _, _, _) => + case ZLine(RET, NoRegisters, _, _) => + labelsToRet ++= currentLabels + currentLabels.clear() + case ZLine(JP | JR, NoRegisters, MemoryAddressConstant(Label(label)), _) => + labelsToJumps ++= currentLabels.map(_ -> label) + currentLabels.clear() + case _ => + currentLabels.clear() + } + } + code.map { + case jump@ZLine(JR | JP, cond, MemoryAddressConstant(Label(label)), true) => + if (labelsToRet(label)) { + options.log.debug(s"Optimizing ${jump.opcode} straight into RET") + ZLine(RET, cond, Constant.Zero) + } else if (labelsToJumps.contains(label)) { + options.log.debug(s"Optimizing ${jump.opcode} straight into a jump") + ZLine.jump(labelsToJumps(label)) + } else jump + case x => x + } + } +} diff --git a/src/main/scala/millfork/output/MosAssembler.scala b/src/main/scala/millfork/output/MosAssembler.scala index b063bafb..d0fd913d 100644 --- a/src/main/scala/millfork/output/MosAssembler.scala +++ b/src/main/scala/millfork/output/MosAssembler.scala @@ -1,6 +1,6 @@ package millfork.output -import millfork.assembly.mos.opt.{HudsonOptimizations, JumpFixing, JumpShortening} +import millfork.assembly.mos.opt.{HudsonOptimizations, JumpFixing, JumpFollowing, JumpShortening} import millfork.assembly._ import millfork.env._ import millfork.error.{ConsoleLogger, FatalErrorReporting} @@ -24,7 +24,7 @@ class MosAssembler(program: Program, val optimizationContext = OptimizationContext(options, Map(), f.environment.maybeGet[ThingInMemory]("__reg"), Set()) if (actuallyOptimize) { val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(f, code, optimizationContext) else code - JumpShortening(f, JumpShortening(f, JumpFixing(f, finalCode, options), optimizationContext), optimizationContext) + JumpShortening(f, JumpShortening(f, JumpFixing(f, JumpFollowing(options, finalCode), options), optimizationContext), optimizationContext) } else JumpFixing(f, code, options) } diff --git a/src/main/scala/millfork/output/Z80Assembler.scala b/src/main/scala/millfork/output/Z80Assembler.scala index 6115aa75..1d4de311 100644 --- a/src/main/scala/millfork/output/Z80Assembler.scala +++ b/src/main/scala/millfork/output/Z80Assembler.scala @@ -2,10 +2,9 @@ package millfork.output import millfork.{CompilationFlag, CompilationOptions, Cpu, Platform} import millfork.assembly.z80._ -import millfork.assembly.z80.opt.JumpShortening +import millfork.assembly.z80.opt.{ConditionalInstructions, JumpFollowing, JumpShortening} import millfork.compiler.z80.Z80Compiler import millfork.env._ -import millfork.error.ConsoleLogger import millfork.node.{NiceFunctionProperty, Program, ZRegister} import scala.collection.mutable @@ -18,7 +17,7 @@ class Z80Assembler(program: Program, platform: Platform) extends AbstractAssembler[ZLine](program, rootEnv, platform, Z80InliningCalculator, Z80Compiler) { override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[ZLine]): List[ZLine] = { if (actuallyOptimize) { - JumpShortening(f, code, options) + JumpShortening(f, ConditionalInstructions(options, JumpFollowing(options, code)), options) } else code } @@ -393,12 +392,10 @@ class Z80Assembler(program: Program, case ZLine(CALL, IfFlagClear(ZFlag.Z), param, _) => - requireIntel8080() writeByte(bank, index, 0xc4) writeWord(bank, index + 1, param) index + 3 case ZLine(CALL, IfFlagClear(ZFlag.C), param, _) => - requireIntel8080() writeByte(bank, index, 0xd4) writeWord(bank, index + 1, param) index + 3 @@ -414,12 +411,10 @@ class Z80Assembler(program: Program, index + 3 case ZLine(CALL, IfFlagSet(ZFlag.Z), param, _) => - requireIntel8080() writeByte(bank, index, 0xcc) writeWord(bank, index + 1, param) index + 3 case ZLine(CALL, IfFlagSet(ZFlag.C), param, _) => - requireIntel8080() writeByte(bank, index, 0xdc) writeWord(bank, index + 1, param) index + 3 @@ -491,11 +486,9 @@ class Z80Assembler(program: Program, index + 2 case ZLine(RET, IfFlagClear(ZFlag.Z), _, _) => - requireIntel8080() writeByte(bank, index, 0xc0) index + 1 case ZLine(RET, IfFlagClear(ZFlag.C), _, _) => - requireIntel8080() writeByte(bank, index, 0xd0) index + 1 case ZLine(RET, IfFlagClear(ZFlag.P), _, _) => @@ -508,11 +501,9 @@ class Z80Assembler(program: Program, index + 1 case ZLine(RET, IfFlagSet(ZFlag.Z), _, _) => - requireIntel8080() writeByte(bank, index, 0xc8) index + 1 case ZLine(RET, IfFlagSet(ZFlag.C), _, _) => - requireIntel8080() writeByte(bank, index, 0xd8) index + 1 case ZLine(RET, IfFlagSet(ZFlag.P), _, _) => diff --git a/src/test/scala/millfork/test/EnumSuite.scala b/src/test/scala/millfork/test/EnumSuite.scala index 10abd06d..e684f539 100644 --- a/src/test/scala/millfork/test/EnumSuite.scala +++ b/src/test/scala/millfork/test/EnumSuite.scala @@ -1,7 +1,7 @@ package millfork.test import millfork.Cpu -import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, ShouldNotCompile} +import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun, ShouldNotCompile} import org.scalatest.{FunSuite, Matchers} /** @@ -40,7 +40,7 @@ class EnumSuite extends FunSuite with Matchers { } test("Enum arrays") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( """ | enum ugly { | a @@ -52,7 +52,7 @@ class EnumSuite extends FunSuite with Matchers { | void main () { | if a2[a] != 6 { crash() } | } - | asm void crash() { + | asm noinline void crash() { | #if ARCH_6502 | sta $bfff | rts