Remove rust.ord on enums
[hiphop-php.git] / hphp / hhbbc / analyze.h
blob7ff338fa742ea403ac4646ce266047949d10cfc9
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
16 #pragma once
18 #include <vector>
19 #include <utility>
21 #include "hphp/hhbbc/misc.h"
22 #include "hphp/hhbbc/representation.h"
23 #include "hphp/hhbbc/interp-state.h"
24 #include "hphp/hhbbc/interp.h"
25 #include "hphp/hhbbc/index.h"
26 #include "hphp/hhbbc/type-structure.h"
27 #include "hphp/hhbbc/type-system.h"
28 #include "hphp/hhbbc/context.h"
30 namespace HPHP::HHBBC {
32 //////////////////////////////////////////////////////////////////////
35 * The result of a function-at-a-time type analysis, as needed for
36 * updating the index. Does not include per-block state.
38 struct FuncAnalysisResult {
40 * Initializes this structure so rpoBlocks contains the func's
41 * blocks according to rpoSortAddDVs(), each bdata entry has an
42 * rpoId index, and all block states are uninitialized.
44 explicit FuncAnalysisResult(AnalysisContext);
46 FuncAnalysisResult(FuncAnalysisResult&&) = default;
47 FuncAnalysisResult& operator=(FuncAnalysisResult&&) = default;
50 * FuncAnalysis carries the Context it was created for because
51 * generally you'll need it again when you look at the analysis
52 * results.
54 * Note that the Context is adjusted to account for the fact that
55 * Closure __invoke methods run in the context of a class other than
56 * their declaring class. So ctx.func->cls will not be the same as
57 * ctx->cls in this case.
59 Context ctx;
62 * If this function allocates closures, this maps each of those
63 * closure classes to the types of its used variables, in their
64 * declared order.
66 ClosureUseVarMap closureUseTypes;
69 * The inferred function return type. May be TBottom if the
70 * function never returns.
72 Type inferredReturn;
75 * The number of times that inferredReturn was refined inside
76 * analyze_class. We need this to track accurately the total number
77 * of refinements.
79 size_t localReturnRefinements = 0;
82 * If the function returns one of its parameters, the index of that
83 * parameter. MaxLocalId and above indicate that it doesn't return a
84 * parameter.
86 LocalId retParam{MaxLocalId};
89 * Flag to indicate that the function is effectFree, in the sense
90 * that calls to it can be constant folded or dced (note that calls
91 * are never truly effect free, because profilers could be enabled,
92 * or other surprise flags could fire - but we ignore that for this
93 * flag).
95 bool effectFree{false};
98 * Flag to indicate whether the analysis should be repeated after the
99 * function's bytecode was updated. The analysis must have made progress
100 * irreversibly reducing the bytecode.
102 * Used to infer the effectFree flag after reduction of MemoGet to a constant.
104 bool reanalyzeOnUpdate{false};
107 * Flag to indicate that an iterator's base was unchanged on at least one path
108 * to that iterator's release. If this is false, we can skip doing the more
109 * expensive Iter local optimization pass (because it will never succeed).
111 bool hasInvariantIterBase{false};
114 * A set of call contexts that we failed to fold.
116 hphp_fast_set<CallContext, CallContextHasher> unfoldableFuncs;
119 * Bitset representing which parameters may affect the result of the
120 * function, assuming it produces one. Note that the parameter type
121 * verification does not count as a use in this context.
123 std::bitset<64> usedParams;
126 * For an 86cinit, any constants that we inferred a type for. For
127 * 86pinit or 86sinit, any resolved initial values for
128 * properties. These two cases are disjoint, so we save memory by
129 * using a variant type.
131 UniqueEither<ResolvedConstants*, ResolvedPropInits*> resolvedInitializers;
134 * Public static property mutations in this function.
136 PublicSPropMutations publicSPropMutations;
139 * Vector of block updates
141 CompactVector<std::pair<BlockId, CompressedBlockUpdate>> blockUpdates;
144 struct FuncAnalysis : FuncAnalysisResult {
145 struct BlockData {
146 uint32_t rpoId;
147 State stateIn;
148 bool noThrow{true};
151 explicit FuncAnalysis(AnalysisContext);
153 FuncAnalysis(FuncAnalysis&&) = default;
154 FuncAnalysis& operator=(FuncAnalysis&&) = default;
156 // Block ids in a reverse post order, with DV initializers.
157 std::vector<BlockId> rpoBlocks;
159 // Block data is indexed by Block::id.
160 std::vector<BlockData> bdata;
164 * Store the worklist of functions to be analyzed during
165 * class-at-a-time analysis (along with intra-class dependencies).
167 struct ClassAnalysisWorklist {
168 const php::Func* next();
170 void scheduleForProp(SString name);
171 void scheduleForPropMutate(SString name);
172 void scheduleForReturnType(const php::Func& callee);
174 void addPropDep(SString name, const php::Func& f) {
175 propDeps[name].emplace(&f);
177 void addPropMutateDep(SString name, const php::Func& f) {
178 propMutateDeps[name].emplace(&f);
180 void addReturnTypeDep(const php::Func& callee, const php::Func& f) {
181 returnTypeDeps[&callee].emplace(&f);
184 bool empty() const { return worklist.empty(); }
186 // Put a func on the worklist. Return true if the func was
187 // scheduled, false if it was already on the list.
188 bool schedule(const php::Func& f);
190 const hphp_fast_set<const php::Func*>*
191 depsForReturnType(const php::Func& f) const {
192 auto const it = returnTypeDeps.find(&f);
193 if (it == returnTypeDeps.end()) return nullptr;
194 return &it->second;
197 private:
198 hphp_fast_set<const php::Func*> inWorklist;
199 std::deque<const php::Func*> worklist;
201 template <typename T> using Deps =
202 hphp_fast_map<T, hphp_fast_set<const php::Func*>>;
203 Deps<SString> propDeps;
204 Deps<SString> propMutateDeps;
205 Deps<const php::Func*> returnTypeDeps;
208 struct ClassAnalysisWork {
209 ClassAnalysisWorklist worklist;
210 hphp_fast_map<const php::Func*, Index::ReturnType> returnTypes;
211 hphp_fast_map<const php::Func*, hphp_fast_set<SString>> propMutators;
212 bool noPropRefine = false;
216 * The result of a class-at-a-time analysis.
218 struct ClassAnalysis {
219 explicit ClassAnalysis(const Context& ctx) :
220 ctx(ctx) {}
222 ClassAnalysis(ClassAnalysis&&) = default;
223 ClassAnalysis& operator=(ClassAnalysis&&) = default;
225 // The context that describes the class we did this analysis for.
226 Context ctx;
228 // FuncAnalysis results for each of the methods on the class, and
229 // for each closure allocated in the class's context.
230 CompactVector<FuncAnalysisResult> methods;
231 CompactVector<FuncAnalysisResult> closures;
233 // Inferred types for private instance and static properties.
234 PropState privateProperties;
235 PropState privateStatics;
237 ResolvedPropInits resolvedProps;
238 ResolvedClsTypeConsts resolvedTypeConsts;
240 ClassAnalysisWork* work{nullptr};
243 //////////////////////////////////////////////////////////////////////
245 struct UnitAnalysis {
246 explicit UnitAnalysis(const Context& ctx) : ctx{ctx} {}
248 UnitAnalysis(UnitAnalysis&&) = default;
249 UnitAnalysis& operator=(UnitAnalysis&&) = default;
251 Context ctx;
253 ResolvedTypeAliases resolvedTypeAliases;
256 //////////////////////////////////////////////////////////////////////
258 // Local state for resolving class constants and reaching a fixed
259 // point.
260 struct ClsConstantWork {
261 ClsConstantWork(const IIndex&, const php::Class&);
263 ClsConstLookupResult lookup(SString);
265 void update(SString, Type);
266 void add(SString);
267 SString next();
269 void setCurrent(SString n) {
270 assertx(!current);
271 current = n;
273 void clearCurrent(SString n) {
274 assertx(current == n);
275 current = nullptr;
278 void schedule(SString);
280 const php::Class& cls;
281 hphp_fast_map<SString, ClsConstInfo> constants;
283 SString current{nullptr};
284 hphp_fast_map<SString, hphp_fast_set<SString>> deps;
285 hphp_fast_set<SString> inWorklist;
286 std::deque<SString> worklist;
289 //////////////////////////////////////////////////////////////////////
292 * Perform a flow-sensitive type analysis on a function, using the
293 * given Index and Context when we need information about things
294 * outside of this function.
296 * This routine makes no changes to the php::Func.
298 FuncAnalysis analyze_func(const IIndex&, const AnalysisContext&,
299 CollectionOpts opts);
302 * Perform a flow-sensitive type analysis on a function, using the
303 * given Index and Context when we need information about things
304 * outside of this function, and assuming that the arguments to the
305 * function have the supplied types.
307 * This function is used to perform callsite-sensitive type inference.
309 * Currently this is not supported for closure bodies.
311 FuncAnalysis analyze_func_inline(const IIndex&,
312 const AnalysisContext&,
313 const Type& thisType,
314 const CompactVector<Type>& args,
315 ClsConstantWork* clsCnsWork = nullptr,
316 CollectionOpts opts = {});
319 * Perform an analysis for a whole php::Class at a time.
321 * This involves doing a analyze_func call on each of its functions,
322 * and inferring some whole-class information at the same time.
324 ClassAnalysis analyze_class(const IIndex&, const Context&);
327 * Perform an analysis of all the class constants for a php::Class
328 * (both declared and inherited).
330 ClassAnalysis analyze_class_constants(const IIndex&, const Context&);
332 //////////////////////////////////////////////////////////////////////
335 * Perform an analysis for a php::Unit. This does not analyze any of
336 * the classes or funcs contained within the unit, that must be done
337 * explicitly. It analyzes type-aliases defined within the unit.
339 UnitAnalysis analyze_unit(const AnalysisIndex& index, const Context& ctx);
341 //////////////////////////////////////////////////////////////////////
344 * Represents the various interp state at some particular instruction
345 * within a block, with an ability to "rewind" the state to previous
346 * instructions (up until the entry of the block).
348 struct PropagatedStates {
349 // Initialize with the end of block state, and the gathered undo log
350 // corresponding to that block interp.
351 PropagatedStates(State&&, StateMutationUndo);
353 // Rewind the state one instruction. Must not be at the beginning of
354 // the block.
355 void next();
357 // Stack and local types:
358 const CompactVector<Type>& stack() const { return m_stack; }
359 const CompactVector<Type>& locals() const { return m_locals; }
361 // The type for a particular local *after* the current instruction
362 // runs.
363 const Type& localAfter(LocalId id) const {
364 for (auto const& p : m_afterLocals) {
365 if (p.first == id) return p.second;
367 assertx(id < m_locals.size());
368 return m_locals[id];
371 // The value pushed by current instruction (IE, the top of the stack
372 // before next()).
373 const Optional<Type>& lastPush() const { return m_lastPush; }
375 // Interp flags for the current instruction.
376 bool wasPEI() const { return currentMark().wasPEI; }
377 bool unreachable() const { return currentMark().unreachable; }
378 auto const& mayReadLocalSet() const { return currentMark().mayReadLocalSet; }
380 private:
381 const StateMutationUndo::Mark& currentMark() const {
382 assertx(!m_undos.events.empty());
383 auto const mark =
384 boost::get<StateMutationUndo::Mark>(&m_undos.events.back());
385 assertx(mark);
386 return *mark;
389 Optional<Type> m_lastPush;
390 CompactVector<Type> m_stack;
391 CompactVector<Type> m_locals;
392 CompactVector<std::pair<LocalId, Type>> m_afterLocals;
393 StateMutationUndo m_undos;
397 * Interpret a block and return a PropagatedStates corresponding to
398 * the state at the end of the block.
400 * Pre: stateIn.initialized == true
402 PropagatedStates
403 locally_propagated_states(const Index&,
404 const AnalysisContext&,
405 CollectedInfo& collect,
406 BlockId bid,
407 State stateIn);
410 * Propagate a block input State to find the output state for a particular
411 * target of the block. This is used to update the in state for a block added
412 * to the CFG in between analysis rounds.
414 State locally_propagated_bid_state(const Index& index,
415 const AnalysisContext& ctx,
416 CollectedInfo& collect,
417 BlockId bid,
418 State state,
419 BlockId targetBid);
421 //////////////////////////////////////////////////////////////////////
424 * Resolve a type-constraint into its equivalent set of HHBBC types.
426 * In general, a type-constraint cannot be represented exactly by a
427 * single HHBBC type, so a lower and upper bound is provided
428 * instead.
430 * A "candidate" type can be provided which will be applied to the
431 * type-constraint and can further constrain the output types. This
432 * is useful for the magic interfaces, whose lower-bound cannot be
433 * precisely represented by a single type.
435 struct ConstraintType {
436 // Lower bound of constraint. Any type which is a subtype of this
437 // is guaranteed to pass a type-check without any side-effects.
438 Type lower;
439 // Upper bound of constraint. Any type which does not intersect
440 // with this is guaranteed to always fail a type-check.
441 Type upper;
442 // If this type-constraint might promote a "classish" type to a
443 // static string as a side-effect.
444 TriBool coerceClassToString{TriBool::No};
445 // Whether this type-constraint might map to a mixed
446 // type-hint. The mixed type-hint has special semantics when it
447 // comes to properties.
448 bool maybeMixed{false};
450 ConstraintType lookup_constraint(const IIndex&,
451 const Context&,
452 const TypeConstraint&,
453 const Type& candidate = TCell);
456 * Returns a tuple containing a type after the parameter type
457 * verification, a flag indicating whether the verification is a no-op
458 * (because it always passes without any conversion), and a flag
459 * indicating whether the verification is effect free (the
460 * verification could convert a type without causing a side-effect).
462 std::tuple<Type, bool, bool> verify_param_type(const IIndex&,
463 const Context&,
464 uint32_t paramId,
465 const Type&);
468 * Given a type, adjust the type for the given type-constraint. If there's no
469 * type-constraint, or if property type-hints aren't being enforced, then return
470 * the type as is. This might return TBottom if the type is not compatible with
471 * the type-hint.
473 Type adjust_type_for_prop(const IIndex& index,
474 const php::Class& propCls,
475 const TypeConstraint* tc,
476 const Type& ty);
479 * Resolve a type-constraint to its equivalent
480 * ConstraintType. Candidate can be used to narrow the type in some
481 * situations. The std::functions are called when classes need to be
482 * resolved, or for "this" type-hints.
484 ConstraintType
485 type_from_constraint(const TypeConstraint& tc,
486 const Type& candidate,
487 const std::function<Optional<res::Class>(SString)>& resolve,
488 const std::function<Optional<Type>()>& self);
491 * Given two ConstraintTypes, compute and return the union.
493 ConstraintType union_constraint(const ConstraintType& a,
494 const ConstraintType& b);
495 std::string show(const ConstraintType&);
497 //////////////////////////////////////////////////////////////////////
500 * Try to resolve self/parent types in the given context.
502 Optional<Type> selfCls(const IIndex&, const Context&);
503 Optional<Type> selfClsExact(const IIndex&, const Context&);
505 Optional<Type> parentCls(const IIndex&, const Context&);
506 Optional<Type> parentClsExact(const IIndex&, const Context&);
508 //////////////////////////////////////////////////////////////////////
511 * Resolve a builtin class with the given name. This just calls
512 * index.resolve_class(), but has some additional sanity checks that
513 * the resultant class is a builtin.
515 res::Class builtin_class(const IIndex&, SString);
517 //////////////////////////////////////////////////////////////////////