codemod 2010-2016 to 2010-present
[hiphop-php.git] / hphp / hhbbc / interp-state.cpp
blob13e540dbd5f0c91c04d76ceddc14ddade47305f8
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 #include "hphp/hhbbc/interp-state.h"
18 #include <string>
20 #include <folly/Format.h>
21 #include <folly/Conv.h>
23 #include "hphp/util/match.h"
24 #include "hphp/hhbbc/analyze.h"
26 namespace HPHP { namespace HHBBC {
28 //////////////////////////////////////////////////////////////////////
30 namespace {
32 template<class JoinOp>
33 bool merge_into(Iter& dst, const Iter& src, JoinOp join) {
34 return match<bool>(
35 dst,
36 [&] (UnknownIter) { return false; },
37 [&] (TrackedIter& diter) {
38 return match<bool>(
39 src,
40 [&] (UnknownIter) {
41 dst = UnknownIter {};
42 return true;
44 [&] (const TrackedIter& siter) {
45 auto k1 = join(diter.kv.first, siter.kv.first);
46 auto k2 = join(diter.kv.second, siter.kv.second);
47 auto const changed = k1 != diter.kv.first || k2 != diter.kv.second;
48 diter.kv = std::make_pair(std::move(k1), std::move(k2));
49 return changed;
56 std::string show(const Iter& iter) {
57 return match<std::string>(
58 iter,
59 [&] (UnknownIter) { return "unk"; },
60 [&] (const TrackedIter& ti) {
61 return folly::format("{}, {}", show(ti.kv.first),
62 show(ti.kv.second)).str();
69 //////////////////////////////////////////////////////////////////////
71 bool operator==(const ActRec& a, const ActRec& b) {
72 auto const fsame =
73 a.func.hasValue() != b.func.hasValue() ? false :
74 a.func.hasValue() ? a.func->same(*b.func) :
75 true;
76 auto const fsame2 =
77 a.fallbackFunc.hasValue() != b.fallbackFunc.hasValue() ? false :
78 a.fallbackFunc.hasValue() ? a.fallbackFunc->same(*b.fallbackFunc) :
79 true;
80 return a.kind == b.kind && fsame && fsame2;
83 bool operator==(const State& a, const State& b) {
84 return a.initialized == b.initialized &&
85 a.thisAvailable == b.thisAvailable &&
86 a.locals == b.locals &&
87 a.stack == b.stack &&
88 a.fpiStack == b.fpiStack;
91 bool operator!=(const ActRec& a, const ActRec& b) { return !(a == b); }
92 bool operator!=(const State& a, const State& b) { return !(a == b); }
94 State without_stacks(const State& src) {
95 auto ret = State{};
96 ret.initialized = src.initialized;
97 ret.thisAvailable = src.thisAvailable;
99 if (UNLIKELY(src.locals.size() > (1LL << 50))) {
100 // gcc 4.9 has a bug where it will spit out a warning:
102 // > In function 'HPHP::HHBBC::State HPHP::HHBBC::without_stacks':
103 // > cc1plus: error: iteration 461168601842738791u invokes undefined
104 // > behavior [-Werror=aggressive-loop-optimizations]
105 // > include/c++/4.9.x/bits/stl_algobase.h:334:4: note: containing loop
107 // The warning is a bug because it computes the number of
108 // iterations by subtracting two pointers; and the result *cannot*
109 // exceed 461168601842738790. (its also a bug because it
110 // shouldn't generate such warnings in its own headers).
112 // in any case, this disables it, and generates no code in O3 builds
113 not_reached();
116 ret.locals = src.locals;
117 ret.iters = src.iters;
118 return ret;
121 //////////////////////////////////////////////////////////////////////
123 PropertiesInfo::PropertiesInfo(const Index& index,
124 Context ctx,
125 ClassAnalysis* cls)
126 : m_cls(cls)
128 if (m_cls == nullptr && ctx.cls != nullptr) {
129 m_privateProperties = index.lookup_private_props(ctx.cls);
130 m_privateStatics = index.lookup_private_statics(ctx.cls);
134 PropState& PropertiesInfo::privateProperties() {
135 if (m_cls != nullptr) {
136 return m_cls->privateProperties;
138 return m_privateProperties;
141 PropState& PropertiesInfo::privateStatics() {
142 if (m_cls != nullptr) {
143 return m_cls->privateStatics;
145 return m_privateStatics;
148 const PropState& PropertiesInfo::privateProperties() const {
149 return const_cast<PropertiesInfo*>(this)->privateProperties();
152 const PropState& PropertiesInfo::privateStatics() const {
153 return const_cast<PropertiesInfo*>(this)->privateStatics();
156 //////////////////////////////////////////////////////////////////////
158 void merge_closure_use_vars_into(ClosureUseVarMap& dst,
159 borrowed_ptr<php::Class> clo,
160 std::vector<Type> types) {
161 auto& current = dst[clo];
162 if (current.empty()) {
163 current = std::move(types);
164 return;
167 assert(types.size() == current.size());
168 for (auto i = uint32_t{0}; i < current.size(); ++i) {
169 current[i] = union_of(std::move(current[i]), std::move(types[i]));
173 bool widen_into(PropState& dst, const PropState& src) {
174 assert(dst.size() == src.size());
176 auto changed = false;
178 auto dstIt = begin(dst);
179 auto srcIt = begin(src);
180 for (; dstIt != end(dst); ++dstIt, ++srcIt) {
181 assert(srcIt != end(src));
182 assert(srcIt->first == dstIt->first);
183 auto const newT = widening_union(dstIt->second, srcIt->second);
184 if (newT != dstIt->second) {
185 changed = true;
186 dstIt->second = newT;
190 return changed;
193 bool merge_into(ActRec& dst, const ActRec& src) {
194 if (dst.kind != src.kind) {
195 dst = ActRec { FPIKind::Unknown };
196 return true;
198 if (dst != src) {
199 dst = ActRec { src.kind };
200 return true;
202 return false;
205 template<class JoinOp>
206 bool merge_impl(State& dst, const State& src, JoinOp join) {
207 if (!dst.initialized) {
208 dst = src;
209 return true;
212 assert(src.initialized);
213 assert(dst.locals.size() == src.locals.size());
214 assert(dst.iters.size() == src.iters.size());
215 assert(dst.stack.size() == src.stack.size());
216 assert(dst.fpiStack.size() == src.fpiStack.size());
218 if (src.unreachable) {
219 // If we're coming from unreachable code and the dst is already
220 // initialized, it doesn't change the dst (whether it is reachable or not).
221 return false;
223 if (dst.unreachable) {
224 // If we're going to code currently believed to be unreachable, take the
225 // src state, and consider the dest state changed only if the source state
226 // was reachable.
227 dst = src;
228 return !src.unreachable;
231 auto changed = false;
233 auto const available = dst.thisAvailable && src.thisAvailable;
234 if (available != dst.thisAvailable) {
235 changed = true;
236 dst.thisAvailable = available;
239 for (auto i = size_t{0}; i < dst.stack.size(); ++i) {
240 auto newT = join(dst.stack[i], src.stack[i]);
241 if (dst.stack[i] != newT) {
242 changed = true;
243 dst.stack[i] = std::move(newT);
247 for (auto i = size_t{0}; i < dst.locals.size(); ++i) {
248 auto newT = join(dst.locals[i], src.locals[i]);
249 if (dst.locals[i] != newT) {
250 changed = true;
251 dst.locals[i] = std::move(newT);
255 for (auto i = size_t{0}; i < dst.iters.size(); ++i) {
256 if (merge_into(dst.iters[i], src.iters[i], join)) {
257 changed = true;
261 for (auto i = size_t{0}; i < dst.fpiStack.size(); ++i) {
262 if (merge_into(dst.fpiStack[i], src.fpiStack[i])) {
263 changed = true;
267 return changed;
270 bool merge_into(State& dst, const State& src) {
271 return merge_impl(dst, src, union_of);
274 bool widen_into(State& dst, const State& src) {
275 return merge_impl(dst, src, widening_union);
278 //////////////////////////////////////////////////////////////////////
280 static std::string fpiKindStr(FPIKind k) {
281 switch (k) {
282 case FPIKind::Unknown: return "unk";
283 case FPIKind::CallableArr: return "arr";
284 case FPIKind::Func: return "func";
285 case FPIKind::Ctor: return "ctor";
286 case FPIKind::ObjMeth: return "objm";
287 case FPIKind::ClsMeth: return "clsm";
288 case FPIKind::ObjInvoke: return "invoke";
290 not_reached();
293 std::string show(const ActRec& a) {
294 return folly::to<std::string>(
295 "ActRec { ",
296 fpiKindStr(a.kind),
297 a.cls || a.func ? ": " : "",
298 a.cls ? show(*a.cls) : "",
299 a.cls && a.func ? "::" : "",
300 a.func ? show(*a.func) : "",
301 a.fallbackFunc ? show(*a.fallbackFunc) : "",
302 " }"
306 std::string state_string(const php::Func& f, const State& st) {
307 std::string ret;
309 if (!st.initialized) {
310 ret = "state: uninitialized\n";
311 return ret;
314 folly::format(&ret, "state{}:\n", st.unreachable ? " (unreachable)" : "");
315 if (f.cls) {
316 folly::format(&ret, "thisAvailable({})\n", st.thisAvailable);
319 for (auto i = size_t{0}; i < st.locals.size(); ++i) {
320 folly::format(&ret, "{: <8} :: {}\n",
321 local_string(borrow(f.locals[i])),
322 show(st.locals[i])
326 for (auto i = size_t{0}; i < st.iters.size(); ++i) {
327 folly::format(&ret, "iter {: <2} :: {}\n", i, show(st.iters[i]));
330 for (auto i = size_t{0}; i < st.stack.size(); ++i) {
331 folly::format(&ret, "stk[{:02}] :: {}\n",
333 show(st.stack[i])
337 return ret;
340 std::string property_state_string(const PropertiesInfo& props) {
341 std::string ret;
343 for (auto& kv : props.privateProperties()) {
344 ret += folly::format("$this->{: <14} :: {}\n",
345 kv.first,
346 show(kv.second)
347 ).str();
349 for (auto& kv : props.privateStatics()) {
350 ret += folly::format("self::${: <14} :: {}\n",
351 kv.first,
352 show(kv.second)
353 ).str();
356 return ret;
359 //////////////////////////////////////////////////////////////////////