Skip to content

Commit

Permalink
Fix the issue for good.
Browse files Browse the repository at this point in the history
Allow other nested assign (incl. binary assign) expressions in any
assign expression's lhs as well as for address-of expressions by
returning the nested lvalue in these cases.
  • Loading branch information
kinke committed Mar 28, 2015
1 parent 07df388 commit d458e46
Showing 1 changed file with 109 additions and 95 deletions.
204 changes: 109 additions & 95 deletions gen/toir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,45 +188,29 @@ static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd, E

//////////////////////////////////////////////////////////////////////////////////////////////

class ToElemVisitor : public Visitor
// Tries to find the proper lvalue subexpression of an assign/binassign expression.
// Returns null if none is found.
static Expression* findLvalueExp(Expression* e)
{
IRState *p;
DValue *result;
public:
ToElemVisitor(IRState *p_) : p(p_), result(0) { }

DValue *getResult() { return result; }

//////////////////////////////////////////////////////////////////////////////////////////

// Import all functions from class Visitor
using Visitor::visit;

//////////////////////////////////////////////////////////////////////////////////////////

class FindLValueVisitor : public Visitor
class FindLvalueVisitor : public Visitor
{
public:
Expression* result;

FindLValueVisitor() : result(NULL) {}
FindLvalueVisitor() : result(NULL) {}

// Import all functions from class Visitor
using Visitor::visit;

void visit(Expression *e)
{
e->error("expression %s does not mask any l-value", e->toChars());
fatal();
}
void visit(Expression* e) {}

#define FORWARD(TYPE) void visit(TYPE *e) { e->e1->accept(this); }
FORWARD(CastExp)
FORWARD(BinAssignExp)
#define FORWARD(TYPE) void visit(TYPE* e) { e->e1->accept(this); }
FORWARD(AssignExp)
FORWARD(BinAssignExp)
FORWARD(CastExp)
#undef FORWARD

#define IMPLEMENT(TYPE) void visit(TYPE *e) { result = e; }
#define IMPLEMENT(TYPE) void visit(TYPE* e) { result = e; }
IMPLEMENT(VarExp)
IMPLEMENT(CallExp)
IMPLEMENT(PtrExp)
Expand All @@ -236,24 +220,62 @@ class ToElemVisitor : public Visitor
#undef IMPLEMENT
};

// Finds the proper lvalue for a binassign expression.
Expression* findLvalue(Expression* e)
{
FindLValueVisitor v;
e->accept(&v);
return v.result;
}
FindLvalueVisitor v;
e->accept(&v);
return v.result;
}

// Evaluates an lvalue expression e and prevents further
// evaluations as long as e->cachedLvalue isn't reset to null.
static DValue* toElemAndCacheLvalue(Expression* e)
{
DValue* value = toElem(e);
e->cachedLvalue = value->getLVal();
return value;
}

void cacheLvalue(Expression *e)
// Evaluates e and returns
// * the (casted) nested lvalue if one is found, otherwise
// * the expression's result.
static DValue* toElemAndTryGetLvalue(Expression* e)
{
Expression* lvalExp = findLvalueExp(e); // may be null
Expression* nestedLvalExp = (lvalExp == e ? NULL : lvalExp);

DValue* nestedLval = NULL;
if (nestedLvalExp)
{
if (e->cachedLvalue)
return;
IF_LOG Logger::println("Caching l-value of %s => %s",
e->toChars(), nestedLvalExp->toChars());

IF_LOG Logger::println("Caching l-value of %s", e->toChars());
LOG_SCOPE;
e->cachedLvalue = toElem(e)->getLVal();
nestedLval = toElemAndCacheLvalue(nestedLvalExp);
}

DValue* value = toElem(e);

if (nestedLvalExp)
nestedLvalExp->cachedLvalue = NULL;

return !nestedLval ? value : DtoCast(e->loc, nestedLval, e->type);
}

//////////////////////////////////////////////////////////////////////////////////////////////

class ToElemVisitor : public Visitor
{
IRState *p;
DValue *result;
public:
ToElemVisitor(IRState *p_) : p(p_), result(0) { }

DValue *getResult() { return result; }

//////////////////////////////////////////////////////////////////////////////////////////

// Import all functions from class Visitor
using Visitor::visit;

//////////////////////////////////////////////////////////////////////////////////////////

void visit(DeclarationExp *e)
Expand Down Expand Up @@ -469,7 +491,7 @@ class ToElemVisitor : public Visitor
}
}

DValue* l = toElem(e->e1);
DValue* l = toElemAndTryGetLvalue(e->e1);

// NRVO for object field initialization in constructor
if (l->isVar() && e->op == TOKconstruct && e->e2->op == TOKcall)
Expand Down Expand Up @@ -515,76 +537,68 @@ class ToElemVisitor : public Visitor

