1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-12-23 23:30:22 +00:00

Extra optimization pass over non-inlined functions (implements #43)

This commit is contained in:
Karol Stasiak 2020-02-27 01:33:34 +01:00
parent 6e65cd1902
commit 21ffbfc466
11 changed files with 127 additions and 16 deletions

View File

@ -9,7 +9,7 @@ void main() {
ensure_mixedcase()
#if CBM_64 || CBM_264
set_bg_color(green)
set_bg_color(white)
#endif
#if CBM_64 || CBM_264 || ZX_SPECTRUM
set_border(red)

View File

@ -5,10 +5,10 @@ import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Paths}
import java.util.Locale
import millfork.assembly.m6809.opt.M6809OptimizationPresets
import millfork.assembly.m6809.opt.{M6809OptimizationPresets, VeryLateM6809AssemblyOptimizations}
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.opt._
import millfork.assembly.z80.opt.Z80OptimizationPresets
import millfork.assembly.z80.opt.{VeryLateI80AssemblyOptimizations, Z80OptimizationPresets}
import millfork.buildinfo.BuildInfo
import millfork.cli.{CliParser, CliStatus}
import millfork.compiler.LabelGenerator
@ -273,7 +273,7 @@ object Main {
// compile
val assembler = new MosAssembler(program, env, platform)
val result = assembler.assemble(callGraph, assemblyOptimizations, options)
val result = assembler.assemble(callGraph, assemblyOptimizations, options, if (optLevel <= 1) (_,_) => Nil else VeryLateMosAssemblyOptimizations.All)
options.log.assertNoErrors("Codegen failed")
options.log.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
options.log.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B")
@ -313,7 +313,7 @@ object Main {
// compile
val assembler = new Z80Assembler(program, env, platform)
val result = assembler.assemble(callGraph, assemblyOptimizations, options)
val result = assembler.assemble(callGraph, assemblyOptimizations, options, if (optLevel <= 1) (_,_) => Nil else VeryLateI80AssemblyOptimizations.All)
options.log.assertNoErrors("Codegen failed")
options.log.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
options.log.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B")
@ -343,7 +343,7 @@ object Main {
// compile
val assembler = new M6809Assembler(program, env, platform)
val result = assembler.assemble(callGraph, assemblyOptimizations, options)
val result = assembler.assemble(callGraph, assemblyOptimizations, options, if (optLevel <= 1) (_,_) => Nil else VeryLateM6809AssemblyOptimizations.All)
options.log.assertNoErrors("Codegen failed")
options.log.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
options.log.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B")
@ -376,7 +376,7 @@ object Main {
// compile
val assembler = new Z80ToX86Crossassembler(program, env, platform)
val result = assembler.assemble(callGraph, assemblyOptimizations, options)
val result = assembler.assemble(callGraph, assemblyOptimizations, options, if (optLevel <= 1) (_,_) => Nil else VeryLateI80AssemblyOptimizations.All)
options.log.assertNoErrors("Codegen failed")
options.log.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
options.log.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B")

View File

@ -0,0 +1,18 @@
package millfork.assembly.m6809.opt
import millfork.CompilationOptions
import millfork.assembly.AssemblyOptimization
import millfork.assembly.m6809.MLine
import millfork.node.NiceFunctionProperty
/**
* @author Karol Stasiak
*/
object VeryLateM6809AssemblyOptimizations {
def All(nice: Set[NiceFunctionProperty], options: CompilationOptions): Seq[AssemblyOptimization[MLine]] = {
val result = Seq.newBuilder[AssemblyOptimization[MLine]]
// TODO: add stuff here
result.result()
}
}

View File

@ -0,0 +1,59 @@
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.{AssemblyLine, State}
import millfork.compiler.mos.MosReturnDispatch
import millfork.node.{MosNiceFunctionProperty, NiceFunctionProperty}
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
/**
* @author Karol Stasiak
*/
//noinspection ZeroIndexToHead
object VeryLateMosAssemblyOptimizations {
val StoresOfImmediatesDifferingByOneViaX = new RuleBasedAssemblyOptimization("",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate) & MatchNumericImmediate(0) & HasIndex8) ~
(Elidable & HasOpcode(STA) & HasAddrModeIn(ZeroPage, Absolute) & DoesntMatterWhatItDoesWith(State.A, State.X)) ~
(Linear & Not(HasOpcode(LDA) & HasAddrMode(Immediate))).*.capture(10) ~
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate) & MatchNumericImmediate(1) & HasIndex8) ~
Where(ctx => ctx.get[Int](0).+(1).&(0xff) == ctx.get[Int](1)) ~
(Elidable & HasOpcode(STA) & HasAddrModeIn(ZeroPage, Absolute) & DoesntMatterWhatItDoesWith(State.A, State.X)) ~~> { code =>
code(0).copy(opcode = LDX) ::
code(1).copy(opcode = STX) :: (
code.drop(2).dropRight(2) ++ List(
AssemblyLine.implied(INX).pos(code(code.size - 2).source),
code.last.copy(opcode = STX)))
}
)
val StoresOfImmediatesDifferingByOneViaY = new RuleBasedAssemblyOptimization("",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate) & MatchNumericImmediate(0) & HasIndex8) ~
(Elidable & HasOpcode(STA) & HasAddrModeIn(ZeroPage, Absolute) & DoesntMatterWhatItDoesWith(State.A, State.Y)) ~
(Linear & Not(HasOpcode(LDA) & HasAddrMode(Immediate))).*.capture(10) ~
(Elidable & HasOpcode(LDA) & HasAddrMode(Immediate) & MatchNumericImmediate(1) & HasIndex8) ~
Where(ctx => ctx.get[Int](0).+(1).&(0xff) == ctx.get[Int](1)) ~
(Elidable & HasOpcode(STA) & HasAddrModeIn(ZeroPage, Absolute) & DoesntMatterWhatItDoesWith(State.A, State.Y)) ~~> { code =>
code(0).copy(opcode = LDY) ::
code(1).copy(opcode = STY) :: (
code.drop(2).dropRight(2) ++ List(
AssemblyLine.implied(INY).pos(code(code.size - 2).source),
code.last.copy(opcode = STY)))
}
)
def All(nice: Set[NiceFunctionProperty], options: CompilationOptions): Seq[AssemblyOptimization[AssemblyLine]] = {
val result = Seq.newBuilder[AssemblyOptimization[AssemblyLine]]
if (!nice(MosNiceFunctionProperty.DoesntChangeX)){
result += StoresOfImmediatesDifferingByOneViaX
}
if (!nice(MosNiceFunctionProperty.DoesntChangeY)){
result += StoresOfImmediatesDifferingByOneViaY
}
result.result()
}
}

