From 81b654d76444d747fdf1d053760e507b50f26e18 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 5 Oct 2026 15:38:06 -0700 Subject: Small peep to drop simple store-updates out of loops --- docs/2026_REBASE/README.md | 5 +- docs/2026_REBASE/assets/memory-final.json | 198 +++++++++++------------------- docs/2026_REBASE/assets/memory-final.svg | 136 ++++++++++---------- docs/2026_REBASE/slides.js | 6 +- docs/chapter-backports.md | 28 +++++ 5 files changed, 168 insertions(+), 205 deletions(-) (limited to 'docs') diff --git a/docs/2026_REBASE/README.md b/docs/2026_REBASE/README.md index 925be1ed..4c3b5114 100644 --- a/docs/2026_REBASE/README.md +++ b/docs/2026_REBASE/README.md @@ -140,13 +140,16 @@ show the global scheduler moving instructions. Discuss GCM from the slide. |---|---|---| | [arithmetic.smp](demos/arithmetic.smp) | 04 | Final expression `2*arg+6`; 51 capture frames | | [control.smp](demos/control.smp) | 08 | Sum 1 through `abs(arg)`; 65 frames | -| [memory.smp](demos/memory.smp) | 17b | Two loops, array/object memory; 319 frames, 54 final nodes | +| [memory.smp](demos/memory.smp) | 17b | Two loops; final totals stored after the loop; 333 frames, 52 final nodes | | [aliases.smp](demos/aliases.smp) | 17b | Short follow-up: writing `p.y` leaves `p.x` unchanged; result 0 | | [inline.smp](demos/inline.smp) | 18 | Two single-caller functions disappear; `return 11;`; 161 frames | | [sccp.smp](demos/sccp.smp) | 24; viewer 25 | Return value 1 verified in the final graph; 76 frames, about 0.38 MB | The large memory example has runtime result `4*arg+10`. It retains loops and memory operations; that formula is not its claimed optimized graph. +The `sum` and `visits` fields become scalar loop Phis, with one store each +afterward. Their memory Phis disappear, and GCM can also place the `Totals` +allocation after the loop. The array initialization still needs its memory Phi. For SCCP, proving a constant result alone does not license deleting effects or a possibly nonterminating loop. diff --git a/docs/2026_REBASE/assets/memory-final.json b/docs/2026_REBASE/assets/memory-final.json index e2f378ed..3900b426 100644 --- a/docs/2026_REBASE/assets/memory-final.json +++ b/docs/2026_REBASE/assets/memory-final.json @@ -1,7 +1,7 @@ { "ver": 1, "comp": "rebase-memory", - "step": 318, + "step": 332, "roots": [ 2, 3 @@ -989,45 +989,6 @@ ], "proj": null }, - { - "id": 94, - "label": "st_sum", - "type": "MEM#2:Bot", - "kind": "MEM", - "edges": [ - { - "idx": 0, - "def": 0, - "role": "CTRL", - "label": null - }, - { - "idx": 1, - "def": 104, - "role": "MEM", - "label": null - }, - { - "idx": 2, - "def": 15, - "role": "DATA", - "label": null - }, - { - "idx": 3, - "def": 4, - "role": "DATA", - "label": null - }, - { - "idx": 4, - "def": 88, - "role": "DATA", - "label": null - } - ], - "proj": null - }, { "id": 99, "label": "Add", @@ -1042,7 +1003,7 @@ }, { "idx": 1, - "def": 138, + "def": 141, "role": "DATA", "label": null }, @@ -1055,45 +1016,6 @@ ], "proj": null }, - { - "id": 100, - "label": "st_visits", - "type": "MEM#3:Bot", - "kind": "MEM", - "edges": [ - { - "idx": 0, - "def": 0, - "role": "CTRL", - "label": null - }, - { - "idx": 1, - "def": 132, - "role": "MEM", - "label": null - }, - { - "idx": 2, - "def": 15, - "role": "DATA", - "label": null - }, - { - "idx": 3, - "def": 51, - "role": "DATA", - "label": null - }, - { - "idx": 4, - "def": 99, - "role": "DATA", - "label": null - } - ], - "proj": null - }, { "id": 103, "label": "Add", @@ -1121,33 +1043,6 @@ ], "proj": null }, - { - "id": 104, - "label": "MemPhi_2", - "type": "MEM#2:Bot", - "kind": "PHI", - "edges": [ - { - "idx": 0, - "def": 65, - "role": "ASSOC", - "label": null - }, - { - "idx": 1, - "def": 16, - "role": "MEM", - "label": null - }, - { - "idx": 2, - "def": 94, - "role": "MEM", - "label": null - } - ], - "proj": null - }, { "id": 106, "label": "MemMerge", @@ -1168,13 +1063,13 @@ }, { "idx": 2, - "def": 104, + "def": 146, "role": "MEM", "label": null }, { "idx": 3, - "def": 132, + "def": 142, "role": "MEM", "label": null }, @@ -1207,7 +1102,7 @@ }, { "idx": 1, - "def": 138, + "def": 141, "role": "DATA", "label": null }, @@ -1302,9 +1197,9 @@ "proj": null }, { - "id": 132, - "label": "MemPhi_3", - "type": "MEM#3:Bot", + "id": 141, + "label": "Phi_visits", + "type": "int", "kind": "PHI", "edges": [ { @@ -1313,6 +1208,33 @@ "role": "ASSOC", "label": null }, + { + "idx": 1, + "def": 4, + "role": "DATA", + "label": null + }, + { + "idx": 2, + "def": 99, + "role": "DATA", + "label": null + } + ], + "proj": null + }, + { + "id": 142, + "label": "st_visits", + "type": "MEM#3:int", + "kind": "MEM", + "edges": [ + { + "idx": 0, + "def": 0, + "role": "CTRL", + "label": null + }, { "idx": 1, "def": 16, @@ -1321,34 +1243,58 @@ }, { "idx": 2, - "def": 100, - "role": "MEM", + "def": 15, + "role": "DATA", + "label": null + }, + { + "idx": 3, + "def": 51, + "role": "DATA", + "label": null + }, + { + "idx": 4, + "def": 141, + "role": "DATA", "label": null } ], "proj": null }, { - "id": 138, - "label": "Phi_visits", - "type": "int", - "kind": "PHI", + "id": 146, + "label": "st_sum", + "type": "MEM#2:int", + "kind": "MEM", "edges": [ { "idx": 0, - "def": 65, - "role": "ASSOC", + "def": 0, + "role": "CTRL", "label": null }, { "idx": 1, + "def": 16, + "role": "MEM", + "label": null + }, + { + "idx": 2, + "def": 15, + "role": "DATA", + "label": null + }, + { + "idx": 3, "def": 4, "role": "DATA", "label": null }, { - "idx": 2, - "def": 99, + "idx": 4, + "def": 131, "role": "DATA", "label": null } @@ -1389,15 +1335,11 @@ 91, 92, 93, - 94, 99, - 100, 103, - 104, 112, 131, - 132, - 138 + 141 ] } ] diff --git a/docs/2026_REBASE/assets/memory-final.svg b/docs/2026_REBASE/assets/memory-final.svg index 205fea1b..47daa792 100644 --- a/docs/2026_REBASE/assets/memory-final.svg +++ b/docs/2026_REBASE/assets/memory-final.svg @@ -1,4 +1,4 @@ -#3[0] → #115 · CTRL#4[0] → #2 · ASSOC#5[0] → #2 · ASSOC#12[0] → #2 · ASSOC#13[2] → #7 · MEM#13[3] → #7 · MEM#14[0] → #6 · CTRL#14[1] → #13 · MEM#14[2] → #12 · DATA#14[3] → #4 · DATA#14[4] → #4 · DATA#18[0] → #2 · ASSOC#24[0] → #2 · ASSOC#26[4] → #7 · MEM#26[5] → #7 · MEM#27[0] → #6 · CTRL#27[1] → #26 · MEM#27[2] → #24 · DATA#27[3] → #18 · DATA#27[4] → #4 · DATA#32[1] → #6 · CTRL#32[2] → #41 · CTRL#34[1] → #4 · DATA#34[2] → #60 · DATA#35[1] → #34 · DATA#35[2] → #18 · DATA#40[0] → #32 · CTRL#40[1] → #35 · DATA#46[0] → #2 · ASSOC#51[0] → #2 · ASSOC#52[0] → #2 · ASSOC#53[1] → #34 · DATA#53[2] → #52 · DATA#54[1] → #53 · DATA#54[2] → #51 · DATA#56[1] → #34 · DATA#56[2] → #8 · DATA#57[0] → #41 · CTRL#57[1] → #124 · MEM#57[2] → #28 · DATA#57[3] → #54 · DATA#57[4] → #56 · DATA#60[1] → #34 · DATA#60[2] → #46 · DATA#65[1] → #42 · CTRL#65[2] → #74 · CTRL#67[1] → #4 · DATA#67[2] → #103 · DATA#68[1] → #67 · DATA#68[2] → #18 · DATA#73[0] → #65 · CTRL#73[1] → #68 · DATA#88[1] → #131 · DATA#88[2] → #93 · DATA#91[1] → #67 · DATA#91[2] → #52 · DATA#92[1] → #91 · DATA#92[2] → #51 · DATA#93[0] → #74 · CTRL#93[1] → #124 · MEM#93[2] → #28 · DATA#93[3] → #92 · DATA#99[1] → #141 · DATA#99[2] → #46 · DATA#103[1] → #67 · DATA#103[2] → #46 · DATA#106[1] → #7 · MEM#106[2] → #146 · MEM#106[3] → #142 · MEM#106[4] → #29 · MEM#106[5] → #124 · MEM#112[1] → #141 · DATA#112[2] → #131 · DATA#115[0] → #75 · CTRL#115[1] → #106 · MEM#115[2] → #112 · DATA#124[1] → #29 · MEM#124[2] → #57 · MEM#131[1] → #4 · DATA#131[2] → #88 · DATA#141[1] → #4 · DATA#141[2] → #99 · DATA#142[1] → #16 · MEM#142[2] → #15 · DATA#142[3] → #51 · DATA#142[4] → #141 · DATA#146[1] → #16 · MEM#146[2] → #15 · DATA#146[3] → #4 · DATA#146[4] → #131 · DATAStart  #2[ Ctrl, MemBot, int]#2 Start [ Ctrl, MemBot, int] [0] → — ASSOC [1] → — CTRL -[2] → — CTRL012Stop  #3#3 Stop +[2] → — CTRL012Stop  #3#3 Stop Bot [0] → #115 CTRL0Con  #40#4 Con 0 -[0] → #2 ASSOC0Xctrl  #5~Ctrl#5 Xctrl +[0] → #2 ASSOC0Xctrl  #5~Ctrl#5 Xctrl ~Ctrl [0] → #2 ASSOC0$ctrl  #6/0Ctrl#6 $ctrl @@ -95,28 +95,28 @@ MemBot [0] → — CTRL [1] → — DATA [2] → #7 MEM -[3] → #7 MEM0123new_Totals  #14[ *Totals {int !sum; int !visits; }, MemBot]#14 new_Totals +[3] → #7 MEM0123new_Totals  #14[ *Totals {int !sum; int !visits; }, MemBot]#14 new_Totals [ *Totals {int !sum; int !visits; }, MemBot] [0] → #6 CTRL [1] → #13 MEM [2] → #12 DATA [3] → #4 DATA -[4] → #4 DATA01234Totals  #15/0*Totals {int !sum; int !visits; }#15 Totals +[4] → #4 DATA01234Totals  #15/0*Totals {int !sum; int !visits; }#15 Totals Projection 0 of #14 *Totals {int !sum; int !visits; } -[0] → #14 DATA$mem  #16/1MemBot#16 $mem +[0] → #14 DATA$mem  #16/1MemBot#16 $mem Projection 1 of #14 MemBot [0] → #14 MEMCon  #184#18 Con 4 -[0] → #2 ASSOC0Con  #2440#24 Con +[0] → #2 ASSOC0Con  #2440#24 Con 40 -[0] → #2 ASSOC0MemMerge  #26MemBot#26 MemMerge +[0] → #2 ASSOC0MemMerge  #26MemBot#26 MemMerge MemBot [0] → — CTRL @@ -124,197 +124,187 @@ MemBot [2] → — DATA [3] → — DATA [4] → #7 MEM -[5] → #7 MEM012345new_ary_int  #27[ *[int] {int #; int ![]; }, MemBot]#27 new_ary_int +[5] → #7 MEM012345new_ary_int  #27[ *[int] {int #; int ![]; }, MemBot]#27 new_ary_int [ *[int] {int #; int ![]; }, MemBot] [0] → #6 CTRL [1] → #26 MEM [2] → #24 DATA [3] → #18 DATA -[4] → #4 DATA01234[int]  #28/0*[int] {int #; int ![]; }#28 [int] +[4] → #4 DATA01234[int]  #28/0*[int] {int #; int ![]; }#28 [int] Projection 0 of #27 *[int] {int #; int ![]; } -[0] → #27 DATA$mem  #29/1MemBot#29 $mem +[0] → #27 DATA$mem  #29/1MemBot#29 $mem Projection 1 of #27 MemBot -[0] → #27 MEMLoop  #32Ctrl#32 Loop +[0] → #27 MEMLoop  #32Ctrl#32 Loop Ctrl [0] → — ASSOC [1] → #6 CTRL -[2] → #41 CTRL012Phi_i  #34int#34 Phi_i +[2] → #41 CTRL012Phi_i  #34int#34 Phi_i int [0] → #32 ASSOC [1] → #4 DATA -[2] → #60 DATA[0] → #32012LT  #35bool#35 LT +[2] → #60 DATA[0] → #32012LT  #35bool#35 LT bool [0] → — CTRL [1] → #34 DATA -[2] → #18 DATA012If  #40[ Ctrl, Ctrl]#40 If +[2] → #18 DATA012If  #40[ Ctrl, Ctrl]#40 If [ Ctrl, Ctrl] [0] → #32 CTRL -[1] → #35 DATA01True  #41/0Ctrl#41 True +[1] → #35 DATA01True  #41/0Ctrl#41 True Projection 0 of #40 Ctrl -[0] → #40 CTRLFalse  #42/1Ctrl#42 False +[0] → #40 CTRLFalse  #42/1Ctrl#42 False Projection 1 of #40 Ctrl -[0] → #40 CTRLCon  #461#46 Con +[0] → #40 CTRLCon  #461#46 Con 1 -[0] → #2 ASSOC0Con  #518#51 Con +[0] → #2 ASSOC0Con  #518#51 Con 8 -[0] → #2 ASSOC0Con  #523#52 Con +[0] → #2 ASSOC0Con  #523#52 Con 3 -[0] → #2 ASSOC0Shl  #53int#53 Shl +[0] → #2 ASSOC0Shl  #53int#53 Shl int [0] → — CTRL [1] → #34 DATA -[2] → #52 DATA012Add  #54int#54 Add +[2] → #52 DATA012Add  #54int#54 Add int [0] → — CTRL [1] → #53 DATA -[2] → #51 DATA012Add  #56int#56 Add +[2] → #51 DATA012Add  #56int#56 Add int [0] → — CTRL [1] → #34 DATA -[2] → #8 DATA012st_ary  #57MEM#5:Bot#57 st_ary +[2] → #8 DATA012st_ary  #57MEM#5:Bot#57 st_ary MEM#5:Bot [0] → #41 CTRL [1] → #124 MEM [2] → #28 DATA [3] → #54 DATA -[4] → #56 DATA01234Add  #60int#60 Add +[4] → #56 DATA01234Add  #60int#60 Add int [0] → — CTRL [1] → #34 DATA -[2] → #46 DATA012Loop  #65Ctrl#65 Loop +[2] → #46 DATA012Loop  #65Ctrl#65 Loop Ctrl [0] → — ASSOC [1] → #42 CTRL -[2] → #74 CTRL012Phi_i  #67int#67 Phi_i +[2] → #74 CTRL012Phi_i  #67int#67 Phi_i int [0] → #65 ASSOC [1] → #4 DATA -[2] → #103 DATA[0] → #65012LT  #68bool#68 LT +[2] → #103 DATA[0] → #65012LT  #68bool#68 LT bool [0] → — CTRL [1] → #67 DATA -[2] → #18 DATA012If  #73[ Ctrl, Ctrl]#73 If +[2] → #18 DATA012If  #73[ Ctrl, Ctrl]#73 If [ Ctrl, Ctrl] [0] → #65 CTRL -[1] → #68 DATA01True  #74/0Ctrl#74 True +[1] → #68 DATA01True  #74/0Ctrl#74 True Projection 0 of #73 Ctrl -[0] → #73 CTRLFalse  #75/1Ctrl#75 False +[0] → #73 CTRLFalse  #75/1Ctrl#75 False Projection 1 of #73 Ctrl -[0] → #73 CTRLAdd  #88int#88 Add +[0] → #73 CTRLAdd  #88int#88 Add int [0] → — CTRL [1] → #131 DATA -[2] → #93 DATA012Shl  #91int#91 Shl +[2] → #93 DATA012Shl  #91int#91 Shl int [0] → — CTRL [1] → #67 DATA -[2] → #52 DATA012Add  #92int#92 Add +[2] → #52 DATA012Add  #92int#92 Add int [0] → — CTRL [1] → #91 DATA -[2] → #51 DATA012ld_ary  #93int#93 ld_ary +[2] → #51 DATA012ld_ary  #93int#93 ld_ary int [0] → #74 CTRL [1] → #124 MEM [2] → #28 DATA -[3] → #92 DATA0123st_sum  #94MEM#2:Bot#94 st_sum -MEM#2:Bot - -[0] → — CTRL -[1] → #104 MEM -[2] → #15 DATA -[3] → #4 DATA -[4] → #88 DATA01234Add  #99int#99 Add +[3] → #92 DATA0123Add  #99int#99 Add int [0] → — CTRL -[1] → #138 DATA -[2] → #46 DATA012st_visits  #100MEM#3:Bot#100 st_visits -MEM#3:Bot - -[0] → — CTRL -[1] → #132 MEM -[2] → #15 DATA -[3] → #51 DATA -[4] → #99 DATA01234Add  #103int#103 Add +[1] → #141 DATA +[2] → #46 DATA012Add  #103int#103 Add int [0] → — CTRL [1] → #67 DATA -[2] → #46 DATA012MemPhi_2  #104MEM#2:Bot#104 MemPhi_2 -MEM#2:Bot - -[0] → #65 ASSOC -[1] → #16 MEM -[2] → #94 MEM[0] → #65012MemMerge  #106MemBot#106 MemMerge +[2] → #46 DATA012MemMerge  #106MemBot#106 MemMerge MemBot [0] → — CTRL [1] → #7 MEM -[2] → #104 MEM -[3] → #132 MEM +[2] → #146 MEM +[3] → #142 MEM [4] → #29 MEM -[5] → #124 MEM012345Add  #112int#112 Add +[5] → #124 MEM012345Add  #112int#112 Add int [0] → — CTRL -[1] → #138 DATA -[2] → #131 DATA012Return  #115[ Ctrl, MemBot, int]#115 Return +[1] → #141 DATA +[2] → #131 DATA012Return  #115[ Ctrl, MemBot, int]#115 Return [ Ctrl, MemBot, int] [0] → #75 CTRL [1] → #106 MEM -[2] → #112 DATA012MemPhi_5  #124MEM#5:Bot#124 MemPhi_5 +[2] → #112 DATA012MemPhi_5  #124MEM#5:Bot#124 MemPhi_5 MEM#5:Bot [0] → #32 ASSOC [1] → #29 MEM -[2] → #57 MEM[0] → #32012Phi_sum  #131int#131 Phi_sum +[2] → #57 MEM[0] → #32012Phi_sum  #131int#131 Phi_sum int [0] → #65 ASSOC [1] → #4 DATA -[2] → #88 DATA[0] → #65012MemPhi_3  #132MEM#3:Bot#132 MemPhi_3 -MEM#3:Bot - -[0] → #65 ASSOC -[1] → #16 MEM -[2] → #100 MEM[0] → #65012Phi_visits  #138int#138 Phi_visits +[2] → #88 DATA[0] → #65012Phi_visits  #141int#141 Phi_visits int [0] → #65 ASSOC [1] → #4 DATA -[2] → #99 DATA[0] → #65012Loop  #32Loop · 12 nodes−Fold regionLoop  #65Loop · 19 nodes−Fold region +[2] → #99 DATA[0] → #65012st_visits  #142MEM#3:int#142 st_visits +MEM#3:int + +[0] → — CTRL +[1] → #16 MEM +[2] → #15 DATA +[3] → #51 DATA +[4] → #141 DATA01234st_sum  #146MEM#2:int#146 st_sum +MEM#2:int + +[0] → — CTRL +[1] → #16 MEM +[2] → #15 DATA +[3] → #4 DATA +[4] → #131 DATA01234Loop  #32Loop · 12 nodes−Fold regionLoop  #65Loop · 15 nodes−Fold region diff --git a/docs/2026_REBASE/slides.js b/docs/2026_REBASE/slides.js index de888f13..9ed9729e 100644 --- a/docs/2026_REBASE/slides.js +++ b/docs/2026_REBASE/slides.js @@ -116,7 +116,7 @@ id:'memory',label:'9–17b · Values, memory, scheduling',section:'Chapters 9–17b',minutes:5,steps:2,demo:'aliases', body:state=>'

