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