Skip to content

Commit e431927

Browse files
maleadtclaude
andauthored
Aggregate codegen errors and lower Julia throws (#248)
This changes codegen to collect independent errors instead of stopping at the first one. Failed values are replaced with poison values so compilation can continue, while follow-on errors caused by those values are suppressed. The final error reports the useful root causes in source order. It also handles Julia throw calls directly. Throws that are unavoidable become compile-time diagnostics, while throws in conditional control flow become runtime Tile IR assertions. Static exception messages are preserved when possible, and exception objects are removed before Tile IR codegen. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent b3b9790 commit e431927

15 files changed

Lines changed: 677 additions & 30 deletions

File tree

src/bytecode/types.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,12 @@ F8E4M3FN(table::TypeTable) = simple_type!(table, SimpleType.F8E4M3FN)
148148
F8E5M2(table::TypeTable) = simple_type!(table, SimpleType.F8E5M2)
149149
function F8E8M0FNU(table::TypeTable)
150150
table.version >= v"13.2" ||
151-
throw(ArgumentError("Float8_E8M0FNU requires Tile IR bytecode v13.2+, got v$(table.version)"))
151+
throw(IRError("Float8_E8M0FNU requires Tile IR bytecode v13.2+, got v$(table.version)"))
152152
simple_type!(table, SimpleType.F8E8M0FNU)
153153
end
154154
function F4E2M1FN(table::TypeTable)
155155
table.version >= v"13.3" ||
156-
throw(ArgumentError("Float4_E2M1FN requires Tile IR bytecode v13.3+, got v$(table.version)"))
156+
throw(IRError("Float4_E2M1FN requires Tile IR bytecode v13.3+, got v$(table.version)"))
157157
simple_type!(table, SimpleType.F4E2M1FN)
158158
end
159159
Token(table::TypeTable) = simple_type!(table, SimpleType.Token)
@@ -242,7 +242,7 @@ function julia_to_tile_dtype!(table::TypeTable, ::Type{T}) where T
242242
elem_dtype = lookup_dtype!(table, eltype(T))
243243
pointer_type!(table, elem_dtype)
244244
else
245-
error("Unsupported Julia type for Tile IR: $T")
245+
throw(IRError("Unsupported Julia type for Tile IR: $T"))
246246
end
247247
end
248248

src/compiler/codegen/control_flow.jl

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,44 @@ function emit_block!(ctx::CGCtx, block::Block; skip_terminator::Bool=false)
2323
ctx.debug_emitter, ctx.sci, inst.ssa_idx; linkage_name=ln)
2424
end
2525
s = inst[:stmt]
26-
if s isa ControlFlowOp
27-
emit_control_flow_op!(ctx, s, value_type(inst), inst.ssa_idx)
28-
else
29-
emit_statement!(ctx, s, inst.ssa_idx, value_type(inst))
26+
# Per-statement diagnostic boundary: an `IRError` from anywhere in
27+
# this statement's emission is recorded (with its kernel-side stack)
28+
# and the result poisoned, so emission continues and one compile can
29+
# report every problem. See compiler/codegen/errors.jl.
30+
ctx.current_ssa_idx = inst.ssa_idx
31+
ctx.touched_poison = false
32+
try
33+
if s isa ControlFlowOp
34+
emit_control_flow_op!(ctx, s, value_type(inst), inst.ssa_idx)
35+
else
36+
emit_statement!(ctx, s, inst.ssa_idx, value_type(inst))
37+
end
38+
# A successful statement that read a poisoned input still
39+
# yields a poison-derived value: taint it so failures further
40+
# down the dataflow chain are suppressed as cascades even when
41+
# intermediate statements emit fine.
42+
if ctx.touched_poison || reads_poison(ctx, s)
43+
push!(ctx.poisoned, inst.ssa_idx)
44+
end
45+
catch e
46+
if !(e isa IRError)
47+
# Anything else escaping emission is a compiler bug, not a
48+
# kernel diagnostic: abort immediately (the context may be
49+
# inconsistent), but point the report at the offending
50+
# kernel statement and ask for an issue.
51+
e isa Union{InternalCompilerError, InterruptException} && rethrow()
52+
throw(InternalCompilerError(source_location(ctx.sci, inst.ssa_idx),
53+
CapturedException(e, catch_backtrace())))
54+
end
55+
ctx.current_ssa_idx = inst.ssa_idx
56+
# Suppress errors derived purely from an already-poisoned input;
57+
# keep only root causes. The static operand check backs up the
58+
# dynamic flag for emitters that throw before reading arguments.
59+
if !(ctx.touched_poison || reads_poison(ctx, s))
60+
record_error!(ctx, e.msg)
61+
end
62+
push!(ctx.poisoned, inst.ssa_idx)
63+
ctx.values[inst.ssa_idx] = poison_value(ctx)
3064
end
3165
end
3266
if !skip_terminator && terminator(block) !== nothing
@@ -62,17 +96,24 @@ function emit_if_op!(ctx::CGCtx, op::IfOp, @nospecialize(parent_result_type), ss
6296
cond_tv === nothing && throw(IRError("Cannot resolve condition for IfOp"))
6397

6498
# Determine result types from parent_result_type (token_order_pass! already
65-
# updated the type to include any token carries via inst[:type] = …)
99+
# updated the type to include any token carries via inst[:type] = …).
100+
#
101+
# A non-representable result (typically `Any`/`Union{}` left when inference
102+
# could not pin a branch's tile down) is recorded but does NOT abort: we
103+
# substitute a poison type and still emit both regions, so the root cause
104+
# inside the branch (e.g. a non-constant tile shape) surfaces too. The IfOp
105+
# result is then marked poison to suppress the cascade at its consumers,
106+
# and the carry diagnostic itself is dropped again if the regions surface
107+
# errors of their own (prune_derived_errors! below).
66108
result_types = TypeId[]
109+
result_poisoned = false
110+
carry_mark = length(ctx.errors)
67111
if parent_result_type !== Nothing
68-
if parent_result_type <: Tuple
69-
for T in parent_result_type.parameters
70-
push!(result_types, tile_type_for_julia!(ctx, T))
71-
end
72-
else
73-
push!(result_types, tile_type_for_julia!(ctx, parent_result_type))
74-
end
112+
Ts = parent_result_type <: Tuple ? collect(parent_result_type.parameters) :
113+
Any[parent_result_type]
114+
result_types, result_poisoned = collect_result_types!(ctx, Ts; context="`if`/`else` result")
75115
end
116+
carry_errors = carry_mark+1:length(ctx.errors)
76117

77118
then_body = function(_)
78119
saved = copy(ctx.block_args)
@@ -87,8 +128,10 @@ function emit_if_op!(ctx::CGCtx, op::IfOp, @nospecialize(parent_result_type), ss
87128
empty!(ctx.block_args); merge!(ctx.block_args, saved)
88129
end
89130
results = encode_IfOp!(then_body, else_body, cb, result_types, cond_tv.v)
131+
prune_derived_errors!(ctx, carry_errors)
90132

91133
ctx.values[ssa_idx] = CGVal(results, parent_result_type)
134+
result_poisoned && push!(ctx.poisoned, ssa_idx)
92135
end
93136

94137
#=============================================================================
@@ -120,9 +163,14 @@ function emit_for_op!(ctx::CGCtx, op::ForOp, @nospecialize(parent_result_type),
120163
push!(init_values, tv.v)
121164
end
122165

123-
# Build result types uniformly from block args
166+
# Build result types uniformly from block args. A non-representable carry
167+
# (typically `Any`/`Union{}`) is recorded but does not abort: poison it and
168+
# still emit the body so the root cause inside the loop surfaces too.
124169
n_carries = length(body_blk.args)
125-
result_types = TypeId[tile_type_for_julia!(ctx, arg.type) for arg in body_blk.args]
170+
carry_mark = length(ctx.errors)
171+
result_types, result_poisoned =
172+
collect_result_types!(ctx, (arg.type for arg in body_blk.args); context="loop carry")
173+
carry_errors = carry_mark+1:length(ctx.errors)
126174

127175
body_builder = function(block_args)
128176
saved = copy(ctx.block_args)
@@ -144,8 +192,10 @@ function emit_for_op!(ctx::CGCtx, op::ForOp, @nospecialize(parent_result_type),
144192
end
145193
results = encode_ForOp!(body_builder, cb, result_types, iv_type,
146194
lower_tv.v, upper_tv.v, step_tv.v, init_values)
195+
prune_derived_errors!(ctx, carry_errors)
147196

148197
ctx.values[ssa_idx] = CGVal(results, parent_result_type)
198+
result_poisoned && push!(ctx.poisoned, ssa_idx)
149199
end
150200

151201
#=============================================================================
@@ -164,7 +214,10 @@ function emit_loop_op!(ctx::CGCtx, op::LoopOp, @nospecialize(parent_result_type)
164214
end
165215

166216
n_carries = length(body_blk.args)
167-
result_types = TypeId[tile_type_for_julia!(ctx, arg.type) for arg in body_blk.args]
217+
carry_mark = length(ctx.errors)
218+
result_types, result_poisoned =
219+
collect_result_types!(ctx, (arg.type for arg in body_blk.args); context="loop carry")
220+
carry_errors = carry_mark+1:length(ctx.errors)
168221

169222
body_builder = function(block_args)
170223
saved = copy(ctx.block_args)
@@ -186,8 +239,10 @@ function emit_loop_op!(ctx::CGCtx, op::LoopOp, @nospecialize(parent_result_type)
186239
empty!(ctx.block_args); merge!(ctx.block_args, saved)
187240
end
188241
results = encode_LoopOp!(body_builder, cb, result_types, init_values)
242+
prune_derived_errors!(ctx, carry_errors)
189243

190244
ctx.values[ssa_idx] = CGVal(results, parent_result_type)
245+
result_poisoned && push!(ctx.poisoned, ssa_idx)
191246
end
192247

193248
#=============================================================================
@@ -212,7 +267,10 @@ function emit_while_op!(ctx::CGCtx, op::WhileOp, @nospecialize(parent_result_typ
212267
end
213268

214269
n_carries = length(before_blk.args)
215-
result_types = TypeId[tile_type_for_julia!(ctx, arg.type) for arg in before_blk.args]
270+
carry_mark = length(ctx.errors)
271+
result_types, result_poisoned =
272+
collect_result_types!(ctx, (arg.type for arg in before_blk.args); context="loop carry")
273+
carry_errors = carry_mark+1:length(ctx.errors)
216274

217275
body_builder = function(block_args)
218276
saved = copy(ctx.block_args)
@@ -299,8 +357,10 @@ function emit_while_op!(ctx::CGCtx, op::WhileOp, @nospecialize(parent_result_typ
299357
empty!(ctx.block_args); merge!(ctx.block_args, saved)
300358
end
301359
results = encode_LoopOp!(body_builder, cb, result_types, init_values)
360+
prune_derived_errors!(ctx, carry_errors)
302361

303362
ctx.values[ssa_idx] = CGVal(results, parent_result_type)
363+
result_poisoned && push!(ctx.poisoned, ssa_idx)
304364
end
305365

306366
#=============================================================================

0 commit comments

Comments
 (0)