1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-08-15 14:29:27 +00:00

Optimization refactoring:

– -Of should imply -finline
– certain optimizations should be controllable
– unused global symbols should be removed even if we're not optimizing
This commit is contained in:
Karol Stasiak 2018-12-27 01:18:29 +01:00
parent b77f9dd5f8
commit f12463aef0
13 changed files with 162 additions and 58 deletions

View File

@ -139,35 +139,68 @@ Use a software stack for stack variables.
## Optimization options ## Optimization options
* `-O0` Disable all optimizations. * `-O0` Disable all optimizations except unused global symbol removal.
* `-O`, `-O2` ... `-O8` Optimize code, various levels. For most code, anything above `-O4` doesn't improve it anymore. * `-O`, `-O2` ... `-O8` Optimize code, various levels. For most code, anything above `-O4` doesn't improve it anymore.
* `-O9` Optimize code using superoptimizer (experimental). Computationally expensive, decent results. * `-O9` Optimize code using superoptimizer (experimental). Computationally very expensive, decent results.
* `-finline`, `-fno-inline` Whether should inline functions automatically. * `-finline`, `-fno-inline` Whether should inline functions automatically.
See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains. See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
`.ini` equivalent: `inline`. `.ini` equivalent: `inline`.
Default: no
* `-fipo`, `-fno-ipo` Whether should perform interprocedural optimization. * `-fipo`, `-fno-ipo` Whether should perform interprocedural optimization.
It enables certain optimization similar to what inlining would enable, but without actual inlining. It enables certain optimization similar to what inlining would enable, but without actual inlining.
`.ini` equivalent: `ipo`. `.ini` equivalent: `ipo`.
Default: no.
* `-fregister-variables`, `-fno-register-variables` Whether should allow moving local variables into CPU registers.
Default: yes.
* `-foptimize-stdlib`, `-fno-optimize-stdlib` * `-foptimize-stdlib`, `-fno-optimize-stdlib`
Whether should replace some standard library calls with constant parameters with more efficient variants. Whether should replace some standard library calls with constant parameters with more efficient variants.
Currently affects `putstrz` and `strzlen`, but may affect more functions in the future. Currently affects `putstrz` and `strzlen`, but may affect more functions in the future.
`.ini` equivalent: `optimize_stdlib`. `.ini` equivalent: `optimize_stdlib`.
Default: no.
* `-ffunction-fallthrough`, `-fno-function-fallthrough`
Whether should replace a tail call by simply putting one function after another.
`.ini` equivalent: `function_fallthrough`.
Default: yes.
* `-ffunction-deduplication`, `-fno-function-deduplication`
Whether identical functions should be merged into one function.
`.ini` equivalent: `function_deduplication`.
Default: yes.
* `-fsubroutine-extraction`, `-fno-subroutine-extraction`
Whether identical fragments of functions should be extracted into subroutines.
`.ini` equivalent: `subroutine_extraction`.
Default: no.
* `-Os`, `--size` Optimize for size, sacrificing some speed (experimental). * `-Os`, `--size` Optimize for size, sacrificing some speed (experimental).
Also enables `-fcode-deduplication`.
* `-Of`, `--fast` Optimize for speed, even if it increases the size a bit (experimental). * `-Of`, `--fast` Optimize for speed, even if it increases the size a bit (experimental).
Also enables `-finline`.
* `-Ob`, `--blast-processing` Optimize for speed, even if it increases the size a lot (experimental). * `-Ob`, `--blast-processing` Optimize for speed, even if it increases the size a lot (experimental).
Enables `-finline` automatically. Also enables `-finline`.
* `-Og`, `--optimize-debugging` Disables optimizations that make debugging harder.
Sets
`-fno-optimize-stdlib`,
`-fno-variable-overlap`,
`-fno-register-variables`,
`-fno-function-fallthrough`,
`-fno-function-deduplication`,
`-fno-subroutine-extraction`
automatically.
* `-fdangerous-optimizations` Use dangerous optimizations (experimental). * `-fdangerous-optimizations` Use dangerous optimizations (experimental).
Dangerous optimizations are more likely to result in broken code. Dangerous optimizations are more likely to result in broken code.
Enables `-fipo` automatically. Also enables `-fipo` and `-foptimize-stdlib` automatically.
`.ini` equivalent: `dangerous_optimizations`. `.ini` equivalent: `dangerous_optimizations`.
Note: for compatibility with versions 0.3.0 and earlier, Note: for compatibility with versions 0.3.0 and earlier,

