kumi 0.0.24 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/golden/array_element/expected/schema_ruby.rb +1 -1
  4. data/golden/array_index/expected/schema_ruby.rb +1 -1
  5. data/golden/array_operations/expected/schema_ruby.rb +1 -1
  6. data/golden/cascade_logic/expected/lir_02_inlined.txt +8 -8
  7. data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
  8. data/golden/chained_fusion/expected/lir_02_inlined.txt +36 -36
  9. data/golden/chained_fusion/expected/lir_03_cse.txt +23 -23
  10. data/golden/chained_fusion/expected/lir_04_1_loop_fusion.txt +25 -25
  11. data/golden/chained_fusion/expected/lir_04_loop_invcm.txt +23 -23
  12. data/golden/chained_fusion/expected/lir_06_const_prop.txt +23 -23
  13. data/golden/chained_fusion/expected/schema_javascript.mjs +23 -23
  14. data/golden/chained_fusion/expected/schema_ruby.rb +28 -28
  15. data/golden/element_arrays/expected/schema_ruby.rb +1 -1
  16. data/golden/empty_and_null_inputs/expected/lir_02_inlined.txt +18 -18
  17. data/golden/empty_and_null_inputs/expected/lir_03_cse.txt +17 -17
  18. data/golden/empty_and_null_inputs/expected/lir_04_1_loop_fusion.txt +17 -17
  19. data/golden/empty_and_null_inputs/expected/lir_04_loop_invcm.txt +17 -17
  20. data/golden/empty_and_null_inputs/expected/lir_06_const_prop.txt +17 -17
  21. data/golden/empty_and_null_inputs/expected/schema_javascript.mjs +13 -13
  22. data/golden/empty_and_null_inputs/expected/schema_ruby.rb +18 -18
  23. data/golden/game_of_life/expected/lir_02_inlined.txt +1291 -1291
  24. data/golden/game_of_life/expected/lir_03_cse.txt +396 -396
  25. data/golden/game_of_life/expected/lir_04_1_loop_fusion.txt +396 -396
  26. data/golden/game_of_life/expected/lir_04_loop_invcm.txt +396 -396
  27. data/golden/game_of_life/expected/lir_06_const_prop.txt +396 -396
  28. data/golden/game_of_life/expected/schema_javascript.mjs +85 -85
  29. data/golden/game_of_life/expected/schema_ruby.rb +86 -86
  30. data/golden/hash_keys/expected/schema_ruby.rb +1 -1
  31. data/golden/hash_value/expected/schema_ruby.rb +1 -1
  32. data/golden/hierarchical_complex/expected/lir_02_inlined.txt +15 -15
  33. data/golden/hierarchical_complex/expected/lir_03_cse.txt +1 -1
  34. data/golden/hierarchical_complex/expected/lir_04_1_loop_fusion.txt +1 -1
  35. data/golden/hierarchical_complex/expected/lir_04_loop_invcm.txt +1 -1
  36. data/golden/hierarchical_complex/expected/lir_06_const_prop.txt +1 -1
  37. data/golden/hierarchical_complex/expected/schema_javascript.mjs +1 -1
  38. data/golden/hierarchical_complex/expected/schema_ruby.rb +2 -2
  39. data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +1 -1
  40. data/golden/input_reference/expected/schema_ruby.rb +1 -1
  41. data/golden/interleaved_fusion/expected/lir_02_inlined.txt +35 -35
  42. data/golden/interleaved_fusion/expected/lir_03_cse.txt +26 -26
  43. data/golden/interleaved_fusion/expected/lir_04_1_loop_fusion.txt +27 -26
  44. data/golden/interleaved_fusion/expected/lir_04_loop_invcm.txt +26 -26
  45. data/golden/interleaved_fusion/expected/lir_06_const_prop.txt +26 -26
  46. data/golden/interleaved_fusion/expected/schema_javascript.mjs +23 -23
  47. data/golden/interleaved_fusion/expected/schema_ruby.rb +29 -29
  48. data/golden/let_inline/expected/schema_ruby.rb +1 -1
  49. data/golden/loop_fusion/expected/lir_02_inlined.txt +17 -17
  50. data/golden/loop_fusion/expected/lir_03_cse.txt +14 -14
  51. data/golden/loop_fusion/expected/lir_04_1_loop_fusion.txt +14 -14
  52. data/golden/loop_fusion/expected/lir_04_loop_invcm.txt +14 -14
  53. data/golden/loop_fusion/expected/lir_06_const_prop.txt +14 -14
  54. data/golden/loop_fusion/expected/schema_javascript.mjs +12 -12
  55. data/golden/loop_fusion/expected/schema_ruby.rb +16 -16
  56. data/golden/min_reduce_scope/expected/schema_ruby.rb +1 -1
  57. data/golden/mixed_dimensions/expected/lir_02_inlined.txt +5 -5
  58. data/golden/mixed_dimensions/expected/lir_03_cse.txt +5 -5
  59. data/golden/mixed_dimensions/expected/lir_04_1_loop_fusion.txt +5 -5
  60. data/golden/mixed_dimensions/expected/lir_04_loop_invcm.txt +5 -5
  61. data/golden/mixed_dimensions/expected/lir_06_const_prop.txt +5 -5
  62. data/golden/mixed_dimensions/expected/schema_javascript.mjs +3 -3
  63. data/golden/mixed_dimensions/expected/schema_ruby.rb +6 -6
  64. data/golden/multirank_hoisting/expected/lir_02_inlined.txt +48 -48
  65. data/golden/multirank_hoisting/expected/lir_03_cse.txt +35 -35
  66. data/golden/multirank_hoisting/expected/lir_04_1_loop_fusion.txt +35 -35
  67. data/golden/multirank_hoisting/expected/lir_04_loop_invcm.txt +35 -35
  68. data/golden/multirank_hoisting/expected/lir_06_const_prop.txt +35 -35
  69. data/golden/multirank_hoisting/expected/schema_javascript.mjs +34 -34
  70. data/golden/multirank_hoisting/expected/schema_ruby.rb +36 -36
  71. data/golden/nested_hash/expected/schema_ruby.rb +1 -1
  72. data/golden/reduction_broadcast/expected/lir_02_inlined.txt +30 -30
  73. data/golden/reduction_broadcast/expected/lir_03_cse.txt +22 -22
  74. data/golden/reduction_broadcast/expected/lir_04_1_loop_fusion.txt +22 -22
  75. data/golden/reduction_broadcast/expected/lir_04_loop_invcm.txt +22 -22
  76. data/golden/reduction_broadcast/expected/lir_06_const_prop.txt +22 -22
  77. data/golden/reduction_broadcast/expected/schema_javascript.mjs +18 -18
  78. data/golden/reduction_broadcast/expected/schema_ruby.rb +23 -23
  79. data/golden/roll/expected/schema_ruby.rb +1 -1
  80. data/golden/shift/expected/schema_ruby.rb +1 -1
  81. data/golden/shift_2d/expected/schema_ruby.rb +1 -1
  82. data/golden/simple_math/expected/schema_ruby.rb +1 -1
  83. data/golden/streaming_basics/expected/lir_02_inlined.txt +25 -25
  84. data/golden/streaming_basics/expected/lir_03_cse.txt +13 -13
  85. data/golden/streaming_basics/expected/lir_04_1_loop_fusion.txt +13 -13
  86. data/golden/streaming_basics/expected/lir_04_loop_invcm.txt +13 -13
  87. data/golden/streaming_basics/expected/lir_06_const_prop.txt +13 -13
  88. data/golden/streaming_basics/expected/schema_javascript.mjs +13 -13
  89. data/golden/streaming_basics/expected/schema_ruby.rb +14 -14
  90. data/golden/tuples/expected/schema_ruby.rb +1 -1
  91. data/golden/tuples_and_arrays/expected/lir_02_inlined.txt +16 -16
  92. data/golden/tuples_and_arrays/expected/lir_03_cse.txt +13 -13
  93. data/golden/tuples_and_arrays/expected/lir_04_1_loop_fusion.txt +13 -13
  94. data/golden/tuples_and_arrays/expected/lir_04_loop_invcm.txt +13 -13
  95. data/golden/tuples_and_arrays/expected/lir_06_const_prop.txt +13 -13
  96. data/golden/tuples_and_arrays/expected/schema_javascript.mjs +13 -13
  97. data/golden/tuples_and_arrays/expected/schema_ruby.rb +14 -14
  98. data/golden/us_tax_2024/expected/lir_02_inlined.txt +524 -524
  99. data/golden/us_tax_2024/expected/lir_03_cse.txt +283 -283
  100. data/golden/us_tax_2024/expected/lir_04_1_loop_fusion.txt +283 -283
  101. data/golden/us_tax_2024/expected/lir_04_loop_invcm.txt +320 -260
  102. data/golden/us_tax_2024/expected/lir_06_const_prop.txt +320 -260
  103. data/golden/us_tax_2024/expected/schema_javascript.mjs +24 -24
  104. data/golden/us_tax_2024/expected/schema_ruby.rb +25 -25
  105. data/golden/with_constants/expected/schema_ruby.rb +1 -1
  106. data/lib/kumi/core/analyzer/passes/lir/inline_declarations_pass.rb +118 -74
  107. data/lib/kumi/version.rb +1 -1
  108. metadata +1 -1
