gollvm: better handling of aggregate-type const conversions

Revamp a portion of the bridge that materializes type conversions for
aggregate constants, so as to enable clean conversions between types
that are structurally identical but are different in their use of
placeholders.

This fixes a compilation problem with the encoding/asn1 package test
exposed as part of the libgo Go 1.11 update.

Change-Id: I0ecf9b23f63c37daeb7c873dcc7e4c8491bed311
Reviewed-on: https://go-review.googlesource.com/c/140117
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/bridge/go-llvm-materialize.cpp b/bridge/go-llvm-materialize.cpp
index 0119ff5..b09a70e 100644
--- a/bridge/go-llvm-materialize.cpp
+++ b/bridge/go-llvm-materialize.cpp
@@ -162,14 +162,34 @@
     }
   }
 
-  // If we're converting an aggregate type to an equivalent one,
-  // the conversion will happen on the pointer. For constant
-  // composite value, we need to make a variable for it to take
-  // the address. And the result will be pointer, so it has a
-  // load pending.
+  // If we're converting between two different Btypes that have the
+  // same underlying LLVM type, then we can create a new Bexpression
+  // for the conversion but not do anything else.
+  if (toType == valType) {
+    Bexpression *rval =
+        nbuilder_.mkConversion(type, expr->value(), expr, location);
+    if (expr->varExprPending())
+      rval->setVarExprPending(expr->varContext());
+    return rval;
+  }
+
+  // If we're applying a conversion to an aggregate constant, call a helper to
+  // see if we can create a new (but equivalent) constant value using the target
+  // type. If this works, we're effectively done. If the conversion doesn't
+  // succeed, materialize a variable containing the constant and apply the
+  // conversion to the variable's type (which will be a pointer to the type of
+  // the constant), then flag the result as "load pending".
   bool pending = false;
   if (expr->isConstant() && val->getType()->isAggregateType()) {
     llvm::Constant *cval = llvm::cast<llvm::Constant>(val);
+    assert(valType == cval->getType());
+    llvm::Value *convertedValue = genConvertedConstant(cval, toType);
+    if (convertedValue != nullptr) {
+      // We have a new value of the correct type. Wrap a conversion
+      // expr around it and return.
+      return nbuilder_.mkConversion(type, convertedValue, expr, location);
+    }
+    // materialize constant into variable.
     Bvariable *cv = genVarForConstant(cval, expr->btype());
     val = cv->value();
     valType = val->getType();
@@ -179,16 +199,6 @@
 
   Bexpression *rval = nullptr;
 
-  // If we're converting between two different Btypes that have the
-  // same underlying LLVM type, then we can create a new Bexpression
-  // for the conversion but not do anything else.
-  if (toType == valType) {
-    rval = nbuilder_.mkConversion(type, expr->value(), expr, location);
-    if (expr->varExprPending())
-      rval->setVarExprPending(expr->varContext());
-    return rval;
-  }
-
   LIRBuilder builder(context_, llvm::ConstantFolder());
 
   // Pointer type to pointer-sized-integer type. Comes up when
@@ -891,8 +901,10 @@
         Bexpression *bex = vals[ii];
         llvm::Constant *con = llvm::cast<llvm::Constant>(bex->value());
         llvm::Type *elt = llct->getTypeAtIndex(ii);
-        if (elt != con->getType())
-          con = llvm::ConstantExpr::getBitCast(con, elt);
+        if (elt != con->getType()) {
+          con = genConvertedConstant(con, elt);
+          assert(con != nullptr);
+        }
         llvals[idx] = con;
       }
       if (numElements != nvals) {
@@ -907,8 +919,10 @@
       for (unsigned long ii = 0; ii < numElements; ++ii) {
         llvm::Constant *con = llvm::cast<llvm::Constant>(vals[ii]->value());
         llvm::Type *elt = llct->getTypeAtIndex(ii);
-        if (elt != con->getType())
-          con = llvm::ConstantExpr::getBitCast(con, elt);
+        if (elt != con->getType()) {
+          con = genConvertedConstant(con, elt);
+          assert(con != nullptr);
+        }
         llvals[ii] = con;
       }
     }
diff --git a/bridge/go-llvm.cpp b/bridge/go-llvm.cpp
index 9e74b2d..5d1dcaf 100644
--- a/bridge/go-llvm.cpp
+++ b/bridge/go-llvm.cpp
@@ -682,6 +682,129 @@
   return expr;
 }
 
