[clang] [Clang][CodeGen][CUDA] Add missing null-check assertions in CGCUDARuntime (PR #181167)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 12 07:45:37 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: UnknownUser (Z3rox-dev)
<details>
<summary>Changes</summary>
## Summary
Add defensive `assert` checks for all `getAs<>()` calls in `CGCUDARuntime.cpp` that were previously dereferenced without null verification.
## Details
The file had **8 unchecked `getAs<>()`** calls across `emitGetParamBuf()` and `EmitCUDADeviceKernelCallExpr()`, including chained dereferences like:
```cpp
E->getCallee()->getType()->getAs<PointerType>()->getPointeeType();
```
where a null return from `getAs<PointerType>()` would cause an immediate crash with no diagnostic.
### Changes:
- Added `assert()` null checks for all `getAs<PointerType>()` and `getAs<FunctionProtoType>()` results
- Eliminated **3 redundant `getAs<>()` calls** by caching results in local variables (`KernelPT`, `KernelFPT`, `LaunchPT`, `LaunchFPT`)
- Broke chained dereferences into separate, checked steps
### Motivation:
While Sema validation currently prevents these types from being null in practice, this change:
1. Follows the established defensive coding pattern used elsewhere in CodeGen (e.g., `CGExprScalar.cpp`, `CGObjCMac.cpp`)
2. Provides executable documentation of the implicit type contract
3. Improves debuggability in assertion-enabled builds
4. Makes the code more resilient to future refactoring that might alter Sema guarantees
**NFC** (no functional change in release builds — asserts are compiled out).
## Testing
- `check-clang-codegen-cuda`: **75/75 passed**
- `check-clang-sema-cuda`: **99/99 passed**
- `clang-format`: clean
---
Full diff: https://github.com/llvm/llvm-project/pull/181167.diff
1 Files Affected:
- (modified) clang/lib/CodeGen/CGCUDARuntime.cpp (+20-14)
``````````diff
diff --git a/clang/lib/CodeGen/CGCUDARuntime.cpp b/clang/lib/CodeGen/CGCUDARuntime.cpp
index 9c831b26c3a7b..545233e4f1221 100644
--- a/clang/lib/CodeGen/CGCUDARuntime.cpp
+++ b/clang/lib/CodeGen/CGCUDARuntime.cpp
@@ -27,6 +27,8 @@ static llvm::Value *emitGetParamBuf(CodeGenFunction &CGF,
auto *GetParamBuf = CGF.getContext().getcudaGetParameterBufferDecl();
const FunctionProtoType *GetParamBufProto =
GetParamBuf->getType()->getAs<FunctionProtoType>();
+ assert(GetParamBufProto &&
+ "cudaGetParameterBuffer must have function proto type");
DeclRefExpr *DRE = DeclRefExpr::Create(
CGF.getContext(), {}, {}, GetParamBuf,
@@ -43,8 +45,10 @@ static llvm::Value *emitGetParamBuf(CodeGenFunction &CGF,
CGF.getContext().getSizeType());
// Calculate parameter sizes.
const PointerType *PT = E->getCallee()->getType()->getAs<PointerType>();
+ assert(PT && "CUDA kernel callee must be a pointer type");
const FunctionProtoType *FTP =
PT->getPointeeType()->getAs<FunctionProtoType>();
+ assert(FTP && "CUDA kernel callee must be a function proto type");
CharUnits Offset = CharUnits::Zero();
for (auto ArgTy : FTP->getParamTypes()) {
auto TInfo = CGF.CGM.getContext().getTypeInfoInChars(ArgTy);
@@ -79,14 +83,16 @@ RValue CGCUDARuntime::EmitCUDADeviceKernelCallExpr(
eval.begin(CGF);
CGF.EmitBlock(ConfigOKBlock);
- QualType KernelCalleeFuncTy =
- E->getCallee()->getType()->getAs<PointerType>()->getPointeeType();
+ const auto *KernelPT = E->getCallee()->getType()->getAs<PointerType>();
+ assert(KernelPT && "CUDA kernel callee must be a pointer type");
+ QualType KernelCalleeFuncTy = KernelPT->getPointeeType();
CGCallee KernelCallee = CGF.EmitCallee(E->getCallee());
// Emit kernel arguments.
CallArgList KernelCallArgs;
- CGF.EmitCallArgs(KernelCallArgs,
- KernelCalleeFuncTy->getAs<FunctionProtoType>(),
- E->arguments(), E->getDirectCallee());
+ const auto *KernelFPT = KernelCalleeFuncTy->getAs<FunctionProtoType>();
+ assert(KernelFPT && "CUDA kernel callee must be a function proto type");
+ CGF.EmitCallArgs(KernelCallArgs, KernelFPT, E->arguments(),
+ E->getDirectCallee());
// Copy emitted kernel arguments into that parameter buffer.
RawAddress CfgBase(Config, CGM.Int8Ty,
/*Alignment=*/CharUnits::fromQuantity(64));
@@ -101,22 +107,22 @@ RValue CGCUDARuntime::EmitCUDADeviceKernelCallExpr(
}
// Make `cudaLaunchDevice` call, i.e. E->getConfig().
const CallExpr *LaunchCall = E->getConfig();
- QualType LaunchCalleeFuncTy = LaunchCall->getCallee()
- ->getType()
- ->getAs<PointerType>()
- ->getPointeeType();
+ const auto *LaunchPT =
+ LaunchCall->getCallee()->getType()->getAs<PointerType>();
+ assert(LaunchPT && "CUDA launch callee must be a pointer type");
+ QualType LaunchCalleeFuncTy = LaunchPT->getPointeeType();
CGCallee LaunchCallee = CGF.EmitCallee(LaunchCall->getCallee());
CallArgList LaunchCallArgs;
- CGF.EmitCallArgs(LaunchCallArgs,
- LaunchCalleeFuncTy->getAs<FunctionProtoType>(),
- LaunchCall->arguments(), LaunchCall->getDirectCallee());
+ const auto *LaunchFPT = LaunchCalleeFuncTy->getAs<FunctionProtoType>();
+ assert(LaunchFPT && "CUDA launch callee must be a function proto type");
+ CGF.EmitCallArgs(LaunchCallArgs, LaunchFPT, LaunchCall->arguments(),
+ LaunchCall->getDirectCallee());
// Replace func and paramterbuffer arguments.
LaunchCallArgs[0] = CallArg(RValue::get(KernelCallee.getFunctionPointer()),
CGM.getContext().VoidPtrTy);
LaunchCallArgs[1] = CallArg(RValue::get(Config), CGM.getContext().VoidPtrTy);
const CGFunctionInfo &LaunchCallInfo = CGM.getTypes().arrangeFreeFunctionCall(
- LaunchCallArgs, LaunchCalleeFuncTy->getAs<FunctionProtoType>(),
- /*ChainCall=*/false);
+ LaunchCallArgs, LaunchFPT, /*ChainCall=*/false);
CGF.EmitCall(LaunchCallInfo, LaunchCallee, ReturnValue, LaunchCallArgs,
CallOrInvoke,
/*IsMustTail=*/false, E->getExprLoc());
``````````
</details>
https://github.com/llvm/llvm-project/pull/181167
More information about the cfe-commits
mailing list