1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-04-11 08:37:00 +00:00

Improve interprocedural optimizations

This commit is contained in:
Karol Stasiak 2019-08-01 19:11:35 +02:00
parent 35fbdcdbb1
commit bb63a73f15
11 changed files with 201 additions and 27 deletions

View File

@ -3,8 +3,10 @@ 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, FlowCache}
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
import millfork.env._
import millfork.node.NiceFunctionProperty
import scala.util.control.Breaks._
/**
* @author Karol Stasiak
@ -17,6 +19,20 @@ object CoarseFlowAnalyzer {
cache.get(code).foreach(return _)
val compilationOptions = optimizationContext.options
val niceFunctionProperties = optimizationContext.niceFunctionProperties
def extractNiceConstant[T](callee: String)(matcher: NiceFunctionProperty => Option[T]): Status[T] = {
var result: Status[T] = AnyStatus
breakable {
niceFunctionProperties.foreach{ np =>
if (np._2 == callee) matcher(np._1) match {
case Some(x) =>
result = SingleStatus(x)
break
case _ =>
}
}
}
result
}
val ceFlag = compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)
val cmosFlag = compilationOptions.flag(CompilationFlag.EmitCmosOpcodes)
val initialStatus =
@ -58,12 +74,37 @@ object CoarseFlowAnalyzer {
case AssemblyLine0(JSR, _, MemoryAddressConstant(th)) =>
currentStatus = initialStatus.copy(
a = if (niceFunctionProperties(DoesntChangeA -> th.name)) currentStatus.a else AnyStatus,
a =
if (niceFunctionProperties(DoesntChangeA -> th.name)) currentStatus.a
else extractNiceConstant(th.name){
case SetsATo(x) => Some(x)
case _ => None
},
ah = if (niceFunctionProperties(DoesntChangeAH -> th.name)) currentStatus.ah else AnyStatus,
x = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.x else AnyStatus,
x = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.x
else extractNiceConstant(th.name){
case SetsXTo(x) => Some(x)
case _ => None
},
eqSX = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.eqSX else false,
eqSpX = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.eqSpX else false,
y = if (niceFunctionProperties(DoesntChangeY -> th.name)) currentStatus.y else AnyStatus,
y = if (niceFunctionProperties(DoesntChangeY -> th.name)) currentStatus.y
else extractNiceConstant(th.name){
case SetsYTo(x) => Some(x)
case _ => None
},
a0 = extractNiceConstant(th.name){
case Bit0OfA(x) => Some(x)
case _ => None
},
a7 = extractNiceConstant(th.name){
case Bit7OfA(x) => Some(x)
case _ => None
},
src = extractNiceConstant(th.name){
case SetsSourceOfNZ(x) => Some(x)
case _ => None
},
iz = if (niceFunctionProperties(DoesntChangeIZ -> th.name)) currentStatus.iz else AnyStatus,
c = if (niceFunctionProperties(DoesntChangeC -> th.name)) currentStatus.c else AnyStatus
)

View File

@ -1,10 +1,14 @@
package millfork.assembly.z80.opt
import millfork.assembly.OptimizationContext
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
import millfork.assembly.z80._
import millfork.env._
import millfork.node.ZRegister
import millfork.{CompilationFlag, CompilationOptions, Cpu}
import millfork.node.Z80NiceFunctionProperty.{DoesntChangeBC, DoesntChangeDE, DoesntChangeHL, SetsATo}
import millfork.node.{NiceFunctionProperty, ZRegister}
import millfork.CompilationFlag
import scala.util.control.Breaks._
/**
* @author Karol Stasiak
@ -13,14 +17,30 @@ object CoarseFlowAnalyzer {
val cache = new FlowCache[ZLine, CpuStatus]("z80 forward")
def analyze(f: NormalFunction, code: List[ZLine], compilationOptions: CompilationOptions): List[CpuStatus] = {
def analyze(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[CpuStatus] = {
cache.get(code).foreach(return _)
val initialStatus = CpuStatus()
val functionStartStatus = CpuStatus()
val emptyStatus = CpuStatus()
val flagArray = Array.fill[CpuStatus](code.length)(emptyStatus)
val codeArray = code.toArray
val compilationOptions = optimizationContext.options
val z80 = compilationOptions.flag(CompilationFlag.EmitZ80Opcodes)
val niceFunctionProperties = optimizationContext.niceFunctionProperties
def extractNiceConstant[T](callee: String)(matcher: NiceFunctionProperty => Option[T]): Status[T] = {
var result: Status[T] = AnyStatus
breakable {
niceFunctionProperties.foreach{ np =>
if (np._2 == callee) matcher(np._1) match {
case Some(x) =>
result = SingleStatus(x)
break
case _ =>
}
}
}
result
}
val preservesB: Set[String] = Set("__mul_u8u8u8")
val preservesC: Set[String] = if (z80) Set("__mul_u8u8u8") else Set()
@ -65,12 +85,17 @@ object CoarseFlowAnalyzer {
if (mayBeCalled) {
val result = if (preservesStackVariables) initialStatus.copy(memIx = currentStatus.memIx) else initialStatus
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
b = if (preservesB(n) || niceFunctionProperties(DoesntChangeBC -> n)) currentStatus.b else result.b,
c = if (preservesC(n) || niceFunctionProperties(DoesntChangeBC -> n)) currentStatus.c else result.c,
d = if (preservesD(n) || niceFunctionProperties(DoesntChangeDE -> n)) currentStatus.d else result.d,
e = if (preservesE(n) || niceFunctionProperties(DoesntChangeDE-> n)) currentStatus.e else result.e,
h = if (preservesH(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.h else result.h,
l = if (preservesL(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.l else result.l,
hl = if (preservesH(n) && preservesL(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.hl else result.hl,
a = extractNiceConstant(n){
case SetsATo(a) => Some(a)
case _ => None
},
)
}

View File

@ -1,6 +1,7 @@
package millfork.assembly.z80.opt
import millfork.CompilationOptions
import millfork.assembly.OptimizationContext
import millfork.assembly.z80.{ZLine, ZLine0, ZOpcode}
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
@ -31,10 +32,10 @@ object FlowAnalyzer {
private val EmptyCpuStatus = CpuStatus()
private val EmptyCpuImportance = CpuImportance()
def analyze(f: NormalFunction, code: List[ZLine], options: CompilationOptions, req: FlowInfoRequirement.Value): List[(FlowInfo, ZLine)] = {
def analyze(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext, req: FlowInfoRequirement.Value): List[(FlowInfo, ZLine)] = {
val forwardFlow = req match {
case FlowInfoRequirement.BothFlows | FlowInfoRequirement.ForwardFlow =>
() => CoarseFlowAnalyzer.analyze(f, code, options)
() => CoarseFlowAnalyzer.analyze(f, code, optimizationContext)
case FlowInfoRequirement.BackwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
() => List.fill(code.size)(EmptyCpuStatus)
}

View File

@ -45,7 +45,7 @@ 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 taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext.options, needsFlowInfo)
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext)
if (changed) optimized else code
}

View File

@ -22,7 +22,7 @@ class VariableStatus(val paramVariables: Set[String],
object VariableStatus {
def apply(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext, typFilter: Type => Boolean): Option[VariableStatus] = {
val flow = FlowAnalyzer.analyze(f, code, optimizationContext.options, FlowInfoRequirement.BothFlows)
val flow = FlowAnalyzer.analyze(f, code, optimizationContext, FlowInfoRequirement.BothFlows)
import millfork.node.ZRegister._
val paramVariables = f.params match {
case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>

View File

@ -2,6 +2,7 @@ package millfork.node
import millfork.assembly.Elidability
import millfork.assembly.m6809.{MAddrMode, MOpcode}
import millfork.assembly.mos.opt.SourceOfNZ
import millfork.assembly.mos.{AddrMode, Opcode}
import millfork.assembly.z80.{ZOpcode, ZRegisters}
import millfork.env.{Constant, ParamPassingConvention, Type}
@ -182,6 +183,12 @@ object MosNiceFunctionProperty {
case object DoesntChangeC extends NiceFunctionProperty("C")
case object DoesntConcernD extends NiceFunctionProperty("D")
case object DoesntChangeZpRegister extends NiceFunctionProperty("reg")
case class SetsSourceOfNZ(sourceOfNZ: SourceOfNZ) extends NiceFunctionProperty(sourceOfNZ + "NZ")
case class SetsXTo(value: Int) extends NiceFunctionProperty("Y=" + value)
case class SetsYTo(value: Int) extends NiceFunctionProperty("Z=" + value)
case class SetsATo(value: Int) extends NiceFunctionProperty("A=" + value)
case class Bit0OfA(value: Boolean) extends NiceFunctionProperty("A0=" + value)
case class Bit7OfA(value: Boolean) extends NiceFunctionProperty("A7=" + value)
}
object Z80NiceFunctionProperty {
@ -189,6 +196,7 @@ object Z80NiceFunctionProperty {
case object DoesntChangeDE extends NiceFunctionProperty("DE")
case object DoesntChangeHL extends NiceFunctionProperty("HL")
case object DoesntChangeIY extends NiceFunctionProperty("IY")
case class SetsATo(value: Int) extends NiceFunctionProperty("A=" + value)
}
object MosRegister extends Enumeration {

View File

@ -243,7 +243,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
log.debug("Inlining " + f, function.position)
inlinedFunctions += f -> c
val tmp = mutable.Set[(NiceFunctionProperty, String)]()
gatherNiceFunctionProperties(tmp, f, c)
gatherNiceFunctionProperties(options, tmp, function, c)
if (tmp.exists(_._1 == IsLeaf)) {
functionsThatCanBeCalledFromInlinedFunctions += function.name
}
@ -254,7 +254,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
compiledFunctions(f) = NormalCompiledFunction(function.declaredBank.getOrElse(platform.defaultCodeBank), code, function.address.isDefined, function.alignment)
optimizedCodeSize += code.map(_.sizeInBytes).sum
if (options.flag(CompilationFlag.InterproceduralOptimization)) {
gatherNiceFunctionProperties(niceFunctionProperties, f, code)
gatherNiceFunctionProperties(options, niceFunctionProperties, function, code)
}
}
function.environment.removedThings.foreach(env.removeVariable)
@ -625,7 +625,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
def quickSimplify(code: List[T]): List[T]
def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[T]): Unit
def gatherNiceFunctionProperties(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[T]): Unit
def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[T]): List[T]

View File

@ -23,7 +23,7 @@ class M6809Assembler(program: Program,
override def quickSimplify(code: List[MLine]): List[MLine] = code
override def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[MLine]): Unit = ()
override def gatherNiceFunctionProperties(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[MLine]): Unit = ()
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[MLine]): List[MLine] = code

View File

@ -1,12 +1,13 @@
package millfork.output
import millfork.assembly.mos.opt.{HudsonOptimizations, JumpFixing, JumpFollowing, JumpShortening}
import millfork.assembly.mos.opt.{CoarseFlowAnalyzer, CpuStatus, HudsonOptimizations, JumpFixing, JumpFollowing, JumpShortening, SourceOfNZ}
import millfork.assembly._
import millfork.env._
import millfork.error.{ConsoleLogger, FatalErrorReporting}
import millfork.node.{MosNiceFunctionProperty, NiceFunctionProperty, Program}
import millfork._
import millfork.assembly.mos._
import millfork.assembly.opt.{SingleStatus, Status}
import millfork.compiler.mos.MosCompiler
import scala.annotation.tailrec
@ -105,16 +106,44 @@ 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 = {
override def gatherNiceFunctionProperties(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[AssemblyLine]): Unit = {
import Opcode._
import AddrMode._
import MosNiceFunctionProperty._
import NiceFunctionProperty._
val functionName = function.name
if (isNaughty(code)) return
val localLabels = code.flatMap {
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) => Some(l)
case _ => None
}.toSet
if (code.exists {
case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if OpcodeClasses.AllDirectJumps(op) => !localLabels(l)
case AssemblyLine0(op, _, _) if OpcodeClasses.AllDirectJumps(op) => true
case AssemblyLine0(BRK | RTI, _, _) => true
case _ => false
}) return
val optimizationContext = OptimizationContext(options, Map(), function.environment.maybeGet[ThingInMemory]("__reg"), Set())
val flow = CoarseFlowAnalyzer.analyze(function, code, optimizationContext)
def rtsPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: Status[T] => Option[NiceFunctionProperty]): Unit = {
val statuses = code.zipWithIndex.flatMap{
case (AssemblyLine0(RTS, _, _), ix) => Some(extractor(flow(ix)))
case _ => None
}.toSet
statuses.toSeq match {
case Seq(only) =>
niceFunctionProperty(only).foreach { np =>
niceFunctionProperties += (np -> functionName)
}
case _ =>
}
}
def simpleRtsPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: T => NiceFunctionProperty): Unit = {
rtsPropertyScan(extractor) {
case SingleStatus(x) => Some(niceFunctionProperty(x))
case _ => None
}
}
def genericPropertyScan(niceFunctionProperty: NiceFunctionProperty)(predicate: AssemblyLine => Boolean): Unit = {
val preserved = code.forall {
case AssemblyLine0(JSR | BSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(th)) => niceFunctionProperties(niceFunctionProperty -> th.name)
@ -171,6 +200,12 @@ class MosAssembler(program: Program,
case AssemblyLine0(JMP, Absolute, th:Thing) => th.name.startsWith(".")
case _ => true
}
simpleRtsPropertyScan(_.src)(SetsSourceOfNZ)
simpleRtsPropertyScan(_.a0)(Bit0OfA)
simpleRtsPropertyScan(_.a7)(Bit7OfA)
simpleRtsPropertyScan(_.a)(SetsATo)
simpleRtsPropertyScan(_.x)(SetsXTo)
simpleRtsPropertyScan(_.y)(SetsYTo)
}
override def bytePseudoopcode: String = "!byte"

View File

@ -1,12 +1,16 @@
package millfork.output
import millfork.assembly.OptimizationContext
import millfork.assembly.opt.{SingleStatus, Status}
import millfork.{CompilationFlag, CompilationOptions, Cpu, Platform}
import millfork.assembly.z80.{ZOpcode, _}
import millfork.assembly.z80.opt.{ConditionalInstructions, JumpFollowing, JumpShortening}
import millfork.assembly.z80.opt.{CoarseFlowAnalyzer, ConditionalInstructions, CpuStatus, JumpFollowing, JumpShortening}
import millfork.compiler.z80.Z80Compiler
import millfork.env._
import millfork.node.Z80NiceFunctionProperty.{DoesntChangeBC, DoesntChangeDE, DoesntChangeHL, DoesntChangeIY, SetsATo}
import millfork.node.{NiceFunctionProperty, Program, ZRegister}
import scala.annotation.tailrec
import scala.collection.mutable
/**
@ -693,8 +697,68 @@ class Z80Assembler(program: Program,
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
override def gatherNiceFunctionProperties(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[ZLine]): Unit = {
import ZOpcode._
val functionName = function.name
if (isNaughty(code)) return
val localLabels = code.flatMap {
case ZLine0(LABEL, _, MemoryAddressConstant(Label(l))) => Some(l)
case _ => None
}.toSet
if (code.exists {
case ZLine0(JP | JR, _, MemoryAddressConstant(Label(l))) => !localLabels(l)
case ZLine0(JP | JR, _, _) => true
case ZLine0(RST, _, _) => true
case _ => false
}) return
val optimizationContext = OptimizationContext(options, Map(), None, Set())
val flow = CoarseFlowAnalyzer.analyze(function, code, optimizationContext)
def retPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: Status[T] => Option[NiceFunctionProperty]): Unit = {
val statuses = code.zipWithIndex.flatMap{
case (ZLine0(RET | RETI | RETN, _, _), ix) => Some(extractor(flow(ix)))
case _ => None
}.toSet
statuses.toSeq match {
case Seq(only) =>
niceFunctionProperty(only).foreach { np =>
niceFunctionProperties += (np -> functionName)
}
case _ =>
}
}
def simpleRetPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: T => NiceFunctionProperty): Unit = {
retPropertyScan(extractor) {
case SingleStatus(x) => Some(niceFunctionProperty(x))
case _ => None
}
}
def genericPropertyScan(niceFunctionProperty: NiceFunctionProperty)(predicate: ZLine => Boolean): Unit = {
val preserved = code.forall {
case ZLine0(JP | JR, _, MemoryAddressConstant(Label(label))) => localLabels(label)
case ZLine0(CALL | JP | JR, _, MemoryAddressConstant(th)) => niceFunctionProperties(niceFunctionProperty -> th.name)
case ZLine0(CALL | JP | JR, _, _) => false
case l => predicate(l)
}
if (preserved) {
niceFunctionProperties += (niceFunctionProperty -> functionName)
}
}
genericPropertyScan(DoesntChangeHL)(l => !l.changesRegister(ZRegister.HL))
genericPropertyScan(DoesntChangeDE)(l => !l.changesRegister(ZRegister.DE))
genericPropertyScan(DoesntChangeBC)(l => !l.changesRegister(ZRegister.BC))
genericPropertyScan(DoesntChangeIY)(l => !l.changesRegister(ZRegister.IY))
simpleRetPropertyScan(_.a)(SetsATo)
}
@tailrec
private def isNaughty(code: List[ZLine]): Boolean = {
import ZOpcode._
code match {
case ZLine0(JP, OneRegister(_), _) :: _ => true
case ZLine0(PUSH, _, _) :: ZLine0(RET | RETI | RETN, _, _) :: _ => true
case _ :: xs => isNaughty(xs)
case Nil => false
}
}
override def bytePseudoopcode: String = "DB"

View File

@ -26,7 +26,7 @@ class Z80ToX86Crossassembler(program: Program,
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 = {
override def gatherNiceFunctionProperties(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[ZLine]): Unit = {
// do nothing yet
}