[clang] [clang][HIP] Normalize __cdecl on abstract function types to the device default CC on amdgcnspirv (PR #210882)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 20 22:57:03 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Arseniy Obolenskiy (aobolensk)
<details>
<summary>Changes</summary>
On amdgcnspirv, __cdecl has no device-side equivalent, so it needs to be normalized to the device default CC to match plain function type
---
Full diff: https://github.com/llvm/llvm-project/pull/210882.diff
2 Files Affected:
- (modified) clang/lib/Sema/SemaType.cpp (+13)
- (added) clang/test/SemaHIP/amdgcnspirv-host-cconv-in-abstract-type.hip (+35)
``````````diff
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index f7789b7475e8e..b994080a58bb4 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8349,6 +8349,19 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
return true;
const FunctionType *fn = unwrapped.get();
+
+ // On amdgcnspirv, __cdecl on an abstract function type has no device-side
+ // equivalent, so normalize it to the device default CC to match an
+ // unadorned function type. Skip named decls and variadic types.
+ if (CC == CC_C && S.getLangOpts().CUDAIsDevice &&
+ S.Context.getTargetInfo().getTriple().isSPIRV() &&
+ state.getDeclarator().mayOmitIdentifier()) {
+ const auto *FnP = dyn_cast<FunctionProtoType>(fn);
+ if (!FnP || !FnP->isVariadic())
+ CC = S.Context.getDefaultCallingConvention(/*IsVariadic=*/false,
+ /*IsCXXMethod=*/false);
+ }
+
CallingConv CCOld = fn->getCallConv();
Attr *CCAttr = getCCTypeAttr(S.Context, attr);
diff --git a/clang/test/SemaHIP/amdgcnspirv-host-cconv-in-abstract-type.hip b/clang/test/SemaHIP/amdgcnspirv-host-cconv-in-abstract-type.hip
new file mode 100644
index 0000000000000..a624edc1bf564
--- /dev/null
+++ b/clang/test/SemaHIP/amdgcnspirv-host-cconv-in-abstract-type.hip
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 %s -fcuda-is-device -std=c++17 -triple spirv32 -aux-triple x86_64-pc-windows-msvc -fms-extensions -verify
+// RUN: %clang_cc1 %s -fcuda-is-device -std=c++17 -triple spirv64 -aux-triple x86_64-pc-windows-msvc -fms-extensions -verify
+// RUN: %clang_cc1 %s -fcuda-is-device -std=c++17 -triple spirv64-amd-amdhsa -aux-triple x86_64-pc-windows-msvc -fms-extensions -verify
+
+// __cdecl on an abstract function type must normalize to the device default
+// so std::function<void()> matches the library specialization keyed on
+// __cdecl on amdgcnspirv Windows.
+
+// expected-no-diagnostics
+
+template <class T> struct classify_fn {
+ static constexpr int value = 0;
+};
+template <class R, class... A> struct classify_fn<R __cdecl(A...)> {
+ static constexpr int value = 1;
+};
+template <class R, class... A> struct classify_fn<R __vectorcall(A...)> {
+ static constexpr int value = 2;
+};
+template <class R> struct classify_fn<R __cdecl(int, ...)> {
+ static constexpr int value = 3;
+};
+
+// Unadorned function types resolve to the __cdecl specialization.
+static_assert(classify_fn<void()>::value == 1, "void() must match __cdecl");
+static_assert(classify_fn<int(double, char)>::value == 1,
+ "int(double,char) must match __cdecl");
+
+// Variadic __cdecl keeps its own specialization, not the device default.
+static_assert(classify_fn<int __cdecl(int, ...)>::value == 3,
+ "variadic __cdecl must match its own specialization");
+
+// __vectorcall must not be folded into the device default alongside __cdecl.
+static_assert(classify_fn<void __vectorcall()>::value == 2,
+ "__vectorcall must match its own specialization, not __cdecl");
``````````
</details>
https://github.com/llvm/llvm-project/pull/210882
More information about the cfe-commits
mailing list