Skip to content

[SYCL] Emit diagnostics appropriately when coexisting with OpenMP #3750

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1809,6 +1809,29 @@ class Sema final {
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/All)
};

private:
// A collection of a pair of undefined functions and their callers known
// to be reachable from a routine on the device (kernel or device function).
typedef std::pair<const FunctionDecl *, const FunctionDecl *> CallPair;
llvm::SmallVector<CallPair> UndefinedReachableFromSyclDevice;

public:
// Helper routine to add a pair of Callee-Caller pair of FunctionDecl *
// to UndefinedReachableFromSyclDevice.
void addFDToReachableFromSyclDevice(const FunctionDecl *Callee,
const FunctionDecl *Caller) {
UndefinedReachableFromSyclDevice.push_back(std::make_pair(Callee, Caller));
}
// Helper routine to check if a pair of Callee-Caller FunctionDecl *
// is in UndefinedReachableFromSyclDevice.
bool isFDReachableFromSyclDevice(const FunctionDecl *Callee,
const FunctionDecl *Caller) {
return llvm::any_of(UndefinedReachableFromSyclDevice,
[Callee, Caller](const CallPair &P) {
return P.first == Callee && P.second == Caller;
});
}

/// A generic diagnostic builder for errors which may or may not be deferred.
///
/// In CUDA, there exist constructs (e.g. variable-length arrays, try/catch)
Expand Down Expand Up @@ -13288,7 +13311,8 @@ class Sema final {
/// properly declared for device compilation.
void finalizeSYCLDelayedAnalysis(const FunctionDecl *Caller,
const FunctionDecl *Callee,
SourceLocation Loc);
SourceLocation Loc,
DeviceDiagnosticReason Reason);

/// Tells whether given variable is a SYCL explicit SIMD extension's "private
/// global" variable - global variable in the private address space.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1665,7 +1665,7 @@ class DeferredDiagnosticsEmitter
S.finalizeOpenMPDelayedAnalysis(Caller, FD, Loc);
// Finalize analysis of SYCL-specific constructs.
if (Caller && S.LangOpts.SYCLIsDevice)
S.finalizeSYCLDelayedAnalysis(Caller, FD, Loc);
S.finalizeSYCLDelayedAnalysis(Caller, FD, Loc, RootReason);
if (Caller)
S.DeviceKnownEmittedFns[FD] = {Caller, Loc};
// Always emit deferred diagnostics for the direct users. This does not
Expand Down
12 changes: 10 additions & 2 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18469,11 +18469,19 @@ Decl *Sema::getObjCDeclContext() const {
}

Sema::DeviceDiagnosticReason Sema::getEmissionReason(const FunctionDecl *FD) {
// FIXME: This should really be a bitwise-or of the language modes.
if (FD->hasAttr<SYCLSimdAttr>())
return Sema::DeviceDiagnosticReason::Esimd;
else if (FD->hasAttr<SYCLDeviceAttr>() || FD->hasAttr<SYCLKernelAttr>())
if (FD->hasAttr<SYCLDeviceAttr>() || FD->hasAttr<SYCLKernelAttr>())
return Sema::DeviceDiagnosticReason::Sycl;
// FIXME: Figure out the logic for OMP and CUDA.
// FIXME: Refine the logic for CUDA and OpenMP.
if (getLangOpts().CUDA)
return getLangOpts().CUDAIsDevice ? Sema::DeviceDiagnosticReason::CudaDevice
: Sema::DeviceDiagnosticReason::CudaHost;
if (getLangOpts().OpenMP)
return getLangOpts().OpenMPIsDevice
? Sema::DeviceDiagnosticReason::OmpDevice
: Sema::DeviceDiagnosticReason::OmpHost;
return Sema::DeviceDiagnosticReason::All;
}

Expand Down
32 changes: 24 additions & 8 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,15 @@ class SingleDeviceFunctionTracker {
return;
}

// If this is a routine that is not defined and it does not have either
// a SYCLKernel or SYCLDevice attribute on it, add it to the set of
// routines potentially reachable on device. This is to diagnose such
// cases later in finalizeSYCLDeviceAnalysis().
if (!CurrentDecl->isDefined() && !CurrentDecl->hasAttr<SYCLKernelAttr>() &&
!CurrentDecl->hasAttr<SYCLDeviceAttr>())
Parent.SemaRef.addFDToReachableFromSyclDevice(CurrentDecl,
CallStack.back());

// We previously thought we could skip this function if we'd seen it before,
// but if we haven't seen it before in this call graph, we can end up
// missing a recursive call. SO, we have to revisit call-graphs we've
Expand Down Expand Up @@ -3885,22 +3894,29 @@ bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) {

void Sema::finalizeSYCLDelayedAnalysis(const FunctionDecl *Caller,
const FunctionDecl *Callee,
SourceLocation Loc) {
SourceLocation Loc,
DeviceDiagnosticReason Reason) {
// Somehow an unspecialized template appears to be in callgraph or list of
// device functions. We don't want to emit diagnostic here.
if (Callee->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
return;

Callee = Callee->getMostRecentDecl();
bool HasAttr =
Callee->hasAttr<SYCLDeviceAttr>() || Callee->hasAttr<SYCLKernelAttr>();

// Disallow functions with neither definition nor SYCL_EXTERNAL mark
bool NotDefinedNoAttr = !Callee->isDefined() && !HasAttr;
// If the reason for the emission of this diagnostic is not SYCL-specific,
// and it is not known to be reachable from a routine on device, do not
// issue a diagnostic.
if ((Reason & DeviceDiagnosticReason::Sycl) == DeviceDiagnosticReason::None &&
!isFDReachableFromSyclDevice(Callee, Caller))
return;

// If Callee has a SYCL attribute, no diagnostic needed.
if (Callee->hasAttr<SYCLDeviceAttr>() || Callee->hasAttr<SYCLKernelAttr>())
return;

if (NotDefinedNoAttr && !Callee->getBuiltinID()) {
Diag(Loc, diag::err_sycl_restrict)
<< Sema::KernelCallUndefinedFunction;
// Diagnose if this is an undefined function and it is not a builtin.
if (!Callee->isDefined() && !Callee->getBuiltinID()) {
Diag(Loc, diag::err_sycl_restrict) << Sema::KernelCallUndefinedFunction;
Diag(Callee->getLocation(), diag::note_previous_decl) << Callee;
Diag(Caller->getLocation(), diag::note_called_by) << Caller;
}
Expand Down
4 changes: 4 additions & 0 deletions clang/test/SemaSYCL/call-to-undefined-function.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -sycl-std=2020 -verify -fsyntax-only %s
// RUN: %clang_cc1 -fsycl-is-device -fopenmp-simd -internal-isystem %S/Inputs -sycl-std=2020 -verify -fsyntax-only %s

// This test checks whether we diagnose cases of unmarked, undefined
// functions called on device from either kernels or sycl device functions.

#include "sycl.hpp"

Expand Down