Skip to content

Commit 430539c

Browse files
Dale-Blackclaude
andcommitted
Fix nested for loops: emit inner while(true) for nested loop headers
The bug: emit_loop_body! never checked for nested loop headers. Inner loops compiled as if-blocks instead of while(true) loops, causing inner break to exit the outer loop. Fix: when emit_loop_body! encounters a statement that's in loop_headers (and isn't the current loop's own header), emit a nested while(true) with proper phi initialization and recursive body emission. Now works: double nested, triple nested, matmul with ND arrays. Tests: 3 new e2e Node.js tests - nested loop sum (1:3 × 1:2 = 18) - triple nested counter (2×2×2 = 8) - 2×2 matrix multiply via triple nested loop + ND arrays = [19,43,22,50] Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c418921 commit 430539c

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

src/compiler/codegen.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,43 @@ function emit_loop_body!(ctx, buf, code, start_idx, end_idx, loop_headers, backw
337337
while i <= end_idx
338338
stmt = code[i]
339339

340+
# ─── Nested loop header: emit inner while(true) ───
341+
if i in loop_headers && i != loop_header
342+
inner_loop_end = 0
343+
for (src, tgt) in backward_edges
344+
if tgt == i
345+
inner_loop_end = src
346+
break
347+
end
348+
end
349+
if inner_loop_end > 0
350+
# Initialize inner loop phi variables
351+
for phi_idx in sort(collect(keys(phi_info)))
352+
if phi_idx >= i && phi_idx <= inner_loop_end
353+
phi = phi_info[phi_idx]
354+
var_name = ctx.js_locals[phi_idx]
355+
for (k, edge) in enumerate(phi.edges)
356+
if edge < i
357+
if isassigned(phi.values, k)
358+
val = compile_value(ctx, phi.values[k])
359+
print(buf, "$(indent)$(var_name) = $(val);\n")
360+
end
361+
end
362+
end
363+
end
364+
end
365+
print(buf, "$(indent)while (true) {\n")
366+
inner_body_start = i
367+
while inner_body_start <= inner_loop_end && code[inner_body_start] isa Core.PhiNode
368+
inner_body_start += 1
369+
end
370+
emit_loop_body!(ctx, buf, code, inner_body_start, inner_loop_end, loop_headers, backward_edges, forward_gotos, phi_info, depth + 1, i)
371+
print(buf, "$(indent)}\n")
372+
i = inner_loop_end + 1
373+
continue
374+
end
375+
end
376+
340377
if stmt isa Core.GotoIfNot
341378
cond = compile_value(ctx, stmt.cond)
342379
target = stmt.dest

test/runtests.jl

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3551,7 +3551,34 @@ process.stdout.write(JSON.stringify(r));
35513551
end
35523552
@test compile_unopt_and_run(fn, "") == "[19,43,22,50]"
35533553
end
3554-
# Note: nested for loops (for i; for j; for k) have a known JST bug
3555-
# where inner loop `break` exits the outer while loop. Single for loops work.
3556-
# TODO: fix nested for loop compilation in JST
3554+
@testset "ND: nested loop sum" begin
3555+
fn = () -> begin
3556+
total = 0
3557+
for i in 1:3; for j in 1:2; total = total + i * j; end; end
3558+
return total
3559+
end
3560+
@test compile_unopt_and_run(fn, "") == "18"
3561+
end
3562+
@testset "ND: triple nested loop" begin
3563+
fn = () -> begin
3564+
total = 0
3565+
for i in 1:2; for j in 1:2; for k in 1:2; total = total + 1; end; end; end
3566+
return total
3567+
end
3568+
@test compile_unopt_and_run(fn, "") == "8"
3569+
end
3570+
@testset "ND: matmul 2x2 (triple nested)" begin
3571+
fn = () -> begin
3572+
A=zeros(2,2); A[1,1]=1.0; A[1,2]=2.0; A[2,1]=3.0; A[2,2]=4.0
3573+
B=zeros(2,2); B[1,1]=5.0; B[1,2]=6.0; B[2,1]=7.0; B[2,2]=8.0
3574+
C=zeros(2,2)
3575+
for i in 1:2; for j in 1:2; s=0.0
3576+
for k in 1:2; s=s+A[i,k]*B[k,j]; end
3577+
C[i,j]=s
3578+
end; end
3579+
C
3580+
end
3581+
# A*B = [[19,22],[43,50]], column-major flat = [19,43,22,50]
3582+
@test compile_unopt_and_run(fn, "") == "[19,43,22,50]"
3583+
end
35573584
end

0 commit comments

Comments
 (0)