Delete atomic_inc
[hiphop-php.git] / hphp / compiler / analysis / analysis_result.cpp
blob59be0ea4c0823c88ba20caa632b9248ea4b71414
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/compiler/analysis/analysis_result.h"
19 #include <iomanip>
20 #include <algorithm>
21 #include <sstream>
22 #include <boost/format.hpp>
23 #include <boost/bind.hpp>
24 #include "hphp/compiler/analysis/alias_manager.h"
25 #include "hphp/compiler/analysis/file_scope.h"
26 #include "hphp/compiler/analysis/class_scope.h"
27 #include "hphp/compiler/analysis/code_error.h"
28 #include "hphp/compiler/analysis/depth_first_visitor.h"
29 #include "hphp/compiler/statement/statement_list.h"
30 #include "hphp/compiler/statement/if_branch_statement.h"
31 #include "hphp/compiler/statement/method_statement.h"
32 #include "hphp/compiler/statement/loop_statement.h"
33 #include "hphp/compiler/statement/class_variable.h"
34 #include "hphp/compiler/statement/use_trait_statement.h"
35 #include "hphp/compiler/analysis/symbol_table.h"
36 #include "hphp/compiler/package.h"
37 #include "hphp/compiler/parser/parser.h"
38 #include "hphp/compiler/option.h"
39 #include "hphp/compiler/analysis/function_scope.h"
40 #include "hphp/compiler/builtin_symbols.h"
41 #include "hphp/compiler/analysis/constant_table.h"
42 #include "hphp/compiler/analysis/variable_table.h"
43 #include "hphp/compiler/expression/scalar_expression.h"
44 #include "hphp/compiler/expression/constant_expression.h"
45 #include "hphp/compiler/expression/expression_list.h"
46 #include "hphp/compiler/expression/array_pair_expression.h"
47 #include "hphp/compiler/expression/simple_function_call.h"
48 #include "hphp/runtime/ext/ext_json.h"
49 #include "hphp/runtime/base/zend-printf.h"
50 #include "hphp/runtime/base/program_functions.h"
51 #include "hphp/util/atomic.h"
52 #include "hphp/util/logger.h"
53 #include "hphp/util/util.h"
54 #include "hphp/util/hash.h"
55 #include "hphp/util/process.h"
56 #include "hphp/util/job_queue.h"
57 #include "hphp/util/timer.h"
59 using namespace HPHP;
60 using std::map;
61 using std::set;
62 using std::ostringstream;
63 using std::ofstream;
64 using std::ifstream;
65 using std::pair;
67 ///////////////////////////////////////////////////////////////////////////////
68 // initialization
70 IMPLEMENT_THREAD_LOCAL(BlockScopeRawPtr,
71 AnalysisResult::s_currentScopeThreadLocal);
73 IMPLEMENT_THREAD_LOCAL(BlockScopeRawPtrFlagsHashMap,
74 AnalysisResult::s_changedScopesMapThreadLocal);
76 AnalysisResult::AnalysisResult()
77 : BlockScope("Root", "", StatementPtr(), BlockScope::ProgramScope),
78 m_arrayLitstrKeyMaxSize(0), m_arrayIntegerKeyMaxSize(0),
79 m_package(nullptr), m_parseOnDemand(false), m_phase(ParseAllFiles) {
80 m_classForcedVariants[0] = m_classForcedVariants[1] = false;
83 void AnalysisResult::appendExtraCode(const std::string &key,
84 const std::string &code) {
85 string &extraCode = m_extraCodes[key];
87 if (extraCode.empty()) {
88 extraCode = "<?php\n";
90 extraCode += code + "\n";
93 void AnalysisResult::appendExtraCode(const std::string &key,
94 const std::string &code) const {
95 lock()->appendExtraCode(key, code);
98 void AnalysisResult::parseExtraCode(const string &key) {
99 Lock lock(getMutex());
100 map<string, string>::iterator iter = m_extraCodes.find(key);
101 if (iter != m_extraCodes.end()) {
102 string code = iter->second;
103 string sfilename = iter->first + "." + Option::LambdaPrefix + "lambda";
104 m_extraCodes.erase(key);
106 const char *filename = m_extraCodeFileNames.add(sfilename.c_str());
107 Compiler::Parser::ParseString(code, shared_from_this(), filename, true);
111 ///////////////////////////////////////////////////////////////////////////////
112 // general functions
114 void AnalysisResult::addFileScope(FileScopePtr fileScope) {
115 assert(fileScope);
116 FileScopePtr &res = m_files[fileScope->getName()];
117 assert(!res);
118 res = fileScope;
119 vertex_descriptor vertex = add_vertex(m_depGraph);
120 fileScope->setVertex(vertex);
121 m_fileVertMap[vertex] = fileScope;
122 m_fileScopes.push_back(fileScope);
125 bool AnalysisResult::inParseOnDemandDirs(const string &filename) const {
126 for (size_t i = 0; i < m_parseOnDemandDirs.size(); i++) {
127 if (filename.find(m_parseOnDemandDirs[i]) == 0) return true;
129 return false;
132 void AnalysisResult::parseOnDemand(const std::string &name) const {
133 if (m_package) {
134 const std::string &root = m_package->getRoot();
135 string rname = name;
136 if (name.find(root) == 0) {
137 rname = name.substr(root.length());
139 if ((m_parseOnDemand || inParseOnDemandDirs(rname)) &&
140 Option::PackageExcludeFiles.find(rname) ==
141 Option::PackageExcludeFiles.end() &&
142 !Option::IsFileExcluded(rname, Option::PackageExcludePatterns)) {
144 Locker l(this);
145 if (m_files.find(rname) != m_files.end()) return;
147 m_package->addSourceFile(rname.c_str());
152 void AnalysisResult::parseOnDemandBy(const string &name,
153 const map<string,string> &amap) const {
154 if (m_package) {
155 auto it = amap.find(name);
156 if (it != amap.end()) {
157 parseOnDemand(Option::AutoloadRoot + it->second);
162 void AnalysisResult::addNSFallbackFunc(ConstructPtr c, FileScopePtr fs) {
163 m_nsFallbackFuncs.insert(std::make_pair(c, fs));
166 FileScopePtr AnalysisResult::findFileScope(const std::string &name) const {
167 StringToFileScopePtrMap::const_iterator iter = m_files.find(name);
168 if (iter != m_files.end()) {
169 return iter->second;
171 return FileScopePtr();
174 FunctionScopePtr AnalysisResult::findFunction(
175 const std::string &funcName) const {
176 StringToFunctionScopePtrMap::const_iterator bit =
177 m_functions.find(funcName);
178 if (bit != m_functions.end() && !bit->second->allowOverride()) {
179 return bit->second;
181 StringToFunctionScopePtrMap::const_iterator iter =
182 m_functionDecs.find(funcName);
183 if (iter != m_functionDecs.end()) {
184 return iter->second;
186 return bit != m_functions.end() ? bit->second : FunctionScopePtr();
189 BlockScopePtr AnalysisResult::findConstantDeclarer(
190 const std::string &name) {
191 if (getConstants()->isPresent(name)) return shared_from_this();
192 StringToFileScopePtrMap::const_iterator iter = m_constDecs.find(name);
193 if (iter != m_constDecs.end()) return iter->second;
194 return BlockScopePtr();
197 ClassScopePtr AnalysisResult::findClass(const std::string &name) const {
198 AnalysisResultConstPtr ar = shared_from_this();
199 string lname = Util::toLower(name);
200 StringToClassScopePtrMap::const_iterator sysIter =
201 m_systemClasses.find(lname);
202 if (sysIter != m_systemClasses.end()) return sysIter->second;
204 StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.find(lname);
205 if (iter != m_classDecs.end() && iter->second.size()) {
206 return iter->second.back();
208 return ClassScopePtr();
211 ClassScopePtr AnalysisResult::findClass(const std::string &name,
212 FindClassBy by) {
213 AnalysisResultPtr ar = shared_from_this();
214 if (by == PropertyName) return ClassScopePtr();
216 string lname = Util::toLower(name);
217 if (by == MethodName) {
218 StringToClassScopePtrVecMap::iterator iter =
219 m_methodToClassDecs.find(lname);
220 if (iter != m_methodToClassDecs.end()) {
221 if (iter->second.size() == 1) {
222 iter->second[0]->findFunction(ar, lname, true)->setDynamic();
223 return ClassScopePtr();
224 } else {
225 // The call to findClass by method name means all these
226 // same-named methods should be dynamic since there will
227 // be an invoke to call one of them.
228 BOOST_FOREACH(ClassScopePtr cls, iter->second) {
229 FunctionScopePtr func = cls->findFunction(ar, lname, true);
230 // Something fishy here
231 if (func) {
232 func->setDynamic();
235 iter->second.clear();
238 } else {
239 return findClass(name);
241 return ClassScopePtr();
244 const ClassScopePtrVec &
245 AnalysisResult::findRedeclaredClasses(const std::string &name) const {
246 StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.find(name);
247 if (iter == m_classDecs.end()) {
248 static ClassScopePtrVec empty;
249 empty.clear();
250 return empty;
252 return iter->second;
255 ClassScopePtrVec AnalysisResult::findClasses(const std::string &name) const {
256 StringToClassScopePtrMap::const_iterator sysIter =
257 m_systemClasses.find(name);
258 if (sysIter != m_systemClasses.end()) {
259 return ClassScopePtrVec(1, sysIter->second);
262 return findRedeclaredClasses(name);
265 bool AnalysisResult::classMemberExists(const std::string &name,
266 FindClassBy by) const {
267 if (by == MethodName) {
268 return m_methodToClassDecs.find(name) != m_methodToClassDecs.end();
270 return m_classDecs.find(name) != m_classDecs.end();
273 ClassScopePtr AnalysisResult::findExactClass(ConstructPtr cs,
274 const std::string &name) const {
275 ClassScopePtr cls = findClass(name);
276 if (!cls || !cls->isRedeclaring()) return cls;
277 if (ClassScopePtr currentCls = cs->getClassScope()) {
278 if (cls->getName() == currentCls->getName()) {
279 return currentCls;
282 if (FileScopePtr currentFile = cs->getFileScope()) {
283 return currentFile->resolveClass(cls);
285 return ClassScopePtr();
288 bool AnalysisResult::checkClassPresent(ConstructPtr cs,
289 const std::string &name) const {
290 if (name == "self" || name == "parent") return true;
291 std::string lowerName = Util::toLower(name);
292 if (ClassScopePtr currentCls = cs->getClassScope()) {
293 if (lowerName == currentCls->getName() ||
294 currentCls->derivesFrom(shared_from_this(), lowerName,
295 true, false)) {
296 return true;
299 if (FileScopePtr currentFile = cs->getFileScope()) {
300 StatementList &stmts = *currentFile->getStmt();
301 for (int i = stmts.getCount(); i--; ) {
302 StatementPtr s = stmts[i];
303 if (s && s->is(Statement::KindOfClassStatement)) {
304 ClassScopePtr scope =
305 static_pointer_cast<ClassStatement>(s)->getClassScope();
306 if (lowerName == scope->getName()) {
307 return true;
309 if (scope->derivesFrom(shared_from_this(), lowerName,
310 true, false)) {
311 return true;
316 return false;
319 int AnalysisResult::getFunctionCount() const {
320 int total = 0;
321 for (StringToFileScopePtrMap::const_iterator iter = m_files.begin();
322 iter != m_files.end(); ++iter) {
323 total += iter->second->getFunctionCount();
325 return total;
328 int AnalysisResult::getClassCount() const {
329 int total = 0;
330 for (StringToFileScopePtrMap::const_iterator iter = m_files.begin();
331 iter != m_files.end(); ++iter) {
332 total += iter->second->getClassCount();
334 return total;
337 void AnalysisResult::countReturnTypes(std::map<std::string, int> &counts) {
338 for (StringToFileScopePtrMap::const_iterator iter = m_files.begin();
339 iter != m_files.end(); ++iter) {
340 iter->second->countReturnTypes(counts);
344 ///////////////////////////////////////////////////////////////////////////////
345 // static analysis functions
347 bool AnalysisResult::declareFunction(FunctionScopePtr funcScope) const {
348 assert(m_phase < AnalyzeAll);
350 string fname = funcScope->getName();
351 // System functions override
352 auto it = m_functions.find(fname);
353 if (it != m_functions.end()) {
354 if (!it->second->allowOverride()) {
355 // we need someone to hold on to a reference to it
356 // even though we're not going to do anything with it
357 this->lock()->m_ignoredScopes.push_back(funcScope);
358 return false;
362 return true;
365 bool AnalysisResult::declareClass(ClassScopePtr classScope) const {
366 assert(m_phase < AnalyzeAll);
368 string cname = classScope->getName();
369 // System classes override
370 if (m_systemClasses.find(cname) != m_systemClasses.end()) {
371 // we need someone to hold on to a reference to it
372 // even though we're not going to do anything with it
373 this->lock()->m_ignoredScopes.push_back(classScope);
374 return false;
377 int mask =
378 (m_classForcedVariants[0] ? VariableTable::NonPrivateNonStaticVars : 0) |
379 (m_classForcedVariants[1] ? VariableTable::NonPrivateStaticVars : 0);
381 if (mask) {
382 AnalysisResultConstPtr ar = shared_from_this();
383 classScope->getVariables()->forceVariants(ar, mask);
385 return true;
388 void AnalysisResult::declareUnknownClass(const std::string &name) {
389 m_classDecs.operator[](name);
392 bool AnalysisResult::declareConst(FileScopePtr fs, const string &name) {
393 if (getConstants()->isPresent(name) ||
394 m_constDecs.find(name) != m_constDecs.end()) {
395 m_constRedeclared.insert(name);
396 return false;
397 } else {
398 m_constDecs[name] = fs;
399 return true;
403 static bool by_source(const BlockScopePtr &b1, const BlockScopePtr &b2) {
404 return b1->getStmt()->getLocation()->
405 compare(b2->getStmt()->getLocation().get()) < 0;
408 void AnalysisResult::canonicalizeSymbolOrder() {
409 getConstants()->canonicalizeSymbolOrder();
410 getVariables()->canonicalizeSymbolOrder();
413 void AnalysisResult::markRedeclaringClasses() {
414 AnalysisResultPtr ar = shared_from_this();
415 for (StringToClassScopePtrVecMap::iterator iter = m_classDecs.begin();
416 iter != m_classDecs.end(); ++iter) {
417 ClassScopePtrVec &classes = iter->second;
418 if (classes.size() > 1) {
419 sort(classes.begin(), classes.end(), by_source);
420 for (unsigned int i = 0; i < classes.size(); i++) {
421 classes[i]->setRedeclaring(ar, i);
426 auto markRedeclaring = [&] (const std::string& name) {
427 auto it = m_classDecs.find(name);
428 if (it != m_classDecs.end()) {
429 auto& classes = it->second;
430 for (unsigned int i = 0; i < classes.size(); ++i) {
431 classes[i]->setRedeclaring(ar, i);
437 * In WholeProgram mode, during parse time we collected all
438 * class_alias calls so we can mark the targets of such calls
439 * redeclaring if necessary.
441 * Two cases here that definitely require this:
443 * - If an alias name has the same name as another class, we need
444 * to mark *that* class as redeclaring, since it may mean
445 * different things in different requests now.
447 * - If an alias name can refer to more than one class, each of
448 * those classes must be marked redeclaring.
450 * In the simple case of a unique alias name and a unique target
451 * name, we might be able to get away with manipulating the target
452 * classes' volatility.
454 * Rather than work through the various cases here, though, we've
455 * just decided to just play it safe and mark all the names involved
456 * as redeclaring for now.
458 for (auto& kv : m_classAliases) {
459 markRedeclaring(Util::toLower(kv.first));
460 markRedeclaring(Util::toLower(kv.second));
464 * Similar to class_alias, when a type alias is declared with the
465 * same name as a class in the program, we need to make sure the
466 * class is marked redeclaring. It is possible in some requests
467 * that things like 'instanceof Foo' will not mean the same thing.
469 for (auto& name : m_typeAliasNames) {
470 markRedeclaring(Util::toLower(name));
474 ///////////////////////////////////////////////////////////////////////////////
475 // Dependencies
477 void AnalysisResult::link(FileScopePtr user, FileScopePtr provider) {
478 if (user != provider) {
479 bool needsLock = getPhase() != AnalyzeAll &&
480 getPhase() != AnalyzeFinal;
481 ConditionalLock lock(m_depGraphMutex, needsLock);
482 add_edge(user->vertex(), provider->vertex(), m_depGraph);
486 bool AnalysisResult::addClassDependency(FileScopePtr usingFile,
487 const std::string &className) {
488 if (BuiltinSymbols::s_classes.find(className) !=
489 BuiltinSymbols::s_classes.end())
490 return true;
492 StringToClassScopePtrVecMap::const_iterator iter =
493 m_classDecs.find(className);
494 if (iter == m_classDecs.end() || !iter->second.size()) return false;
495 ClassScopePtr classScope = iter->second[0];
496 if (iter->second.size() != 1) {
497 classScope = usingFile->resolveClass(classScope);
498 if (!classScope) return false;
500 FileScopePtr fileScope = classScope->getContainingFile();
501 link(usingFile, fileScope);
502 return true;
505 bool AnalysisResult::addFunctionDependency(FileScopePtr usingFile,
506 const std::string &functionName) {
507 if (BuiltinSymbols::s_functions.find(functionName) !=
508 BuiltinSymbols::s_functions.end())
509 return true;
510 StringToFunctionScopePtrMap::const_iterator iter =
511 m_functionDecs.find(functionName);
512 if (iter == m_functionDecs.end()) return false;
513 FunctionScopePtr functionScope = iter->second;
514 if (functionScope->isRedeclaring()) {
515 functionScope = usingFile->resolveFunction(functionScope);
516 if (!functionScope) return false;
518 FileScopePtr fileScope = functionScope->getContainingFile();
519 link(usingFile, fileScope);
520 return true;
523 bool AnalysisResult::addIncludeDependency(FileScopePtr usingFile,
524 const std::string &includeFilename) {
525 assert(!includeFilename.empty());
526 FileScopePtr fileScope = findFileScope(includeFilename);
527 if (fileScope) {
528 link(usingFile, fileScope);
529 return true;
530 } else {
531 return false;
535 bool AnalysisResult::addConstantDependency(FileScopePtr usingFile,
536 const std::string &constantName) {
537 if (m_constants->isPresent(constantName))
538 return true;
540 StringToFileScopePtrMap::const_iterator iter =
541 m_constDecs.find(constantName);
542 if (iter == m_constDecs.end()) return false;
543 FileScopePtr fileScope = iter->second;
544 link(usingFile, fileScope);
545 return true;
548 bool AnalysisResult::isConstantDeclared(const std::string &constName) const {
549 if (m_constants->isPresent(constName)) return true;
550 StringToFileScopePtrMap::const_iterator iter = m_constDecs.find(constName);
551 if (iter == m_constDecs.end()) return false;
552 FileScopePtr fileScope = iter->second;
553 ConstantTablePtr constants = fileScope->getConstants();
554 ConstructPtr decl = constants->getValue(constName);
555 if (decl) return true;
556 return false;
559 bool AnalysisResult::isConstantRedeclared(const std::string &constName) const {
560 return m_constRedeclared.find(constName) != m_constRedeclared.end();
563 bool AnalysisResult::isSystemConstant(const std::string &constName) const {
564 return m_constants->isSystem(constName);
567 ///////////////////////////////////////////////////////////////////////////////
568 // Program
570 void AnalysisResult::loadBuiltins() {
571 AnalysisResultPtr ar = shared_from_this();
572 BuiltinSymbols::LoadFunctions(ar, m_functions);
573 BuiltinSymbols::LoadClasses(ar, m_systemClasses);
574 BuiltinSymbols::LoadVariables(ar, m_variables);
575 BuiltinSymbols::LoadConstants(ar, m_constants);
578 void AnalysisResult::checkClassDerivations() {
579 AnalysisResultPtr ar = shared_from_this();
580 ClassScopePtr cls;
581 for (StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.begin();
582 iter != m_classDecs.end(); ++iter) {
583 BOOST_FOREACH(cls, iter->second) {
584 hphp_string_iset seen;
585 cls->checkDerivation(ar, seen);
586 if (Option::WholeProgram) {
587 cls->importUsedTraits(ar);
593 void AnalysisResult::resolveNSFallbackFuncs() {
594 for (auto &pair : m_nsFallbackFuncs) {
595 SimpleFunctionCallPtr sfc =
596 static_pointer_cast<SimpleFunctionCall>(pair.first);
597 sfc->resolveNSFallbackFunc(
598 shared_from_this(),
599 pair.second
604 void AnalysisResult::collectFunctionsAndClasses(FileScopePtr fs) {
605 const StringToFunctionScopePtrMap &funcs = fs->getFunctions();
607 for (StringToFunctionScopePtrMap::const_iterator iter = funcs.begin();
608 iter != funcs.end(); ++iter) {
609 FunctionScopePtr func = iter->second;
610 if (!func->inPseudoMain()) {
611 FunctionScopePtr &funcDec = m_functionDecs[iter->first];
612 if (funcDec) {
613 FunctionScopePtrVec &funcVec = m_functionReDecs[iter->first];
614 int sz = funcVec.size();
615 if (!sz) {
616 funcDec->setRedeclaring(sz++);
617 funcVec.push_back(funcDec);
619 func->setRedeclaring(sz++);
620 funcVec.push_back(func);
621 } else {
622 funcDec = func;
627 if (const StringToFunctionScopePtrVecMap *redec = fs->getRedecFunctions()) {
628 for (StringToFunctionScopePtrVecMap::const_iterator iter = redec->begin();
629 iter != redec->end(); ++iter) {
630 FunctionScopePtrVec::const_iterator i = iter->second.begin();
631 FunctionScopePtrVec::const_iterator e = iter->second.end();
632 FunctionScopePtr &funcDec = m_functionDecs[iter->first];
633 assert(funcDec); // because the first one was in funcs above
634 FunctionScopePtrVec &funcVec = m_functionReDecs[iter->first];
635 int sz = funcVec.size();
636 if (!sz) {
637 funcDec->setRedeclaring(sz++);
638 funcVec.push_back(funcDec);
640 while (++i != e) { // we already added the first one
641 (*i)->setRedeclaring(sz++);
642 funcVec.push_back(*i);
647 const StringToClassScopePtrVecMap &classes = fs->getClasses();
648 for (StringToClassScopePtrVecMap::const_iterator iter = classes.begin();
649 iter != classes.end(); ++iter) {
650 ClassScopePtrVec &clsVec = m_classDecs[iter->first];
651 clsVec.insert(clsVec.end(), iter->second.begin(), iter->second.end());
654 m_classAliases.insert(fs->getClassAliases().begin(),
655 fs->getClassAliases().end());
656 m_typeAliasNames.insert(fs->getTypeAliasNames().begin(),
657 fs->getTypeAliasNames().end());
660 static bool by_filename(const FileScopePtr &f1, const FileScopePtr &f2) {
661 return f1->getName() < f2->getName();
664 void AnalysisResult::analyzeProgram(bool system /* = false */) {
665 AnalysisResultPtr ar = shared_from_this();
667 getVariables()->forceVariants(ar, VariableTable::AnyVars);
668 getVariables()->setAttribute(VariableTable::ContainsLDynamicVariable);
669 getVariables()->setAttribute(VariableTable::ContainsExtract);
670 getVariables()->setAttribute(VariableTable::ForceGlobal);
672 // Analyze Includes
673 Logger::Verbose("Analyzing Includes");
674 sort(m_fileScopes.begin(), m_fileScopes.end(), by_filename); // fixed order
675 unsigned int i = 0;
676 for (i = 0; i < m_fileScopes.size(); i++) {
677 collectFunctionsAndClasses(m_fileScopes[i]);
680 // Keep generated code identical without randomness
681 canonicalizeSymbolOrder();
683 markRedeclaringClasses();
685 // Analyze some special cases
686 for (set<string>::const_iterator it = Option::VolatileClasses.begin();
687 it != Option::VolatileClasses.end(); ++it) {
688 ClassScopePtr cls = findClass(Util::toLower(*it));
689 if (cls && cls->isUserClass()) {
690 cls->setVolatile();
694 checkClassDerivations();
695 resolveNSFallbackFuncs();
697 // Analyze All
698 Logger::Verbose("Analyzing All");
699 setPhase(AnalysisResult::AnalyzeAll);
700 for (i = 0; i < m_fileScopes.size(); i++) {
701 m_fileScopes[i]->analyzeProgram(ar);
705 Note that cls->collectMethods() can add entries to m_classDecs,
706 which can invalidate iterators. So we have to create an array
707 and then iterate over that.
708 The new entries added to m_classDecs are always empty, so it
709 doesnt matter that we dont include them in the iteration
711 ClassScopePtr cls;
712 std::vector<ClassScopePtr> classes;
713 classes.reserve(m_classDecs.size());
714 for (StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.begin();
715 iter != m_classDecs.end(); ++iter) {
716 BOOST_FOREACH(cls, iter->second) {
717 classes.push_back(cls);
721 // Collect methods
722 BOOST_FOREACH(cls, classes) {
723 if (cls->isRedeclaring()) {
724 cls->setStaticDynamic(ar);
726 StringToFunctionScopePtrMap methods;
727 cls->collectMethods(ar, methods);
728 bool needAbstractMethodImpl =
729 (!cls->isAbstract() && !cls->isInterface() &&
730 !cls->derivesFromRedeclaring() &&
731 !cls->getAttribute(ClassScope::UsesUnknownTrait));
732 for (StringToFunctionScopePtrMap::const_iterator iterMethod =
733 methods.begin(); iterMethod != methods.end(); ++iterMethod) {
734 FunctionScopePtr func = iterMethod->second;
735 if (Option::WholeProgram && !func->hasImpl() && needAbstractMethodImpl) {
736 FunctionScopePtr tmpFunc =
737 cls->findFunction(ar, func->getName(), true, true);
738 always_assert(!tmpFunc || !tmpFunc->hasImpl());
739 Compiler::Error(Compiler::MissingAbstractMethodImpl,
740 func->getStmt(), cls->getStmt());
742 m_methodToClassDecs[iterMethod->first].push_back(cls);
746 string cname;
747 BOOST_FOREACH(tie(cname, cls), m_systemClasses) {
748 StringToFunctionScopePtrMap methods;
749 cls->collectMethods(ar, methods);
750 for (StringToFunctionScopePtrMap::const_iterator iterMethod =
751 methods.begin(); iterMethod != methods.end(); ++iterMethod) {
752 m_methodToClassDecs[iterMethod->first].push_back(cls);
756 // Analyze perfect virtuals
757 if (Option::AnalyzePerfectVirtuals && !system) {
758 analyzePerfectVirtuals();
762 void AnalysisResult::analyzeIncludes() {
763 AnalysisResultPtr ar = shared_from_this();
764 for (unsigned i = 0; i < m_fileScopes.size(); i++) {
765 m_fileScopes[i]->analyzeIncludes(ar);
769 static void addClassRootMethods(AnalysisResultPtr ar, ClassScopePtr cls,
770 hphp_string_set &methods) {
771 const StringToFunctionScopePtrMap &funcs = cls->getFunctions();
772 for (StringToFunctionScopePtrMap::const_iterator iter =
773 funcs.begin(); iter != funcs.end(); ++iter) {
774 ClassScopePtrVec roots;
775 cls->getRootParents(ar, iter->first, roots, cls);
776 for (unsigned int i = 0; i < roots.size(); i++) {
777 methods.insert(roots[i]->getName() + "::" + iter->first);
782 static void addClassRootMethods(AnalysisResultPtr ar, ClassScopePtr cls,
783 StringToFunctionScopePtrVecMap &methods) {
784 const StringToFunctionScopePtrMap &funcs = cls->getFunctions();
785 for (StringToFunctionScopePtrMap::const_iterator iter =
786 funcs.begin(); iter != funcs.end(); ++iter) {
787 ClassScopePtr root = cls->getRootParent(ar, iter->first);
788 string cluster = root->getName() + "::" + iter->first;
789 FunctionScopePtrVec &fs = methods[cluster];
790 fs.push_back(iter->second);
794 void AnalysisResult::analyzePerfectVirtuals() {
795 AnalysisResultPtr ar = shared_from_this();
797 StringToFunctionScopePtrVecMap methods;
798 hphp_string_set redeclaringMethods;
799 for (StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.begin();
800 iter != m_classDecs.end(); ++iter) {
801 for (unsigned int i = 0; i < iter->second.size(); i++) {
802 ClassScopePtr cls = iter->second[i];
804 // being conservative, not to do redeclaring classes at all
805 if (cls->derivesFromRedeclaring()) {
806 addClassRootMethods(ar, cls, redeclaringMethods);
807 continue;
810 // classes derived from system or extension classes can be complicated
811 ClassScopePtr root = cls->getRootParent(ar);
812 if (!root->isUserClass() || root->isExtensionClass()) continue;
814 // cluster virtual methods by a root parent that also defined the method
815 addClassRootMethods(ar, cls, methods);
818 // if ANY class in the hierarchy is a reclaring one, ignore
819 for (hphp_string_set::const_iterator iter = redeclaringMethods.begin();
820 iter != redeclaringMethods.end(); ++iter) {
821 methods.erase(*iter);
823 for (StringToFunctionScopePtrVecMap::const_iterator iter = methods.begin();
824 iter != methods.end(); ++iter) {
825 // if it's unique, ignore
826 const FunctionScopePtrVec &funcs = iter->second;
827 if (funcs.size() < 2) {
828 continue;
831 if (!funcs[0]->isPrivate()) {
832 bool perfect = true;
833 for (unsigned int i = 1; i < funcs.size(); i++) {
834 if (funcs[i]->isPrivate() || !funcs[0]->matchParams(funcs[i])) {
835 perfect = false;
836 break;
839 if (perfect) {
840 for (unsigned int i = 0; i < funcs.size(); i++) {
841 funcs[i]->setPerfectVirtual();
848 void AnalysisResult::analyzeProgramFinal() {
849 AnalysisResultPtr ar = shared_from_this();
850 setPhase(AnalysisResult::AnalyzeFinal);
851 for (uint i = 0; i < m_fileScopes.size(); i++) {
852 m_fileScopes[i]->analyzeProgram(ar);
855 // Keep generated code identical without randomness
856 canonicalizeSymbolOrder();
858 // XXX: this is only here because canonicalizeSymbolOrder used to do
859 // it---is it necessary to repeat at this phase? (Probably not ...)
860 markRedeclaringClasses();
862 setPhase(AnalysisResult::CodeGen);
865 static void dumpVisitor(AnalysisResultPtr ar, StatementPtr s, void *data) {
866 s->dump(0, ar);
869 void AnalysisResult::dump() {
870 visitFiles(dumpVisitor, 0);
871 fflush(0);
874 void AnalysisResult::docJson(const string &filename) {
875 ofstream f(filename.c_str());
876 if (f.fail()) {
877 Logger::Error("Could not open file for writing doc JSON: %s",
878 filename.c_str());
879 return;
881 JSON::DocTarget::OutputStream out(f, shared_from_this());
882 JSON::DocTarget::MapStream ms(out);
884 ms.add("userland", m_fileScopes);
886 ClassScopePtrVec systemClasses;
887 systemClasses.reserve(m_systemClasses.size());
888 for (StringToClassScopePtrMap::iterator it = m_systemClasses.begin();
889 it != m_systemClasses.end(); ++it) {
890 systemClasses.push_back(it->second);
892 // just generate system classes for now
893 ms.add("system", systemClasses);
895 ms.done();
896 f.close();
899 void AnalysisResult::visitFiles(void (*cb)(AnalysisResultPtr,
900 StatementPtr, void*), void *data) {
901 AnalysisResultPtr ar = shared_from_this();
902 for (StringToFileScopePtrMap::const_iterator iter = m_files.begin();
903 iter != m_files.end(); ++iter) {
904 FileScopePtr file = iter->second;
906 file->visit(ar, cb, data);
910 void AnalysisResult::getScopesSet(BlockScopeRawPtrQueue &v) {
911 for (StringToFileScopePtrMap::const_iterator iter = m_files.begin();
912 iter != m_files.end(); ++iter) {
913 FileScopePtr file = iter->second;
914 file->getScopesSet(v);
918 ///////////////////////////////////////////////////////////////////////////////
919 // optimization functions
921 namespace HPHP {
922 ///////////////////////////////////////////////////////////////////////////////
924 template <typename When>
925 struct OptWorker;
927 template <typename When>
928 struct OptVisitor {
929 typedef OptVisitor<When> Visitor;
931 OptVisitor(AnalysisResultPtr ar, unsigned nscope) :
932 m_ar(ar), m_nscope(nscope), m_dispatcher(0) {
934 /* implicit */ OptVisitor(const Visitor &po)
935 : m_ar(po.m_ar)
936 , m_nscope(po.m_nscope)
937 , m_dispatcher(po.m_dispatcher)
939 const_cast<Visitor&>(po).m_dispatcher = 0;
941 ~OptVisitor() {
942 delete m_dispatcher;
945 void start() {
946 m_dispatcher->start();
949 void wait() {
950 m_dispatcher->waitEmpty(false);
953 void stop() {
954 m_dispatcher->waitEmpty();
957 int getQueuedJobs() {
958 return m_dispatcher->getQueuedJobs();
961 int getActiveWorker() {
962 return m_dispatcher->getActiveWorker();
965 AnalysisResultPtr m_ar;
966 unsigned m_nscope;
967 JobQueueDispatcher<BlockScope *, OptWorker<When> > *m_dispatcher;
970 template <typename When>
971 class OptWorker : public JobQueueWorker<BlockScope *, true, true> {
972 public:
973 OptWorker() {}
975 virtual void onThreadEnter() {
978 virtual void onThreadExit() {
981 virtual void doJob(BlockScope *scope) {
982 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
983 ++AnalysisResult::s_NumDoJobCalls;
984 ConcurrentBlockScopeRawPtrIntHashMap::accessor acc;
985 AnalysisResult::s_DoJobUniqueScopes.insert(acc,
986 BlockScopeRawPtr(scope));
987 acc->second += 1;
988 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
989 try {
990 DepthFirstVisitor<When, OptVisitor > *visitor =
991 (DepthFirstVisitor<When, OptVisitor >*)m_opaque;
993 Lock ldep(BlockScope::s_depsMutex);
994 Lock lstate(BlockScope::s_jobStateMutex);
995 always_assert(scope->getMark() == BlockScope::MarkReady);
996 if (scope->getNumDepsToWaitFor()) {
997 scope->setMark(BlockScope::MarkWaiting);
998 return;
1000 scope->setMark(BlockScope::MarkProcessing);
1003 scope->setForceRerun(false);
1004 scope->setNeedsReschedule(false);
1006 // creates on demand
1007 AnalysisResult::s_changedScopesMapThreadLocal->clear();
1008 int useKinds = visitor->visitScope(BlockScopeRawPtr(scope));
1009 assert(useKinds >= 0);
1012 Lock l2(BlockScope::s_depsMutex);
1013 Lock l1(BlockScope::s_jobStateMutex);
1015 assert(scope->getMark() == BlockScope::MarkProcessing);
1016 assert(scope->getNumDepsToWaitFor() == 0);
1017 scope->assertNumDepsSanity();
1019 // re-enqueue changed scopes, regardless of rescheduling exception.
1020 // this is because we might have made changes to other scopes which we
1021 // do not undo, so we need to announce their updates
1022 BlockScopeRawPtrFlagsHashMap::const_iterator localIt =
1023 AnalysisResult::s_changedScopesMapThreadLocal->begin();
1024 BlockScopeRawPtrFlagsHashMap::const_iterator localEnd =
1025 AnalysisResult::s_changedScopesMapThreadLocal->end();
1026 for (; localIt != localEnd; ++localIt) {
1027 const BlockScopeRawPtrFlagsVec &ordered =
1028 localIt->first->getOrderedUsers();
1029 for (BlockScopeRawPtrFlagsVec::const_iterator userIt =
1030 ordered.begin(), userEnd = ordered.end();
1031 userIt != userEnd; ++userIt) {
1032 BlockScopeRawPtrFlagsVec::value_type pf = *userIt;
1033 if ((pf->second & GetPhaseInterestMask<When>()) &&
1034 (pf->second & localIt->second)) {
1035 int m = pf->first->getMark();
1036 switch (m) {
1037 case BlockScope::MarkWaiting:
1038 case BlockScope::MarkReady:
1039 ; // no-op
1040 break;
1041 case BlockScope::MarkProcessing:
1042 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1043 ++AnalysisResult::s_NumForceRerunGlobal;
1044 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1045 pf->first->setForceRerun(true);
1046 break;
1047 case BlockScope::MarkProcessed:
1048 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1049 ++AnalysisResult::s_NumReactivateGlobal;
1050 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1051 if (visitor->activateScope(pf->first)) {
1052 visitor->enqueue(pf->first);
1054 break;
1055 default: assert(false);
1060 AnalysisResult::s_changedScopesMapThreadLocal.destroy();
1062 if (scope->needsReschedule()) {
1063 // This signals an error in visitScope() which the scope can possibly
1064 // recover from if run again. an example is a lock contention error
1065 // (where the scope had to bail out). thus, we simply want to
1066 // re-enqueue it (w/o activating dependents, since this scope hasn't
1067 // actually finished running)
1068 scope->setRescheduleFlags(
1069 scope->rescheduleFlags() | useKinds);
1070 if (visitor->activateScope(BlockScopeRawPtr(scope))) {
1071 visitor->enqueue(BlockScopeRawPtr(scope));
1073 } else {
1074 useKinds |= scope->rescheduleFlags();
1075 scope->setRescheduleFlags(0);
1077 const BlockScopeRawPtrFlagsVec &ordered = scope->getOrderedUsers();
1078 for (BlockScopeRawPtrFlagsVec::const_iterator it = ordered.begin(),
1079 end = ordered.end(); it != end; ++it) {
1080 BlockScopeRawPtrFlagsVec::value_type pf = *it;
1081 if (pf->second & GetPhaseInterestMask<When>()) {
1082 int m = pf->first->getMark();
1083 if (pf->second & useKinds && m == BlockScope::MarkProcessed) {
1084 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1085 ++AnalysisResult::s_NumReactivateUseKinds;
1086 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1087 bool ready = visitor->activateScope(pf->first);
1088 always_assert(!ready);
1089 m = BlockScope::MarkWaiting;
1092 if (m == BlockScope::MarkWaiting || m == BlockScope::MarkReady) {
1093 int nd = pf->first->getNumDepsToWaitFor();
1094 always_assert(nd >= 1);
1095 if (!pf->first->decNumDepsToWaitFor() &&
1096 m == BlockScope::MarkWaiting) {
1097 pf->first->setMark(BlockScope::MarkReady);
1098 visitor->enqueue(pf->first);
1100 } else if (pf->second & useKinds &&
1101 m == BlockScope::MarkProcessing) {
1102 // This is conservative: If we have a user who is currently
1103 // processing (yes, this can potentially happen if we add a
1104 // user *after* the initial dep graph has been formed), then we
1105 // have no guarantee that the scope read this scope's updates
1106 // in its entirety. Thus, we must force it to run again in
1107 // order to be able to observe all the updates.
1108 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1109 ++AnalysisResult::s_NumForceRerunUseKinds;
1110 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1111 always_assert(pf->first->getNumDepsToWaitFor() == 0);
1112 pf->first->setForceRerun(true);
1116 scope->setMark(BlockScope::MarkProcessed);
1117 if (scope->forceRerun()) {
1118 if (visitor->activateScope(BlockScopeRawPtr(scope))) {
1119 visitor->enqueue(BlockScopeRawPtr(scope));
1121 } else {
1122 const BlockScopeRawPtrFlagsPtrVec &deps = scope->getDeps();
1123 for (BlockScopeRawPtrFlagsPtrVec::const_iterator it = deps.begin(),
1124 end = deps.end(); it != end; ++it) {
1125 const BlockScopeRawPtrFlagsPtrPair &p(*it);
1126 if (*p.second & GetPhaseInterestMask<When>()) {
1127 if (p.first->getMark() == BlockScope::MarkProcessing) {
1128 bool ready = visitor->activateScope(BlockScopeRawPtr(scope));
1129 always_assert(!ready);
1130 break;
1137 } catch (Exception &e) {
1138 Logger::Error("%s", e.getMessage().c_str());
1143 // Pre, InferTypes, and Post defined in depth_first_visitor.h
1145 typedef OptVisitor<Pre> PreOptVisitor;
1146 typedef OptWorker<Pre> PreOptWorker;
1147 typedef OptVisitor<InferTypes> InferTypesVisitor;
1148 typedef OptWorker<InferTypes> InferTypesWorker;
1149 typedef OptVisitor<Post> PostOptVisitor;
1150 typedef OptWorker<Post> PostOptWorker;
1152 template<>
1153 void OptWorker<Pre>::onThreadEnter() {
1154 hphp_session_init();
1157 template<>
1158 void OptWorker<Pre>::onThreadExit() {
1159 hphp_session_exit();
1163 * Unfortunately we cannot template specialize on something like this w/o
1164 * complaints about incomplete class declarations:
1166 * template <class When>
1167 * void DepthFirstVisitor<When, OptVisitor>::setup() { ... }
1169 * And as such, this evil exists
1172 #define IMPLEMENT_OPT_VISITOR_SETUP(worker) \
1173 do { \
1174 unsigned int threadCount = Option::ParserThreadCount; \
1175 if (threadCount > this->m_data.m_nscope) { \
1176 threadCount = this->m_data.m_nscope; \
1178 if (threadCount <= 0) threadCount = 1; \
1179 this->m_data.m_dispatcher = \
1180 new JobQueueDispatcher<BlockScope *, worker >( \
1181 threadCount, true, 0, false, this); \
1182 } while (0)
1184 #define IMPLEMENT_OPT_VISITOR_ENQUEUE(scope) \
1185 do { \
1186 assert((scope)->getMark() == BlockScope::MarkReady); \
1187 this->m_data.m_dispatcher->enqueue((scope).get()); \
1188 } while (0)
1190 template<>
1191 void DepthFirstVisitor<Pre, OptVisitor>::setup() {
1192 IMPLEMENT_OPT_VISITOR_SETUP(PreOptWorker);
1195 template<>
1196 void DepthFirstVisitor<InferTypes, OptVisitor>::setup() {
1197 IMPLEMENT_OPT_VISITOR_SETUP(InferTypesWorker);
1200 template<>
1201 void DepthFirstVisitor<Post, OptVisitor>::setup() {
1202 IMPLEMENT_OPT_VISITOR_SETUP(PostOptWorker);
1205 template<>
1206 void DepthFirstVisitor<Pre, OptVisitor>::enqueue(BlockScopeRawPtr scope) {
1207 IMPLEMENT_OPT_VISITOR_ENQUEUE(scope);
1210 template<>
1211 void DepthFirstVisitor<InferTypes, OptVisitor>::enqueue(
1212 BlockScopeRawPtr scope) {
1213 IMPLEMENT_OPT_VISITOR_ENQUEUE(scope);
1216 template<>
1217 void DepthFirstVisitor<Post, OptVisitor>::enqueue(BlockScopeRawPtr scope) {
1218 IMPLEMENT_OPT_VISITOR_ENQUEUE(scope);
1221 template <typename When>
1222 void
1223 AnalysisResult::preWaitCallback(bool first,
1224 const BlockScopeRawPtrQueue &scopes,
1225 void *opaque) {
1226 // default is no-op
1229 template <typename When>
1230 bool
1231 AnalysisResult::postWaitCallback(bool first,
1232 bool again,
1233 const BlockScopeRawPtrQueue &scopes,
1234 void *opaque) {
1235 // default is no-op
1236 return again;
1239 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1240 std::atomic<int> AnalysisResult::s_NumDoJobCalls(0);
1241 std::atomic<int> AnalysisResult::s_NumForceRerunGlobal(0);
1242 std::atomic<int> AnalysisResult::s_NumReactivateGlobal(0);
1243 std::atomic<int> AnalysisResult::s_NumForceRerunUseKinds(0);
1244 std::atomic<int> AnalysisResult::s_NumReactivateUseKinds(0);
1246 ConcurrentBlockScopeRawPtrIntHashMap
1247 AnalysisResult::s_DoJobUniqueScopes;
1249 static inline int CountScopesWaiting(const BlockScopeRawPtrQueue &scopes) {
1250 int s = 0;
1251 for (BlockScopeRawPtrQueue::const_iterator it = scopes.begin();
1252 it != scopes.end(); ++it) {
1253 int m = (*it)->getMark();
1254 assert(m == BlockScope::MarkWaiting ||
1255 m == BlockScope::MarkProcessed);
1256 if (m == BlockScope::MarkWaiting) s++;
1258 return s;
1261 static inline void DumpScope(BlockScopeRawPtr scope, const char *prefix,
1262 bool newline = true) {
1263 assert(scope->is(BlockScope::FunctionScope) ||
1264 scope->is(BlockScope::ClassScope));
1265 const char *type = scope->is(BlockScope::FunctionScope) ?
1266 "function" : "class";
1267 std::cout << prefix << type << " " << scope->getName() << " @ "
1268 << scope->getContainingFile()->getName();
1269 if (newline) std::cout << std::endl;
1272 static inline void DumpScopeWithDeps(BlockScopeRawPtr scope) {
1273 assert(scope->is(BlockScope::FunctionScope) ||
1274 scope->is(BlockScope::ClassScope));
1275 DumpScope(scope, "");
1276 const BlockScopeRawPtrFlagsVec &ordered = scope->getOrderedUsers();
1277 for (BlockScopeRawPtrFlagsVec::const_iterator it = ordered.begin(),
1278 end = ordered.end(); it != end; ++it) {
1279 BlockScopeRawPtrFlagsVec::value_type pf = *it;
1280 string prefix = " ";
1281 prefix += "(";
1282 prefix += boost::lexical_cast<string>(pf->second);
1283 prefix += ") ";
1284 DumpScope(pf->first, prefix.c_str());
1288 typedef std::pair<BlockScopeRawPtr, int> BIPair;
1289 struct BIPairCmp {
1290 inline bool operator()(const BIPair &lhs, const BIPair &rhs) const {
1291 return lhs.second > rhs.second;
1294 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1296 template <typename When>
1297 void
1298 AnalysisResult::processScopesParallel(const char *id,
1299 void *opaque /* = NULL */) {
1300 BlockScopeRawPtrQueue scopes;
1301 getScopesSet(scopes);
1303 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1304 std::cout << "processScopesParallel(" << id << "): "
1305 << scopes.size() << " scopes" << std::endl;
1306 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1308 DepthFirstVisitor<When, OptVisitor> dfv(
1309 OptVisitor<When>(shared_from_this(), scopes.size()));
1311 bool first = true;
1312 bool again;
1313 dfv.data().start();
1314 do {
1316 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1317 std::cout << "-----------------------------------" << std::endl;
1318 AnalysisResult::s_NumDoJobCalls = 0;
1319 AnalysisResult::s_NumForceRerunGlobal = 0;
1320 AnalysisResult::s_NumReactivateGlobal = 0;
1321 AnalysisResult::s_NumForceRerunUseKinds = 0;
1322 AnalysisResult::s_NumReactivateUseKinds = 0;
1324 AnalysisResult::s_DoJobUniqueScopes.clear();
1325 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1327 #ifdef HPHP_INSTRUMENT_TYPE_INF
1328 assert(RescheduleException::s_NumReschedules == 0);
1329 assert(RescheduleException::s_NumForceRerunSelfCaller == 0);
1330 assert(RescheduleException::s_NumRetTypesChanged == 0);
1331 assert(BaseTryLock::s_LockProfileMap.empty());
1332 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1334 BlockScopeRawPtrQueue enqueued;
1335 again = dfv.visitParallel(scopes, first, enqueued);
1336 preWaitCallback<When>(first, scopes, opaque);
1338 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1340 std::cout << "Enqueued " << enqueued.size() <<
1341 " scopes in visitParallel()" << std::endl;
1342 if (enqueued.size() < 100) {
1343 for (BlockScopeRawPtrQueue::const_iterator it = enqueued.begin();
1344 it != enqueued.end(); ++it) {
1345 DumpScopeWithDeps(*it);
1348 Timer timer(Timer::WallTime, "dfv.wait()");
1349 dfv.data().wait();
1351 #else
1352 dfv.data().wait();
1353 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1355 assert(!dfv.data().getQueuedJobs());
1356 assert(!dfv.data().getActiveWorker());
1358 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1359 std::cout << "Number of doJob() calls: "
1360 << AnalysisResult::s_NumDoJobCalls << std::endl;
1361 std::cout << "Number of scopes which got doJob() called: "
1362 << AnalysisResult::s_DoJobUniqueScopes.size() << std::endl;
1363 std::vector<BIPair> v(
1364 AnalysisResult::s_DoJobUniqueScopes.begin(),
1365 AnalysisResult::s_DoJobUniqueScopes.end());
1366 if (!v.empty()) {
1367 sort(v.begin(), v.end(), BIPairCmp());
1368 std::vector<BIPair>::const_iterator end =
1369 v.size() > 20 ? v.begin() + 20 : v.end();
1370 for (std::vector<BIPair>::const_iterator it = v.begin();
1371 it != end; ++it) {
1372 string prefix;
1373 prefix += boost::lexical_cast<string>((*it).second);
1374 prefix += " times: ";
1375 DumpScope((*it).first, prefix.c_str());
1377 std::cout << "Number of global force reruns: "
1378 << AnalysisResult::s_NumForceRerunGlobal << std::endl;
1379 std::cout << "Number of global reactivates: "
1380 << AnalysisResult::s_NumReactivateGlobal << std::endl;
1381 std::cout << "Number of use kind force reruns: "
1382 << AnalysisResult::s_NumForceRerunUseKinds << std::endl;
1383 std::cout << "Number of use kind reactivates: "
1384 << AnalysisResult::s_NumReactivateUseKinds << std::endl;
1386 int numWaiting = CountScopesWaiting(scopes);
1387 std::cout << "Number of waiting scopes: " << numWaiting << std::endl;
1388 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1390 again = postWaitCallback<When>(first, again, scopes, opaque);
1391 first = false;
1392 } while (again);
1393 dfv.data().stop();
1395 for (BlockScopeRawPtrQueue::iterator
1396 it = scopes.begin(), end = scopes.end();
1397 it != end; ++it) {
1398 assert((*it)->getMark() == BlockScope::MarkProcessed);
1399 assert((*it)->getNumDepsToWaitFor() == 0);
1400 assert(!(*it)->needsReschedule());
1401 assert((*it)->rescheduleFlags() == 0);
1405 ///////////////////////////////////////////////////////////////////////////////
1406 // pre-opt
1408 template<>
1409 int DepthFirstVisitor<Pre, OptVisitor>::visitScope(BlockScopeRawPtr scope) {
1410 int updates, all_updates = 0;
1411 StatementPtr stmt = scope->getStmt();
1412 if (MethodStatementPtr m =
1413 dynamic_pointer_cast<MethodStatement>(stmt)) {
1414 WriteLock lock(m->getFunctionScope()->getInlineMutex());
1415 do {
1416 scope->clearUpdated();
1417 if (Option::LocalCopyProp || Option::EliminateDeadCode) {
1418 AliasManager am(-1);
1419 if (am.optimize(this->m_data.m_ar, m)) {
1420 scope->addUpdates(BlockScope::UseKindCaller);
1422 } else {
1423 StatementPtr rep = this->visitStmtRecur(stmt);
1424 always_assert(!rep);
1426 updates = scope->getUpdated();
1427 all_updates |= updates;
1428 } while (updates);
1429 if (all_updates & BlockScope::UseKindCaller &&
1430 !m->getFunctionScope()->getInlineAsExpr()) {
1431 all_updates &= ~BlockScope::UseKindCaller;
1433 return all_updates;
1436 do {
1437 scope->clearUpdated();
1438 StatementPtr rep = this->visitStmtRecur(stmt);
1439 always_assert(!rep);
1440 updates = scope->getUpdated();
1441 all_updates |= updates;
1442 } while (updates);
1444 return all_updates;
1447 template<>
1448 ExpressionPtr DepthFirstVisitor<Pre, OptVisitor>::visit(ExpressionPtr e) {
1449 return e->preOptimize(this->m_data.m_ar);
1452 template<>
1453 StatementPtr DepthFirstVisitor<Pre, OptVisitor>::visit(StatementPtr stmt) {
1454 return stmt->preOptimize(this->m_data.m_ar);
1457 void AnalysisResult::preOptimize() {
1458 setPhase(FirstPreOptimize);
1459 processScopesParallel<Pre>("PreOptimize");
1462 ///////////////////////////////////////////////////////////////////////////////
1463 // infer types
1465 template<>
1467 DepthFirstVisitor<InferTypes, OptVisitor>::visitScope(BlockScopeRawPtr scope) {
1468 // acquire a lock on the scope
1469 SimpleLock lock(scope->getInferTypesMutex());
1471 // set the thread local to this scope-
1472 // use an object to do this so if an exception is thrown we can take
1473 // advantage of stack-unwinding
1475 // NOTE: this *must* happen *after* the lock has been acquired, since there
1476 // is code which depends on this ordering
1477 SetCurrentScope sc(scope);
1479 StatementPtr stmt = scope->getStmt();
1480 MethodStatementPtr m =
1481 dynamic_pointer_cast<MethodStatement>(stmt);
1482 bool pushPrev = m && !scope->isFirstPass() &&
1483 !scope->getContainingFunction()->inPseudoMain();
1484 if (m) {
1485 if (pushPrev) scope->getVariables()->beginLocal();
1486 scope->getContainingFunction()->pushReturnType();
1489 int ret = 0;
1490 try {
1491 bool done;
1492 do {
1493 scope->clearUpdated();
1494 if (m) {
1495 scope->getContainingFunction()->clearRetExprs();
1496 m->inferFunctionTypes(this->m_data.m_ar);
1497 } else {
1498 for (int i = 0, n = stmt->getKidCount(); i < n; i++) {
1499 StatementPtr kid(
1500 dynamic_pointer_cast<Statement>(stmt->getNthKid(i)));
1501 if (kid) {
1502 kid->inferTypes(this->m_data.m_ar);
1507 done = !scope->getUpdated();
1508 ret |= scope->getUpdated();
1509 scope->incPass();
1510 } while (!done);
1512 if (m) {
1513 bool changed = scope->getContainingFunction()->popReturnType();
1514 if (changed && scope->selfUser() & BlockScope::UseKindCallerReturn) {
1515 // for a recursive caller, we must let the scope run again, because
1516 // there are potentially AST nodes which are interested in the updated
1517 // return type
1518 #ifdef HPHP_INSTRUMENT_TYPE_INF
1519 ++RescheduleException::s_NumForceRerunSelfCaller;
1520 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1521 scope->setForceRerun(true);
1523 if (pushPrev) {
1524 scope->getVariables()->endLocal();
1525 ret = 0; // since we really care about the updated flags *after*
1526 // endLocal()
1528 scope->getContainingFunction()->fixRetExprs();
1529 ret |= scope->getUpdated();
1530 scope->clearUpdated();
1532 } catch (RescheduleException &e) {
1533 // potential deadlock detected- reschedule
1534 // this scope to run at a later time
1535 #ifdef HPHP_INSTRUMENT_TYPE_INF
1536 ++RescheduleException::s_NumReschedules;
1537 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1538 ret |= scope->getUpdated();
1539 if (m) {
1540 scope->getContainingFunction()->resetReturnType();
1541 if (pushPrev) {
1542 scope->getVariables()->resetLocal();
1543 ret = 0; // since we really care about the updated flags *after*
1544 // resetLocal()
1546 scope->getContainingFunction()->fixRetExprs();
1547 ret |= scope->getUpdated();
1548 scope->clearUpdated();
1550 scope->setNeedsReschedule(true);
1553 // inc regardless of reschedule exception or not, since these are the
1554 // semantics of run id
1555 scope->incRunId();
1557 return ret;
1560 template<>
1561 bool AnalysisResult::postWaitCallback<InferTypes>(
1562 bool first, bool again, const BlockScopeRawPtrQueue &scopes, void *opaque) {
1564 #ifdef HPHP_INSTRUMENT_TYPE_INF
1565 std::cout << "Number of rescheduled: " <<
1566 RescheduleException::s_NumReschedules << std::endl;
1567 RescheduleException::s_NumReschedules = 0;
1569 std::cout << "Number of force rerun self callers: " <<
1570 RescheduleException::s_NumForceRerunSelfCaller << std::endl;
1571 RescheduleException::s_NumForceRerunSelfCaller = 0;
1573 std::cout << "Number of return types changed: " <<
1574 RescheduleException::s_NumRetTypesChanged << std::endl;
1575 RescheduleException::s_NumRetTypesChanged = 0;
1577 std::cout << "Lock contention: " << std::endl;
1578 for (LProfileMap::const_iterator it = BaseTryLock::s_LockProfileMap.begin();
1579 it != BaseTryLock::s_LockProfileMap.end(); ++it) {
1580 const LEntry &entry = it->first;
1581 int count = it->second;
1582 std::cout << "(" << entry.first << "@" << entry.second << "): " <<
1583 count << std::endl;
1586 BaseTryLock::s_LockProfileMap.clear();
1587 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1589 return again;
1592 #ifdef HPHP_INSTRUMENT_TYPE_INF
1593 std::atomic<int> RescheduleException::s_NumReschedules(0);
1594 std::atomic<int> RescheduleException::s_NumForceRerunSelfCaller(0);
1595 std::atomic<int> RescheduleException::s_NumRetTypesChanged(0);
1596 LProfileMap BaseTryLock::s_LockProfileMap;
1597 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1599 void AnalysisResult::inferTypes() {
1600 setPhase(FirstInference);
1601 BlockScopeRawPtrQueue scopes;
1602 getScopesSet(scopes);
1604 for (BlockScopeRawPtrQueue::iterator
1605 it = scopes.begin(), end = scopes.end();
1606 it != end; ++it) {
1607 (*it)->setInTypeInference(true);
1608 (*it)->clearUpdated();
1609 assert((*it)->getNumDepsToWaitFor() == 0);
1612 #ifdef HPHP_INSTRUMENT_TYPE_INF
1613 assert(RescheduleException::s_NumReschedules == 0);
1614 assert(RescheduleException::s_NumForceRerunSelfCaller == 0);
1615 assert(BaseTryLock::s_LockProfileMap.empty());
1616 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1618 processScopesParallel<InferTypes>("InferTypes");
1620 for (BlockScopeRawPtrQueue::iterator
1621 it = scopes.begin(), end = scopes.end();
1622 it != end; ++it) {
1623 (*it)->setInTypeInference(false);
1624 (*it)->clearUpdated();
1625 assert((*it)->getMark() == BlockScope::MarkProcessed);
1626 assert((*it)->getNumDepsToWaitFor() == 0);
1630 ///////////////////////////////////////////////////////////////////////////////
1631 // post-opt
1633 template<>
1634 int DepthFirstVisitor<Post, OptVisitor>::visit(BlockScopeRawPtr scope) {
1635 scope->clearUpdated();
1636 StatementPtr stmt = scope->getStmt();
1637 bool done = false;
1638 if (MethodStatementPtr m =
1639 dynamic_pointer_cast<MethodStatement>(stmt)) {
1641 AliasManager am(1);
1642 if (am.optimize(this->m_data.m_ar, m)) {
1643 scope->addUpdates(BlockScope::UseKindCaller);
1645 if (Option::LocalCopyProp || Option::EliminateDeadCode) {
1646 done = true;
1650 if (!done) {
1651 StatementPtr rep = this->visitStmtRecur(stmt);
1652 always_assert(!rep);
1655 return scope->getUpdated();
1658 template<>
1659 ExpressionPtr DepthFirstVisitor<Post, OptVisitor>::visit(ExpressionPtr e) {
1660 return e->postOptimize(this->m_data.m_ar);
1663 template<>
1664 StatementPtr DepthFirstVisitor<Post, OptVisitor>::visit(StatementPtr stmt) {
1665 return stmt->postOptimize(this->m_data.m_ar);
1668 class FinalWorker : public JobQueueWorker<MethodStatementPtr> {
1669 public:
1670 virtual void doJob(MethodStatementPtr m) {
1671 try {
1672 AliasManager am(1);
1673 am.finalSetup(((AnalysisResult*)m_opaque)->shared_from_this(), m);
1674 } catch (Exception &e) {
1675 Logger::Error("%s", e.getMessage().c_str());
1680 template<>
1681 void AnalysisResult::preWaitCallback<Post>(
1682 bool first, const BlockScopeRawPtrQueue &scopes, void *opaque) {
1683 assert(!Option::ControlFlow || opaque != nullptr);
1684 if (first && Option::ControlFlow) {
1685 JobQueueDispatcher<FinalWorker::JobType, FinalWorker> *dispatcher
1686 = (JobQueueDispatcher<FinalWorker::JobType, FinalWorker> *) opaque;
1687 for (BlockScopeRawPtrQueue::const_iterator it = scopes.begin(),
1688 end = scopes.end(); it != end; ++it) {
1689 BlockScopeRawPtr scope = *it;
1690 if (MethodStatementPtr m =
1691 dynamic_pointer_cast<MethodStatement>(scope->getStmt())) {
1692 dispatcher->enqueue(m);
1698 void AnalysisResult::postOptimize() {
1699 setPhase(AnalysisResult::PostOptimize);
1700 if (Option::ControlFlow) {
1701 BlockScopeRawPtrQueue scopes;
1702 getScopesSet(scopes);
1704 unsigned int threadCount = Option::ParserThreadCount;
1705 if (threadCount > scopes.size()) {
1706 threadCount = scopes.size();
1708 if (threadCount <= 0) threadCount = 1;
1710 JobQueueDispatcher<FinalWorker::JobType, FinalWorker> dispatcher(
1711 threadCount, true, 0, false, this);
1713 processScopesParallel<Post>("PostOptimize", &dispatcher);
1715 dispatcher.start();
1716 dispatcher.stop();
1717 } else {
1718 processScopesParallel<Post>("PostOptimize");
1722 ///////////////////////////////////////////////////////////////////////////////
1723 } // namespace HPHP
1725 ///////////////////////////////////////////////////////////////////////////////
1726 // code generation functions
1728 string AnalysisResult::prepareFile(const char *root, const string &fileName,
1729 bool chop, bool stripPath /* = true */) {
1730 string fullPath = root;
1731 if (!fullPath.empty() && fullPath[fullPath.size() - 1] != '/') {
1732 fullPath += "/";
1735 string file = fileName;
1736 if (stripPath) {
1737 size_t npos = file.rfind('/');
1738 if (npos != string::npos) {
1739 file = file.substr(npos + 1);
1743 if (chop && file.size() > 4 && file.substr(file.length() - 4) == ".php") {
1744 fullPath += file.substr(0, file.length() - 4);
1745 } else {
1746 fullPath += file;
1748 for (int pos = strlen(root); pos < (int)fullPath.size(); pos++) {
1749 if (fullPath[pos] == '/') {
1750 mkdir(fullPath.substr(0, pos).c_str(), 0777);
1753 return fullPath;
1756 void
1757 AnalysisResult::forceClassVariants(
1758 ClassScopePtr curScope,
1759 bool doStatic,
1760 bool acquireLocks /* = false */) {
1761 if (curScope) {
1762 COND_TRY_LOCK(curScope, acquireLocks);
1763 curScope->getVariables()->forceVariants(
1764 shared_from_this(), VariableTable::GetVarClassMask(true, doStatic),
1765 false);
1768 ConditionalLock lock(getMutex(), acquireLocks);
1769 if (m_classForcedVariants[doStatic]) {
1770 return;
1773 AnalysisResultPtr ar = shared_from_this();
1774 for (StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.begin();
1775 iter != m_classDecs.end(); ++iter) {
1776 BOOST_FOREACH(ClassScopePtr cls, iter->second) {
1777 COND_TRY_LOCK(cls, acquireLocks);
1778 cls->getVariables()->forceVariants(
1779 ar, VariableTable::GetVarClassMask(false, doStatic), false);
1783 m_classForcedVariants[doStatic] = true;
1786 void AnalysisResult::forceClassVariants(
1787 const std::string &name,
1788 ClassScopePtr curScope,
1789 bool doStatic,
1790 bool acquireLocks /* = false */) {
1791 if (curScope) {
1792 COND_TRY_LOCK(curScope, acquireLocks);
1793 curScope->getVariables()->forceVariant(
1794 shared_from_this(), name, VariableTable::GetVarClassMask(true, doStatic));
1797 ConditionalLock lock(getMutex(), acquireLocks);
1798 if (m_classForcedVariants[doStatic]) {
1799 return;
1802 AnalysisResultPtr ar = shared_from_this();
1803 for (StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.begin();
1804 iter != m_classDecs.end(); ++iter) {
1805 BOOST_FOREACH(ClassScopePtr cls, iter->second) {
1806 COND_TRY_LOCK(cls, acquireLocks);
1807 cls->getVariables()->forceVariant(
1808 ar, name, VariableTable::GetVarClassMask(false, doStatic));
1813 bool AnalysisResult::outputAllPHP(CodeGenerator::Output output) {
1814 AnalysisResultPtr ar = shared_from_this();
1815 switch (output) {
1816 case CodeGenerator::PickledPHP:
1817 for (StringToFileScopePtrMap::const_iterator iter = m_files.begin();
1818 iter != m_files.end(); ++iter) {
1819 string fullPath = prepareFile(m_outputPath.c_str(), iter->first, false);
1820 ofstream f(fullPath.c_str());
1821 if (f) {
1822 CodeGenerator cg(&f, output);
1823 cg_printf("<?php\n");
1824 Logger::Info("Generating %s...", fullPath.c_str());
1825 iter->second->getStmt()->outputPHP(cg, ar);
1826 f.close();
1827 } else {
1828 Logger::Error("Unable to open %s for write", fullPath.c_str());
1831 return true; // we are done
1832 default:
1833 assert(false);
1836 return true;