Memory is dataflow, too.

'+source(state.demo||'aliases',true)+'

Partition memory into
equivalence classes.

'+memoryPartition(state)+states(state.demo==='memory'?[ '
data[i]Array element memory is another slice.
', - '
t.sum / t.visitsSeparate field classes carry separate dependencies.
', + '
t.sum / t.visitsCarry values around the loop; store the final totals once.
', '
4*arg + 10Runtime result; the graph still contains loops.
' ]:[ '
p.x → Pair.xAll Pair.x fields share one equivalence class.
', @@ -127,9 +127,9 @@ '0:00–0:45: Briefly show Domains and Memory types, then return to Examples. Use the partition picture: Pair.x, Pair.y, and the rest are disjoint equivalence classes whose union is whole memory. The classes group locations across all instances, not one class per object.', '0:45–1:15: Run make -C chapter17b view. Paste Short alias example. Show that p.y changes while p.x does not, and the result becomes zero.', '1:15–3:15: Trace the Loads and Store through their field equivalence classes. Same-class accesses may alias; different classes cannot. MemMerge recombines the slices without emitting an instruction. Explain why writing p.y does not clobber p.x. Read-only access would not make a Load independent of memory.', - '3:15–4:30: Switch back to this slide, select Array + object loops, Copy code, then compile it in the same viewer. Jump Last, fold/unfold the loops, and inspect loop-carried memory and the sum and visits aliases.', + '3:15–4:30: Switch back to this slide, select Array + object loops, Copy code, then compile it in the same viewer. Jump Last: array writes still carry memory around the first loop. The second loop carries sum and visits as scalar Phis; their stores move after the loop and their memory Phis disappear.', '4:30–5:00: GCM uses these dependencies to choose legal placements; it does not guess ordering from source order. Return to slides.' - ],'

