mirror of
https://github.com/KarolS/millfork.git
synced 2024-09-11 02:55:10 +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:
parent
b77f9dd5f8
commit
f12463aef0
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
55
src/main/scala/millfork/Context.scala
Normal file
55
src/main/scala/millfork/Context.scala
Normal 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)))
|
||||||
|
}
|
||||||
|
}
|
@ -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:", "")
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
runStage(compiledFunctions, deduplicateIdenticalFunctions)
|
if (options.flag(CompilationFlag.FunctionDeduplication)) {
|
||||||
runStage(compiledFunctions, eliminateTailJumps)
|
runStage(compiledFunctions, deduplicateIdenticalFunctions)
|
||||||
runStage(compiledFunctions, eliminateTailJumps)
|
}
|
||||||
runStage(compiledFunctions, eliminateTailJumps)
|
if (options.flag(CompilationFlag.FunctionFallthrough)) {
|
||||||
runStage(compiledFunctions, eliminateRemainingTrivialTailJumps)
|
runStage(compiledFunctions, eliminateTailJumps)
|
||||||
runStage(compiledFunctions, eliminateRemainingTrivialTailJumps)
|
runStage(compiledFunctions, eliminateTailJumps)
|
||||||
runStage(compiledFunctions, eliminateRemainingTrivialTailJumps)
|
runStage(compiledFunctions, eliminateTailJumps)
|
||||||
|
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 {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user