Skip to content

Commit b670ab7

Browse files
committed
recommit 1b978dd [CUDA][HIP][OpenMP] Emit deferred diagnostics by a post-parsing AST travese
Differential Revision: https://reviews.llvm.org/D70172
1 parent 278c00c commit b670ab7

21 files changed

+257
-296
lines changed

clang/include/clang/Sema/ExternalSemaSource.h

+9
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,15 @@ class ExternalSemaSource : public ExternalASTSource {
193193
llvm::MapVector<const FunctionDecl *, std::unique_ptr<LateParsedTemplate>>
194194
&LPTMap) {}
195195

196+
/// Read the set of decls to be checked for deferred diags.
197+
///
198+
/// The external source should append its own potentially emitted function
199+
/// and variable decls which may cause deferred diags. Note that this routine
200+
/// may be invoked multiple times; the external source should take care not to
201+
/// introduce the same declarations repeatedly.
202+
virtual void ReadDeclsToCheckForDeferredDiags(
203+
llvm::SmallVector<Decl *, 4> &Decls) {}
204+
196205
/// \copydoc Sema::CorrectTypo
197206
/// \note LookupKind must correspond to a valid Sema::LookupNameKind
198207
///

clang/include/clang/Sema/MultiplexExternalSemaSource.h

+9
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,15 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
332332
llvm::MapVector<const FunctionDecl *, std::unique_ptr<LateParsedTemplate>>
333333
&LPTMap) override;
334334

335+
/// Read the set of decls to be checked for deferred diags.
336+
///
337+
/// The external source should append its own potentially emitted function
338+
/// and variable decls which may cause deferred diags. Note that this routine
339+
/// may be invoked multiple times; the external source should take care not to
340+
/// introduce the same declarations repeatedly.
341+
void ReadDeclsToCheckForDeferredDiags(
342+
llvm::SmallVector<Decl *, 4> &Decls) override;
343+
335344
/// \copydoc ExternalSemaSource::CorrectTypo
336345
/// \note Returns the first nonempty correction.
337346
TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,

clang/include/clang/Sema/Sema.h

