gollvm: synthesize LHS/RHS context for var expressions
Up until now the bridge code would rely on the frontend to pass in a
tag on each var expression to indicate whether the variable appeared
in a left-hand-side or right-hand-side context. This change adds
machinery to synthesize the correct tags from within the bridge,
without any input from the front end.
Also as part of this change:
- move the expansion of complex operations (compare, arithmetic, etc)
a bit earlier in the compiler flow, so as to avoid a lot ofu
materialize() / resolve calls along the way
- revamp handling of compound expressions (N_Compound) to be more
consistent with other expressions with respect to llvm::Value:
initial Bexpression will have a NULL llvm::Value, then a value is
assigned during materialization.
Change-Id: Ic70e62d20122118bb123d7babb7ae64ef45a75e9
Reviewed-on: https://go-review.googlesource.com/47930
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/llvm-gofrontend/go-llvm-bexpression.h b/llvm-gofrontend/go-llvm-bexpression.h
index 37e0fea..c61b535 100644
--- a/llvm-gofrontend/go-llvm-bexpression.h
+++ b/llvm-gofrontend/go-llvm-bexpression.h
@@ -86,8 +86,7 @@
bool pending() const { return pending_; }
unsigned addrLevel() const { return addrLevel_; }
- unsigned lvalue() const { return lvalue_; }
- void incrementAddrLevel() { addrLevel_ += 1; }
+ bool lvalue() const { return lvalue_; }
void setPending(bool lvalue, unsigned addrLevel) {
assert(!pending_);
pending_ = true;
@@ -95,6 +94,11 @@
addrLevel_ = addrLevel;
}
void reset() { assert(pending_); pending_ = false; }
+ bool equal(const VarContext &other) const {
+ return (pending_ == other.pending_ &&
+ addrLevel_ == other.addrLevel_ &&
+ lvalue_ == other.lvalue_);
+ }
private:
unsigned addrLevel_;
diff --git a/llvm-gofrontend/go-llvm-bnode.cpp b/llvm-gofrontend/go-llvm-bnode.cpp
index c49bc16..71a3a0e 100644
--- a/llvm-gofrontend/go-llvm-bnode.cpp
+++ b/llvm-gofrontend/go-llvm-bnode.cpp
@@ -544,8 +544,6 @@
new Bexpression(N_UnaryOp, kids, val, typ, loc);
rval->u.op = op;
appendInstIfNeeded(rval, val);
- if (src->varExprPending())
- rval->setVarExprPending(src->varContext());
return archive(rval);
}
@@ -556,8 +554,6 @@
Bexpression *rval =
new Bexpression(N_Conversion, kids, val, typ, loc);
appendInstIfNeeded(rval, val);
- if (src->varExprPending())
- rval->setVarExprPending(src->varContext());
return archive(rval);
}
@@ -646,8 +642,6 @@
new Bexpression(N_StructField, kids, val, typ, loc);
appendInstIfNeeded(rval, val);
rval->u.fieldIndex = fieldIndex;
- if (structval->varExprPending())
- rval->setVarExprPending(structval->varContext());
return archive(rval);
}
@@ -661,8 +655,6 @@
Bexpression *rval =
new Bexpression(N_ArrayIndex, kids, val, typ, loc);
appendInstIfNeeded(rval, val);
- if (arval->varExprPending())
- rval->setVarExprPending(arval->varContext());
return archive(rval);
}
@@ -681,14 +673,13 @@
Bexpression *BnodeBuilder::mkCompound(Bstatement *st,
Bexpression *expr,
+ llvm::Value *val,
Location loc)
{
std::vector<Bnode *> kids = { st, expr };
Bexpression *rval =
- new Bexpression(N_Compound, kids, expr->value(), expr->btype(), loc);
+ new Bexpression(N_Compound, kids, val, expr->btype(), loc);
rval->setTag(expr->tag());
- if (expr->varExprPending())
- rval->setVarExprPending(expr->varContext());
return archive(rval);
}
diff --git a/llvm-gofrontend/go-llvm-bnode.h b/llvm-gofrontend/go-llvm-bnode.h
index 9c8ce2e..16d5ec5 100644
--- a/llvm-gofrontend/go-llvm-bnode.h
+++ b/llvm-gofrontend/go-llvm-bnode.h
@@ -253,7 +253,7 @@
Bexpression *mkBinaryOp(Operator op, Btype *typ, llvm::Value *val,
Bexpression *left, Bexpression *right,
Binstructions &instructions, Location loc);
- Bexpression *mkCompound(Bstatement *st, Bexpression *expr,
+ Bexpression *mkCompound(Bstatement *st, Bexpression *expr, llvm::Value *val,
Location loc);
Bexpression *mkStructField(Btype *type, llvm::Value *value,
Bexpression *structval, unsigned fieldIndex,
diff --git a/llvm-gofrontend/go-llvm-materialize.cpp b/llvm-gofrontend/go-llvm-materialize.cpp
index f8b6649..a45ff13 100644
--- a/llvm-gofrontend/go-llvm-materialize.cpp
+++ b/llvm-gofrontend/go-llvm-materialize.cpp
@@ -121,10 +121,6 @@
assert(iexprs.size() == 1);
Bexpression *expr = iexprs[0];
- // 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());
@@ -256,46 +252,6 @@
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?
- auto p = makeTempVar(expr, location);
- Bvariable *var = p.first;
- Bstatement *einit = p.second;
-
- 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.
- auto p2 = makeTempVar(val, location);
- Bvariable *rvar = p2.first;
- Bstatement *rinit = p2.second;
- Bexpression *rvex = nbuilder_.mkVar(rvar, location);
- Bstatement *init = statement_list(std::vector<Bstatement*>{einit, rinit});
- return nbuilder_.mkCompound(init, rvex, location);
-}
-
llvm::Value *Llvm_backend::makePointerOffsetGEP(llvm::PointerType *llpt,
llvm::Value *idxval,
llvm::Value *sptr)
@@ -386,7 +342,10 @@
// Compound expressions can be used to produce lvalues, so we don't
// want to call resolve() on bexpr here.
- Bexpression *rval = nbuilder_.mkCompound(bstat, bexpr, location);
+ Bexpression *rval = nbuilder_.mkCompound(bstat, bexpr, bexpr->value(),
+ location);
+ if (bexpr->varExprPending())
+ rval->setVarExprPending(bexpr->varContext());
return rval;
}
@@ -455,7 +414,8 @@
Bexpression *rval = (tempv ?
var_expression(tempv, VE_rvalue, location) :
nbuilder_.mkVoidValue(void_type()));
- Bexpression *result = compound_expression(ifStmt, rval, location);
+ Bexpression *result =
+ materialize(compound_expression(ifStmt, rval, location));
return result;
}
@@ -628,11 +588,6 @@
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);
@@ -774,73 +729,6 @@
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?
- auto p = makeTempVar(left, location), p2 = makeTempVar(right, location);
- Bvariable *lvar = p.first, *rvar = p2.first;
- Bstatement *linit = p.second, *rinit = p2.second;
-
- 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 materialize(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.
- auto p3 = makeTempVar(val, location);
- Bvariable *vvar = p3.first;
- Bstatement *vinit = p3.second;
- Bexpression *vvex = nbuilder_.mkVar(vvar, location);
- Bstatement *init = statement_list(std::vector<Bstatement*>{linit, rinit, vinit});
- return compound_expression(init, vvex, location);
-}
-
Bexpression *Llvm_backend::materializeComposite(Bexpression *comExpr)
{
Location location = comExpr->location();
@@ -1522,6 +1410,185 @@
return srcVal;
}
+// Walk the specified expression and invoke setVarExprPending on
+// each var expression, with correct lvalue/rvalue tag depending on
+// context.
+
+class VarContextVisitor {
+ public:
+ VarContextVisitor(Bexpression *top,
+ Varexpr_context lvalueContext,
+ bool dumpDiffs)
+ : dumpDiffs_(dumpDiffs)
+ {
+ if (lvalueContext == VE_lvalue && isMem(top))
+ setLvalue(top);
+ }
+
+ std::pair< std::pair<VisitDisp, VisitChildDisp>, Bnode *>
+ visitChildPre(Bnode *parent, Bnode *child) {
+
+ Bexpression *eparent = parent->castToBexpression();
+ assert(eparent != nullptr);
+
+ // Don't descend into stmts or non-var nodes with values.
+ Bexpression *echild = child->castToBexpression();
+ if (child->isStmt() || (echild->value() != nullptr &&
+ echild->flavor() != N_Var))
+ return std::make_pair(std::make_pair(ContinueWalk, SkipChild), child);
+
+ // Propagate lvalue property down from parent to child through a memory
+ // operation such as a fieldref or arrayindex if applicable. For example,
+ // for the tree build from go code "x.f1[y]" such as
+ //
+ // array_index
+ // / \.
+ // field var(y)
+ // /
+ // var(x)
+ //
+ // If the top node (array_index) is in a left-hand-side position,
+ // then we want to flag "field" and "var(x)" nodes as being in an
+ // LHS context also, but not the "var(y)" node (it is not being
+ // assigned).
+ assert(echild != nullptr);
+ if (isLvalue(eparent) && isMem(echild)) {
+ Bexpression *cmem = memArg(eparent);
+ assert(cmem != nullptr);
+ if (cmem == echild)
+ setLvalue(echild);
+ }
+ return std::make_pair(std::make_pair(ContinueWalk, VisitChild), child);
+ }
+
+ // Apply "var pending" tags to var exprs bottom up, once downward propagation
+ // of lvalue context is complete.
+ std::pair<VisitDisp, Bnode *> visitNodePost(Bnode *node)
+ {
+ Bexpression *expr = node->castToBexpression();
+ if (expr == nullptr || (expr->value() && expr->flavor() != N_Var))
+ return std::make_pair(ContinueWalk, expr);
+
+ if (expr->flavor() == N_Var)
+ setVarExprPending(expr, isLvalue(expr), 0);
+
+ // Debugging only. This is intended to provide a way to compare the
+ // tags applied by the frontend vs the tags this visitor generates.
+ if (dumpDiffs_)
+ dumpDiff(expr);
+
+ auto it = varcontext_.find(expr);
+ if (it != varcontext_.end()) {
+ VarContext vc(it->second);
+ bool lvalue = vc.lvalue();
+ if (expr->varExprPending()) {
+ assert(vc.equal(expr->varContext()));
+ } else {
+ expr->setVarExprPending(lvalue, 0);
+ }
+ }
+
+ return std::make_pair(ContinueWalk, expr);
+ }
+
+ // boilerplate
+ std::pair<VisitDisp, Bnode *> visitNodePre(Bnode *node) {
+ return std::make_pair(ContinueWalk, node);
+ }
+
+ // boilerplate
+ std::pair<VisitDisp, Bnode *> visitChildPost(Bnode *parent, Bnode *child) {
+ return std::make_pair(ContinueWalk, child);
+ }
+
+ private:
+
+ // Debugging only.
+ void dumpDiff(Bexpression *expr)
+ {
+ if (expr->value() && expr->flavor() != N_Var)
+ return;
+
+ VarContext oldvc;
+ if (expr->varExprPending())
+ oldvc = expr->varContext();
+
+ VarContext newvc;
+ auto nit = varcontext_.find(expr);
+ if (nit != varcontext_.end())
+ newvc = nit->second;
+
+ if (newvc.equal(oldvc))
+ return;
+
+ std::cerr << "Expr " << expr->id()
+ << " " << expr->flavstr() << " ";
+ if (oldvc.pending()) {
+ std::cerr << "old VC(" << (oldvc.pending() ? "true" : "false")
+ << "," << (oldvc.lvalue() ? "lval" : "rval")
+ << "," << oldvc.addrLevel() << ") ";
+ }
+ if (newvc.pending()) {
+ std::cerr << "new VC(" << (newvc.pending() ? "true" : "false")
+ << "," << (newvc.lvalue() ? "lval" : "rval")
+ << "," << newvc.addrLevel() << ") ";
+ }
+ std::cerr << "\n";
+ expr->dump();
+ std::cerr << "\n";
+ }
+
+ Bexpression *memArg(Bexpression *expr) {
+ const std::vector<Bnode *> &kids = expr->children();
+ switch(expr->flavor()) {
+ case N_StructField:
+ case N_ArrayIndex:
+ case N_Address:
+ case N_Deref:
+ case N_Conversion:
+ case N_PointerOffset:
+ return kids[0]->castToBexpression();
+ default:
+ return nullptr;
+ }
+ }
+
+ bool isMem(Bexpression *expr) {
+ switch(expr->flavor()) {
+ case N_Var:
+ case N_StructField:
+ case N_Address:
+ case N_Deref:
+ case N_Conversion:
+ case N_ArrayIndex:
+ case N_PointerOffset:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void setLvalue(Bexpression *expr) {
+ assert(lvalue_.find(expr) == lvalue_.end());
+ lvalue_.insert(expr);
+ }
+
+ bool isLvalue(Bexpression *expr) {
+ return lvalue_.find(expr) != lvalue_.end();
+ }
+
+ void setVarExprPending(Bexpression *expr, bool lvalue, unsigned addrLevel) {
+ assert(expr);
+ VarContext vc(lvalue, addrLevel);
+ varcontext_[expr] = vc;
+ }
+
+ private:
+ std::set<Bnode *> lvalue_;
+ std::map<Bexpression *, VarContext> varcontext_;
+ bool dumpDiffs_;
+};
+
// Helper visitor class for expression folding; removes redundant
// nodes in preparation for materialization.
@@ -1589,7 +1656,7 @@
std::pair<VisitDisp, Bnode *> visitNodePost(Bnode *node) {
Bexpression *expr = node->castToBexpression();
if (expr && expr->value())
- return std::make_pair(ContinueWalk, node);
+ return std::make_pair(ContinueWalk, node);
switch(node->flavor()) {
case N_EmptyStmt:
case N_LabelStmt:
@@ -1677,19 +1744,36 @@
}
std::pair<VisitDisp, Bnode *> visitChildPost(Bnode *parent, Bnode *child) {
- return std::make_pair(ContinueWalk, child);
+ return std::make_pair(ContinueWalk, child);
}
- private:
- Llvm_backend *be_;
+ private:
+ Llvm_backend *be_;
};
-Bexpression *Llvm_backend::materialize(Bexpression *expr)
+Bexpression *Llvm_backend::materialize(Bexpression *expr,
+ Varexpr_context lvalueContext)
+
{
// Repair any node sharing at this point-- the materializer
// assumes that any node it visits can be destroyed/replaced
// without impacting some other portion of the tree.
enforceTreeIntegrity(expr);
+ // TODO:
+ // - don't really need a treewalk in the non-LHS case to apply
+ // can context tags; it would be simpler to only do the walk
+ // in the LHS case (and just apply var context tags when
+ // var expression is initially created)
+ // - an extension of the above: pass in the LHS/RHS context
+ // and propagate recursively during the materialize() walk,
+ // instead of having a separate tree-walk here.
+
+ // Locate and tag var expressions within the tree, selecting LHS or
+ // RHS context as appropriate. Needs to be done after the call above
+ // so as to insure that there are no share var expressions.
+ VarContextVisitor vcvis(expr, lvalueContext, false);
+ update_walk_nodes(expr, vcvis);
+
// Perform some basic folding operations. This is easier to do
// here so as not to worry about sharing.
FoldVisitor fvis(this);
diff --git a/llvm-gofrontend/go-llvm.cpp b/llvm-gofrontend/go-llvm.cpp
index 1347e17..56f9dbb 100644
--- a/llvm-gofrontend/go-llvm.cpp
+++ b/llvm-gofrontend/go-llvm.cpp
@@ -877,7 +877,6 @@
Bexpression *varexp = nbuilder_.mkVar(var, location);
varexp->setTag(var->name().c_str());
- varexp->setVarExprPending(in_lvalue_pos == VE_lvalue, 0);
return varexp;
}
@@ -1139,6 +1138,46 @@
return rval;
}
+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?
+ auto p = makeTempVar(expr, location);
+ Bvariable *var = p.first;
+ Bstatement *einit = p.second;
+
+ 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.
+ auto p2 = makeTempVar(val, location);
+ Bvariable *rvar = p2.first;
+ Bstatement *rinit = p2.second;
+ Bexpression *rvex = nbuilder_.mkVar(rvar, location);
+ Bstatement *init = statement_list(std::vector<Bstatement*>{einit, rinit});
+ return nbuilder_.mkCompound(init, rvex, nullptr, location);
+}
+
// An expression that converts an expression to a different type.
Bexpression *Llvm_backend::convert_expression(Btype *type,
@@ -1158,6 +1197,10 @@
toType = type->type();
}
+ // Complex -> complex conversion
+ if (type->castToBComplexType())
+ return makeComplexConvertExpr(type, expr, location);
+
Bexpression *rval = nbuilder_.mkConversion(type, nullptr, expr, location);
return rval;
}
@@ -1204,7 +1247,7 @@
if (bstat == errorStatement() || bexpr == errorExpression())
return errorExpression();
- Bexpression *rval = nbuilder_.mkCompound(bstat, bexpr, location);
+ Bexpression *rval = nbuilder_.mkCompound(bstat, bexpr, nullptr, location);
return rval;
}
@@ -1253,6 +1296,73 @@
return rval;
}
+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?
+ auto p = makeTempVar(left, location), p2 = makeTempVar(right, location);
+ Bvariable *lvar = p.first, *rvar = p2.first;
+ Bstatement *linit = p.second, *rinit = p2.second;
+
+ 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 materialize(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.
+ auto p3 = makeTempVar(val, location);
+ Bvariable *vvar = p3.first;
+ Bstatement *vinit = p3.second;
+ Bexpression *vvex = nbuilder_.mkVar(vvar, location);
+ Bstatement *init = statement_list(std::vector<Bstatement*>{linit, rinit, vinit});
+ return compound_expression(init, vvex, location);
+}
+
// Return an expression for the binary operation LEFT OP RIGHT.
Bexpression *Llvm_backend::binary_expression(Operator op, Bexpression *left,
@@ -1261,6 +1371,14 @@
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);
+
// Arbitrarily select the left child type as the type for the binop.
// This may be revised later during materializeBinary.
Btype *btype = left->btype();
@@ -1445,7 +1563,7 @@
if (lhs == errorExpression() || rhs == errorExpression() ||
bfunction == errorFunction_.get())
return errorStatement();
- lhs = materialize(lhs);
+ lhs = materialize(lhs, VE_lvalue);
rhs = materialize(rhs);
Bexpression *lhs2 = resolveVarContext(lhs, VE_lvalue);
Bexpression *rhs2 = rhs;
diff --git a/llvm-gofrontend/go-llvm.h b/llvm-gofrontend/go-llvm.h
index 13a99e3..1f58a1e 100644
--- a/llvm-gofrontend/go-llvm.h
+++ b/llvm-gofrontend/go-llvm.h
@@ -654,8 +654,10 @@
public:
- // Exposed for unit testing.
- Bexpression *materialize(Bexpression *expr);
+ // Performs a bottom-up walk to materialize LLVM values for each
+ // node in the expression tree. Made public for unit testing.
+ Bexpression *materialize(Bexpression *expr,
+ Varexpr_context lvalueContext=VE_rvalue);
// Helper routines to materialize llvm::Value's for expression nodes,
// invoked by routine above. Public primarily because we need to call
diff --git a/unittests/BackendCore/BackendExprTests.cpp b/unittests/BackendCore/BackendExprTests.cpp
index 6110875..e3d2d5c 100644
--- a/unittests/BackendCore/BackendExprTests.cpp
+++ b/unittests/BackendCore/BackendExprTests.cpp
@@ -536,12 +536,14 @@
// Same here.
Bexpression *ve3 = be->var_expression(loc1, VE_lvalue, loc);
+ Bexpression *ve3r = be->var_expression(loc1, VE_rvalue, loc);
EXPECT_EQ(repr(ve3->value()), "%loc1 = alloca i64");
- h.mkExprStmt(ve3);
+ h.mkAssign(ve3, ve3r);
Bexpression *ve4 = be->var_expression(loc1, VE_lvalue, loc);
+ Bexpression *ve4r = be->var_expression(loc1, VE_rvalue, loc);
EXPECT_EQ(repr(ve4->value()), "%loc1 = alloca i64");
EXPECT_NE(ve3, ve4);
- h.mkExprStmt(ve4);
+ h.mkAssign(ve4, ve4r);
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");