Skip to content

Commit bcadb1f

Browse files
committed
Revert "[CUDA][HIP][OpenMP] Emit deferred diagnostics by a post-parsing AST travese"
This reverts commit 1b978dd.
1 parent 94a4ca4 commit bcadb1f

13 files changed

+349
-201
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,12 +1464,6 @@ class Sema final {
14641464

14651465
void emitAndClearUnusedLocalTypedefWarnings();
14661466

1467-
// Emit all deferred diagnostics.
1468-
void emitDeferredDiags();
1469-
// Emit any deferred diagnostics for FD and erase them from the map in which
1470-
// they're stored.
1471-
void emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack);
1472-
14731467
enum TUFragmentKind {
14741468
/// The global module fragment, between 'module;' and a module-declaration.
14751469
Global,
@@ -3689,8 +3683,7 @@ class Sema final {
36893683
TemplateDiscarded, // Discarded due to uninstantiated templates
36903684
Unknown,
36913685
};
3692-
FunctionEmissionStatus getEmissionStatus(FunctionDecl *Decl,
3693-
bool Final = false);
3686+
FunctionEmissionStatus getEmissionStatus(FunctionDecl *Decl);
36943687

36953688
// Whether the callee should be ignored in CUDA/HIP/OpenMP host/device check.
36963689
bool shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee);
@@ -9684,10 +9677,22 @@ class Sema final {
96849677
/// Pop OpenMP function region for non-capturing function.
96859678
void popOpenMPFunctionRegion(const sema::FunctionScopeInfo *OldFSI);
96869679

9680+
/// Check whether we're allowed to call Callee from the current function.
9681+
void checkOpenMPDeviceFunction(SourceLocation Loc, FunctionDecl *Callee,
9682+
bool CheckForDelayedContext = true);
9683+
9684+
/// Check whether we're allowed to call Callee from the current function.
9685+
void checkOpenMPHostFunction(SourceLocation Loc, FunctionDecl *Callee,
9686+
bool CheckCaller = true);
9687+
96879688
/// Check if the expression is allowed to be used in expressions for the
96889689
/// OpenMP devices.
96899690
void checkOpenMPDeviceExpr(const Expr *E);
96909691

9692+
/// Finishes analysis of the deferred functions calls that may be declared as
9693+
/// host/nohost during device/host compilation.
9694+
void finalizeOpenMPDelayedAnalysis();
9695+
96919696
/// Checks if a type or a declaration is disabled due to the owning extension
96929697
/// being disabled, and emits diagnostic messages if it is disabled.
96939698
/// \param D type or declaration to be checked.
@@ -9870,11 +9875,6 @@ class Sema final {
98709875
void
98719876
checkDeclIsAllowedInOpenMPTarget(Expr *E, Decl *D,
98729877
SourceLocation IdLoc = SourceLocation());
9873-
/// Finishes analysis of the deferred functions calls that may be declared as
9874-
/// host/nohost during device/host compilation.
9875-
void finalizeOpenMPDelayedAnalysis(const FunctionDecl *Caller,
9876-
const FunctionDecl *Callee,
9877-
SourceLocation Loc);
98789878
/// Return true inside OpenMP declare target region.
98799879
bool isInOpenMPDeclareTargetContext() const {
98809880
return DeclareTargetNestingLevel > 0;
@@ -11223,6 +11223,18 @@ class Sema final {
1122311223
/* Caller = */ FunctionDeclAndLoc>
1122411224
DeviceKnownEmittedFns;
1122511225

11226+
/// A partial call graph maintained during CUDA/OpenMP device code compilation
11227+
/// to support deferred diagnostics.
11228+
///
11229+
/// Functions are only added here if, at the time they're considered, they are
11230+
/// not known-emitted. As soon as we discover that a function is
11231+
/// known-emitted, we remove it and everything it transitively calls from this
11232+
/// set and add those functions to DeviceKnownEmittedFns.
11233+
llvm::DenseMap</* Caller = */ CanonicalDeclPtr<FunctionDecl>,
11234+
/* Callees = */ llvm::MapVector<CanonicalDeclPtr<FunctionDecl>,
11235+
SourceLocation>>
11236+
DeviceCallGraph;
11237+
1122611238
/// Diagnostic builder for CUDA/OpenMP devices errors which may or may not be
1122711239
/// deferred.
1122811240
///
@@ -11297,6 +11309,14 @@ class Sema final {
1129711309
llvm::Optional<unsigned> PartialDiagId;
1129811310
};
1129911311

11312+
/// Indicate that this function (and thus everything it transtively calls)
11313+
/// will be codegen'ed, and emit any deferred diagnostics on this function and
11314+
/// its (transitive) callees.
11315+
void markKnownEmitted(
11316+
Sema &S, FunctionDecl *OrigCaller, FunctionDecl *OrigCallee,
11317+
SourceLocation OrigLoc,
11318+
const llvm::function_ref<bool(Sema &, FunctionDecl *)> IsKnownEmitted);
11319+
1130011320
/// Creates a DeviceDiagBuilder that emits the diagnostic if the current context
1130111321
/// is "used as device code".
1130211322
///

clang/lib/Sema/Sema.cpp

Lines changed: 75 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14-
#include "UsedDeclVisitor.h"
1514
#include "clang/AST/ASTContext.h"
1615
#include "clang/AST/ASTDiagnostic.h"
1716
#include "clang/AST/DeclCXX.h"
@@ -955,7 +954,9 @@ void Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) {
955954
PerformPendingInstantiations();
956955
}
957956

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

960961
assert(LateParsedInstantiations.empty() &&
961962
"end of TU template instantiation should not create more "
@@ -1450,128 +1451,27 @@ static void emitCallStackNotes(Sema &S, FunctionDecl *FD) {
14501451

14511452
// Emit any deferred diagnostics for FD and erase them from the map in which
14521453
// they're stored.
1453-
void Sema::emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack) {
1454-
auto It = DeviceDeferredDiags.find(FD);
1455-
if (It == DeviceDeferredDiags.end())
1454+
static void emitDeferredDiags(Sema &S, FunctionDecl *FD, bool ShowCallStack) {
1455+
auto It = S.DeviceDeferredDiags.find(FD);
1456+
if (It == S.DeviceDeferredDiags.end())
14561457
return;
14571458
bool HasWarningOrError = false;
14581459
for (PartialDiagnosticAt &PDAt : It->second) {
14591460
const SourceLocation &Loc = PDAt.first;
14601461
const PartialDiagnostic &PD = PDAt.second;
1461-
HasWarningOrError |= getDiagnostics().getDiagnosticLevel(
1462+
HasWarningOrError |= S.getDiagnostics().getDiagnosticLevel(
14621463
PD.getDiagID(), Loc) >= DiagnosticsEngine::Warning;
1463-
DiagnosticBuilder Builder(Diags.Report(Loc, PD.getDiagID()));
1464+
DiagnosticBuilder Builder(S.Diags.Report(Loc, PD.getDiagID()));
14641465
Builder.setForceEmit();
14651466
PD.Emit(Builder);
14661467
}
1468+
S.DeviceDeferredDiags.erase(It);
14671469

14681470
// FIXME: Should this be called after every warning/error emitted in the loop
14691471
// above, instead of just once per function? That would be consistent with
14701472
// how we handle immediate errors, but it also seems like a bit much.
14711473
if (HasWarningOrError && ShowCallStack)
1472-
emitCallStackNotes(*this, FD);
1473-
}
1474-
1475-
namespace {
1476-
/// Helper class that emits deferred diagnostic messages if an entity directly
1477-
/// or indirectly using the function that causes the deferred diagnostic
1478-
/// messages is known to be emitted.
1479-
class DeferredDiagnosticsEmitter
1480-
: public UsedDeclVisitor<DeferredDiagnosticsEmitter> {
1481-
public:
1482-
typedef UsedDeclVisitor<DeferredDiagnosticsEmitter> Inherited;
1483-
llvm::SmallSet<CanonicalDeclPtr<Decl>, 4> Visited;
1484-
llvm::SmallVector<CanonicalDeclPtr<FunctionDecl>, 4> UseStack;
1485-
bool ShouldEmit;
1486-
unsigned InOMPDeviceContext;
1487-
1488-
DeferredDiagnosticsEmitter(Sema &S)
1489-
: Inherited(S), ShouldEmit(false), InOMPDeviceContext(0) {}
1490-
1491-
void VisitDeclRefExpr(DeclRefExpr *E) {
1492-
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl())) {
1493-
visitUsedDecl(E->getLocation(), FD);
1494-
}
1495-
}
1496-
1497-
void VisitMemberExpr(MemberExpr *E) {
1498-
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getMemberDecl()))
1499-
visitUsedDecl(E->getMemberLoc(), FD);
1500-
}
1501-
1502-
void VisitOMPTargetDirective(OMPTargetDirective *Node) {
1503-
++InOMPDeviceContext;
1504-
Inherited::VisitOMPTargetDirective(Node);
1505-
--InOMPDeviceContext;
1506-
}
1507-
1508-
void VisitCapturedStmt(CapturedStmt *Node) {
1509-
visitUsedDecl(Node->getBeginLoc(), Node->getCapturedDecl());
1510-
Inherited::VisitCapturedStmt(Node);
1511-
}
1512-
1513-
void visitUsedDecl(SourceLocation Loc, Decl *D) {
1514-
if (auto *TD = dyn_cast<TranslationUnitDecl>(D)) {
1515-
for (auto *DD : TD->decls()) {
1516-
visitUsedDecl(Loc, DD);
1517-
}
1518-
} else if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) {
1519-
for (auto *DD : FTD->specializations()) {
1520-
visitUsedDecl(Loc, DD);
1521-
}
1522-
} else if (auto *FD = dyn_cast<FunctionDecl>(D)) {
1523-
FunctionDecl *Caller = UseStack.empty() ? nullptr : UseStack.back();
1524-
auto IsKnownEmitted = S.getEmissionStatus(FD, /*Final=*/true) ==
1525-
Sema::FunctionEmissionStatus::Emitted;
1526-
if (!Caller)
1527-
ShouldEmit = IsKnownEmitted;
1528-
if ((!ShouldEmit && !S.getLangOpts().OpenMP && !Caller) ||
1529-
S.shouldIgnoreInHostDeviceCheck(FD) || Visited.count(D))
1530-
return;
1531-
// Finalize analysis of OpenMP-specific constructs.
1532-
if (Caller && S.LangOpts.OpenMP && UseStack.size() == 1)
1533-
S.finalizeOpenMPDelayedAnalysis(Caller, FD, Loc);
1534-
if (Caller)
1535-
S.DeviceKnownEmittedFns[FD] = {Caller, Loc};
1536-
if (ShouldEmit || InOMPDeviceContext)
1537-
S.emitDeferredDiags(FD, Caller);
1538-
Visited.insert(D);
1539-
UseStack.push_back(FD);
1540-
if (auto *S = FD->getBody()) {
1541-
this->Visit(S);
1542-
}
1543-
UseStack.pop_back();
1544-
Visited.erase(D);
1545-
} else if (auto *RD = dyn_cast<RecordDecl>(D)) {
1546-
for (auto *DD : RD->decls()) {
1547-
visitUsedDecl(Loc, DD);
1548-
}
1549-
} else if (auto *CD = dyn_cast<CapturedDecl>(D)) {
1550-
if (auto *S = CD->getBody()) {
1551-
this->Visit(S);
1552-
}
1553-
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
1554-
if (auto *Init = VD->getInit()) {
1555-
auto DevTy = OMPDeclareTargetDeclAttr::getDeviceType(VD);
1556-
bool IsDev = DevTy && (*DevTy == OMPDeclareTargetDeclAttr::DT_NoHost ||
1557-
*DevTy == OMPDeclareTargetDeclAttr::DT_Any);
1558-
if (IsDev)
1559-
++InOMPDeviceContext;
1560-
this->Visit(Init);
1561-
if (IsDev)
1562-
--InOMPDeviceContext;
1563-
}
1564-
}
1565-
}
1566-
};
1567-
} // namespace
1568-
1569-
void Sema::emitDeferredDiags() {
1570-
if (DeviceDeferredDiags.empty() && !LangOpts.OpenMP)
1571-
return;
1572-
1573-
DeferredDiagnosticsEmitter(*this).visitUsedDecl(
1574-
SourceLocation(), Context.getTranslationUnitDecl());
1474+
emitCallStackNotes(S, FD);
15751475
}
15761476

15771477
// In CUDA, there are some constructs which may appear in semantically-valid
@@ -1644,6 +1544,71 @@ Sema::DeviceDiagBuilder::~DeviceDiagBuilder() {
16441544
}
16451545
}
16461546

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

