Skip to content

Commit db7cb6f

Browse files
DenisBakhvalovErich Keane
andauthored
[Sema] Introduce deferred diagnostics for coexisting languages (#3395)
Multiple offloading languages can coexist in the same translation unit, while having different semantics and thus require different diagnostics. The same construct in the device code may be valid for one language but invalid for another, while coexisting in a single translation unit. One such example is SYCL/ESIMD, which have difference in semantic analysis of globals (will be uploaded in consequent patches). This patch introduces a generic mechanism for distinguishing deferred diagnostics for coexisting languages. The idea of the mechanism is to 1) extend a deferred diagnostic to store a 'reason' for creating it and 2) keep track of the funciton context while emitting a deferred diagnostic. Example: when a certain expression is allowed only for ESIMD and is forbidden for SYCL, we store in the diagnostic itself that we will emit it only for SYCL code. Later when we traverse a call graph of to-codegen functions we keep track of the function context, which is determined by the root function. If the context matches the reason of saving the diagnostic, we emit it, otherwise not. Co-authored-by: Erich Keane <[email protected]>
1 parent f361802 commit db7cb6f

File tree

10 files changed

+244
-42
lines changed

10 files changed

+244
-42
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11214,6 +11214,8 @@ def err_ext_int_max_size : Error<"%select{signed|unsigned}0 _ExtInt of bit "
1121411214
"sizes greater than %1 not supported">;
1121511215
def err_esimd_glob_cant_init : Error<
1121611216
"SYCL explicit SIMD does not permit private global variable to have an initializer">;
11217+
def err_esimd_global_in_sycl_context : Error<
11218+
"ESIMD globals cannot be used in a SYCL context">;
1121711219

1121811220
def err_sycl_kernel_incorrectly_named : Error<"%0 is an invalid kernel name type">;
1121911221
def note_invalid_type_in_sycl_kernel : Note<

clang/include/clang/Sema/Sema.h

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,6 +1769,34 @@ class Sema final {
17691769
}
17701770
};
17711771

1772+
/// Bitmask to contain the list of reasons a single diagnostic should be
1773+
/// emitted, based on its language. This permits multiple offload systems
1774+
/// to coexist in the same translation unit.
1775+
enum class DeviceDiagnosticReason {
1776+
/// Diagnostic doesn't apply to anything. Included for completeness, but
1777+
/// should make this a no-op.
1778+
None = 0,
1779+
/// OpenMP specific diagnostic.
1780+
OmpDevice = 1 << 0,
1781+
OmpHost = 1 << 1,
1782+
OmpAll = OmpDevice | OmpHost,
1783+
/// CUDA specific diagnostics.
1784+
CudaDevice = 1 << 2,
1785+
CudaHost = 1 << 3,
1786+
CudaAll = CudaDevice | CudaHost,
1787+
/// SYCL specific diagnostic.
1788+
Sycl = 1 << 4,
1789+
/// ESIMD specific diagnostic.
1790+
Esimd = 1 << 5,
1791+
/// A flag representing 'all'. This can be used to avoid the check
1792+
/// all-together and make this behave as it did before the
1793+
/// DiagnosticReason was added (that is, unconditionally emit).
1794+
/// Note: This needs to be updated if any flags above are added.
1795+
All = OmpAll | CudaAll | Sycl | Esimd,
1796+
1797+
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/All)
1798+
};
1799+
17721800
/// A generic diagnostic builder for errors which may or may not be deferred.
17731801
///
17741802
/// In CUDA, there exist constructs (e.g. variable-length arrays, try/catch)
@@ -1801,7 +1829,7 @@ class Sema final {
18011829
};
18021830

18031831
SemaDiagnosticBuilder(Kind K, SourceLocation Loc, unsigned DiagID,
1804-
FunctionDecl *Fn, Sema &S);
1832+
FunctionDecl *Fn, Sema &S, DeviceDiagnosticReason R);
18051833
SemaDiagnosticBuilder(SemaDiagnosticBuilder &&D);
18061834
SemaDiagnosticBuilder(const SemaDiagnosticBuilder &) = default;
18071835
~SemaDiagnosticBuilder();
@@ -1826,7 +1854,9 @@ class Sema final {
18261854
if (Diag.ImmediateDiag.hasValue())
18271855
*Diag.ImmediateDiag << Value;
18281856
else if (Diag.PartialDiagId.hasValue())
1829-
Diag.S.DeviceDeferredDiags[Diag.Fn][*Diag.PartialDiagId].second
1857+
Diag.S.DeviceDeferredDiags[Diag.Fn][*Diag.PartialDiagId]
1858+
.getDiag()
1859+
.second
18301860
<< Value;
18311861
return Diag;
18321862
}
@@ -1840,7 +1870,8 @@ class Sema final {
18401870
if (ImmediateDiag.hasValue())
18411871
*ImmediateDiag << std::move(V);
18421872
else if (PartialDiagId.hasValue())
1843-
S.DeviceDeferredDiags[Fn][*PartialDiagId].second << std::move(V);
1873+
S.DeviceDeferredDiags[Fn][*PartialDiagId].getDiag().second
1874+
<< std::move(V);
18441875
return *this;
18451876
}
18461877

@@ -1849,15 +1880,18 @@ class Sema final {
18491880
if (Diag.ImmediateDiag.hasValue())
18501881
PD.Emit(*Diag.ImmediateDiag);
18511882
else if (Diag.PartialDiagId.hasValue())
1852-
Diag.S.DeviceDeferredDiags[Diag.Fn][*Diag.PartialDiagId].second = PD;
1883+
Diag.S.DeviceDeferredDiags[Diag.Fn][*Diag.PartialDiagId]
1884+
.getDiag()
1885+
.second = PD;
18531886
return Diag;
18541887
}
18551888

18561889
void AddFixItHint(const FixItHint &Hint) const {
18571890
if (ImmediateDiag.hasValue())
18581891
ImmediateDiag->AddFixItHint(Hint);
18591892
else if (PartialDiagId.hasValue())
1860-
S.DeviceDeferredDiags[Fn][*PartialDiagId].second.AddFixItHint(Hint);
1893+
S.DeviceDeferredDiags[Fn][*PartialDiagId].getDiag().second.AddFixItHint(
1894+
Hint);
18611895
}
18621896

18631897
friend ExprResult ExprError(const SemaDiagnosticBuilder &) {
@@ -4281,6 +4315,7 @@ class Sema final {
42814315
};
42824316
FunctionEmissionStatus getEmissionStatus(FunctionDecl *Decl,
42834317
bool Final = false);
4318+
DeviceDiagnosticReason getEmissionReason(const FunctionDecl *Decl);
42844319

42854320
// Whether the callee should be ignored in CUDA/HIP/OpenMP host/device check.
42864321
bool shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee);
@@ -12157,11 +12192,25 @@ class Sema final {
1215712192
/// before incrementing, so you can emit an error.
1215812193
bool PopForceCUDAHostDevice();
1215912194

12195+
class DeviceDeferredDiagnostic {
12196+
public:
12197+
DeviceDeferredDiagnostic(SourceLocation SL, const PartialDiagnostic &PD,
12198+
DeviceDiagnosticReason R)
12199+
: Diagnostic(SL, PD), Reason(R) {}
12200+
12201+
PartialDiagnosticAt &getDiag() { return Diagnostic; }
12202+
DeviceDiagnosticReason getReason() const { return Reason; }
12203+
12204+
private:
12205+
PartialDiagnosticAt Diagnostic;
12206+
DeviceDiagnosticReason Reason;
12207+
};
12208+
1216012209
/// Diagnostics that are emitted only if we discover that the given function
1216112210
/// must be codegen'ed. Because handling these correctly adds overhead to
1216212211
/// compilation, this is currently only enabled for CUDA compilations.
1216312212
llvm::DenseMap<CanonicalDeclPtr<FunctionDecl>,
12164-
std::vector<PartialDiagnosticAt>>
12213+
std::vector<DeviceDeferredDiagnostic>>
1216512214
DeviceDeferredDiags;
1216612215

1216712216
/// A pair of a canonical FunctionDecl and a SourceLocation. When used as the
@@ -13124,8 +13173,10 @@ class Sema final {
1312413173
/// if (!S.Context.getTargetInfo().hasFloat128Type() &&
1312513174
/// S.getLangOpts().SYCLIsDevice)
1312613175
/// SYCLDiagIfDeviceCode(Loc, diag::err_type_unsupported) << "__float128";
13127-
SemaDiagnosticBuilder SYCLDiagIfDeviceCode(SourceLocation Loc,
13128-
unsigned DiagID);
13176+
SemaDiagnosticBuilder SYCLDiagIfDeviceCode(
13177+
SourceLocation Loc, unsigned DiagID,
13178+
DeviceDiagnosticReason Reason = DeviceDiagnosticReason::Sycl |
13179+
DeviceDiagnosticReason::Esimd);
1312913180

1313013181
/// Check whether we're allowed to call Callee from the current context.
1313113182
///

clang/lib/Sema/Sema.cpp

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,7 +1530,8 @@ bool Sema::hasUncompilableErrorOccurred() const {
15301530
if (Loc == DeviceDeferredDiags.end())
15311531
return false;
15321532
for (auto PDAt : Loc->second) {
1533-
if (DiagnosticIDs::isDefaultMappingAsError(PDAt.second.getDiagID()))
1533+
if (DiagnosticIDs::isDefaultMappingAsError(
1534+
PDAt.getDiag().second.getDiagID()))
15341535
return true;
15351536
}
15361537
return false;
@@ -1600,6 +1601,8 @@ class DeferredDiagnosticsEmitter
16001601
// Emission state of the root node of the current use graph.
16011602
bool ShouldEmitRootNode;
16021603

1604+
Sema::DeviceDiagnosticReason RootReason = Sema::DeviceDiagnosticReason::All;
1605+
16031606
// Current OpenMP device context level. It is initialized to 0 and each
16041607
// entering of device context increases it by 1 and each exit decreases
16051608
// it by 1. Non-zero value indicates it is currently in device context.
@@ -1617,10 +1620,20 @@ class DeferredDiagnosticsEmitter
16171620
void visitUsedDecl(SourceLocation Loc, Decl *D) {
16181621
if (isa<VarDecl>(D))
16191622
return;
1620-
if (auto *FD = dyn_cast<FunctionDecl>(D))
1623+
if (auto *FD = dyn_cast<FunctionDecl>(D)) {
1624+
Sema::DeviceDiagnosticReason SaveReason = RootReason;
1625+
// Allow switching context from SYCL to ESIMD. Switching back is not
1626+
// allowed. I.e., once we entered ESIMD code we stay there until we exit
1627+
// the subgraph.
1628+
if ((RootReason == Sema::DeviceDiagnosticReason::Sycl) &&
1629+
(S.getEmissionReason(FD) == Sema::DeviceDiagnosticReason::Esimd))
1630+
RootReason = Sema::DeviceDiagnosticReason::Esimd;
16211631
checkFunc(Loc, FD);
1622-
else
1632+
// Restore the context
1633+
RootReason = SaveReason;
1634+
} else {
16231635
Inherited::visitUsedDecl(Loc, D);
1636+
}
16241637
}
16251638

16261639
void checkVar(VarDecl *VD) {
@@ -1675,9 +1688,13 @@ class DeferredDiagnosticsEmitter
16751688
if (auto *FD = dyn_cast<FunctionDecl>(D)) {
16761689
ShouldEmitRootNode = S.getEmissionStatus(FD, /*Final=*/true) ==
16771690
Sema::FunctionEmissionStatus::Emitted;
1691+
RootReason = S.getEmissionReason(FD);
16781692
checkFunc(SourceLocation(), FD);
1679-
} else
1693+
} else {
1694+
// Global VarDecls don't really have a reason, so set this to 'ALL'.
1695+
RootReason = Sema::DeviceDiagnosticReason::All;
16801696
checkVar(cast<VarDecl>(D));
1697+
}
16811698
}
16821699

16831700
// Emit any deferred diagnostics for FD
@@ -1687,15 +1704,22 @@ class DeferredDiagnosticsEmitter
16871704
return;
16881705
bool HasWarningOrError = false;
16891706
bool FirstDiag = true;
1690-
for (PartialDiagnosticAt &PDAt : It->second) {
1707+
for (Sema::DeviceDeferredDiagnostic &D : It->second) {
16911708
// Respect error limit.
16921709
if (S.Diags.hasFatalErrorOccurred())
16931710
return;
1694-
const SourceLocation &Loc = PDAt.first;
1695-
const PartialDiagnostic &PD = PDAt.second;
1711+
const SourceLocation &Loc = D.getDiag().first;
1712+
const PartialDiagnostic &PD = D.getDiag().second;
1713+
Sema::DeviceDiagnosticReason Reason = D.getReason();
16961714
HasWarningOrError |=
16971715
S.getDiagnostics().getDiagnosticLevel(PD.getDiagID(), Loc) >=
16981716
DiagnosticsEngine::Warning;
1717+
1718+
// If the diagnostic doesn't apply to this call graph, skip this
1719+
// diagnostic.
1720+
if ((RootReason & Reason) == Sema::DeviceDiagnosticReason::None)
1721+
continue;
1722+
16991723
{
17001724
DiagnosticBuilder Builder(S.Diags.Report(Loc, PD.getDiagID()));
17011725
PD.Emit(Builder);
@@ -1752,7 +1776,8 @@ void Sema::emitDeferredDiags() {
17521776

17531777
Sema::SemaDiagnosticBuilder::SemaDiagnosticBuilder(Kind K, SourceLocation Loc,
17541778
unsigned DiagID,
1755-
FunctionDecl *Fn, Sema &S)
1779+
FunctionDecl *Fn, Sema &S,
1780+
DeviceDiagnosticReason R)
17561781
: S(S), Loc(Loc), DiagID(DiagID), Fn(Fn),
17571782
ShowCallStack(K == K_ImmediateWithCallStack || K == K_Deferred) {
17581783
switch (K) {
@@ -1767,7 +1792,7 @@ Sema::SemaDiagnosticBuilder::SemaDiagnosticBuilder(Kind K, SourceLocation Loc,
17671792
assert(Fn && "Must have a function to attach the deferred diag to.");
17681793
auto &Diags = S.DeviceDeferredDiags[Fn];
17691794
PartialDiagId.emplace(Diags.size());
1770-
Diags.emplace_back(Loc, S.PDiag(DiagID));
1795+
Diags.emplace_back(Loc, S.PDiag(DiagID), R);
17711796
break;
17721797
}
17731798
}
@@ -1811,7 +1836,7 @@ Sema::targetDiag(SourceLocation Loc, unsigned DiagID, FunctionDecl *FD) {
18111836
return SYCLDiagIfDeviceCode(Loc, DiagID);
18121837

18131838
return SemaDiagnosticBuilder(SemaDiagnosticBuilder::K_Immediate, Loc, DiagID,
1814-
FD, *this);
1839+
FD, *this, DeviceDiagnosticReason::All);
18151840
}
18161841

18171842
Sema::SemaDiagnosticBuilder Sema::Diag(SourceLocation Loc, unsigned DiagID,
@@ -1827,7 +1852,8 @@ Sema::SemaDiagnosticBuilder Sema::Diag(SourceLocation Loc, unsigned DiagID,
18271852
if (!ShouldDefer) {
18281853
SetIsLastErrorImmediate(true);
18291854
return SemaDiagnosticBuilder(SemaDiagnosticBuilder::K_Immediate, Loc,
1830-
DiagID, getCurFunctionDecl(), *this);
1855+
DiagID, getCurFunctionDecl(), *this,
1856+
DeviceDiagnosticReason::All);
18311857
}
18321858

18331859
SemaDiagnosticBuilder DB = getLangOpts().CUDAIsDevice

clang/lib/Sema/SemaCUDA.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,8 @@ Sema::SemaDiagnosticBuilder Sema::CUDADiagIfDeviceCode(SourceLocation Loc,
669669
}
670670
}();
671671
return SemaDiagnosticBuilder(DiagKind, Loc, DiagID,
672-
dyn_cast<FunctionDecl>(CurContext), *this);
672+
dyn_cast<FunctionDecl>(CurContext), *this,
673+
DeviceDiagnosticReason::CudaDevice);
673674
}
674675

675676
Sema::SemaDiagnosticBuilder Sema::CUDADiagIfHostCode(SourceLocation Loc,
@@ -698,7 +699,8 @@ Sema::SemaDiagnosticBuilder Sema::CUDADiagIfHostCode(SourceLocation Loc,
698699
}
699700
}();
700701
return SemaDiagnosticBuilder(DiagKind, Loc, DiagID,
701-
dyn_cast<FunctionDecl>(CurContext), *this);
702+
dyn_cast<FunctionDecl>(CurContext), *this,
703+
DeviceDiagnosticReason::CudaHost);
702704
}
703705

704706
bool Sema::CheckCUDACall(SourceLocation Loc, FunctionDecl *Callee) {
@@ -746,12 +748,14 @@ bool Sema::CheckCUDACall(SourceLocation Loc, FunctionDecl *Callee) {
746748
if (!LocsWithCUDACallDiags.insert({Caller, Loc}).second)
747749
return true;
748750

749-
SemaDiagnosticBuilder(DiagKind, Loc, diag::err_ref_bad_target, Caller, *this)
751+
SemaDiagnosticBuilder(DiagKind, Loc, diag::err_ref_bad_target, Caller, *this,
752+
DeviceDiagnosticReason::CudaAll)
750753
<< IdentifyCUDATarget(Callee) << /*function*/ 0 << Callee
751754
<< IdentifyCUDATarget(Caller);
752755
if (!Callee->getBuiltinID())
753756
SemaDiagnosticBuilder(DiagKind, Callee->getLocation(),
754-
diag::note_previous_decl, Caller, *this)
757+
diag::note_previous_decl, Caller, *this,
758+
DeviceDiagnosticReason::CudaAll)
755759
<< Callee;
756760
return DiagKind != SemaDiagnosticBuilder::K_Immediate &&
757761
DiagKind != SemaDiagnosticBuilder::K_ImmediateWithCallStack;
@@ -794,11 +798,13 @@ void Sema::CUDACheckLambdaCapture(CXXMethodDecl *Callee,
794798
auto DiagKind = SemaDiagnosticBuilder::K_Deferred;
795799
if (Capture.isVariableCapture()) {
796800
SemaDiagnosticBuilder(DiagKind, Capture.getLocation(),
797-
diag::err_capture_bad_target, Callee, *this)
801+
diag::err_capture_bad_target, Callee, *this,
802+
DeviceDiagnosticReason::CudaAll)
798803
<< Capture.getVariable();
799804
} else if (Capture.isThisCapture()) {
800805
SemaDiagnosticBuilder(DiagKind, Capture.getLocation(),
801-
diag::err_capture_bad_target_this_ptr, Callee, *this);
806+
diag::err_capture_bad_target_this_ptr, Callee, *this,
807+
DeviceDiagnosticReason::CudaAll);
802808
}
803809
return;
804810
}

clang/lib/Sema/SemaDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18434,6 +18434,15 @@ Decl *Sema::getObjCDeclContext() const {
1843418434
return (dyn_cast_or_null<ObjCContainerDecl>(CurContext));
1843518435
}
1843618436

18437+
Sema::DeviceDiagnosticReason Sema::getEmissionReason(const FunctionDecl *FD) {
18438+
if (FD->hasAttr<SYCLSimdAttr>())
18439+
return Sema::DeviceDiagnosticReason::Esimd;
18440+
else if (FD->hasAttr<SYCLDeviceAttr>() || FD->hasAttr<SYCLKernelAttr>())
18441+
return Sema::DeviceDiagnosticReason::Sycl;
18442+
// FIXME: Figure out the logic for OMP and CUDA.
18443+
return Sema::DeviceDiagnosticReason::All;
18444+
}
18445+
1843718446
Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD,
1843818447
bool Final) {
1843918448
assert(FD && "Expected non-null FunctionDecl");

clang/lib/Sema/SemaExpr.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,14 +219,21 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
219219
bool IsRuntimeEvaluated =
220220
ExprEvalContexts.empty() ||
221221
(!isUnevaluatedContext() && !isConstantEvaluated());
222+
bool IsEsimdPrivateGlobal = isSYCLEsimdPrivateGlobal(VD);
222223
if (IsRuntimeEvaluated && !IsConst && VD->getStorageClass() == SC_Static)
223224
SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict)
224225
<< Sema::KernelNonConstStaticDataVariable;
225226
// Non-const globals are allowed for SYCL explicit SIMD.
226-
else if (IsRuntimeEvaluated && !isSYCLEsimdPrivateGlobal(VD) &&
227-
!IsConst && VD->hasGlobalStorage() && !isa<ParmVarDecl>(VD))
227+
else if (IsRuntimeEvaluated && !IsEsimdPrivateGlobal && !IsConst &&
228+
VD->hasGlobalStorage())
228229
SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict)
229230
<< Sema::KernelGlobalVariable;
231+
// ESIMD globals cannot be used in a SYCL context.
232+
else if (IsRuntimeEvaluated && IsEsimdPrivateGlobal &&
233+
VD->hasGlobalStorage())
234+
SYCLDiagIfDeviceCode(*Locs.begin(),
235+
diag::err_esimd_global_in_sycl_context,
236+
Sema::DeviceDiagnosticReason::Sycl);
230237
// Disallow const statics and globals that are not zero-initialized
231238
// or constant-initialized.
232239
else if (IsRuntimeEvaluated && IsConst && VD->hasGlobalStorage() &&

clang/lib/Sema/SemaOpenMP.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,7 +1931,8 @@ Sema::SemaDiagnosticBuilder Sema::diagIfOpenMPDeviceCode(SourceLocation Loc,
19311931
}
19321932
}
19331933

1934-
return SemaDiagnosticBuilder(Kind, Loc, DiagID, FD, *this);
1934+
return SemaDiagnosticBuilder(Kind, Loc, DiagID, FD, *this,
1935+
DeviceDiagnosticReason::OmpDevice);
19351936
}
19361937

19371938
Sema::SemaDiagnosticBuilder Sema::diagIfOpenMPHostCode(SourceLocation Loc,
@@ -1958,7 +1959,8 @@ Sema::SemaDiagnosticBuilder Sema::diagIfOpenMPHostCode(SourceLocation Loc,
19581959
}
19591960
}
19601961

1961-
return SemaDiagnosticBuilder(Kind, Loc, DiagID, FD, *this);
1962+
return SemaDiagnosticBuilder(Kind, Loc, DiagID, FD, *this,
1963+
DeviceDiagnosticReason::OmpHost);
19621964
}
19631965

19641966
static OpenMPDefaultmapClauseKind

0 commit comments

Comments
 (0)