+// This helper function takes a constant value and a specified type and tries to
+// create an equivalent constant value using using the new type. It is designed
+// primarily for aggreate types, but will work for other types as well.
+//
+// Background: conversions of aggregate constant values are a problem area for
+// the bridge, due to the front end's use of placeholder types, and to the way
+// that LLVM implements placeholders.
+//
+// For example, suppose that the front end creates a placholder struct type PH,
+// then later on resolves the placeholder to the struct type "{ i64, i64 }". In
+// the world of LLVM types, the resulting type is named (has name "PH") but is
+// also a struct type with two fields.
+//
+// Elsewhere in the compilation constant struct value of "{ 13, 14 }" is created
+// using an anonymous struct type T of the form "{ i64, i64 }". Since this type
+// is not named, it has a separate, distinct LLVM type from PH.
+//
+// The front end sees these two types as equivalent, however, so it can often
+// take constant values of type T and assign them (either directly or via an
+// initializer) to variables of type PH (or vice versa), adding conversions if
+// the backend types don't match. LLVM, however, doesn't allow you to convert an
+// aggregate value V1 (with type T1) to another value V2 (with different type
+// T2) using a bitcast -- the IR builder will reject the cast.
+//
+// One way around this is to take the address of V1, convert the resulting
+// pointer to "pointer to T2", then dereference the pointer and assign
+// the result to V2.
+//
+// This routine takes a different route, which is to pick apart the constant
+// value (of type T1) and create an equivalent constant value of type T2.
+// Note that if the types are sufficiently different (for example, different
+// number of struct fields or different size) the process will fail and
+// a null pointer will be returned.
+
+llvm::Constant *Llvm_backend::genConvertedConstant(llvm::Constant *fromVal,
+                                                   llvm::Type *llToType)
+{
+  llvm::Type *llFromType = fromVal->getType();
+  if (llFromType == llToType)
+    return fromVal;
+
+  // There must be agreement in type class (struct -> struct, etc).
+  bool isFromComposite = llvm::isa<llvm::CompositeType>(llFromType);
+  bool isToComposite = llvm::isa<llvm::CompositeType>(llToType);
+  if (isFromComposite != isToComposite)
+    return nullptr;
+
+  // If the type in question is not an aggregate, go ahead and
+  // apply a bitcast to perform the conversion.
+  LIRBuilder builder(context_, llvm::ConstantFolder());
+  if (! isToComposite) {
+    llvm::Value *bitcast = builder.CreateBitCast(fromVal, llToType);
+    assert(llvm::isa<llvm::Constant>(bitcast));
+    llvm::Constant *constCast = llvm::cast<llvm::Constant>(bitcast);
+    return constCast;
+  }
+
+  // Collect disposition of type (struct vs array) and number of elements,
+  // and weed out any clashes between 'from' and 'to' types.
+  unsigned numElements = 0;
+  bool isFromStructTy = llFromType->isStructTy();
+  bool isToStructTy = llToType->isStructTy();
+  if (isFromStructTy) {
+    if (! isToStructTy)
+      return nullptr;
+    llvm::StructType *fromStTy = llvm::cast<llvm::StructType>(llFromType);
+    llvm::StructType *toStTy = llvm::cast<llvm::StructType>(llToType);
+    numElements = fromStTy->getNumElements();
+    if (numElements != toStTy->getNumElements())
+      return nullptr;
+  } else {
+    assert(llFromType->isArrayTy());
+    if (! llToType->isArrayTy())
+      return nullptr;
+    llvm::ArrayType *fromArTy = llvm::cast<llvm::ArrayType>(llFromType);
+    llvm::ArrayType *toArTy = llvm::cast<llvm::ArrayType>(llToType);
+    numElements = fromArTy->getNumElements();
+    if (numElements != toArTy->getNumElements())
+      return nullptr;
+  }
+
+  // Do a lookup to see if we have a memoized value from a previous
+  // conversion.
+  auto candidate = std::make_pair(fromVal, llToType);
+  auto it = genConvConstMap_.find(candidate);
+  if (it != genConvConstMap_.end())
+    return it->second;
+
+  // Grab from/to types as composites.
+  llvm::CompositeType *llFromCT = llvm::cast<llvm::CompositeType>(llFromType);
+  llvm::CompositeType *llToCT = llvm::cast<llvm::CompositeType>(llToType);
+  assert(llFromCT != nullptr);
+  assert(llToCT != nullptr);
+
+  // Walk through the child values and convert them.
+  llvm::SmallVector<llvm::Constant *, 64> newvals(numElements);
+  for (unsigned idx = 0; idx < numElements; idx++) {
+    llvm::Type *toEltType = llToCT->getTypeAtIndex(idx);
+    llvm::Constant *constElt = fromVal->getAggregateElement(idx);
+    llvm::Constant *convElt = genConvertedConstant(constElt, toEltType);
+    if (convElt == nullptr)
+      return nullptr;
+    newvals[idx] = convElt;
+  }
+
+  // Materialize final constant value.
+  llvm::Constant *toVal = nullptr;
+  if (llToType->isStructTy()) {
+    llvm::StructType *llst = llvm::cast<llvm::StructType>(llToType);
+    toVal = llvm::ConstantStruct::get(llst, newvals);
+  } else {
+    llvm::ArrayType *llat = llvm::cast<llvm::ArrayType>(llToType);
+    toVal = llvm::ConstantArray::get(llat, newvals);
+  }
+
+  // Cache the result.
+  genConvConstMap_[candidate] = toVal;
+
+  // Return the final product.
+  return toVal;
+}
+
+
 Bvariable *Llvm_backend::genVarForConstant(llvm::Constant *conval,
                                            Btype *type)
 {
diff --git a/bridge/go-llvm.h b/bridge/go-llvm.h
index 4f5f4f7..6b3b008 100644
--- a/bridge/go-llvm.h
+++ b/bridge/go-llvm.h
@@ -556,6 +556,13 @@
   // Materialize a composite constant into a variable
   Bvariable *genVarForConstant(llvm::Constant *conval, Btype *type);
 
+  // Convert a constant to another type. Returns nullptr if the conversion is
+  // not allowed. Intended primarily for aggregate constants, where the two
+  // types are structurally identical but may differ due to the presence
+  // or absence of placeholders.
+  llvm::Constant *genConvertedConstant(llvm::Constant *fromVal,
+                                       llvm::Type *llToType);
+
   // Examine vector of values to test whether they are constants.
   // Checks for and handles pending composite inits.
   static bool
@@ -788,6 +795,11 @@
   // constant values (created in genVarForConstant).
   std::unordered_map<llvm::Value *, Bvariable *> genVarConstMap_;
 
+  // This table is a cache for converted constants; key is a pair
+  // [FromVal,ToType] and value is a constant value equivalent
+  // to FromVal but converted to ToType.
+  pairvalmap<llvm::Constant *, llvm::Type *, llvm::Constant *> genConvConstMap_;
+
   // Table for commoning strings by value. String constants have
   // concrete types like "[5 x i8]", whereas we would like to return
   // things that have type "i8*". To manage this, we eagerly create
diff --git a/unittests/BackendCore/BackendCoreTests.cpp b/unittests/BackendCore/BackendCoreTests.cpp
index c77e5b8..1eed879 100644
--- a/unittests/BackendCore/BackendCoreTests.cpp
+++ b/unittests/BackendCore/BackendCoreTests.cpp
@@ -395,4 +395,75 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 }
 
