Fix reverse translation of non-overloaded AMDGCN pointer intrinsics - #273
Fix reverse translation of non-overloaded AMDGCN pointer intrinsics#273idubinov wants to merge 10 commits into
Conversation
Non-overloaded AMDGCN intrinsics that return a pointer in a fixed address space -- e.g. llvm.amdgcn.implicitarg.ptr, which must return ptr addrspace(4) -- are lowered to a `spirv.llvm_*` imported function during forward translation. On reverse translation SPIRVToLLVM::transFunction recreated them with Function::Create() using the SPIR-V derived function type. SPIR-V has no notion of the AMDGPU-specific address spaces, so the return pointer falls back to addrspace(0), producing a declaration that mismatches the intrinsic's required prototype and fails the IR verifier: intrinsic return type expected ptr addrspace(4), but got ptr This surfaces at runtime when finalizing amdgcnspirv HIP code objects: with recent HIP headers threadIdx/blockIdx/blockDim lower to llvm.amdgcn.workitem.id / workgroup.id / implicitarg.ptr, so every kernel that reads those crashes in comgr's in-process clang codegen at first kernel launch (SIGSEGV in the broken-module teardown path). memcpy/ptrmask/umul_with_overflow already have per-intrinsic special cases just above; instead of adding one per intrinsic, reconstruct any non-overloaded intrinsic from its canonical declaration via Intrinsic::getOrInsertDeclaration, which yields the correct verifier-valid prototype. Overloaded intrinsics still fall through to the existing type-derived handling since they need overload types from the SPIR-V function type.
aobolensk
left a comment
There was a problem hiding this comment.
Any test for this case?
|
|
||
| F = cast<Function>(mapValue(BF, F)); | ||
|
|
||
| if (F->isIntrinsic()) { |
There was a problem hiding this comment.
For me it looks like a more general problem of some sort. Do you know why other intrinsics do not require getOrInsertDeclaration but AMDGCN ones do?
There was a problem hiding this comment.
Most LLVM intrinsics are "known" to the translator: they map to real SPIR-V opcodes/ExtInsts and are reconstructed on reverse from that semantic representation, so their type is never in doubt. AMDGCN intrinsics have no SPIR-V equivalent, so they travel as opaque -spirv-allow-unknown-intrinsics passthroughs — only a mangled name plus a SPIR-V function type survive, and since SPIR-V encodes pointers by storage class, the AMDGPU address spaces (e.g. the constant AS(4) that implicitarg.ptr must return) get lost when the type is rebuilt. Because they're still named LLVM intrinsics, the verifier enforces their exact prototype - so we have to recover it from the intrinsic definition via getOrInsertDeclaration rather than trust the address-space-lossy reconstructed type.
Intrinsic::getOrInsertDeclaration(M, IID) builds the declaration from the intrinsic's authoritative .td definition, so it sets the entire prototype at once - including the correct return type with its required address space (ptr addrspace(4)) - not just the name.
Covers the reader-side counterpart of #246: a non-overloaded AMDGCN intrinsic that returns a pointer in the constant address space (llvm.amdgcn.implicitarg.ptr, ptr addrspace(4)) must be reconstructed from its canonical intrinsic declaration on reverse translation. The intrinsic is imported as an external function whose pointer return uses the Function storage class (addrspace(0)). Without the fix the reader rebuilds the declaration from that SPIR-V function type, dropping addrspace(4) and producing verifier-invalid IR; the test fails in that case.
Co-authored-by: Arseniy Obolenskiy <gooddoog@student.su>
| Intrinsic::ID IID = Intrinsic::not_intrinsic; | ||
| if (FuncNameRef.starts_with("llvm.amdgcn.")) | ||
| IID = Intrinsic::lookupIntrinsicID(FuncName); | ||
| if (IID != Intrinsic::not_intrinsic && !Intrinsic::isOverloaded(IID)) |
There was a problem hiding this comment.
If we're supposed to mimic the patch #246 with removal ugly WA related to "llvm.amdgcn.is.shared" and "llvm.amdgcn.is.private", should we remove/update this part
SPIRV-LLVM-Translator/lib/SPIRV/SPIRVReader.cpp
Lines 2947 to 2959 in 82000fe
There was a problem hiding this comment.
Removed everything i have found related to duplicated logic for recovering address spaces and etc. Now types are recovered from .td files
Now that non-overloaded llvm.amdgcn.* intrinsics are reconstructed from LLVM's canonical intrinsic table on reverse translation, the forward and mangling-time address-space rewrites that compensated for the lost storage class are redundant and can be removed: - SPIRVWriter.cpp (transScavengedType): drop the CanonicalFT return-type and argument-type address-space rewrites for AMD intrinsics. - SPIRVReader.cpp (transBuiltinFromInst): drop the AMD-only pre-remap of argument address spaces before mangleOpenClBuiltin; the normal vendor-independent mangling path produces the same result. - SPIRVReader.cpp (OpFunctionCall): collapse the isKernel reconciliation and the hardcoded is.shared / is.private special case into a single formal-vs-actual pointer address-space reconciliation driven by the callee's canonical parameter types. - OCLUtil.h: delete the now-unused mapAMDGCNAddrSpaceToSPIRV helper. mapSPIRVAddrSpaceToAMDGPU is retained: it is the functional reverse storage-class -> AMDGPU address-space mapping, not part of the crutch. Verified: amd-llvm-spirv lit suite unchanged (1010 pass, only the 4 pre-existing DebugInfo failures); reverse translation of a spirv-val validated corpus (amdgcn/atomic/__spirv builtins incl. the divergent generic-pointer case) is byte-identical with and without these changes; HIP-Basic, Applications and Libraries amdgcnspirv examples pass, and check-comgr is green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The reverse-translation gate must test FuncName (dots, after the
spirv.<mangled> -> llvm.<intrinsic> rewrite), not FuncNameRef, which for
imported intrinsics such as spirv.llvm_amdgcn_implicitarg_ptr still holds
the underscore form "llvm_amdgcn_implicitarg_ptr" after consume_front.
With FuncNameRef the starts_with("llvm.amdgcn.") check is always false, so
the intrinsic is never reconstructed from LLVM's canonical declaration and
llvm.amdgcn.implicitarg.ptr reverts to addrspace(0), reintroducing the
verifier failure and the HIP kernel-launch crash.
Verified: HIP-Basic 23/23, Applications 7/7, Libraries 16/16 amdgcnspirv
examples pass; minimal implicitarg.ptr reverse yields ptr addrspace(4).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
due to problems with CI machine, the ROCm Examples tests passed on local machine gfx90a: |
aobolensk
left a comment
There was a problem hiding this comment.
That looks much better, because we're removing the difference with the upstream translator
The similar changes to PR246, but for reverse translation.
Fixes CI ROCm Examples / HIP Basic test
More info provided: (#271)
Reflecting #186, removed as much as I think related to constant address space etc. Now the types are resolves on the reader side based on .td files instead of duct tape solutions we had previously.
I want to ask to take a look, I may miss something or misunderstand
Summary
Non-overloaded AMDGCN intrinsics that return a pointer in a fixed address space — e.g.
llvm.amdgcn.implicitarg.ptr, which must returnptr addrspace(4)— are lowered to aspirv.llvm_*imported function during forward translation. On reverse translation,SPIRVToLLVM::transFunctionrecreated them withFunction::Create()using the SPIR-V–derived function type. SPIR-V has no notion of the AMDGPU-specific address spaces, so the return pointer falls back toaddrspace(0), producing a declaration that mismatches the intrinsic's required prototype and fails the IR verifier:memcpy/ptrmask/umul_with_overflowalready have per-intrinsic special cases just above. Instead of adding one per intrinsic, this reconstructs any non-overloaded intrinsic from its canonical declaration viaIntrinsic::getOrInsertDeclaration, which yields the correct, verifier-valid prototype. Overloaded intrinsics still fall through to the existing type-derived handling (they need overload types from the SPIR-V function type).Impact
This surfaces at runtime when finalizing amdgcnspirv HIP code objects. With recent HIP headers (
clrSWDEV-548892 / #2439),threadIdx/blockIdx/blockDimlower tollvm.amdgcn.workitem.id/workgroup.id/implicitarg.ptr. The broken reverse-translated module is fed to comgr's in-process clang codegen at first kernel launch, which crashes (SIGSEGV in the broken-module teardown path). This reproduces as HIP-Basic rocm-examples segfaults in theTest rocm-examplesCI job (16/23 examples).Verification (local, gfx90a + amd-staging clang-24 toolchain)
-DCMAKE_HIP_ARCHITECTURES=amdgcnspirv.ctest21/21 pass.amd-llvm-spirv -ron real HIP SPIR-V no longer emits the brokendeclare ptr @llvm.amdgcn.implicitarg.ptr();opt -passes=verifyis clean.check-amd-llvm-spirv: no new failures (the 4 pre-existingDebugInfo/*failures are unrelated — they fail identically without this change).Notes
.sptcarrying the regularizedspirv.llvm_amdgcn_implicitarg_ptrimport) is prepared but omitted here because its raw SPIR-V trips push secret-scanning on an embedded__hip_cuid_*string; happy to add it (redacted) if wanted.