You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
importer does not recognize mbarrier_arrive_no_complete; kernels fail verification with symbol-not-found #774
cuda_device::barrier::mbarrier_arrive_no_complete (crates/cuda-device/src/barrier.rs:128) is a device stub with no importer support: it has zero references under crates/mir-importer/src/, while its sibling mbarrier intrinsics are recognized through the generated intrinsic tables (crates/mir-importer/src/translator/terminator/intrinsics/generated.rs).
A kernel that calls it compiles through the frontend but fails module verification with:
Symbol cuda_device__barrier__mbarrier_arrive_no_complete not found
Discovered while shaping the regression fixture for PR #772 (the fixture was made independent of it).
Fix: route it through the same generated-intrinsic path as the other mbarrier operations (likely an entry in cuda-intrinsics-gen so generated.rs picks it up on regeneration), lowering to the corresponding llvm.nvvm.mbarrier.arrive.noComplete intrinsic, plus a small example or importer test that calls it.
Per the repo rule that compiler gaps are bugs: the device API surface advertises the function, so the compile path should either support it or refuse it with a clear unsupported-construct diagnostic rather than a late symbol-not-found verification error.
Looked into routing this through the generated path, and hit a blocker worth
raising before anyone writes the record: the current signature cannot express
the intrinsic, so this needs an API decision rather than just a table entry.
intrinsics/imported.json has exactly two noComplete arrive records, and both
take a count:
pubunsafefnmbarrier_arrive_no_complete(bar:*constBarrier)// no count, no token
so it is short one operand and drops the i64 state. Its doc block also shows
mbarrier.arrive.noComplete.shared.b64 _, [addr];
which is not a form the ISA accepts — the real instruction is mbarrier.arrive.noComplete.shared.b64 $state, [$addr], $count;. The
already-generated sibling shows the shape the family uses: crates/cuda-device/src/generated/mbarrier_basic.rs has pub unsafe fn mbarrier_arrive(bar: *const Barrier) -> u64, i.e. generated and
token-returning, while no_complete is hand-written in barrier.rs and
predates that path.
So "route it through the generated path" also means changing the public
signature. Two ways, and the choice is yours:
Match the intrinsic — mbarrier_arrive_no_complete(bar: *const Barrier, count: u32) -> u64. Consistent with mbarrier_arrive, exposes the count the
ISA requires, lets the caller keep or drop the token.
Keep the fire-and-forget shape — leave the signature alone and have the
generated wrapper pass count = 1 and discard the state. Preserves the
documented intent and breaks no source, but bakes in a count the caller
cannot choose.
Either is a real API break only on paper: the function has zero call sites in
the tree and currently fails module verification for anyone who calls it, so
nothing working depends on today's shape.
Happy to implement whichever you prefer — including the ledger entry, dialect op,
regeneration, and an sm_80 example (the A10G I test on runs mbarrier, so this one
gets real execution evidence rather than compile-only). Just say which signature.
The count is a defining operand of the PTX/NVVM operation, and keeping the returned state preserves the real contract; callers that only need signaling can explicitly discard it. I don’t want the public intrinsic to silently bake in count = 1.
The current function does not work through device codegen and has no in-tree callers, so correcting its signature is the right tradeoff. We should route it through the generated catalog/ABI path, remove the stale handwritten stub, and document the important safety precondition: the count must be valid and this operation must not complete the current barrier phase.
Exact dynamic-count and returned-state checks for both backends, plus the sm_80 run you proposed, would cover it well.
Activity
vyncint commentedon Aug 10, 2026
Looked into routing this through the generated path, and hit a blocker worth
raising before anyone writes the record: the current signature cannot express
the intrinsic, so this needs an API decision rather than just a table entry.
intrinsics/imported.jsonhas exactly twonoCompletearrive records, and bothtake a count:
There is no zero-count variant. The stub is
so it is short one operand and drops the
i64state. Its doc block also showswhich is not a form the ISA accepts — the real instruction is
mbarrier.arrive.noComplete.shared.b64 $state, [$addr], $count;. Thealready-generated sibling shows the shape the family uses:
crates/cuda-device/src/generated/mbarrier_basic.rshaspub unsafe fn mbarrier_arrive(bar: *const Barrier) -> u64, i.e. generated andtoken-returning, while
no_completeis hand-written inbarrier.rsandpredates that path.
So "route it through the generated path" also means changing the public
signature. Two ways, and the choice is yours:
mbarrier_arrive_no_complete(bar: *const Barrier, count: u32) -> u64. Consistent withmbarrier_arrive, exposes the count theISA requires, lets the caller keep or drop the token.
generated wrapper pass
count = 1and discard the state. Preserves thedocumented intent and breaks no source, but bakes in a count the caller
cannot choose.
Either is a real API break only on paper: the function has zero call sites in
the tree and currently fails module verification for anyone who calls it, so
nothing working depends on today's shape.
Happy to implement whichever you prefer — including the ledger entry, dialect op,
regeneration, and an sm_80 example (the A10G I test on runs mbarrier, so this one
gets real execution evidence rather than compile-only). Just say which signature.
nihalpasham commentedon Aug 14, 2026
I recommend we take option 1 and make this the exact low-level primitive:
The count is a defining operand of the PTX/NVVM operation, and keeping the returned state preserves the real contract; callers that only need signaling can explicitly discard it. I don’t want the public intrinsic to silently bake in count = 1.
The current function does not work through device codegen and has no in-tree callers, so correcting its signature is the right tradeoff. We should route it through the generated catalog/ABI path, remove the stale handwritten stub, and document the important safety precondition: the count must be valid and this operation must not complete the current barrier phase.
Exact dynamic-count and returned-state checks for both backends, plus the sm_80 run you proposed, would cover it well.
feat(intrinsics): route mbarrier_arrive_no_complete through the gener…
Implement generated mbarrier_arrive_no_complete intrinsic (#1101)