+19-33
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,18 @@ class Sema final {
14921492

14931493
void emitAndClearUnusedLocalTypedefWarnings();
14941494

1495+
private:
1496+
/// Function or variable declarations to be checked for whether the deferred
1497+
/// diagnostics should be emitted.
1498+
SmallVector<Decl *, 4> DeclsToCheckForDeferredDiags;
1499+
1500+
public:
1501+
// Emit all deferred diagnostics.
1502+
void emitDeferredDiags();
1503+
// Emit any deferred diagnostics for FD and erase them from the map in which
1504+
// they're stored.
1505+
void emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack);
1506+
14951507
enum TUFragmentKind {
14961508
/// The global module fragment, between 'module;' and a module-declaration.
14971509
Global,
@@ -3767,7 +3779,8 @@ class Sema final {
37673779
TemplateDiscarded, // Discarded due to uninstantiated templates
37683780
Unknown,
37693781
};
3770-
FunctionEmissionStatus getEmissionStatus(FunctionDecl *Decl);
3782+
FunctionEmissionStatus getEmissionStatus(FunctionDecl *Decl,
3783+
bool Final = false);
37713784

37723785
// Whether the callee should be ignored in CUDA/HIP/OpenMP host/device check.
37733786
bool shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee);
@@ -9767,22 +9780,10 @@ class Sema final {
97679780
/// Pop OpenMP function region for non-capturing function.
97689781
void popOpenMPFunctionRegion(const sema::FunctionScopeInfo *OldFSI);
97699782

9770-
/// Check whether we're allowed to call Callee from the current function.
9771-
void checkOpenMPDeviceFunction(SourceLocation Loc, FunctionDecl *Callee,
9772-
bool CheckForDelayedContext = true);
9773-
9774-
/// Check whether we're allowed to call Callee from the current function.
9775-
void checkOpenMPHostFunction(SourceLocation Loc, FunctionDecl *Callee,
9776-
bool CheckCaller = true);
9777-
97789783
/// Check if the expression is allowed to be used in expressions for the
97799784
/// OpenMP devices.
97809785
void checkOpenMPDeviceExpr(const Expr *E);
97819786

9782-
/// Finishes analysis of the deferred functions calls that may be declared as
9783-
/// host/nohost during device/host compilation.
9784-
void finalizeOpenMPDelayedAnalysis();
9785-
97869787
/// Checks if a type or a declaration is disabled due to the owning extension
97879788
/// being disabled, and emits diagnostic messages if it is disabled.
97889789
/// \param D type or declaration to be checked.
@@ -9973,6 +9974,11 @@ class Sema final {
99739974
void
99749975
checkDeclIsAllowedInOpenMPTarget(Expr *E, Decl *D,
99759976
SourceLocation IdLoc = SourceLocation());
9977+
/// Finishes analysis of the deferred functions calls that may be declared as
9978+
/// host/nohost during device/host compilation.
9979+
void finalizeOpenMPDelayedAnalysis(const FunctionDecl *Caller,
9980+
const FunctionDecl *Callee,
9981+
SourceLocation Loc);
99769982
/// Return true inside OpenMP declare target region.
99779983
bool isInOpenMPDeclareTargetContext() const {
99789984
return DeclareTargetNestingLevel > 0;
@@ -11359,18 +11365,6 @@ class Sema final {
1135911365
/* Caller = */ FunctionDeclAndLoc>
1136011366
DeviceKnownEmittedFns;
1136111367

11362-
/// A partial call graph maintained during CUDA/OpenMP device code compilation
11363-
/// to support deferred diagnostics.
11364-
///
11365-
/// Functions are only added here if, at the time they're considered, they are
11366-
/// not known-emitted. As soon as we discover that a function is
11367-
/// known-emitted, we remove it and everything it transitively calls from this
11368-
/// set and add those functions to DeviceKnownEmittedFns.
11369-
llvm::DenseMap</* Caller = */ CanonicalDeclPtr<FunctionDecl>,
11370-
/* Callees = */ llvm::MapVector<CanonicalDeclPtr<FunctionDecl>,
11371-
SourceLocation>>
11372-
DeviceCallGraph;
11373-
1137411368
/// Diagnostic builder for CUDA/OpenMP devices errors which may or may not be
1137511369
/// deferred.
1137611370
///
@@ -11445,14 +11439,6 @@ class Sema final {
1144511439
llvm::Optional<unsigned> PartialDiagId;
1144611440
};
1144711441

11448-
/// Indicate that this function (and thus everything it transtively calls)
11449-
/// will be codegen'ed, and emit any deferred diagnostics on this function and
11450-
/// its (transitive) callees.
11451-
void markKnownEmitted(
11452-
Sema &S, FunctionDecl *OrigCaller, FunctionDecl *OrigCallee,
11453-
SourceLocation OrigLoc,
11454-
const llvm::function_ref<bool(Sema &, FunctionDecl *)> IsKnownEmitted);
11455-
1145611442
/// Creates a DeviceDiagBuilder that emits the diagnostic if the current context
1145711443
/// is "used as device code".
1145811444
///

clang/include/clang/Serialization/ASTBitCodes.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,10 @@ namespace serialization {
650650
PP_CONDITIONAL_STACK = 62,
651651

652652
/// A table of skipped ranges within the preprocessing record.
653-
PPD_SKIPPED_RANGES = 63
653+
PPD_SKIPPED_RANGES = 63,
654+
655+
/// Record code for the Decls to be checked for deferred diags.
656+
DECLS_TO_CHECK_FOR_DEFERRED_DIAGS = 64,
654657
};
655658

656659
/// Record types used within a source manager block.

clang/include/clang/Serialization/ASTReader.h

+9
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,12 @@ class ASTReader
890890
// A list of late parsed template function data.
891891
SmallVector<uint64_t, 1> LateParsedTemplates;
892892

893+
/// The IDs of all decls to be checked for deferred diags.
894+
///
895+
/// Sema tracks these to emit deferred diags.
896+
SmallVector<uint64_t, 4> DeclsToCheckForDeferredDiags;
897+
898+
893899
public:
894900
struct ImportedSubmodule {
895901
serialization::SubmoduleID ID;
@@ -1983,6 +1989,9 @@ class ASTReader
19831989
void ReadUnusedLocalTypedefNameCandidates(
19841990
llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) override;
19851991

1992+
void ReadDeclsToCheckForDeferredDiags(
1993+
llvm::SmallVector<Decl *, 4> &Decls) override;
1994+
19861995
void ReadReferencedSelectors(
19871996
SmallVectorImpl<std::pair<Selector, SourceLocation>> &Sels) override;
19881997

clang/lib/Sema/MultiplexExternalSemaSource.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,12 @@ void MultiplexExternalSemaSource::ReadExtVectorDecls(
275275
Sources[i]->ReadExtVectorDecls(Decls);
276276
}
277277

278+
void MultiplexExternalSemaSource::ReadDeclsToCheckForDeferredDiags(
279+
llvm::SmallVector<Decl *, 4> &Decls) {
280+
for(size_t i = 0; i < Sources.size(); ++i)
281+
Sources[i]->ReadDeclsToCheckForDeferredDiags(Decls);
282+
}
283+
278284
void MultiplexExternalSemaSource::ReadUnusedLocalTypedefNameCandidates(
279285
llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) {
280286
for(size_t i = 0; i < Sources.size(); ++i)

clang/lib/Sema/Sema.cpp

+96-81
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14+
#include "UsedDeclVisitor.h"
1415
#include "clang/AST/ASTContext.h"
1516
#include "clang/AST/ASTDiagnostic.h"
1617
#include "clang/AST/DeclCXX.h"
@@ -955,9 +956,7 @@ void Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) {
955956
PerformPendingInstantiations();
956957
}
957958

958-
// Finalize analysis of OpenMP-specific constructs.
959-
if (LangOpts.OpenMP)
960-
finalizeOpenMPDelayedAnalysis();
959+
emitDeferredDiags();
961960

962961
assert(LateParsedInstantiations.empty() &&
963962
"end of TU template instantiation should not create more "
@@ -1452,27 +1451,108 @@ static void emitCallStackNotes(Sema &S, FunctionDecl *FD) {
14521451

14531452
// Emit any deferred diagnostics for FD and erase them from the map in which
14541453
// they're stored.
1455-
static void emitDeferredDiags(Sema &S, FunctionDecl *FD, bool ShowCallStack) {
1456-
auto It = S.DeviceDeferredDiags.find(FD);
1457-
if (It == S.DeviceDeferredDiags.end())
1454+
void Sema::emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack) {
1455+
auto It = DeviceDeferredDiags.find(FD);
1456+
if (It == DeviceDeferredDiags.end())
14581457
return;
14591458
bool HasWarningOrError = false;
1459+
bool FirstDiag = true;
14601460
for (PartialDiagnosticAt &PDAt : It->second) {
14611461
const SourceLocation &Loc = PDAt.first;
14621462
const PartialDiagnostic &PD = PDAt.second;
1463-
HasWarningOrError |= S.getDiagnostics().getDiagnosticLevel(
1463+
HasWarningOrError |= getDiagnostics().getDiagnosticLevel(
14641464
PD.getDiagID(), Loc) >= DiagnosticsEngine::Warning;
1465-
DiagnosticBuilder Builder(S.Diags.Report(Loc, PD.getDiagID()));
1466-
Builder.setForceEmit();
1467-
PD.Emit(Builder);
1465+
{
1466+
DiagnosticBuilder Builder(Diags.Report(Loc, PD.getDiagID()));
1467+
Builder.setForceEmit();
1468+
PD.Emit(Builder);
1469+
}
1470+
1471+
// Emit the note on the first diagnostic in case too many diagnostics cause
1472+
// the note not emitted.
1473+
if (FirstDiag && HasWarningOrError && ShowCallStack) {
1474+
emitCallStackNotes(*this, FD);
1475+
FirstDiag = false;
1476+
}
14681477
}
1469-
S.DeviceDeferredDiags.erase(It);
14701478

1471-
// FIXME: Should this be called after every warning/error emitted in the loop
1472-
// above, instead of just once per function? That would be consistent with
1473-
// how we handle immediate errors, but it also seems like a bit much.
1474-
if (HasWarningOrError && ShowCallStack)
1475-
emitCallStackNotes(S, FD);
1479+
}
1480+
1481+
namespace {
1482+
/// Helper class that emits deferred diagnostic messages if an entity directly
1483+
/// or indirectly using the function that causes the deferred diagnostic
1484+
/// messages is known to be emitted.
1485+
class DeferredDiagnosticsEmitter
1486+
: public UsedDeclVisitor<DeferredDiagnosticsEmitter> {
1487+
public:
1488+
typedef UsedDeclVisitor<DeferredDiagnosticsEmitter> Inherited;
1489+
llvm::SmallSet<CanonicalDeclPtr<Decl>, 4> Visited;
1490+
llvm::SmallVector<CanonicalDeclPtr<FunctionDecl>, 4> UseStack;
1491+
bool ShouldEmit;
1492+
unsigned InOMPDeviceContext;
1493+
1494+
DeferredDiagnosticsEmitter(Sema &S)
1495+
: Inherited(S), ShouldEmit(false), InOMPDeviceContext(0) {}
1496+
1497+
void VisitOMPTargetDirective(OMPTargetDirective *Node) {
1498+
++InOMPDeviceContext;
1499+
Inherited::VisitOMPTargetDirective(Node);
1500+
--InOMPDeviceContext;
1501+
}
1502+
1503+
void visitUsedDecl(SourceLocation Loc, Decl *D) {
1504+
if (auto *FD = dyn_cast<FunctionDecl>(D)) {
1505+
FunctionDecl *Caller = UseStack.empty() ? nullptr : UseStack.back();
1506+
auto IsKnownEmitted = S.getEmissionStatus(FD, /*Final=*/true) ==
1507+
Sema::FunctionEmissionStatus::Emitted;
1508+
if (!Caller)
1509+
ShouldEmit = IsKnownEmitted;
1510+
if ((!ShouldEmit && !S.getLangOpts().OpenMP && !Caller) ||
1511+
S.shouldIgnoreInHostDeviceCheck(FD) || Visited.count(D))
1512+
return;
1513+
// Finalize analysis of OpenMP-specific constructs.
1514+
if (Caller && S.LangOpts.OpenMP && UseStack.size() == 1)
1515+
S.finalizeOpenMPDelayedAnalysis(Caller, FD, Loc);
1516+
if (Caller)
1517+
S.DeviceKnownEmittedFns[FD] = {Caller, Loc};
1518+
if (ShouldEmit || InOMPDeviceContext)
1519+
S.emitDeferredDiags(FD, Caller);
1520+
Visited.insert(D);
1521+
UseStack.push_back(FD);
1522+
if (auto *S = FD->getBody()) {
1523+
this->Visit(S);
1524+
}
1525+
UseStack.pop_back();
1526+
Visited.erase(D);
1527+
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
1528+
if (auto *Init = VD->getInit()) {
1529+
auto DevTy = OMPDeclareTargetDeclAttr::getDeviceType(VD);
1530+
bool IsDev = DevTy && (*DevTy == OMPDeclareTargetDeclAttr::DT_NoHost ||
1531+
*DevTy == OMPDeclareTargetDeclAttr::DT_Any);
1532+
if (IsDev)
1533+
++InOMPDeviceContext;
1534+
this->Visit(Init);
1535+
if (IsDev)
1536+
--InOMPDeviceContext;
1537+
}
1538+
} else
1539+
Inherited::visitUsedDecl(Loc, D);
1540+
}
1541+
};
1542+
} // namespace
1543+
1544+
void Sema::emitDeferredDiags() {
1545+
if (ExternalSource)
1546+
ExternalSource->ReadDeclsToCheckForDeferredDiags(
1547+
DeclsToCheckForDeferredDiags);
1548+
1549+
if ((DeviceDeferredDiags.empty() && !LangOpts.OpenMP) ||
1550+
DeclsToCheckForDeferredDiags.empty())
1551+
return;
1552+
1553+
DeferredDiagnosticsEmitter DDE(*this);
1554+
for (auto D : DeclsToCheckForDeferredDiags)
1555+
DDE.visitUsedDecl(SourceLocation(), D);
14761556
}
14771557

14781558
// In CUDA, there are some constructs which may appear in semantically-valid
@@ -1545,71 +1625,6 @@ Sema::DeviceDiagBuilder::~DeviceDiagBuilder() {
15451625
}
15461626
}
15471627

1548-
// Indicate that this function (and thus everything it transtively calls) will
1549-
// be codegen'ed, and emit any deferred diagnostics on this function and its
1550-
// (transitive) callees.
1551-
void Sema::markKnownEmitted(
1552-
Sema &S, FunctionDecl *OrigCaller, FunctionDecl *OrigCallee,
1553-
SourceLocation OrigLoc,
1554-
const llvm::function_ref<bool(Sema &, FunctionDecl *)> IsKnownEmitted) {
1555-
// Nothing to do if we already know that FD is emitted.
1556-
if (IsKnownEmitted(S, OrigCallee)) {
1557-
assert(!S.DeviceCallGraph.count(OrigCallee));
1558-
return;
1559-
}
1560-
1561-
// We've just discovered that OrigCallee is known-emitted. Walk our call
1562-
// graph to see what else we can now discover also must be emitted.
1563-
1564-
struct CallInfo {
1565-
FunctionDecl *Caller;
1566-
FunctionDecl *Callee;
1567-
SourceLocation Loc;
1568-
};
1569-
llvm::SmallVector<CallInfo, 4> Worklist = {{OrigCaller, OrigCallee, OrigLoc}};
1570-
llvm::SmallSet<CanonicalDeclPtr<FunctionDecl>, 4> Seen;
1571-
Seen.insert(OrigCallee);
1572-
while (!Worklist.empty()) {
1573-
CallInfo C = Worklist.pop_back_val();
1574-
assert(!IsKnownEmitted(S, C.Callee) &&
1575-
"Worklist should not contain known-emitted functions.");
1576-
S.DeviceKnownEmittedFns[C.Callee] = {C.Caller, C.Loc};
1577-
emitDeferredDiags(S, C.Callee, C.Caller);
1578-
1579-
// If this is a template instantiation, explore its callgraph as well:
1580-
// Non-dependent calls are part of the template's callgraph, while dependent
1581-
// calls are part of to the instantiation's call graph.
1582-
if (auto *Templ = C.Callee->getPrimaryTemplate()) {
1583-
FunctionDecl *TemplFD = Templ->getAsFunction();
1584-
if (!Seen.count(TemplFD) && !S.DeviceKnownEmittedFns.count(TemplFD)) {
1585-
Seen.insert(TemplFD);
1586-
Worklist.push_back(
1587-
{/* Caller = */ C.Caller, /* Callee = */ TemplFD, C.Loc});
1588-
}
1589-
}
1590-
1591-
// Add all functions called by Callee to our worklist.
1592-
auto CGIt = S.DeviceCallGraph.find(C.Callee);
1593-
if (CGIt == S.DeviceCallGraph.end())
1594-
continue;
1595-
1596-
for (std::pair<CanonicalDeclPtr<FunctionDecl>, SourceLocation> FDLoc :
1597-
CGIt->second) {
1598-
FunctionDecl *NewCallee = FDLoc.first;
1599-
SourceLocation CallLoc = FDLoc.second;
1600-
if (Seen.count(NewCallee) || IsKnownEmitted(S, NewCallee))
1601-
continue;
1602-
Seen.insert(NewCallee);
1603-
Worklist.push_back(
1604-
{/* Caller = */ C.Callee, /* Callee = */ NewCallee, CallLoc});
1605-
}
1606-
1607-
// C.Callee is now known-emitted, so we no longer need to maintain its list
1608-
// of callees in DeviceCallGraph.
1609-
S.DeviceCallGraph.erase(CGIt);
1610-
}
1611-
}
1612-
16131628
Sema::DeviceDiagBuilder Sema::targetDiag(SourceLocation Loc, unsigned DiagID) {
16141629
if (LangOpts.OpenMP)
16151630
return LangOpts.OpenMPIsDevice ? diagIfOpenMPDeviceCode(Loc, DiagID)

0 commit comments

Comments
 (0)