Skip to content

Commit

Permalink
[CUDA] Emit deferred diagnostics during Sema rather than during codegen.
Browse files Browse the repository at this point in the history
Summary:
Emitting deferred diagnostics during codegen was a hack.  It did work,
but usability was poor, both for us as compiler devs and for users.  We
don't codegen if there are any sema errors, so for users this meant that
they wouldn't see deferred errors if there were any non-deferred errors.
For devs, this meant that we had to carefully split up our tests so that
when we tested deferred errors, we didn't emit any non-deferred errors.

This change moves checking for deferred errors into Sema.  See the big
comment in SemaCUDA.cpp for an overview of the idea.

This checking adds overhead to compilation, because we have to maintain
a partial call graph.  As a result, this change makes deferred errors a
CUDA-only concept (whereas before they were a general concept).  If
anyone else wants to use this framework for something other than CUDA,
we can generalize at that time.

This patch makes the minimal set of test changes -- after this lands,
I'll go back through and do a cleanup of the tests that we no longer
have to split up.

Reviewers: rnk

Subscribers: cfe-commits, rsmith, tra

Differential Revision: https://reviews.llvm.org/D25541

llvm-svn: 284158
  • Loading branch information
Justin Lebar committed Oct 13, 2016
1 parent a81682a commit 23d9542
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 141 deletions.
11 changes: 0 additions & 11 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -448,12 +448,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// \brief Allocator for partial diagnostics.
PartialDiagnostic::StorageAllocator DiagAllocator;

/// Diagnostics that are emitted if and only if the given function is
/// codegen'ed. Access these through FunctionDecl::addDeferredDiag() and
/// FunctionDecl::takeDeferredDiags().
llvm::DenseMap<const FunctionDecl *, std::vector<PartialDiagnosticAt>>
DeferredDiags;

/// \brief The current C++ ABI.
std::unique_ptr<CXXABI> ABI;
CXXABI *createCXXABI(const TargetInfo &T);
Expand Down Expand Up @@ -604,11 +598,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
return DiagAllocator;
}

decltype(DeferredDiags) &getDeferredDiags() { return DeferredDiags; }
const decltype(DeferredDiags) &getDeferredDiags() const {
return DeferredDiags;
}

const TargetInfo &getTargetInfo() const { return *Target; }
const TargetInfo *getAuxTargetInfo() const { return AuxTarget; }

Expand Down
8 changes: 0 additions & 8 deletions clang/include/clang/AST/Decl.h
Expand Up @@ -2271,14 +2271,6 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
/// returns 0.
unsigned getMemoryFunctionKind() const;

/// Add a diagnostic to be emitted if and when this function is codegen'ed.
void addDeferredDiag(PartialDiagnosticAt PD);

