xpu-port

SkillDev tools

Execute a single-target CUDA-to-XPU port of a PyTorch repo with libcst-based scan, mechanical rewrite, and CPU FP64 vs target-dtype correctness verify on one forward pass. Use when the request says "port", "port my repo to XPU", "port my repo at <path> to XPU", "rewrite the CUDA calls to XPU", "apply the mechanical transforms", "run the scan and rewrite", "make the port changes now". Not for the "migrate" verb ("migrate my repo", "migrate this repo to XPU") or a bare whole-repo workflow request where scope is not yet set, those start with cuda-to-xpu-migration, whose plan routes here. Not for assessment-only, throughput (torch-xpu-bench), op-level slowness (torch-xpu-profile), custom CUDA C++ extensions, or dual-target CUDA+XPU codebases.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the xpu-port skill

What this skill tells your AI

The instructions your AI receives, as published by intel/skills in skills/xpu-port/SKILL.md and read by ahel’s review.

Three deterministic scripts; the agent drives the loop.

scripts/
├── xpu_port_scan.py     # libcst, classifies sites mechanical/semantic/escalate
├── xpu_port_rewrite.py  # libcst, named transforms, --check previews diff
└── xpu_port_verify.py   # CPU FP64 reference vs target dtype, one forward
pip install libcst

Targets

Single-target XPU only. After the port, the codebase runs on Intel XPU. CUDA call sites are rewritten, not dual-gated. If the user wants both CUDA and XPU at runtime (dual-target), stop and say so — that's a different problem (per-call-site guards, device-aware factories) and this skill produces broken half-ported code if used for it.

Backstop — wrong entry point

This skill executes; it does not assess or route. One redirect exists, pointing one way. If the request is really an assessment or a whole-repo migration ("migrate this repo", "what would it take to run on XPU" — scope not yet set), hand it to cuda-to-xpu-migration and stop; its report's Next steps lead back here for the Python surface.

The same backstop covers mis-scoped ports. This skill reads Python source only — its gates say nothing about Dockerfiles, launch scripts, or dependency pins. When no migration report is in hand and the step-1 scan comes back empty while its advisory flags NVIDIA/CUDA infrastructure surfaces, the port was mis-scoped: hand it to cuda-to-xpu-migration and stop. When a migration report is in hand, execute the Python surface it scoped and list its non-Python surfaces as documented-not-rewritten — an advisory on the final scan does not reopen this gate (see step 5).

Procedure

The port's minimum gate is final scan empty + verifier green. Both are necessary; neither alone is sufficient (for service repos with no single forward, the serving skill's smoke test stands in for the verifier — see step 6).

Important: the verifier confirms one forward pass is numerically correct — it does not confirm the training loop runs end-to-end (e.g. torch.compile, custom LR schedulers, or data-loader workers may still fail on XPU). After the gates pass, run at least 2 real training iterations on the target device before declaring the port complete.

1. Scan

python3 path/to/xpu_port_scan.py . > findings.json

Findings tagged mechanical (safe to rewrite), semantic (agent edits per references/semantic-patterns.md), escalate (custom .cu, CUDAExtension, CUDA-only attrs — agent decides per finding). Each finding also carries a route field: a skill name (e.g. torch-xpu-profile) names the skill an orchestrating agent should chain to for that site; null means there is no single downstream skill — either xpu-port resolves the site in-loop (a mechanical transform or a semantic hand-edit) or the site is escalate (redesign, no mechanical port). Read bucket first: null does not by itself mean "already handled".

