From c2b38ad2b160a51d18c9238d46d419b40c4fd672 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 15 Mar 2013 00:22:13 -0700 Subject: [PATCH] {,List}AssignmentExpression: add support for RHS first evaluation order Add a flag to {,List}AssignmentExpression that changes evaluation order to RHS before LHS. This will be used by YieldExpression. --- hphp/compiler/analysis/alias_manager.cpp | 1 + hphp/compiler/analysis/emitter.cpp | 86 ++++++++++++------- hphp/compiler/analysis/emitter.h | 2 + .../expression/assignment_expression.cpp | 10 ++- .../expression/assignment_expression.h | 4 +- hphp/compiler/expression/list_assignment.cpp | 9 +- hphp/compiler/expression/list_assignment.h | 5 +- 7 files changed, 77 insertions(+), 40 deletions(-) diff --git a/hphp/compiler/analysis/alias_manager.cpp b/hphp/compiler/analysis/alias_manager.cpp index 97cf72db3..1d74f3e46 100644 --- a/hphp/compiler/analysis/alias_manager.cpp +++ b/hphp/compiler/analysis/alias_manager.cpp @@ -1841,6 +1841,7 @@ ExpressionPtr AliasManager::canonicalizeRecur(ExpressionPtr e) { int n = e->getKidCount(); if (n < 2) delayVars = false; + if (e->is(Expression::KindOfAssignmentExpression)) delayVars = false; m_inCall += inCall; for (int j = delayVars ? 0 : 1; j < 2; j++) { diff --git a/hphp/compiler/analysis/emitter.cpp b/hphp/compiler/analysis/emitter.cpp index 40dd35b8d..842a66c13 100644 --- a/hphp/compiler/analysis/emitter.cpp +++ b/hphp/compiler/analysis/emitter.cpp @@ -2742,11 +2742,26 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) { case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr ae( static_pointer_cast(node)); + ExpressionPtr rhs = ae->getValue(); + Id tempLocal = -1; + Offset start = InvalidAbsoluteOffset; + + if (ae->isRhsFirst()) { + assert(!rhs->hasContext(Expression::RefValue)); + tempLocal = emitVisitAndSetUnnamedL(e, rhs); + start = m_ue.bcPos(); + } visit(ae->getVariable()); emitClsIfSPropBase(e); - visit(ae->getValue()); - if (ae->getValue()->hasContext(Expression::RefValue)) { + + if (ae->isRhsFirst()) { + emitPushAndFreeUnnamedL(e, tempLocal, start); + } else { + visit(rhs); + } + + if (rhs->hasContext(Expression::RefValue)) { emitConvertToVar(e); emitBind(e); if (ae->hasAnyContext(Expression::AccessContext| @@ -3259,9 +3274,19 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) { ListAssignmentPtr la(static_pointer_cast(node)); ExpressionPtr rhs = la->getArray(); - if (!rhs) { - // visitListAssignmentLHS should have handled this - assert(false); + // visitListAssignmentLHS should have handled this + assert(rhs); + + bool nullRHS = la->getRHSKind() == ListAssignment::Null; + // Assign RHS to temp local, unless it's already a simple variable + bool simpleRHS = rhs->is(Expression::KindOfSimpleVariable) + && !static_pointer_cast(rhs)->getAlwaysStash(); + Id tempLocal = -1; + Offset start = InvalidAbsoluteOffset; + + if (!simpleRHS && la->isRhsFirst()) { + tempLocal = emitVisitAndSetUnnamedL(e, rhs); + start = m_ue.bcPos(); } // We use "index chains" to deal with nested list assignment. We will @@ -3271,19 +3296,10 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) { IndexChain workingChain; visitListAssignmentLHS(e, la, workingChain, indexChains); - bool nullRHS = la->getRHSKind() == ListAssignment::Null; - // Assign RHS to temp local, unless it's already a simple variable - bool simpleRHS = rhs->is(Expression::KindOfSimpleVariable) - && !static_pointer_cast(rhs)->getAlwaysStash(); - Id tempLocal = -1; - Offset start = InvalidAbsoluteOffset; - if (!simpleRHS) { - tempLocal = m_curFunc->allocUnnamedLocal(); - emitVirtualLocal(tempLocal); - visit(rhs); - emitConvertToCell(e); - emitSet(e); - emitPop(e); + if (!simpleRHS && !la->isRhsFirst()) { + assert(tempLocal == -1); + assert(start == InvalidAbsoluteOffset); + tempLocal = emitVisitAndSetUnnamedL(e, rhs); start = m_ue.bcPos(); } @@ -3319,20 +3335,9 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) { if (simpleRHS) { visit(rhs); } else { - emitVirtualLocal(tempLocal); - emitCGet(e); + emitPushAndFreeUnnamedL(e, tempLocal, start); } - // Null out and free unnamed local - if (!simpleRHS) { - assert(tempLocal >= 0); - assert(start != InvalidAbsoluteOffset); - newFaultRegion(start, m_ue.bcPos(), - new UnsetUnnamedLocalThunklet(tempLocal)); - emitVirtualLocal(tempLocal); - emitUnset(e); - m_curFunc->freeUnnamedLocal(tempLocal); - } return true; } @@ -4151,6 +4156,27 @@ void EmitterVisitor::emitVGet(Emitter& e) { } } +Id EmitterVisitor::emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp) { + Id tempLocal = m_curFunc->allocUnnamedLocal(); + emitVirtualLocal(tempLocal); + visit(exp); + emitConvertToCell(e); + emitSet(e); + emitPop(e); + return tempLocal; +} + +void EmitterVisitor::emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal, Offset start) { + assert(tempLocal >= 0); + assert(start != InvalidAbsoluteOffset); + emitVirtualLocal(tempLocal); + emitCGet(e); + newFaultRegion(start, m_ue.bcPos(), new UnsetUnnamedLocalThunklet(tempLocal)); + emitVirtualLocal(tempLocal); + emitUnset(e); + m_curFunc->freeUnnamedLocal(tempLocal); +} + EmitterVisitor::PassByRefKind EmitterVisitor::getPassByRefKind(ExpressionPtr exp) { // The PassByRefKind of a list assignment expression is determined // by the PassByRefKind of the RHS. This loop will repeatedly recurse diff --git a/hphp/compiler/analysis/emitter.h b/hphp/compiler/analysis/emitter.h index 117a69968..a510903dd 100644 --- a/hphp/compiler/analysis/emitter.h +++ b/hphp/compiler/analysis/emitter.h @@ -582,6 +582,8 @@ public: template void emitVirtualClassBase(Emitter&, Expr* node); void emitResolveClsBase(Emitter& e, int pos); void emitClsIfSPropBase(Emitter& e); + Id emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp); + void emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal, Offset start); Label* getContinuationGotoLabel(StatementPtr s); void emitContinuationSwitch(Emitter& e, SwitchStatementPtr s); DataType analyzeSwitch(SwitchStatementPtr s, SwitchState& state); diff --git a/hphp/compiler/expression/assignment_expression.cpp b/hphp/compiler/expression/assignment_expression.cpp index ed36433c4..a5325ec8c 100644 --- a/hphp/compiler/expression/assignment_expression.cpp +++ b/hphp/compiler/expression/assignment_expression.cpp @@ -42,9 +42,11 @@ using namespace HPHP; AssignmentExpression::AssignmentExpression (EXPRESSION_CONSTRUCTOR_PARAMETERS, - ExpressionPtr variable, ExpressionPtr value, bool ref) + ExpressionPtr variable, ExpressionPtr value, bool ref, + bool rhsFirst /* = false */) : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(AssignmentExpression)), - m_variable(variable), m_value(value), m_ref(ref) { + m_variable(variable), m_value(value), m_ref(ref), m_rhsFirst(rhsFirst) { + assert(!m_ref || !m_rhsFirst); m_variable->setContext(Expression::DeepAssignmentLHS); m_variable->setContext(Expression::AssignmentLHS); m_variable->setContext(Expression::LValue); @@ -135,7 +137,7 @@ void AssignmentExpression::analyzeProgram(AnalysisResultPtr ar) { } ConstructPtr AssignmentExpression::getNthKid(int n) const { - switch (n) { + switch (m_rhsFirst ? 1 - n : n) { case 0: return m_variable; case 1: @@ -152,7 +154,7 @@ int AssignmentExpression::getKidCount() const { } void AssignmentExpression::setNthKid(int n, ConstructPtr cp) { - switch (n) { + switch (m_rhsFirst ? 1 - n : n) { case 0: m_variable = boost::dynamic_pointer_cast(cp); break; diff --git a/hphp/compiler/expression/assignment_expression.h b/hphp/compiler/expression/assignment_expression.h index 8f0b2e3dc..ebbd76ddc 100644 --- a/hphp/compiler/expression/assignment_expression.h +++ b/hphp/compiler/expression/assignment_expression.h @@ -29,7 +29,7 @@ class AssignmentExpression : public Expression, public IParseHandler { public: AssignmentExpression(EXPRESSION_CONSTRUCTOR_PARAMETERS, ExpressionPtr variable, ExpressionPtr value, - bool ref); + bool ref, bool rhsFirst = false); DECLARE_EXPRESSION_VIRTUAL_FUNCTIONS; ExpressionPtr preOptimize(AnalysisResultConstPtr ar); @@ -49,6 +49,7 @@ public: ExpressionPtr getValue() { return m_value;} void setVariable(ExpressionPtr v) { m_variable = v; } void setValue(ExpressionPtr v) { m_value = v; } + bool isRhsFirst() { return m_rhsFirst; } int getLocalEffects() const; // $GLOBALS[] = ; @@ -59,6 +60,7 @@ private: ExpressionPtr m_variable; ExpressionPtr m_value; bool m_ref; + bool m_rhsFirst; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/compiler/expression/list_assignment.cpp b/hphp/compiler/expression/list_assignment.cpp index e5c5c3a8b..e5745f87d 100644 --- a/hphp/compiler/expression/list_assignment.cpp +++ b/hphp/compiler/expression/list_assignment.cpp @@ -147,9 +147,10 @@ static bool AssignmentCouldSet(ExpressionListPtr vars, ExpressionPtr var) { ListAssignment::ListAssignment (EXPRESSION_CONSTRUCTOR_PARAMETERS, - ExpressionListPtr variables, ExpressionPtr array) + ExpressionListPtr variables, ExpressionPtr array, bool rhsFirst /* = false */) : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ListAssignment)), - m_variables(variables), m_array(array), m_rhsKind(Regular) { + m_variables(variables), m_array(array), m_rhsKind(Regular), + m_rhsFirst(rhsFirst) { setLValue(); if (m_array) { @@ -217,7 +218,7 @@ void ListAssignment::analyzeProgram(AnalysisResultPtr ar) { } ConstructPtr ListAssignment::getNthKid(int n) const { - switch (n) { + switch (m_rhsFirst ? 1 - n : n) { case 0: return m_variables; case 1: @@ -234,7 +235,7 @@ int ListAssignment::getKidCount() const { } void ListAssignment::setNthKid(int n, ConstructPtr cp) { - switch (n) { + switch (m_rhsFirst ? 1 - n : n) { case 0: m_variables = boost::dynamic_pointer_cast(cp); break; diff --git a/hphp/compiler/expression/list_assignment.h b/hphp/compiler/expression/list_assignment.h index bc8adb5b9..e68244fb7 100644 --- a/hphp/compiler/expression/list_assignment.h +++ b/hphp/compiler/expression/list_assignment.h @@ -35,7 +35,8 @@ public: Null }; ListAssignment(EXPRESSION_CONSTRUCTOR_PARAMETERS, - ExpressionListPtr variables, ExpressionPtr array); + ExpressionListPtr variables, ExpressionPtr array, + bool rhsFirst = false); DECLARE_EXPRESSION_VIRTUAL_FUNCTIONS; @@ -43,10 +44,12 @@ public: ExpressionListPtr getVariables() const { return m_variables; } ExpressionPtr getArray() const { return m_array; } + bool isRhsFirst() { return m_rhsFirst; } private: ExpressionListPtr m_variables; ExpressionPtr m_array; RHSKind m_rhsKind; + bool m_rhsFirst; void setLValue(); };