Skip to content

[DEMO] Compiler Optimization#3169

Draft
DBooots wants to merge 108 commits into
KSP-KOS:developfrom
DBooots:compiler_optimizations
Draft

[DEMO] Compiler Optimization#3169
DBooots wants to merge 108 commits into
KSP-KOS:developfrom
DBooots:compiler_optimizations

Conversation

@DBooots
Copy link
Copy Markdown
Contributor

@DBooots DBooots commented Mar 14, 2026

Demo only at this time

This PR adds an optimization stage to the compiler. This stage converts the Opcodes into a three address code interim representation, upon which various optimizing passes are performed. The interim representation is then converted back into opcodes and emitted to the program context as normal.
The optimizing passes currently implemented trim opcodes without changing any control flow, except to remove branches that are shown to be inaccessible.

Currently Implemented Passes

Suffix Replacement
Replaces CONSTANT: fields with the constant value, and replaces aliased SHIP: fields with the direct alias. This saves 1 opcode per ship alias replaced, and allows constant folding of the CONSTANT values.
Constant Propagation
Where a variable is assigned a constant, it replaces accesses of that variable with the constant (cognizant of global variables and triggers messing with variable values). This allows constant folding of the assigned values.
Constant Folding
Performs arithmetic, including scalar math functions, to reduce formulas to their simplest form. This includes the CONSTANT: fields from Suffix Replacement and the propagated constant variables from Constant Propagation. This step saves between 1 and 3 opcodes for every folding optimization that is performed.
Dead Code Elimination
Eliminates any branches where the condition is known to be false after propagating and folding constants.
Peephole Optimizations
This pass does a number of small optimizations:

  • Replaces string indexing against a lexicon with suffix accessing, when the string is a valid identifier. This saves 1 opcode for every index set or get.
  • Replaces parameterless suffix calls with a direct suffix access (since the suffix get opcode checks if it's a method and invokes it for free). This saves 2 opcodes for each suffix method call, but feels like cheating.
  • Replaces calls to VectorDotProduct() with a simple multiplication opcode. This saves 2 opcodes for every VectorDotProduct() that is performed.
  • Replaces double negation or double logical not operations with the original value. This probably doesn't come up often, but it saves 2 opcodes every time.
  • Replaces not->branch[true|false] with branch[false|true]. This saves 1 opcode every branch simplified.
  • Performs algebraic simplification (not necessarily of constants) to reduce the number of opcodes. E.g. A*B+A*C=A*(B+C), X*X*X = X^3, or -A+B = B-A . This saves multiple opcodes wherever possible.

Scope Simplification
Any scope that does not define any variables is eliminated. This saves 2 opcodes for each scope that is collapsed.

Next Steps

  • More testing! While I've added some unit tests, which run correctly, and I've reviewed the opcode output from those tests to see that the optimization passes are doing what they are supposed to, unit testing only covers simple cases. I need to compile larger and more complicated programs and see what breaks.
  • Further optimization passes. The OptimizationLevel.cs file includes the roadmap for future passes that could be implemented. So far only the O1 (Minimal) level is finished.
  • Adding optimization level specification to the compile function. Unit testing has direct access to the Compile Options fed to the compiler. We'll need to add support for in-game compilation level selection. I think I've got it hardcoded to use no optimizations in the interpreter (prioritizing responsivity over performance) and Minimal for general compilation (running or compiling programs), but this is only a development hack.

DBooots added 30 commits March 1, 2026 22:53
…OptimizationLevel enum to select level of optimizaiton; BaseIntegrationTest uses no optimization. Default the interpreter to no optimization since it won't be worth it there.
…IRBuilder generates basic blocks, which IREmitter can then convert back to kRISC opcodes. This initial implementation does not accomplish any optimization yet (the opposite sometimes) but all current tests pass on the outputted opcodes.
…nalities with IRVariable but not be a subclass.
…or logical operation. This will be used for constant folding during compile. Hopefully the C# compiler will inline these back into the original Opcode methods, but the impact to execution should be minimal either way.
…heir most reduced form. Invalid operations found during folding throw a compilation exception, which should report the problematic line and column in the source.

Also add tests to confirm correct functionality (and improve current test suite for equivalent non-optimizing tests).
…d adding a virtual 'interim CPU' to accomplish the function call through the FunctionManager in order to avoid rewriting the function call logic.
…ng the AssemblyWalk attribute to discover all classes implementing IOptimizationPass.
…n be aliased with the alias (saves one opcode per access). This also replaces all constant() or constant: suffix accesses with the constant value, and does so in time for the constant folding pass.
…ally) every emitted opcode will have a location that makes sense.
…entation for whole-program optimizations (e.g. function inlining).
…d function and lock labels. Fix fallthrough jumps happening at the wrong time.
…optimizing of function code fragments. Add inspection methods to UserFunction and UserFunctionCollection to intercept the code fragments during the optimization pipeline.
…re something would be popped from the stack, but a BasicBlock's stack is empty at that point.
…of pass sort indices to give more space for expansion.
…. Note that scopes are not fully implemented yet.
…that 0/0=1 or 0/X=0 for all X (including 0) instead of throwing an exception. But that's already undefined behaviour so this should be acceptable.
…ter passes to do targeted constant folding after making a change.
DBooots added 30 commits May 17, 2026 23:02
Make equal-priority operations interchangeable for folding across algebraic branches.
Fix logic for when operands are folded to a constant, and when they are just simplified.
Disable some optimizations that could go awry if function names have changed.
…s that become redundant following constant propagation.
… Ordering pass that minimizes unconditional jumps.
…sses occur first (SortIndex < -2000) if they want. Finally a use for negative numbers in the int field!
…red so nested IResultingInstructions can know which BasicBlock they're within.
…ves an unconditional jump from the loop's end by repeating the conditional check at that point and conditionally jumping to the start of the loop body instead of unconditionally to the loop header. For safety, this pass occurs before the SSA conversion.
…nctions.

Now a user function that returns a constant, without doing anything outside itself, can be treated as a constant. This also enables type inferencing of user function returns.
…vel to keep with the philosophy that outright removing code like that could have an effect (but should be safe).
…his is accomplished by treating calls to non-invariant functions as not Equal to each other (which they aren't, because they can affect external things).
…ssary scope removal. Even though it assigns new variables, doing that at a wider scope is always better for that pass. Remove commented-out code. Fix CEE mutating operands that it shouldn't target.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants