TypePtr Symbol::setType(AnalysisResultConstPtr ar, BlockScopeRawPtr scope, TypePtr type, bool coerced) { if (!type) return type; if (ar->getPhase() == AnalysisResult::FirstInference) { // at this point, you *must* have a lock (if you are user scope) if (scope->is(BlockScope::FunctionScope)) { FunctionScopeRawPtr f = boost::static_pointer_cast<FunctionScope>(scope); if (f->isUserFunction()) { f->getInferTypesMutex().assertOwnedBySelf(); } } else if (scope->is(BlockScope::ClassScope)) { ClassScopeRawPtr c = boost::static_pointer_cast<ClassScope>(scope); if (c->isUserClass()) { c->getInferTypesMutex().assertOwnedBySelf(); } } } TypePtr oldType = m_coerced; if (!oldType) oldType = Type::Some; if (!coerced) return oldType; type = CoerceTo(ar, m_coerced, type); ASSERT(!isRefClosureVar() || (type && type->is(Type::KindOfVariant))); if (ar->getPhase() >= AnalysisResult::AnalyzeAll && !Type::SameType(oldType, type)) { triggerUpdates(scope); } return type; }
ExpressionPtr ConstantExpression::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() < AnalysisResult::FirstPreOptimize) { return ExpressionPtr(); } ConstructPtr decl; while (!isScalar() && !m_dynamic && !(m_context & LValue)) { const Symbol *sym = resolveNS(ar); if (!sym) { // The constant may be defined in a native extension, so check if its // available and persistent. auto const cns = Unit::lookupPersistentCns(makeStaticString(m_name)); if (!cns) break; auto const& value = tvAsCVarRef(cns); if (!value.isAllowedAsConstantValue()) break; auto rep = makeScalarExpression(ar, value); rep->setComment(getText()); copyLocationTo(rep); return replaceValue(rep); } if (!const_cast<Symbol*>(sym)->checkDefined() || sym->isDynamic()) { sym = 0; m_dynamic = true; } if (!sym) break; if (!sym->isSystem()) BlockScope::s_constMutex.lock(); auto value = dynamic_pointer_cast<Expression>(sym->getValue()); if (!sym->isSystem()) BlockScope::s_constMutex.unlock(); if (!value || !value->isScalar()) { if (!m_depsSet && sym->getDeclaration()) { sym->getDeclaration()->getScope()->addUse( getScope(), BlockScope::UseKindConstRef); m_depsSet = true; } break; } Variant scalarValue; if (value->getScalarValue(scalarValue) && !scalarValue.isAllowedAsConstantValue()) { // block further optimization const_cast<Symbol*>(sym)->setDynamic(); m_dynamic = true; break; } if (sym->isSystem() && !value->is(KindOfScalarExpression)) { if (ExpressionPtr opt = value->preOptimize(ar)) { value = opt; } } ExpressionPtr rep = Clone(value, getScope()); rep->setComment(getText()); copyLocationTo(rep); return replaceValue(rep); } return ExpressionPtr(); }
void ExpressionList::optimize(AnalysisResultConstPtr ar) { bool changed = false; size_t i = m_exps.size(); if (m_kind != ListKindParam) { size_t skip = m_kind == ListKindLeft ? 0 : i - 1; while (i--) { if (i != skip) { ExpressionPtr &e = m_exps[i]; if (!e || e->getContainedEffects() == NoEffect) { removeElement(i); changed = true; } else if (e->is(KindOfExpressionList)) { ExpressionListPtr el(static_pointer_cast<ExpressionList>(e)); removeElement(i); for (size_t j = el->getCount(); j--; ) { insertElement((*el)[j], i); } changed = true; } else if (e->getLocalEffects() == NoEffect) { e = e->unneeded(); // changed already handled by unneeded } } } if (m_exps.size() == 1) { m_kind = ListKindWrapped; } else if (m_kind == ListKindLeft && m_exps[0]->isScalar()) { ExpressionPtr e = m_exps[0]; removeElement(0); addElement(e); m_kind = ListKindWrapped; } } else { if (hasContext(UnsetContext) && ar->getPhase() >= AnalysisResult::PostOptimize) { while (i--) { ExpressionPtr &e = m_exps[i]; if (e->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(e); if (var->checkUnused()) { const std::string &name = var->getName(); VariableTablePtr variables = getScope()->getVariables(); if (!variables->isNeeded(name)) { removeElement(i); changed = true; } } } } } } if (changed) { getScope()->addUpdates(BlockScope::UseKindCaller); } }
ExpressionPtr ObjectMethodExpression::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() < AnalysisResult::FirstPreOptimize) { return ExpressionPtr(); } if (m_classScope && m_funcScope && (!m_funcScope->isVirtual() || (!ar->isSystem() && !m_funcScope->hasOverride()))) { return inliner(ar, m_object, ""); } return ExpressionPtr(); }
TypePtr ConstantTable::check(BlockScopeRawPtr context, const std::string &name, TypePtr type, bool coerce, AnalysisResultConstPtr ar, ConstructPtr construct, const std::vector<std::string> &bases, BlockScope *&defScope) { ASSERT(!m_blockScope.is(BlockScope::FunctionScope)); bool isClassScope = m_blockScope.is(BlockScope::ClassScope); TypePtr actualType; defScope = NULL; if (name == "true" || name == "false") { actualType = Type::Boolean; } else { Symbol *sym = getSymbol(name); if (!sym) { if (ar->getPhase() != AnalysisResult::AnalyzeInclude) { if (isClassScope) { ClassScopeRawPtr parent = findBase(ar, name, bases); if (parent) { actualType = parent->getConstants()->check( context, name, type, coerce, ar, construct, bases, defScope); if (defScope) return actualType; } } Compiler::Error(Compiler::UseUndeclaredConstant, construct); actualType = isClassScope ? Type::Variant : Type::String; } } else { ASSERT(sym->isPresent()); ASSERT(sym->getType()); ASSERT(sym->isConstant()); defScope = &m_blockScope; if (isClassScope) { // if the current scope is a function scope, grab the lock. // otherwise if it's a class scope, then *try* to grab the lock. if (context->is(BlockScope::FunctionScope)) { GET_LOCK(BlockScopeRawPtr(&m_blockScope)); return setType(ar, sym, type, coerce); } else { TRY_LOCK(BlockScopeRawPtr(&m_blockScope)); return setType(ar, sym, type, coerce); } } else { Lock lock(m_blockScope.getMutex()); return setType(ar, sym, type, coerce); } } } return actualType; }
ExpressionPtr IncludeExpression::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() >= AnalysisResult::FirstPreOptimize) { if (m_include.empty()) { bool dr = m_documentRoot; m_include = CheckInclude(shared_from_this(), m_exp, dr); m_documentRoot = dr; m_depsSet = false; } if (!m_depsSet && !m_include.empty()) { analyzeInclude(ar, m_include); m_depsSet = true; } } return ExpressionPtr(); }
ExpressionPtr ConstantExpression::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() < AnalysisResult::FirstPreOptimize) { return ExpressionPtr(); } ConstructPtr decl; while (!isScalar() && !m_dynamic && !(m_context & LValue)) { const Symbol *sym = resolveNS(ar); if (sym && (!const_cast<Symbol*>(sym)->checkDefined() || sym->isDynamic())) { sym = 0; m_dynamic = true; } if (!sym) break; if (!sym->isSystem()) BlockScope::s_constMutex.lock(); auto value = dynamic_pointer_cast<Expression>(sym->getValue()); if (!sym->isSystem()) BlockScope::s_constMutex.unlock(); if (!value || !value->isScalar()) { if (!m_depsSet && sym->getDeclaration()) { sym->getDeclaration()->getScope()->addUse( getScope(), BlockScope::UseKindConstRef); m_depsSet = true; } break; } Variant scalarValue; if (value->getScalarValue(scalarValue) && !scalarValue.isAllowedAsConstantValue()) { // block further optimization const_cast<Symbol*>(sym)->setDynamic(); m_dynamic = true; break; } if (sym->isSystem() && !value->is(KindOfScalarExpression)) { if (ExpressionPtr opt = value->preOptimize(ar)) { value = opt; } } ExpressionPtr rep = Clone(value, getScope()); rep->setComment(getText()); copyLocationTo(rep); return replaceValue(rep); } return ExpressionPtr(); }
ExpressionPtr ClassConstantExpression::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() < AnalysisResult::FirstPreOptimize) { return ExpressionPtr(); } if (m_class) { updateClassName(); if (m_class) { return ExpressionPtr(); } } ClassScopePtr cls = resolveClass(); if (!cls || (cls->isVolatile() && !isPresent())) { if (cls && !m_depsSet) { cls->addUse(getScope(), BlockScope::UseKindConstRef); m_depsSet = true; } return ExpressionPtr(); } ConstantTablePtr constants = cls->getConstants(); ClassScopePtr defClass = cls; ConstructPtr decl = constants->getValueRecur(ar, m_varName, defClass); if (decl) { BlockScope::s_constMutex.lock(); ExpressionPtr value = dynamic_pointer_cast<Expression>(decl); BlockScope::s_constMutex.unlock(); if (!value->isScalar() && (value->is(KindOfClassConstantExpression) || value->is(KindOfConstantExpression))) { std::set<ExpressionPtr> seen; do { if (!seen.insert(value).second) return ExpressionPtr(); value = value->preOptimize(ar); if (!value) return ExpressionPtr(); } while (!value->isScalar() && (value->is(KindOfClassConstantExpression) || value->is(KindOfConstantExpression))); } ExpressionPtr rep = Clone(value, getScope()); rep->setComment(getText()); copyLocationTo(rep); return replaceValue(rep); } return ExpressionPtr(); }
TypePtr ConstantTable::check(const std::string &name, TypePtr type, bool coerce, AnalysisResultConstPtr ar, ConstructPtr construct, const std::vector<std::string> &bases, BlockScope *&defScope) { TypePtr actualType; defScope = NULL; if (name == "true" || name == "false") { actualType = Type::Boolean; } else { Symbol *sym = genSymbol(name, true); if (!sym->valueSet()) { if (ar->getPhase() != AnalysisResult::AnalyzeInclude) { actualType = checkBases(name, type, coerce, ar, construct, bases, defScope); if (defScope) return actualType; Compiler::Error(Compiler::UseUndeclaredConstant, construct); if (m_blockScope.is(BlockScope::ClassScope)) { actualType = Type::Variant; } else { actualType = Type::String; } setType(ar, sym, actualType, true); } } else { if (sym->getType()) { defScope = &m_blockScope; actualType = sym->getType(); if (actualType->is(Type::KindOfSome) || actualType->is(Type::KindOfAny)) { setType(ar, sym, type, true); return type; } } else { actualType = checkBases(name, type, coerce, ar, construct, bases, defScope); if (defScope) return actualType; actualType = Type::Some; setType(ar, sym, actualType, true); sym->setDeclaration(construct); } } } return actualType; }
TypePtr Symbol::setType(AnalysisResultConstPtr ar, BlockScopeRawPtr scope, TypePtr type, bool coerced) { if (!type) return type; TypePtr oldType = m_coerced; if (!oldType) oldType = Type::Some; if (!coerced) return oldType; type = CoerceTo(ar, m_coerced, type); assert(!isRefClosureVar() || (type && type->is(Type::KindOfVariant))); if (ar->getPhase() >= AnalysisResult::AnalyzeAll && !Type::SameType(oldType, type)) { triggerUpdates(scope); } return type; }
ExpressionPtr UnaryOpExpression::preOptimize(AnalysisResultConstPtr ar) { Variant value; Variant result; if (m_exp && ar->getPhase() >= AnalysisResult::FirstPreOptimize) { if (m_op == T_UNSET) { if (m_exp->isScalar() || (m_exp->is(KindOfExpressionList) && static_pointer_cast<ExpressionList>(m_exp)->getCount() == 0)) { recomputeEffects(); return CONSTANT("null"); } return ExpressionPtr(); } } if (m_op == T_ISSET && m_exp->is(KindOfExpressionList) && static_pointer_cast<ExpressionList>(m_exp)->getListKind() == ExpressionList::ListKindParam) { ExpressionListPtr el(static_pointer_cast<ExpressionList>(m_exp)); result = true; int i = 0, n = el->getCount(); for (; i < n; i++) { ExpressionPtr e((*el)[i]); if (!e || !e->isScalar() || !e->getScalarValue(value)) break; if (!isset(value)) { result = false; } } if (i == n) { return replaceValue(makeScalarExpression(ar, result)); } } else if (m_op != T_ARRAY && m_exp && m_exp->isScalar() && m_exp->getScalarValue(value) && preCompute(value, result)) { return replaceValue(makeScalarExpression(ar, result)); } return ExpressionPtr(); }
ExpressionPtr ObjectMethodExpression::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() < AnalysisResult::FirstPreOptimize) { return ExpressionPtr(); } if (m_classScope && m_funcScope && (!m_funcScope->isVirtual() || (Option::WholeProgram && !m_funcScope->hasOverride()))) { if (Option::DynamicInvokeFunctions.size()) { if (Option::DynamicInvokeFunctions.find( m_classScope->getName() + "::" + m_funcScope->getName()) != Option::DynamicInvokeFunctions.end()) { setNoInline(); } } return inliner(ar, m_object, ""); } return ExpressionPtr(); }
ExpressionPtr UnaryOpExpression::preOptimize(AnalysisResultConstPtr ar) { Variant value; Variant result; if (m_exp && ar->getPhase() >= AnalysisResult::FirstPreOptimize) { if (m_op == T_UNSET) { if (m_exp->isScalar() || (m_exp->is(KindOfExpressionList) && static_pointer_cast<ExpressionList>(m_exp)->getCount() == 0)) { recomputeEffects(); return CONSTANT("null"); } return ExpressionPtr(); } } if (m_op == T_ISSET && m_exp->is(KindOfExpressionList) && static_pointer_cast<ExpressionList>(m_exp)->getListKind() == ExpressionList::ListKindParam) { ExpressionListPtr el(static_pointer_cast<ExpressionList>(m_exp)); result = true; int i = 0, n = el->getCount(); for (; i < n; i++) { ExpressionPtr e((*el)[i]); if (!e || !e->isScalar() || !e->getScalarValue(value)) break; if (!isset(value)) { result = false; } } if (i == n) { return replaceValue(makeScalarExpression(ar, result)); } } else if (m_op != T_ARRAY && m_exp && m_exp->isScalar() && m_exp->getScalarValue(value) && preCompute(value, result)) { return replaceValue(makeScalarExpression(ar, result)); } else if (m_op == T_BOOL_CAST) { switch (m_exp->getKindOf()) { default: break; case KindOfBinaryOpExpression: { int op = static_pointer_cast<BinaryOpExpression>(m_exp)->getOp(); switch (op) { case T_LOGICAL_OR: case T_BOOLEAN_OR: case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_XOR: case T_INSTANCEOF: case '<': case T_IS_SMALLER_OR_EQUAL: case '>': case T_IS_GREATER_OR_EQUAL: case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: case T_IS_EQUAL: case T_IS_NOT_EQUAL: return m_exp; } break; } case KindOfUnaryOpExpression: { int op = static_pointer_cast<UnaryOpExpression>(m_exp)->getOp(); switch (op) { case T_BOOL_CAST: case '!': case T_ISSET: case T_EMPTY: case T_PRINT: return m_exp; } break; } } } return ExpressionPtr(); }
ExpressionPtr AssignmentExpression::preOptimize(AnalysisResultConstPtr ar) { if (Option::EliminateDeadCode && ar->getPhase() >= AnalysisResult::FirstPreOptimize) { // otherwise used & needed flags may not be up to date yet ExpressionPtr rep = optimize(ar); if (rep) return rep; } if (m_variable->getContainedEffects() & ~(CreateEffect|AccessorEffect)) { return ExpressionPtr(); } ExpressionPtr val = m_value; while (val) { if (val->is(KindOfExpressionList)) { ExpressionListPtr el(static_pointer_cast<ExpressionList>(val)); val = el->listValue(); continue; } if (val->is(KindOfAssignmentExpression)) { val = static_pointer_cast<AssignmentExpression>(val)->m_value; continue; } break; } if (val && val->isScalar()) { if (val != m_value) { ExpressionListPtr rep(new ExpressionList( getScope(), getLocation(), KindOfExpressionList, ExpressionList::ListKindWrapped)); rep->addElement(m_value); m_value = val->clone(); rep->addElement(static_pointer_cast<Expression>(shared_from_this())); return replaceValue(rep); } if (!m_ref && m_variable->is(KindOfArrayElementExpression)) { ArrayElementExpressionPtr ae( static_pointer_cast<ArrayElementExpression>(m_variable)); ExpressionPtr avar(ae->getVariable()); ExpressionPtr aoff(ae->getOffset()); if (!aoff || aoff->isScalar()) { avar = avar->getCanonLVal(); while (avar) { if (avar->isScalar()) { Variant v,o,r; if (!avar->getScalarValue(v)) break; if (!val->getScalarValue(r)) break; try { g_context->setThrowAllErrors(true); if (aoff) { if (!aoff->getScalarValue(o)) break; v.set(o, r); } else { v.append(r); } g_context->setThrowAllErrors(false); } catch (...) { break; } ExpressionPtr rep( new AssignmentExpression( getScope(), getLocation(), KindOfAssignmentExpression, m_variable->replaceValue(Clone(ae->getVariable())), makeScalarExpression(ar, v), false)); if (!isUnused()) { ExpressionListPtr el( new ExpressionList( getScope(), getLocation(), KindOfExpressionList, ExpressionList::ListKindWrapped)); el->addElement(rep); el->addElement(val); rep = el; } return replaceValue(rep); } avar = avar->getCanonPtr(); } g_context->setThrowAllErrors(false); } } } return ExpressionPtr(); }
StatementPtr IfStatement::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() < AnalysisResult::FirstPreOptimize) { return StatementPtr(); } // we cannot optimize away the code inside if statement, because // there may be a goto that goes into if statement. if (hasReachableLabel()) { return StatementPtr(); } bool changed = false; int i; int j; Variant value; bool hoist = false; for (i = 0; i < m_stmts->getCount(); i++) { IfBranchStatementPtr branch = dynamic_pointer_cast<IfBranchStatement>((*m_stmts)[i]); ExpressionPtr condition = branch->getCondition(); if (!condition) { StatementPtr stmt = branch->getStmt(); if (stmt) { if (!i && ((getFunctionScope() && !getFunctionScope()->inPseudoMain()) || !stmt->hasDecl())) { hoist = true; break; } if (stmt->is(KindOfIfStatement)) { StatementListPtr sub_stmts = dynamic_pointer_cast<IfStatement>(stmt)->m_stmts; m_stmts->removeElement(i); changed = true; for (j = 0; j < sub_stmts->getCount(); j++) { m_stmts->insertElement((*sub_stmts)[j], i++); } } } break; } else if (condition->getEffectiveScalar(value)) { if (value.toBoolean()) { hoist = !i && ((getFunctionScope() && !getFunctionScope()->inPseudoMain()) || !branch->hasDecl()); break; } else if (!condition->hasEffect()) { m_stmts->removeElement(i--); changed = true; } else if (branch->getStmt()) { branch->clearStmt(); changed = true; } } } if (!changed && i && i == m_stmts->getCount()) return StatementPtr(); // either else branch or if (true) branch without further declarations i++; while (i < m_stmts->getCount()) { m_stmts->removeElement(i); changed = true; } // if there is only one branch left, return stmt. if (hoist) { IfBranchStatementPtr branch = dynamic_pointer_cast<IfBranchStatement>((*m_stmts)[0]); return branch->getStmt() ? branch->getStmt() : NULL_STATEMENT(); } else if (m_stmts->getCount() == 0) { return NULL_STATEMENT(); } else { return changed ? static_pointer_cast<Statement>(shared_from_this()) : StatementPtr(); } }
// foldConst() is callable from the parse phase as well as the analysis phase. // We take advantage of this during the parse phase to reduce very simple // expressions down to a single scalar and keep the parse tree smaller, // especially in cases of long chains of binary operators. However, we limit // the effectivness of this during parse to ensure that we eliminate only // very simple scalars that don't require analysis in later phases. For now, // that's just simply scalar values. ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { ExpressionPtr optExp; Variant v1; Variant v2; if (!m_exp2->getScalarValue(v2)) { if ((ar->getPhase() != AnalysisResult::ParseAllFiles) && m_exp1->isScalar() && m_exp1->getScalarValue(v1)) { switch (m_op) { case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v1.isNull()) { return makeIsNull(ar, getRange(), m_exp2, m_op == T_IS_NOT_IDENTICAL); } break; case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { ExpressionPtr rep = v1.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getRange(), rep, T_BOOL_CAST, true)); return replaceValue(rep); } case '+': case '.': case '*': case '&': case '|': case '^': if (m_exp2->is(KindOfBinaryOpExpression)) { auto binOpExp = dynamic_pointer_cast<BinaryOpExpression>(m_exp2); if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) { auto aExp = m_exp1; auto bExp = binOpExp->m_exp1; auto cExp = binOpExp->m_exp2; if (aExp->isArray() || bExp->isArray() || cExp->isArray()) { break; } m_exp1 = binOpExp = Clone(binOpExp); m_exp2 = cExp; binOpExp->m_exp1 = aExp; binOpExp->m_exp2 = bExp; if (auto optExp = binOpExp->foldConst(ar)) { m_exp1 = optExp; } return static_pointer_cast<Expression>(shared_from_this()); } } break; default: break; } } return ExpressionPtr(); } if (m_exp1->isScalar()) { if (!m_exp1->getScalarValue(v1)) return ExpressionPtr(); try { auto scalar1 = dynamic_pointer_cast<ScalarExpression>(m_exp1); auto scalar2 = dynamic_pointer_cast<ScalarExpression>(m_exp2); // Some data, like the values of __CLASS__ and friends, are not available // while we're still in the initial parse phase. if (ar->getPhase() == AnalysisResult::ParseAllFiles) { if ((scalar1 && scalar1->needsTranslation()) || (scalar2 && scalar2->needsTranslation())) { return ExpressionPtr(); } } if ((!Option::WholeProgram || !Option::ParseTimeOpts) && getScope()) { // In the VM, don't optimize __CLASS__ if within a trait, since // __CLASS__ is not resolved yet. auto cs = getClassScope(); if (cs && cs->isTrait()) { if ((scalar1 && scalar1->getType() == T_CLASS_C) || (scalar2 && scalar2->getType() == T_CLASS_C)) { return ExpressionPtr(); } } } Variant result; auto add = RuntimeOption::IntsOverflowToInts ? cellAdd : cellAddO; auto sub = RuntimeOption::IntsOverflowToInts ? cellSub : cellSubO; auto mul = RuntimeOption::IntsOverflowToInts ? cellMul : cellMulO; switch (m_op) { case T_LOGICAL_XOR: result = static_cast<bool>(v1.toBoolean() ^ v2.toBoolean()); break; case '|': *result.asCell() = cellBitOr(*v1.asCell(), *v2.asCell()); break; case '&': *result.asCell() = cellBitAnd(*v1.asCell(), *v2.asCell()); break; case '^': *result.asCell() = cellBitXor(*v1.asCell(), *v2.asCell()); break; case '.': if (v1.isArray() || v2.isArray()) { return ExpressionPtr(); } result = concat(v1.toString(), v2.toString()); break; case T_IS_IDENTICAL: result = same(v1, v2); break; case T_IS_NOT_IDENTICAL: result = !same(v1, v2); break; case T_IS_EQUAL: result = equal(v1, v2); break; case T_IS_NOT_EQUAL: result = !equal(v1, v2); break; case '<': result = less(v1, v2); break; case T_IS_SMALLER_OR_EQUAL: result = cellLessOrEqual(*v1.asCell(), *v2.asCell()); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = cellGreaterOrEqual(*v1.asCell(), *v2.asCell()); break; case T_SPACESHIP: result = cellCompare(*v1.asCell(), *v2.asCell()); break; case '+': *result.asCell() = add(*v1.asCell(), *v2.asCell()); break; case '-': *result.asCell() = sub(*v1.asCell(), *v2.asCell()); break; case '*': *result.asCell() = mul(*v1.asCell(), *v2.asCell()); break; case '/': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } *result.asCell() = cellDiv(*v1.asCell(), *v2.asCell()); break; case '%': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } *result.asCell() = cellMod(*v1.asCell(), *v2.asCell()); break; case T_SL: { int64_t shift = v2.toInt64(); if (!RuntimeOption::PHP7_IntSemantics) { result = v1.toInt64() << (shift & 63); } else if (shift >= 64) { result = 0; } else if (shift < 0) { // This raises an error, and so can't be folded. return ExpressionPtr(); } else { result = v1.toInt64() << (shift & 63); } break; } case T_SR: { int64_t shift = v2.toInt64(); if (!RuntimeOption::PHP7_IntSemantics) { result = v1.toInt64() >> (shift & 63); } else if (shift >= 64) { result = v1.toInt64() >= 0 ? 0 : -1; } else if (shift < 0) { // This raises an error, and so can't be folded. return ExpressionPtr(); } else { result = v1.toInt64() >> (shift & 63); } break; } case T_BOOLEAN_OR: result = v1.toBoolean() || v2.toBoolean(); break; case T_BOOLEAN_AND: result = v1.toBoolean() && v2.toBoolean(); break; case T_LOGICAL_OR: result = v1.toBoolean() || v2.toBoolean(); break; case T_LOGICAL_AND: result = v1.toBoolean() && v2.toBoolean(); break; case T_INSTANCEOF: { if (v2.isString()) { if (v1.isArray() && interface_supports_array(v2.getStringData())) { result = true; break; } if (v1.isString() && interface_supports_string(v2.getStringData())) { result = true; break; } if (v1.isInteger() && interface_supports_int(v2.getStringData())) { result = true; break; } if (v1.isDouble() && interface_supports_double(v2.getStringData())) { result = true; break; } } result = false; break; } default: return ExpressionPtr(); } return makeScalarExpression(ar, result); } catch (...) {
StatementPtr ExpStatement::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() != AnalysisResult::AnalyzeInclude) { m_exp = m_exp->unneeded(); } return StatementPtr(); }
// foldConst() is callable from the parse phase as well as the analysis phase. // We take advantage of this during the parse phase to reduce very simple // expressions down to a single scalar and keep the parse tree smaller, // especially in cases of long chains of binary operators. However, we limit // the effectivness of this during parse to ensure that we eliminate only // very simple scalars that don't require analysis in later phases. For now, // that's just simply scalar values. ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { ExpressionPtr optExp; Variant v1; Variant v2; if (!m_exp2->getScalarValue(v2)) { if ((ar->getPhase() != AnalysisResult::ParseAllFiles) && m_exp1->isScalar() && m_exp1->getScalarValue(v1)) { switch (m_op) { case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v1.isNull()) { return makeIsNull(ar, getLocation(), m_exp2, m_op == T_IS_NOT_IDENTICAL); } break; case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { ExpressionPtr rep = v1.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); return replaceValue(rep); } case '+': case '.': case '*': case '&': case '|': case '^': if (m_exp2->is(KindOfBinaryOpExpression)) { BinaryOpExpressionPtr binOpExp = dynamic_pointer_cast<BinaryOpExpression>(m_exp2); if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) { ExpressionPtr aExp = m_exp1; ExpressionPtr bExp = binOpExp->m_exp1; ExpressionPtr cExp = binOpExp->m_exp2; m_exp1 = binOpExp = Clone(binOpExp); m_exp2 = cExp; binOpExp->m_exp1 = aExp; binOpExp->m_exp2 = bExp; if (ExpressionPtr optExp = binOpExp->foldConst(ar)) { m_exp1 = optExp; } return static_pointer_cast<Expression>(shared_from_this()); } } break; default: break; } } return ExpressionPtr(); } if (m_exp1->isScalar()) { if (!m_exp1->getScalarValue(v1)) return ExpressionPtr(); try { ScalarExpressionPtr scalar1 = dynamic_pointer_cast<ScalarExpression>(m_exp1); ScalarExpressionPtr scalar2 = dynamic_pointer_cast<ScalarExpression>(m_exp2); // Some data, like the values of __CLASS__ and friends, are not available // while we're still in the initial parse phase. if (ar->getPhase() == AnalysisResult::ParseAllFiles) { if ((scalar1 && scalar1->needsTranslation()) || (scalar2 && scalar2->needsTranslation())) { return ExpressionPtr(); } } if (!Option::WholeProgram || !Option::ParseTimeOpts) { // In the VM, don't optimize __CLASS__ if within a trait, since // __CLASS__ is not resolved yet. ClassScopeRawPtr clsScope = getOriginalClass(); if (clsScope && clsScope->isTrait()) { if ((scalar1 && scalar1->getType() == T_CLASS_C) || (scalar2 && scalar2->getType() == T_CLASS_C)) { return ExpressionPtr(); } } } Variant result; switch (m_op) { case T_LOGICAL_XOR: result = logical_xor(v1, v2); break; case '|': result = bitwise_or(v1, v2); break; case '&': result = bitwise_and(v1, v2); break; case '^': result = bitwise_xor(v1, v2); break; case '.': result = concat(v1, v2); break; case T_IS_IDENTICAL: result = same(v1, v2); break; case T_IS_NOT_IDENTICAL: result = !same(v1, v2); break; case T_IS_EQUAL: result = equal(v1, v2); break; case T_IS_NOT_EQUAL: result = !equal(v1, v2); break; case '<': result = less(v1, v2); break; case T_IS_SMALLER_OR_EQUAL: result = less_or_equal(v1, v2); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = more_or_equal(v1, v2); break; case '+': result = plus(v1, v2); break; case '-': result = minus(v1, v2); break; case '*': result = multiply(v1, v2); break; case '/': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } result = divide(v1, v2); break; case '%': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } result = modulo(v1, v2); break; case T_SL: result = shift_left(v1, v2); break; case T_SR: result = shift_right(v1, v2); break; case T_BOOLEAN_OR: result = v1 || v2; break; case T_BOOLEAN_AND: result = v1 && v2; break; case T_LOGICAL_OR: result = v1 || v2; break; case T_LOGICAL_AND: result = v1 && v2; break; case T_INSTANCEOF: result = false; break; default: return ExpressionPtr(); } return makeScalarExpression(ar, result); } catch (...) { } } else if (ar->getPhase() != AnalysisResult::ParseAllFiles) { switch (m_op) { case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND); ExpressionPtr rep = useFirst ? m_exp1 : m_exp2; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); if (!useFirst) { ExpressionListPtr l( new ExpressionList( getScope(), getLocation(), ExpressionList::ListKindComma)); l->addElement(m_exp1); l->addElement(rep); l->setActualType(Type::Boolean); rep = l; } rep->setExpectedType(getExpectedType()); return replaceValue(rep); } case T_LOGICAL_XOR: case '|': case '&': case '^': case '.': case '+': case '*': optExp = foldRightAssoc(ar); if (optExp) return optExp; break; case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v2.isNull()) { return makeIsNull(ar, getLocation(), m_exp1, m_op == T_IS_NOT_IDENTICAL); } break; default: break; } } return ExpressionPtr(); }
StatementPtr ExpStatement::preOptimize(AnalysisResultConstPtr ar) { assert (ar->getPhase() > AnalysisResult::AnalyzeAll); m_exp = m_exp->unneeded(); return StatementPtr(); }
// foldConst() is callable from the parse phase as well as the analysis phase. // We take advantage of this during the parse phase to reduce very simple // expressions down to a single scalar and keep the parse tree smaller, // especially in cases of long chains of binary operators. However, we limit // the effectivness of this during parse to ensure that we eliminate only // very simple scalars that don't require analysis in later phases. For now, // that's just simply scalar values. ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { ExpressionPtr optExp; Variant v1; Variant v2; if (!m_exp2->getScalarValue(v2)) { if ((ar->getPhase() != AnalysisResult::ParseAllFiles) && m_exp1->isScalar() && m_exp1->getScalarValue(v1)) { switch (m_op) { case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v1.isNull()) { return makeIsNull(ar, getLocation(), m_exp2, m_op == T_IS_NOT_IDENTICAL); } break; case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { ExpressionPtr rep = v1.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); return replaceValue(rep); } case '+': case '.': case '*': case '&': case '|': case '^': if (m_exp2->is(KindOfBinaryOpExpression)) { BinaryOpExpressionPtr binOpExp = dynamic_pointer_cast<BinaryOpExpression>(m_exp2); if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) { ExpressionPtr aExp = m_exp1; ExpressionPtr bExp = binOpExp->m_exp1; ExpressionPtr cExp = binOpExp->m_exp2; if (aExp->isArray() || bExp->isArray() || cExp->isArray()) { break; } m_exp1 = binOpExp = Clone(binOpExp); m_exp2 = cExp; binOpExp->m_exp1 = aExp; binOpExp->m_exp2 = bExp; if (ExpressionPtr optExp = binOpExp->foldConst(ar)) { m_exp1 = optExp; } return static_pointer_cast<Expression>(shared_from_this()); } } break; default: break; } } return ExpressionPtr(); } if (m_exp1->isScalar()) { if (!m_exp1->getScalarValue(v1)) return ExpressionPtr(); try { ScalarExpressionPtr scalar1 = dynamic_pointer_cast<ScalarExpression>(m_exp1); ScalarExpressionPtr scalar2 = dynamic_pointer_cast<ScalarExpression>(m_exp2); // Some data, like the values of __CLASS__ and friends, are not available // while we're still in the initial parse phase. if (ar->getPhase() == AnalysisResult::ParseAllFiles) { if ((scalar1 && scalar1->needsTranslation()) || (scalar2 && scalar2->needsTranslation())) { return ExpressionPtr(); } } if (!Option::WholeProgram || !Option::ParseTimeOpts) { // In the VM, don't optimize __CLASS__ if within a trait, since // __CLASS__ is not resolved yet. ClassScopeRawPtr clsScope = getOriginalClass(); if (clsScope && clsScope->isTrait()) { if ((scalar1 && scalar1->getType() == T_CLASS_C) || (scalar2 && scalar2->getType() == T_CLASS_C)) { return ExpressionPtr(); } } } Variant result; switch (m_op) { case T_LOGICAL_XOR: result = static_cast<bool>(v1.toBoolean() ^ v2.toBoolean()); break; case '|': *result.asCell() = cellBitOr(*v1.asCell(), *v2.asCell()); break; case '&': *result.asCell() = cellBitAnd(*v1.asCell(), *v2.asCell()); break; case '^': *result.asCell() = cellBitXor(*v1.asCell(), *v2.asCell()); break; case '.': if (v1.isArray() || v2.isArray()) { return ExpressionPtr(); } result = concat(v1.toString(), v2.toString()); break; case T_IS_IDENTICAL: result = same(v1, v2); break; case T_IS_NOT_IDENTICAL: result = !same(v1, v2); break; case T_IS_EQUAL: result = equal(v1, v2); break; case T_IS_NOT_EQUAL: result = !equal(v1, v2); break; case '<': result = less(v1, v2); break; case T_IS_SMALLER_OR_EQUAL: result = cellLessOrEqual(*v1.asCell(), *v2.asCell()); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = cellGreaterOrEqual(*v1.asCell(), *v2.asCell()); break; case '+': *result.asCell() = cellAdd(*v1.asCell(), *v2.asCell()); break; case '-': *result.asCell() = cellSub(*v1.asCell(), *v2.asCell()); break; case '*': *result.asCell() = cellMul(*v1.asCell(), *v2.asCell()); break; case '/': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } *result.asCell() = cellDiv(*v1.asCell(), *v2.asCell()); break; case '%': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } *result.asCell() = cellMod(*v1.asCell(), *v2.asCell()); break; case T_SL: result = v1.toInt64() << v2.toInt64(); break; case T_SR: result = v1.toInt64() >> v2.toInt64(); break; case T_BOOLEAN_OR: result = v1.toBoolean() || v2.toBoolean(); break; case T_BOOLEAN_AND: result = v1.toBoolean() && v2.toBoolean(); break; case T_LOGICAL_OR: result = v1.toBoolean() || v2.toBoolean(); break; case T_LOGICAL_AND: result = v1.toBoolean() && v2.toBoolean(); break; case T_INSTANCEOF: { if (v2.isString()) { if (v1.isArray() && interface_supports_array(v2.getStringData())) { result = true; break; } if (v1.isString() && interface_supports_string(v2.getStringData())) { result = true; break; } if (v1.isInteger() && interface_supports_int(v2.getStringData())) { result = true; break; } if (v1.isDouble() && interface_supports_double(v2.getStringData())) { result = true; break; } } result = false; break; } default: return ExpressionPtr(); } return makeScalarExpression(ar, result); } catch (...) { } } else if (ar->getPhase() != AnalysisResult::ParseAllFiles) {
StatementPtr InterfaceStatement::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() >= AnalysisResult::AnalyzeAll) { checkVolatile(ar); } return StatementPtr(); }