Skip to content

Commit 932022e

Browse files
authored
Force array data pointers to stay flat in AMD GPU codegen (#29053)
Force array data pointers to stay flat in AMD GPU codegen The `shoc-sort` GPU test crashed with a "Memory access fault by GPU" only when compiled with `--optimize`, and bisecting the LLVM backend pipeline traced the regression to the `InferAddressSpaces` pass (which only runs at `-O1` and above). Comparing device IR between the working and crashing builds showed that with optimizations every array data pointer was rewritten from the flat/generic address space to `addrspace(1)` global, and the resulting loads were stamped with `!amdgpu.noclobber`. That `noclobber` is incorrect for the recursive in-place scan kernel, which reads and writes the same buffer through different array descriptors, so stale reads corrupted the offset data and produced an out-of-bounds store in `reorderData`. Chapel materializes array data pointers by chasing the array descriptor in memory, and `getAssumedAddrSpace` assumes any flat pointer loaded from the kernarg segment is global, which is what drives the unsafe promotion. To prevent it, the array data base pointer is now laundered through an identity inline-asm "barrier" (`call ptr asm "", "=v,0"(ptr)`) that address-space inference cannot see through, keeping the pointer and everything derived from it flat. The barrier is emitted only for `_ddata` base pointers in `codegenElementPtr`, and only when generating AMD GPU device code, so non-GPU and CUDA codegen and all other loads are unaffected. I don't actually know if this is the best possible fix, but it works and seems to have no adverse affects. This feels brittle and like it could inhibit other optimizations, but its the best fix I have at this time. Note: AI was used to help me diagnose and fix this failure, the concept for the fix and the code for it are both AI generated. Related to #28955 [Reviewed by @benharsh]
2 parents 036021c + 560a192 commit 932022e

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

compiler/codegen/cg-expr.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "wellknown.h"
4242

4343
#ifdef HAVE_LLVM
44+
#include "llvm/IR/InlineAsm.h"
4445
#include "llvm/IR/Module.h"
4546
#include "llvmTracker.h"
4647
#include "llvmUtil.h"
@@ -697,6 +698,36 @@ llvm::StoreInst* codegenStoreLLVM(GenRet val,
697698
ptr.noalias,
698699
!ptr.mustPointOutsideOrderIndependentLoop);
699700
}
701+
702+
// in some cases, we really really want pointers to stay flat pointers and not
703+
// be inferred to a global address space (this seems to happen mainly with the
704+
// AMD GPU backend). The primary use case for this is array data pointers,
705+
// which are obtained from the array descriptor. Its not actually safe for the
706+
// backend to infer that these pointers are global, since they are obtained
707+
// from a descriptor and the backend may assume that different descriptors
708+
// cannot alias. This function is used to launder such pointers through an
709+
// identity inline assembly barrier, which prevents the backend from inferring
710+
// the address space. I don't know if this is the best way to do this, but it
711+
// does seem to work
712+
static llvm::Value* codegenGpuKeepPointerFlat(llvm::Value* val) {
713+
if (!gCodegenGPU) return val;
714+
if (getGpuCodegenType() != GpuCodegenType::GPU_CG_AMD_HIP) return val;
715+
716+
llvm::PointerType* ptrTy = llvm::dyn_cast<llvm::PointerType>(val->getType());
717+
if (ptrTy == NULL || ptrTy->getAddressSpace() != 0) return val;
718+
719+
GenInfo* info = gGenInfo;
720+
llvm::Type* argTypes[] = { val->getType() };
721+
llvm::FunctionType* asmTy =
722+
llvm::FunctionType::get(val->getType(), argTypes, /*isVarArg=*/false);
723+
llvm::InlineAsm* identity =
724+
llvm::InlineAsm::get(asmTy, /*AsmString=*/"", /*Constraints=*/"=v,0",
725+
/*hasSideEffects=*/false);
726+
llvm::CallInst* ret = info->irBuilder->CreateCall(identity, { val });
727+
trackLLVMValue(ret);
728+
return ret;
729+
}
730+
700731
// Create an LLVM load instruction possibly adding
701732
// appropriate metadata based upon the Chapel type of ptr.
702733
static
@@ -1502,6 +1533,15 @@ GenRet codegenElementPtr(GenRet base, GenRet index, bool ddataPtr=false) {
15021533
}
15031534
} else {
15041535
#ifdef HAVE_LLVM
1536+
// For accesses into array data (_ddata), keep the base data pointer in the
1537+
// flat (generic) address space so that the AMD GPU backend's
1538+
// InferAddressSpaces pass does not promote it to the global address space.
1539+
// See codegenGpuKeepPointerFlat for why that promotion is unsafe for
1540+
// descriptor-chased array pointers.
1541+
if (baseValType->symbol->hasFlag(FLAG_DATA_CLASS)) {
1542+
base.val = codegenGpuKeepPointerFlat(base.val);
1543+
}
1544+
15051545
unsigned AS = base.val->getType()->getPointerAddressSpace();
15061546

15071547
// in LLVM, arrays are not pointers and cannot be used in

0 commit comments

Comments
 (0)