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);