2 +----------------------------------------------------------------------+
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"
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"
62 using std::ostringstream
;
67 ///////////////////////////////////////////////////////////////////////////////
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 ///////////////////////////////////////////////////////////////////////////////
114 void AnalysisResult::addFileScope(FileScopePtr fileScope
) {
116 FileScopePtr
&res
= m_files
[fileScope
->getName()];
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;
132 void AnalysisResult::parseOnDemand(const std::string
&name
) const {
134 const std::string
&root
= m_package
->getRoot();
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
)) {
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 {
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()) {
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()) {
181 StringToFunctionScopePtrMap::const_iterator iter
=
182 m_functionDecs
.find(funcName
);
183 if (iter
!= m_functionDecs
.end()) {
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
,
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();
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
235 iter
->second
.clear();
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
;
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()) {
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
,
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()) {
309 if (scope
->derivesFrom(shared_from_this(), lowerName
,
319 int AnalysisResult::getFunctionCount() const {
321 for (StringToFileScopePtrMap::const_iterator iter
= m_files
.begin();
322 iter
!= m_files
.end(); ++iter
) {
323 total
+= iter
->second
->getFunctionCount();
328 int AnalysisResult::getClassCount() const {
330 for (StringToFileScopePtrMap::const_iterator iter
= m_files
.begin();
331 iter
!= m_files
.end(); ++iter
) {
332 total
+= iter
->second
->getClassCount();
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
);
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
);
378 (m_classForcedVariants
[0] ? VariableTable::NonPrivateNonStaticVars
: 0) |
379 (m_classForcedVariants
[1] ? VariableTable::NonPrivateStaticVars
: 0);
382 AnalysisResultConstPtr ar
= shared_from_this();
383 classScope
->getVariables()->forceVariants(ar
, mask
);
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
);
398 m_constDecs
[name
] = fs
;
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 ///////////////////////////////////////////////////////////////////////////////
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())
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
);
505 bool AnalysisResult::addFunctionDependency(FileScopePtr usingFile
,
506 const std::string
&functionName
) {
507 if (BuiltinSymbols::s_functions
.find(functionName
) !=
508 BuiltinSymbols::s_functions
.end())
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
);
523 bool AnalysisResult::addIncludeDependency(FileScopePtr usingFile
,
524 const std::string
&includeFilename
) {
525 assert(!includeFilename
.empty());
526 FileScopePtr fileScope
= findFileScope(includeFilename
);
528 link(usingFile
, fileScope
);
535 bool AnalysisResult::addConstantDependency(FileScopePtr usingFile
,
536 const std::string
&constantName
) {
537 if (m_constants
->isPresent(constantName
))
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
);
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;
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 ///////////////////////////////////////////////////////////////////////////////
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();
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(
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
];
613 FunctionScopePtrVec
&funcVec
= m_functionReDecs
[iter
->first
];
614 int sz
= funcVec
.size();
616 funcDec
->setRedeclaring(sz
++);
617 funcVec
.push_back(funcDec
);
619 func
->setRedeclaring(sz
++);
620 funcVec
.push_back(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();
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
);
673 Logger::Verbose("Analyzing Includes");
674 sort(m_fileScopes
.begin(), m_fileScopes
.end(), by_filename
); // fixed order
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()) {
694 checkClassDerivations();
695 resolveNSFallbackFuncs();
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
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
);
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
);
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
);
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) {
831 if (!funcs
[0]->isPrivate()) {
833 for (unsigned int i
= 1; i
< funcs
.size(); i
++) {
834 if (funcs
[i
]->isPrivate() || !funcs
[0]->matchParams(funcs
[i
])) {
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
) {
869 void AnalysisResult::dump() {
870 visitFiles(dumpVisitor
, 0);
874 void AnalysisResult::docJson(const string
&filename
) {
875 ofstream
f(filename
.c_str());
877 Logger::Error("Could not open file for writing doc JSON: %s",
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
);
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
922 ///////////////////////////////////////////////////////////////////////////////
924 template <typename When
>
927 template <typename When
>
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
)
936 , m_nscope(po
.m_nscope
)
937 , m_dispatcher(po
.m_dispatcher
)
939 const_cast<Visitor
&>(po
).m_dispatcher
= 0;
946 m_dispatcher
->start();
950 m_dispatcher
->waitEmpty(false);
954 m_dispatcher
->waitEmpty();
957 int getQueuedJobs() {
958 return m_dispatcher
->getQueuedJobs();
961 int getActiveWorker() {
962 return m_dispatcher
->getActiveWorker();
965 AnalysisResultPtr m_ar
;
967 JobQueueDispatcher
<BlockScope
*, OptWorker
<When
> > *m_dispatcher
;
970 template <typename When
>
971 class OptWorker
: public JobQueueWorker
<BlockScope
*, true, true> {
975 virtual void onThreadEnter() {
978 virtual void onThreadExit() {
981 virtual void doJob(BlockScope
*scope
) {
982 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
983 atomic_inc(AnalysisResult::s_NumDoJobCalls
);
984 ConcurrentBlockScopeRawPtrIntHashMap::accessor acc
;
985 AnalysisResult::s_DoJobUniqueScopes
.insert(acc
,
986 BlockScopeRawPtr(scope
));
988 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
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
);
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();
1037 case BlockScope::MarkWaiting
:
1038 case BlockScope::MarkReady
:
1041 case BlockScope::MarkProcessing
:
1042 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1043 atomic_inc(AnalysisResult::s_NumForceRerunGlobal
);
1044 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1045 pf
->first
->setForceRerun(true);
1047 case BlockScope::MarkProcessed
:
1048 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1049 atomic_inc(AnalysisResult::s_NumReactivateGlobal
);
1050 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
1051 if (visitor
->activateScope(pf
->first
)) {
1052 visitor
->enqueue(pf
->first
);
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
));
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 atomic_inc(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 atomic_inc(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
));
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
);
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
;
1153 void OptWorker
<Pre
>::onThreadEnter() {
1154 hphp_session_init();
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) \
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); \
1184 #define IMPLEMENT_OPT_VISITOR_ENQUEUE(scope) \
1186 assert((scope)->getMark() == BlockScope::MarkReady); \
1187 this->m_data.m_dispatcher->enqueue((scope).get()); \
1191 void DepthFirstVisitor
<Pre
, OptVisitor
>::setup() {
1192 IMPLEMENT_OPT_VISITOR_SETUP(PreOptWorker
);
1196 void DepthFirstVisitor
<InferTypes
, OptVisitor
>::setup() {
1197 IMPLEMENT_OPT_VISITOR_SETUP(InferTypesWorker
);
1201 void DepthFirstVisitor
<Post
, OptVisitor
>::setup() {
1202 IMPLEMENT_OPT_VISITOR_SETUP(PostOptWorker
);
1206 void DepthFirstVisitor
<Pre
, OptVisitor
>::enqueue(BlockScopeRawPtr scope
) {
1207 IMPLEMENT_OPT_VISITOR_ENQUEUE(scope
);
1211 void DepthFirstVisitor
<InferTypes
, OptVisitor
>::enqueue(
1212 BlockScopeRawPtr scope
) {
1213 IMPLEMENT_OPT_VISITOR_ENQUEUE(scope
);
1217 void DepthFirstVisitor
<Post
, OptVisitor
>::enqueue(BlockScopeRawPtr scope
) {
1218 IMPLEMENT_OPT_VISITOR_ENQUEUE(scope
);
1221 template <typename When
>
1223 AnalysisResult::preWaitCallback(bool first
,
1224 const BlockScopeRawPtrQueue
&scopes
,
1229 template <typename When
>
1231 AnalysisResult::postWaitCallback(bool first
,
1233 const BlockScopeRawPtrQueue
&scopes
,
1239 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
1240 int AnalysisResult::s_NumDoJobCalls
= 0;
1241 int AnalysisResult::s_NumForceRerunGlobal
= 0;
1242 int AnalysisResult::s_NumReactivateGlobal
= 0;
1243 int AnalysisResult::s_NumForceRerunUseKinds
= 0;
1244 int AnalysisResult::s_NumReactivateUseKinds
= 0;
1246 ConcurrentBlockScopeRawPtrIntHashMap
1247 AnalysisResult::s_DoJobUniqueScopes
;
1249 static inline int CountScopesWaiting(const BlockScopeRawPtrQueue
&scopes
) {
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
++;
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
= " ";
1282 prefix
+= boost::lexical_cast
<string
>(pf
->second
);
1284 DumpScope(pf
->first
, prefix
.c_str());
1288 typedef std::pair
<BlockScopeRawPtr
, int> BIPair
;
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
>
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()));
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()");
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());
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();
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
);
1395 for (BlockScopeRawPtrQueue::iterator
1396 it
= scopes
.begin(), end
= scopes
.end();
1398 assert((*it
)->getMark() == BlockScope::MarkProcessed
);
1399 assert((*it
)->getNumDepsToWaitFor() == 0);
1400 assert(!(*it
)->needsReschedule());
1401 assert((*it
)->rescheduleFlags() == 0);
1405 ///////////////////////////////////////////////////////////////////////////////
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());
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
);
1423 StatementPtr rep
= this->visitStmtRecur(stmt
);
1424 always_assert(!rep
);
1426 updates
= scope
->getUpdated();
1427 all_updates
|= updates
;
1429 if (all_updates
& BlockScope::UseKindCaller
&&
1430 !m
->getFunctionScope()->getInlineAsExpr()) {
1431 all_updates
&= ~BlockScope::UseKindCaller
;
1437 scope
->clearUpdated();
1438 StatementPtr rep
= this->visitStmtRecur(stmt
);
1439 always_assert(!rep
);
1440 updates
= scope
->getUpdated();
1441 all_updates
|= updates
;
1448 ExpressionPtr DepthFirstVisitor
<Pre
, OptVisitor
>::visit(ExpressionPtr e
) {
1449 return e
->preOptimize(this->m_data
.m_ar
);
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 ///////////////////////////////////////////////////////////////////////////////
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();
1485 if (pushPrev
) scope
->getVariables()->beginLocal();
1486 scope
->getContainingFunction()->pushReturnType();
1493 scope
->clearUpdated();
1495 scope
->getContainingFunction()->clearRetExprs();
1496 m
->inferFunctionTypes(this->m_data
.m_ar
);
1498 for (int i
= 0, n
= stmt
->getKidCount(); i
< n
; i
++) {
1500 dynamic_pointer_cast
<Statement
>(stmt
->getNthKid(i
)));
1502 kid
->inferTypes(this->m_data
.m_ar
);
1507 done
= !scope
->getUpdated();
1508 ret
|= scope
->getUpdated();
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
1518 #ifdef HPHP_INSTRUMENT_TYPE_INF
1519 atomic_inc(RescheduleException::s_NumForceRerunSelfCaller
);
1520 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1521 scope
->setForceRerun(true);
1524 scope
->getVariables()->endLocal();
1525 ret
= 0; // since we really care about the updated flags *after*
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 atomic_inc(RescheduleException::s_NumReschedules
);
1537 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1538 ret
|= scope
->getUpdated();
1540 scope
->getContainingFunction()->resetReturnType();
1542 scope
->getVariables()->resetLocal();
1543 ret
= 0; // since we really care about the updated flags *after*
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
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
<< "): " <<
1586 BaseTryLock::s_LockProfileMap
.clear();
1587 #endif /* HPHP_INSTRUMENT_TYPE_INF */
1592 #ifdef HPHP_INSTRUMENT_TYPE_INF
1593 int RescheduleException::s_NumReschedules
= 0;
1594 int RescheduleException::s_NumForceRerunSelfCaller
= 0;
1595 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();
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();
1623 (*it
)->setInTypeInference(false);
1624 (*it
)->clearUpdated();
1625 assert((*it
)->getMark() == BlockScope::MarkProcessed
);
1626 assert((*it
)->getNumDepsToWaitFor() == 0);
1630 ///////////////////////////////////////////////////////////////////////////////
1634 int DepthFirstVisitor
<Post
, OptVisitor
>::visit(BlockScopeRawPtr scope
) {
1635 scope
->clearUpdated();
1636 StatementPtr stmt
= scope
->getStmt();
1638 if (MethodStatementPtr m
=
1639 dynamic_pointer_cast
<MethodStatement
>(stmt
)) {
1642 if (am
.optimize(this->m_data
.m_ar
, m
)) {
1643 scope
->addUpdates(BlockScope::UseKindCaller
);
1645 if (Option::LocalCopyProp
|| Option::EliminateDeadCode
) {
1651 StatementPtr rep
= this->visitStmtRecur(stmt
);
1652 always_assert(!rep
);
1655 return scope
->getUpdated();
1659 ExpressionPtr DepthFirstVisitor
<Post
, OptVisitor
>::visit(ExpressionPtr e
) {
1660 return e
->postOptimize(this->m_data
.m_ar
);
1664 StatementPtr DepthFirstVisitor
<Post
, OptVisitor
>::visit(StatementPtr stmt
) {
1665 return stmt
->postOptimize(this->m_data
.m_ar
);
1668 class FinalWorker
: public JobQueueWorker
<MethodStatementPtr
> {
1670 virtual void doJob(MethodStatementPtr m
) {
1673 am
.finalSetup(((AnalysisResult
*)m_opaque
)->shared_from_this(), m
);
1674 } catch (Exception
&e
) {
1675 Logger::Error("%s", e
.getMessage().c_str());
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
);
1718 processScopesParallel
<Post
>("PostOptimize");
1722 ///////////////////////////////////////////////////////////////////////////////
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] != '/') {
1735 string file
= fileName
;
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);
1748 for (int pos
= strlen(root
); pos
< (int)fullPath
.size(); pos
++) {
1749 if (fullPath
[pos
] == '/') {
1750 mkdir(fullPath
.substr(0, pos
).c_str(), 0777);
1757 AnalysisResult::forceClassVariants(
1758 ClassScopePtr curScope
,
1760 bool acquireLocks
/* = false */) {
1762 COND_TRY_LOCK(curScope
, acquireLocks
);
1763 curScope
->getVariables()->forceVariants(
1764 shared_from_this(), VariableTable::GetVarClassMask(true, doStatic
),
1768 ConditionalLock
lock(getMutex(), acquireLocks
);
1769 if (m_classForcedVariants
[doStatic
]) {
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
,
1790 bool acquireLocks
/* = false */) {
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
]) {
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();
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());
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
);
1828 Logger::Error("Unable to open %s for write", fullPath
.c_str());
1831 return true; // we are done