View File

@ -58,7 +58,7 @@ Default: the same as `encoding`.
* `prevent_jmp_indirect_bug` whether the compiler should try to avoid the indirect JMP bug, * `prevent_jmp_indirect_bug` whether the compiler should try to avoid the indirect JMP bug,
default is `false` on 65C02-compatible or non-6502 processors and `true` elsewhere default is `false` on 65C02-compatible or non-6502 processors and `true` elsewhere
* `compact_dispatch_params` whether parameter values in return dispatch statements may overlap other objects, default is `true` * `compact_dispatch_params` whether parameter values in return dispatch statements may overlap other objects, default is `true`.
This may cause problems if the parameter table is stored next to a hardware register that has side effects when reading. This may cause problems if the parameter table is stored next to a hardware register that has side effects when reading.
* `lunix` generate relocatable code for LUnix/LNG, default is `false` * `lunix` generate relocatable code for LUnix/LNG, default is `false`
@ -71,6 +71,12 @@ Default: the same as `encoding`.
* `ipo` - enable interprocedural optimization, default is `false`. * `ipo` - enable interprocedural optimization, default is `false`.
* `function_fallthrough` whether should replace a tail call by simply putting one function after another, default is `true`.
* `function_deduplication` whether identical functions should be merged into one function, default is `true`.
* `subroutine_extraction` whether identical fragments of functions should be extracted into subroutines, default is `false`.
* `lenient_encoding` - allow for automatic substitution of invalid characters in string literals using the default encodings, default is `false`. * `lenient_encoding` - allow for automatic substitution of invalid characters in string literals using the default encodings, default is `false`.
* `use_shadow_registers_for_irq` use Z80 shadow registers in interrupt routines, default is `true` for Z80 and `false` otherwise * `use_shadow_registers_for_irq` use Z80 shadow registers in interrupt routines, default is `true` for Z80 and `false` otherwise

View File

