Class: Kumi::Core::Analyzer::Passes::Codegen::Loop::Js::Emitter::StreamPlan
- Inherits:
-
Object
- Object
- Kumi::Core::Analyzer::Passes::Codegen::Loop::Js::Emitter::StreamPlan
- Defined in:
- lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb
Overview
Decides, per streaming function, which arrays and records can be materialized into caller-owned or module-persistent buffers instead of fresh allocations.
- managed arrays: the returned array plus any array built solely to be pushed into a managed array. They are written by cursor and truncated, so the previous call's storage is reused.
- managed objects: records built solely to be pushed into a managed array. The previous call's element at the same slot is mutated instead of allocating a new object.
- persisted scratch: intermediate arrays that are only written by push and read by index, hoisted to module scope. Reads are always bounded by the current call's loops, so stale tails are inert.
Instance Attribute Summary collapse
-
#managed_arrays ⇒ Object
readonly
Returns the value of attribute managed_arrays.
-
#managed_objects ⇒ Object
readonly
Returns the value of attribute managed_objects.
-
#persisted_scratch ⇒ Object
readonly
Returns the value of attribute persisted_scratch.
-
#target_reg ⇒ Object
readonly
Returns the value of attribute target_reg.
Instance Method Summary collapse
- #cursor?(reg) ⇒ Boolean
-
#initialize(fn) ⇒ StreamPlan
constructor
A new instance of StreamPlan.
Constructor Details
#initialize(fn) ⇒ StreamPlan
Returns a new instance of StreamPlan.
50 51 52 53 54 55 56 57 58 59 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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb', line 50 def initialize(fn) @fn = fn instrs = fn.entry_block.instructions producer_idx = {} producer = {} use_count = Hash.new(0) pushes = [] # [instruction index, parent reg, child reg] excluded = {} init_depth = {} pushed_somewhere = {} depth = 0 instrs.each_with_index do |instr, idx| if instr.result producer[instr.result] = instr producer_idx[instr.result] = idx end instr.inputs.each { |r| use_count[r] += 1 } case instr.opcode when :array_push pushes << [idx, instr.inputs[0], instr.inputs[1]] pushed_somewhere[instr.inputs[1]] = true when :loop_start excluded[instr.inputs.first] = true depth += 1 when :loop_end then depth -= 1 when :array_len then excluded[instr.inputs.first] = true when :shift_read then excluded[instr.inputs[0]] = true when :array_init then init_depth[instr.result] = depth end end @target_reg = direct_return_array_reg(fn) @managed_arrays = {} # reg => parent reg (nil for reuse roots) @managed_objects = {} # reg => parent reg @persisted_scratch = {} # Persistence is only safe for arrays created once per call whose # identity never escapes into another array. instrs.each do |instr| next unless instr.opcode == :array_init next if instr.result == @target_reg next if excluded[instr.result] || pushed_somewhere[instr.result] next unless init_depth[instr.result].zero? @persisted_scratch[instr.result] = true end roots = {} roots[@target_reg] = true if @target_reg @persisted_scratch.each_key { |r| roots[r] = true } return if roots.empty? @managed_arrays[@target_reg] = nil if @target_reg reusable = roots.dup loop do changed = false pushes.each do |push_idx, parent, child| next unless reusable.key?(parent) next if reusable.key?(child) || @managed_objects.key?(child) prod = producer[child] next unless prod case prod.opcode when :array_init next if excluded[child] pushes_into = pushes.count { |_, p, _| p == child } pushed = pushes.count { |_, _, c| c == child } next unless pushed == 1 && use_count[child] == pushes_into + 1 @managed_arrays[child] = parent reusable[child] = true changed = true when :make_object next unless use_count[child] == 1 && producer_idx[child] + 1 == push_idx next if Array(prod.attributes[:keys]).empty? @managed_objects[child] = parent changed = true end end break unless changed end end |
Instance Attribute Details
#managed_arrays ⇒ Object (readonly)
Returns the value of attribute managed_arrays.
48 49 50 |
# File 'lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb', line 48 def managed_arrays @managed_arrays end |
#managed_objects ⇒ Object (readonly)
Returns the value of attribute managed_objects.
48 49 50 |
# File 'lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb', line 48 def managed_objects @managed_objects end |
#persisted_scratch ⇒ Object (readonly)
Returns the value of attribute persisted_scratch.
48 49 50 |
# File 'lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb', line 48 def persisted_scratch @persisted_scratch end |
#target_reg ⇒ Object (readonly)
Returns the value of attribute target_reg.
48 49 50 |
# File 'lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb', line 48 def target_reg @target_reg end |
Instance Method Details
#cursor?(reg) ⇒ Boolean
138 139 140 |
# File 'lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb', line 138 def cursor?(reg) @managed_arrays.key?(reg) || @persisted_scratch.key?(reg) end |