[clang] [Clang][HIP] Suppress availability diagnostics for mismatched host/device overloads (PR #93546)
Fabian Ritter via cfe-commits
cfe-commits at lists.llvm.org
Tue May 28 06:27:22 PDT 2024
https://github.com/ritter-x2a created https://github.com/llvm/llvm-project/pull/93546
Outside of function bodies, the resolution of host/device overloads for functions in HIP/CUDA operates as if in a host-device context. This means that the device overload is used in the device compilation phase and the host overload is used in the host compilation phase.
Therefore, the following code would cause a deprecation warning during host compilation, even though val is only used as part of a device function:
```C++
__attribute__((host, deprecated)) constexpr int val(void) {return 1;}
__attribute__((device)) constexpr int val(void) {return 1;}
__attribute__((device)) std::enable_if<(val() > 0), int>::type fun(void) {
return 42;
}
```
As only the available device overload is used during device compilation, where code for fun is actually generated, this diagnostic is spurious.
This patch suppresses availability diagnostics in such situations: When an unavailable host function is used in a device context during host compilation or when an unavailable device function is used in a host context during device compilation.
This change is necessary to avoid spurious warnings with #91478, e.g., in the rocPRIM library.
>From d88250fc1493ed8ab2780678deb15620b1897620 Mon Sep 17 00:00:00 2001
From: Fabian Ritter <fabian.ritter at amd.com>
Date: Tue, 28 May 2024 07:45:52 -0400
Subject: [PATCH] [Clang][HIP] Suppress availability diagnostics for mismatched
host/device overloads
Outside of function bodies, the resolution of host/device overloads for
functions in HIP/CUDA operates as if in a host-device context. This means that
the device overload is used in the device compilation phase and the host
overload is used in the host compilation phase.
Therefore, the following code would cause a deprecation warning during host
compilation, even though val is only used as part of a device function:
__attribute__((host, deprecated)) constexpr int val(void) {return 1;}
__attribute__((device)) constexpr int val(void) {return 1;}
__attribute__((device)) std::enable_if<(val() > 0), int>::type fun(void) {
return 42;
}
As only the available device overload is used during device compilation, where
code for fun is actually generated, this diagnostic is spurious.
This patch suppresses availability diagnostics in such situations: When an
unavailable host function is used in a device context during host compilation
or when an unavailable device function is used in a host context during device
compilation.
---
clang/lib/Sema/SemaAvailability.cpp | 53 +++++++
...lability-warnings-mismatched-attributes.cu | 149 ++++++++++++++++++
2 files changed, 202 insertions(+)
create mode 100644 clang/test/SemaCUDA/suppress-availability-warnings-mismatched-attributes.cu
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index 22f5a2f663477..984789489098a 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -20,6 +20,7 @@
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaCUDA.h"
#include "clang/Sema/SemaObjC.h"
#include "llvm/ADT/StringRef.h"
#include <optional>
@@ -156,6 +157,58 @@ static bool ShouldDiagnoseAvailabilityInContext(
}
}
+ if (S.getLangOpts().CUDA || S.getLangOpts().HIP) {
+ // In CUDA/HIP, do not diagnose uses of unavailable host or device function
+ // overloads when they occur in the context of a Decl with an explicitly
+ // given opposite target.
+ // We encounter this if the OffendingDecl is used outside of a function
+ // body, e.g., in template arguments for a function's return or parameter
+ // types. In this case, overloads of the called function are resolved as if
+ // in a host-device context, i.e., the device overload is chosen in the
+ // device compilation phase and the host overload in the host compilation
+ // phase. As code is only generated for the variant with matching targets,
+ // an availabiliy diagnostic for the variant with non-matching targets would
+ // be spurious.
+
+ if (auto *OffendingFunDecl = llvm::dyn_cast<FunctionDecl>(OffendingDecl)) {
+ Decl *ActualCtx = Ctx;
+ if (auto *FTD = llvm::dyn_cast<FunctionTemplateDecl>(Ctx)) {
+ // Attributes of template Decls are only on the templated Decl
+ ActualCtx = FTD->getTemplatedDecl();
+ }
+ if (auto *CtxFun = llvm::dyn_cast<FunctionDecl>(ActualCtx)) {
+ auto TargetIs = [&S](const FunctionDecl *FD, CUDAFunctionTarget FT) {
+ return S.CUDA().IdentifyTarget(FD, /* IgnoreImplicitHDAttr */ true) ==
+ FT;
+ };
+
+ bool CtxIsHost = TargetIs(CtxFun, CUDAFunctionTarget::Host);
+ bool CtxIsDevice = TargetIs(CtxFun, CUDAFunctionTarget::Device);
+
+ bool OffendingDeclIsHost =
+ TargetIs(OffendingFunDecl, CUDAFunctionTarget::Host);
+ bool OffendingDeclIsDevice =
+ TargetIs(OffendingFunDecl, CUDAFunctionTarget::Device);
+
+ // There is a way to call a device function from host code (and vice
+ // versa, analogously) that passes semantic analysis: As constexprs,
+ // when there is no host overload. In this case, a diagnostic is
+ // necessary. Characteristic for this situation is that the device
+ // function will also be used in a host context during host compilation.
+ // Therefore, only suppress diagnostics if a host function is used in a
+ // device context during host compilation or a device function is used
+ // in a host context during device compilation.
+ bool CompilingForDevice = S.getLangOpts().CUDAIsDevice;
+ bool CompilingForHost = !CompilingForDevice;
+
+ if ((OffendingDeclIsHost && CtxIsDevice && CompilingForHost) ||
+ (OffendingDeclIsDevice && CtxIsHost && CompilingForDevice)) {
+ return false;
+ }
+ }
+ }
+ }
+
// Checks if we should emit the availability diagnostic in the context of C.
auto CheckContext = [&](const Decl *C) {
if (K == AR_NotYetIntroduced) {
diff --git a/clang/test/SemaCUDA/suppress-availability-warnings-mismatched-attributes.cu b/clang/test/SemaCUDA/suppress-availability-warnings-mismatched-attributes.cu
new file mode 100644
index 0000000000000..c3023d16565cf
--- /dev/null
+++ b/clang/test/SemaCUDA/suppress-availability-warnings-mismatched-attributes.cu
@@ -0,0 +1,149 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify=expected,onhost %s
+// RUN: %clang_cc1 -triple nvptx64-nvidia-cuda -fsyntax-only -fcuda-is-device -verify=expected,ondevice %s
+
+template <bool C, class T = void> struct my_enable_if {};
+
+template <class T> struct my_enable_if<true, T> {
+ typedef T type;
+};
+
+__attribute__((host, device)) void use(int x);
+
+__attribute__((device)) constexpr int OverloadFunHostDepr(void) { return 1; }
+__attribute__((host, deprecated("Host variant"))) constexpr int OverloadFunHostDepr(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}}
+
+
+__attribute__((device, deprecated("Device variant"))) constexpr int OverloadFunDeviceDepr(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}}
+__attribute__((host)) constexpr int OverloadFunDeviceDepr(void) { return 1; }
+
+
+template<typename T>
+__attribute__((device)) constexpr T TemplateOverloadFun(void) { return 1; }
+
+template<typename T>
+__attribute__((host, deprecated("Host variant"))) constexpr T TemplateOverloadFun(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}}
+
+
+__attribute__((device, deprecated)) constexpr int // expected-note 0+ {{has been explicitly marked deprecated here}}
+DeviceOnlyFunDeprecated(void) { return 1; }
+
+__attribute__((host, deprecated)) constexpr int // expected-note 0+ {{has been explicitly marked deprecated here}}
+HostOnlyFunDeprecated(void) { return 1; }
+
+class FunSelector {
+public:
+ template<int X> __attribute__((device))
+ auto devicefun(void) -> typename my_enable_if<(X == OverloadFunHostDepr()), int>::type {
+ return 1;
+ }
+
+ template<int X> __attribute__((device))
+ auto devicefun(void) -> typename my_enable_if<(X != OverloadFunHostDepr()), int>::type {
+ return 0;
+ }
+
+ template<int X> __attribute__((device))
+ auto devicefun_wrong(void) -> typename my_enable_if<(X == OverloadFunDeviceDepr()), int>::type { // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}}
+ return 1;
+ }
+
+ template<int X> __attribute__((device))
+ auto devicefun_wrong(void) -> typename my_enable_if<(X != OverloadFunDeviceDepr()), int>::type { // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}}
+ return 0;
+ }
+
+ template<int X> __attribute__((host))
+ auto hostfun(void) -> typename my_enable_if<(X == OverloadFunDeviceDepr()), int>::type {
+ return 1;
+ }
+
+ template<int X> __attribute__((host))
+ auto hostfun(void) -> typename my_enable_if<(X != OverloadFunDeviceDepr()), int>::type {
+ return 0;
+ }
+
+ template<int X> __attribute__((host))
+ auto hostfun_wrong(void) -> typename my_enable_if<(X == OverloadFunHostDepr()), int>::type { // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
+ return 1;
+ }
+
+ template<int X> __attribute__((host))
+ auto hostfun_wrong(void) -> typename my_enable_if<(X != OverloadFunHostDepr()), int>::type { // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
+ return 0;
+ }
+};
+
+
+// These should not be diagnosed since the device overload of
+// OverloadFunHostDepr is not deprecated:
+__attribute__((device)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type
+DeviceUserOverloadFunHostDepr1(void) { return 2; }
+
+my_enable_if<(OverloadFunHostDepr() > 0), int>::type __attribute__((device))
+DeviceUserOverloadFunHostDepr2(void) { return 2; }
+
+__attribute__((device))
+my_enable_if<(OverloadFunHostDepr() > 0), int>::type constexpr
+DeviceUserOverloadFunHostDeprConstexpr(void) { return 2; }
+
+
+// Analogously for OverloadFunDeviceDepr:
+__attribute__((host)) my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type
+DeviceUserOverloadFunDeviceDepr1(void) { return 2; }
+
+my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type __attribute__((host))
+DeviceUserOverloadFunDeviceDepr2(void) { return 2; }
+
+__attribute__((host))
+my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type constexpr
+DeviceUserOverloadFunDeviceDeprConstexpr(void) { return 2; }
+
+
+// Actual uses of the deprecated overloads should be diagnosed:
+__attribute__((host, device)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
+HostDeviceUserOverloadFunHostDepr(void) { return 3; }
+
+__attribute__((host)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type constexpr // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
+HostUserOverloadFunHostDeprConstexpr(void) { return 3; }
+
+__attribute__((device)) my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type constexpr // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}}
+HostUserOverloadFunDeviceDeprConstexpr(void) { return 3; }
+
+
+// Making the offending decl a template shouldn't change anything:
+__attribute__((host)) my_enable_if<(TemplateOverloadFun<int>() > 0), int>::type // onhost-warning {{'TemplateOverloadFun<int>' is deprecated: Host variant}}
+HostUserTemplateOverloadFun(void) { return 3; }
+
+__attribute__((device)) my_enable_if<(TemplateOverloadFun<int>() > 0), int>::type
+DeviceUserTemplateOverloadFun(void) { return 3; }
+
+
+// If the constexpr function is actually called from the mismatched context, diagnostics should be issued:
+__attribute__((host))
+my_enable_if<(DeviceOnlyFunDeprecated() > 0), int>::type constexpr // onhost-warning {{'DeviceOnlyFunDeprecated' is deprecated}}
+HostUserDeviceOnlyFunDeprecated(void) { return 3; }
+
+__attribute__((device))
+my_enable_if<(HostOnlyFunDeprecated() > 0), int>::type constexpr // ondevice-warning {{'HostOnlyFunDeprecated' is deprecated}}
+DeviceUserHostOnlyFunDeprecated(void) { return 3; }
+
+// Diagnostics for uses in function bodies should work as expected:
+__attribute__((device, deprecated)) constexpr int DeviceVarConstDepr = 1; // expected-note 0+ {{has been explicitly marked deprecated here}}
+
+__attribute__((host)) void HostUser(void) {
+ use(DeviceVarConstDepr); // expected-warning {{'DeviceVarConstDepr' is deprecated}}
+ use(HostOnlyFunDeprecated()); // expected-warning {{'HostOnlyFunDeprecated' is deprecated}}
+ use(OverloadFunHostDepr()); // expected-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
+ use(TemplateOverloadFun<int>()); // expected-warning {{'TemplateOverloadFun<int>' is deprecated: Host variant}}
+
+ use(OverloadFunDeviceDepr());
+}
+
+__attribute__((device)) void DeviceUser(void) {
+ use(DeviceVarConstDepr); // expected-warning {{'DeviceVarConstDepr' is deprecated}}
+ use(DeviceOnlyFunDeprecated()); // expected-warning {{'DeviceOnlyFunDeprecated' is deprecated}}
+ use(OverloadFunDeviceDepr()); // expected-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}}
+
+ use(OverloadFunHostDepr());
+ use(TemplateOverloadFun<int>());
+}
More information about the cfe-commits
mailing list