Skip to content

fix(rocsparse): remove erroneous __restrict__ qualifiers on shared memory - #10270

Merged
ntrost57 merged 2 commits into
ROCm:developfrom
michaelselehov:users/michaelselehov/rocsparse/lcompiler-2518
Aug 5, 2026
Merged

fix(rocsparse): remove erroneous __restrict__ qualifiers on shared memory#10270
ntrost57 merged 2 commits into
ROCm:developfrom
michaelselehov:users/michaelselehov/rocsparse/lcompiler-2518

Conversation

@michaelselehov

@michaelselehov michaelselehov commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Motivation

segmented_blockreduce() in csrmm_device_nnz_split.h takes two pointers into block-shared LDS, and both were marked __restrict__. Each thread reads vals[tid - j], which another thread wrote. The attribute therefore does not hold.

Clang lowers __restrict__ to LLVM noalias. LangRef states that noalias also covers accesses from other threads, see llvm/llvm-project#211507. The compiler is free to forward the LDS reads across __syncthreads(). The reduction then drops the contributions of the neighbour threads and returns wrong values.

The attribute also buys nothing here. The helper is force-inlined, and its two arguments are distinct __shared__ arrays, so alias analysis already proves that they do not alias.

JIRA ID : LCOMPILER-2518

Technical Details

This is analogous to #10229 and #9629.

The failure appears with a compiler that contains c3628c7f125b "Reapply [AA] No synchronization effects for never-escaping identified local". That commit tells alias analysis that a synchronizing operation cannot affect an object that never escapes the function. An LDS array declared inside a kernel is such an object. Together with the invalid __restrict__, this lets the compiler drop the cross-thread partial sums.

I confirmed both sides on MI300X (gfx942):

compiler source result
current weekly __restrict__ present 40 pass, 8 fail
current weekly __restrict__ removed 48 pass
same commit, AA change disabled __restrict__ present 48 pass

Test Plan

Build and run, on MI300X:

hipsparse-test --gtest_filter='*nightly/spmm_csc.generic*'

The failing cases use the Chebyshev4 matrix, transA = T, and f64_r.

Test Result

Before:

[==========] 48 tests from 1 test suite ran.
[  PASSED  ] 40 tests.
[  FAILED  ] 8 tests

With this patch, all 48 tests pass.

Submission Checklist

…_blockreduce

segmented_blockreduce() in csrmm_device_nnz_split.h takes two pointers into
block-shared LDS, and both were marked __restrict__. Each thread reads
vals[tid - j], which another thread wrote, so the attribute does not hold.

