gollvm: add support for -fdebug-prefix-map= option

Implement support for -fdebug-prefix-map (previously this flag was
accepted but ignored).

Change-Id: I16d23e5432b1d4f7c81144b6795b7cf23d6a3134
Reviewed-on: https://go-review.googlesource.com/113722
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/bridge/go-llvm-dibuildhelper.cpp b/bridge/go-llvm-dibuildhelper.cpp
index 612f8fb..3f9853f 100644
--- a/bridge/go-llvm-dibuildhelper.cpp
+++ b/bridge/go-llvm-dibuildhelper.cpp
@@ -43,8 +43,8 @@
   // Create compile unit
   llvm::SmallString<256> currentDir;
   llvm::sys::fs::current_path(currentDir);
-  auto primaryFile =
-      dibuilder_->createFile(linemap_->get_initial_file(), currentDir);
+  std::string filestr = applyDebugPrefix(linemap_->get_initial_file());
+  auto primaryFile = dibuilder_->createFile(filestr, currentDir);
   bool isOptimized = true;
   std::string compileFlags; // FIXME
   unsigned runtimeVersion = 0; // not sure what would be for Go
@@ -284,9 +284,24 @@
   }
 }
 
+void DIBuildHelper::addDebugPrefix(std::pair<llvm::StringRef, llvm::StringRef> pref)
+{
+  std::string from(pref.first);
+  std::string to(pref.second);
+  debugPrefixMap_[from] = to;
+}
+
+std::string DIBuildHelper::applyDebugPrefix(llvm::StringRef path) {
+  for (const auto &remap : debugPrefixMap_)
+    if (path.startswith(remap.first))
+      return (llvm::Twine(remap.second) +
+              path.substr(remap.first.size())).str();
+  return path.str();
+}
+
 llvm::DIFile *DIBuildHelper::diFileFromLocation(Location location)
 {
-  std::string locfile = linemap()->location_file(location);
+  std::string locfile = applyDebugPrefix(linemap()->location_file(location));
   llvm::StringRef locdir = llvm::sys::path::parent_path(locfile);
   llvm::StringRef locfilename = llvm::sys::path::filename(locfile);
   if (linemap()->is_predeclared(location))
diff --git a/bridge/go-llvm-dibuildhelper.h b/bridge/go-llvm-dibuildhelper.h
index 53b708c..4d5df84 100644
--- a/bridge/go-llvm-dibuildhelper.h
+++ b/bridge/go-llvm-dibuildhelper.h
@@ -66,6 +66,9 @@
 
   void processExprInst(Bexpression *expr, llvm::Instruction *inst);
 
+  // Support for -fdebug-prefix
+  void addDebugPrefix(std::pair<llvm::StringRef, llvm::StringRef>);
+
   // Return module scope
   llvm::DIScope *moduleScope() const { return moduleScope_; }
 
@@ -94,6 +97,7 @@
   std::unique_ptr<llvm::DIBuilder> dibuilder_;
   std::vector<llvm::DIScope*> diScopeStack_;
   std::unordered_map<Btype *, llvm::DIType*> typeCache_;
+  std::unordered_map<std::string, std::string> debugPrefixMap_;
   std::vector<std::pair<Bvariable *, bool> > globalsToProcess_;
 
   // The following items are specific to the current function we're visiting.
@@ -110,6 +114,7 @@
   void processVarsInBLock(const std::vector<Bvariable*> &vars,
                           llvm::DIScope *scope);
   void markBlocks(Bnode *node);
+  std::string applyDebugPrefix(llvm::StringRef path);
 };
 
 #endif // !defined(GO_LLVM_DIBUILDHELPER_H)
diff --git a/bridge/go-llvm.cpp b/bridge/go-llvm.cpp
index 547990c..1e09d29 100644
--- a/bridge/go-llvm.cpp
+++ b/bridge/go-llvm.cpp
@@ -135,6 +135,11 @@
   setTypeManagerTraceLevel(level);
 }
 
+void Llvm_backend::addDebugPrefix(std::pair<llvm::StringRef, llvm::StringRef> prefix)
+{
+  dibuildhelper_->addDebugPrefix(prefix);
+}
+
 void
 Llvm_backend::verifyModule()
 {
@@ -3494,7 +3499,7 @@
   // Create and populate entry block
   llvm::BasicBlock *entryBlock = genEntryBlock(function);
 
-  // Avoid debug meta-generation if errors seen
+  // Avoid debug metadata generation if errors seen
   DIBuildHelper *dibh = nullptr;
   if (createDebugMetaData_ && errorCount_ == 0 && !go_be_saw_errors())
     dibh = dibuildhelper();
diff --git a/bridge/go-llvm.h b/bridge/go-llvm.h
index 7b965d3..d3700b9 100644
--- a/bridge/go-llvm.h
+++ b/bridge/go-llvm.h
@@ -342,12 +342,12 @@
   // DI build helper. Will be NULL if debug meta-data generation disabled.
   DIBuildHelper *dibuildhelper() { return dibuildhelper_.get(); }
 
+  // Support for -fdebug-prefix=
+  void addDebugPrefix(std::pair<llvm::StringRef, llvm::StringRef> prefix);
+
   // Bnode builder
   BnodeBuilder &nodeBuilder() { return nbuilder_; }
 
-  // Return top-level debug meta data object for module
-  llvm::DICompileUnit *getDICompUnit();
-
   // Finalize export data for the module. Exposed for unit testing.
   void finalizeExportData();
 
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
index 2c49b5a..4100966 100644
--- a/driver/CompileGo.cpp
+++ b/driver/CompileGo.cpp
@@ -427,6 +427,10 @@
   bridge_->setTraceLevel(*tl);
   bridge_->setNoInline(args_.hasArg(gollvm::options::OPT_fno_inline));
 
+  // Honor -fdebug-prefix=... option.
+  for (const auto &arg : driver_.args().getAllArgValues(gollvm::options::OPT_fdebug_prefix_map_EQ))
+    bridge_->addDebugPrefix(llvm::StringRef(arg).split('='));
+
   // Support -fgo-dump-ast
   if (args_.hasArg(gollvm::options::OPT_fgo_dump_ast))
     go_enable_dump("ast");
diff --git a/unittests/BackendCore/BackendDebugEmit.cpp b/unittests/BackendCore/BackendDebugEmit.cpp
index d45c3e3..77179a9 100644
--- a/unittests/BackendCore/BackendDebugEmit.cpp
+++ b/unittests/BackendCore/BackendDebugEmit.cpp
@@ -36,7 +36,7 @@
         %x = alloca i32
         store i32 0, i32* %x
         call void @llvm.dbg.declare(metadata i32* %x, metadata !3,
-                                    metadata !DIExpression()), !dbg !11
+                                    metadata !DIExpression()), !dbg !10
         ret void
       }
   )RAW_RESULT";