+TEST(BackendCallTests, TestCompositeInitGvarConvert) {
+
+  FcnTestHarness h("foo");
+  Llvm_backend *be = h.be();
+  Location loc;
+
+  // Regular struct of type { i8, *i8, i32 }
+  Btype *bt = be->bool_type();
+  Btype *pbt = be->pointer_type(bt);
+  Btype *bi32t = be->integer_type(false, 32);
+  Btype *s3t = mkBackendStruct(be, bt, "f0", pbt, "f1", bi32t, "f2", nullptr);
+
+  // Placeholder version of type { i8, *i8, i32 }
+  Btype *phst1 = be->placeholder_struct_type("ph", loc);
+  std::vector<Backend::Btyped_identifier> fields = {
+      Backend::Btyped_identifier("f0", bt, loc),
+      Backend::Btyped_identifier("f1", pbt, loc),
+      Backend::Btyped_identifier("f3", bi32t, loc)};
+  be->set_placeholder_struct_type(phst1, fields);
+
+  // Another struct that uses the both types above as fields
+  Btype *sqt = mkBackendStruct(be, s3t, "f0", phst1, "f1", nullptr);
+
+  // Create a global variable using this type
+  Bvariable *g1 =
+      be->global_variable("gv", "gv", sqt, false, /* is_external */
+                          false,                  /* is_hidden */
+                          false, /* unique_section */
+                          loc);
+
+  // Create initializers using the various types.  Note: the initial
+  // value for "f0" (non-placeholder type) is created using the
+  // corresponding placeholder and vice versa -- the intent is to
+  // make sure the conversion is handled properly.
+  Bexpression *f1con, *f2con, *topcon;
+  {
+    std::vector<Bexpression *> vals;
+    vals.push_back(be->zero_expression(bt));
+    vals.push_back(be->zero_expression(pbt));
+    vals.push_back(mkInt32Const(be, int32_t(101)));
+    f1con = be->constructor_expression(s3t, vals, loc);
+  }
+  {
+    std::vector<Bexpression *> vals;
+    vals.push_back(be->zero_expression(bt));
+    vals.push_back(be->zero_expression(pbt));
+    vals.push_back(mkInt32Const(be, int32_t(101)));
+    f2con = be->constructor_expression(phst1, vals, loc);
+  }
+  {
+    std::vector<Bexpression *> vals;
+    Bexpression *bc1 = be->convert_expression(phst1, f1con, loc);
+    vals.push_back(bc1);
+    Bexpression *bc2 = be->convert_expression(s3t, f2con, loc);
+    vals.push_back(bc2);
+    topcon = be->constructor_expression(sqt, vals, loc);
+  }
+
+  // Set initializer
+  be->global_variable_set_init(g1, topcon);
+
+  bool broken = h.finish(StripDebugInfo);
+  EXPECT_FALSE(broken && "Module failed to verify.");
+
+  be->dumpModule();
+
+  bool ok = h.expectModuleDumpContains("@gv = global { { i8, i8*, i32 }, %ph.0 } { { i8, i8*, i32 } { i8 0, i8* null, i32 101 }, %ph.0 { i8 0, i8* null, i32 101 } }");
+  EXPECT_TRUE(ok);
+
+}
+
 }