Add a HHIR-level peephole optimization to reorder CheckTypes
[hiphop-php.git] / hphp / util / rds-local.h
blob7980de33d6abc8eebf78cc471086848022002b09
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 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_RDS_LOCAL_H_
18 #define incl_HPHP_RDS_LOCAL_H_
20 #include "hphp/util/alloc.h"
21 #include "hphp/util/assertions.h"
22 #include "hphp/util/optional.h"
23 #include "hphp/util/thread-local.h"
25 namespace HPHP {
27 struct RequestEventHandler;
28 struct ArrayData;
30 namespace rds {
31 namespace local {
33 /**
34 * RDSLocals are stored in RDS on request threads, and in a malloc'ed buffer on
35 * non request threads. Their destructor is called as a RDS is cleaned up, or
36 * as the backing space is cleaned up. They expose the same API as thread
37 * locals, and can be used in a similar manner. The core difference is that
38 * they will be tied to a request data segment on request threads, rather than
39 * a thread local section.
40 * There are two options for how RDS locals should be initialized:
41 * - FirstUse
42 * - Similar to standard thread locals.
43 * - Explicitly
44 * - Similar to thread local no check.
46 * RDSLocals that inherit from RequestEventHandler have their requestInit, and
47 * requestEnd methods called at first use in a request, and at RequestFini.
48 * For that reason, they cannot be tagged as explicitly being initialized.
49 * This is to support legacy behavior. Favor using RDS locals that do not
50 * inherit from RequestEventHandler. Init and destroy logic can be done in
51 * InitFiniNodes and extension initialisation.
53 * Finally, for performance purposes, extremely hot RDSLocals can be added to
54 * the HotRDSLocals struct, which stores the data flat in thread local storage.
55 * Swapping RDS sections requires copying this data in and out of backing RDS
56 * storage. Avoid using these if possible.
58 * How to use rds local macros:
60 * Use RDS_LOCAL to declare a *static* class field or global as RDS local
61 * struct SomeClass {
62 * static RDS_LOCAL(SomeFieldType, field);
63 * };
64 * extern RDS_LOCAL(SomeGlobalType, rl_myGlobal);
66 * Use RDS_LOCAL in the cpp file to implement the field/global:
67 * RDS_LOCAL(SomeFieldType, SomeClass::field);
68 * RDS_LOCAL(SomeGlobalType, rl_myGlobal);
71 ///////////////////////////////////////////////////////////////////////////////
72 // RDS Local Hot
74 // How to use hot RDS local macros:
76 // In a header:
77 // extern DECLARE_RDS_LOCAL_HOTVALUE(SomeType, rl_hotGlobal);
79 // In an implementation file:
80 // IMPLEMENT_RDS_LOCAL_HOTVALUE(SomeType, rl_hotGlobal);
82 // Finally add rl_hotGlobal as a member to the HotRDSLocals struct.
85 #define DECLARE_RDS_LOCAL_HOTVALUE(T, f) \
86 struct RLHotWrapper_ ## f { \
87 RLHotWrapper_ ## f& operator=(T&& v) { \
88 ::HPHP::rds::local::detail::rl_hotSection.f = v; \
89 return *this; \
90 } \
91 operator T&() { \
92 return ::HPHP::rds::local::detail::rl_hotSection.f; \
93 } \
94 } f;
96 #define IMPLEMENT_RDS_LOCAL_HOTVALUE(T, f) \
97 RLHotWrapper_ ## f f;
99 namespace detail {
100 struct HotRDSLocals {
101 void* rdslocal_base;
102 void* g_context;
104 bool t_eager_gc;
106 TYPE_SCAN_IGNORE_FIELD(rdslocal_base);
107 TYPE_SCAN_IGNORE_FIELD(g_context);
109 static_assert(sizeof(HotRDSLocals) <= 64,
110 "It is essential HotRDSLocals is small. Consider using "
111 "normal rds locals if possible. Hot rds locals are copied "
112 "every user level context switch.");
114 extern __thread HotRDSLocals rl_hotSection;
115 extern uint32_t s_usedbytes;
119 ///////////////////////////////////////////////////////////////////////////////
120 // RDS Local
122 #define RDS_LOCAL(T, f) \
123 ::HPHP::rds::local::RDSLocal<T, ::HPHP::rds::local::Initialize::FirstUse> f
125 #define RDS_LOCAL_NO_CHECK(T, f) \
126 ::HPHP::rds::local::RDSLocal<T, ::HPHP::rds::local::Initialize::Explicitly> f
128 namespace detail {
130 struct RDSLocalNode {
131 virtual ~RDSLocalNode() {}
133 virtual void init() = 0;
134 virtual void fini() = 0;
136 virtual void* nodeLocation() = 0;
137 virtual size_t nodeSize() = 0;
138 virtual type_scan::Index nodeTypeIndex() = 0;
141 RDSLocalNode* m_next;
142 // s_RDSLocalsBase is the base of the RDSLocal section in RDS. Its only
143 // used for computing RDS offsets for use in the JIT.
144 static uint32_t s_RDSLocalsBase;
146 template<typename Fn>
147 void iterateRoot(Fn fn) {
148 fn(nodeLocation(), nodeSize(), nodeTypeIndex());
152 extern RDSLocalNode* head;
154 template<typename Fn>
155 void iterate(Fn fn) {
156 for (auto p = head; p != nullptr; p = p->m_next) {
157 fn(p);
161 void initializeRequestEventHandler(RequestEventHandler* h);
163 template<typename T, typename Enable = void>
164 struct RDSLocalBase;
166 template<typename T>
167 struct RDSLocalBase<T, std::enable_if_t<std::is_copy_constructible<T>::value>>
168 : RDSLocalNode {
169 Optional<T> m_defaultValue;
170 void setDefault(const T& v) {
171 m_defaultValue = v;
173 Optional<T> getDefault() const {
174 return m_defaultValue;
178 template<typename T>
179 struct RDSLocalBase<T, std::enable_if_t<!std::is_copy_constructible<T>::value>>
180 : RDSLocalNode {
181 void setDefault(const T& v) {}
182 Optional<T> getDefault() const { return std::nullopt; }
185 ///////////////////////////////////////////////////////////////////////////////
188 struct Configuration {
189 // Returns a handle to the base of the RDS locals in the RDS region. If no
190 // RDS section is used and RDS locals are backed by other allocation, this
191 // can be left as nullptr. The parameter is the space required for the
192 // RDS locals.
193 uint32_t(*rdsInitFunc)(size_t) = nullptr;
194 // Initializes a pointer to the base of the RDS locals area to be used by the
195 // calling thread/fiber. It is passed the size required for the RDS locals
196 // (guaranteed to be identical to the size that was passed to rdsInitFunc),
197 // and the handle that rdsInitFunc returned.
198 void*(*initFunc)(size_t, uint32_t) = nullptr;
199 // Call to deallocate the region passed as the parameter. The parameter is
200 // guaranteed to be a pointer returned by initFunc earlier. It also will
201 // not be in RDS.
202 void(*finiFunc)(void*) = nullptr;
203 // Returns true if the region passed as a parameter lives within RDS. If
204 // RDS is not in use, this may be left as nullptr.
205 bool(*inRdsFunc)(void*, size_t) = nullptr;
206 // Register the parameter to have its requestInit, and requestShutdown
207 // methods called at request start and end. If no RequestEventHandlers are
208 // stored in RDS locals, then this may be left as nullptr.
209 void(*initRequestEventHandler)(RequestEventHandler*) = nullptr;
212 namespace detail {
213 extern Configuration g_config;
216 struct RegisterConfig {
217 explicit RegisterConfig(Configuration&& config) {
218 detail::g_config = std::move(config);
222 // RDSInit is called to configure offsets into the rds local area.
223 void RDSInit();
224 // init is called to allocate and initialize the rdslocals.
225 void init();
226 // fini deallocates the rdslocals.
227 void fini(bool inrds = false);
229 template<typename Fn>
230 void iterateRoots(Fn fn) {
231 detail::iterate([&](detail::RDSLocalNode* p) {
232 p->iterateRoot(fn);
236 enum class Initialize : uint8_t {
237 FirstUse,
238 Explicitly,
241 template<typename T, Initialize Init>
242 struct RDSLocal : private detail::RDSLocalBase<T> {
243 static auto constexpr REH = std::is_base_of<RequestEventHandler, T>::value;
244 template<typename T1>
245 using REH_t = std::enable_if_t<REH, T1>;
246 template<typename T1>
247 using NREH_t = std::enable_if_t<!REH, T1>;
248 static_assert(Init != Initialize::Explicitly || !REH,
249 "Can't explicilty initialize a subtype of RequestEventHandler");
251 RDSLocal();
253 template<typename T1 = T>
254 explicit RDSLocal(
255 const T& v,
256 std::enable_if_t<std::is_copy_constructible<T1>::value>* = 0)
257 : RDSLocal() {
258 this->setDefault(v);
261 NEVER_INLINE void create();
262 void destroy();
263 void nullOut();
265 bool isNull() const { return !(detail::rl_hotSection.rdslocal_base &&
266 node().has_value()); }
267 explicit operator bool() const { return !isNull(); }
269 template<typename T1 = T, typename = decltype(std::declval<T1&>()[0])>
270 decltype(std::declval<T1&>()[0]) operator[](size_t i) {
271 return (*get())[i];
274 T* operator->() const { return get(); }
275 T& operator*() const { return *get(); }
277 T* getCheck() const;
278 T* get() const;
280 template<typename T1 = T*>
281 NREH_t<T1> getNoCheck() const {
282 assertx(!isNull());
283 return &node().value();
286 template<typename B = bool>
287 NREH_t<B> getInited() const {
288 return !isNull();
291 template<typename B = bool>
292 REH_t<B> getInited() const {
293 return !isNull() && node().value().getInited();
296 size_t getRDSOffset() const {
297 return this->s_RDSLocalsBase + m_offset;
300 size_t getRawOffset() const {
301 return m_offset;
304 template <typename... Args>
305 void emplace(Args&& ...args);
307 protected:
308 template<typename T1 = T>
309 std::enable_if_t<!std::is_copy_constructible<T1>::value> defaultInit() {}
310 template<typename T1 = T>
311 std::enable_if_t<std::is_copy_constructible<T1>::value> defaultInit() {
312 if (auto const& defaultValue = this->getDefault()) {
313 emplace(*defaultValue);
317 void init() override {
318 assertx(detail::rl_hotSection.rdslocal_base);
319 // Initialize the Node so that it is unset.
320 new (&node()) Node();
321 defaultInit();
323 void fini() override {
324 destroy();
327 template<typename T1 = T*>
328 REH_t<T1> getNoCheck() const {
329 assertx(!isNull());
330 return &node().value();
333 template<typename V = void>
334 REH_t<V> rehInit() const {
335 if (!getInited()) {
336 assertx(detail::g_config.initRequestEventHandler);
337 detail::g_config.initRequestEventHandler(getNoCheck());
340 template<typename V = void>
341 NREH_t<V> rehInit() const {}
343 // Node reimplements parts of what Optional supplies, however it does
344 // so trusting that the user will check values are present as appropriate.
345 struct Node {
346 Node() : hasValue(false) {}
347 ~Node() { clear(); }
348 bool has_value() const noexcept { return hasValue; }
349 T& value() noexcept {
350 assertx(hasValue);
351 return storage;
353 template<typename... Args>
354 void emplace(Args&&... args) {
355 clear();
356 const void* ptr = &storage;
357 new (const_cast<void*>(ptr)) T(std::forward<Args>(args)...);
358 hasValue = true;
360 template<typename T1 = T>
361 std::enable_if_t<!std::is_trivially_destructible<T1>::value, void> clear() {
362 if (hasValue) {
363 hasValue = false;
364 storage.~T();
367 template<typename T1 = T>
368 std::enable_if_t<std::is_trivially_destructible<T1>::value, void> clear() {
369 hasValue = false;
371 void nullOut() {
372 hasValue = false;
374 TYPE_SCAN_CUSTOM() {
375 if (hasValue) {
376 scanner.scan(storage);
379 private:
380 union {
381 char empty;
382 T storage;
384 bool hasValue;
387 virtual Node& node() const {
388 return *reinterpret_cast<Node*>(
389 ((char*)detail::rl_hotSection.rdslocal_base + m_offset));
392 void* nodeLocation() override { return &node(); }
393 size_t nodeSize() override { return sizeof(Node); }
394 type_scan::Index nodeTypeIndex() override {
395 return type_scan::getIndexForScan<Node>();
398 uint32_t m_offset;
401 // The initialisation of the RDS locals will happen at first use, or through
402 // explicit initialisation calls. RDS locals are stored in the rds local
403 // section on request threads, so they are not assigned generation numbers, and
404 // are not invalidated at the end of a request.
406 // On non request threads RDS locals are stored in a block allocated in the
407 // heap. This is destroyed as rds::local::fini is called.
409 template<typename T, Initialize Init>
410 RDSLocal<T, Init>::RDSLocal() {
411 this->m_next = detail::head;
412 detail::head = this;
413 auto const align = folly::nextPowTwo(alignof(T)) - 1;
414 always_assert(align < 16);
415 detail::s_usedbytes = (detail::s_usedbytes + align) & ~align;
416 m_offset = detail::s_usedbytes;
417 detail::s_usedbytes += sizeof(Node);
420 template<typename T, Initialize Init>
421 void RDSLocal<T, Init>::create() {
422 node().emplace();
425 template<typename T, Initialize Init>
426 void RDSLocal<T, Init>::destroy() {
427 if (!isNull()) {
428 node().clear();
432 template<typename T, Initialize Init>
433 void RDSLocal<T, Init>::nullOut() {
434 if (!isNull()) {
435 node().nullOut();
439 template<typename T, Initialize Init>
440 T* RDSLocal<T, Init>::getCheck() const {
441 assertx(detail::rl_hotSection.rdslocal_base);
442 if (!node().has_value()) {
443 const_cast<RDSLocal*>(this)->create();
445 rehInit();
446 return getNoCheck();
449 template<typename T, Initialize Init>
450 T* RDSLocal<T, Init>::get() const {
451 if (Init == Initialize::Explicitly) {
452 return getNoCheck();
454 return getCheck();
457 template<typename T, Initialize Init>
458 template<typename... Args>
459 void RDSLocal<T, Init>::emplace(Args&&... args) {
460 node().emplace(std::forward<Args>(args) ... );
464 // This aliased rds local type is the same as an rds local, except it also
465 // stores a pointer to the rds storage in the hot rds locals struct. This
466 // saves up to 1 instruction and 1 load per access. g_context serves as an
467 // example for how it is used. It should be used sparingly as it requires
468 // storing a pointer in HotRDSLocals.
469 template<typename T, Initialize Init,
470 void* detail::HotRDSLocals::*ptr>
471 struct AliasedRDSLocal : RDSLocal<T, Init> {
472 void init() override {
473 detail::rl_hotSection.*ptr = &RDSLocal<T, Init>::node();
474 RDSLocal<T, Init>::init();
477 void fini() override {
478 RDSLocal<T, Init>::fini();
479 detail::rl_hotSection.*ptr = nullptr;
482 typename RDSLocal<T, Init>::Node& node() const override {
483 assertx(detail::rl_hotSection.*ptr);
484 return *(typename RDSLocal<T, Init>::Node*)(detail::rl_hotSection.*ptr);
489 ///////////////////////////////////////////////////////////////////////////////
490 // Legacy Request Local Macros (DEPRECATED)
492 // These are implemented using the same structures as above. Please avoid
493 // using these.
495 #define IMPLEMENT_REQUEST_LOCAL(T,f) \
496 ::HPHP::rds::local::RDSLocal<T, ::HPHP::rds::local::Initialize::FirstUse> f
498 #define DECLARE_STATIC_REQUEST_LOCAL(T,f) \
499 static ::HPHP::rds::local::RDSLocal< \
500 T, \
501 ::HPHP::rds::local::Initialize::FirstUse \
504 #define IMPLEMENT_STATIC_REQUEST_LOCAL(T,f) \
505 static ::HPHP::rds::local::RDSLocal< \
506 T, \
507 ::HPHP::rds::local::Initialize::FirstUse \
510 #define DECLARE_EXTERN_REQUEST_LOCAL(T,f) \
511 extern ::HPHP::rds::local::RDSLocal< \
512 T, \
513 ::HPHP::rds::local::Initialize::FirstUse \
518 * Old documentation for request locals:
520 * vvvvvv DEPRECATED vvvvvv
521 * A RequestLocal<T> is automatically thread local, plus it has two handlers
522 * to do extra work on request init and shutdown times. T needs to derive from
523 * RequestEventHandler, so it will register itself with execution engines to
524 * be called at request shutdown time.
526 * Example:
528 * struct MyRequestLocalClass final : RequestEventHandler {
529 * void requestInit() override {...}
530 * void requestShutdown() override {...}
531 * };
532 * IMPLEMENT_STATIC_REQUEST_LOCAL(MyRequestLocalClass, s_data);
534 * How to use the request-local macros:
536 * Use DECLARE_STATIC_REQUEST_LOCAL to declare a *static* class field as
537 * request local:
538 * struct SomeClass {
539 * DECLARE_STATIC_REQUEST_LOCAL(SomeFieldType, f);
542 * Use IMPLEMENT_STATIC_REQUEST_LOCAL in the cpp file to implement the field:
543 * IMPLEMENT_STATIC_REQUEST_LOCAL(SomeFieldType, SomeClass::f);
545 * The DECLARE_REQUEST_LOCAL and IMPLEMENT_REQUEST_LOCAL macros are provided
546 * for declaring/implementing request locals in the global scope.
548 * Remember: *Never* write IMPLEMENT_STATIC_REQUEST_LOCAL in a header file.
549 * ^^^^^^ DEPRECATED ^^^^^^
552 ///////////////////////////////////////////////////////////////////////////////
555 #endif // incl_HPHP_RDS_LOCAL_H_