@ -218,13 +218,13 @@ object Cpu extends Enumeration {
import CompilationFlag._ import CompilationFlag._
private val mosAlwaysDefaultFlags = Set( private val alwaysDefaultFlags = Set(
VariableOverlap, CompactReturnDispatchParams VariableOverlap, CompactReturnDispatchParams, FunctionFallthrough, RegisterVariables, FunctionDeduplication
) )
private val i80AlwaysDefaultFlags = Set( private val mosAlwaysDefaultFlags = alwaysDefaultFlags
VariableOverlap, CompactReturnDispatchParams
) private val i80AlwaysDefaultFlags = alwaysDefaultFlags
def defaultFlags(x: Cpu.Value): Set[CompilationFlag.Value] = x match { def defaultFlags(x: Cpu.Value): Set[CompilationFlag.Value] = x match {
case StrictMos => case StrictMos =>
@ -316,8 +316,9 @@ object CompilationFlag extends Enumeration {
UseIntelSyntaxForInput, UseIntelSyntaxForInput,
UseIntelSyntaxForOutput, UseIntelSyntaxForOutput,
// optimization options: // optimization options:
OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed, OptimizeForDebugging,
DangerousOptimizations, InlineFunctions, InterproceduralOptimization, DangerousOptimizations, InlineFunctions, InterproceduralOptimization,
FunctionFallthrough, RegisterVariables, FunctionDeduplication, SubroutineExtraction,
OptimizeStdlib, OptimizeStdlib,
// memory allocation options // memory allocation options
VariableOverlap, CompactReturnDispatchParams, LUnixRelocatableCode, VariableOverlap, CompactReturnDispatchParams, LUnixRelocatableCode,
@ -356,6 +357,9 @@ object CompilationFlag extends Enumeration {
"input_intel_syntax" -> UseIntelSyntaxForInput, "input_intel_syntax" -> UseIntelSyntaxForInput,
"ipo" -> InterproceduralOptimization, "ipo" -> InterproceduralOptimization,
"optimize_stdlib" -> OptimizeStdlib, "optimize_stdlib" -> OptimizeStdlib,
"function_fallthrough" -> FunctionFallthrough,
"function_deduplication" -> FunctionDeduplication,
"subroutine_extraction" -> SubroutineExtraction,
"inline" -> InlineFunctions, "inline" -> InlineFunctions,
"dangerous_optimizations" -> DangerousOptimizations, "dangerous_optimizations" -> DangerousOptimizations,
"decimal_mode" -> DecimalMode, "decimal_mode" -> DecimalMode,

View File

@ -0,0 +1,55 @@
package millfork
import millfork.error.Logger
/**
* @author Karol Stasiak
*/
case class Context(errorReporting: Logger,
inputFileNames: List[String],
outputFileName: Option[String] = None,
runFileName: Option[String] = None,
optimizationLevel: Option[Int] = None,
zpRegisterSize: Option[Int] = None,
platform: Option[String] = None,
outputAssembly: Boolean = false,
outputLabels: Boolean = false,
includePath: List[String] = Nil,
flags: Map[CompilationFlag.Value, Boolean] = Map(),
features: Map[String, Long] = Map(),
verbosity: Option[Int] = None) {
def changeFlag(f: CompilationFlag.Value, b: Boolean): Context = {
if (flags.contains(f)) {
if (flags(f) != b) {
errorReporting.error("Conflicting flags")
}
this
} else {
copy(flags = this.flags + (f -> b))
}
}
def filloutFlags(): Context = {
var addons = Map[CompilationFlag.Value, Boolean]()
if (flags.contains(CompilationFlag.OptimizeForSpeed)) {
addons += CompilationFlag.InlineFunctions -> true
}
if (flags.contains(CompilationFlag.OptimizeForSize)) {
addons += CompilationFlag.SubroutineExtraction -> true
}
if (flags.contains(CompilationFlag.DangerousOptimizations)) {
addons += CompilationFlag.InterproceduralOptimization -> true
addons += CompilationFlag.OptimizeStdlib -> true
}
if (flags.contains(CompilationFlag.OptimizeForDebugging)) {
addons += CompilationFlag.VariableOverlap -> false
addons += CompilationFlag.RegisterVariables -> false
addons += CompilationFlag.FunctionDeduplication -> false
addons += CompilationFlag.SubroutineExtraction -> false
addons += CompilationFlag.FunctionFallthrough -> false
addons += CompilationFlag.OptimizeStdlib -> false
}
copy(flags = flags ++ addons.filterKeys(k => !flags.contains(k)))
}
}

View File

@ -18,34 +18,7 @@ import millfork.node.StandardCallGraph
import millfork.output._ import millfork.output._
import millfork.parser.{MosSourceLoadingQueue, ZSourceLoadingQueue} import millfork.parser.{MosSourceLoadingQueue, ZSourceLoadingQueue}
/**
* @author Karol Stasiak
*/
case class Context(errorReporting: Logger,
inputFileNames: List[String],
outputFileName: Option[String] = None,
runFileName: Option[String] = None,
optimizationLevel: Option[Int] = None,
zpRegisterSize: Option[Int] = None,
platform: Option[String] = None,
outputAssembly: Boolean = false,
outputLabels: Boolean = false,
includePath: List[String] = Nil,
flags: Map[CompilationFlag.Value, Boolean] = Map(),
features: Map[String, Long] = Map(),
verbosity: Option[Int] = None) {
def changeFlag(f: CompilationFlag.Value, b: Boolean): Context = {
if (flags.contains(f)) {
if (flags(f) != b) {
errorReporting.error("Conflicting flags")
}
this
} else {
copy(flags = this.flags + (f -> b))
}
}
}
object Main { object Main {
@ -77,7 +50,7 @@ object Main {
errorReporting.trace("This program comes with ABSOLUTELY NO WARRANTY.") errorReporting.trace("This program comes with ABSOLUTELY NO WARRANTY.")
errorReporting.trace("This is free software, and you are welcome to redistribute it under certain conditions") errorReporting.trace("This is free software, and you are welcome to redistribute it under certain conditions")
errorReporting.trace("You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/") errorReporting.trace("You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/")
val c = fixMissingIncludePath(c0) val c = fixMissingIncludePath(c0).filloutFlags()
if (c.includePath.isEmpty) { if (c.includePath.isEmpty) {
errorReporting.warn("Failed to detect the default include directory, consider using the -I option") errorReporting.warn("Failed to detect the default include directory, consider using the -I option")
} }
@ -213,7 +186,7 @@ object Main {
val program = if (optLevel > 0) { val program = if (optLevel > 0) {
OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options)) OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
} else { } else {
unoptimized OptimizationPresets.NodeOpt0.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
} }
val callGraph = new StandardCallGraph(program, options.log) val callGraph = new StandardCallGraph(program, options.log)
@ -265,7 +238,7 @@ object Main {
val program = if (optLevel > 0) { val program = if (optLevel > 0) {
OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options)) OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
} else { } else {
unoptimized OptimizationPresets.NodeOpt0.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
} }
val callGraph = new StandardCallGraph(program, options.log) val callGraph = new StandardCallGraph(program, options.log)
@ -430,10 +403,10 @@ object Main {
}.description("Whether hardware decimal mode should be used (6502 only).") }.description("Whether hardware decimal mode should be used (6502 only).")
boolean("-fvariable-overlap", "-fno-variable-overlap").action { (c, v) => boolean("-fvariable-overlap", "-fno-variable-overlap").action { (c, v) =>
c.changeFlag(CompilationFlag.VariableOverlap, v) c.changeFlag(CompilationFlag.VariableOverlap, v)
}.description("Whether variables should overlap if their scopes do not intersect.") }.description("Whether variables should overlap if their scopes do not intersect. Enabled by default.")
boolean("-fcompact-dispatch-params", "-fno-compact-dispatch-params").action { (c, v) => boolean("-fcompact-dispatch-params", "-fno-compact-dispatch-params").action { (c, v) =>
c.changeFlag(CompilationFlag.CompactReturnDispatchParams, v) c.changeFlag(CompilationFlag.CompactReturnDispatchParams, v)
}.description("Whether parameter values in return dispatch statements may overlap other objects.") }.description("Whether parameter values in return dispatch statements may overlap other objects. Enabled by default.")
boolean("-fbounds-checking", "-fno-bounds-checking").action { (c, v) => boolean("-fbounds-checking", "-fno-bounds-checking").action { (c, v) =>
c.changeFlag(CompilationFlag.VariableOverlap, v) c.changeFlag(CompilationFlag.VariableOverlap, v)
}.description("Whether should insert bounds checking on array access.") }.description("Whether should insert bounds checking on array access.")
@ -506,28 +479,42 @@ object Main {
boolean("-foptimize-stdlib", "-fno-optimize-stdlib").action { (c, v) => boolean("-foptimize-stdlib", "-fno-optimize-stdlib").action { (c, v) =>
c.changeFlag(CompilationFlag.OptimizeStdlib, v) c.changeFlag(CompilationFlag.OptimizeStdlib, v)
}.description("Optimize standard library calls.") }.description("Optimize standard library calls.")
boolean("-fsubroutine-extraction", "-fno-subroutine-extraction").action { (c, v) =>
c.changeFlag(CompilationFlag.SubroutineExtraction, v)
}.description("Extract identical code fragments into subroutines.")
boolean("-ffunction-fallthrough", "-fno-function-fallthrough").action { (c, v) =>
c.changeFlag(CompilationFlag.FunctionFallthrough, v)
}.description("Replace tail calls by simply putting one function after another. Enabled by default.")
boolean("-ffunction-deduplication", "-fno-function-deduplication").action { (c, v) =>
c.changeFlag(CompilationFlag.FunctionDeduplication, v)
}.description("Merge identical functions into one function. Enabled by default.")
boolean("-fregister-variables", "-fno-register-variables").action { (c, v) =>
c.changeFlag(CompilationFlag.RegisterVariables, v)
}.description("Allow moving local variables into CPU registers. Enabled by default.")
flag("-Os", "--size").action { c => flag("-Os", "--size").action { c =>
c.changeFlag(CompilationFlag.OptimizeForSize, true). c.changeFlag(CompilationFlag.OptimizeForSize, true).
changeFlag(CompilationFlag.OptimizeForSpeed, false). changeFlag(CompilationFlag.OptimizeForSpeed, false).
changeFlag(CompilationFlag.OptimizeForSonicSpeed, false) changeFlag(CompilationFlag.OptimizeForSonicSpeed, false)
}.description("Prefer smaller code even if it is slightly slower (experimental).") }.description("Prefer smaller code even if it is slightly slower (experimental). Implies -fsubroutine-extraction.")
flag("-Of", "--fast").action { c => flag("-Of", "--fast").action { c =>
c.changeFlag(CompilationFlag.OptimizeForSize, false). c.changeFlag(CompilationFlag.OptimizeForSize, false).
changeFlag(CompilationFlag.OptimizeForSpeed, true). changeFlag(CompilationFlag.OptimizeForSpeed, true).
changeFlag(CompilationFlag.OptimizeForSonicSpeed, false) changeFlag(CompilationFlag.OptimizeForSonicSpeed, false)
}.description("Prefer faster code even if it is slightly bigger (experimental).") }.description("Prefer faster code even if it is slightly bigger (experimental). Implies -finline.")
flag("-Ob", "--blast-processing").action { c => flag("-Ob", "--blast-processing").action { c =>
c.changeFlag(CompilationFlag.OptimizeForSize, false). c.changeFlag(CompilationFlag.OptimizeForSize, false).
changeFlag(CompilationFlag.OptimizeForSpeed, true). changeFlag(CompilationFlag.OptimizeForSpeed, true).
changeFlag(CompilationFlag.OptimizeForSonicSpeed, true). changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
changeFlag(CompilationFlag.InlineFunctions, true)
}.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.") }.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.")
flag("--dangerous-optimizations").action { c => flag("--dangerous-optimizations").action { c =>
c.changeFlag(CompilationFlag.DangerousOptimizations, true) c.changeFlag(CompilationFlag.DangerousOptimizations, true)
}.description("Use dangerous optimizations (experimental).").hidden() }.description("Use dangerous optimizations (experimental).").hidden()
boolean("-fdangerous-optimizations", "-fno-dangerous-optimizations").action { (c, v) => boolean("-fdangerous-optimizations", "-fno-dangerous-optimizations").action { (c, v) =>
c.changeFlag(CompilationFlag.DangerousOptimizations, v) c.changeFlag(CompilationFlag.DangerousOptimizations, v)
}.description("Use dangerous optimizations (experimental). Implies -fipo.") }.description("Use dangerous optimizations (experimental). Implies -fipo and -foptimize-stdlib.")
flag("-Og", "--optimize-debugging").action { c =>
c.changeFlag(CompilationFlag.OptimizeForDebugging, true)
}.description("Disable optimizations that make debugging harder (experimental).")
fluff("", "Warning options:", "") fluff("", "Warning options:", "")

View File

@ -15,6 +15,10 @@ object OptimizationPresets {
UnusedLocalVariables, UnusedLocalVariables,
UnusedGlobalVariables, UnusedGlobalVariables,
) )
val NodeOpt0 = List(
UnusedFunctions,
UnusedGlobalVariables,
)
val AssOpt: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]]( val AssOpt: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]](
UnusedLabelRemoval, UnusedLabelRemoval,
AlwaysGoodOptimizations.NonetAddition, AlwaysGoodOptimizations.NonetAddition,

View File

@ -1,6 +1,6 @@
package millfork.assembly package millfork.assembly
import millfork.CompilationOptions import millfork.{CompilationFlag, CompilationOptions}
import millfork.compiler.LabelGenerator import millfork.compiler.LabelGenerator
import millfork.env.{NormalFunction, ThingInMemory} import millfork.env.{NormalFunction, ThingInMemory}
import millfork.error.Logger import millfork.error.Logger
@ -23,4 +23,6 @@ trait AssemblyOptimization[T <: AbstractCode] {
def name: String def name: String
def optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T] def optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T]
def requiredFlags: Set[CompilationFlag.Value] = Set.empty
} }

