Class: Optimize::Pipeline

Inherits:
Object
  • Object
show all
Defined in:
lib/optimize/pipeline.rb

Defined Under Namespace

Classes: FixedPointOverflow

Constant Summary collapse

MAX_ITERATIONS =
8

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(passes) ⇒ Pipeline

Returns a new instance of Pipeline.



54
55
56
# File 'lib/optimize/pipeline.rb', line 54

def initialize(passes)
  @passes = passes
end

Instance Attribute Details

#passesObject (readonly)

Returns the value of attribute passes.



52
53
54
# File 'lib/optimize/pipeline.rb', line 52

def passes
  @passes
end

Class Method Details

.defaultObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/optimize/pipeline.rb', line 26

def self.default
  new([
    Passes::InliningPass.new,
    # DeadStashElimPass runs directly after InliningPass to clean up the
    # argument-stash round-trip (`setlocal X; getlocal X` with X
    # unreferenced elsewhere). Exposes the producer to the following
    # instruction so arith/const-fold can cascade through it.
    Passes::DeadStashElimPass.new,
    Passes::ArithReassocPass.new,
    # ConstFoldTier2Pass rewrites frozen top-level constant references
    # to their literal values. Runs before the other const-folders so
    # `FOO + 1` → `42 + 1` → `43` cascades in one pipeline run.
    Passes::ConstFoldTier2Pass.new,
    # ConstFoldEnvPass runs BEFORE ConstFoldPass so `ENV["FLAG"] == "true"`
    # can cascade through string-eq folding in a single pipeline run.
    Passes::ConstFoldEnvPass.new,
    Passes::ConstFoldPass.new,
    Passes::IdentityElimPass.new,
    # DeadBranchFoldPass runs LAST — any earlier pass that turns a
    # branch condition into a literal (ConstFold*, IdentityElim) feeds
    # this pass, which collapses `<lit>; branch*` into either a
    # `jump` (taken) or a drop (not taken).
    Passes::DeadBranchFoldPass.new,
  ])
end

Instance Method Details

#run(ir, type_env:, env_snapshot: nil) ⇒ Object

Run all passes over every Function in the IR tree. Returns the Optimize::Log accumulated during the run.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/optimize/pipeline.rb', line 60

def run(ir, type_env:, env_snapshot: nil)
  log = Log.new
  object_table = ir.misc && ir.misc[:object_table]
  callee_map = build_callee_map(ir)
  slot_type_map, signature_map = build_type_maps(ir, type_env, object_table)

  seen_fns = {}.compare_by_identity
  each_function(ir) do |function|
    next if seen_fns[function]
    seen_fns[function] = true
    one_shot_passes, iterative_passes = @passes.partition(&:one_shot?)

    # One-shot passes: run exactly once per function.
    one_shot_passes.each do |pass|
      run_single_pass(pass, function, type_env, log, object_table, callee_map,
                      slot_type_map, signature_map, env_snapshot)
    end

    # Iterative passes: sweep until rewrite_count stops growing. Cap at
    # MAX_ITERATIONS; raising beyond that signals either pass oscillation
    # or a pass that records rewrites without changing IR.
    iterations = 0
    if iterative_passes.any?
      loop do
        iterations += 1
        snapshot = log.rewrite_count
        iterative_passes.each do |pass|
          run_single_pass(pass, function, type_env, log, object_table, callee_map,
                          slot_type_map, signature_map, env_snapshot)
        end
        break if log.rewrite_count == snapshot
        if iterations >= MAX_ITERATIONS
          raise FixedPointOverflow.new(function_name: function.name, iterations: iterations)
        end
      end
    end

    log.record_convergence(function.name, iterations)
  end
  log
end