/// Gets this object's list of deferred diagnostics, if there are any.
///
/// Although this is logically const, it clears our list of deferred diags.
std::vector<PartialDiagnosticAt> takeDeferredDiags() const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) {
Expand Down
43 changes: 32 additions & 11 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -9245,6 +9245,30 @@ class Sema {
/// before incrementing, so you can emit an error.
bool PopForceCUDAHostDevice();

/// Diagnostics that are emitted only if we discover that the given function
/// must be codegen'ed. Because handling these correctly adds overhead to
/// compilation, this is currently only enabled for CUDA compilations.
llvm::DenseMap<const FunctionDecl *, std::vector<PartialDiagnosticAt>>
CUDADeferredDiags;

/// Raw encodings of SourceLocations for which CheckCUDACall has emitted a
/// (maybe deferred) "bad call" diagnostic. We use this to avoid emitting the
/// same deferred diag twice.
llvm::DenseSet<unsigned> LocsWithCUDACallDiags;

/// The set of CUDA functions that we've discovered must be emitted by tracing
/// the call graph. Functions that we can tell a priori must be emitted
/// aren't added to this set.
llvm::DenseSet<FunctionDecl *> CUDAKnownEmittedFns;

/// A partial call graph maintained during CUDA compilation to support
/// deferred diagnostics. Specifically, functions are only added here if, at
/// the time they're added, they are not known-emitted. As soon as we
/// discover that a function is known-emitted, we remove it and everything it
/// transitively calls from this set and add those functions to
/// CUDAKnownEmittedFns.
llvm::DenseMap<FunctionDecl *, llvm::SetVector<FunctionDecl *>> CUDACallGraph;

/// Diagnostic builder for CUDA errors which may or may not be deferred.
///
/// In CUDA, there exist constructs (e.g. variable-length arrays, try/catch)
Expand Down Expand Up @@ -9298,12 +9322,15 @@ class Sema {

private:
struct PartialDiagnosticInfo {
PartialDiagnosticInfo(SourceLocation Loc, PartialDiagnostic PD,
PartialDiagnosticInfo(Sema &S, SourceLocation Loc, PartialDiagnostic PD,
FunctionDecl *Fn)
: Loc(Loc), PD(std::move(PD)), Fn(Fn) {}
: S(S), Loc(Loc), PD(std::move(PD)), Fn(Fn) {}

~PartialDiagnosticInfo() { Fn->addDeferredDiag({Loc, std::move(PD)}); }
~PartialDiagnosticInfo() {
S.CUDADeferredDiags[Fn].push_back({Loc, std::move(PD)});
}

Sema &S;
SourceLocation Loc;
PartialDiagnostic PD;
FunctionDecl *Fn;
Expand All @@ -9322,8 +9349,8 @@ class Sema {
/// - If CurContext is a __device__ or __global__ function, emits the
/// diagnostics immediately.
/// - If CurContext is a __host__ __device__ function and we are compiling for
/// the device, creates a deferred diagnostic which is emitted if and when
/// the function is codegen'ed.
/// the device, creates a diagnostic which is emitted if and when we realize
/// that the function will be codegen'ed.
///
/// Example usage:
///
Expand Down Expand Up @@ -9397,12 +9424,6 @@ class Sema {
void maybeAddCUDAHostDeviceAttrs(Scope *S, FunctionDecl *FD,
const LookupResult &Previous);

private:
/// Raw encodings of SourceLocations for which CheckCUDACall has emitted a
/// (maybe deferred) "bad call" diagnostic. We use this to avoid emitting the
/// same deferred diag twice.
llvm::DenseSet<unsigned> LocsWithCUDACallDiags;

public:
/// Check whether we're allowed to call Callee from the current context.
///
Expand Down
14 changes: 0 additions & 14 deletions clang/lib/AST/Decl.cpp
Expand Up @@ -3473,20 +3473,6 @@ unsigned FunctionDecl::getMemoryFunctionKind() const {
return 0;
}

void FunctionDecl::addDeferredDiag(PartialDiagnosticAt PD) {
getASTContext().getDeferredDiags()[this].push_back(std::move(PD));
}

std::vector<PartialDiagnosticAt> FunctionDecl::takeDeferredDiags() const {
auto &DD = getASTContext().getDeferredDiags();
auto It = DD.find(this);
if (It == DD.end())
return {};
auto Ret = std::move(It->second);
DD.erase(It);
return Ret;
}

//===----------------------------------------------------------------------===//
// FieldDecl Implementation
//===----------------------------------------------------------------------===//
Expand Down
44 changes: 0 additions & 44 deletions clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -499,19 +499,6 @@ void CodeGenModule::Release() {
EmitVersionIdentMetadata();

EmitTargetMetadata();

// Emit any deferred diagnostics gathered during codegen. We didn't emit them
// when we first discovered them because that would have halted codegen,
// preventing us from gathering other deferred diags.
for (const PartialDiagnosticAt &DiagAt : DeferredDiags) {
SourceLocation Loc = DiagAt.first;
const PartialDiagnostic &PD = DiagAt.second;
DiagnosticBuilder Builder(getDiags().Report(Loc, PD.getDiagID()));
PD.Emit(Builder);
}
// Clear the deferred diags so they don't outlive the ASTContext's
// PartialDiagnostic allocator.
DeferredDiags.clear();
}

void CodeGenModule::UpdateCompletedType(const TagDecl *TD) {
Expand Down Expand Up @@ -2913,37 +2900,6 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
llvm::GlobalValue *GV) {
const auto *D = cast<FunctionDecl>(GD.getDecl());

// Emit this function's deferred diagnostics, if none of them are errors. If
// any of them are errors, don't codegen the function, but also don't emit any
// of the diagnostics just yet. Emitting an error during codegen stops
// further codegen, and we want to display as many deferred diags as possible.
// We'll emit the now twice-deferred diags at the very end of codegen.
//
// (If a function has both error and non-error diags, we don't emit the
// non-error diags here, because order can be significant, e.g. with notes
// that follow errors.)
auto Diags = D->takeDeferredDiags();
if (auto *Templ = D->getPrimaryTemplate()) {
auto TemplDiags = Templ->getAsFunction()->takeDeferredDiags();
Diags.insert(Diags.end(), TemplDiags.begin(), TemplDiags.end());
}
bool HasError = llvm::any_of(Diags, [this](const PartialDiagnosticAt &PDAt) {
return getDiags().getDiagnosticLevel(PDAt.second.getDiagID(), PDAt.first) >=
DiagnosticsEngine::Error;
});
if (HasError) {
DeferredDiags.insert(DeferredDiags.end(),
std::make_move_iterator(Diags.begin()),
std::make_move_iterator(Diags.end()));
return;
}
for (PartialDiagnosticAt &PDAt : Diags) {
const SourceLocation &Loc = PDAt.first;
const PartialDiagnostic &PD = PDAt.second;
DiagnosticBuilder Builder(getDiags().Report(Loc, PD.getDiagID()));
PD.Emit(Builder);
}

// Compute the function info and LLVM type.
const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD);
llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
Expand Down
4 changes: 0 additions & 4 deletions clang/lib/CodeGen/CodeGenModule.h
Expand Up @@ -490,10 +490,6 @@ class CodeGenModule : public CodeGenTypeCache {
/// MDNodes.
llvm::DenseMap<QualType, llvm::Metadata *> MetadataIdMap;

/// Diags gathered from FunctionDecl::takeDeferredDiags(). Emitted at the
/// very end of codegen.
std::vector<std::pair<SourceLocation, PartialDiagnostic>> DeferredDiags;

public:
CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts,
const PreprocessorOptions &ppopts,
Expand Down

0 comments on commit 23d9542

Please sign in to comment.