//////////////////////////////////////////////////////////////////////////////////////////

template <typename Exp>
DValue *binAssign(BinAssignExp *be)
template <typename BinExp, bool useLvalForBinExpLhs>
static DValue* binAssign(BinAssignExp* e)
{
Loc loc = be->loc;

// Find the lvalue for the expression
Expression* lval = findLvalue(be->e1);
cacheLvalue(lval);

// Evaluate the expression
Exp e3(loc, be->e1, be->e2);
e3.type = be->e1->type;

DValue* dst = toElem(lval);
DValue* res = toElem(&e3);

// Now that we are done with the expression, clear the cached lvalue
lval->cachedLvalue = NULL;
Loc loc = e->loc;

// Assign the (casted) value and return it
DValue* stval = DtoCast(loc, res, dst->getType());
DtoAssign(loc, dst, stval);
return DtoCast(loc, res, be->type);
}
// find the lhs' lvalue expression
Expression* lvalExp = findLvalueExp(e->e1);
if (!lvalExp)
{
e->error("expression %s does not mask any l-value", e->e1->toChars());
fatal();
}

template <typename Exp>
DValue *binShiftAssign(BinAssignExp *be)
{
Loc loc = be->loc;
// pre-evaluate and cache the lvalue subexpression
DValue* lval = NULL;
{
IF_LOG Logger::println("Caching l-value of %s => %s",
e->toChars(), lvalExp->toChars());

// Find the lvalue for the expression
Expression* lval = findLvalue(be->e1);
cacheLvalue(lval);
LOG_SCOPE;
lval = toElemAndCacheLvalue(lvalExp);
}

// Evaluate the expression
Exp e3(loc, lval, be->e2);
e3.type = lval->type;
// evaluate the underlying binary expression
Expression* lhsForBinExp = (useLvalForBinExpLhs ? lvalExp : e->e1);
BinExp binExp(loc, lhsForBinExp, e->e2);
binExp.type = lhsForBinExp->type;
DValue* result = toElem(&binExp);

DValue* dst = toElem(lval);
DValue* res = toElem(&e3);
lvalExp->cachedLvalue = NULL;

// Now that we are done with the expression, clear the cached lvalue
lval->cachedLvalue = NULL;
// assign the (casted) result to lval
DValue* assignedResult = DtoCast(loc, result, lval->type);
DtoAssign(loc, lval, assignedResult);

// Assign the value and return it
DtoAssign(loc, dst, res);
return DtoCast(loc, res, be->type);
// return the (casted) result
return e->type == assignedResult->type
? assignedResult
: DtoCast(loc, result, e->type);
}

#define BIN_ASSIGN(X, op) \
void visit(X##AssignExp *e) \
#define BIN_ASSIGN(Op, useLvalForBinExpLhs) \
void visit(Op##AssignExp *e) \
{ \
IF_LOG Logger::print(#X"AssignExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); \
IF_LOG Logger::print(#Op"AssignExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); \
LOG_SCOPE; \
result = op <X##Exp>(e);\
result = binAssign<Op##Exp, useLvalForBinExpLhs>(e); \
}

BIN_ASSIGN(Add, binAssign)
BIN_ASSIGN(Min, binAssign)
BIN_ASSIGN(Mul, binAssign)
BIN_ASSIGN(Div, binAssign)
BIN_ASSIGN(Mod, binAssign)
BIN_ASSIGN(And, binAssign)
BIN_ASSIGN(Or, binAssign)
BIN_ASSIGN(Xor, binAssign)
BIN_ASSIGN(Shl, binShiftAssign)
BIN_ASSIGN(Shr, binShiftAssign)
BIN_ASSIGN(Ushr, binShiftAssign)
BIN_ASSIGN(Add, false)
BIN_ASSIGN(Min, false)
BIN_ASSIGN(Mul, false)
BIN_ASSIGN(Div, false)
BIN_ASSIGN(Mod, false)
BIN_ASSIGN(And, false)
BIN_ASSIGN(Or, false)
BIN_ASSIGN(Xor, false)
BIN_ASSIGN(Shl, true)
BIN_ASSIGN(Shr, true)
BIN_ASSIGN(Ushr, true)

#undef BIN_ASSIGN
#undef BIN_ASSIGN

//////////////////////////////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -1199,7 +1213,7 @@ class ToElemVisitor : public Visitor
}
}

DValue* v = toElem(e->e1);
DValue* v = toElemAndTryGetLvalue(e->e1);
if (v->isField()) {
Logger::println("is field");
result = v;
Expand Down

0 comments on commit d458e46

Please sign in to comment.