Make comparison IR ops layout agnostic
[hiphop-php.git] / hphp / runtime / base / heap-scan.h
blob38988bb484868572a5de3f352d7d8c12f0800375
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 #ifndef HPHP_HEAP_SCAN_H
17 #define HPHP_HEAP_SCAN_H
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/heap-graph.h"
21 #include "hphp/runtime/base/memory-manager.h"
22 #include "hphp/runtime/base/mixed-array.h"
23 #include "hphp/runtime/base/mixed-array-defs.h"
24 #include "hphp/runtime/base/object-data.h"
25 #include "hphp/runtime/base/packed-array.h"
26 #include "hphp/runtime/base/packed-array-defs.h"
27 #include "hphp/runtime/base/rds-header.h"
28 #include "hphp/runtime/base/req-root.h"
29 #include "hphp/runtime/base/resource-data.h"
30 #include "hphp/runtime/base/string-data.h"
31 #include "hphp/runtime/base/request-info.h"
33 #include "hphp/runtime/ext/asio/asio-external-thread-event-queue.h"
34 #include "hphp/runtime/ext/asio/ext_async-function-wait-handle.h"
35 #include "hphp/runtime/ext/asio/ext_async-generator.h"
36 #include "hphp/runtime/ext/asio/ext_external-thread-event-wait-handle.h"
37 #include "hphp/runtime/ext/asio/ext_reschedule-wait-handle.h"
38 #include "hphp/runtime/ext/asio/ext_resumable-wait-handle.h"
39 #include "hphp/runtime/ext/asio/ext_sleep-wait-handle.h"
40 #include "hphp/runtime/ext/extension-registry.h"
41 #include "hphp/runtime/ext/generator/ext_generator.h"
43 #include "hphp/runtime/server/server-note.h"
45 #include "hphp/runtime/vm/named-entity.h"
46 #include "hphp/runtime/vm/named-entity-defs.h"
47 #include "hphp/runtime/vm/runtime.h"
49 #include "hphp/util/hphp-config.h"
51 #include "hphp/util/rds-local.h"
52 #include "hphp/util/type-scan.h"
54 namespace HPHP {
56 inline void scanFrameSlots(const ActRec* ar, type_scan::Scanner& scanner) {
57 // layout: [iters][locals][ActRec]
58 // ^ar
59 auto num_locals = ar->func()->numLocals();
60 auto locals = frame_local(ar, num_locals - 1);
61 scanner.scan(*locals, num_locals * sizeof(TypedValue));
62 auto num_iters = ar->func()->numIterators();
63 auto iters = frame_iter(ar, num_iters - 1);
64 scanner.scan(*iters, num_iters * sizeof(Iter));
67 inline void scanNative(const NativeNode* node, type_scan::Scanner& scanner) {
68 auto obj = Native::obj(node);
69 auto ndi = obj->getVMClass()->getNativeDataInfo();
70 auto data = (const char*)obj - ndi->sz;
71 scanner.scanByIndex(node->typeIndex(), data, ndi->sz);
72 if (auto off = node->arOff()) {
73 scanFrameSlots((const ActRec*)((const char*)node + off), scanner);
77 inline void scanAFWH(const c_Awaitable* wh, type_scan::Scanner& scanner) {
78 assertx(!wh->hasNativeData());
79 // scan ResumableHeader before object
80 auto r = Resumable::FromObj(wh);
81 if (!wh->isFinished()) {
82 scanFrameSlots(r->actRec(), scanner);
83 scanner.scan(*r);
85 return wh->scan(scanner);
88 inline void scanMemoSlots(const ObjectData* obj,
89 type_scan::Scanner& scanner,
90 bool isNative) {
91 auto const cls = obj->getVMClass();
92 assertx(cls->hasMemoSlots());
94 if (!obj->getAttribute(ObjectData::UsedMemoCache)) return;
96 auto const numSlots = cls->numMemoSlots();
97 if (!isNative) {
98 for (Slot i = 0; i < numSlots; ++i) scanner.scan(*obj->memoSlot(i));
99 } else {
100 auto const ndi = cls->getNativeDataInfo();
101 for (Slot i = 0; i < numSlots; ++i) {
102 scanner.scan(*obj->memoSlotNativeData(i, ndi->sz));
107 inline void scanHeapObject(const HeapObject* h, type_scan::Scanner& scanner) {
108 switch (h->kind()) {
109 case HeaderKind::Packed:
110 case HeaderKind::Vec:
111 return PackedArray::scan(static_cast<const ArrayData*>(h), scanner);
112 case HeaderKind::Mixed:
113 case HeaderKind::Dict:
114 return static_cast<const MixedArray*>(h)->scan(scanner);
115 case HeaderKind::Keyset:
116 return static_cast<const SetArray*>(h)->scan(scanner);
117 case HeaderKind::BespokeVArray:
118 case HeaderKind::BespokeDArray:
119 case HeaderKind::BespokeDict:
120 case HeaderKind::BespokeVec:
121 case HeaderKind::BespokeKeyset:
122 return static_cast<const BespokeArray*>(h)->scan(scanner);
123 case HeaderKind::Closure:
124 scanner.scan(*static_cast<const c_Closure*>(h)->hdr());
125 return static_cast<const c_Closure*>(h)->scan(scanner);
126 case HeaderKind::Object:
127 // NativeObject should hit the NativeData case below.
128 return static_cast<const ObjectData*>(h)->scan(scanner);
129 case HeaderKind::WaitHandle:
130 // scan C++ properties after [ObjectData] header. should pick up
131 // unioned and bit-packed fields
132 return static_cast<const c_Awaitable*>(h)->scan(scanner);
133 case HeaderKind::AwaitAllWH:
134 // scan C++ properties after [ObjectData] header. should pick up
135 // unioned and bit-packed fields
136 return static_cast<const c_AwaitAllWaitHandle*>(h)->scan(scanner);
137 case HeaderKind::AsyncFuncWH:
138 return scanAFWH(static_cast<const c_Awaitable*>(h), scanner);
139 case HeaderKind::NativeData: {
140 auto native = static_cast<const NativeNode*>(h);
141 scanNative(native, scanner);
142 auto const obj = Native::obj(native);
143 if (UNLIKELY(obj->getVMClass()->hasMemoSlots())) {
144 scanMemoSlots(obj, scanner, true);
146 return obj->scan(scanner);
148 case HeaderKind::AsyncFuncFrame:
149 return scanAFWH(asyncFuncWH(h), scanner);
150 case HeaderKind::ClosureHdr:
151 scanner.scan(*static_cast<const ClosureHdr*>(h));
152 return closureObj(h)->scan(scanner);
153 case HeaderKind::MemoData: {
154 auto const obj = memoObj(h);
155 scanMemoSlots(obj, scanner, false);
156 return obj->scan(scanner);
158 case HeaderKind::Pair:
159 return static_cast<const c_Pair*>(h)->scan(scanner);
160 case HeaderKind::Vector:
161 case HeaderKind::ImmVector:
162 return static_cast<const BaseVector*>(h)->scan(scanner);
163 case HeaderKind::Map:
164 case HeaderKind::ImmMap:
165 case HeaderKind::Set:
166 case HeaderKind::ImmSet:
167 return static_cast<const HashCollection*>(h)->scan(scanner);
168 case HeaderKind::Resource: {
169 auto res = static_cast<const ResourceHdr*>(h);
170 return scanner.scanByIndex(res->typeIndex(), res->data(),
171 res->heapSize() - sizeof(ResourceHdr));
173 case HeaderKind::ClsMeth:
174 // ClsMeth only holds pointers to non-request allocated data
175 return;
176 case HeaderKind::RClsMeth: {
177 auto const rclsmeth = static_cast<const RClsMethData*>(h);
178 return PackedArray::scan(rclsmeth->m_arr, scanner);
180 case HeaderKind::Record:
181 return static_cast<const RecordData*>(h)->scan(scanner);
182 case HeaderKind::RFunc: {
183 auto const rfunc = static_cast<const RFuncData*>(h);
184 return PackedArray::scan(rfunc->m_arr, scanner);
186 case HeaderKind::Cpp:
187 case HeaderKind::SmallMalloc:
188 case HeaderKind::BigMalloc: {
189 auto n = static_cast<const MallocNode*>(h);
190 return scanner.scanByIndex(n->typeIndex(), n + 1,
191 n->nbytes - sizeof(MallocNode));
193 case HeaderKind::String:
194 case HeaderKind::Free:
195 case HeaderKind::Hole:
196 // these don't have pointers. some clients might generically
197 // scan them even if they aren't interesting.
198 return;
199 case HeaderKind::NativeObject:
200 // should have scanned the NativeData header.
201 break;
202 case HeaderKind::Slab:
203 // these aren't legitimate headers, and heap iteration should skip them.
204 break;
206 always_assert(false && "corrupt header in worklist");
209 inline void c_AwaitAllWaitHandle::scan(type_scan::Scanner& scanner) const {
210 scanner.scanByIndex(m_tyindex, this, heapSize());
211 ObjectData::scan(scanner); // in case of dynprops
214 inline void c_Awaitable::scan(type_scan::Scanner& scanner) const {
215 assertx(kind() != HeaderKind::AwaitAllWH);
216 auto const size =
217 kind() == HeaderKind::AsyncFuncWH ? sizeof(c_AsyncFunctionWaitHandle) :
218 asio_object_size(this);
219 scanner.scanByIndex(m_tyindex, this, size);
220 ObjectData::scan(scanner);
223 inline void RecordBase::scan(type_scan::Scanner& scanner) const {
224 auto fields = fieldVec();
225 scanner.scan(*fields, m_record->numFields() * sizeof(*fields));
228 inline void RecordData::scan(type_scan::Scanner& scanner) const {
229 RecordBase::scan(scanner);
232 inline void ObjectData::scan(type_scan::Scanner& scanner) const {
233 props()->scan(m_cls->countablePropsEnd(), scanner);
234 if (getAttribute(HasDynPropArr)) {
235 // nb: dynamic property arrays are in ExecutionContext::dynPropTable,
236 // which is not marked as a root. Scan the entry pair, so both the key
237 // and value are scanned.
238 auto iter = g_context->dynPropTable.find(this);
239 scanner.scan(*iter); // *iter is pair<ObjectData*,ArrayNoDtor>
243 // [<-stack[iters[locals[params[ActRec[stack[iters[locals[ActRec...
244 // ^m_top ^fp ^firstAR
246 // +-----------------------+
247 // top -> | current eval stack |
248 // +-----------------------+
249 // | iterators |
250 // +-----------------------+
251 // | locals, params |
252 // +-----------------------+
253 // fp -> | current ActRec |
254 // +-----------------------+
255 // | caller's eval stack |
256 // +-----------------------+
257 // ...
258 // +-----------------------+
259 // firstAR -> | ActRec |
260 // +-----------------------+
262 // fp{sfp}... forms the true execution stack, but it chains
263 // in and out of resumables. sp always points into the vm stack section.
264 // locals/iterators/actrec are missing in some stack frames.
266 // ActRec:
267 // m_sfp prev ActRec*
268 // m_savedRIP return addr
269 // m_func Func*
270 // m_callOffAndFlags caller's vmpc
271 // m_argc_flags
272 // m_this|m_cls ObjectData* or Class*
273 // m_varenv|extraArgs
275 // for reference, see vm/unwind.cpp and visitStackElms() in bytecode.h
277 // Descriptive wrapper types to annotate root nodes
278 struct PhpStack { TYPE_SCAN_CONSERVATIVE_ALL; void* dummy; };
279 struct CppStack { TYPE_SCAN_CONSERVATIVE_ALL; void* dummy; };
281 template<class Fn>
282 void MemoryManager::iterateRoots(Fn fn) const {
283 fn(&m_sweepables, sizeof(m_sweepables),
284 type_scan::getIndexForScan<SweepableList>());
285 for (auto& node: m_natives) {
286 fn(&node, sizeof(node), type_scan::getIndexForScan<NativeNode*>());
288 for (const auto root : m_root_handles) {
289 root->iterate(fn);
293 template<class Fn> void ThreadLocalManager::iterate(Fn fn) const {
294 auto list = getList(pthread_getspecific(m_key));
295 if (!list) return;
296 for (auto p = list->head; p != nullptr;) {
297 auto node = static_cast<ThreadLocalNode<void>*>(p);
298 if (node->m_p) {
299 fn(node->m_p, node->m_size, node->m_tyindex);
301 p = node->m_next;
305 // Visit request-local roots. Each invocation of fn represents one root
306 // instance of a given type and size, containing potentially several
307 // pointers.
308 template<class Fn> void iterateConservativeRoots(Fn fn) {
309 auto rds = rds::header();
311 // php header and stack
312 // TODO #6509338 exactly scan the php stack.
313 if (rds) {
314 fn(rds, sizeof(*rds), type_scan::getIndexForScan<rds::Header>());
315 auto stack_end = rds->vmRegs.stack.getStackHighAddress();
316 auto sp = rds->vmRegs.stack.top();
317 fn(sp, uintptr_t(stack_end) - uintptr_t(sp),
318 type_scan::getIndexForScan<PhpStack>());
321 if (!g_context.isNull()) {
322 // m_nestedVMs contains MInstrState, which has a conservatively-scanned
323 // fields. Scan it now, then ignore when ExecutionContext is scanned.
324 fn(&g_context->m_nestedVMs, sizeof(g_context->m_nestedVMs),
325 type_scan::getIndexForScan<ExecutionContext::VMStateVec>());
328 // cpp stack. ensure stack contains callee-saved registers.
329 CALLEE_SAVED_BARRIER();
330 auto sp = stack_top_ptr_conservative();
331 fn(sp, s_stackLimit + s_stackSize - uintptr_t(sp),
332 type_scan::getIndexForScan<CppStack>());
335 template<class Fn> void iterateExactRoots(Fn fn) {
336 auto rds = rds::header();
338 // normal section
339 if (rds) {
340 rds::forEachNormalAlloc(fn);
343 // Local section (mainly static properties).
344 // static properties have a per-class, versioned, bool in rds::Normal,
345 // tracked by Class::m_sPropCacheInit, plus one TypedValue in rds::Local
346 // for each property, tracked in Class::m_sPropCache. Just scan the
347 // properties in rds::Local. We ignore the state of the bool, because it
348 // is not initialized until after all sprops are initialized, and it's
349 // necessary to scan static properties *during* initialization.
350 if (rds) {
351 rds::forEachLocalAlloc(fn);
354 rds::local::iterateRoots(fn);
356 // Root handles & sweep lists
357 tl_heap->iterateRoots(fn);
359 // ThreadLocal nodes (but skip MemoryManager).
360 ThreadLocalManager::GetManager().iterate(fn);
363 template<class Fn> void iterateRoots(Fn fn) {
364 iterateConservativeRoots(fn);
365 iterateExactRoots(fn);
369 #endif