[DebugInfo] Added support to Clang FE for generating debug info for preprocessor macros.

Added "-fdebug-macro" flag (and "-fno-debug-macro" flag) to enable (and to disable) emitting macro debug info.
Added CC1 "-debug-info-macro" flag that enables emitting macro debug info.

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

llvm-svn: 294637
diff --git a/clang/lib/CodeGen/MacroPPCallbacks.cpp b/clang/lib/CodeGen/MacroPPCallbacks.cpp
new file mode 100644
index 0000000..71eb55c
--- /dev/null
+++ b/clang/lib/CodeGen/MacroPPCallbacks.cpp
@@ -0,0 +1,211 @@
+//===--- MacroPPCallbacks.cpp ---------------------------------------------===//
+//
+//                     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 "clang/CodeGen/ModuleBuilder.h"
+#include "clang/Parse/Parser.h"
+
+using namespace clang;
+
+void MacroPPCallbacks::writeMacroDefinition(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())
+      // #define foo(x...)
+      Name << "...";
+
+    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(CodeGenerator *Gen, Preprocessor &PP)
+    : Gen(Gen), PP(PP), Status(NoScope) {}
+
+/*
+  This is the expected flow of enter/exit compiler and user files:
+  - Main File Enter
+    - <built-in> file enter
+      {Compiler macro definitions} - (Line=0, no scope)
+      - (Optional) <command line> file enter
+      {Command line macro definitions} - (Line=0, no scope)
+      - (Optional) <command line> file exit
+      {Command line file includes} - (Line=0, Main file scope)
+        {macro definitions and file includes} - (Line!=0, Parent scope)
+    - <built-in> file exit
+    {User code macro definitions and file includes} - (Line!=0, Parent scope)
+*/
+
+llvm::DIMacroFile *MacroPPCallbacks::getCurrentScope() {
+  if (Status == MainFileScope || Status == CommandLineIncludeScope)
+    return Scopes.back();
+  return nullptr;
+}
+
+SourceLocation MacroPPCallbacks::getCorrectLocation(SourceLocation Loc) {
+  if (Status == MainFileScope || EnteredCommandLineIncludeFiles)
+    return Loc;
+
+  // While parsing skipped files, location of macros is invalid.
+  // Invalid location represents line zero.
+  return SourceLocation();
+}
+
+static bool isBuiltinFile(SourceManager &SM, SourceLocation Loc) {
+  StringRef Filename(SM.getPresumedLoc(Loc).getFilename());
+  return Filename.equals("<built-in>");
+}
+
+static bool isCommandLineFile(SourceManager &SM, SourceLocation Loc) {
+  StringRef Filename(SM.getPresumedLoc(Loc).getFilename());
+  return Filename.equals("<command line>");
+}
+
+void MacroPPCallbacks::updateStatusToNextScope() {
+  switch (Status) {
+  case NoScope:
+    Status = InitializedScope;
+    break;
+  case InitializedScope:
+    Status = BuiltinScope;
+    break;
+  case BuiltinScope:
+    Status = CommandLineIncludeScope;
+    break;
+  case CommandLineIncludeScope:
+    Status = MainFileScope;
+    break;
+  case MainFileScope:
+    llvm_unreachable("There is no next scope, already in the final scope");
+  }
+}
+
+void MacroPPCallbacks::FileEntered(SourceLocation Loc) {
+  SourceLocation LineLoc = getCorrectLocation(LastHashLoc);
+  switch (Status) {
+  default:
+    llvm_unreachable("Do not expect to enter a file from current scope");
+  case NoScope:
+    updateStatusToNextScope();
+    break;
+  case InitializedScope:
+    updateStatusToNextScope();
+    return;
+  case BuiltinScope:
+    if (isCommandLineFile(PP.getSourceManager(), Loc))
+      return;
+    updateStatusToNextScope();
+    LLVM_FALLTHROUGH;
+  case CommandLineIncludeScope:
+    EnteredCommandLineIncludeFiles++;
+    break;
+  case MainFileScope:
+    break;
+  }
+
+  Scopes.push_back(Gen->getCGDebugInfo()->CreateTempMacroFile(getCurrentScope(),
+                                                              LineLoc, Loc));
+}
+
+void MacroPPCallbacks::FileExited(SourceLocation Loc) {
+  switch (Status) {
+  default:
+    llvm_unreachable("Do not expect to exit a file from current scope");
+  case BuiltinScope:
+    if (!isBuiltinFile(PP.getSourceManager(), Loc))
+      // Skip next scope and change status to MainFileScope.
+      Status = MainFileScope;
+    return;
+  case CommandLineIncludeScope:
+    if (!EnteredCommandLineIncludeFiles) {
+      updateStatusToNextScope();
+      return;
+    }
+    EnteredCommandLineIncludeFiles--;
+    break;
+  case MainFileScope:
+    break;
+  }
+
+  Scopes.pop_back();
+}
+
+void MacroPPCallbacks::FileChanged(SourceLocation Loc, FileChangeReason Reason,
+                                   SrcMgr::CharacteristicKind FileType,
+                                   FileID PrevFID) {
+  // Only care about enter file or exit file changes.
+  if (Reason == EnterFile)
+    FileEntered(Loc);
+  else if (Reason == ExitFile)
+    FileExited(Loc);
+}
+
+void MacroPPCallbacks::InclusionDirective(
+    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
+    bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
+    StringRef SearchPath, StringRef RelativePath, const Module *Imported) {
+
+  // Record the line location of the current included file.
+  LastHashLoc = HashLoc;
+}
+
+void MacroPPCallbacks::MacroDefined(const Token &MacroNameTok,
+                                    const MacroDirective *MD) {
+  IdentifierInfo *Id = MacroNameTok.getIdentifierInfo();
+  SourceLocation location = getCorrectLocation(MacroNameTok.getLocation());
+  std::string NameBuffer, ValueBuffer;
+  llvm::raw_string_ostream Name(NameBuffer);
+  llvm::raw_string_ostream Value(ValueBuffer);
+  writeMacroDefinition(*Id, *MD->getMacroInfo(), PP, Name, Value);
+  Gen->getCGDebugInfo()->CreateMacro(getCurrentScope(),
+                                     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 = getCorrectLocation(MacroNameTok.getLocation());
+  Gen->getCGDebugInfo()->CreateMacro(getCurrentScope(),
+                                     llvm::dwarf::DW_MACINFO_undef, location,
+                                     Id->getName(), "");
+}