You are Agent A (IR Dump & Analyze). Benchmark the available reference backends, select the reference backend per structural variant, dump CUDA Tile MLIR, and write the structured analysis consumed by later agents. Do not write cutile-rs Rust code.
Output Protocol
Write all detailed evidence to files under
$CUTILE_KERNEL_OUT_ROOT/{kernel_name}/, then return only a literal validator
block and one verdict line.
Required files:
reference/analysis.json- one single-variant structural IR
reference/reference.mlir, or one top-level structural IRreference/reference_{variant}.mlirper multi-variant kernel - optional dtype/proof supplements under
reference/supplements/, never as top-levelreference/reference_{variant}_{dtype}.mlir baseline_perf_cutile.txtwhen cuTile-Python is availablebaseline_perf_triton_tileir.txtwhen Triton-TileIR is availablebaseline_perf.txtcopied from the selected winning reference backend for single-reference compatibilityreports/agent_a.mdreports/agent_logs/agent_a.md
Verdicts:
PASS: all required artifacts exist and all A-stage validators exit 0.FAIL_FIXABLE: the environment worked, but a fixable A-stage artifact problem remains after you tried the repairs in this file.BLOCKED: tools, GPU, imports, or source availability prevent a trustworthy dump or benchmark. WriteSKILL_METHODOLOGY_BLOCKED.txt.
Assume the parent will not call Agent A again for the same kernel. If a validator fails, repair it inside this Agent A run when possible.
Hard Safety Rule
Never emit a Bash command containing the destructive recursive-delete token
spelled as r + m + space + dash-r-f. Clean temp dirs with:
mkdir -p ${TMPDIR:-/tmp}/cutile_bc ${TMPDIR:-/tmp}/cutile_tileir
find ${TMPDIR:-/tmp}/cutile_bc ${TMPDIR:-/tmp}/cutile_tileir -mindepth 1 -delete 2>/dev/null || trueStep 0: First File Context
Your first useful file context should be loaded in one batched read when the tool supports it:
.agents/skills/tilegym-converting-cutile-triton-to-cutile-rs/agents/agent_a.md.agents/skills/tilegym-converting-cutile-triton-to-cutile-rs/references/env-block.md
If the prompt gives different absolute paths, use those. If a Read tool does not
expand $TILEGYM_PATH, do not keep retrying the literal variable path; resolve it
once with Bash and use the absolute path.
Read the op source and tests after the skill files:
$TILEGYM_PATH/src/tilegym/ops/cutile/{kernel_name}.py$TILEGYM_PATH/src/tilegym/ops/triton/{kernel_name}.pywhen it exists$TILEGYM_PATH/tests/ops/test_{kernel_name}.py
Step 1: Read The Op And Tests
Extract:
- public op signature and backend registration name
- exact
@ct.kernelfunctions called by the cuTile backend - launch grid, tile sizes, dtype gates, transpose/layout assumptions, and
ct.Constantparameters - test class and
test_perfparametrization - exact correctness reference function from the test class
- exact tolerances from
assertCorrectness,assertAllClose, or equivalent helpers
Do not invent shapes. The valid benchmark set is the tilegym test_perf set, or
a bounded subset explicitly documented from that set when runtime requires it.
Step 2: Discover Structural Variants
Record every structural variant in analysis.json.
Variant sources:
- Explicit test params such as
static_persistent,use_tma,transpose, or dtype families when they change the kernel body, tile rank/shape, memory API, or schedule. - Shape-dependent wrapper dispatch before
ct.launch. - Multiple
ct.launchcalls or selected kernel functions. ct.Constantbranches inside the@ct.kernelbody that change tile rank, tile shape, memory API, or MMA/reduce structure.
Python JIT eliminates untaken ct.Constant branches, but Rust type-checks both
sides. If branches use different tile types, Agent B needs separate
#[cutile::entry()] functions and FFI dispatch. Record the branch condition and
the triggering test_perf configs.
For each test_perf config, record which structural variant it hits in
config_to_variant.
Structural IRs vs Dtype Supplements
A top-level reference/reference_{variant}.mlir means "Agent B must write an
entry for this structural variant." Do not put proof-only dtype dumps there.
If an extra dump only proves dtype lowering for the same structural variant,
treat it as a supplement. Common example: an f32 dump proves ftof ... -> ...xtf32 casts before mmaf, while f16 uses the same non-persistent structural
body.
For a supplement:
- Create
reference/supplements/. - Write the file there, for example
reference/supplements/reference_non_persistent_f32.mlir. - Add a field on the owning structural variant, for example
"reference_ir_f32_supplement": "reference/supplements/reference_non_persistent_f32.mlir". - Do not add a new
kernel_variants[]entry for the supplement. - Do not point any
config_to_variantentry at the supplement. - Do not create a separate entry function solely for dtype evidence.
Step 3: Benchmark Available Reference Backends
Run bounded test_perf for cuTile-Python and Triton-TileIR when available. Use the
real test class, not a broad module-level selector.
Command shape:
cd "$TILEGYM_PATH"
python -m pytest tests/ops/test_{kernel_name}.py::{TestClass}::test_perf \
-k "<backend and bounded test_perf selector>" \
-sv --print-record --timeout=600 \
| tee "$CUTILE_KERNEL_OUT_ROOT/{kernel_name}/baseline_perf_<backend>.txt"Use CUPTI timing from --print-record. Do not use torch.cuda.Event timings for
reference selection.
Select the lower geomean backend per variant. For a single-variant kernel, copy
the winning log to baseline_perf.txt.
Environment
Inline the environment for every Python/pytest command. Use the values from
references/env-block.md, including:
export PATH=$(dirname "$TILEIRAS_BIN"):$PATH
export PYTHONPATH=$TRITON_TILEIR_PYTHONPATH:$TILEGYM_PATH/src:$PYTHONPATH
export ENABLE_TILE=1 TILEIR_ENABLE_FTZ=1 TILEIR_ENABLE_APPROX=1
export CUPTI=1 WARMUP=100 REP=50 MIN_REP=2
# $CUDA_TOOLKIT_PATH is preflight-verified and inherited; do not re-hardcode it.Record resolved tool paths and GPU name in reports/agent_logs/agent_a.md.
Step 4: Dump CUDA Tile MLIR
Prefer the public two-step cuTile dump path:
mkdir -p ${TMPDIR:-/tmp}/cutile_bc ${TMPDIR:-/tmp}/cutile_tileir
find ${TMPDIR:-/tmp}/cutile_bc ${TMPDIR:-/tmp}/cutile_tileir -mindepth 1 -delete 2>/dev/null || true
CUDA_TILE_DUMP_BYTECODE=${TMPDIR:-/tmp}/cutile_bc <env vars> python - <<'PY'
# import torch, tilegym
# set backend to the selected reference backend
# allocate the selected test_perf shape
# call the public tilegym op exactly as the test does
PY
CUDA_TILE_TRANSLATE_BIN="$(dirname "$CUDA_TILE_OPT_BIN")/cuda-tile-translate"
for bc in ${TMPDIR:-/tmp}/cutile_bc/*.tileirbc; do
out="${TMPDIR:-/tmp}/cutile_tileir/$(basename "${bc%.tileirbc}").cuda_tile.mlir"
"$CUDA_TILE_TRANSLATE_BIN" --cudatilebc-to-mlir "$bc" > "$out"
done
"$CUDA_TILE_OPT_BIN" --canonicalize --cse <dump>.cuda_tile.mlir \
-o "$CUTILE_KERNEL_OUT_ROOT/{kernel_name}/reference/reference.mlir"Structural dumps go to reference/reference.mlir or top-level
reference/reference_{variant}.mlir. Supplement dumps go under
reference/supplements/ as described above.
The copied file must be CUDA Tile MLIR containing cuda_tile.module, not
Python-level Tile IR.
Step 5: Entry-Symbol Contract And Op-Named Alias Repair
validate_ir.sh <ir> {kernel_name} requires an entry symbol containing
{kernel_name}. Most kernels already satisfy this because the source function
name matches the op. Some cuTile ops do not.
If validation fails only because no entry symbol contains {kernel_name}, repair
the artifact in the same Agent A run:
- Confirm the IR is otherwise valid by running
validate_ir.shagainst the real entry prefix. - Re-dump the identical Python kernel under an alias whose name contains the op name and preserves the real source name as a suffix.
- Canonicalize the alias dump exactly like the original dump.
- Normalize only the entry symbol token in both files and diff them. The body must be identical modulo the label.
- Install the alias dump as
reference.mliror the owning top-levelreference_{variant}.mlir. - Save the original as
reference/orig_mangled_entry.mlir.bakor another name that does not match top-levelreference*.mlir.
Generic cuTile alias pattern:
import cuda.tile as ct
import tilegym.ops.cutile.{kernel_name} as M
orig_kernel = M.{real_ct_kernel_function}
pyfunc = orig_kernel._pyfunc
alias_name = "{kernel_name}__{real_ct_kernel_function}"
pyfunc.__name__ = alias_name
pyfunc.__qualname__ = alias_name
aliased_kernel = ct.kernel(pyfunc)Launch aliased_kernel with the exact same args, constants, grid, stream, dtype,
shape, and environment as the original dump.
Then verify:
sed -E 's/@{kernel_name}__[A-Za-z0-9_]+/@ENTRY/; s/@{real_ct_kernel_function}/@ENTRY/' old.mlir > ${TMPDIR:-/tmp}/old_norm.mlir
sed -E 's/@{kernel_name}__[A-Za-z0-9_]+/@ENTRY/; s/@{real_ct_kernel_function}/@ENTRY/' new.mlir > ${TMPDIR:-/tmp}/new_norm.mlir
diff -u ${TMPDIR:-/tmp}/old_norm.mlir ${TMPDIR:-/tmp}/new_norm.mlirIf the body differs, do not install the alias dump.
Step 6: Write analysis.json
Use stable structured fields. Include at least:
{
"kernel_name": "op_name",
"source": "src/tilegym/ops/cutile/op_name.py",
"source_file": "src/tilegym/ops/cutile/op_name.py",
"launch_path": "direct",
"pattern": "single|multi_variant",
"ops_used": [],
"reference_backend": "cutile",
"reference_backend_selection": {
"method": "CUPTI geomean over test_perf configs",
"cutile_geomean_ms": 0.0,
"triton_tileir_geomean_ms": 0.0,
"winner": "cutile"
},
"test_perf_configs": [],
"kernel_variants": [
{
"variant_name": "default",
"variant_type": "single",
"kernel_function": "ct_kernel_function",
"dispatch_condition": "all configs",
"reference_ir": "reference/reference.mlir",
"reference_ir_f32_supplement": null,
"constants": [],
"reference_backend": "cutile",
"autotune_configs": null
}
],
"config_to_variant": {},
"correctness_reference": {
"function": "copy exact test reference",
"source": "tests/ops/test_op.py::TestClass::reference"
},
"correctness_tolerance": {
"f16": {"atol": 0.001, "rtol": 0.001},
"bf16": {"atol": 0.01, "rtol": 0.01},
"f32": {"atol": 0.00001, "rtol": 0.00001}
},
"autotune_backends": [],
"autotune_key": [],
"autotune_configs": null,
"entry_symbol_policy": {
"reference_entry_contains_kernel_name": true,
"alias_used": false,
"real_kernel_function": "ct_kernel_function"
}
}Omit supplement fields or set them to null when no supplement exists. When a
supplement exists, the path must start with reference/supplements/.
Do not put free-form note keys inside correctness_tolerance as if they were
dtype entries. Validators may treat every child of that object as a tolerance
record.
Step 7: Validator Gate - Must Execute, Not Read
This is mandatory. Reading validate_ir.sh, validate_analysis.sh, or
validate_perf_log.sh is not validator execution and will be audited as missing
validators. After writing artifacts and reports, run the validators and paste the
complete command/output block into the final response.
Use this exact command shape:
BASE="${CUTILE_KERNEL_OUT_ROOT}/{kernel_name}"
SK=".agents/skills/tilegym-converting-cutile-triton-to-cutile-rs/scripts"
structural_refs=()
for ref in "$BASE/reference/reference.mlir" "$BASE"/reference/reference_*.mlir; do
[ -f "$ref" ] || continue
structural_refs+=("$ref")
done
set +e
for ref in "${structural_refs[@]}"; do
echo "$ bash $SK/validate_ir.sh $ref {kernel_name} 2>&1"
bash "$SK/validate_ir.sh" "$ref" {kernel_name} 2>&1
echo "exit=$?"
done
echo "$ bash $SK/validate_analysis.sh {kernel_name} 2>&1"
bash "$SK/validate_analysis.sh" {kernel_name} 2>&1
echo "exit=$?"
for log in "$BASE/baseline_perf_cutile.txt" "$BASE/baseline_perf_triton_tileir.txt"; do
[ -s "$log" ] || continue
case "$(basename "$log")" in
baseline_perf_cutile.txt) label=baseline_perf_cutile ;;
baseline_perf_triton_tileir.txt) label=baseline_perf_triton_tileir ;;
esac
echo "$ bash $SK/validate_perf_log.sh $log $label 2>&1"
bash "$SK/validate_perf_log.sh" "$log" "$label" 2>&1
echo "exit=$?"
done
set -eEvery parseable exit=<n> in the final block must be zero for VERDICT: PASS.
If any validator exits non-zero, repair the artifact and rerun this gate. Do not
return PASS from stale or partial validator output.
Supplements are optional dtype evidence. If you validate them, do it separately and do not let them create structural variant obligations.
Common Failure Repairs
Missing cuda_tile.module: copied Python-level IR. Translate bytecode withcuda-tile-translate --cudatilebc-to-mlir.No entry symbol contains kernel name: use the op-named alias repair in this file; do not assume the dump is wrong when the real source kernel name differs from the public op.analysis.json validation failed: remove free-form dtype-like keys, add exact correctness reference/tolerance, ensurekernel_variantsnames matchconfig_to_variant, and keep dtype supplements underreference/supplements/.- f32/tf32 proof dump treated as a variant: move it to
reference/supplements/, cite it from the owning variant withreference_ir_f32_supplement, and remove anyconfig_to_varianttarget for it. - perf-log validator missing records: rerun the bounded
test_perfcommand with--print-recordand copy the full log. - Triton-TileIR unavailable: document the import/tool failure and use cuTile-Python as the reference if it benchmarked successfully.
Step 8: Reports
reports/agent_a.md should include:
- source files read
- test class and selected
test_perfconfigs - backend benchmark commands and geomean selection
- all structural variants and dispatch conditions
- supplement IRs and owning variants, if any
- dump command, bytecode file(s), translated MLIR file(s)
- entry symbol(s), including alias proof if used
- analysis.json summary
- validator output
reports/agent_logs/agent_a.md should include command-oriented notes: env,
commands, paths, line counts, validator outputs, and any bounded skips.
Final Response Contract
End your response exactly like this, with no narrative after the verdict:
<VALIDATOR_OUTPUT>
$ bash ...validate_ir.sh ...
...
exit=0
$ bash ...validate_analysis.sh ...
...
exit=0
$ bash ...validate_perf_log.sh ...
...
exit=0
</VALIDATOR_OUTPUT>
VERDICT: PASS