[clang] [clang][HIP] Normalize __cdecl on abstract function types to the device default CC on amdgcnspirv (PR #210882)
Arseniy Obolenskiy via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 20 22:56:28 PDT 2026
https://github.com/aobolensk created https://github.com/llvm/llvm-project/pull/210882
On amdgcnspirv, __cdecl has no device-side equivalent, so it needs to be normalized to the device default CC to match plain function type
>From 17a63a34403b3cd2620454adf7f898f775301d0f Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 21 Jul 2026 07:54:07 +0200
Subject: [PATCH] [clang][HIP] Normalize __cdecl on abstract function types to
the device default CC on amdgcnspirv
On amdgcnspirv, __cdecl has no device-side equivalent, so it needs to be normalized to the device default CC to match plain function type
---
clang/lib/Sema/SemaType.cpp | 13 +++++++
...mdgcnspirv-host-cconv-in-abstract-type.hip | 35 +++++++++++++++++++
2 files changed, 48 insertions(+)
create mode 100644 clang/test/SemaHIP/amdgcnspirv-host-cconv-in-abstract-type.hip
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");
More information about the cfe-commits
mailing list