Index: include/clang/AST/ASTConsumer.h =================================================================== --- include/clang/AST/ASTConsumer.h +++ include/clang/AST/ASTConsumer.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_AST_ASTCONSUMER_H #include "llvm/ADT/StringRef.h" +#include namespace clang { class ASTContext; @@ -29,6 +30,8 @@ class VarDecl; class FunctionDecl; class ImportDecl; + class PPCallbacks; + class Preprocessor; /// ASTConsumer - This is an abstract interface that should be implemented by /// clients that read ASTs. This abstraction layer allows the client to be @@ -152,6 +155,12 @@ /// body may be parsed anyway if it is needed (for instance, if it contains /// the code completion point or is constexpr). virtual bool shouldSkipFunctionBody(Decl *D) { return true; } + + /// If the consumer is interested in notifications from Preprocessor, + /// for example: notifications on macro definitions, etc., it should return + /// a pointer to a PPCallbacks here. + /// The caller takes ownership on the returned pointer. + virtual std::unique_ptr CreatePreprocessorCallbacks(Preprocessor &PP); }; } // end namespace clang. Index: lib/AST/ASTConsumer.cpp =================================================================== --- lib/AST/ASTConsumer.cpp +++ lib/AST/ASTConsumer.cpp @@ -15,6 +15,7 @@ #include "llvm/Bitcode/BitstreamReader.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclGroup.h" +#include "clang/Lex/PPCallbacks.h" using namespace clang; bool ASTConsumer::HandleTopLevelDecl(DeclGroupRef D) { @@ -30,3 +31,8 @@ void ASTConsumer::HandleImplicitImportDecl(ImportDecl *D) { HandleTopLevelDecl(DeclGroupRef(D)); } + +std::unique_ptr +ASTConsumer::CreatePreprocessorCallbacks(Preprocessor &PP) { + return nullptr; +} Index: lib/CodeGen/CGDebugInfo.h =================================================================== --- lib/CodeGen/CGDebugInfo.h +++ lib/CodeGen/CGDebugInfo.h @@ -376,6 +376,15 @@ void completeTemplateDefinition(const ClassTemplateSpecializationDecl &SD); + /// Get Macro debug info. + llvm::DIMacro *CreateMacro(llvm::DIMacroFile *Parent, unsigned MType, + SourceLocation LineLoc, StringRef Name, + StringRef Value); + /// Get Macro File debug info. + llvm::DIMacroFile *CreateMacroFile(llvm::DIMacroFile *Parent, + SourceLocation LineLoc, + SourceLocation FileLoc); + private: /// Emit call to llvm.dbg.declare for a variable declaration. void EmitDeclare(const VarDecl *decl, llvm::Value *AI, Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -2096,6 +2096,22 @@ FullName); } +llvm::DIMacro *CGDebugInfo::CreateMacro(llvm::DIMacroFile *Parent, + unsigned MType, SourceLocation LineLoc, + StringRef Name, StringRef Value) { + unsigned Line = LineLoc.isInvalid() ? 0 : getLineNumber(LineLoc); + return DBuilder.createMacro(Parent, Line, MType, Name, Value); +} + +llvm::DIMacroFile *CGDebugInfo::CreateMacroFile(llvm::DIMacroFile *Parent, + SourceLocation LineLoc, + SourceLocation FileLoc) { + llvm::DIFile *FName = getOrCreateFile(FileLoc); + unsigned Line = LineLoc.isInvalid() ? 0 : getLineNumber(LineLoc); + + return DBuilder.createMacroFile(Parent, Line, FName, llvm::DIMacroNodeArray()); +} + static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) { Qualifiers Quals; do { Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -70,6 +70,7 @@ CodeGenTypes.cpp CoverageMappingGen.cpp ItaniumCXXABI.cpp + MacroPPCallbacks.cpp MicrosoftCXXABI.cpp ModuleBuilder.cpp ObjectFilePCHContainerOperations.cpp Index: lib/CodeGen/CodeGenAction.cpp =================================================================== --- lib/CodeGen/CodeGenAction.cpp +++ lib/CodeGen/CodeGenAction.cpp @@ -227,6 +227,11 @@ Gen->HandleDependentLibrary(Opts); } + std::unique_ptr + CreatePreprocessorCallbacks(Preprocessor &PP) override { + return Gen->CreatePreprocessorCallbacks(PP); + } + static void InlineAsmDiagHandler(const llvm::SMDiagnostic &SM,void *Context, unsigned LocCookie) { SourceLocation Loc = SourceLocation::getFromRawEncoding(LocCookie); Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -76,6 +76,8 @@ class CXXDestructorDecl; class Module; class CoverageSourceInfo; +class Preprocessor; +class PPCallbacks; namespace CodeGen { @@ -585,6 +587,8 @@ CGDebugInfo *getModuleDebugInfo() { return DebugInfo; } + std::unique_ptr createPreprocessorCallbacks(Preprocessor &PP); + llvm::MDNode *getNoObjCARCExceptionsMetadata() { if (!NoObjCARCExceptionsMetadata) NoObjCARCExceptionsMetadata = llvm::MDNode::get(getLLVMContext(), None); Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -24,6 +24,7 @@ #include "CodeGenPGO.h" #include "CodeGenTBAA.h" #include "CoverageMappingGen.h" +#include "MacroPPCallbacks.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" @@ -208,6 +209,16 @@ CUDARuntime = CreateNVCUDARuntime(*this); } +std::unique_ptr +CodeGenModule::createPreprocessorCallbacks(Preprocessor &PP) { + // Enable generating macro debug info only in FullDebugInfo mode. + if (CodeGenOpts.getDebugInfo() < CodeGenOptions::FullDebugInfo || + !getModuleDebugInfo()) + return nullptr; + + return std::make_unique(*getModuleDebugInfo(), PP); +} + void CodeGenModule::addReplacement(StringRef Name, llvm::Constant *C) { Replacements[Name] = C; } Index: lib/CodeGen/MacroPPCallbacks.h =================================================================== --- lib/CodeGen/MacroPPCallbacks.h +++ lib/CodeGen/MacroPPCallbacks.h @@ -0,0 +1,82 @@ +//===--- MacroPPCallbacks.h -------------------------------------*- 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 implementation for the macro preprocessors callbacks. +// +//===----------------------------------------------------------------------===// + +#include "clang/Lex/PPCallbacks.h" +#include "clang/Parse/Parser.h" +#include + +namespace llvm { +class DIMacroFile; +} +namespace clang { + +namespace CodeGen { +class CGDebugInfo; +} +class MacroPPCallbacks : public PPCallbacks { + /// Debug info code generator + CodeGen::CGDebugInfo &DebugInfo; + /// Preprocessor + Preprocessor &PP; + /// Location (used for line number) for recent included file. + SourceLocation LastHashLoc; + /// Location (used for file name) for first included file (source main). + SourceLocation FirstIncludeFile; + /// Indicates that first file inclusion occurred. + bool FirstInclude; + /// Indicates that DIMacroFile entry was created for first included file. + bool FirstIncludeDone; + /// Counts current number of command line included files, which was entered + /// and was not exited yet. + int CommandIncludeFiles; + /// Number of fake files that should skip that were not exited yet. + int SkipFiles; + /// Parent contains all entered files that were not exited yet according to + /// the inclusion order. + std::vector Parents; + + /// Use the passed preprocessor to calculate the macro name and value from + /// the given macro info and identifier info. + static void getMacroDefinition(const IdentifierInfo &II, const MacroInfo &MI, + Preprocessor &PP, raw_ostream &Name, + raw_ostream &Value); + +public: + MacroPPCallbacks(CodeGen::CGDebugInfo &DI, Preprocessor &PP); + + /// Callback invoked whenever a source file is entered or exited. + /// + /// \param Loc Indicates the new location. + /// \param PrevFID the file that was exited if \p Reason is ExitFile. + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID = FileID()) override; + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported) override; + + /// Hook called whenever a macro definition is seen. + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override; + + /// Hook called whenever a macro \#undef is seen. + /// + /// MD is released immediately following this callback. + void MacroUndefined(const Token &MacroNameTok, + const MacroDefinition &MD) override; +}; + +} // end namespace clang Index: lib/CodeGen/MacroPPCallbacks.cpp =================================================================== --- lib/CodeGen/MacroPPCallbacks.cpp +++ lib/CodeGen/MacroPPCallbacks.cpp @@ -0,0 +1,172 @@ +//===--- MacroPPCallbacks.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains implementation for the macro preprocessors callbacks. +// +//===----------------------------------------------------------------------===// + +#include "MacroPPCallbacks.h" +#include "CGDebugInfo.h" +#include + +using namespace clang; + +void MacroPPCallbacks::getMacroDefinition(const IdentifierInfo &II, + const MacroInfo &MI, Preprocessor &PP, + raw_ostream &Name, + raw_ostream &Value) { + Name << II.getName(); + + if (MI.isFunctionLike()) { + Name << '('; + if (!MI.arg_empty()) { + MacroInfo::arg_iterator AI = MI.arg_begin(), E = MI.arg_end(); + for (; AI + 1 != E; ++AI) { + Name << (*AI)->getName(); + Name << ','; + } + + // Last argument. + if ((*AI)->getName() == "__VA_ARGS__") + Name << "..."; + else + Name << (*AI)->getName(); + } + + if (MI.isGNUVarargs()) + Name << "..."; // #define foo(x...) + + Name << ')'; + } + + SmallString<128> SpellingBuffer; + bool First = true; + for (const auto &T : MI.tokens()) { + if (!First && T.hasLeadingSpace()) + Value << ' '; + + Value << PP.getSpelling(T, SpellingBuffer); + First = false; + } +} + +MacroPPCallbacks::MacroPPCallbacks(CodeGen::CGDebugInfo &DI, Preprocessor &PP) + : DebugInfo(DI), PP(PP), FirstInclude(false), FirstIncludeDone(false), + // SkipFiles is equal to 2, because Preprocessor opens two fake files + // "" and "" without invoking the "FileChange" + // callback. We should not create DIMacroFile for these fake files. + // CommandIncludeFiles counts the number of currently processed files, + // which are included from command line. Initialized to zero. + CommandIncludeFiles(0), SkipFiles(2) { + Parents.push_back(nullptr); +} + +void MacroPPCallbacks::FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + switch (Reason) { + case EnterFile: + if (!FirstInclude) { + // First included file is always the source main file. + // Record its location. + FirstInclude = true; + FirstIncludeFile = Loc; + // Can not create DIMacroFile for source main file and push it to parent + // list at this point. Because, command line macros should have nullptr + // parent. + } + if (CommandIncludeFiles) { + // Preprocessor just entered a file included from command line. + // Create DIMacroFile entry for source main file if such was not created. + if (FirstInclude && !FirstIncludeDone) { + assert(FirstIncludeFile.isValid()); + Parents.push_back(DebugInfo.CreateMacroFile( + Parents.back(), SourceLocation(), FirstIncludeFile)); + FirstIncludeDone = true; + } + // Create DIMacroFile entry for command line included file. + Parents.push_back( + DebugInfo.CreateMacroFile(Parents.back(), SourceLocation(), Loc)); + } else if (!SkipFiles) + // There is no more files to skip create DIMacroFile entry for current + // included file. + Parents.push_back( + DebugInfo.CreateMacroFile(Parents.back(), LastHashLoc, Loc)); + return; + case ExitFile: + if (CommandIncludeFiles) { + // Preprocessor just exited a file included from command line. + // Removed the DIMacroFile entry from the parent stack and decrease the + // command line included files counter. + Parents.pop_back(); + --CommandIncludeFiles; + } else if (SkipFiles) { + // Exiting one of the fake files that should be skipped. + // Decrease the skip files counter. + if (!(--SkipFiles)) { + // This was the last file to skip, create DIMacroFile entry for source + // main file if such was not created. + if (FirstInclude && !FirstIncludeDone) { + assert(FirstIncludeFile.isValid()); + DebugInfo.CreateMacroFile(Parents.back(), SourceLocation(), + FirstIncludeFile); + FirstIncludeDone = true; + } + } + } else + // Exiting an actual include file removed its DIMacroFile entry from the + // parent stack. + Parents.pop_back(); + return; + default: + return; + } +} + +void MacroPPCallbacks::InclusionDirective( + SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, + bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, const Module *Imported) { + // FIXME: find a better way to check if this is #include + if (FileName.empty()) + return; + + // If preprocessor did not exit all skip files, the current include file must + // be included from command line. + if (SkipFiles) + // Increase the command line include files counter. + CommandIncludeFiles++; + + // Record the location, i.e. line number, of the current included file. + LastHashLoc = HashLoc; +} + +void MacroPPCallbacks::MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) { + IdentifierInfo *Id = MacroNameTok.getIdentifierInfo(); + SourceLocation location; + // While parsing skipped files location of macros is invalid. + // Invalid location represents line zero. + if (!SkipFiles) + location = MacroNameTok.getLocation(); + std::string NameBuffer, ValueBuffer; + llvm::raw_string_ostream Name(NameBuffer); + llvm::raw_string_ostream Value(ValueBuffer); + getMacroDefinition(*Id, *MD->getMacroInfo(), PP, Name, Value); + DebugInfo.CreateMacro(Parents.back(), llvm::dwarf::DW_MACINFO_define, + location, Name.str(), Value.str()); +} + +void MacroPPCallbacks::MacroUndefined(const Token &MacroNameTok, + const MacroDefinition &MD) { + IdentifierInfo *Id = MacroNameTok.getIdentifierInfo(); + SourceLocation location = MacroNameTok.getLocation(); + DebugInfo.CreateMacro(Parents.back(), llvm::dwarf::DW_MACINFO_undef, location, + Id->getName(), ""); +} Index: lib/CodeGen/ModuleBuilder.cpp =================================================================== --- lib/CodeGen/ModuleBuilder.cpp +++ lib/CodeGen/ModuleBuilder.cpp @@ -20,6 +20,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CodeGenOptions.h" +#include "clang/Lex/PPCallbacks.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/LLVMContext.h" @@ -234,6 +235,11 @@ void HandleDependentLibrary(llvm::StringRef Lib) override { Builder->AddDependentLib(Lib); } + + std::unique_ptr + CreatePreprocessorCallbacks(Preprocessor &PP) override { + return Builder->createPreprocessorCallbacks(PP); + } }; } Index: lib/Parse/ParseAST.cpp =================================================================== --- lib/Parse/ParseAST.cpp +++ lib/Parse/ParseAST.cpp @@ -128,6 +128,12 @@ new Parser(S.getPreprocessor(), S, SkipFunctionBodies)); Parser &P = *ParseOP.get(); + std::unique_ptr Callbacks = + Consumer->CreatePreprocessorCallbacks(S.getPreprocessor()); + if (Callbacks) { + S.getPreprocessor().addPPCallbacks(std::move(Callbacks)); + } + llvm::CrashRecoveryContextCleanupRegistrar CleanupPrettyStack(llvm::SavePrettyStackState()); PrettyStackTraceParserEntry CrashInfo(P);