2 +----------------------------------------------------------------------+
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"
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 //////////////////////////////////////////////////////////////////////
32 template<class JoinOp
>
33 bool merge_into(Iter
& dst
, const Iter
& src
, JoinOp join
) {
36 [&] (UnknownIter
) { return false; },
37 [&] (TrackedIter
& diter
) {
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
));
56 std::string
show(const Iter
& iter
) {
57 return match
<std::string
>(
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
) {
73 a
.func
.hasValue() != b
.func
.hasValue() ? false :
74 a
.func
.hasValue() ? a
.func
->same(*b
.func
) :
77 a
.fallbackFunc
.hasValue() != b
.fallbackFunc
.hasValue() ? false :
78 a
.fallbackFunc
.hasValue() ? a
.fallbackFunc
->same(*b
.fallbackFunc
) :
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
&&
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
) {
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
116 ret
.locals
= src
.locals
;
117 ret
.iters
= src
.iters
;
121 //////////////////////////////////////////////////////////////////////
123 PropertiesInfo::PropertiesInfo(const Index
& index
,
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
);
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
) {
186 dstIt
->second
= newT
;
193 bool merge_into(ActRec
& dst
, const ActRec
& src
) {
194 if (dst
.kind
!= src
.kind
) {
195 dst
= ActRec
{ FPIKind::Unknown
};
199 dst
= ActRec
{ src
.kind
};
205 template<class JoinOp
>
206 bool merge_impl(State
& dst
, const State
& src
, JoinOp join
) {
207 if (!dst
.initialized
) {
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).
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
228 return !src
.unreachable
;
231 auto changed
= false;
233 auto const available
= dst
.thisAvailable
&& src
.thisAvailable
;
234 if (available
!= dst
.thisAvailable
) {
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
) {
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
) {
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
)) {
261 for (auto i
= size_t{0}; i
< dst
.fpiStack
.size(); ++i
) {
262 if (merge_into(dst
.fpiStack
[i
], src
.fpiStack
[i
])) {
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
) {
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";
293 std::string
show(const ActRec
& a
) {
294 return folly::to
<std::string
>(
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
) : "",
306 std::string
state_string(const php::Func
& f
, const State
& st
) {
309 if (!st
.initialized
) {
310 ret
= "state: uninitialized\n";
314 folly::format(&ret
, "state{}:\n", st
.unreachable
? " (unreachable)" : "");
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
])),
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",
340 std::string
property_state_string(const PropertiesInfo
& props
) {
343 for (auto& kv
: props
.privateProperties()) {
344 ret
+= folly::format("$this->{: <14} :: {}\n",
349 for (auto& kv
: props
.privateStatics()) {
350 ret
+= folly::format("self::${: <14} :: {}\n",
359 //////////////////////////////////////////////////////////////////////