The graph viewer captures parsing/optimization/type checking, not GCM execution. The large graph has 54 nodes at its final checkpoint; do not step through all 319 frames.

Research context: Diwan, McKinley, and Moss, Type-Based Alias Analysis, PLDI 1998, sections 3.5–3.6 (p. 113). A runtime upper bound left at most an average 2.5% more heap loads eliminable beyond their analysis on eight Modula-3 benchmarks. This is a load-elimination bound, not a speedup percentage. More static opportunities did not translate into corresponding runtime gains. This supports choosing useful, inexpensive alias distinctions; it does not establish that this exact equivalence-class scheme is optimal for Java. The study is a close match to the recalled oracle experiment, not an identified IBM/Java paper.

') + ],'

The graph viewer captures parsing/optimization/type checking, not GCM execution. The large graph has 52 nodes at its final checkpoint; do not step through all 333 frames.

Research context: Diwan, McKinley, and Moss, Type-Based Alias Analysis, PLDI 1998, sections 3.5–3.6 (p. 113). A runtime upper bound left at most an average 2.5% more heap loads eliminable beyond their analysis on eight Modula-3 benchmarks. This is a load-elimination bound, not a speedup percentage. More static opportunities did not translate into corresponding runtime gains. This supports choosing useful, inexpensive alias distinctions; it does not establish that this exact equivalence-class scheme is optimal for Java. The study is a close match to the recalled oracle experiment, not an identified IBM/Java paper.

