gollvm: add attributes for some functions to help optimization

Teach the backend some facts about some runtime functions, to
help optimizations.

Change-Id: I7bca0f98042e45f87c91eab556c2e0e62a64d65f
Reviewed-on: https://go-review.googlesource.com/c/gollvm/+/183839
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/bridge/go-llvm.cpp b/bridge/go-llvm.cpp
index bc18b97..6daf115 100644
--- a/bridge/go-llvm.cpp
+++ b/bridge/go-llvm.cpp
@@ -55,6 +55,7 @@
     , noInline_(false)
     , noFpElim_(false)
     , useSplitStack_(true)
+    , compilingRuntime_(false)
     , checkIntegrity_(true)
     , createDebugMetaData_(true)
     , exportDataStarted_(false)
@@ -2658,6 +2659,73 @@
     if (isGCLeaf(fns))
       fcn->addFnAttr("gc-leaf-function");
 
+    // attributes about runtime functions, to help the optimizer
+    if (fns == "runtime.newobject" ||
+        fns == "runtime.makeslice" ||
+        fns == "runtime.makeslice64" ||
+        fns == "runtime.makechan" ||
+        fns == "runtime.makechan64") {
+      fcn->addAttribute(llvm::AttributeList::ReturnIndex,
+                        llvm::Attribute::NonNull);
+      fcn->addAttribute(llvm::AttributeList::ReturnIndex,
+                        llvm::Attribute::NoAlias);
+    }
+
+    // makemap may return its argument, so not noalias.
+    if (fns == "runtime.makemap" ||
+        fns == "runtime.makemap64" ||
+        fns == "runtime.makemap_small")
+      fcn->addAttribute(llvm::AttributeList::ReturnIndex,
+                        llvm::Attribute::NonNull);
+
+    // mapaccess1 and mapassign never return nil.
+    if (fns == "runtime.mapaccess1" ||
+        fns == "runtime.mapaccess1_fast32" ||
+        fns == "runtime.mapaccess1_fast64" ||
+        fns == "runtime.mapaccess1_faststr" ||
+        fns == "runtime.mapaccess1_fat" ||
+        fns == "runtime.mapassign" ||
+        fns == "runtime.mapassign_fast32" ||
+        fns == "runtime.mapassign_fast64" ||
+        fns == "runtime.mapassign_fast32ptr" ||
+        fns == "runtime.mapassign_fast64ptr" ||
+        fns == "runtime.mapassign_faststr")
+      fcn->addAttribute(llvm::AttributeList::ReturnIndex,
+                        llvm::Attribute::NonNull);
+
+    // mapaccess is pure function.
+    if (!compilingRuntime_ &&
+        (fns == "runtime.mapaccess1" ||
+         fns == "runtime.mapaccess1_fast32" ||
+         fns == "runtime.mapaccess1_fast64" ||
+         fns == "runtime.mapaccess1_faststr" ||
+         fns == "runtime.mapaccess1_fat" ||
+         fns == "runtime.mapaccess2" ||
+         fns == "runtime.mapaccess2_fast32" ||
+         fns == "runtime.mapaccess2_fast64" ||
+         fns == "runtime.mapaccess2_faststr" ||
+         fns == "runtime.mapaccess2_fat"))
+      fcn->addFnAttr(llvm::Attribute::ReadOnly);
+
+    // memcmp-like.
+    if (fns == "runtime.memequal" ||
+        fns == "runtime.cmpstring") {
+      fcn->addFnAttr(llvm::Attribute::ReadOnly);
+      fcn->addFnAttr(llvm::Attribute::ArgMemOnly);
+    }
+
+    if (fns == "runtime.memclrNoHeapPointers")
+      fcn->addFnAttr(llvm::Attribute::ArgMemOnly);
+
+    // These functions are called in unlikely branches. But they
+    // themselves are not actually cold in the runtime. So only
+    // mark cold when we are not compiling the runtime.
+    if (!compilingRuntime_ &&
+        (fns == "runtime.gcWriteBarrier" ||
+         fns == "runtime.typedmemmove" ||
+         fns == "runtime.growslice"))
+      fcn->addFnAttr(llvm::Attribute::Cold);
+
     fcnValue = fcn;
 
     // Fix up references to declaration of old type.
diff --git a/bridge/go-llvm.h b/bridge/go-llvm.h
index 2a73e9e..0d4824b 100644
--- a/bridge/go-llvm.h
+++ b/bridge/go-llvm.h
@@ -387,20 +387,23 @@
   unsigned traceLevel() const { return traceLevel_; }
 
   // Disable inlining if set to true.
-  void setNoInline(bool b) { noInline_ = b; };
+  void setNoInline(bool b) { noInline_ = b; }
 
   // Disable frame pointer elimination if set to true.
-  void setNoFpElim(bool b) { noFpElim_ = b; };
+  void setNoFpElim(bool b) { noFpElim_ = b; }
 
   // Enable/disable the use of split stacks.
-  void setUseSplitStack(bool b) { useSplitStack_ = b; };
+  void setUseSplitStack(bool b) { useSplitStack_ = b; }
 
   // Target CPU and features
   void setTargetCpuAttr(const std::string &cpu);
   void setTargetFeaturesAttr(const std::string &attrs);
 
   // Set GC strategy
-  void setGCStrategy(std::string s) { gcStrategy_ = s; };
+  void setGCStrategy(std::string s) { gcStrategy_ = s; }
+
+  // Whether we are compiling the runtime package
+  void setCompilingRuntime() { compilingRuntime_ = true; }
 
   // Personality function
   llvm::Function *personalityFunction();
@@ -753,6 +756,9 @@
   // Whether to use split stacks.
   bool useSplitStack_;
 
+  // Whether we are compiling the runtime.
+  bool compilingRuntime_;
+
   // Whether to check for unexpected node sharing (e.g. same Bexpression
   // or statement pointed to by multiple parents).
   bool checkIntegrity_;
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
index d6c3b8f..14ff429 100644
--- a/driver/CompileGo.cpp
+++ b/driver/CompileGo.cpp
@@ -692,6 +692,9 @@
   args.backend = bridge_.get();
   go_create_gogo (&args);
 
+  if (args.compiling_runtime)
+    bridge_->setCompilingRuntime();
+
   /* The default precision for floating point numbers.  This is used
      for floating point constants with abstract type.  This may
      eventually be controllable by a command line option.  */