gollvm: variable linkage and initializer fixes

When the frontend creates an implicit variable or immutable struct and
sets the "hidden" flag, this should translate into LLVM default
visibility and internal linkage (as opposed to hidden visibility).
Add support for placing variables in COMDAT groups where requested
by the frontend. Don't include initializers for externally defined
global variables.

Change-Id: Ia93a2e14b89c9bd9efe4860d7546a774d835ca5b
Reviewed-on: https://go-review.googlesource.com/45330
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/goparse-llvm.cpp b/goparse-llvm.cpp
index 020eee9..e986f0b 100644
--- a/goparse-llvm.cpp
+++ b/goparse-llvm.cpp
@@ -284,6 +284,14 @@
                                      Options, getRelocModel(), CMModel, OLvl));
   assert(Target && "Could not allocate target machine!");
 
+  // FIXME: this hard-wires on the equivalent of -ffunction-sections
+  // and -fdata-sections, since there doesn't seem to be a high-level
+  // hook for selecting a separate section for a specific variable or
+  // function (other than forcing it into a comdat, which is not
+  // always what we want).
+  Options.FunctionSections = true;
+  Options.DataSections = true;
+
   // Print a stack trace if we signal out.
   llvm::LLVMContext Context;
   bool hasError = false;
diff --git a/llvm-gofrontend/go-llvm.cpp b/llvm-gofrontend/go-llvm.cpp
index 41d1650..2cb38b5 100644
--- a/llvm-gofrontend/go-llvm.cpp
+++ b/llvm-gofrontend/go-llvm.cpp
@@ -697,6 +697,7 @@
   Bvariable *rv = makeModuleVar(type, ctag, "", Location(),
                                 MV_Constant, MV_DefaultSection,
                                 MV_NotInComdat, MV_DefaultVisibility,
+                                MV_NotExternallyInitialized,
                                 llvm::GlobalValue::PrivateLinkage,
                                 conval, 0);
   assert(llvm::isa<llvm::GlobalVariable>(rv->value()));
@@ -1061,6 +1062,7 @@
       makeModuleVar(makeAuxType(scon->getType()),
                     "", "", Location(), MV_Constant, MV_DefaultSection,
                     MV_NotInComdat, MV_DefaultVisibility,
+                    MV_NotExternallyInitialized,
                     llvm::GlobalValue::PrivateLinkage, scon, 1);
   llvm::Constant *varval = llvm::cast<llvm::Constant>(svar->value());
   llvm::Constant *bitcast =
@@ -2694,6 +2696,7 @@
                             ModVarSec inUniqueSection,
                             ModVarComdat inComdat,
                             ModVarVis isHiddenVisibility,
+                            ModVarExtInit isExtInit,
                             llvm::GlobalValue::LinkageTypes linkage,
                             llvm::Constant *initializer,
                             unsigned alignment)
@@ -2706,15 +2709,17 @@
   assert(datalayout().getTypeSizeInBits(btype->type()) != 0);
 #endif
 
-  // FIXME: add support for this
+  // FIXME: at the moment the driver is enabling separate sections
+  // for all variables, since there doesn't seem to be an easily
+  // accessible hook for requesting a separate section for a single
+  // variable.
   assert(inUniqueSection == MV_DefaultSection);
 
-  // FIXME: add support for this
-  assert(inComdat == MV_NotInComdat);
-
   // FIXME: add DIGlobalVariable to debug info for this variable
 