Empty scan on an NVIDIA-stacked repo: if the scan found no real CUDA sites (synthetic unreadable/unparseable bookkeeping findings don't count) but the JSON contains an advisory block (also printed to stderr; emitted on directory scans only — a single-file scan never carries one), the repo has NVIDIA/CUDA surface xpu-port cannot rewrite — container files, dependency pins, launch scripts, or distinctive cloud NIM/API endpoints (e.g. integrate.api.nvidia.com) in source. That is an API-first migration, not a torch.cuda port. Stop and route to cuda-to-xpu-migration (assessment) then xpu-deploy-plan (serving plan). That is the no-plan case: with a migration report already in hand, don't bounce back — follow the report's routes for the non-Python surfaces instead (see Backstop). A bare 0 with no advisory is a hint, not a proof of "nothing to migrate": the check is bounded (distinctive markers, capped file read), so for anything beyond a pure PyTorch repo, confirm with cuda-to-xpu-migration's full inventory scan.

2. Mechanical pass

for t in device_string cuda_to_xpu dot_cuda imports dist_backend amp_autocast amp_gradscaler; do
    python3 path/to/xpu_port_rewrite.py --transform "$t" --path .
done

--check previews the diff without writing. Read references/cuda_to_xpu_whitelist.md before running the mechanical pass to understand what cuda_to_xpu rewrites and why CUDA-only attrs are flagged instead.

3. Re-scan (gate)

python3 path/to/xpu_port_scan.py . > findings.after_mechanical.json

Mechanical bucket must be 0. If not, a transform missed something — don't proceed until resolved.

4. Semantic + escalate edits

Read references/semantic-patterns.md before editing any semantic finding. Hand-edit each semantic finding per that reference. For each escalate: replace, document, or stop. Don't invent fixes.

Common semantics on real repos:

  • TF32 toggles → delete
  • device_type == 'cuda' runtime gates → flip to 'xpu'
  • flash-attn / bitsandbytes hard imports → replace with XPU-aware fallback

Full table: references/semantic-patterns.md.

5. Final scan (gate)

python3 path/to/xpu_port_scan.py . > findings.final.json

Empty = done with edits. Anything left = a missed semantic edit or an escalation that needs a recorded decision.

An advisory block on this final scan is not a failed gate: it lists the infra surfaces (Dockerfiles, dependency pins, launch scripts) the Python port intentionally left untouched. It does not reopen the Python gate. If no migration report is in hand, route to cuda-to-xpu-migration; if a report is already in hand, record those surfaces as outside the Python port and continue with the report's routes.

6. Verify

# Write a builder file (avoids shell-quoting issues):
cat > verify_builder.py << 'EOF'
import sys, torch
sys.path.insert(0, ".")
from model import GPT, GPTConfig
m = GPT(GPTConfig(n_layer=2, n_head=2, n_embd=64,
                  block_size=64, vocab_size=128))
x = {"idx": torch.randint(0, 128, (1, 16))}
out = (m, x)
EOF

python3 path/to/xpu_port_verify.py \
    --builder-file verify_builder.py \
    --target-dtype bfloat16 \
    > verify.json

--builder (inline string) also works but is fragile with shell quoting for multi-line builders. Prefer --builder-file for anything beyond a one-liner.

Reference is CPU FP64; target is XPU at the chosen dtype. PYTORCH_ENABLE_XPU_FALLBACK=0 is set unconditionally at import time so silent CPU fallback always raises. Without --no-xpu, XPU must be present or the script exits with an error. --no-xpu runs CPU-vs-FP64 — useful on hosts without an Intel GPU but not a substitute for the target box.

JSON on stdout; builder prints redirected to stderr.

Target dtypertolatol
float321e-51e-6
bfloat161e-21e-3
float165e-31e-3

--rtol / --atol to override. A failing verify means either the port is genuinely off or the model isn't deterministic at this scale (dropout left on, RNG not seeded). Re-run with --seed 0.

No single forward to verify? Service / microservice repos (an HTTP API, a Celery worker, a multi-process app) often have no one nn.Module forward pass to build. The FP64 verifier is N/A there — skip it and confirm correctness with the runtime smoke test from the serving skill (vllm-xpu-run / sglang-xpu-run / torch-xpu-run) instead. The final-scan gate still applies.

7. Profile (optional)

After correctness passes, if the port is slow, invoke torch-xpu-profile to find the hot op. Common substitutions in references/semantic-patterns.md "After-profile" section.

Distributed backend

xccl, not ccl (the legacy torch_ccl plugin). The dist_backend transform rewrites init_process_group(backend="nccl") → "xccl" and bare backend = "nccl" assignments.

What this skill does NOT cover

  • Assessment-only / migration plan → cuda-to-xpu-migration
  • CUDA → XPU rule reference → torch-xpu-run
  • Throughput / TTFT / TPOT → torch-xpu-bench
  • Op-level slowness → torch-xpu-profile, xpu-profile-unitrace
  • Container / driver setup → xpu-container-run, xpu-discover
  • Custom CUDA C++ extensions, dual-target codebases (out of scope; see "Targets")

References

Signals

GitHub stars
21
Forks
9
Last commit
Sep 2026

ahel review

  • K1binfo
    installs-packages

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Item type
skill
Key
xpu-port
Source
github.com/intel/skills