All skills
nvidia avatar

/tilegym-converting-cutile-triton-to-cutile-rs

@48a26cf
by NVIDIA Corporationnvidia/tilegym821 stars
89

Use this skill to convert, port, or translate Triton-TileIR or cuTile-Python GPU kernels to cutile-rs (Rust). The orchestrator runs scripts/preflight.sh, then drives a bounded Agent A -> B -> D -> E pipeline (Agent C is diagnostic, Agent F optional), delegating all kernel/host/correctness/perf work to sub-agents and routing by each stage's single-line VERDICT.

  • 44 files
  • 452.9 KB
  • CC-BY-4
  • Updated 2 months ago
  • GitHub

Use this Skill: https://skilld.dev/gh/nvidia/tilegym/tilegym-converting-cutile-triton-to-cutile-rs

This session only. Nothing lands on disk.

agentsagent_a.md

≈3.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

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 IR reference/reference_{variant}.mlir per multi-variant kernel
  • optional dtype/proof supplements under reference/supplements/, never as top-level reference/reference_{variant}_{dtype}.mlir
  • baseline_perf_cutile.txt when cuTile-Python is available
  • baseline_perf_triton_tileir.txt when Triton-TileIR is available
  • baseline_perf.txt copied from the selected winning reference backend for single-reference compatibility
  • reports/agent_a.md
  • reports/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. Write SKILL_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 || true

Step 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}.py when 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.kernel functions called by the cuTile backend
  • launch grid, tile sizes, dtype gates, transpose/layout assumptions, and ct.Constant parameters
  • test class and test_perf parametrization
  • 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:

  1. 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.
  2. Shape-dependent wrapper dispatch before ct.launch.
  3. Multiple ct.launch calls or selected kernel functions.
  4. ct.Constant branches inside the @ct.kernel body 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:

  1. Create reference/supplements/.
  2. Write the file there, for example reference/supplements/reference_non_persistent_f32.mlir.
  3. Add a field on the owning structural variant, for example "reference_ir_f32_supplement": "reference/supplements/reference_non_persistent_f32.mlir".
  4. Do not add a new kernel_variants[] entry for the supplement.
  5. Do not point any config_to_variant entry at the supplement.
  6. 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:

  1. Confirm the IR is otherwise valid by running validate_ir.sh against the real entry prefix.
  2. Re-dump the identical Python kernel under an alias whose name contains the op name and preserves the real source name as a suffix.
  3. Canonicalize the alias dump exactly like the original dump.
  4. Normalize only the entry symbol token in both files and diff them. The body must be identical modulo the label.
  5. Install the alias dump as reference.mlir or the owning top-level reference_{variant}.mlir.
  6. Save the original as reference/orig_mangled_entry.mlir.bak or another name that does not match top-level reference*.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.mlir

If 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 -e

Every 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 with cuda-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, ensure kernel_variants names match config_to_variant, and keep dtype supplements under reference/supplements/.
  • f32/tf32 proof dump treated as a variant: move it to reference/supplements/, cite it from the owning variant with reference_ir_f32_supplement, and remove any config_to_variant target for it.
  • perf-log validator missing records: rerun the bounded test_perf command with --print-record and 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_perf configs
  • 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

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at 48a26cf. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 4 days ago.

Activeupdated 2 months ago
metadata
{
  "author": "TileGym Team <TileGym@nvidia.com>"
}

README badge

README badge for nvidia/tilegym/tilegym-converting-cutile-triton-to-cutile-rs