@@ -1,26 +1,26 @@
1
1
  export function _summary(input) {
2
- let acc3280 = 0.0;
3
- let t3281 = input["fed"];
4
- let t3282 = t3281["single"];
5
- let t3283 = t3282["rates"];
6
- let acc3349 = 0.0;
7
2
  let t3301 = input["income"];
8
3
  let t3302 = input["fed"];
9
- const t3306 = 0;
10
- const t3325 = -1;
11
- const t3327 = 100000000000.0;
12
4
  let t3303 = t3302["single"];
13
5
  let t3304 = t3303["std"];
14
6
  let t3305 = t3301 - t3304;
7
+ const t3306 = 0;
15
8
  let t3307 = [t3305, t3306];
16
9
  let t3308 = Math.max(...t3307);
10
+ let acc3280 = 0.0;
11
+ let t3281 = input["fed"];
12
+ let t3282 = t3281["single"];
13
+ let t3283 = t3282["rates"];
14
+ let acc3349 = 0.0;
15
+ const t3325 = -1;
16
+ const t3327 = 100000000000.0;
17
17
  t3283.forEach((t3284, t3285) => {
18
18
  let t3320 = t3284["lo"];
19
19
  let t3333 = t3284["hi"];
20
20
  let t3341 = t3284["rate"];
21
+ let t3297 = t3308 >= t3320;
21
22
  let t3326 = t3333 == t3325;
22
23
  let t3329 = t3326 ? t3327 : t3333;
23
- let t3297 = t3308 >= t3320;
24
24
  let t3299 = t3308 < t3329;
25
25
  let t3300 = t3297 && t3299;
26
26
  let t3289 = t3300 ? t3341 : t3306;
@@ -170,22 +170,22 @@ export function _summary(input) {
170
170
  "retirement_contrib": t558,
171
171
  "take_home": t3815
172
172
  };
173
- let acc3914 = 0.0;
174
- let t3916 = t3281["married_joint"];
175
- let t3917 = t3916["rates"];
176
- let acc3983 = 0.0;
177
173
  let t3937 = t3302["married_joint"];
178
174
  let t3938 = t3937["std"];
179
175
  let t3939 = t3301 - t3938;
180
176
  let t3941 = [t3939, t3306];
181
177
  let t3942 = Math.max(...t3941);
178
+ let acc3914 = 0.0;
179
+ let t3916 = t3281["married_joint"];
180
+ let t3917 = t3916["rates"];
181
+ let acc3983 = 0.0;
182
182
  t3917.forEach((t3918, t3919) => {
183
183
  let t3954 = t3918["lo"];
184
184
  let t3967 = t3918["hi"];
185
185
  let t3975 = t3918["rate"];
186
+ let t3931 = t3942 >= t3954;
186
187
  let t3960 = t3967 == t3325;
187
188
  let t3963 = t3960 ? t3327 : t3967;
188
- let t3931 = t3942 >= t3954;
189
189
  let t3933 = t3942 < t3963;
190
190
  let t3934 = t3931 && t3933;
191
191
  let t3923 = t3934 ? t3975 : t3306;
@@ -308,22 +308,22 @@ export function _summary(input) {
308
308
  "retirement_contrib": t558,
309
309
  "take_home": t4459
310
310
  };
311
- let acc4560 = 0.0;
312
- let t4562 = t3281["married_separate"];
313
- let t4563 = t4562["rates"];
314
- let acc4629 = 0.0;
315
311
  let t4583 = t3302["married_separate"];
316
312
  let t4584 = t4583["std"];
317
313
  let t4585 = t3301 - t4584;
318
314
  let t4587 = [t4585, t3306];
319
315
  let t4588 = Math.max(...t4587);
316
+ let acc4560 = 0.0;
317
+ let t4562 = t3281["married_separate"];
318
+ let t4563 = t4562["rates"];
319
+ let acc4629 = 0.0;
320
320
  t4563.forEach((t4564, t4565) => {
321
321
  let t4600 = t4564["lo"];
322
322
  let t4613 = t4564["hi"];
323
323
  let t4621 = t4564["rate"];
324
+ let t4577 = t4588 >= t4600;
324
325
  let t4606 = t4613 == t3325;
325
326
  let t4609 = t4606 ? t3327 : t4613;
326
- let t4577 = t4588 >= t4600;
327
327
  let t4579 = t4588 < t4609;
328
328
  let t4580 = t4577 && t4579;
329
329
  let t4569 = t4580 ? t4621 : t3306;
@@ -446,22 +446,22 @@ export function _summary(input) {
446
446
  "retirement_contrib": t558,
447
447
  "take_home": t5105
448
448
  };
449
- let acc5206 = 0.0;
450
- let t5208 = t3281["head_of_household"];
451
- let t5209 = t5208["rates"];
452
- let acc5275 = 0.0;
453
449
  let t5229 = t3302["head_of_household"];
454
450
  let t5230 = t5229["std"];
455
451
  let t5231 = t3301 - t5230;
456
452
  let t5233 = [t5231, t3306];
457
453
  let t5234 = Math.max(...t5233);
454
+ let acc5206 = 0.0;
455
+ let t5208 = t3281["head_of_household"];
456
+ let t5209 = t5208["rates"];
457
+ let acc5275 = 0.0;
458
458
  t5209.forEach((t5210, t5211) => {
459
459
  let t5246 = t5210["lo"];
460
460
  let t5259 = t5210["hi"];
461
461
  let t5267 = t5210["rate"];
462
+ let t5223 = t5234 >= t5246;
462
463
  let t5252 = t5259 == t3325;
463
464
  let t5255 = t5252 ? t3327 : t5259;
464
- let t5223 = t5234 >= t5246;
465
465
  let t5225 = t5234 < t5255;
466
466
  let t5226 = t5223 && t5225;
467
467
  let t5215 = t5226 ? t5267 : t3306;
@@ -1,5 +1,5 @@
1
1
  # Autogenerated by Kumi Codegen
2
- module Kumi::Compiled::KUMI_579630ac2e4efb73c2ff1d43016b1220da91788dd8ebe96021285d536e08ec39
2
+ module Kumi::Compiled::KUMI_f6cd45a92014eb42b276ebc3f95d8fc9ce044e3e8e8d7326dbe60f6d699e146a
3
3
  def self.from(input_data = nil)
4
4
  instance = Object.new
5
5
  instance.extend(self)
@@ -26,28 +26,28 @@ module Kumi::Compiled::KUMI_579630ac2e4efb73c2ff1d43016b1220da91788dd8ebe9602128
26
26
  end
27
27
 
28
28
  def _summary(input = @input)
29
- acc3280 = 0.0
30
- t3281 = input["fed"] || input[:fed]
31
- t3282 = t3281["single"] || t3281[:single]
32
- t3283 = t3282["rates"] || t3282[:rates]
33
- acc3349 = 0.0
34
29
  t3301 = input["income"] || input[:income]
35
30
  t3302 = input["fed"] || input[:fed]
36
- t3306 = 0
37
- t3325 = -1
38
- t3327 = 100000000000.0
39
31
  t3303 = t3302["single"] || t3302[:single]
40
32
  t3304 = t3303["std"] || t3303[:std]
41
33
  t3305 = t3301 - t3304
34
+ t3306 = 0
42
35
  t3307 = [t3305, t3306]
43
36
  t3308 = t3307.max
37
+ acc3280 = 0.0
38
+ t3281 = input["fed"] || input[:fed]
39
+ t3282 = t3281["single"] || t3281[:single]
40
+ t3283 = t3282["rates"] || t3282[:rates]
41
+ acc3349 = 0.0
42
+ t3325 = -1
43
+ t3327 = 100000000000.0
44
44
  t3283.each_with_index do |t3284, t3285|
45
45
  t3320 = t3284["lo"] || t3284[:lo]
46
46
  t3333 = t3284["hi"] || t3284[:hi]
47
47
  t3341 = t3284["rate"] || t3284[:rate]
48
+ t3297 = t3308 >= t3320
48
49
  t3326 = t3333 == t3325
49
50
  t3329 = t3326 ? t3327 : t3333
50
- t3297 = t3308 >= t3320
51
51
  t3299 = t3308 < t3329
52
52
  t3300 = t3297 && t3299
53
53
  t3289 = t3300 ? t3341 : t3306
@@ -204,22 +204,22 @@ module Kumi::Compiled::KUMI_579630ac2e4efb73c2ff1d43016b1220da91788dd8ebe9602128
204
204
  "retirement_contrib" => t558,
205
205
  "take_home" => t3815
206
206
  }
207
- acc3914 = 0.0
208
- t3916 = t3281["married_joint"] || t3281[:married_joint]
209
- t3917 = t3916["rates"] || t3916[:rates]
210
- acc3983 = 0.0
211
207
  t3937 = t3302["married_joint"] || t3302[:married_joint]
212
208
  t3938 = t3937["std"] || t3937[:std]
213
209
  t3939 = t3301 - t3938
214
210
  t3941 = [t3939, t3306]
215
211
  t3942 = t3941.max
212
+ acc3914 = 0.0
213
+ t3916 = t3281["married_joint"] || t3281[:married_joint]
214
+ t3917 = t3916["rates"] || t3916[:rates]
215
+ acc3983 = 0.0
216
216
  t3917.each_with_index do |t3918, t3919|
217
217
  t3954 = t3918["lo"] || t3918[:lo]
218
218
  t3967 = t3918["hi"] || t3918[:hi]
219
219
  t3975 = t3918["rate"] || t3918[:rate]
220
+ t3931 = t3942 >= t3954
220
221
  t3960 = t3967 == t3325
221
222
  t3963 = t3960 ? t3327 : t3967
222
- t3931 = t3942 >= t3954
223
223
  t3933 = t3942 < t3963
224
224
  t3934 = t3931 && t3933
225
225
  t3923 = t3934 ? t3975 : t3306
@@ -349,22 +349,22 @@ module Kumi::Compiled::KUMI_579630ac2e4efb73c2ff1d43016b1220da91788dd8ebe9602128
349
349
  "retirement_contrib" => t558,
350
350
  "take_home" => t4459
351
351
  }
352
- acc4560 = 0.0
353
- t4562 = t3281["married_separate"] || t3281[:married_separate]
354
- t4563 = t4562["rates"] || t4562[:rates]
355
- acc4629 = 0.0
356
352
  t4583 = t3302["married_separate"] || t3302[:married_separate]
357
353
  t4584 = t4583["std"] || t4583[:std]
358
354
  t4585 = t3301 - t4584
359
355
  t4587 = [t4585, t3306]
360
356
  t4588 = t4587.max
357
+ acc4560 = 0.0
358
+ t4562 = t3281["married_separate"] || t3281[:married_separate]
359
+ t4563 = t4562["rates"] || t4562[:rates]
360
+ acc4629 = 0.0
361
361
  t4563.each_with_index do |t4564, t4565|
362
362
  t4600 = t4564["lo"] || t4564[:lo]
363
363
  t4613 = t4564["hi"] || t4564[:hi]
364
364
  t4621 = t4564["rate"] || t4564[:rate]
365
+ t4577 = t4588 >= t4600
365
366
  t4606 = t4613 == t3325
366
367
  t4609 = t4606 ? t3327 : t4613
367
- t4577 = t4588 >= t4600
368
368
  t4579 = t4588 < t4609
369
369
  t4580 = t4577 && t4579
370
370
  t4569 = t4580 ? t4621 : t3306
@@ -494,22 +494,22 @@ module Kumi::Compiled::KUMI_579630ac2e4efb73c2ff1d43016b1220da91788dd8ebe9602128
494
494
  "retirement_contrib" => t558,
495
495
  "take_home" => t5105
496
496
  }
497
- acc5206 = 0.0
498
- t5208 = t3281["head_of_household"] || t3281[:head_of_household]
499
- t5209 = t5208["rates"] || t5208[:rates]
500
- acc5275 = 0.0
501
497
  t5229 = t3302["head_of_household"] || t3302[:head_of_household]
502
498
  t5230 = t5229["std"] || t5229[:std]
503
499
  t5231 = t3301 - t5230
504
500
  t5233 = [t5231, t3306]
505
501
  t5234 = t5233.max
502
+ acc5206 = 0.0
503
+ t5208 = t3281["head_of_household"] || t3281[:head_of_household]
504
+ t5209 = t5208["rates"] || t5208[:rates]
505
+ acc5275 = 0.0
506
506
  t5209.each_with_index do |t5210, t5211|
507
507
  t5246 = t5210["lo"] || t5210[:lo]
508
508
  t5259 = t5210["hi"] || t5210[:hi]
509
509
  t5267 = t5210["rate"] || t5210[:rate]
510
+ t5223 = t5234 >= t5246
510
511
  t5252 = t5259 == t3325
511
512
  t5255 = t5252 ? t3327 : t5259
512
- t5223 = t5234 >= t5246
513
513
  t5225 = t5234 < t5255
514
514
  t5226 = t5223 && t5225
515
515
  t5215 = t5226 ? t5267 : t3306
@@ -1,5 +1,5 @@
1
1
  # Autogenerated by Kumi Codegen
2
- module Kumi::Compiled::KUMI_637cda4c2c7cf245cb3f0b33719df27309bc57cdf29851183113d43acd8b22fe
2
+ module Kumi::Compiled::KUMI_ee04981edda49a2945319fd2a0457a60507a9e934cb7a4fdf3eeb6545dd1ef47
3
3
  def self.from(input_data = nil)
4
4
  instance = Object.new
5
5
  instance.extend(self)
@@ -41,22 +41,27 @@ module Kumi
41
41
  [fused, changed]
42
42
  end
43
43
 
44
+ # ---------------- core ----------------
45
+
46
+ Hoist = Struct.new(:ops, :target_depth, keyword_init: true)
47
+
44
48
  def inline_top_level_decl(ops)
45
49
  env = Env.new
46
50
  reg_map = {}
47
51
  rename_map = {}
48
- processed, hoisted = process_and_hoist_block(ops, env, reg_map, rename_map)
49
- raise "Orphaned code was hoisted to top level" unless hoisted.empty?
52
+ processed, hoist_pkgs = process_and_hoist_block(ops, env, reg_map, rename_map)
50
53
 
51
- processed
52
- end
54
+ top_emit, bubble = hoist_pkgs.partition { |p| p.target_depth == 0 }
55
+ raise "Orphaned code hoist with target depth(s): #{bubble.map(&:target_depth).uniq.inspect}" unless bubble.empty?
53
56
 
54
- # ---------------- core ----------------
57
+ top_emit.flat_map(&:ops) + processed
58
+ end
55
59
 
56
- # returns [processed_ops, hoisted_ops]
60
+ # returns [processed_ops, hoist_pkgs]
61
+ # returns [processed_ops, hoist_pkgs]
57
62
  def process_and_hoist_block(block_ops, env, reg_map, rename_map)
58
63
  out = []
59
- hoisted = []
64
+ hoisted_pkgs = []
60
65
  i = 0
61
66
  while i < block_ops.length
62
67
  ins = block_ops[i]
@@ -67,110 +72,144 @@ module Kumi
67
72
 
68
73
  env.push(ins)
69
74
  child_rename = {}
70
- processed_body, hoisted_from_child =
75
+ processed_body, child_hoists =
71
76
  process_and_hoist_block(loop_body, env, reg_map, child_rename)
77
+
78
+ depth_here = env.axes.length
79
+ child_el = ins.attributes[:as_element]
80
+ child_ix = ins.attributes[:as_index]
81
+
82
+ # Partition hoists: those that belong *inside* this loop vs bubble upward
83
+ inside_pkgs, bubble_pkgs = child_hoists.partition { |p| p.target_depth == depth_here }
84
+
85
+ # Renames: never let aliases to this loop's el/idx escape upward
86
+ safe_pairs = child_rename.reject { |_, v| v == child_el || v == child_ix }
72
87
  env.pop
73
88
 
74
- out.concat(hoisted_from_child) # emit hoists before loop
75
- rename_map.merge!(child_rename) # make child renames visible
89
+ # Merge only safe renames into outer scope
90
+ rename_map.merge!(safe_pairs)
91
+
92
+ # Emit loop shell
93
+ out << rewrite(ins, reg_map, rename_map)
94
+
95
+ # Local view inside loop: apply both local and outer renames, with local taking precedence
96
+ local_map = child_rename.merge(rename_map)
97
+
98
+ # Emit hoists that belong at this depth *inside* the loop, before the body
99
+ inside_ops = inside_pkgs.flat_map(&:ops)
100
+ out.concat(rewrite_block(inside_ops, local_map))
76
101
 
77
- out << rewrite(ins, reg_map, rename_map) # loop shell
78
- out.concat(processed_body)
102
+ # Emit rewritten body
103
+ out.concat(rewrite_block(processed_body, local_map))
104
+
105
+ # Close loop
79
106
  out << rewrite(block_ops[end_idx], reg_map, rename_map)
80
107
 
108
+ # Bubble remaining hoists to outer scopes
109
+ hoisted_pkgs.concat(bubble_pkgs)
110
+
111
+ # Extra safety: ensure no aliases to this loop's el/idx remain in outer map
112
+ rename_map.delete_if { |_, v| v == child_el || v == child_ix }
113
+
81
114
  i = end_idx
82
115
 
83
116
  when :LoadDeclaration
84
- inline_ops, hoist_ops =
85
- handle_load_declaration(ins, env, reg_map, rename_map)
117
+ inline_ops, new_pkgs = handle_load_declaration(ins, env, reg_map, rename_map)
86
118
  out.concat(inline_ops)
87
- hoisted.concat(hoist_ops)
119
+ hoisted_pkgs.concat(new_pkgs)
88
120
 
89
121
  else
90
122
  out << rewrite(ins, reg_map, rename_map)
91
123
  end
92
124
  i += 1
93
125
  end
94
- [out, hoisted]
126
+ [out, hoisted_pkgs]
95
127
  end
96
128
 
97
- # returns [inline_ops, hoisted_ops]
98
- def handle_load_declaration(call_ins, env, reg_map, outer_rename_map)
99
- raise "LoadDeclaration missing callee" unless call_ins.immediates&.first&.respond_to?(:value)
100
-
101
- callee = call_ins.immediates.first.value.to_sym
102
- raise "LoadDeclaration callee #{callee} not found" unless @ops_by_decl.key?(callee)
129
+ # returns [inline_ops, hoist_pkgs]
130
+ def handle_load_declaration(ins, env, _reg_map, rename_map)
131
+ callee = ins.immediates.first.value.to_sym
103
132
 
104
- decl_axes = fetch_decl_axes(callee, call_ins)
105
- site_axes = env.axes
133
+ # axes presence and agreement with callee gamma
134
+ decl_axes = ins.attributes.fetch(:axes) { raise "LoadDeclaration missing :axes for #{callee}" }
135
+ gamma_axes = @gamma.fetch(callee).axes
136
+ raise "axes mismatch for #{callee}: decl=#{decl_axes.inspect} gamma=#{gamma_axes.inspect}" unless decl_axes == gamma_axes
106
137
 
107
- body, yield_reg, callee_axis_regs = inline_callee_core(callee)
108
- axis_map = remap_axes(callee_axis_regs, env)
138
+ body, yield_reg, callee_regs = inline_callee_core(callee)
139
+ remap = remap_axes(callee_regs, env)
109
140
 
141
+ # per-callsite freshening
110
142
  local_reg_map = {}
111
- _acc, fresh_ops = freshen(body, local_reg_map, pre_map: axis_map)
112
-
113
- nested_rename = {}
114
- processed_inner, hoisted_inner =
115
- process_and_hoist_block(fresh_ops, env, {}, nested_rename)
116
-
117
- prelim = local_reg_map[yield_reg] || axis_map[yield_reg] || yield_reg
118
- mapped_yield = resolve_rename(prelim, nested_rename)
119
-
120
- # def/dominance guard
121
- defs_inline = defs_in(processed_inner)
122
- defs_hoisted = defs_in(hoisted_inner)
123
- unless defs_inline.include?(mapped_yield) || defs_hoisted.include?(mapped_yield)
124
- msg = [
125
- "inliner: mapped yield #{mapped_yield} has no def in emitted ops for #{callee}",
126
- " original yield: #{yield_reg}",
127
- " prelim mapping: #{prelim}",
128
- " nested_rename keys: #{nested_rename.keys.inspect}",
129
- " inline defs size: #{defs_inline.size}",
130
- " hoisted defs size: #{defs_hoisted.size}"
131
- ].join("\n")
132
- raise msg
143
+ _acc, fresh_ops = freshen(body, local_reg_map, pre_map: remap)
144
+
145
+ # recursively process nested calls
146
+ processed_inline, nested_pkgs = process_and_hoist_block(fresh_ops, env, {}, rename_map)
147
+
148
+ # compute yielded register mapping, then resolve through any renames created by nested inlines
149
+ mapped_yield =
150
+ local_reg_map[yield_reg] || remap[yield_reg] ||
151
+ (raise "inliner: yielded reg #{yield_reg} not produced in inlined body for #{callee}")
152
+ resolved_yield = resolve_rename(mapped_yield, rename_map)
153
+
154
+ # sanity: resolved_yield must be definable at site
155
+ emitted_defs = processed_inline.map(&:result_register).compact +
156
+ nested_pkgs.flat_map { |p| p.ops }.map(&:result_register).compact
157
+ unless emitted_defs.include?(resolved_yield) || env.ambient_regs.include?(resolved_yield)
158
+ raise "inliner: mapped yield #{resolved_yield} has no def in emitted ops for #{callee}\n" \
159
+ "original yield: #{yield_reg}\n" \
160
+ "inline defs size: #{processed_inline.count { |x| x.result_register }}\n" \
161
+ "nested hoist defs size: #{nested_pkgs.flat_map { |p| p.ops }.count { |x| x.result_register }}"
133
162
  end
134
163
 
135
- outer_rename_map[call_ins.result_register] = mapped_yield if call_ins.result_register
164
+ # final rename for call site result uses the resolved register
165
+ rename_map[ins.result_register] = resolved_yield
166
+
167
+ # decide placement by depth
168
+ site_depth = env.axes.length
169
+ callee_depth = decl_axes.length
170
+
171
+ if callee_depth < site_depth
172
+ forb = forbidden_ambient_after(callee_depth, env)
173
+ used = uses_of(processed_inline)
174
+ bad = used & forb
175
+ unless bad.empty?
176
+ raise "scope error: would hoist ops using deeper-axis regs #{bad.inspect} " \
177
+ "(callee_depth=#{callee_depth}, site_depth=#{site_depth})"
178
+ end
179
+ pkgs = nested_pkgs + [Hoist.new(ops: processed_inline, target_depth: callee_depth)]
180
+ [[], pkgs]
181
+
182
+ elsif callee_depth == site_depth
183
+ emit, bubble = nested_pkgs.partition { |p| p.target_depth == site_depth }
184
+ [(emit.flat_map(&:ops) + processed_inline), bubble]
136
185
 
137
- if prefix?(decl_axes, site_axes) && decl_axes.length < site_axes.length
138
- [[], hoisted_inner + processed_inner] # hoist
139
- elsif decl_axes == site_axes
140
- [hoisted_inner + processed_inner, []] # inline in place
141
186
  else
142
- [[rewrite(call_ins, reg_map, outer_rename_map)], []] # cannot inline
187
+ [[rewrite(ins, {}, rename_map)], []]
143
188
  end
144
189
  end
145
190
 
146
191
  # ---------------- helpers ----------------
192
+ def rewrite_block(ops, rename)
193
+ # Ensure late-added renames apply to a block we built earlier.
194
+ ops.map { |ins| rewrite(ins, {}, rename) }
195
+ end
147
196
 
148
- def resolve_rename(reg, rename, limit: 64)
197
+ def resolve_rename(reg, rename)
149
198
  seen = {}
150
199
  cur = reg
151
- limit.times do
152
- nxt = rename[cur]
153
- break unless nxt
154
- raise "inliner: rename cycle at #{cur}" if seen[nxt]
155
-
156
- seen[nxt] = true
157
- cur = nxt
200
+ while (n = rename[cur]) && !seen[n]
201
+ seen[cur] = true
202
+ cur = n
158
203
  end
159
204
  cur
160
205
  end
161
206
 
162
- def defs_in(ops)
163
- ops.each_with_object(Set.new) { |ins, s| s << ins.result_register if ins.result_register }
207
+ def uses_of(ops)
208
+ ops.flat_map { |x| Array(x.inputs) }.compact
164
209
  end
165
210
 
166
- def fetch_decl_axes(callee, call_ins)
167
- attr_axes = call_ins.attributes && call_ins.attributes[:axes]
168
- gamma_axes = Array(@gamma.fetch(callee)&.axes || [])
169
- ax = attr_axes.nil? ? gamma_axes : Array(attr_axes)
170
- raise "LoadDeclaration missing :axes" if ax.nil?
171
- raise "inliner: non-array axes for #{callee}: #{ax.inspect}" unless ax.is_a?(Array)
172
-
173
- ax
211
+ def forbidden_ambient_after(depth, env)
212
+ env.frames_after(depth).flat_map { |f| [f[:el], f[:idx]] }
174
213
  end
175
214
 
176
215
  def find_matching_loop_end(ops, start_index)
@@ -195,6 +234,7 @@ module Kumi
195
234
  class Env
196
235
  def initialize = @frames = []
197
236
  def axes = @frames.map { _1[:axis] }
237
+ def ambient_regs = @frames.flat_map { |f| [f[:el], f[:idx]] }
198
238
 
199
239
  def push(loop_ins)
200
240
  @frames << {
@@ -210,6 +250,10 @@ module Kumi
210
250
  @frames.reverse.find { _1[:axis] == axis } ||
211
251
  raise("no element for axis=#{axis.inspect}")
212
252
  end
253
+
254
+ def frames_after(depth)
255
+ @frames[depth..] || []
256
+ end
213
257
  end
214
258
 
215
259
  def detect_all_gammas(ops_by_decl)
@@ -240,10 +284,10 @@ module Kumi
240
284
  end
241
285
 
242
286
  def inline_callee_core(callee_name)
243
- ops = Array(@ops_by_decl.fetch(callee_name)[:operations])
287
+ ops = Array(@ops_by_decl.fetch(callee_name)[:operations])
244
288
  info = @gamma.fetch(callee_name)
245
289
  axes = info.axes
246
- k = axes.length
290
+ k = axes.length
247
291
 
248
292
  yi = ops.rindex { |x| x.opcode == :Yield } or raise "callee #{callee_name} has no Yield"
249
293
  yielded_reg = Array(ops[yi].inputs).first
data/lib/kumi/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- VERSION = "0.0.24"
4
+ VERSION = "0.0.25"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kumi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.24
4
+ version: 0.0.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - André Muta