-  llvm::Constant *init = llvm::Constant::getNullValue(btype->type());
+  llvm::Constant *init =
+      (isExtInit == MV_ExternallyInitialized ? nullptr :
+       llvm::Constant::getNullValue(btype->type()));
   std::string gname(asm_name.empty() ? name : asm_name);
   llvm::GlobalVariable *glob = new llvm::GlobalVariable(
       module(), btype->type(), isConstant == MV_Constant,
@@ -2725,6 +2730,15 @@
     glob->setAlignment(alignment);
   if (initializer)
     glob->setInitializer(initializer);
+  if (inComdat == MV_InComdat) {
+    assert(! gname.empty());
+    glob->setComdat(module().getOrInsertComdat(gname));
+  }
+  if (isExtInit == MV_ExternallyInitialized) {
+    assert(!init);
+    glob->setExternallyInitialized(true);
+  }
+
   bool addressTaken = true; // for now
   Bvariable *bv =
       new Bvariable(btype, location, gname, GlobalVar, addressTaken, glob);
@@ -2741,22 +2755,20 @@
                                          bool is_external,
                                          bool is_hidden,
                                          bool in_unique_section,
-                                         Location location) {
-#if 0
-  llvm::GlobalValue::LinkageTypes linkage =
-      (is_external || is_hidden ? llvm::GlobalValue::ExternalLinkage
-       : llvm::GlobalValue::InternalLinkage);
-#endif
+                                         Location location)
+{
   llvm::GlobalValue::LinkageTypes linkage = llvm::GlobalValue::ExternalLinkage;
 
   ModVarSec inUniqSec =
       (in_unique_section ? MV_UniqueSection : MV_DefaultSection);
   ModVarVis varVis =
       (is_hidden ? MV_HiddenVisibility : MV_DefaultVisibility);
+  ModVarExtInit extInit =
+      (is_external ? MV_ExternallyInitialized : MV_NotExternallyInitialized);
   Bvariable *gvar =
       makeModuleVar(btype, var_name, asm_name, location,
                     MV_NonConstant, inUniqSec, MV_NotInComdat,
-                    varVis, linkage, nullptr);
+                    varVis, extInit, linkage, nullptr);
   return gvar;
 }
 
@@ -2870,20 +2882,19 @@
   assert(!(is_hidden && is_common));
 
   llvm::GlobalValue::LinkageTypes linkage =
-      (is_hidden ? llvm::GlobalValue::ExternalLinkage
-       : llvm::GlobalValue::WeakODRLinkage);
+      (is_hidden ? llvm::GlobalValue::InternalLinkage
+       : llvm::GlobalValue::ExternalLinkage);
 
-  // bool isComdat = is_common;
-  ModVarComdat inComdat = MV_NotInComdat; // for now
-  ModVarSec inUniqSec = MV_DefaultSection; // override for now
-  ModVarVis varVis =
-      (is_hidden ? MV_HiddenVisibility : MV_DefaultVisibility);
+  ModVarComdat inComdat = (is_common ? MV_InComdat : MV_NotInComdat);
+  ModVarSec inUniqSec = MV_DefaultSection;
+  ModVarVis varVis = MV_DefaultVisibility;
   ModVarConstant isConst = (is_constant ? MV_Constant : MV_NonConstant);
+  ModVarExtInit extInit = MV_NotExternallyInitialized;
 
   Bvariable *gvar =
       makeModuleVar(btype, name, asm_name, Location(),
                     isConst, inUniqSec, inComdat,
-                    varVis, linkage, nullptr, alignment);
+                    varVis, extInit, linkage, nullptr, alignment);
   return gvar;
 }
 
@@ -2931,17 +2942,17 @@
   assert(!(is_hidden && is_common));
 
   llvm::GlobalValue::LinkageTypes linkage =
-      (is_hidden ? llvm::GlobalValue::ExternalLinkage
-       : llvm::GlobalValue::WeakODRLinkage);
+      (is_hidden ? llvm::GlobalValue::InternalLinkage
+       : llvm::GlobalValue::ExternalLinkage);
 
-  ModVarComdat inComdat = MV_NotInComdat; // for now
-  ModVarSec inUniqueSec = MV_DefaultSection; // override for now
-  ModVarVis varVis =
-      (is_hidden ? MV_HiddenVisibility : MV_DefaultVisibility);
+  ModVarSec inUniqueSec = MV_DefaultSection;
+  ModVarComdat inComdat = (is_common ? MV_InComdat : MV_NotInComdat);
+  ModVarVis varVis = MV_DefaultVisibility;
+  ModVarExtInit extInit = MV_NotExternallyInitialized;
   Bvariable *gvar =
       makeModuleVar(btype, name, asm_name, location,
                     MV_Constant, inUniqueSec, inComdat,
-                    varVis, linkage, nullptr);
+                    varVis, extInit, linkage, nullptr);
   return gvar;
 }
 
