gollvm: cleanup of convert_expression()

Fix some bugs in Llvm_backend::convert_expression(), mostly related to
conversions between ints and floats. A lot of the int/int conversions
were being generated incorrectly due to the fact that the previous
implementation would do nothing if the underlying LLVM types matched
(ex: int32 -> uint32); this is now cleaned up. Fix a couple of cases
where the conversion code was calling llvm::Type::isFloatTy instead of
llvm::Type::isFloatingPointTy.  Improve the unit test coverage for
scalar int/float conversions.

Change-Id: I43f9b5b9766a23e8e2031af6182218d3fd57585a
Reviewed-on: https://go-review.googlesource.com/45139
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/llvm-gofrontend/go-llvm.cpp b/llvm-gofrontend/go-llvm.cpp
index b97b7e8..41d1650 100644
--- a/llvm-gofrontend/go-llvm.cpp
+++ b/llvm-gofrontend/go-llvm.cpp
@@ -1118,32 +1118,62 @@
   if (expr->btype() == type)
     return expr;
 
-  llvm::Value *val = expr->value();
-  assert(val);
-
-  // No-op casts are ok
-  llvm::Type *toType = type->type();
-  llvm::Type *valType = val->getType();
-  if (toType == valType)
-    return expr;
-
   // When the frontend casts something to function type, what this
   // really means in the LLVM realm is "pointer to function" type.
+  llvm::Type *toType = type->type();
   if (toType->isFunctionTy()) {
     type = pointer_type(type);
     toType = type->type();
   }
 
-  LIRBuilder builder(context_, llvm::ConstantFolder());
-
-  // Adjust the "to" type if pending var expr.
-  if (expr->varExprPending()) {
-    llvm::Type *pet = llvm::PointerType::get(expr->btype()->type(),
-                                             addressSpace_);
-    if (valType == pet)
-      toType = llvm::PointerType::get(toType, addressSpace_);
+  // For composite-init-pending values, materialize a variable now.
+  if (expr->compositeInitPending()) {
+    assert(!expr->varExprPending());
+    expr = resolveCompositeInit(expr, nullptr);
   }
 
+  llvm::Value *val = expr->value();
+  assert(val);
+  llvm::Type *valType = val->getType();
+
+  // In the varexpr pending case, decide what to do depending on whether
+  // the var is in an lvalue or rvalue context. For something like
+  //
+  //     var y int32
+  //     z = int64(y)
+  //
+  // we want to force the load of "y" before converting to int64. For
+  // an lvalue context, the conversion will be applied to the pointed-to-type
+  // as well as the value type.
+  if (expr->varExprPending()) {
+    bool lvalue = expr->varContext().lvalue();
+    if (!lvalue) {
+      expr = resolveVarContext(expr);
+      val = expr->value();
+      valType = val->getType();
+    }
+    if (lvalue || useCopyForLoadStore(type->type())) {
+      llvm::Type *pet = llvm::PointerType::get(expr->btype()->type(),
+                                               addressSpace_);
+      if (valType == pet)
+        toType = llvm::PointerType::get(toType, addressSpace_);
+    }
+  }
+
+  // Compare types for equality using Btype::equivalent, since we need to
+  // pick up on differences in the Btype that are not present in the
+  // LLVM type (for example, uint32 vs int32), and since we want to
+  // screen out cases where there are different LLVM types as a result
+  // the use of opaque structs.
+  if (expr->btype()->equivalent(*type)) {
+     if (toType == valType)
+       return expr;
+     else
+       return nbuilder_.mkConversion(type, expr->value(), expr, location);
+  }
+
+  LIRBuilder builder(context_, llvm::ConstantFolder());
+
   // Pointer type to pointer-sized-integer type. Comes up when
   // converting function pointer to function descriptor (during
   // creation of function descriptor vals) or constant array to
@@ -1157,7 +1187,7 @@
 
   // Pointer-sized-integer type pointer type. This comes up
   // in type hash/compare functions.
-  if (toType && valType == llvmIntegerType()) {
+  if (toType->isPointerTy() && valType == llvmIntegerType()) {
     std::string tname(namegen("itpcast"));
     llvm::Value *itpcast = builder.CreateIntToPtr(val, toType, tname);
     return nbuilder_.mkConversion(type, itpcast, expr, location);
@@ -1181,7 +1211,8 @@
     unsigned tobits = toIntTyp->getBitWidth();
     llvm::Value *conv = nullptr;
     if (tobits > valbits) {
-      if (type->castToBIntegerType()->isUnsigned())
+      if (expr->btype()->type() == llvmBoolType() ||
+          expr->btype()->castToBIntegerType()->isUnsigned())
         conv = builder.CreateZExt(val, toType, namegen("zext"));
       else
         conv = builder.CreateSExt(val, toType, namegen("sext"));
@@ -1192,29 +1223,29 @@
   }
 
   // Float -> float conversions
-  if (toType->isFloatTy() && valType->isFloatTy()) {
+  if (toType->isFloatingPointTy() && valType->isFloatingPointTy()) {
     llvm::Value *conv = nullptr;
     if (toType == llvmFloatType() && valType == llvmDoubleType())
-      conv = builder.CreateFPTrunc(val, toType, namegen("trunc"));
+      conv = builder.CreateFPTrunc(val, toType, namegen("fptrunc"));
     else if (toType == llvmDoubleType() && valType == llvmFloatType())
-      conv = builder.CreateFPExt(val, toType, namegen("trunc"));
+      conv = builder.CreateFPExt(val, toType, namegen("fpext"));
     else
       assert(0 && "unexpected float type");
     return nbuilder_.mkConversion(type, conv, expr, location);
   }
 
   // Float -> integer conversions
-  if (toType->isIntegerTy() && valType->isFloatTy()) {
+  if (toType->isIntegerTy() && valType->isFloatingPointTy()) {
     llvm::Value *conv = nullptr;
     if (type->castToBIntegerType()->isUnsigned())
       conv = builder.CreateFPToUI(val, toType, namegen("ftoui"));
     else
-      conv = builder.CreateFPToUI(val, toType, namegen("ftosi"));
+      conv = builder.CreateFPToSI(val, toType, namegen("ftosi"));
     return nbuilder_.mkConversion(type, conv, expr, location);
   }
 
   // Integer -> float conversions
-  if (toType->isFloatTy() && valType->isIntegerTy()) {
+  if (toType->isFloatingPointTy() && valType->isIntegerTy()) {
     llvm::Value *conv = nullptr;
     if (expr->btype()->castToBIntegerType()->isUnsigned())
       conv = builder.CreateUIToFP(val, toType, namegen("uitof"));
@@ -2376,9 +2407,10 @@
   }
 
   // Case 8: also when creating slice values it's common for the
-  // frontend to assign pointer-to-X to unsafe.Pointer or equivalent,
+  // frontend to assign pointer-to-X to unsafe.Pointer (and vice versa)
   // without an explicit cast. Allow this for now.
-  if (dstToType == llvmPtrType() && llvm::isa<llvm::PointerType>(srcType)) {
+  if ((dstToType == llvmPtrType() && llvm::isa<llvm::PointerType>(srcType)) ||
+      (srcType == llvmPtrType() && llvm::isa<llvm::PointerType>(dstToType))) {
     std::string tag(namegen("cast"));
     llvm::Value *bitcast = builder->CreateBitCast(srcVal, dstToType, tag);
     return bitcast;
diff --git a/unittests/BackendCore/BackendExprTests.cpp b/unittests/BackendCore/BackendExprTests.cpp
index 9225312..b75f8ee 100644
--- a/unittests/BackendCore/BackendExprTests.cpp
+++ b/unittests/BackendCore/BackendExprTests.cpp
@@ -224,6 +224,142 @@
   EXPECT_FALSE(broken && "Module failed to verify.");
 }
 
+TEST(BackendExprTests, TestFloatConversionExpressions) {
+  FcnTestHarness h;
+  Llvm_backend *be = h.be();
+  Btype *bf32t = be->float_type(32);
+  Btype *bf64t = be->float_type(64);
+  Btype *bi32t = be->integer_type(false, 32);
+  Btype *bi64t = be->integer_type(false, 64);
+  Btype *bu32t = be->integer_type(true, 32);
+  Btype *bu64t = be->integer_type(true, 64);
+  BFunctionType *befty1 = mkFuncTyp(be,
+                                    L_PARM, bf32t,
+                                    L_PARM, bf64t,
+                                    L_PARM, bi32t,
+                                    L_PARM, bi64t,
+                                    L_PARM, bu32t,
+                                    L_PARM, bu64t,
+                                    L_END);
+  Bfunction *func = h.mkFunction("foo", befty1);
+  Location loc = h.loc();
+
+  std::vector<Bvariable *> parms;
+  for (unsigned ii = 0; ii < 6; ii++)
+    parms.push_back(func->getNthParamVar(ii));
+
+  // Generate pairwise conversions between permutations of p0 -- p5.
+  for (unsigned jj = 0; jj < 6; ++jj) {
+    for (unsigned ii = 0; ii < jj; ++ii) {
+      Bvariable *vii = parms[ii];
+      Bvariable *vjj = parms[jj];
+      Bexpression *vel1 = be->var_expression(vii, VE_lvalue, loc);
+      Bexpression *ver1 = be->var_expression(vjj, VE_rvalue, loc);
+      Bexpression *conv1 = be->convert_expression(vel1->btype(), ver1, loc);
+      h.mkAssign(vel1, conv1);
+      Bexpression *vel2 = be->var_expression(vjj, VE_lvalue, loc);
+      Bexpression *ver2 = be->var_expression(vii, VE_rvalue, loc);
+      Bexpression *conv2 = be->convert_expression(vel2->btype(), ver2, loc);
+      h.mkAssign(vel2, conv2);
+    }
+  }
+
+  const char *exp = R"RAW_RESULT(
+%p1.ld.0 = load double, double* %p1.addr
+  %fptrunc.0 = fptrunc double %p1.ld.0 to float
+  store float %fptrunc.0, float* %p0.addr
+  %p0.ld.0 = load float, float* %p0.addr
+  %fpext.0 = fpext float %p0.ld.0 to double
+  store double %fpext.0, double* %p1.addr
+  %p2.ld.0 = load i32, i32* %p2.addr
+  %sitof.0 = sitofp i32 %p2.ld.0 to float
+  store float %sitof.0, float* %p0.addr
+  %p0.ld.1 = load float, float* %p0.addr
+  %ftosi.0 = fptosi float %p0.ld.1 to i32
+  store i32 %ftosi.0, i32* %p2.addr
+  %p2.ld.1 = load i32, i32* %p2.addr
+  %sitof.1 = sitofp i32 %p2.ld.1 to double
+  store double %sitof.1, double* %p1.addr
+  %p1.ld.1 = load double, double* %p1.addr
+  %ftosi.1 = fptosi double %p1.ld.1 to i32
+  store i32 %ftosi.1, i32* %p2.addr
+  %p3.ld.0 = load i64, i64* %p3.addr
+  %sitof.2 = sitofp i64 %p3.ld.0 to float
+  store float %sitof.2, float* %p0.addr
+  %p0.ld.2 = load float, float* %p0.addr
+  %ftosi.2 = fptosi float %p0.ld.2 to i64
+  store i64 %ftosi.2, i64* %p3.addr
+  %p3.ld.1 = load i64, i64* %p3.addr
+  %sitof.3 = sitofp i64 %p3.ld.1 to double
+  store double %sitof.3, double* %p1.addr
+  %p1.ld.2 = load double, double* %p1.addr
+  %ftosi.3 = fptosi double %p1.ld.2 to i64
+  store i64 %ftosi.3, i64* %p3.addr
+  %p3.ld.2 = load i64, i64* %p3.addr
+  %trunc.0 = trunc i64 %p3.ld.2 to i32
+  store i32 %trunc.0, i32* %p2.addr
+  %p2.ld.2 = load i32, i32* %p2.addr
+  %sext.0 = sext i32 %p2.ld.2 to i64
+  store i64 %sext.0, i64* %p3.addr
+  %p4.ld.0 = load i32, i32* %p4.addr
+  %uitof.0 = uitofp i32 %p4.ld.0 to float
+  store float %uitof.0, float* %p0.addr
+  %p0.ld.3 = load float, float* %p0.addr
+  %ftoui.0 = fptoui float %p0.ld.3 to i32
+  store i32 %ftoui.0, i32* %p4.addr
+  %p4.ld.1 = load i32, i32* %p4.addr
+  %uitof.1 = uitofp i32 %p4.ld.1 to double
+  store double %uitof.1, double* %p1.addr
+  %p1.ld.3 = load double, double* %p1.addr
+  %ftoui.1 = fptoui double %p1.ld.3 to i32
+  store i32 %ftoui.1, i32* %p4.addr
+  %p4.ld.2 = load i32, i32* %p4.addr
+  store i32 %p4.ld.2, i32* %p2.addr
+  %p2.ld.3 = load i32, i32* %p2.addr
+  store i32 %p2.ld.3, i32* %p4.addr
+  %p4.ld.3 = load i32, i32* %p4.addr
+  %zext.0 = zext i32 %p4.ld.3 to i64
+  store i64 %zext.0, i64* %p3.addr
+  %p3.ld.3 = load i64, i64* %p3.addr
+  %trunc.3 = trunc i64 %p3.ld.3 to i32
+  store i32 %trunc.3, i32* %p4.addr
+  %p5.ld.0 = load i64, i64* %p5.addr
+  %uitof.2 = uitofp i64 %p5.ld.0 to float
+  store float %uitof.2, float* %p0.addr
+  %p0.ld.4 = load float, float* %p0.addr
+  %ftoui.2 = fptoui float %p0.ld.4 to i64
+  store i64 %ftoui.2, i64* %p5.addr
+  %p5.ld.1 = load i64, i64* %p5.addr
+  %uitof.3 = uitofp i64 %p5.ld.1 to double
+  store double %uitof.3, double* %p1.addr
+  %p1.ld.4 = load double, double* %p1.addr
+  %ftoui.3 = fptoui double %p1.ld.4 to i64
+  store i64 %ftoui.3, i64* %p5.addr
+  %p5.ld.2 = load i64, i64* %p5.addr
+  %trunc.4 = trunc i64 %p5.ld.2 to i32
+  store i32 %trunc.4, i32* %p2.addr
+  %p2.ld.4 = load i32, i32* %p2.addr
+  %sext.1 = sext i32 %p2.ld.4 to i64
+  store i64 %sext.1, i64* %p5.addr
+  %p5.ld.3 = load i64, i64* %p5.addr
+  store i64 %p5.ld.3, i64* %p3.addr
+  %p3.ld.4 = load i64, i64* %p3.addr
+  store i64 %p3.ld.4, i64* %p5.addr
+  %p5.ld.4 = load i64, i64* %p5.addr
+  %trunc.7 = trunc i64 %p5.ld.4 to i32
+  store i32 %trunc.7, i32* %p4.addr
+  %p4.ld.4 = load i32, i32* %p4.addr
+  %zext.1 = zext i32 %p4.ld.4 to i64
+  store i64 %zext.1, i64* %p5.addr
+    )RAW_RESULT";
+
+  bool isOK = h.expectBlock(exp);
+  EXPECT_TRUE(isOK && "Block does not have expected contents");
+
+  bool broken = h.finish(StripDebugInfo);
+  EXPECT_FALSE(broken && "Module failed to verify.");
+}
+
 TEST(BackendExprTests, MakeVarExpressions) {
   FcnTestHarness h("foo");
   Llvm_backend *be = h.be();
diff --git a/unittests/BackendCore/BackendNodeTests.cpp b/unittests/BackendCore/BackendNodeTests.cpp
index 5669db8..50e5271 100644
--- a/unittests/BackendCore/BackendNodeTests.cpp
+++ b/unittests/BackendCore/BackendNodeTests.cpp
@@ -164,12 +164,12 @@
 
   const char *exp = R"RAW_RESULT(
   %x.ld.0 = load i32, i32* %x
-  %cast.0 = bitcast i16* %z to i32*
-  %.ld.0 = load i32, i32* %cast.0
-  %add.0 = add i32 %x.ld.0, %.ld.0
+  %z.ld.0 = load i16, i16* %z
+  %sext.0 = sext i16 %z.ld.0 to i32
+  %add.0 = add i32 %x.ld.0, %sext.0
   %y.ld.0 = load i32*, i32** %y
-  %.ld.1 = load i32, i32* %y.ld.0
-  %add.1 = add i32 %add.0, %.ld.1
+  %.ld.0 = load i32, i32* %y.ld.0
+  %add.1 = add i32 %add.0, %.ld.0
     )RAW_RESULT";
 
   bool isOK = h.expectRepr(add, exp);
diff --git a/unittests/BackendCore/BackendPointerExprTests.cpp b/unittests/BackendCore/BackendPointerExprTests.cpp
index be01d57..5535b53 100644
--- a/unittests/BackendCore/BackendPointerExprTests.cpp
+++ b/unittests/BackendCore/BackendPointerExprTests.cpp
@@ -409,10 +409,10 @@
 
   Bvariable *p0 = func->getNthParamVar(0);
   Bvariable *p3 = func->getNthParamVar(2);
+  Btype *bi64t = be->integer_type(false, 64);
 
   {
     // deref(ptr_offset(p3, 5)) = 9
-    Btype *bi64t = be->integer_type(false, 64);
     Bexpression *ve = be->var_expression(p3, VE_lvalue, loc);
     Bexpression *cfive = mkInt32Const(be, 5);
     Bexpression *poe1 = be->pointer_offset_expression(ve, cfive, loc);
@@ -422,27 +422,26 @@
   }
 
   {
-    // p0 = deref(ptr_offset((uint32*)p3, 7))
+    // p0 = int32(deref(ptr_offset(p3, 7)))
     Btype *bi32t = be->integer_type(false, 32);
     Bexpression *ve = be->var_expression(p0, VE_lvalue, loc);
     Bexpression *ver = be->var_expression(p3, VE_rvalue, loc);
-    Btype *bpi32t = be->pointer_type(bi32t);
-    Bexpression *bcon = be->convert_expression(bpi32t, ver, loc);
     Bexpression *cseven = mkInt32Const(be, 7);
-    Bexpression *poe2 = be->pointer_offset_expression(bcon, cseven, loc);
-    Bexpression *der = be->indirect_expression(bi32t, poe2, false, loc);
-    h.mkAssign(ve, der);
+    Bexpression *poe2 = be->pointer_offset_expression(ver, cseven, loc);
+    Bexpression *der = be->indirect_expression(bi64t, poe2, false, loc);
+    Bexpression *con32 = be->convert_expression(bi32t, der, loc);
+    h.mkAssign(ve, con32);
   }
 
   const char *exp = R"RAW_RESULT(
   %ptroff.0 = getelementptr i64*, i64** %param3.addr, i32 5
   %param3.ptroff.ld.0 = load i64*, i64** %ptroff.0
   store i64 9, i64* %param3.ptroff.ld.0
-  %cast.0 = bitcast i64** %param3.addr to i32**
-  %ptroff.1 = getelementptr i32*, i32** %cast.0, i32 7
-  %.ptroff.ld.0 = load i32*, i32** %ptroff.1
-  %.ld.0 = load i32, i32* %.ptroff.ld.0
-  store i32 %.ld.0, i32* %param1.addr
+  %ptroff.1 = getelementptr i64*, i64** %param3.addr, i32 7
+  %param3.ptroff.ld.1 = load i64*, i64** %ptroff.1
+  %.ld.0 = load i64, i64* %param3.ptroff.ld.1
+  %trunc.0 = trunc i64 %.ld.0 to i32
+  store i32 %trunc.0, i32* %param1.addr
   )RAW_RESULT";
 
   bool isOK = h.expectBlock(exp);