driver, bridge: add stack map support machinary

Add -enable-gc={0,1} command line flag for enabling GC (stack
map) support. Default to false for now.

When the GC support is enabled, the driver will incorporate GC
related backend passes. And the bridge will attach the GC
strategy to each function, as well as attach a dummy personality
function to functions that don't have one, to ensure the
exception table is always generated.

Change-Id: I86dfd4a2c79bf00305e4301103a240b053060f17
Reviewed-on: https://go-review.googlesource.com/c/137763
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/bridge/go-llvm.cpp b/bridge/go-llvm.cpp
index 52c7c8a..3751410 100644
--- a/bridge/go-llvm.cpp
+++ b/bridge/go-llvm.cpp
@@ -64,6 +64,8 @@
     , builtinTable_(new BuiltinTable(typeManager(), false))
     , errorFunction_(nullptr)
     , personalityFunction_(nullptr)
+    , dummyPersonalityFunction_(nullptr)
+    , gcStrategy_("")
 {
   // If nobody passed in a linemap, create one for internal use (unit testing)
   if (!linemap_) {
@@ -368,6 +370,22 @@
   return personalityFunction_;
 }
 
+llvm::Function *Llvm_backend::dummyPersonalityFunction()
+{
+  if (gcStrategy_.empty())
+    // GC is not enabled, no need to attach dummy personality function.
+    return nullptr;
+  if (dummyPersonalityFunction_)
+    return dummyPersonalityFunction_;
+
+  llvm::FunctionType *pft = personalityFunctionType();
+  llvm::GlobalValue::LinkageTypes plinkage = llvm::GlobalValue::ExternalLinkage;
+  const char *pfn = "__gccgo_personality_dummy";
+  dummyPersonalityFunction_ =
+      llvm::Function::Create(pft, plinkage, pfn, module_);
+  return dummyPersonalityFunction_;
+}
+
 Bfunction *Llvm_backend::createIntrinsicFcn(const std::string &name,
                                             llvm::Function *fcn)
 {
@@ -2438,6 +2456,7 @@
   llvm::Constant *init = nullptr;
   glob = new llvm::GlobalVariable(module(), btype->type(), isConstant,
                                   linkage, init, gname);
+
   Bvariable *bv =
       new Bvariable(btype, location, name, GlobalVar, false, glob);
   assert(valueVarMap_.find(bv->value()) == valueVarMap_.end());
@@ -2562,6 +2581,9 @@
     llvm::Twine fnt(fns);
     llvm::Function *fcn = llvm::Function::Create(fty, linkage, fnt, module_);
 
+    if (!gcStrategy_.empty())
+      fcn->setGC(gcStrategy_);
+
     fcn->addFnAttr("disable-tail-calls", "true");
 
     // inline/noinline
@@ -2770,6 +2792,11 @@
 void GenBlocks::finishFunction(llvm::BasicBlock *entry)
 {
   function_->fixupProlog(entry, newTemporaries_);
+
+  llvm::Function *func = function_->function();
+  if (! func->hasPersonalityFn())
+    func->setPersonalityFn(be_->dummyPersonalityFunction());
+
   if (dibuildhelper_)
     dibuildhelper_->endFunction(function_);
 }
diff --git a/bridge/go-llvm.h b/bridge/go-llvm.h
index 2c68fa8..67ac52b 100644
--- a/bridge/go-llvm.h
+++ b/bridge/go-llvm.h
@@ -398,9 +398,15 @@
   void setTargetCpuAttr(const std::string &cpu);
   void setTargetFeaturesAttr(const std::string &attrs);
 
+  // Set GC strategy
+  void setGCStrategy(std::string s) { gcStrategy_ = s; };
+
   // Personality function
   llvm::Function *personalityFunction();
 
+  // Dummy personality function
+  llvm::Function *dummyPersonalityFunction();
+
  private:
   Bexpression *errorExpression() const { return errorExpression_; }
   Bstatement *errorStatement() const { return errorStatement_; }
@@ -834,10 +840,14 @@
 
   // Personality function
   llvm::Function *personalityFunction_;
+  llvm::Function *dummyPersonalityFunction_;
 
   // Target cpu and attributes to be attached to any generated fcns.
   std::string targetCpuAttr_;
   std::string targetFeaturesAttr_;
+
+  // GC strategy
+  std::string gcStrategy_;
 };
 
 #endif
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
index 77ae703..62ddfd6 100644
--- a/driver/CompileGo.cpp
+++ b/driver/CompileGo.cpp
@@ -19,6 +19,7 @@
 #include "mpfr.h"
 #include "GollvmOptions.h"
 #include "GollvmConfig.h"
+#include "GollvmPasses.h"
 
 #include "Action.h"
 #include "Artifact.h"
@@ -34,6 +35,8 @@
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Bitcode/BitcodeWriterPass.h"
 #include "llvm/Config/llvm-config.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/IR/IRPrintingPasses.h"
@@ -46,6 +49,7 @@
 #include "llvm/Option/OptTable.h"
 #include "llvm/Option/Option.h"
 #include "llvm/Passes/PassBuilder.h"
+#include "llvm/CodeGen/Passes.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Format.h"
@@ -104,6 +108,7 @@
   std::unique_ptr<TargetLibraryInfoImpl> tlii_;
   std::string targetCpuAttr_;
   std::string targetFeaturesAttr_;
+  bool enable_gc_;
 
   void createPasses(legacy::PassManager &MPM,
                     legacy::FunctionPassManager &FPM);
@@ -140,7 +145,8 @@
       args_(tc.driver().args()),
       cgolvl_(CodeGenOpt::Default),
       olvl_(2),
-      hasError_(false)
+      hasError_(false),
+      enable_gc_(false)
 {
   InitializeAllTargets();
   InitializeAllTargetMCs();
@@ -498,6 +504,14 @@
   for (const auto &arg : driver_.args().getAllArgValues(gollvm::options::OPT_fdebug_prefix_map_EQ))
     bridge_->addDebugPrefix(llvm::StringRef(arg).split('='));
 
+  llvm::Optional<unsigned> enable_gc =
+      driver_.getLastArgAsInteger(gollvm::options::OPT_enable_gc_EQ, 0u);
+  if (enable_gc && *enable_gc) {
+    enable_gc_ = true;
+    bridge_->setGCStrategy("go");
+    linkGoGC();
+    linkGoGCPrinter();
+  }
   // Support -fgo-dump-ast
   if (args_.hasArg(gollvm::options::OPT_fgo_dump_ast))
     go_enable_dump("ast");
@@ -737,6 +751,11 @@
       createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
   createPasses(modulePasses, functionPasses);
 
+  // Add statepoint insertion pass to the end of optimization pipeline,
+  // right before lowering to machine IR.
+  if (enable_gc_)
+    modulePasses.add(createGoStatepointsLegacyPass());
+
   legacy::PassManager codeGenPasses;
   bool noverify = args_.hasArg(gollvm::options::OPT_noverify);
   TargetMachine::CodeGenFileType ft = TargetMachine::CGFT_AssemblyFile;
@@ -763,10 +782,30 @@
 
   // Codegen setup
   codeGenPasses.add(new TargetLibraryInfoWrapperPass(*tlii_));
-  if (target_->addPassesToEmitFile(codeGenPasses, *OS, nullptr, ft,
-                                   /*DisableVerify=*/ noverify)) {
-    errs() << "error: unable to interface with target\n";
-    return false;
+
+  // Add stackmap machine IR pass to the end of the machine passes,
+  // right before AsmPrinter.
+  // FIXME: the code below is essentially duplicate of
+  // LLVMTargetMachine::addPassesToEmitFile (and its callee).
+  // TODO: error check.
+  {
+    LLVMTargetMachine *lltm = (LLVMTargetMachine*)(target_.get()); // FIXME: TargetMachine doesn't support llvm::cast?
+    TargetPassConfig *passConfig = lltm->createPassConfig(codeGenPasses);
+    // Set PassConfig options provided by TargetMachine.
+    passConfig->setDisableVerify(noverify);
+    codeGenPasses.add(passConfig);
+    MachineModuleInfo *MMI = new MachineModuleInfo(lltm);
+    codeGenPasses.add(MMI);
+    passConfig->addISelPasses();
+    passConfig->addMachinePasses();
+    passConfig->setInitialized();
+
+    if (enable_gc_)
+      codeGenPasses.add(createGoAnnotationPass());
+
+    lltm->addAsmPrinter(codeGenPasses, *OS, nullptr, ft, MMI->getContext());
+
+    codeGenPasses.add(createFreeMachineFunctionPass());
   }
 
 run:
diff --git a/driver/GollvmOptions.td b/driver/GollvmOptions.td
index aa026a0..a997b08 100644
--- a/driver/GollvmOptions.td
+++ b/driver/GollvmOptions.td
@@ -441,3 +441,6 @@
 
 def gcc_toolchain_EQ : Joined<["--"], "gcc-toolchain=">, Flags<[DriverOption]>,
   HelpText<"Use the gcc toolchain at the given directory">;
+
+def enable_gc_EQ : Joined<["-"], "enable-gc=">,
+  HelpText<"Enable stack map generation">;