Class: Kumi::IR::Loop::Passes::CopyCleanup

Inherits:
Passes::Base show all
Defined in:
lib/kumi/ir/loop/passes/copy_cleanup.rb

Overview

Two classic, syntax-preserving cleanups on the final LoopIR, so both the Ruby and JS emitters get shorter output from one place:

1. Copy propagation. `ref` and `acc_load` emit a bare `x = y` copy
   (see the emitters). Since every LoopIR result is assigned once,
   rewriting every use of `x` to `y` and dropping the copy is always
   safe — it is pure renaming. This collapses chains like
   `t15 = t22; t10 = t15 - t23`  =>  `t10 = t22 - t23`, and lets a
   function `return` its accumulator directly instead of via a temp.

2. Dead-code elimination. After propagation, an instruction whose
   result is read nowhere and which has no side effect (a pure
   compute: constant/load/kernel_call/select/make_object/index_read/
   array_len/ref/acc_load) is removed. Loop control, pushes, and
   accumulator steps are kept — they act through effects, not a read.

Neither transform changes evaluation order or which registers are live across loop boundaries, so the scope-aware Loop::Validator still holds.

Constant Summary collapse

PURE_OPS =

Opcodes that are pure: their only observable effect is the value they produce, so an unused result means the whole instruction is dead.

%i[
  constant load_input load_field kernel_call select make_object
  ref acc_load array_len index_read shift_read shift_in_bounds
].freeze
COPY_OPS =

Opcodes that are a bare copy of their single input.

%i[ref acc_load].freeze

Instance Method Summary collapse

Instance Method Details

#run(graph:, context: {}) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



36
37
38
39
# File 'lib/kumi/ir/loop/passes/copy_cleanup.rb', line 36

def run(graph:, context: {}) # rubocop:disable Lint/UnusedMethodArgument
  functions = graph.functions.values.map { |fn| process_function(fn) }
  Loop::Module.new(name: graph.name, functions: functions)
end