@@ -3110,10 +3121,9 @@
     bfunc->setSplitStack(Bfunction::NoSplit);
 
   // TODO: unique section support. llvm::GlobalObject has support for
-  // setting COMDAT groups and section names, but nothing to manage how
-  // section names are created or doled out as far as I can tell, need
-  // to look a little more closely at how -ffunction-sections is implemented
-  // for clang/LLVM.
+  // setting COMDAT groups and section names, but there doesn't seem
+  // to be an interface available to request a unique section on a
+  // per-function basis (only a translation-unit-wide default).
   assert(!in_unique_section || is_declaration);
 
   if (is_declaration)
diff --git a/llvm-gofrontend/go-llvm.h b/llvm-gofrontend/go-llvm.h
index 6440624..ceacc92 100644
--- a/llvm-gofrontend/go-llvm.h
+++ b/llvm-gofrontend/go-llvm.h
@@ -416,6 +416,7 @@
   enum ModVarSec { MV_UniqueSection, MV_DefaultSection };
   enum ModVarComdat { MV_InComdat, MV_NotInComdat };
   enum ModVarVis { MV_HiddenVisibility, MV_DefaultVisibility };
+  enum ModVarExtInit { MV_ExternallyInitialized, MV_NotExternallyInitialized };
 
   // Make a module-scope variable (static, global, or external).
   Bvariable *makeModuleVar(Btype *btype,
@@ -426,6 +427,7 @@
                            ModVarSec inUniqueSection,
                            ModVarComdat inComdat,
                            ModVarVis isHiddenVisibility,
+                           ModVarExtInit isExtInit,
                            llvm::GlobalValue::LinkageTypes linkage,
                            llvm::Constant *initializer,
                            unsigned alignmentInBytes = 0);
diff --git a/unittests/BackendCore/BackendVarTests.cpp b/unittests/BackendCore/BackendVarTests.cpp
index 117d884..7fc4f95 100644
--- a/unittests/BackendCore/BackendVarTests.cpp
+++ b/unittests/BackendCore/BackendVarTests.cpp
@@ -288,7 +288,7 @@
   Btype *uintptrt = be->integer_type(true, be->type_size(pbt)*8);
   Btype *desct = mkBackendStruct(be, uintptrt, "x", nullptr);
   Bvariable *ims = be->immutable_struct("desc", "desc",
-                                        false, false, desct, loc);
+                                        true, false, desct, loc);
   Bexpression *fp = be->function_code_expression(func, loc);
   Bexpression *confp = be->convert_expression(uintptrt, fp, loc);
 
@@ -298,9 +298,8 @@
   be->immutable_struct_set_init(ims, "", false, false,
                                 desct, loc, scon);
 
-  // Q: do we want weak_odr here?
   const char *exp = R"RAW_RESULT(
-    @desc = weak_odr constant { i64 } { i64 ptrtoint
+    @desc = internal constant { i64 } { i64 ptrtoint
     (i64 (i8*, i32, i32, i64*)* @foo to i64) }
   )RAW_RESULT";
 
@@ -343,13 +342,13 @@
                                  isHidden, isConst, isCommon, con1);
 
   const char *exp1 = R"RAW_RESULT(
-     @v1 = weak_odr global { i32, i32 } { i32 101, i32 202 }, align 8
+     @v1 = global { i32, i32 } { i32 101, i32 202 }, align 8
     )RAW_RESULT";
 
   bool isOK1 = h.expectValue(ims1->value(), exp1);
   EXPECT_TRUE(isOK1 && "Value does not have expected contents");
 
-  // Case 2: common, no init value.
+  // Case 2: const, common, no init value.
   isConst = true;
   isCommon = true;
   Bvariable *ims2 =
@@ -359,7 +358,7 @@
                                  isHidden, isConst, isCommon, nullptr);
 
   const char *exp2 = R"RAW_RESULT(
-    @v2 = weak_odr constant { i32, i32 } zeroinitializer, align 8
+    @v2 = constant { i32, i32 } zeroinitializer, comdat, align 8
     )RAW_RESULT";
 
   bool isOK2 = h.expectValue(ims2->value(), exp2);