mirror of
https://github.com/KarolS/millfork.git
synced 2024-10-09 13:57:05 +00:00
Following jumps. Generating conditional returns/calls.
This commit is contained in:
parent
2d0f3a5a12
commit
9581891d66
62
src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala
Normal file
62
src/main/scala/millfork/assembly/mos/opt/JumpFollowing.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,13 +17,19 @@ object ZFlag extends Enumeration {
|
|||||||
val AllButZ: Seq[Value] = Seq(P, C, H, N, S)
|
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 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 {
|
case class OneRegister(register: ZRegister.Value) extends ZRegisters {
|
||||||
// if (register == ZRegister.MEM_IY_D || register == ZRegister.MEM_IX_D) ???
|
// if (register == ZRegister.MEM_IY_D || register == ZRegister.MEM_IX_D) ???
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala
Normal file
47
src/main/scala/millfork/assembly/z80/opt/JumpFollowing.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package millfork.output
|
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.assembly._
|
||||||
import millfork.env._
|
import millfork.env._
|
||||||
import millfork.error.{ConsoleLogger, FatalErrorReporting}
|
import millfork.error.{ConsoleLogger, FatalErrorReporting}
|
||||||
@ -24,7 +24,7 @@ class MosAssembler(program: Program,
|
|||||||
val optimizationContext = OptimizationContext(options, Map(), f.environment.maybeGet[ThingInMemory]("__reg"), Set())
|
val optimizationContext = OptimizationContext(options, Map(), f.environment.maybeGet[ThingInMemory]("__reg"), Set())
|
||||||
if (actuallyOptimize) {
|
if (actuallyOptimize) {
|
||||||
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(f, code, optimizationContext) else code
|
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)
|
else JumpFixing(f, code, options)
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,9 @@ package millfork.output
|
|||||||
|
|
||||||
import millfork.{CompilationFlag, CompilationOptions, Cpu, Platform}
|
import millfork.{CompilationFlag, CompilationOptions, Cpu, Platform}
|
||||||
import millfork.assembly.z80._
|
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.compiler.z80.Z80Compiler
|
||||||
import millfork.env._
|
import millfork.env._
|
||||||
import millfork.error.ConsoleLogger
|
|
||||||
import millfork.node.{NiceFunctionProperty, Program, ZRegister}
|
import millfork.node.{NiceFunctionProperty, Program, ZRegister}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
@ -18,7 +17,7 @@ class Z80Assembler(program: Program,
|
|||||||
platform: Platform) extends AbstractAssembler[ZLine](program, rootEnv, platform, Z80InliningCalculator, Z80Compiler) {
|
platform: Platform) extends AbstractAssembler[ZLine](program, rootEnv, platform, Z80InliningCalculator, Z80Compiler) {
|
||||||
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[ZLine]): List[ZLine] = {
|
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[ZLine]): List[ZLine] = {
|
||||||
if (actuallyOptimize) {
|
if (actuallyOptimize) {
|
||||||
JumpShortening(f, code, options)
|
JumpShortening(f, ConditionalInstructions(options, JumpFollowing(options, code)), options)
|
||||||
} else code
|
} else code
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,12 +392,10 @@ class Z80Assembler(program: Program,
|
|||||||
|
|
||||||
|
|
||||||
case ZLine(CALL, IfFlagClear(ZFlag.Z), param, _) =>
|
case ZLine(CALL, IfFlagClear(ZFlag.Z), param, _) =>
|
||||||
requireIntel8080()
|
|
||||||
writeByte(bank, index, 0xc4)
|
writeByte(bank, index, 0xc4)
|
||||||
writeWord(bank, index + 1, param)
|
writeWord(bank, index + 1, param)
|
||||||
index + 3
|
index + 3
|
||||||
case ZLine(CALL, IfFlagClear(ZFlag.C), param, _) =>
|
case ZLine(CALL, IfFlagClear(ZFlag.C), param, _) =>
|
||||||
requireIntel8080()
|
|
||||||
writeByte(bank, index, 0xd4)
|
writeByte(bank, index, 0xd4)
|
||||||
writeWord(bank, index + 1, param)
|
writeWord(bank, index + 1, param)
|
||||||
index + 3
|
index + 3
|
||||||
@ -414,12 +411,10 @@ class Z80Assembler(program: Program,
|
|||||||
index + 3
|
index + 3
|
||||||
|
|
||||||
case ZLine(CALL, IfFlagSet(ZFlag.Z), param, _) =>
|
case ZLine(CALL, IfFlagSet(ZFlag.Z), param, _) =>
|
||||||
requireIntel8080()
|
|
||||||
writeByte(bank, index, 0xcc)
|
writeByte(bank, index, 0xcc)
|
||||||
writeWord(bank, index + 1, param)
|
writeWord(bank, index + 1, param)
|
||||||
index + 3
|
index + 3
|
||||||
case ZLine(CALL, IfFlagSet(ZFlag.C), param, _) =>
|
case ZLine(CALL, IfFlagSet(ZFlag.C), param, _) =>
|
||||||
requireIntel8080()
|
|
||||||
writeByte(bank, index, 0xdc)
|
writeByte(bank, index, 0xdc)
|
||||||
writeWord(bank, index + 1, param)
|
writeWord(bank, index + 1, param)
|
||||||
index + 3
|
index + 3
|
||||||
@ -491,11 +486,9 @@ class Z80Assembler(program: Program,
|
|||||||
index + 2
|
index + 2
|
||||||
|
|
||||||
case ZLine(RET, IfFlagClear(ZFlag.Z), _, _) =>
|
case ZLine(RET, IfFlagClear(ZFlag.Z), _, _) =>
|
||||||
requireIntel8080()
|
|
||||||
writeByte(bank, index, 0xc0)
|
writeByte(bank, index, 0xc0)
|
||||||
index + 1
|
index + 1
|
||||||
case ZLine(RET, IfFlagClear(ZFlag.C), _, _) =>
|
case ZLine(RET, IfFlagClear(ZFlag.C), _, _) =>
|
||||||
requireIntel8080()
|
|
||||||
writeByte(bank, index, 0xd0)
|
writeByte(bank, index, 0xd0)
|
||||||
index + 1
|
index + 1
|
||||||
case ZLine(RET, IfFlagClear(ZFlag.P), _, _) =>
|
case ZLine(RET, IfFlagClear(ZFlag.P), _, _) =>
|
||||||
@ -508,11 +501,9 @@ class Z80Assembler(program: Program,
|
|||||||
index + 1
|
index + 1
|
||||||
|
|
||||||
case ZLine(RET, IfFlagSet(ZFlag.Z), _, _) =>
|
case ZLine(RET, IfFlagSet(ZFlag.Z), _, _) =>
|
||||||
requireIntel8080()
|
|
||||||
writeByte(bank, index, 0xc8)
|
writeByte(bank, index, 0xc8)
|
||||||
index + 1
|
index + 1
|
||||||
case ZLine(RET, IfFlagSet(ZFlag.C), _, _) =>
|
case ZLine(RET, IfFlagSet(ZFlag.C), _, _) =>
|
||||||
requireIntel8080()
|
|
||||||
writeByte(bank, index, 0xd8)
|
writeByte(bank, index, 0xd8)
|
||||||
index + 1
|
index + 1
|
||||||
case ZLine(RET, IfFlagSet(ZFlag.P), _, _) =>
|
case ZLine(RET, IfFlagSet(ZFlag.P), _, _) =>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package millfork.test
|
package millfork.test
|
||||||
|
|
||||||
import millfork.Cpu
|
import millfork.Cpu
|
||||||
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, ShouldNotCompile}
|
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun, ShouldNotCompile}
|
||||||
import org.scalatest.{FunSuite, Matchers}
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +40,7 @@ class EnumSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Enum arrays") {
|
test("Enum arrays") {
|
||||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
"""
|
"""
|
||||||
| enum ugly {
|
| enum ugly {
|
||||||
| a
|
| a
|
||||||
@ -52,7 +52,7 @@ class EnumSuite extends FunSuite with Matchers {
|
|||||||
| void main () {
|
| void main () {
|
||||||
| if a2[a] != 6 { crash() }
|
| if a2[a] != 6 { crash() }
|
||||||
| }
|
| }
|
||||||
| asm void crash() {
|
| asm noinline void crash() {
|
||||||
| #if ARCH_6502
|
| #if ARCH_6502
|
||||||
| sta $bfff
|
| sta $bfff
|
||||||
| rts
|
| rts
|
||||||
|
Loading…
Reference in New Issue
Block a user