View File

@ -17,6 +17,8 @@ import scala.util.control.TailCalls.{TailRec, done, tailcall}
*/ */
object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] { object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] {
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
object CyclesAndBytes { object CyclesAndBytes {
val Zero = CyclesAndBytes(0, 0) val Zero = CyclesAndBytes(0, 0)
} }

View File

@ -13,6 +13,8 @@ import scala.collection.mutable.ListBuffer
*/ */
object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] { object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
override def name = "Allocating variables to single registers" override def name = "Allocating variables to single registers"
object CyclesAndBytes { object CyclesAndBytes {

View File

@ -14,6 +14,8 @@ import scala.collection.mutable.ListBuffer
*/ */
object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] { object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
override def name = "Allocating variables to register pairs" override def name = "Allocating variables to register pairs"
object CyclesAndBytes { object CyclesAndBytes {

View File

@ -163,7 +163,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
def deduplicate(options: CompilationOptions, compiledFunctions: mutable.Map[String, CompiledFunction[T]]): Unit def deduplicate(options: CompilationOptions, compiledFunctions: mutable.Map[String, CompiledFunction[T]]): Unit
def assemble(callGraph: CallGraph, optimizations: Seq[AssemblyOptimization[T]], options: CompilationOptions): AssemblerOutput = { def assemble(callGraph: CallGraph, unfilteredOptimizations: Seq[AssemblyOptimization[T]], options: CompilationOptions): AssemblerOutput = {
mem.programName = options.outputFileName.getOrElse("MILLFORK") mem.programName = options.outputFileName.getOrElse("MILLFORK")
val platform = options.platform val platform = options.platform
val variableAllocators = platform.variableAllocators val variableAllocators = platform.variableAllocators
@ -174,6 +174,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
zpOccupied(i + 1) = false zpOccupied(i + 1) = false
} }
val optimizations = unfilteredOptimizations.filter(_.requiredFlags.forall(options.flag))
val assembly = mutable.ArrayBuffer[String]() val assembly = mutable.ArrayBuffer[String]()
val inliningResult = inliningCalculator.calculate( val inliningResult = inliningCalculator.calculate(

View File

@ -13,16 +13,20 @@ import scala.collection.mutable.ListBuffer
abstract class Deduplicate[T <: AbstractCode](env: Environment, options: CompilationOptions) { abstract class Deduplicate[T <: AbstractCode](env: Environment, options: CompilationOptions) {
def apply(compiledFunctions: mutable.Map[String, CompiledFunction[T]]): Unit = { def apply(compiledFunctions: mutable.Map[String, CompiledFunction[T]]): Unit = {
if (options.flag(CompilationFlag.OptimizeForSize)) { if (options.flag(CompilationFlag.SubroutineExtraction)) {
runStage(compiledFunctions, extractCommonCode) runStage(compiledFunctions, extractCommonCode)
} }
if (options.flag(CompilationFlag.FunctionDeduplication)) {
runStage(compiledFunctions, deduplicateIdenticalFunctions) runStage(compiledFunctions, deduplicateIdenticalFunctions)
}
if (options.flag(CompilationFlag.FunctionFallthrough)) {
runStage(compiledFunctions, eliminateTailJumps) runStage(compiledFunctions, eliminateTailJumps)
runStage(compiledFunctions, eliminateTailJumps) runStage(compiledFunctions, eliminateTailJumps)
runStage(compiledFunctions, eliminateTailJumps) runStage(compiledFunctions, eliminateTailJumps)
runStage(compiledFunctions, eliminateRemainingTrivialTailJumps) runStage(compiledFunctions, eliminateRemainingTrivialTailJumps)
runStage(compiledFunctions, eliminateRemainingTrivialTailJumps) runStage(compiledFunctions, eliminateRemainingTrivialTailJumps)
runStage(compiledFunctions, eliminateRemainingTrivialTailJumps) runStage(compiledFunctions, eliminateRemainingTrivialTailJumps)
}
fixDoubleRedirects(compiledFunctions) fixDoubleRedirects(compiledFunctions)
// println(compiledFunctions.map { // println(compiledFunctions.map {
// case (k, v) => k + " " + (v match { // case (k, v) => k + " " + (v match {

View File

@ -144,6 +144,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
CompilationFlag.EmitNative65816Opcodes -> (platform.cpu == millfork.Cpu.Sixteen && native16), CompilationFlag.EmitNative65816Opcodes -> (platform.cpu == millfork.Cpu.Sixteen && native16),
CompilationFlag.Emit65CE02Opcodes -> (platform.cpu == millfork.Cpu.CE02), CompilationFlag.Emit65CE02Opcodes -> (platform.cpu == millfork.Cpu.CE02),
CompilationFlag.EmitHudsonOpcodes -> (platform.cpu == millfork.Cpu.HuC6280), CompilationFlag.EmitHudsonOpcodes -> (platform.cpu == millfork.Cpu.HuC6280),
CompilationFlag.SubroutineExtraction -> optimizeForSize,
CompilationFlag.OptimizeForSize -> optimizeForSize, CompilationFlag.OptimizeForSize -> optimizeForSize,
CompilationFlag.OptimizeForSpeed -> blastProcessing, CompilationFlag.OptimizeForSpeed -> blastProcessing,
CompilationFlag.OptimizeForSonicSpeed -> blastProcessing CompilationFlag.OptimizeForSonicSpeed -> blastProcessing