') }, { id:'inline',label:'18 · Functions and Calls',section:'Chapter 18',minutes:5,steps:2,demo:'inline', diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md index a8be1e24..53fd6a9c 100644 --- a/docs/chapter-backports.md +++ b/docs/chapter-backports.md @@ -109,6 +109,34 @@ Existing spill expectations changed in the String fixture: These records describe completed work, not outstanding implementation tasks. Historical chapter numbers and measurements below refer to their original runs. +### Loop store sinking: Chapters 17b-25 (2026-10-05) + +The Chapter 17b presentation peephole is forwarded through Chapter 25. A loop +memory Phi whose backedge is a sole-use Store becomes a Store of a scalar Phi. +An entry Load preserves zero-trip behavior. The address must be a constant +field offset of an allocation dominating the loop; array elements are excluded. +Wait for parallel bulk-memory splitting before replacing the precise Phi. +Chapter 25 preserves the Store's frozen width and excludes unresolved aliases, +undecided widths, and constructor initialization Stores. + +Existing suites exposed three small supporting fixes: Loads through a TOP +pointer stay TOP in 17b-23; Chapter 25 keeps a MemMerge alive while flattening a +dead memory cycle; the shared printer computes placement for constants even +when they have no cached block. Existing graph assertions now count reachable +definitions and allow scalar field Phis. The synthetic-exit fixture uses a +varying pointer so it still exercises loop-carried memory after store sinking. +No new test methods were added for the forward port. Spill expectations were +updated for the changed graphs, including the infinite-loop, String, and Scan +fixtures; runtime and register-correctness assertions remain enabled. + +Validation: full Make suites pass in 17b (306 including its fuzzer wrapper), +18 (339), 19 (383), 20 (405), 21 (444), 22 (465), 23 (487), and 24 (517), plus +the separate fuzzer wrappers in 18-24. Chapter 25 passes all six suite groups +(403, 39, 9, 23, 1, and 19), including rebuilt sys.o, native execution, and +the existing fuzzer seeds. Shared printer and ISA checks pass. Logs are in +`build/rebase-talk/loop-store-*.log`. The presentation example still has 333 +capture frames and 52 final nodes, with both Totals stores outside the loop. + ### Shared ISA encoders and evaluators: Chapters 21-25 Issue #257's extraction is implemented in [`isa/`](../isa/README.md). `X86` -- cgit v1.2.3