yaxunl updated this revision to Diff 254840.
yaxunl marked 2 inline comments as done.
yaxunl added a comment.
revised by John's comments
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D77028/new/
https://reviews.llvm.org/D77028
Files:
clang/include/clang/Sema/Sema.h
clang/lib/Sema/Sema.cpp
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -1436,63 +1436,87 @@
}
// Print notes showing how we can reach FD starting from an a priori
-// known-callable function.
-static void emitCallStackNotes(Sema &S, FunctionDecl *FD) {
+// known-callable function. If \HasDiags is not null pointer, the flag
+// about whether diagnostics emitted for a function is set.
+static void emitCallStackNotes(
+ Sema &S, FunctionDecl *FD,
+ llvm::DenseMap<CanonicalDeclPtr<FunctionDecl>, bool> *HasDiags = nullptr) {
auto FnIt = S.DeviceKnownEmittedFns.find(FD);
while (FnIt != S.DeviceKnownEmittedFns.end()) {
DiagnosticBuilder Builder(
S.Diags.Report(FnIt->second.Loc, diag::note_called_by));
Builder << FnIt->second.FD;
Builder.setForceEmit();
+ // Set the flag about whether deferred diagnostics directly or indirectly
+ // triggered by a function.
+ if (HasDiags)
+ (*HasDiags)[FnIt->second.FD] = true;
FnIt = S.DeviceKnownEmittedFns.find(FnIt->second.FD);
}
}
-// Emit any deferred diagnostics for FD and erase them from the map in which
-// they're stored.
-void Sema::emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack) {
- auto It = DeviceDeferredDiags.find(FD);
- if (It == DeviceDeferredDiags.end())
- return;
- bool HasWarningOrError = false;
- bool FirstDiag = true;
- for (PartialDiagnosticAt &PDAt : It->second) {
- const SourceLocation &Loc = PDAt.first;
- const PartialDiagnostic &PD = PDAt.second;
- HasWarningOrError |= getDiagnostics().getDiagnosticLevel(
- PD.getDiagID(), Loc) >= DiagnosticsEngine::Warning;
- {
- DiagnosticBuilder Builder(Diags.Report(Loc, PD.getDiagID()));
- Builder.setForceEmit();
- PD.Emit(Builder);
- }
-
- // Emit the note on the first diagnostic in case too many diagnostics cause
- // the note not emitted.
- if (FirstDiag && HasWarningOrError && ShowCallStack) {
- emitCallStackNotes(*this, FD);
- FirstDiag = false;
- }
- }
-
-}
-
namespace {
+
/// Helper class that emits deferred diagnostic messages if an entity directly
/// or indirectly using the function that causes the deferred diagnostic
/// messages is known to be emitted.
+///
+/// During parsing of AST, certain diagnostic messages are recorded as deferred
+/// diagnostics since it is unknown whether the functions containing such
+/// diagnostics will be emitted. A list of potentially emitted functions and
+/// variables that may potentially trigger emission of functions are also
+/// recorded. DeferredDiagnosticsEmitter recursively visits used functions
+/// by each function to emit deferred diagnostics.
+///
+/// During the visit, certain OpenMP directives or initializer of variables
+/// with certain OpenMP attributes will cause subsequent visiting of any
+/// functions enter a state which is called OpenMP device context in this
+/// implementation. The state is exited when the directive or initializer is
+/// exited. This state can change the emission states of subsequent uses
+/// of functions.
+///
+/// Conceptually the functions or variables to be visited form a use graph
+/// where the parent node uses the child node. At any point of the visit,
+/// the tree nodes traversed from the tree root to the current node form a use
+/// stack. The emission state of the current node depends on two factors:
+/// 1. the emission state of the root node
+/// 2. whether the current node is in OpenMP device context
+/// If the function is decided to be emitted, its contained deferred diagnostics
+/// are emitted, together with the information about the use stack.
+///
+/// The flag about whether deferred diagnostics directly or indirectly triggered
+/// by a function is added. A function is visited at least once to add the flag
+/// about whether it triggers diagnostics. For subsequent visits, if the flag
+/// for deferred diagnostics triggered by the function is false, the function
+/// is skipped, since the subtree starting at this node does not trigger any
+/// deferred diagnostics and does not trigger any OpenMP device context,
+/// otherwise the flag cannot be false.
+///
class DeferredDiagnosticsEmitter
: public UsedDeclVisitor<DeferredDiagnosticsEmitter> {
public:
typedef UsedDeclVisitor<DeferredDiagnosticsEmitter> Inherited;
llvm::SmallSet<CanonicalDeclPtr<Decl>, 4> Visited;
llvm::SmallVector<CanonicalDeclPtr<FunctionDecl>, 4> UseStack;
- bool ShouldEmit;
+
+ // Whether deferred diagnostics are triggered directly or indirectly by
+ // a declaration. HasDiags[0] is for the case not in OpenMP device context.
+ // HasDiags[1] is for the case in OpenMP device context. We need two maps
+ // because diagnostics emission may be different depending on whether it
+ // is in OpenMP device context.
+ llvm::DenseMap<CanonicalDeclPtr<FunctionDecl>, bool> HasDiagsMaps[2];
+
+ // Emission state of the root node of the current use graph.
+ bool ShouldEmitRootNode;
+
+ // Current OpenMP device context level. It is initialized to 0 and each
+ // entering of device context increases it by 1 and each exit decreases
+ // it by 1. Non-zero value indicates it is currently in device context.
unsigned InOMPDeviceContext;
DeferredDiagnosticsEmitter(Sema &S)
- : Inherited(S), ShouldEmit(false), InOMPDeviceContext(0) {}
+ : Inherited(S), ShouldEmitRootNode(false), InOMPDeviceContext(0) {}
void VisitOMPTargetDirective(OMPTargetDirective *Node) {
++InOMPDeviceContext;
@@ -1525,21 +1549,26 @@
}
void checkFunc(SourceLocation Loc, FunctionDecl *FD) {
+ auto &HasDiags = HasDiagsMaps[InOMPDeviceContext];
+ auto HasDiagsIt = HasDiags.find(FD);
FunctionDecl *Caller = UseStack.empty() ? nullptr : UseStack.back();
- auto IsKnownEmitted = S.getEmissionStatus(FD, /*Final=*/true) ==
- Sema::FunctionEmissionStatus::Emitted;
- if (!Caller)
- ShouldEmit = IsKnownEmitted;
- if ((!ShouldEmit && !S.getLangOpts().OpenMP && !Caller) ||
+ if ((!ShouldEmitRootNode && !S.getLangOpts().OpenMP && !Caller) ||
S.shouldIgnoreInHostDeviceCheck(FD) || Visited.count(FD))
return;
// Finalize analysis of OpenMP-specific constructs.
if (Caller && S.LangOpts.OpenMP && UseStack.size() == 1)
S.finalizeOpenMPDelayedAnalysis(Caller, FD, Loc);
+ // If the flag for deferred diagnostics for the function is available
+ // and is false, the visit of the function and its used functions can be
+ // skipped since they will not trigger any deferred diagnostics.
+ else if (HasDiagsIt != HasDiags.end() && !HasDiagsIt->second)
+ return;
+ if (HasDiagsIt == HasDiags.end())
+ HasDiags[FD] = false;
if (Caller)
S.DeviceKnownEmittedFns[FD] = {Caller, Loc};
- if (ShouldEmit || InOMPDeviceContext)
- S.emitDeferredDiags(FD, Caller);
+ if (ShouldEmitRootNode || InOMPDeviceContext)
+ emitDeferredDiags(FD, Caller);
Visited.insert(FD);
UseStack.push_back(FD);
if (auto *S = FD->getBody()) {
@@ -1550,11 +1579,43 @@
}
void checkRecordedDecl(Decl *D) {
- if (auto *FD = dyn_cast<FunctionDecl>(D))
+ if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+ ShouldEmitRootNode = S.getEmissionStatus(FD, /*Final=*/true) ==
+ Sema::FunctionEmissionStatus::Emitted;
checkFunc(SourceLocation(), FD);
- else
+ } else
checkVar(cast<VarDecl>(D));
}
+
+ // Emit any deferred diagnostics for FD
+ void emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack) {
+ auto &HasDiags = HasDiagsMaps[InOMPDeviceContext];
+ auto It = S.DeviceDeferredDiags.find(FD);
+ if (It == S.DeviceDeferredDiags.end())
+ return;
+ bool HasWarningOrError = false;
+ bool FirstDiag = true;
+ for (PartialDiagnosticAt &PDAt : It->second) {
+ const SourceLocation &Loc = PDAt.first;
+ const PartialDiagnostic &PD = PDAt.second;
+ HasWarningOrError |=
+ S.getDiagnostics().getDiagnosticLevel(PD.getDiagID(), Loc) >=
+ DiagnosticsEngine::Warning;
+ {
+ DiagnosticBuilder Builder(S.Diags.Report(Loc, PD.getDiagID()));
+ Builder.setForceEmit();
+ PD.Emit(Builder);
+ }
+
+ HasDiags[FD] = true;
+ // Emit the note on the first diagnostic in case too many diagnostics
+ // cause the note not emitted.
+ if (FirstDiag && HasWarningOrError && ShowCallStack) {
+ emitCallStackNotes(S, FD, &HasDiags);
+ FirstDiag = false;
+ }
+ }
+ }
};
} // namespace
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -1501,9 +1501,6 @@
public:
// Emit all deferred diagnostics.
void emitDeferredDiags();
- // Emit any deferred diagnostics for FD and erase them from the map in which
- // they're stored.
- void emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack);
enum TUFragmentKind {
/// The global module fragment, between 'module;' and a module-declaration.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits