Skip to content

Commit f7dcc77

Browse files
Merge pull request #1156 from intel/sync_msft_22062026
Sync with Microsoft ONNX Runtime - 22062026
2 parents 01baa40 + a5c4814 commit f7dcc77

172 files changed

Lines changed: 4848 additions & 1271406 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/cuda-attention-kernel-patterns/SKILL.md

Lines changed: 171 additions & 24 deletions
Large diffs are not rendered by default.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
---
2+
name: cuda-cutlass-fmha-incremental-rebuild
3+
description: >
4+
Use when rebuilding ONNX Runtime CUDA after editing CUTLASS fused-MHA headers
5+
(onnxruntime/contrib_ops/cuda/bert/cutlass_fmha/*.h such as kernel_forward.h or
6+
fmha_launch_template.h), or when a header edit "passed" an incremental build but
7+
test behavior did not change. Explains the nvcc depfile gotcha that produces stale
8+
Memory-Efficient-Attention (MEA) kernels and binaries, and how to force a correct
9+
recompile. Also covers disk-space frugality on shared GPU dev boxes.
10+
---
11+
12+
# Incremental rebuilds silently use STALE CUTLASS fused-MHA kernels
13+
14+
> The **general** false-green principles (stale binary, wrong-artifact mtime) are summarised
15+
> in the `ort-test` skill's "False-green taxonomy". This skill is the CUDA/CUTLASS-specific
16+
> detail.
17+
18+
## The gotcha (verification-integrity bug)
19+
20+
`nvcc`-generated depfiles do **not** track the CUTLASS fused-MHA headers under
21+
`onnxruntime/contrib_ops/cuda/bert/cutlass_fmha/` (e.g. `kernel_forward.h`,
22+
`fmha_launch_template.h`). These headers are `#include`d by the `fmha_sm*.cu`
23+
translation units, but the build system does not record that dependency.
24+
25+
Consequence: after you edit one of those headers, an **incremental** `build.sh`:
26+
27+
- does **not** recompile `fmha_sm*.cu`,
28+
- reports `[100%] Built target ...` and exits 0,
29+
- leaves the recompiled artifacts — the `fmha_sm*.cu.o` objects and the
30+
`libonnxruntime_providers_cuda.so` they link into — **unchanged** (same `mtime` as
31+
the pre-edit build).
32+
33+
(Do **not** use the gtest test-exe mtime as the stale symptom: in the shared-provider
34+
build the exe `dlopen`s the `.so` and is **not** relinked, so its mtime stays old even
35+
after a *correct* rebuild — see "How to confirm" below. The reliable diagnostic signal
36+
is the `fmha_sm*.cu.o` / `.so` mtime.)
37+
38+
So your "successful" rebuild is running the **old** kernel. Tests that should now
39+
pass (or fail) reflect the previous code, not your edit. This silently invalidates
40+
any FAIL→PASS / PASS→FAIL verification.
41+
42+
## The fix — force recompile the .cu units
43+
44+
Before rebuilding after editing any `cutlass_fmha/*.h` header:
45+
46+
```bash
47+
touch onnxruntime/contrib_ops/cuda/bert/cutlass_fmha/*.cu
48+
```
49+
50+
Then run the normal build command. This forces the `fmha_sm*.cu` translation units
51+
(and downstream binaries) to recompile against your header change.
52+
53+
## How to confirm the rebuild was real (don't trust "[100%] Built")
54+
55+
Confirm that the artifact which actually **links** the recompiled `fmha_sm*.cu.o`
56+
is newer than your header edit.
57+
58+
⚠️ **Do NOT just check the test EXE mtime — it can falsely flag a good build as
59+
stale.** In the shared-provider build configuration (the default here), the CUDA
60+
execution provider is a **shared module**: the recompiled `fmha_sm*.cu.o` link into
61+
`libonnxruntime_providers_cuda.so`, and the `onnxruntime_provider_test` executable
62+
**dlopens** that `.so` — it is **not relinked**. So after a *correct* rebuild the
63+
test exe `mtime` stays **old** while the `.so` advances. Checking the exe alone
64+
would wrongly conclude the build was stale.
65+
66+
Check the right artifact for your link mode:
67+
68+
- **Shared-provider build (default):** the `.so` that links the recompiled `.o`
69+
`build/<dir>/<cfg>/libonnxruntime_providers_cuda.so`
70+
- **Statically-linked provider:** the test exe itself (`onnxruntime_provider_test`)
71+
72+
Safest check — `stat` both the recompiled object and the `.so`, and confirm BOTH
73+
are newer than the header edit:
74+
75+
```bash
76+
stat -c '%y %n' onnxruntime/contrib_ops/cuda/bert/cutlass_fmha/kernel_forward.h
77+
# in your build dir, e.g. build/Debug_quickbuild/Debug/:
78+
stat -c '%y %n' libonnxruntime_providers_cuda.so
79+
# and the actual recompiled object (path varies by build dir):
80+
find . -name 'fmha_sm80.cu.o' -exec stat -c '%y %n' {} +
81+
```
82+
83+
If the `.so` (and the `fmha_sm*.cu.o`) timestamps are older than (or equal to) the
84+
header edit, the build was stale — `touch` the `.cu` files and rebuild. The most
85+
reliable signal of all is behavioral: a test that was failing now passes (a stale
86+
binary cannot flip its result).
87+
88+
## Related: pick the right test binary
89+
90+
This is the **CUDA/CUTLASS instance of false-green mode 1** (zero-match / wrong binary) —
91+
see the `ort-test` skill's "False-green taxonomy" for the general principle. In short:
92+
attention/MEA/Flash boundary gtests (e.g. `FlashStructuralEmptyRows*`,
93+
`Attention_Causal_NonPadKVSeqLen_MEA_*`) live in **`onnxruntime_provider_test`**, which CI
94+
runs; `onnxruntime_test_all` does not contain them and gives a false green. Verify the
95+
MEA/Flash boundary fix against `onnxruntime_provider_test`.
96+
97+
## Related: disk frugality on shared GPU dev boxes
98+
99+
Full ORT CUDA builds are large (test binaries ~1 GB each; a build dir can reach
100+
tens of GB). On a shared box, `/home` filling to 100% makes builds fail in
101+
non-obvious places — e.g. `git submodule sync` reporting `No space left on device`
102+
or a `config.lock` error, not an obvious "disk full" at the compile step.
103+
104+
Before a big rebuild, check free space and clean only clearly-stale, regenerable
105+
build directories (old dated experiment dirs). Never delete another agent's active
106+
build dir or anything ambiguous:
107+
108+
```bash
109+
df -h /home
110+
du -sh build/* | sort -h
111+
```

.agents/skills/ort-build/SKILL.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ You do **not** need `--update` when only modifying existing `.cc`/`.h` files —
6666
| `--use_cuda` | Enable CUDA EP. Requires `--cuda_home`/`--cudnn_home` or `CUDA_HOME`/`CUDNN_HOME` env vars. On Windows, only `cuda_home`/`CUDA_HOME` is validated. |
6767
| `--target T` | Build a specific CMake target (requires `--build`; e.g., `onnxruntime_common`, `onnxruntime_test_all`) |
6868
| `--use_webgpu` | Enable WebGPU EP. To run its tests locally on Linux without a GPU, see the `webgpu-local-testing` skill. |
69+
| `--cmake_extra_defines onnxruntime_QUICK_BUILD=ON` | Faster CUDA build: instantiates a reduced kernel set. **Side effect:** Flash is compiled for head_dim 128 only, so most attention shapes fall back to **MEA** (changes which attention kernel is compiled/dispatched). Don't use it to characterize Flash-vs-arch behavior. |
6970
| `--build_dir` | Build output directory |
7071

7172
## Build output path
@@ -79,6 +80,15 @@ It may be customized with `--build_dir`.
7980
## Agent tips
8081

8182
- **Activate a Python virtual environment** before building. See "Python > Virtual environment" in `AGENTS.md`.
83+
- **Build flags can silently reroute which kernel/code path executes.** A build option can
84+
change *which* kernel is compiled, and therefore which code path actually runs — so a CI
85+
failure can live in a different code path than your local build exercises. Before
86+
hypothesizing a hardware- or algorithm-specific cause (e.g. "this GPU arch miscomputes"),
87+
first identify **which kernel actually ran** for the failing configuration (see the
88+
`ort-test` skill → "Verify which path/kernel actually executed"). Concrete instance:
89+
`onnxruntime_QUICK_BUILD=ON` compiles FlashAttention for head_dim 128 only, so most
90+
attention shapes silently dispatch to Memory-Efficient Attention instead of Flash —
91+
details in the `cuda-attention-kernel-patterns` skill.
8292
- **Prefer `python tools/ci_build/build.py` directly** over `build.bat`/`build.sh` when redirecting output. The `.bat` wrapper runs in `cmd.exe`, which breaks PowerShell redirection.
8393
- **Redirect output to a file** (e.g., `> build_log.txt 2>&1`). Build output is large and will overflow terminal buffers.
8494
- **Run builds in the background** — a full build can take tens of minutes to over an hour. Poll the log for `"Build complete"` or errors.

.agents/skills/ort-test/SKILL.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ ONNX Runtime uses **Google Test** for C++ and **unittest** (preferred) / **pytes
1616
| `onnxruntime_test_all` | Core framework, graph, optimizer, session tests |
1717
| `onnxruntime_provider_test` | Operator/kernel tests (Conv, MatMul, etc.) across execution providers |
1818

19+
### Two `attention_op_test.cc` files — don't confuse them
20+
21+
There are two same-named files testing **different operators**. Both build into
22+
`onnxruntime_provider_test`:
23+
24+
| Path | Operator | gtest suite |
25+
|---|---|---|
26+
| `test/providers/cpu/llm/attention_op_test.cc` | **ONNX-domain** `Attention` (opset 23/24) | `AttentionTest.*` |
27+
| `test/contrib_ops/attention_op_test.cc` | **contrib** MultiHeadAttention / GroupQueryAttention | `ContribOpAttentionTest.*` |
28+
29+
The MEA negative-offset regression tests (`Attention_Causal_NonPadKVSeqLen_MEA_*`,
30+
e.g. `..._MEA_NegOffset_ForceFlashDisabled_FP16_CUDA`) live in the **providers/cpu/llm** file —
31+
the ONNX-domain op.
32+
1933
Use `--gtest_filter` to select specific tests:
2034

2135
```bash
@@ -76,7 +90,62 @@ Python test naming convention: `test_<method>_<expected_behavior>_[when_<conditi
7690
## Agent tips
7791

7892
- **Activate a Python virtual environment** before running tests. See "Python > Virtual environment" in `AGENTS.md`.
93+
- **Beware false-green results** — a green run does not always prove anything. See the
94+
"False-green taxonomy" section below for the four ways a test can pass without testing
95+
your change.
7996
- **Redirect test output to a file** (e.g., `> test_output.txt 2>&1`) — output can be large.
8097
- For C++ tests, verify the build directory exists and a prior build completed before running.
8198
- Use `--gtest_filter` to run a targeted subset when the full suite takes too long.
8299
- **Running WebGPU tests locally on Linux without a GPU** — WebGPU op tests build into `onnxruntime_provider_test` and can run against a software Vulkan adapter (Mesa lavapipe). See the `webgpu-local-testing` skill.
100+
101+
## False-green taxonomy — ways a test can "pass" without proving anything
102+
103+
A green result is not always a real pass. Watch for all five modes:
104+
105+
1. **Zero-match filter.** A `--gtest_filter` that matches no tests still exits 0 (green).
106+
Confirm the `[==========] N tests ran` line is non-zero — a zero-match run prints
107+
`0 tests from 0 test suites`. Many operator/kernel gtests run only in
108+
**`onnxruntime_provider_test`** (CI runs this), NOT `onnxruntime_test_all`; the wrong
109+
binary matches nothing and looks green.
110+
2. **Stale binary from an incremental build.** If the build did not actually recompile your
111+
change (e.g. a header not tracked by the compiler's depfile), the "passing" run executes
112+
the OLD code. A test that was failing cannot truly flip to passing without a real
113+
rebuild — treat an unexpected FAIL→PASS with suspicion and confirm the linked artifact's
114+
mtime advanced. CUDA/CUTLASS instance (nvcc depfiles don't track `cutlass_fmha/*.h`): see
115+
the `cuda-cutlass-fmha-incremental-rebuild` skill.
116+
3. **Checking the wrong artifact's freshness.** With a dlopen'd shared provider (e.g.
117+
`libonnxruntime_providers_cuda.so`), the test executable is NOT relinked when the provider
118+
recompiles — its mtime stays old while the `.so` advances. Verify the artifact that
119+
actually links your change, not the test exe. Detail: `cuda-cutlass-fmha-incremental-rebuild`
120+
skill.
121+
4. **A correct fallback path masks the intended path.** A value-only assertion can pass via a
122+
*different, correct* code path without ever exercising the one you meant to test (e.g. a
123+
test meant for MEA silently handled by the unfused fallback). Assert/verify **which path
124+
ran**, not just the output value — see "Verify which path/kernel actually executed" below.
125+
5. **Arch-portability false-green (verified on only one GPU arch).** A CUDA kernel that
126+
launches on a large-dynamic-smem arch (e.g. sm90/H100, ~227KB) can **fail to launch** on a
127+
smaller opt-in cap (sm86/89 ~99KB, sm80 ~163KB) with `CUDA failure 1: invalid argument`
128+
and a path with no fallback (e.g. ORT's MEA) turns that into a hard error, not a silent
129+
degrade. So a green run on your local GPU can mask a launch failure on CI's arch. Verify
130+
arch-portability, or pick a config whose shared-memory footprint fits **every** target arch
131+
(e.g. a small `head_size`). Concrete instance: CUTLASS MEA `head_size=512` FP16 exceeds
132+
sm86's smem opt-in cap and dies at launch — live bug #28388 (the
133+
`cuda-attention-kernel-patterns` skill §1 has the dispatch detail).
134+
135+
## Verify which path/kernel actually executed
136+
137+
Value equality alone does not prove the intended code path ran — a correct fallback can
138+
produce the right answer (false-green mode 4 above). When a test targets a specific
139+
kernel/path, confirm it actually dispatched there instead of trusting the output:
140+
141+
- Enable verbose logging and check the dispatch log line. ORT attention logs one of these
142+
exact strings (`core/providers/cuda/llm/attention.cc`):
143+
- `ONNX Attention: using Flash Attention` (:1400)
144+
- `ONNX Attention: using Memory Efficient Attention` (:1451)
145+
- `Attention: using unified unfused path` (:1482) — note: **no `ONNX ` prefix** and it
146+
reads "unified unfused path", not "Unfused".
147+
- Or force the path via the relevant env var / build config AND add a compile-time guard so
148+
the test **SKIPs** (not silently passes) when the target path is unavailable — e.g.
149+
`SKIP_IF_MEA_NOT_COMPILED`.
150+
151+
Operator-specific routing/forcing details: `cuda-attention-kernel-patterns` skill §1/§7.

AGENTS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ Use `reserve()` not `resize()`. Do not use `absl::` directly — use the ORT typ
6666
- Prefer `gsl::span<const T>` over `const std::vector<T>&` for input parameters
6767
- Prefer `std::string_view` by value over `const std::string&`
6868
- `SafeInt<size_t>` (from `core/common/safeint.h`) for memory size arithmetic
69+
- **Signed vs unsigned on negative-capable differences.** Any expression of the form `a - b`
70+
that can be negative (an offset or remaining-budget computed from counts, e.g.
71+
`num_keys - num_queries`) must be stored and compared using a **signed** type
72+
(`int32_t`/`int64_t`), and any unsigned operand must be `static_cast` to signed *before*
73+
the subtraction/comparison. An unsigned result silently wraps to a huge value (`~4.29e9`
74+
for `uint32_t`), which can permanently satisfy or skip a relational guard with **no crash
75+
and no warning** — a correct-looking-but-wrong result. Concrete ORT instance + the exact
76+
fix sites: CUTLASS FMHA `causal_diagonal_offset`, see the `cuda-attention-kernel-patterns`
77+
skill §12.
6978
- Don't use `else` after `return`
7079
- Avoid `long` (ambiguous width) — use `int64_t` for dimensions, `size_t` for counts
7180
- `using namespace` allowed in limited scope but never at global scope in headers

cmake/external/onnxruntime_external_deps.cmake

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,19 @@ if (onnxruntime_USE_WEBGPU)
797797
#
798798
${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/dawn/dawn_buffer_fix_injection.patch &&
799799

800+
# The dawn_parallel_build_fix.patch contains the following changes:
801+
#
802+
# - (private) Fix parallel build race condition in emdawnwebgpu header copy
803+
# The emdawnwebgpu_headers_gen_add macro's add_custom_command uses cmake -E copy
804+
# without ensuring the destination directory exists first. When building with
805+
# parallel jobs (-j32), the copy commands for webgpu_glfw.h and
806+
# webgpu_enum_class_bitmasks.h can run before any DawnJSONGenerator command
807+
# has created gen/src/emdawnwebgpu/include/webgpu/, causing the copy to fail.
808+
# This patch adds cmake -E make_directory before the copy so the directory is
809+
# always present regardless of parallel build ordering.
810+
#
811+
${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/dawn/dawn_parallel_build_fix.patch &&
812+
800813
# Remove the test folder to speed up potential file scan operations (70k+ files not needed for build).
801814
# Using <SOURCE_DIR> token ensures the correct absolute path regardless of working directory.
802815
${CMAKE_COMMAND} -E rm -rf <SOURCE_DIR>/test)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
diff --git a/src/emdawnwebgpu/CMakeLists.txt b/src/emdawnwebgpu/CMakeLists.txt
2+
--- a/src/emdawnwebgpu/CMakeLists.txt
3+
+++ b/src/emdawnwebgpu/CMakeLists.txt
4+
@@ -36,6 +36,7 @@ macro(emdawnwebgpu_headers_gen_add filename)
5+
"${EM_BUILD_GEN_DIR}/include/webgpu/${filename}"
6+
MAIN_DEPENDENCY
7+
"${DAWN_INCLUDE_DIR}/webgpu/${filename}"
8+
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${EM_BUILD_GEN_DIR}/include/webgpu"
9+
COMMAND ${CMAKE_COMMAND} -E copy
10+
"${DAWN_INCLUDE_DIR}/webgpu/${filename}"
11+
"${EM_BUILD_GEN_DIR}/include/webgpu/${filename}"

docs/OperatorKernels.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ The **OpSet Version** column uses the following notation:
115115
|||[17, 19]|**T1** = tensor(double), tensor(float)<br/> **T2** = tensor(int32), tensor(int64)|
116116
|DeformConv|*in* X:**T**<br> *in* W:**T**<br> *in* offset:**T**<br> *in* B:**T**<br> *in* mask:**T**<br> *out* Y:**T**|22+|**T** = tensor(double), tensor(float)|
117117
|||[19, 21]|**T** = tensor(double), tensor(float)|
118-
|DepthToSpace|*in* input:**T**<br> *out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(uint8)|
119-
|||[11, 12]|**T** = tensor(double), tensor(float), tensor(uint8)|
118+
|DepthToSpace|*in* input:**T**<br> *out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(int8), tensor(uint8)|
119+
|||[11, 12]|**T** = tensor(double), tensor(float), tensor(int8), tensor(uint8)|
120120
|||[1, 10]|**T** = tensor(double), tensor(float)|
121121
|DequantizeLinear|*in* x:**T**<br> *in* x_scale:**tensor(float)**<br> *in* x_zero_point:**T**<br> *out* y:**tensor(float)**<br><br>or<br><br>*in* x:**T1**<br> *in* x_scale:**T2**<br> *in* x_zero_point:**T1**<br> *out* y:**T2**<br><br>or<br><br>*in* x:**T1**<br> *in* x_scale:**T2**<br> *in* x_zero_point:**T1**<br> *out* y:**T3**|25+|**T1** = tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int2), tensor(int32), tensor(int4), tensor(int8), tensor(uint16), tensor(uint2), tensor(uint4), tensor(uint8)<br/> **T2** = tensor(float), tensor(float16)|
122122
|||24|**T1** = tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int4), tensor(int8), tensor(uint16), tensor(uint4), tensor(uint8)<br/> **T2** = tensor(float), tensor(float16)|
@@ -477,8 +477,8 @@ The **OpSet Version** column uses the following notation:
477477
|||[1, 21]|**T** = tensor(float)|
478478
|Softsign|*in* input:**T**<br> *out* output:**T**|22+|**T** = tensor(float)|
479479
|||[1, 21]|**T** = tensor(float)|
480-
|SpaceToDepth|*in* input:**T**<br> *out* output:**T**|13+|**T** = tensor(double), tensor(float)|
481-
|||[1, 12]|**T** = tensor(double), tensor(float)|
480+
|SpaceToDepth|*in* input:**T**<br> *out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(int8), tensor(uint8)|
481+
|||[1, 12]|**T** = tensor(double), tensor(float), tensor(int8), tensor(uint8)|
482482
|Split|*in* input:**T**<br> *in* split:**T**<br> *out* outputs...:**T**<br><br>or<br><br>*in* input:**T**<br> *in* split:**tensor(int64)**<br> *out* outputs:**T**<br><br>or<br><br>*in* input:**T**<br> *out* outputs:**T**|18+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)|
483483
|||[13, 17]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)|
484484
|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)|

0 commit comments

Comments
 (0)