clang/lib/Sema/SemaCUDA.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,25 @@ bool Sema::CheckCUDACall(SourceLocation Loc, FunctionDecl *Callee) {
674674
// Otherwise, mark the call in our call graph so we can traverse it later.
675675
bool CallerKnownEmitted =
676676
getEmissionStatus(Caller) == FunctionEmissionStatus::Emitted;
677+
if (CallerKnownEmitted) {
678+
// Host-side references to a __global__ function refer to the stub, so the
679+
// function itself is never emitted and therefore should not be marked.
680+
if (!shouldIgnoreInHostDeviceCheck(Callee))
681+
markKnownEmitted(
682+
*this, Caller, Callee, Loc, [](Sema &S, FunctionDecl *FD) {
683+
return S.getEmissionStatus(FD) == FunctionEmissionStatus::Emitted;
684+
});
685+
} else {
686+
// If we have
687+
// host fn calls kernel fn calls host+device,
688+
// the HD function does not get instantiated on the host. We model this by
689+
// omitting at the call to the kernel from the callgraph. This ensures
690+
// that, when compiling for host, only HD functions actually called from the
691+
// host get marked as known-emitted.
692+
if (!shouldIgnoreInHostDeviceCheck(Callee))
693+
DeviceCallGraph[Caller].insert({Callee, Loc});
694+
}
695+
677696
DeviceDiagBuilder::Kind DiagKind = [this, Caller, Callee,
678697
CallerKnownEmitted] {
679698
switch (IdentifyCUDAPreference(Caller, Callee)) {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17929,8 +17929,7 @@ Decl *Sema::getObjCDeclContext() const {
1792917929
return (dyn_cast_or_null<ObjCContainerDecl>(CurContext));
1793017930
}
1793117931

17932-
Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD,
17933-
bool Final) {
17932+
Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD) {
1793417933
// Templates are emitted when they're instantiated.
1793517934
if (FD->isDependentContext())
1793617935
return FunctionEmissionStatus::TemplateDiscarded;
@@ -17942,10 +17941,8 @@ Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD,
1794217941
if (DevTy.hasValue()) {
1794317942
if (*DevTy == OMPDeclareTargetDeclAttr::DT_Host)
1794417943
OMPES = FunctionEmissionStatus::OMPDiscarded;
17945-
else if (*DevTy == OMPDeclareTargetDeclAttr::DT_NoHost ||
17946-
*DevTy == OMPDeclareTargetDeclAttr::DT_Any) {
17944+
else if (DeviceKnownEmittedFns.count(FD) > 0)
1794717945
OMPES = FunctionEmissionStatus::Emitted;
17948-
}
1794917946
}
1795017947
} else if (LangOpts.OpenMP) {
1795117948
// In OpenMP 4.5 all the functions are host functions.
@@ -17961,11 +17958,10 @@ Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD,
1796117958
if (DevTy.hasValue()) {
1796217959
if (*DevTy == OMPDeclareTargetDeclAttr::DT_NoHost) {
1796317960
OMPES = FunctionEmissionStatus::OMPDiscarded;
17964-
} else if (*DevTy == OMPDeclareTargetDeclAttr::DT_Host ||
17965-
*DevTy == OMPDeclareTargetDeclAttr::DT_Any)
17961+
} else if (DeviceKnownEmittedFns.count(FD) > 0) {
1796617962
OMPES = FunctionEmissionStatus::Emitted;
17967-
} else if (Final)
17968-
OMPES = FunctionEmissionStatus::Emitted;
17963+
}
17964+
}
1796917965
}
1797017966
}
1797117967
if (OMPES == FunctionEmissionStatus::OMPDiscarded ||
@@ -18000,7 +17996,9 @@ Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD,
1800017996

1800117997
// Otherwise, the function is known-emitted if it's in our set of
1800217998
// known-emitted functions.
18003-
return FunctionEmissionStatus::Unknown;
17999+
return (DeviceKnownEmittedFns.count(FD) > 0)
18000+
? FunctionEmissionStatus::Emitted
18001+
: FunctionEmissionStatus::Unknown;
1800418002
}
1800518003

1800618004
bool Sema::shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee) {

0 commit comments

Comments
 (0)