gollvm: complex number support
Add support of complex expressions:
- real, imag, complex builtins
- add, sub, mult (division is lowered to runtime calls
by the front end)
- comparisons (==, !=)
- conversions (complex64 <-> complex128)
- creating complex constants
Change-Id: I0d935f088ccac7af1b7028d4e88116c7b77231ef
Reviewed-on: https://go-review.googlesource.com/45530
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/llvm-gofrontend/go-llvm-bnode.cpp b/llvm-gofrontend/go-llvm-bnode.cpp
index e950bc8..7636acf 100644
--- a/llvm-gofrontend/go-llvm-bnode.cpp
+++ b/llvm-gofrontend/go-llvm-bnode.cpp
@@ -432,6 +432,7 @@
inst->setName(name);
Bvariable *tvar = new Bvariable(varType, loc, name, LocalVar, true, inst);
tempvars_[inst] = tvar;
+ tvar->markAsTemporary();
return tvar;
}
diff --git a/llvm-gofrontend/go-llvm-btype.cpp b/llvm-gofrontend/go-llvm-btype.cpp
index 3a6311d..c5f434d 100644
--- a/llvm-gofrontend/go-llvm-btype.cpp
+++ b/llvm-gofrontend/go-llvm-btype.cpp
@@ -68,7 +68,7 @@
if (isPlaceholder() != other.isPlaceholder())
return false;
bool compareLlvmType = true;
- if ((ctl & IgnoreNames) != 0 && type()->isStructTy())
+ if ((ctl & IgnoreNames) != 0 && type()->isStructTy() && flavor_ != ComplexT)
compareLlvmType = false;
if (compareLlvmType && type() != other.type())
return false;
diff --git a/llvm-gofrontend/go-llvm.cpp b/llvm-gofrontend/go-llvm.cpp
index 2cb38b5..d1c22fa 100644
--- a/llvm-gofrontend/go-llvm.cpp
+++ b/llvm-gofrontend/go-llvm.cpp
@@ -882,7 +882,6 @@
std::string tname(namegen("tmp"));
tvar = nbuilder_.mkTempVar(expr->btype(), expr->location(), tname);
assert(tvar != errorVariable_.get());
- tvar->markAsTemporary();
storage = tvar->value();
setPending = true;
}
@@ -1030,8 +1029,53 @@
Bexpression *Llvm_backend::complex_constant_expression(Btype *btype,
mpc_t val) {
- assert(false && "Llvm_backend::complex_constant_expression not yet impl");
- return nullptr;
+ if (btype == errorType())
+ return errorExpression();
+
+ BComplexType *bct = btype->castToBComplexType();
+ assert(bct);
+ llvm::Type *llt = btype->type();
+ assert(llt->isStructTy());
+ llvm::StructType *llst = llvm::cast<llvm::StructType>(llt);
+ assert(llst->getNumElements() == 2);
+ llvm::Type *llet = llst->getElementType(0);
+ assert(llet == llst->getElementType(1));
+
+ std::vector<Bexpression *> exps;
+
+ if (llet == llvmFloatType()) {
+ float frval = mpfr_get_flt(mpc_realref(val), GMP_RNDN);
+ float fival = mpfr_get_flt(mpc_imagref(val), GMP_RNDN);
+ llvm::APFloat apr(frval);
+ llvm::APFloat api(fival);
+ llvm::Constant *rcon = llvm::ConstantFP::get(context_, apr);
+ llvm::Constant *icon = llvm::ConstantFP::get(context_, api);
+
+ Btype *bet = floatType(32);
+ Bexpression *brcon = nbuilder_.mkConst(bet, rcon);
+ Bexpression *bicon = nbuilder_.mkConst(bet, icon);
+ exps.push_back(brcon);
+ exps.push_back(bicon);
+ } else if (llet == llvmDoubleType()) {
+ double drval = mpfr_get_d(mpc_realref(val), GMP_RNDN);
+ double dival = mpfr_get_d(mpc_imagref(val), GMP_RNDN);
+ llvm::APFloat apr(drval);
+ llvm::APFloat api(dival);
+ llvm::Constant *rcon = llvm::ConstantFP::get(context_, apr);
+ llvm::Constant *icon = llvm::ConstantFP::get(context_, api);
+
+ Btype *bet = floatType(64);
+ Bexpression *brcon = nbuilder_.mkConst(bet, rcon);
+ Bexpression *bicon = nbuilder_.mkConst(bet, icon);
+ exps.push_back(brcon);
+ exps.push_back(bicon);
+ } else {
+ assert(false && "unknown complex type");
+ return nullptr;
+ }
+
+ std::vector<unsigned long> indexes = {0, 1};
+ return makeConstCompositeExpr(btype, llst, 2, indexes, exps, Location());
}
// Make a constant string expression.
@@ -1090,16 +1134,72 @@
Bexpression *Llvm_backend::real_part_expression(Bexpression *bcomplex,
Location location) {
- assert(false && "Llvm_backend::real_part_expression not yet impl");
- return nullptr;
+ if (bcomplex == errorExpression())
+ return errorExpression();
+
+ if (bcomplex->compositeInitPending())
+ bcomplex = resolveCompositeInit(bcomplex, nullptr);
+
+ // Construct an appropriate GEP
+ llvm::Type *llt = bcomplex->btype()->type();
+ assert(llt->isStructTy());
+ llvm::StructType *llst = llvm::cast<llvm::StructType>(llt);
+ llvm::Value *cval = bcomplex->value();
+ llvm::Value *fval;
+ if (llvm::isa<llvm::Constant>(cval) && cval->getType()->isStructTy())
+ fval = llvm::cast<llvm::Constant>(cval)->getAggregateElement((unsigned int)0);
+ else
+ fval = makeFieldGEP(llst, 0, cval);
+ BComplexType *bct = bcomplex->btype()->castToBComplexType();
+ assert(bct);
+ Btype *bft = floatType(bct->bits()/2);
+
+ // Wrap result in a Bexpression
+ Bexpression *rval = nbuilder_.mkStructField(bft, fval, bcomplex,
+ 0, location);
+
+ std::string tag(bcomplex->tag());
+ tag += ".real";
+ rval->setTag(tag);
+
+ // We're done
+ return rval;
}
// Return the imaginary part of a complex expression.
Bexpression *Llvm_backend::imag_part_expression(Bexpression *bcomplex,
Location location) {
- assert(false && "Llvm_backend::imag_part_expression not yet impl");
- return nullptr;
+ if (bcomplex == errorExpression())
+ return errorExpression();
+
+ if (bcomplex->compositeInitPending())
+ bcomplex = resolveCompositeInit(bcomplex, nullptr);
+
+ // Construct an appropriate GEP
+ llvm::Type *llt = bcomplex->btype()->type();
+ assert(llt->isStructTy());
+ llvm::StructType *llst = llvm::cast<llvm::StructType>(llt);
+ llvm::Value *cval = bcomplex->value();
+ llvm::Value *fval;
+ if (llvm::isa<llvm::Constant>(cval) && cval->getType()->isStructTy())
+ fval = llvm::cast<llvm::Constant>(cval)->getAggregateElement((unsigned int)1);
+ else
+ fval = makeFieldGEP(llst, 1, cval);
+ BComplexType *bct = bcomplex->btype()->castToBComplexType();
+ assert(bct);
+ Btype *bft = floatType(bct->bits()/2);
+
+ // Wrap result in a Bexpression
+ Bexpression *rval = nbuilder_.mkStructField(bft, fval, bcomplex,
+ 1, location);
+
+ std::string tag(bcomplex->tag());
+ tag += ".imag";
+ rval->setTag(tag);
+
+ // We're done
+ return rval;
}
// Make a complex expression given its real and imaginary parts.
@@ -1107,8 +1207,26 @@
Bexpression *Llvm_backend::complex_expression(Bexpression *breal,
Bexpression *bimag,
Location location) {
- assert(false && "Llvm_backend::complex_expression not yet impl");
- return nullptr;
+ if (breal == errorExpression() || bimag == errorExpression())
+ return errorExpression();
+
+ assert(breal->btype() == bimag->btype());
+ BFloatType *bet = breal->btype()->castToBFloatType();
+ assert(bet);
+ Btype *bct = complexType(bet->bits()*2);
+ llvm::Type *llt = bct->type();
+ assert(llt->isStructTy());
+ llvm::StructType *llst = llvm::cast<llvm::StructType>(llt);
+
+ std::vector<Bexpression *> vals = {breal, bimag};
+ std::vector<unsigned long> indexes = {0, 1};
+
+ // Constant values?
+ bool isConstant = valuesAreConstant(vals);
+ if (isConstant)
+ return makeConstCompositeExpr(bct, llst, 2, indexes, vals, location);
+ else
+ return makeDelayedCompositeExpr(bct, llst, 2, indexes, vals, location);
}
// An expression that converts an expression to a different type.
@@ -1128,6 +1246,10 @@
toType = type->type();
}
+ // Complex -> complex conversion
+ if (type->castToBComplexType())
+ return makeComplexConvertExpr(type, expr, location);
+
// For composite-init-pending values, materialize a variable now.
if (expr->compositeInitPending()) {
assert(!expr->varExprPending());
@@ -1264,6 +1386,48 @@
return expr;
}
+Bexpression *Llvm_backend::makeComplexConvertExpr(Btype *type,
+ Bexpression *expr,
+ Location location) {
+ if (expr->btype()->type() == type->type())
+ return expr;
+
+ BComplexType *bct = type->castToBComplexType();
+ assert(bct);
+ assert(expr->btype()->castToBComplexType());
+ Btype *bft = floatType(bct->bits()/2);
+
+ // We need to avoid sharing between real part and imag part of the operand.
+ // Create temp variables and assign operand to the temp variable first.
+ // TODO: maybe not make temp var if the operand is already a var or constant?
+ std::string tname1(namegen("tmp"));
+ Bvariable *var = nbuilder_.mkTempVar(expr->btype(), location, tname1);
+ Bfunction *dummyFcn = errorFunction_.get();
+ Bstatement *einit = makeInitStatement(dummyFcn, var, expr);
+
+ Bexpression *vex = nbuilder_.mkVar(var, location);
+ vex->setVarExprPending(false, 0);
+ Bexpression *vexr = real_part_expression(vex, location);
+ Bexpression *vexi = imag_part_expression(vex, location);
+
+ // Make the conversion
+ Bexpression *valr = convert_expression(bft, vexr, location);
+ Bexpression *vali = convert_expression(bft, vexi, location);
+ Bexpression *val = complex_expression(valr, vali, location);
+
+ // Wrap result and the init statements in a compound expression.
+ // Currently we can't resolve composite storage for compound
+ // expression, so we resolve the inner complex expression
+ // here with another temp variable.
+ std::string tname2(namegen("tmp"));
+ Bvariable *rvar = nbuilder_.mkTempVar(val->btype(), location, tname2);
+ Bstatement *rinit = makeInitStatement(dummyFcn, rvar, val);
+ Bexpression *rvex = nbuilder_.mkVar(rvar, location);
+ Bstatement *init = statement_list(std::vector<Bstatement*>{einit, rinit});
+ return compound_expression(init, rvex, location);
+}
+
+
// Get the address of a function.
Bexpression *Llvm_backend::function_code_expression(Bfunction *bfunc,
@@ -1309,6 +1473,7 @@
unsigned fieldIndex,
llvm::Value *sptr)
{
+ assert(sptr->getType()->isPointerTy());
LIRBuilder builder(context_, llvm::ConstantFolder());
assert(fieldIndex < llst->getNumElements());
std::string tag(namegen("field"));
@@ -1591,12 +1756,18 @@
if (left == errorExpression() || right == errorExpression())
return errorExpression();
+ Btype *bltype = left->btype();
+ Btype *brtype = right->btype();
+ BComplexType *blctype = bltype->castToBComplexType();
+ BComplexType *brctype = brtype->castToBComplexType();
+ assert((blctype == nullptr) == (brctype == nullptr));
+ if (blctype)
+ return makeComplexBinaryExpr(op, left, right, location);
+
left = resolveVarContext(left);
right = resolveVarContext(right);
assert(left->value() && right->value());
- Btype *bltype = left->btype();
- Btype *brtype = right->btype();
std::pair<llvm::Value *, llvm::Value *> converted =
convertForBinary(left, right);
llvm::Value *leftVal = converted.first;
@@ -1733,6 +1904,76 @@
return nbuilder_.mkBinaryOp(op, bltype, val, left, right, location);
}
+Bexpression *Llvm_backend::makeComplexBinaryExpr(Operator op, Bexpression *left,
+ Bexpression *right,
+ Location location) {
+ // We need to avoid sharing between real part and imag part of the operand.
+ // Create temp variables and assign operands to the temp vars first.
+ // TODO: maybe not make temp var if the operand is already a var or constant?
+ std::string tname1(namegen("tmp")), tname2(namegen("tmp"));
+ Bvariable *lvar = nbuilder_.mkTempVar(left->btype(), location, tname1);
+ Bvariable *rvar = nbuilder_.mkTempVar(right->btype(), location, tname2);
+ Bfunction *dummyFcn = errorFunction_.get();
+ Bstatement *linit = makeInitStatement(dummyFcn, lvar, left);
+ Bstatement *rinit = makeInitStatement(dummyFcn, rvar, right);
+
+ Bexpression *lvex = nbuilder_.mkVar(lvar, location);
+ lvex->setVarExprPending(false, 0);
+ Bexpression *rvex = nbuilder_.mkVar(rvar, location);
+ rvex->setVarExprPending(false, 0);
+ Bexpression *lr = real_part_expression(lvex, location);
+ Bexpression *li = imag_part_expression(lvex, location);
+ Bexpression *rr = real_part_expression(rvex, location);
+ Bexpression *ri = imag_part_expression(rvex, location);
+ Bexpression *val;
+
+ switch (op) {
+ case OPERATOR_PLUS:
+ case OPERATOR_MINUS: {
+ Bexpression *valr = binary_expression(op, lr, rr, location);
+ Bexpression *vali = binary_expression(op, li, ri, location);
+ val = complex_expression(valr, vali, location);
+ break;
+ }
+ case OPERATOR_MULT: {
+ // (a+bi)*(c+di) = (ac-bd) + (ad+bc)i
+ Bexpression *ac = binary_expression(OPERATOR_MULT, lr, rr, location);
+ Bexpression *bd = binary_expression(OPERATOR_MULT, li, ri, location);
+ Bexpression *ad = binary_expression(OPERATOR_MULT, lr, ri, location);
+ Bexpression *bc = binary_expression(OPERATOR_MULT, li, rr, location);
+ Bexpression *valr = binary_expression(OPERATOR_MINUS, ac, bd, location);
+ Bexpression *vali = binary_expression(OPERATOR_PLUS, ad, bc, location);
+ val = complex_expression(valr, vali, location);
+ break;
+ }
+ case OPERATOR_EQEQ:
+ case OPERATOR_NOTEQ: {
+ Bexpression *cmpr = binary_expression(op, lr, rr, location);
+ Bexpression *cmpi = binary_expression(op, li, ri, location);
+ if (op == OPERATOR_EQEQ)
+ val = binary_expression(OPERATOR_ANDAND, cmpr, cmpi, location);
+ else
+ val = binary_expression(OPERATOR_OROR, cmpr, cmpi, location);
+ Bstatement *init = statement_list(std::vector<Bstatement*>{linit, rinit});
+ return compound_expression(init, val, location);
+ }
+ default:
+ std::cerr << "Op " << op << " unhandled\n";
+ assert(false);
+ }
+
+ // Wrap result and the init statements in a compound expression.
+ // Currently we can't resolve composite storage for compound
+ // expression, so we resolve the inner complex expression
+ // here with another temp variable.
+ std::string tname3(namegen("tmp"));
+ Bvariable *vvar = nbuilder_.mkTempVar(val->btype(), location, tname3);
+ Bstatement *vinit = makeInitStatement(dummyFcn, vvar, val);
+ Bexpression *vvex = nbuilder_.mkVar(vvar, location);
+ Bstatement *init = statement_list(std::vector<Bstatement*>{linit, rinit, vinit});
+ return compound_expression(init, vvex, location);
+}
+
bool
Llvm_backend::valuesAreConstant(const std::vector<Bexpression *> &vals)
{
@@ -2291,6 +2532,14 @@
if (var == errorVariable_.get() || init == errorExpression() ||
bfunction == errorFunction_.get())
return errorStatement();
+
+ return makeInitStatement(bfunction, var, init);
+}
+
+Bstatement *Llvm_backend::makeInitStatement(Bfunction *bfunction,
+ Bvariable *var,
+ Bexpression *init)
+{
if (init) {
if (init->compositeInitPending()) {
init = resolveCompositeInit(init, var->value());
diff --git a/llvm-gofrontend/go-llvm.h b/llvm-gofrontend/go-llvm.h
index ceacc92..59747da 100644
--- a/llvm-gofrontend/go-llvm.h
+++ b/llvm-gofrontend/go-llvm.h
@@ -448,6 +448,16 @@
const std::vector<Bexpression *> &vals,
Location location);
+ // Helper for creating a complex binary expression.
+ Bexpression *makeComplexBinaryExpr(Operator op,
+ Bexpression *left,
+ Bexpression *right,
+ Location location);
+
+ // Helper for creating a complex conversion expression.
+ Bexpression *makeComplexConvertExpr(Btype *type, Bexpression *expr,
+ Location location);
+
// Field GEP helper
llvm::Value *makeFieldGEP(llvm::StructType *llst,
unsigned fieldIndex,
@@ -467,6 +477,10 @@
Bstatement *makeAssignment(Bfunction *function, llvm::Value *lvalue,
Bexpression *lhs, Bexpression *rhs, Location);
+ // Helper to make init statement
+ Bstatement *makeInitStatement(Bfunction *bfunction, Bvariable *var,
+ Bexpression *init);
+
// Helper to set up entry block for function
llvm::BasicBlock *genEntryBlock(Bfunction *bfunction);
diff --git a/unittests/BackendCore/BackendExprTests.cpp b/unittests/BackendCore/BackendExprTests.cpp
index b75f8ee..0b045d9 100644
--- a/unittests/BackendCore/BackendExprTests.cpp
+++ b/unittests/BackendCore/BackendExprTests.cpp
@@ -119,6 +119,44 @@
}
}
+TEST(BackendExprTests, MakeComplexConstExpr) {
+ llvm::LLVMContext C;
+
+ std::unique_ptr<Backend> be(go_get_backend(C));
+
+ // Complex constants
+ Btype *bc64t = be->complex_type(64);
+ Btype *bc128t = be->complex_type(128);
+ Btype *bf32t = be->float_type(32);
+ Btype *bf64t = be->float_type(64);
+ static const double f64vals[] = {1.7976931348623158e+308, 0.0, 1.1,
+ 2.2250738585072014e-308};
+ for (auto valr : f64vals)
+ for (auto vali : f64vals) {
+ mpc_t mpc_val;
+ mpc_init2(mpc_val, 256);
+ mpc_set_d_d(mpc_val, valr, vali, GMP_RNDN);
+
+ Bexpression *bc64val = be->complex_constant_expression(bc64t, mpc_val);
+ ASSERT_TRUE(bc64val != nullptr);
+ llvm::StructType *llc64st = llvm::cast<llvm::StructType>(bc64t->type());
+ llvm::SmallVector<llvm::Constant *, 2> llf32vals(2);
+ llf32vals[0] = llvm::ConstantFP::get(bf32t->type(), valr);
+ llf32vals[1] = llvm::ConstantFP::get(bf32t->type(), vali);
+ EXPECT_EQ(bc64val->value(), llvm::ConstantStruct::get(llc64st, llf32vals));
+
+ Bexpression *bc128val = be->complex_constant_expression(bc128t, mpc_val);
+ ASSERT_TRUE(bc128val != nullptr);
+ llvm::StructType *llc128st = llvm::cast<llvm::StructType>(bc128t->type());
+ llvm::SmallVector<llvm::Constant *, 2> llf64vals(2);
+ llf64vals[0] = llvm::ConstantFP::get(bf64t->type(), valr);
+ llf64vals[1] = llvm::ConstantFP::get(bf64t->type(), vali);
+ EXPECT_EQ(bc128val->value(), llvm::ConstantStruct::get(llc128st, llf64vals));
+
+ mpc_clear(mpc_val);
+ }
+}
+
TEST(BackendExprTests, MakeZeroValueExpr) {
llvm::LLVMContext C;
@@ -139,6 +177,10 @@
Btype *s2t = mkBackendStruct(be, pbt, "f1", bi32t, "f2", nullptr);
Bexpression *bszero = be->zero_expression(s2t);
ASSERT_TRUE(bszero != nullptr);
+ Btype *bct = be->complex_type(128);
+ Bexpression *bczero = be->zero_expression(bct);
+ ASSERT_TRUE(bczero != nullptr);
+ EXPECT_EQ(repr(bczero->value()), "{ double, double } zeroinitializer");
// Error handling
EXPECT_EQ(be->zero_expression(be->error_type()), be->error_expression());
@@ -360,6 +402,120 @@
EXPECT_FALSE(broken && "Module failed to verify.");
}
+TEST(BackendExprTests, TestComplexConversionExpression) {
+ FcnTestHarness h("foo");
+ Llvm_backend *be = h.be();
+ BFunctionType *befty = mkFuncTyp(be, L_END);
+ Bfunction *func = h.mkFunction("foo", befty);
+ Location loc;
+
+ Btype *bc64t = be->complex_type(64);
+ Btype *bc128t = be->complex_type(128);
+
+ // var a, b complex64
+ // var x, y complex128
+ Bvariable *a = h.mkLocal("a", bc64t);
+ Bvariable *b = h.mkLocal("b", bc64t);
+ Bvariable *x = h.mkLocal("x", bc128t);
+ Bvariable *y = h.mkLocal("y", bc128t);
+
+ // a = complex64(x)
+ Bexpression *avex1 = be->var_expression(a, VE_lvalue, loc);
+ Bexpression *xvex1 = be->var_expression(x, VE_rvalue, loc);
+ Bexpression *convex1 = be->convert_expression(bc64t, xvex1, loc);
+ h.mkAssign(avex1, convex1);
+
+ // y = complex128(b)
+ Bexpression *yvex2 = be->var_expression(y, VE_lvalue, loc);
+ Bexpression *bvex2 = be->var_expression(b, VE_rvalue, loc);
+ Bexpression *convex2 = be->convert_expression(bc128t, bvex2, loc);
+ h.mkAssign(yvex2, convex2);
+
+ // No-op conversions
+ // a = complex64(b)
+ Bexpression *avex3 = be->var_expression(a, VE_lvalue, loc);
+ Bexpression *bvex3 = be->var_expression(b, VE_rvalue, loc);
+ Bexpression *convex3 = be->convert_expression(bc64t, bvex3, loc);
+ h.mkAssign(avex3, convex3);
+
+ // x = complex128(y)
+ Bexpression *xvex4 = be->var_expression(x, VE_lvalue, loc);
+ Bexpression *yvex4 = be->var_expression(y, VE_rvalue, loc);
+ Bexpression *convex4 = be->convert_expression(bc128t, yvex4, loc);
+ h.mkAssign(xvex4, convex4);
+
+ const char *exp = R"RAW_RESULT(
+ define void @foo.1(i8* nest %nest.1) #0 {
+ entry:
+ %tmp.3 = alloca { double, double }
+ %tmp.2 = alloca { float, float }
+ %tmp.1 = alloca { float, float }
+ %tmp.0 = alloca { double, double }
+ %a = alloca { float, float }
+ %b = alloca { float, float }
+ %x = alloca { double, double }
+ %y = alloca { double, double }
+ %cast.0 = bitcast { float, float }* %a to i8*
+ %cast.1 = bitcast { float, float }* @const.0 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.0, i8* %cast.1, i64 8, i32 8, i1 false)
+ %cast.2 = bitcast { float, float }* %b to i8*
+ %cast.3 = bitcast { float, float }* @const.1 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.2, i8* %cast.3, i64 8, i32 8, i1 false)
+ %cast.4 = bitcast { double, double }* %x to i8*
+ %cast.5 = bitcast { double, double }* @const.2 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.4, i8* %cast.5, i64 16, i32 8, i1 false)
+ %cast.6 = bitcast { double, double }* %y to i8*
+ %cast.7 = bitcast { double, double }* @const.3 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.6, i8* %cast.7, i64 16, i32 8, i1 false)
+ %cast.8 = bitcast { double, double }* %tmp.0 to i8*
+ %cast.9 = bitcast { double, double }* %x to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.8, i8* %cast.9, i64 16, i32 8, i1 false)
+ %field.0 = getelementptr inbounds { double, double }, { double, double }* %tmp.0, i32 0, i32 0
+ %.real.ld.0 = load double, double* %field.0
+ %fptrunc.0 = fptrunc double %.real.ld.0 to float
+ %field.1 = getelementptr inbounds { double, double }, { double, double }* %tmp.0, i32 0, i32 1
+ %.imag.ld.0 = load double, double* %field.1
+ %fptrunc.1 = fptrunc double %.imag.ld.0 to float
+ %field.2 = getelementptr inbounds { float, float }, { float, float }* %tmp.1, i32 0, i32 0
+ store float %fptrunc.0, float* %field.2
+ %field.3 = getelementptr inbounds { float, float }, { float, float }* %tmp.1, i32 0, i32 1
+ store float %fptrunc.1, float* %field.3
+ %cast.10 = bitcast { float, float }* %a to i8*
+ %cast.11 = bitcast { float, float }* %tmp.1 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.10, i8* %cast.11, i64 8, i32 8, i1 false)
+ %cast.12 = bitcast { float, float }* %tmp.2 to i8*
+ %cast.13 = bitcast { float, float }* %b to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.12, i8* %cast.13, i64 8, i32 8, i1 false)
+ %field.4 = getelementptr inbounds { float, float }, { float, float }* %tmp.2, i32 0, i32 0
+ %.real.ld.1 = load float, float* %field.4
+ %fpext.0 = fpext float %.real.ld.1 to double
+ %field.5 = getelementptr inbounds { float, float }, { float, float }* %tmp.2, i32 0, i32 1
+ %.imag.ld.1 = load float, float* %field.5
+ %fpext.1 = fpext float %.imag.ld.1 to double
+ %field.6 = getelementptr inbounds { double, double }, { double, double }* %tmp.3, i32 0, i32 0
+ store double %fpext.0, double* %field.6
+ %field.7 = getelementptr inbounds { double, double }, { double, double }* %tmp.3, i32 0, i32 1
+ store double %fpext.1, double* %field.7
+ %cast.14 = bitcast { double, double }* %y to i8*
+ %cast.15 = bitcast { double, double }* %tmp.3 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.14, i8* %cast.15, i64 16, i32 8, i1 false)
+ %cast.16 = bitcast { float, float }* %a to i8*
+ %cast.17 = bitcast { float, float }* %b to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.16, i8* %cast.17, i64 8, i32 8, i1 false)
+ %cast.18 = bitcast { double, double }* %x to i8*
+ %cast.19 = bitcast { double, double }* %y to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.18, i8* %cast.19, i64 16, i32 8, i1 false)
+ ret void
+ }
+ )RAW_RESULT";
+
+ bool broken = h.finish(StripDebugInfo);
+ EXPECT_FALSE(broken && "Module failed to verify.");
+
+ bool isOK = h.expectValue(func->function(), exp);
+ EXPECT_TRUE(isOK && "Function does not have expected contents");
+}
+
TEST(BackendExprTests, MakeVarExpressions) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
@@ -805,6 +961,255 @@
EXPECT_FALSE(broken && "Module failed to verify.");
}
+TEST(BackendExprTests, TestComplexOps) {
+ FcnTestHarness h;
+ Llvm_backend *be = h.be();
+ BFunctionType *befty = mkFuncTyp(be, L_END);
+ Bfunction *func = h.mkFunction("foo", befty);
+ Location loc;
+
+ Operator optotest[] = {OPERATOR_PLUS, OPERATOR_MINUS, OPERATOR_MULT,
+ OPERATOR_EQEQ, OPERATOR_NOTEQ};
+
+ // var x, y, z complex128
+ // var b bool
+ Btype *bc128t = be->complex_type(128);
+ Btype *bt = be->bool_type();
+ Bvariable *x = h.mkLocal("x", bc128t);
+ Bvariable *y = h.mkLocal("y", bc128t);
+ Bvariable *z = h.mkLocal("z", bc128t);
+ Bvariable *b = h.mkLocal("b", bt);
+
+ for (auto op : optotest) {
+ Bexpression *bleft = be->var_expression(x, VE_rvalue, loc);
+ Bexpression *bright = be->var_expression(y, VE_rvalue, loc);
+ Bexpression *bop = be->binary_expression(op, bleft, bright, Location());
+ Bexpression *bvex = be->var_expression(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ ? b : z,
+ VE_lvalue, loc);
+ h.mkAssign(bvex, bop);
+ }
+
+ const char *exp = R"RAW_RESULT(
+ define void @foo(i8* nest %nest.0) #0 {
+ entry:
+ %tmp.12 = alloca { double, double }
+ %tmp.11 = alloca { double, double }
+ %tmp.10 = alloca { double, double }
+ %tmp.9 = alloca { double, double }
+ %tmp.8 = alloca { double, double }
+ %tmp.7 = alloca { double, double }
+ %tmp.6 = alloca { double, double }
+ %tmp.4 = alloca { double, double }
+ %tmp.3 = alloca { double, double }
+ %tmp.2 = alloca { double, double }
+ %tmp.5 = alloca { double, double }
+ %tmp.1 = alloca { double, double }
+ %tmp.0 = alloca { double, double }
+ %x = alloca { double, double }
+ %y = alloca { double, double }
+ %z = alloca { double, double }
+ %b = alloca i8
+ %cast.0 = bitcast { double, double }* %x to i8*
+ %cast.1 = bitcast { double, double }* @const.0 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.0, i8* %cast.1, i64 16, i32 8, i1 false)
+ %cast.2 = bitcast { double, double }* %y to i8*
+ %cast.3 = bitcast { double, double }* @const.1 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.2, i8* %cast.3, i64 16, i32 8, i1 false)
+ %cast.4 = bitcast { double, double }* %z to i8*
+ %cast.5 = bitcast { double, double }* @const.2 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.4, i8* %cast.5, i64 16, i32 8, i1 false)
+ store i8 0, i8* %b
+ %cast.6 = bitcast { double, double }* %tmp.0 to i8*
+ %cast.7 = bitcast { double, double }* %x to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.6, i8* %cast.7, i64 16, i32 8, i1 false)
+ %cast.8 = bitcast { double, double }* %tmp.1 to i8*
+ %cast.9 = bitcast { double, double }* %y to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.8, i8* %cast.9, i64 16, i32 8, i1 false)
+ %field.0 = getelementptr inbounds { double, double }, { double, double }* %tmp.0, i32 0, i32 0
+ %.real.ld.0 = load double, double* %field.0
+ %field.2 = getelementptr inbounds { double, double }, { double, double }* %tmp.1, i32 0, i32 0
+ %.real.ld.1 = load double, double* %field.2
+ %fadd.0 = fadd double %.real.ld.0, %.real.ld.1
+ %field.1 = getelementptr inbounds { double, double }, { double, double }* %tmp.0, i32 0, i32 1
+ %.imag.ld.0 = load double, double* %field.1
+ %field.3 = getelementptr inbounds { double, double }, { double, double }* %tmp.1, i32 0, i32 1
+ %.imag.ld.1 = load double, double* %field.3
+ %fadd.1 = fadd double %.imag.ld.0, %.imag.ld.1
+ %field.4 = getelementptr inbounds { double, double }, { double, double }* %tmp.2, i32 0, i32 0
+ store double %fadd.0, double* %field.4
+ %field.5 = getelementptr inbounds { double, double }, { double, double }* %tmp.2, i32 0, i32 1
+ store double %fadd.1, double* %field.5
+ %cast.10 = bitcast { double, double }* %z to i8*
+ %cast.11 = bitcast { double, double }* %tmp.2 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.10, i8* %cast.11, i64 16, i32 8, i1 false)
+ %cast.12 = bitcast { double, double }* %tmp.3 to i8*
+ %cast.13 = bitcast { double, double }* %x to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.12, i8* %cast.13, i64 16, i32 8, i1 false)
+ %cast.14 = bitcast { double, double }* %tmp.4 to i8*
+ %cast.15 = bitcast { double, double }* %y to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.14, i8* %cast.15, i64 16, i32 8, i1 false)
+ %field.6 = getelementptr inbounds { double, double }, { double, double }* %tmp.3, i32 0, i32 0
+ %.real.ld.2 = load double, double* %field.6
+ %field.8 = getelementptr inbounds { double, double }, { double, double }* %tmp.4, i32 0, i32 0
+ %.real.ld.3 = load double, double* %field.8
+ %fsub.0 = fsub double %.real.ld.2, %.real.ld.3
+ %field.7 = getelementptr inbounds { double, double }, { double, double }* %tmp.3, i32 0, i32 1
+ %.imag.ld.2 = load double, double* %field.7
+ %field.9 = getelementptr inbounds { double, double }, { double, double }* %tmp.4, i32 0, i32 1
+ %.imag.ld.3 = load double, double* %field.9
+ %fsub.1 = fsub double %.imag.ld.2, %.imag.ld.3
+ %field.10 = getelementptr inbounds { double, double }, { double, double }* %tmp.5, i32 0, i32 0
+ store double %fsub.0, double* %field.10
+ %field.11 = getelementptr inbounds { double, double }, { double, double }* %tmp.5, i32 0, i32 1
+ store double %fsub.1, double* %field.11
+ %cast.16 = bitcast { double, double }* %z to i8*
+ %cast.17 = bitcast { double, double }* %tmp.5 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.16, i8* %cast.17, i64 16, i32 8, i1 false)
+ %cast.18 = bitcast { double, double }* %tmp.6 to i8*
+ %cast.19 = bitcast { double, double }* %x to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.18, i8* %cast.19, i64 16, i32 8, i1 false)
+ %cast.20 = bitcast { double, double }* %tmp.7 to i8*
+ %cast.21 = bitcast { double, double }* %y to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.20, i8* %cast.21, i64 16, i32 8, i1 false)
+ %field.12 = getelementptr inbounds { double, double }, { double, double }* %tmp.6, i32 0, i32 0
+ %.real.ld.4 = load double, double* %field.12
+ %field.14 = getelementptr inbounds { double, double }, { double, double }* %tmp.7, i32 0, i32 0
+ %.real.ld.5 = load double, double* %field.14
+ %fmul.0 = fmul double %.real.ld.4, %.real.ld.5
+ %field.13 = getelementptr inbounds { double, double }, { double, double }* %tmp.6, i32 0, i32 1
+ %.imag.ld.4 = load double, double* %field.13
+ %field.15 = getelementptr inbounds { double, double }, { double, double }* %tmp.7, i32 0, i32 1
+ %.imag.ld.5 = load double, double* %field.15
+ %fmul.1 = fmul double %.imag.ld.4, %.imag.ld.5
+ %fsub.2 = fsub double %fmul.0, %fmul.1
+ %field.121 = getelementptr inbounds { double, double }, { double, double }* %tmp.6, i32 0, i32 0
+ %.real.ld.6 = load double, double* %field.12
+ %field.152 = getelementptr inbounds { double, double }, { double, double }* %tmp.7, i32 0, i32 1
+ %.imag.ld.6 = load double, double* %field.15
+ %fmul.2 = fmul double %.real.ld.6, %.imag.ld.6
+ %field.133 = getelementptr inbounds { double, double }, { double, double }* %tmp.6, i32 0, i32 1
+ %.imag.ld.7 = load double, double* %field.13
+ %field.144 = getelementptr inbounds { double, double }, { double, double }* %tmp.7, i32 0, i32 0
+ %.real.ld.7 = load double, double* %field.14
+ %fmul.3 = fmul double %.imag.ld.7, %.real.ld.7
+ %fadd.2 = fadd double %fmul.2, %fmul.3
+ %field.16 = getelementptr inbounds { double, double }, { double, double }* %tmp.8, i32 0, i32 0
+ store double %fsub.2, double* %field.16
+ %field.17 = getelementptr inbounds { double, double }, { double, double }* %tmp.8, i32 0, i32 1
+ store double %fadd.2, double* %field.17
+ %cast.22 = bitcast { double, double }* %z to i8*
+ %cast.23 = bitcast { double, double }* %tmp.8 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.22, i8* %cast.23, i64 16, i32 8, i1 false)
+ %cast.24 = bitcast { double, double }* %tmp.9 to i8*
+ %cast.25 = bitcast { double, double }* %x to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.24, i8* %cast.25, i64 16, i32 8, i1 false)
+ %cast.26 = bitcast { double, double }* %tmp.10 to i8*
+ %cast.27 = bitcast { double, double }* %y to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.26, i8* %cast.27, i64 16, i32 8, i1 false)
+ %field.18 = getelementptr inbounds { double, double }, { double, double }* %tmp.9, i32 0, i32 0
+ %.real.ld.8 = load double, double* %field.18
+ %field.20 = getelementptr inbounds { double, double }, { double, double }* %tmp.10, i32 0, i32 0
+ %.real.ld.9 = load double, double* %field.20
+ %fcmp.0 = fcmp oeq double %.real.ld.8, %.real.ld.9
+ %zext.0 = zext i1 %fcmp.0 to i8
+ %field.19 = getelementptr inbounds { double, double }, { double, double }* %tmp.9, i32 0, i32 1
+ %.imag.ld.8 = load double, double* %field.19
+ %field.21 = getelementptr inbounds { double, double }, { double, double }* %tmp.10, i32 0, i32 1
+ %.imag.ld.9 = load double, double* %field.21
+ %fcmp.1 = fcmp oeq double %.imag.ld.8, %.imag.ld.9
+ %zext.1 = zext i1 %fcmp.1 to i8
+ %iand.0 = and i8 %zext.0, %zext.1
+ store i8 %iand.0, i8* %b
+ %cast.28 = bitcast { double, double }* %tmp.11 to i8*
+ %cast.29 = bitcast { double, double }* %x to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.28, i8* %cast.29, i64 16, i32 8, i1 false)
+ %cast.30 = bitcast { double, double }* %tmp.12 to i8*
+ %cast.31 = bitcast { double, double }* %y to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.30, i8* %cast.31, i64 16, i32 8, i1 false)
+ %field.22 = getelementptr inbounds { double, double }, { double, double }* %tmp.11, i32 0, i32 0
+ %.real.ld.10 = load double, double* %field.22
+ %field.24 = getelementptr inbounds { double, double }, { double, double }* %tmp.12, i32 0, i32 0
+ %.real.ld.11 = load double, double* %field.24
+ %fcmp.2 = fcmp one double %.real.ld.10, %.real.ld.11
+ %zext.2 = zext i1 %fcmp.2 to i8
+ %field.23 = getelementptr inbounds { double, double }, { double, double }* %tmp.11, i32 0, i32 1
+ %.imag.ld.10 = load double, double* %field.23
+ %field.25 = getelementptr inbounds { double, double }, { double, double }* %tmp.12, i32 0, i32 1
+ %.imag.ld.11 = load double, double* %field.25
+ %fcmp.3 = fcmp one double %.imag.ld.10, %.imag.ld.11
+ %zext.3 = zext i1 %fcmp.3 to i8
+ %ior.0 = or i8 %zext.2, %zext.3
+ store i8 %ior.0, i8* %b
+ ret void
+ }
+ )RAW_RESULT";
+
+ bool broken = h.finish(StripDebugInfo);
+ EXPECT_FALSE(broken && "Module failed to verify.");
+
+ bool isOK = h.expectValue(func->function(), exp);
+ EXPECT_TRUE(isOK && "Block does not have expected contents");
+}
+
+TEST(BackendExprTests, TestComplexExpressions) {
+ FcnTestHarness h("foo");
+ Llvm_backend *be = h.be();
+ Location loc;
+
+ // var a, b float64
+ // var x complex128
+ Btype *bf64t = be->float_type(64);
+ Btype *bc128t = be->complex_type(128);
+ Bvariable *a = h.mkLocal("a", bf64t);
+ Bvariable *b = h.mkLocal("b", bf64t);
+ Bvariable *x = h.mkLocal("x", bc128t);
+
+ // a = real(x)
+ Bexpression *avex1 = be->var_expression(a, VE_lvalue, loc);
+ Bexpression *xvex1 = be->var_expression(x, VE_rvalue, loc);
+ Bexpression *realex = be->real_part_expression(xvex1, loc);
+ h.mkAssign(avex1, realex);
+
+ // b = imag(x)
+ Bexpression *bvex2 = be->var_expression(b, VE_lvalue, loc);
+ Bexpression *xvex2 = be->var_expression(x, VE_rvalue, loc);
+ Bexpression *imagex = be->imag_part_expression(xvex2, loc);
+ h.mkAssign(bvex2, imagex);
+
+ // x = complex(b, a)
+ Bexpression *xvex3 = be->var_expression(x, VE_lvalue, loc);
+ Bexpression *bvex3 = be->var_expression(b, VE_rvalue, loc);
+ Bexpression *avex3 = be->var_expression(a, VE_rvalue, loc);
+ Bexpression *compex = be->complex_expression(bvex3, avex3, loc);
+ h.mkAssign(xvex3, compex);
+
+ const char *exp = R"RAW_RESULT(
+ store double 0.000000e+00, double* %a
+ store double 0.000000e+00, double* %b
+ %cast.0 = bitcast { double, double }* %x to i8*
+ %cast.1 = bitcast { double, double }* @const.0 to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %cast.0, i8* %cast.1, i64 16, i32 8, i1 false)
+ %field.0 = getelementptr inbounds { double, double }, { double, double }* %x, i32 0, i32 0
+ %x.real.ld.0 = load double, double* %field.0
+ store double %x.real.ld.0, double* %a
+ %field.1 = getelementptr inbounds { double, double }, { double, double }* %x, i32 0, i32 1
+ %x.imag.ld.0 = load double, double* %field.1
+ store double %x.imag.ld.0, double* %b
+ %b.ld.0 = load double, double* %b
+ %a.ld.0 = load double, double* %a
+ %field.2 = getelementptr inbounds { double, double }, { double, double }* %x, i32 0, i32 0
+ store double %b.ld.0, double* %field.2
+ %field.3 = getelementptr inbounds { double, double }, { double, double }* %x, i32 0, i32 1
+ store double %a.ld.0, double* %field.3
+ )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, CreateStringConstantExpressions) {
FcnTestHarness h("foo");