@@ -69,7 +69,7 @@
     define void @foo(i8* nest %nest.0, { i64, i64, i64 }* byval %p0) #0 {
     entry:
       call void @llvm.dbg.declare(metadata { i64, i64, i64 }* %p0, metadata !3,
-                                  metadata !DIExpression()), !dbg !15
+                                  metadata !DIExpression()), !dbg !16
       ret void
     }
   )RAW_RESULT";
@@ -211,4 +211,31 @@
 
 }
 
+TEST(BackendDebugEmit, TestDebugPrefixMap) {
+
+  FcnTestHarness h;
+  Llvm_backend *be = h.be();
+  Btype *bi64t = be->integer_type(false, 64);
+  BFunctionType *befty = mkFuncTyp(be, L_PARM, bi64t, L_RES, bi64t, L_END);
+
+  llvm::StringRef from2("/bar");
+  llvm::StringRef to2("/something");
+  be->addDebugPrefix(std::make_pair(from2, to2));
+
+  Location loc = h.newFileLineLoc("/bar/another/barcode.go", 11);
+  Bfunction *func = h.mkFunction("bar", befty);
+  Bvariable *p0 = func->getNthParamVar(0);
+  Bexpression *vec = be->var_expression(p0, loc);
+  h.mkReturn(std::vector<Bexpression*>{vec});
+
+  bool broken = h.finish(PreserveDebugInfo);
+  EXPECT_FALSE(broken && "Module failed to verify.");
+
+  be->dumpModule();
+
+  // Check for remapped source file.
+  bool ok = h.expectModuleDumpContains("!DIFile(filename: \"barcode.go\", directory: \"/something/another\")");
+  EXPECT_TRUE(ok);
+}
+
 }
diff --git a/unittests/BackendCore/TestUtils.cpp b/unittests/BackendCore/TestUtils.cpp
index eaf1380..9334045 100644
--- a/unittests/BackendCore/TestUtils.cpp
+++ b/unittests/BackendCore/TestUtils.cpp
@@ -232,7 +232,8 @@
   return func;
 }
 
-Bfunction *mkFuncFromType(Backend *be, const char *fname, BFunctionType *befty)
+Bfunction *mkFuncFromType(Backend *be, const char *fname,
+                          BFunctionType *befty, Location loc)
 {
   bool visible = true;
   bool is_declaration = false;
@@ -240,7 +241,6 @@
   bool split_stack = true;
   bool unique_sec = false;
   bool no_ret = false;
-  Location loc;
   Bfunction *func = be->function(befty, fname, fname, visible,
                                  is_declaration, is_inl,
                                  split_stack, no_ret, unique_sec, loc);
@@ -425,9 +425,17 @@
   return loc_;
 }
 
+Location FcnTestHarness::newFileLineLoc(const char *file, unsigned line)
+{
+  lineNum_ = line;
+  be_->linemap()->start_file(file, lineNum_);
+  loc_ = be_->linemap()->get_location(lineNum_);
+  return loc_;
+}
+
 Bfunction *FcnTestHarness::mkFunction(const char *fcnName, BFunctionType *befty)
 {
-  func_ = mkFuncFromType(be(), fcnName, befty);
+  func_ = mkFuncFromType(be(), fcnName, befty, loc_);
   entryBlock_ = be()->block(func_, nullptr, emptyVarList_, loc_, loc_);
   curBlock_ = be()->block(func_, nullptr, emptyVarList_, loc_, loc_);
   return func_;
diff --git a/unittests/BackendCore/TestUtils.h b/unittests/BackendCore/TestUtils.h
index 986975f..12f64e3 100644
--- a/unittests/BackendCore/TestUtils.h
+++ b/unittests/BackendCore/TestUtils.h
@@ -108,7 +108,8 @@
 Bfunction *mkFunci32o64(Backend *be, const char *fname, bool mkParams = true);
 
 // Returns function created from type
-Bfunction *mkFuncFromType(Backend *be, const char *fname, BFunctionType *befty);
+Bfunction *mkFuncFromType(Backend *be, const char *fname,
+                          BFunctionType *befty, Location loc = Location());
 
 // Manufacture an unsigned 64-bit integer constant
 Bexpression *mkUint64Const(Backend *be, uint64_t val);
@@ -168,6 +169,9 @@
   // Update the dummy location to something new, then return it.
   Location newloc();
 
+  // New location with specified file and line.
+  Location newFileLineLoc(const char *file, unsigned line);
+
   // Return current function
   Bfunction *func() const { return func_; }