Skip to content

Commit

Permalink
[Templight] Template Instantiation Observer
Browse files Browse the repository at this point in the history
This patch adds a base-class called TemplateInstantiationObserver which gets
notified whenever a template instantiation is entered or exited during
semantic analysis. This is a base class used to implement the template
profiling and debugging tool called
Templight (https://github.com/mikael-s-persson/templight).

The patch also makes a few more changes:

* ActiveTemplateInstantiation class is moved out of the Sema class (so it can be used with inclusion of Sema.h).
* CreateFrontendAction function in front-end utilities is given external linkage (not longer a hidden static function).
* TemplateInstObserverChain data member added to Sema class to hold the list of template-inst observers.
* Notifications to the template-inst observer are added at the key places where templates are instantiated.

Patch by: Abel Sinkovics!

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

llvm-svn: 324808
  • Loading branch information
Xazax-hun committed Feb 10, 2018
1 parent ec81ae3 commit 207e7b1
Show file tree
Hide file tree
Showing 25 changed files with 1,113 additions and 4 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/CC1Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,8 @@ def ast_dump : Flag<["-"], "ast-dump">,
HelpText<"Build ASTs and then debug dump them">;
def ast_dump_all : Flag<["-"], "ast-dump-all">,
HelpText<"Build ASTs and then debug dump them, forcing deserialization">;
def templight_dump : Flag<["-"], "templight-dump">,
HelpText<"Dump templight information to stdout">;
def ast_dump_lookups : Flag<["-"], "ast-dump-lookups">,
HelpText<"Build ASTs and then debug dump their name lookup tables">;
def ast_view : Flag<["-"], "ast-view">,
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Frontend/FrontendActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ class VerifyPCHAction : public ASTFrontendAction {
bool hasCodeCompletionSupport() const override { return false; }
};

class TemplightDumpAction : public ASTFrontendAction {
protected:
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override;

void ExecuteAction() override;
};

/**
* \brief Frontend action adaptor that merges ASTs together.
*
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ namespace frontend {
RewriteObjC, ///< ObjC->C Rewriter.
RewriteTest, ///< Rewriter playground
RunAnalysis, ///< Run one or more source code analyses.
TemplightDump, ///< Dump template instantiations
MigrateSource, ///< Run migrator.
RunPreprocessorOnly ///< Just lex, no output.
};
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/FrontendTool/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,18 @@
#ifndef LLVM_CLANG_FRONTENDTOOL_UTILS_H
#define LLVM_CLANG_FRONTENDTOOL_UTILS_H

#include <memory>

namespace clang {

class CompilerInstance;
class FrontendAction;

/// Construct the FrontendAction of a compiler invocation based on the
/// options specified for the compiler invocation.
///
/// \return - The created FrontendAction object
std::unique_ptr<FrontendAction> CreateFrontendAction(CompilerInstance &CI);

/// ExecuteCompilerInvocation - Execute the given actions described by the
/// compiler invocation object in the given compiler instance.
Expand Down
15 changes: 15 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ namespace clang {
class TemplateArgumentList;
class TemplateArgumentLoc;
class TemplateDecl;
class TemplateInstantiationCallback;
class TemplateParameterList;
class TemplatePartialOrderingContext;
class TemplateTemplateParmDecl;
Expand Down Expand Up @@ -7127,6 +7128,12 @@ class Sema {
/// We are defining a synthesized function (such as a defaulted special
/// member).
DefiningSynthesizedFunction,

/// Added for Template instantiation observation.
/// Memoization means we are _not_ instantiating a template because
/// it is already instantiated (but we entered a context where we
/// would have had to if it was not already instantiated).
Memoization
} Kind;

/// \brief Was the enclosing context a non-instantiation SFINAE context?
Expand Down Expand Up @@ -7235,6 +7242,14 @@ class Sema {
// FIXME: Does this belong in Sema? It's tough to implement it anywhere else.
unsigned LastEmittedCodeSynthesisContextDepth = 0;

/// \brief The template instantiation callbacks to trace or track
/// instantiations (objects can be chained).
///
/// This callbacks is used to print, trace or track template
/// instantiations as they are being constructed.
std::vector<std::unique_ptr<TemplateInstantiationCallback>>
TemplateInstCallbacks;

/// \brief The current index into pack expansion arguments that will be
/// used for substitution of parameter packs.
///
Expand Down
83 changes: 83 additions & 0 deletions clang/include/clang/Sema/TemplateInstCallback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//===- TemplateInstCallback.h - Template Instantiation Callback - C++ --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This file defines the TemplateInstantiationCallback class, which is the
// base class for callbacks that will be notified at template instantiations.
//
//===---------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TEMPLATE_INST_CALLBACK_H
#define LLVM_CLANG_TEMPLATE_INST_CALLBACK_H

#include "clang/Sema/Sema.h"

namespace clang {

/// \brief This is a base class for callbacks that will be notified at every
/// template instantiation.
class TemplateInstantiationCallback {
public:
virtual ~TemplateInstantiationCallback() = default;

/// \brief Called before doing AST-parsing.
virtual void initialize(const Sema &TheSema) = 0;

/// \brief Called after AST-parsing is completed.
virtual void finalize(const Sema &TheSema) = 0;

/// \brief Called when instantiation of a template just began.
virtual void atTemplateBegin(const Sema &TheSema,
const Sema::CodeSynthesisContext &Inst) = 0;

/// \brief Called when instantiation of a template is just about to end.
virtual void atTemplateEnd(const Sema &TheSema,
const Sema::CodeSynthesisContext &Inst) = 0;
};

template <class TemplateInstantiationCallbackPtrs>
void initialize(TemplateInstantiationCallbackPtrs &Callbacks,
const Sema &TheSema) {
for (auto &C : Callbacks) {
if (C)
C->initialize(TheSema);
}
}

template <class TemplateInstantiationCallbackPtrs>
void finalize(TemplateInstantiationCallbackPtrs &Callbacks,
const Sema &TheSema) {
for (auto &C : Callbacks) {
if (C)
C->finalize(TheSema);
}
}

template <class TemplateInstantiationCallbackPtrs>
void atTemplateBegin(TemplateInstantiationCallbackPtrs &Callbacks,
const Sema &TheSema,
const Sema::CodeSynthesisContext &Inst) {
for (auto &C : Callbacks) {
if (C)
C->atTemplateBegin(TheSema, Inst);
}
}

template <class TemplateInstantiationCallbackPtrs>
void atTemplateEnd(TemplateInstantiationCallbackPtrs &Callbacks,
const Sema &TheSema,
const Sema::CodeSynthesisContext &Inst) {
for (auto &C : Callbacks) {
if (C)
C->atTemplateEnd(TheSema, Inst);
}
}

} // namespace clang

#endif
3 changes: 3 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
Opts.ProgramAction = frontend::PrintPreamble; break;
case OPT_E:
Opts.ProgramAction = frontend::PrintPreprocessedInput; break;
case OPT_templight_dump:
Opts.ProgramAction = frontend::TemplightDump; break;
case OPT_rewrite_macros:
Opts.ProgramAction = frontend::RewriteMacros; break;
case OPT_rewrite_objc:
Expand Down Expand Up @@ -2620,6 +2622,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
case frontend::RewriteObjC:
case frontend::RewriteTest:
case frontend::RunAnalysis:
case frontend::TemplightDump:
case frontend::MigrateSource:
return false;

Expand Down
153 changes: 153 additions & 0 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,36 @@
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Sema/TemplateInstCallback.h"
#include "clang/Serialization/ASTReader.h"
#include "clang/Serialization/ASTWriter.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/YAMLTraits.h"
#include <memory>
#include <system_error>

using namespace clang;

namespace {
CodeCompleteConsumer *GetCodeCompletionConsumer(CompilerInstance &CI) {
return CI.hasCodeCompletionConsumer() ? &CI.getCodeCompletionConsumer()
: nullptr;
}

void EnsureSemaIsCreated(CompilerInstance &CI, FrontendAction &Action) {
if (Action.hasCodeCompletionSupport() &&
!CI.getFrontendOpts().CodeCompletionAt.FileName.empty())
CI.createCodeCompletionConsumer();

if (!CI.hasSema())
CI.createSema(Action.getTranslationUnitKind(),
GetCodeCompletionConsumer(CI));
}
} // namespace

//===----------------------------------------------------------------------===//
// Custom Actions
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -261,6 +280,140 @@ void VerifyPCHAction::ExecuteAction() {
ASTReader::ARR_ConfigurationMismatch);
}

namespace {
struct TemplightEntry {
std::string Name;
std::string Kind;
std::string Event;
std::string DefinitionLocation;
std::string PointOfInstantiation;
};
} // namespace

namespace llvm {
namespace yaml {
template <> struct MappingTraits<TemplightEntry> {
static void mapping(IO &io, TemplightEntry &fields) {
io.mapRequired("name", fields.Name);
io.mapRequired("kind", fields.Kind);
io.mapRequired("event", fields.Event);
io.mapRequired("orig", fields.DefinitionLocation);
io.mapRequired("poi", fields.PointOfInstantiation);
}
};
} // namespace yaml
} // namespace llvm

namespace {
class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
using CodeSynthesisContext = Sema::CodeSynthesisContext;

public:
virtual void initialize(const Sema &) {}

virtual void finalize(const Sema &) {}

virtual void atTemplateBegin(const Sema &TheSema,
const CodeSynthesisContext &Inst) override {
displayTemplightEntry<true>(llvm::outs(), TheSema, Inst);
}

virtual void atTemplateEnd(const Sema &TheSema,
const CodeSynthesisContext &Inst) override {
displayTemplightEntry<false>(llvm::outs(), TheSema, Inst);
}

private:
static std::string toString(CodeSynthesisContext::SynthesisKind Kind) {
switch (Kind) {
case CodeSynthesisContext::TemplateInstantiation:
return "TemplateInstantiation";
case CodeSynthesisContext::DefaultTemplateArgumentInstantiation:
return "DefaultTemplateArgumentInstantiation";
case CodeSynthesisContext::DefaultFunctionArgumentInstantiation:
return "DefaultFunctionArgumentInstantiation";
case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution:
return "ExplicitTemplateArgumentSubstitution";
case CodeSynthesisContext::DeducedTemplateArgumentSubstitution:
return "DeducedTemplateArgumentSubstitution";
case CodeSynthesisContext::PriorTemplateArgumentSubstitution:
return "PriorTemplateArgumentSubstitution";
case CodeSynthesisContext::DefaultTemplateArgumentChecking:
return "DefaultTemplateArgumentChecking";
case CodeSynthesisContext::ExceptionSpecInstantiation:
return "ExceptionSpecInstantiation";
case CodeSynthesisContext::DeclaringSpecialMember:
return "DeclaringSpecialMember";
case CodeSynthesisContext::DefiningSynthesizedFunction:
return "DefiningSynthesizedFunction";
case CodeSynthesisContext::Memoization:
return "Memoization";
}
return "";
}

template <bool BeginInstantiation>
static void displayTemplightEntry(llvm::raw_ostream &Out, const Sema &TheSema,
const CodeSynthesisContext &Inst) {
std::string YAML;
{
llvm::raw_string_ostream OS(YAML);
llvm::yaml::Output YO(OS);
TemplightEntry Entry =
getTemplightEntry<BeginInstantiation>(TheSema, Inst);
llvm::yaml::EmptyContext Context;
llvm::yaml::yamlize(YO, Entry, true, Context);
}
Out << "---" << YAML << "\n";
}

template <bool BeginInstantiation>
static TemplightEntry getTemplightEntry(const Sema &TheSema,
const CodeSynthesisContext &Inst) {
TemplightEntry Entry;
Entry.Kind = toString(Inst.Kind);
Entry.Event = BeginInstantiation ? "Begin" : "End";
if (auto *NamedTemplate = dyn_cast_or_null<NamedDecl>(Inst.Entity)) {
llvm::raw_string_ostream OS(Entry.Name);
NamedTemplate->getNameForDiagnostic(OS, TheSema.getLangOpts(), true);
const PresumedLoc DefLoc =
TheSema.getSourceManager().getPresumedLoc(Inst.Entity->getLocation());
if(!DefLoc.isInvalid())
Entry.DefinitionLocation = std::string(DefLoc.getFilename()) + ":" +
std::to_string(DefLoc.getLine()) + ":" +
std::to_string(DefLoc.getColumn());
}
const PresumedLoc PoiLoc =
TheSema.getSourceManager().getPresumedLoc(Inst.PointOfInstantiation);
if (!PoiLoc.isInvalid()) {
Entry.PointOfInstantiation = std::string(PoiLoc.getFilename()) + ":" +
std::to_string(PoiLoc.getLine()) + ":" +
std::to_string(PoiLoc.getColumn());
}
return Entry;
}
};
} // namespace

std::unique_ptr<ASTConsumer>
TemplightDumpAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
return llvm::make_unique<ASTConsumer>();
}

void TemplightDumpAction::ExecuteAction() {
CompilerInstance &CI = getCompilerInstance();

// This part is normally done by ASTFrontEndAction, but needs to happen
// before Templight observers can be created
// FIXME: Move the truncation aspect of this into Sema, we delayed this till
// here so the source manager would be initialized.
EnsureSemaIsCreated(CI, *this);

CI.getSema().TemplateInstCallbacks.push_back(
llvm::make_unique<DefaultTemplateInstCallback>());
ASTFrontendAction::ExecuteAction();
}

namespace {
/// \brief AST reader listener that dumps module information for a module
/// file.
Expand Down
Loading

0 comments on commit 207e7b1

Please sign in to comment.