In a module with more than one func.func, the parser bounds each function by a span that runs to
the end of the module rather than the end of that function. One root cause, four symptoms —
the loud one is that every function is parsed with the last function's body.
Repro
Plain arith.constant bodies are enough — no ktdp ops, memory spaces or pointer arithmetic.
from ktir_cpu.parser import KTIRParser
fns = KTIRParser().parse_module("""
module {
func.func @f0(%a: index) {
%0 = arith.constant 0 : index
return
}
func.func @f1(%a: index) attributes {grid = [32]} {
%0 = arith.constant 1 : index
%1 = arith.constant 11 : index
return
}
}
""").functions
for n in ("f0", "f1"):
print(n, [o.attributes.get("value") for o in fns[n].operations
if o.op_type == "arith.constant"], fns[n].grid)
# actual: f0 [1, 11] (32, 1, 1) <- f1's body, and f1's grid
# f1 [1, 11] (32, 1, 1)
# expected: f0 [0] (1, 1, 1) <- @f0 declares no grid, so it should default
# f1 [1, 11] (32, 1, 1)
Each IRFunction is a distinct object with the correct name — only the body and the grid come
from the wrong function.
Cause
_extract_brace_body (parser.py:149) is documented as "Extract the last top-level
brace-balanced block after start" and terminates on the enclosing }. That is correct for a
single-function module — it skips attributes { ... } and the last remaining block is the body.
With several functions the scan runs past every later function to the module's closing brace.
func_header = mlir_text[match.start():body_end] (parser.py:121) inherits the same over-long
span, so the grid regex searches the whole module.
Symptoms
| # |
symptom |
consequence |
visibility |
| 1 |
every function gets the last function's body |
the wrong program runs |
loud |
| 2 |
tensor_sizes cross-contaminated (ir_types.py:558 walks that same body) |
inferred parameter table is the last function's |
loud |
| 3 |
grid leaks across functions: a function with no grid attribute inherits a later one's |
runs on the wrong core count, still produces numbers |
silent |
| 4 |
use_counts (parser.py:135) built from the wrong body |
LX charge/release windows don't match the executing function |
silent |
Symptom 3 also constrains the fix: the function span is what needs bounding, not just body
extraction — taking the first brace-balanced block after the header that isn't
attributes { ... } fixes symptom 1 and leaves symptom 3 in place.
Relation to #151
That issue reports the same over-long func_header and concludes it is functionally harmless
because _parse_grid_attribute just scans for grid[...]. That holds for a single-function
module; in a multi-function module the scan reaches a later function's grid — symptom 3.
Relation to #187
Subsumed by it: a grammar-based parser takes each body from the AST rather than a brace heuristic.
This is filed as a concrete case with a repro, usable as a regression test for whichever
replacement lands — not as an argument for point-fixing instead.
Scope
- Seen on the default regex
KTIRParser. MLIRFrontendParser looks unaffected by inspection (it
takes each body from func_op.regions[0].blocks[0]) but was not exercised — mlir_ktdp /
tools_ktdp bindings unavailable locally.
- No multi-function module exists under
examples/ or tests/, consistent with this not having
been hit.
- Workaroundable by splitting a module into single-function modules before loading, so this is not
blocking. Symptom 3 has no such workaround for anyone emitting multi-function modules without a
grid on every function.
docs/gap_analysis.md row 36 reads "The parser can find func.func inside a module { ... }
wrapper" — true for finding them, but the bodies are wrong. Worth correcting alongside the fix.
In a module with more than one
func.func, the parser bounds each function by a span that runs tothe end of the module rather than the end of that function. One root cause, four symptoms —
the loud one is that every function is parsed with the last function's body.
Repro
Plain
arith.constantbodies are enough — noktdpops, memory spaces or pointer arithmetic.Each
IRFunctionis a distinct object with the correct name — only the body and the grid comefrom the wrong function.
Cause
_extract_brace_body(parser.py:149) is documented as "Extract the last top-levelbrace-balanced block after start" and terminates on the enclosing
}. That is correct for asingle-function module — it skips
attributes { ... }and the last remaining block is the body.With several functions the scan runs past every later function to the module's closing brace.
func_header = mlir_text[match.start():body_end](parser.py:121) inherits the same over-longspan, so the
gridregex searches the whole module.Symptoms
tensor_sizescross-contaminated (ir_types.py:558walks that same body)gridleaks across functions: a function with nogridattribute inherits a later one'suse_counts(parser.py:135) built from the wrong bodySymptom 3 also constrains the fix: the function span is what needs bounding, not just body
extraction — taking the first brace-balanced block after the header that isn't
attributes { ... }fixes symptom 1 and leaves symptom 3 in place.Relation to #151
That issue reports the same over-long
func_headerand concludes it is functionally harmlessbecause
_parse_grid_attributejust scans forgrid[...]. That holds for a single-functionmodule; in a multi-function module the scan reaches a later function's
grid— symptom 3.Relation to #187
Subsumed by it: a grammar-based parser takes each body from the AST rather than a brace heuristic.
This is filed as a concrete case with a repro, usable as a regression test for whichever
replacement lands — not as an argument for point-fixing instead.
Scope
KTIRParser.MLIRFrontendParserlooks unaffected by inspection (ittakes each body from
func_op.regions[0].blocks[0]) but was not exercised —mlir_ktdp/tools_ktdpbindings unavailable locally.examples/ortests/, consistent with this not havingbeen hit.
blocking. Symptom 3 has no such workaround for anyone emitting multi-function modules without a
gridon every function.docs/gap_analysis.mdrow 36 reads "The parser can findfunc.funcinside amodule { ... }wrapper" — true for finding them, but the bodies are wrong. Worth correcting alongside the fix.