View File

@ -0,0 +1,19 @@
package millfork.assembly.z80.opt
import millfork.CompilationOptions
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.ZLine
import millfork.node.NiceFunctionProperty
/**
* @author Karol Stasiak
*/
object VeryLateI80AssemblyOptimizations {
def All(nice: Set[NiceFunctionProperty], options: CompilationOptions): Seq[AssemblyOptimization[ZLine]] = {
val result = Seq.newBuilder[AssemblyOptimization[ZLine]]
// TODO: add stuff here
result.result()
}
}

View File

@ -195,7 +195,11 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
protected def subbyte(c: Constant, index: Int, totalSize: Int): Constant = if (platform.isBigEndian) c.subbyteBe(index, totalSize) else c.subbyte(index)
def assemble(callGraph: CallGraph, unfilteredOptimizations: Seq[AssemblyOptimization[T]], options: CompilationOptions): AssemblerOutput = {
def assemble(
callGraph: CallGraph,
unfilteredOptimizations: Seq[AssemblyOptimization[T]],
options: CompilationOptions,
veryLateOptimizations: (Set[NiceFunctionProperty], CompilationOptions) => Seq[AssemblyOptimization[T]]): AssemblerOutput = {
mem.programName = options.outputFileName.getOrElse("MILLFORK")
val platform = options.platform
val variableAllocators = platform.variableAllocators
@ -277,7 +281,14 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
case None =>
log.trace("Not inlining " + f, function.position)
functionsThatCanBeCalledFromInlinedFunctions += function.name
compiledFunctions(f) = NormalCompiledFunction(function.declaredBank.getOrElse(platform.defaultCodeBank), code, function.address.isDefined, function.alignment)
val thisFunctionNiceProperties = niceFunctionProperties.filter(_._2.==(f)).map(_._1).toSet
val labelMapImm = labelMap.toMap
val niceFunctionPropertiesImm = niceFunctionProperties.toSet
val extraOptimizedCode = veryLateOptimizations(thisFunctionNiceProperties, options).foldLeft(code) { (c, opt) =>
val code = opt.optimize(function, c, OptimizationContext(options, labelMapImm, env.maybeGet[ThingInMemory]("__reg"), niceFunctionPropertiesImm))
if (code eq c) code else quickSimplify(code)
}
compiledFunctions(f) = NormalCompiledFunction(function.declaredBank.getOrElse(platform.defaultCodeBank), extraOptimizedCode, function.address.isDefined, function.alignment)
optimizedCodeSize += code.map(_.sizeInBytes).sum
if (options.flag(CompilationFlag.InterproceduralOptimization)) {
gatherNiceFunctionProperties(options, niceFunctionProperties, function, code)

View File

@ -10,6 +10,7 @@ import javax.swing.SwingUtilities
import millfork._
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.ZLine
import millfork.assembly.z80.opt.VeryLateI80AssemblyOptimizations
import millfork.compiler.z80.Z80Compiler
import millfork.compiler.{CompilationContext, LabelGenerator}
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
@ -135,7 +136,7 @@ class EmuI86Run(nodeOptimizations: List[NodeOptimization], assemblyOptimizations
val env2 = new Environment(None, "", CpuFamily.I80, options)
env2.collectDeclarations(program, options)
val assembler = new Z80ToX86Crossassembler(program, env2, platform)
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
val output = assembler.assemble(callGraph, assemblyOptimizations, options, VeryLateI80AssemblyOptimizations.All)
println(";;; compiled: -----------------")
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("////; DISCARD_")).foreach(println)
println(";;; ---------------------------")

View File

@ -7,6 +7,7 @@ import fastparse.core.Parsed.{Failure, Success}
import millfork._
import millfork.assembly.AssemblyOptimization
import millfork.assembly.m6809.MLine
import millfork.assembly.m6809.opt.VeryLateM6809AssemblyOptimizations
import millfork.compiler.m6809.M6809Compiler
import millfork.compiler.{CompilationContext, LabelGenerator}
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
@ -144,7 +145,7 @@ class EmuM6809Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizat
val env2 = new Environment(None, "", CpuFamily.M6502, options)
env2.collectDeclarations(program, options)
val assembler = new M6809Assembler(program, env2, platform)
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
val output = assembler.assemble(callGraph, assemblyOptimizations, options, VeryLateM6809AssemblyOptimizations.All)
println(";;; compiled: -----------------")
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
println(";;; ---------------------------")

View File

@ -9,6 +9,7 @@ import com.loomcom.symon.{Bus, Cpu, CpuState}
import fastparse.core.Parsed.{Failure, Success}
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.opt.VeryLateMosAssemblyOptimizations
import millfork.compiler.{CompilationContext, LabelGenerator}
import millfork.compiler.mos.MosCompiler
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
@ -221,7 +222,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
val env2 = new Environment(None, "", CpuFamily.M6502, options)
env2.collectDeclarations(program, options)
val assembler = new MosAssembler(program, env2, platform)
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
val output = assembler.assemble(callGraph, assemblyOptimizations, options, VeryLateMosAssemblyOptimizations.All)
println(";;; compiled: -----------------")
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
println(";;; ---------------------------")

View File

@ -18,6 +18,7 @@ import millfork.node.opt.NodeOptimization
import millfork.output.{MemoryBank, Z80Assembler}
import millfork.parser.{MosParser, PreprocessingResult, Preprocessor, Z80Parser}
import millfork._
import millfork.assembly.z80.opt.VeryLateI80AssemblyOptimizations
import millfork.compiler.z80.Z80Compiler
import org.scalatest.Matchers
@ -143,7 +144,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
val env2 = new Environment(None, "", CpuFamily.I80, options)
env2.collectDeclarations(program, options)
val assembler = new Z80Assembler(program, env2, platform)
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
val output = assembler.assemble(callGraph, assemblyOptimizations, options, VeryLateI80AssemblyOptimizations.All)
println(";;; compiled: -----------------")
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("////; DISCARD_")).foreach(println)
println(";;; ---------------------------")

View File

@ -95,17 +95,17 @@ object ShouldNotCompile extends Matchers {
cpuFamily match {
case CpuFamily.M6502 =>
val assembler = new MosAssembler(program, env2, platform)
val output = assembler.assemble(callGraph, Nil, options)
val output = assembler.assemble(callGraph, Nil, options, (_, _) => Nil)
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
fail("Failed: Compilation succeeded for 6502")
case CpuFamily.I80 =>
val assembler = new Z80Assembler(program, env2, platform)
val output = assembler.assemble(callGraph, Nil, options)
val output = assembler.assemble(callGraph, Nil, options, (_, _) => Nil)
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
fail("Failed: Compilation succeeded for Z80")
case CpuFamily.M6809 =>
val assembler = new M6809Assembler(program, env2, platform)
val output = assembler.assemble(callGraph, Nil, options)
val output = assembler.assemble(callGraph, Nil, options, (_, _) => Nil)
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
fail("Failed: Compilation succeeded for 6809")
case _ =>