Clang lowers __restrict__ to LLVM noalias. LangRef states that noalias also
covers accesses from other threads (llvm/llvm-project#211507). The compiler is
therefore free to forward the LDS reads across __syncthreads(). The reduction
then drops the contributions of the neighbour threads and returns wrong values.

The attribute also buys nothing here. The helper is force-inlined, and its two
arguments are distinct __shared__ arrays, so alias analysis already proves that
they do not alias.

JIRA ID : LCOMPILER-2518

Assisted-by: Claude Opus
@therock-pr-bot

therock-pr-bot Bot commented Aug 1, 2026

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
📝 PR Description ✅ Pass
Forbidden Files ✅ Pass
🧪 Unit Test ⚠️ Warning Error: Source/code files changed without an accompanying unit test.
Expected: add at least one test file named like test_<name>.py / test_<name>.cpp (or <name>_test.*).
Current: code file(s) changed: projects/rocsparse/library/src/level3/csrmm_device_nnz_split.h; no test file found
🔎 pre-commit ✅ Pass
🚫 Draft PR 🔜 To Be Enabled
🚩 Feature Flag 🔜 To Be Enabled
📊 Code Coverage 🔜 To Be Enabled
🤖 therock-pr-bot ✅ Pass

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

@therock-pr-bot

therock-pr-bot Bot commented Aug 1, 2026

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

@michaelselehov michaelselehov changed the title fix(rocsparse): remove erroneous __restrict__ qualifiers in segmented… fix(rocsparse): remove erroneous __restrict__ qualifiers on shared memory Aug 1, 2026
@ronlieb
ronlieb requested a review from zGoldthorpe August 1, 2026 10:25

@ronlieb ronlieb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, and we need a codeowner to approve

@ntrost57
ntrost57 enabled auto-merge (squash) August 3, 2026 09:10

@kliegeois kliegeois left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look good but I am not sure if the changelog modification is in the correct release.

@kliegeois
kliegeois disabled auto-merge August 4, 2026 13:17
Comment thread projects/rocsparse/CHANGELOG.md Outdated
@kliegeois
kliegeois enabled auto-merge (squash) August 4, 2026 13:55
Comment thread projects/rocsparse/CHANGELOG.md Outdated

@dso-amd dso-amd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (see my comments though on the changelog entry).

Co-authored-by: Daniel So <dso@amd.com>
@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #10270      +/-   ##
===========================================
- Coverage    69.62%   69.62%   -0.00%     
===========================================
  Files         2741     2741              
  Lines       451540   451737     +197     
  Branches     66531    66559      +28     
===========================================
+ Hits        314366   314493     +127     
- Misses      116966   117020      +54     
- Partials     20208    20224      +16     
Flag Coverage Δ *Carryforward flag
TensileLite 34.19% <ø> (ø) Carriedforward from 28c519e
TensileLite-CPP 38.12% <ø> (ø) Carriedforward from 28c519e
TensileLite-Unit 64.91% <ø> (ø) Carriedforward from 28c519e
hipBLAS 90.62% <ø> (ø) Carriedforward from 28c519e
hipBLASLt 34.89% <ø> (ø) Carriedforward from 28c519e
hipCUB 83.47% <ø> (ø) Carriedforward from 28c519e
hipDNN 86.39% <ø> (ø) Carriedforward from 28c519e
hipFFT 47.34% <ø> (ø) Carriedforward from 28c519e
hipRAND 76.12% <ø> (ø) Carriedforward from 28c519e
hipSOLVER 69.18% <ø> (ø) Carriedforward from 28c519e
hipSPARSE 86.27% <ø> (ø) Carriedforward from 28c519e
rocBLAS 47.95% <ø> (ø) Carriedforward from 28c519e
rocFFT 47.61% <ø> (ø) Carriedforward from 28c519e
rocRAND 57.02% <ø> (ø) Carriedforward from 28c519e
rocSOLVER 76.92% <ø> (ø) Carriedforward from 28c519e
rocSPARSE 72.57% <ø> (-0.02%) ⬇️
rocThrust 91.60% <ø> (ø) Carriedforward from 28c519e

*This pull request uses carry forward flags. Click here to find out more.
see 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

auto-merge was automatically disabled August 5, 2026 14:13

Invalid email address

@ntrost57
ntrost57 merged commit 54cce93 into ROCm:develop Aug 5, 2026
91 of 118 checks passed
jsandham added a commit that referenced this pull request Aug 12, 2026
## Motivation

Audit and fix incorrect usage of `__restrict__` in rocSPARSE kernels.

## Background
Clang lowers `__restrict__` to LLVM `noalias`, which is interpreted to
exclude accesses **from other threads**, not just other pointers in the
same thread. Combined with the AA change "no synchronization effects for
never-escaping locals," this lets the compiler forward/hoist a **plain**
load across `__syncthreads()` and drop writes made by sibling threads
(the root cause of upstream PR #10270). So `__restrict__` is invalid on:
(a) two pointers that provably alias, and (b) any buffer used to
exchange data between threads via **non-atomic** reads.

## Fixes applied

### 1. Provable pointer aliasing — ITILU0 `device_calculate`
`precond/itilu0/rocsparse_csritilu0_async_inplace.cpp` —
`device_calculate` took `T* __restrict__ y_` and `const T* __restrict__
ilu0_`, but both call sites pass `y_ = ilu0_ + k` alongside `ilu0_`, so
the two `__restrict__` pointers alias by construction. Removed
`__restrict__` from `y_` and `ilu0_`.

### 2. LDS cross-thread reductions (identical to PR #10270)
Segmented block-reduction helpers that do plain `data[tid] +=
data[tid+stride]` across `__syncthreads()` on a `__restrict__` pointer
into non-escaping `__shared__` memory:
- `extra/csrgemm_device.h` — `csrgemm_group_reduce`
- `extra/csrgemm_symbolic_device.h` — `csrgemm_symbolic_group_reduce`
- `extra/bsrgemm_device.h` — `bsrgemm_group_reduce`

### 3. Shared-memory hash tables (can hang, not just miscompute)
`insert_key` / `insert_pair` / `insert_pair_rxc` do a plain `const I
temp = table[hash];` in a loop that must observe other threads'
`atomic_cas` writes; `__restrict__` on `table` lets the compiler hoist
the load out of the loop. Removed `__restrict__` from `table` (kept it
on the atomic-only `data` param) in:
- `extra/csrgemm_device.h`, `extra/csrgemm_symbolic_device.h`,
`extra/csrgemm_numeric_device.h`, `extra/bsrgemm_device.h`

### 4. Cross-thread producer/consumer on global scratch
- **`workspace_B`** (multi-pass GEMM chunk buffer): one lane writes
`workspace_B[j]`, all lanes plain-read it next chunk across
`__syncthreads()`. Removed `__restrict__` in the 4 device headers and
the 4 matching kernel-wrapper `.cpp` signatures
(`rocsparse_csrgemm_calc.cpp`, `rocsparse_csrgemm_symbolic_calc.cpp`,
`rocsparse_csrgemm_numeric_calc.cpp`, `rocsparse_bsrgemm_calc.cpp`).
- **ITILU0 async in-place factors**: `lval_/uval_/dval_` in
`csritilu0x_async.cpp` (`kernel_correction`,
`kernel_correction_no_norm`) and `ilu0_` in
`csritilu0_async_inplace.cpp` (`kernel_calculate`,
`kernel_calculate_coo`) are read by some threads while written by others
in the same launch (no ping-pong snapshot). Removed `__restrict__` and
annotated the kernels.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants