Allow wide tv_val and arbitrary layout for object-data properties
[hiphop-php.git] / hphp / runtime / base / tv-val.h
blobea2d49dd1c58b13b055d9025d2e96b324723420d
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 #pragma once
19 #include "hphp/runtime/base/datatype.h"
20 #include "hphp/runtime/base/typed-value.h"
22 #include "hphp/util/compact-tagged-ptrs.h"
23 #include "hphp/util/compilation-flags.h"
25 #include <cstddef>
26 #include <type_traits>
28 namespace HPHP {
30 namespace tv_val_detail {
32 * These structs are used to add dummy() and is_dummy() functions to tv_rval
33 * only.
35 struct empty {};
37 template<typename T>
38 struct with_dummy {
40 * The canonical non-null "missing" rval. Only valid for tv_rval (is_const ==
41 * true). These are actually defined in tv_val_detail::with_dummy; see above.
43 * Some users of tv_rval prefer to use a dummy rval-to-Uninit to represent a
44 * missing element, instead of a nullptr rval, so that tv() is always valid.
45 * These functions provide and test for such a value.
47 * static tv_val dummy();
48 * bool is_dummy() const;
50 INLINE_FLATTEN static T dummy() { return T { &immutable_uninit_base }; }
51 INLINE_FLATTEN bool is_dummy() const {
52 return static_cast<const T&>(*this) == dummy();
56 template<typename T>
57 INLINE_FLATTEN T* get_ptr(T* ptr) {
58 return ptr;
61 template<typename T, typename Tag>
62 INLINE_FLATTEN T* get_ptr(CompactTaggedPtr<T, Tag> ptr) {
63 return ptr.ptr();
68 * NOTE: You probably do not want to use tv_val directly. You should instead
69 * use tv_lval or tv_rval, for mutable or constant pointees, respectively.
71 * tv_val is a thin wrapper around the concept of a pointer to a
72 * TypedValue. For now, it contains a normal TypedValue*, but in the future we
73 * will explore different memory layout options that will likely require
74 * passing around separate pointers to the value and type of a TypedValue. The
75 * goal of tv_val is to replace TypedValue* in as much code as possible, so
76 * these alternate layout options can be explored with minimal disruption.
78 * Like all pointers, tv_val is nullable/optional. The presence of a value can
79 * be detected via is_set(), explicit cast to a bool, or comparison with
80 * nullptr.
82 * If tag_t is non-void, CompactTaggedPtr will be used internally to store a
83 * tag. This has no space overhead, but has a slight penalty at runtime.
85 template<bool is_const, typename tag_t = void>
86 struct tv_val : std::conditional<is_const,
87 tv_val_detail::with_dummy<tv_val<true>>,
88 tv_val_detail::empty>::type {
89 private:
90 template<typename T> using maybe_const_t =
91 typename std::conditional<is_const, const T, T>::type;
92 template<typename T, typename R = T> using with_tag_t =
93 typename std::enable_if<!std::is_same<T, void>::value, R>::type;
95 public:
96 using value_t = maybe_const_t<Value>;
97 using type_t = maybe_const_t<DataType>;
98 using tv_t = maybe_const_t<TypedValue>;
101 * These values expose details about the internal representation of a tv_val,
102 * and should only be inspected while generating code that works with
103 * tv_vals.
105 * If you do change these, you must also update the return registers used in
106 * runtime/base/hash-table-*.S.
108 static constexpr int type_idx = wide_tv_val ? 0 : -1;
109 static constexpr int val_idx = wide_tv_val ? 1 : 0;
111 INLINE_FLATTEN tv_val();
112 /* implicit */ INLINE_FLATTEN tv_val(std::nullptr_t);
113 /* implicit */ INLINE_FLATTEN tv_val(tv_t* lval);
114 INLINE_FLATTEN tv_val(type_t* type, value_t* val);
117 * Construct from a tv_val without a tag and a tag.
119 template<typename Tag = tag_t>
120 INLINE_FLATTEN tv_val(tv_val<is_const> lval, with_tag_t<Tag> t);
122 INLINE_FLATTEN bool operator==(tv_val other) const;
123 INLINE_FLATTEN bool operator!=(tv_val other) const;
126 * Whether this tv_val is set.
128 INLINE_FLATTEN bool is_set() const;
129 INLINE_FLATTEN explicit operator bool() const;
130 INLINE_FLATTEN bool operator==(std::nullptr_t) const;
131 INLINE_FLATTEN bool operator!=(std::nullptr_t) const;
134 * Implicit cast to tv_rval.
136 /* implicit */ INLINE_FLATTEN operator tv_val<true>() const;
139 * Explicit cast to tv_lval.
141 * This is the moral equivalent of:
142 * const_cast<TypedValue*>(const TypedValue*)
144 INLINE_FLATTEN tv_val<false> as_lval() const;
147 * References to the value and type.
149 * @requires: is_set()
151 INLINE_FLATTEN value_t& val() const;
152 INLINE_FLATTEN type_t& type() const;
155 * Get a copy of the referenced value and type as a TypedValue.
157 * @requires: is_set()
159 INLINE_FLATTEN TypedValue tv() const;
160 INLINE_FLATTEN TypedValue operator*() const;
162 template<typename Tag = tag_t>
163 INLINE_FLATTEN with_tag_t<Tag> tag() const;
165 template<typename Tag = tag_t>
166 INLINE_FLATTEN with_tag_t<Tag, tv_val<is_const>> drop_tag() const;
168 TYPE_SCAN_CUSTOM() {
169 if (isRefcountedType(type())) scanner.scan(val().pcnt);
172 private:
173 template<bool, typename> friend struct tv_val;
175 template<typename T>
176 using maybe_tagged_t = std::conditional_t<
177 std::is_same<tag_t, void>::value, T*, CompactTaggedPtr<T, tag_t>
181 * Default storage type: a single TypedValue*.
183 struct storage {
184 INLINE_FLATTEN storage()
185 : m_tv{} {}
187 INLINE_FLATTEN storage(type_t* type, value_t* val)
188 : m_tv{reinterpret_cast<tv_t*>(val)}
190 assertx(val == nullptr || &m_tv->m_type == type);
193 template<typename Tag = tag_t>
194 INLINE_FLATTEN storage(type_t* type, value_t* val, with_tag_t<Tag> tag)
195 : m_tv{tag, reinterpret_cast<tv_t*>(val)}
197 assertx(val == nullptr || &m_tv->m_type == type);
200 INLINE_FLATTEN bool operator==(const storage& o) const {
201 return m_tv == o.m_tv;
204 INLINE_FLATTEN bool operator!=(const storage& o) const {
205 return m_tv != o.m_tv;
208 INLINE_FLATTEN type_t* type() const { return &m_tv->m_type; }
209 INLINE_FLATTEN value_t* val() const { return &m_tv->m_data; }
210 INLINE_FLATTEN bool is_set() const { return static_cast<bool>(m_tv); }
212 template<typename Tag = tag_t>
213 INLINE_FLATTEN with_tag_t<Tag> tag() const { return m_tv.tag(); }
215 private:
216 maybe_tagged_t<tv_t> m_tv;
220 * Wide storage type: separate pointers for the type and the value. m_type is
221 * only meangingful is m_val != nullptr.
223 struct wide_storage {
224 INLINE_FLATTEN wide_storage()
225 : m_type{}, m_val{} {}
227 INLINE_FLATTEN wide_storage(type_t* type, value_t* val)
228 : m_type{type}
229 , m_val{val}
231 assertx((type && val) || val == nullptr);
234 template<typename Tag = tag_t>
235 INLINE_FLATTEN wide_storage(type_t* type, value_t* val, with_tag_t<Tag> tag)
236 : m_type{tag, type}
237 , m_val{val}
239 assertx((type && val) || val == nullptr);
242 INLINE_FLATTEN bool operator==(const wide_storage& o) const {
243 return m_val == o.m_val && (m_type == o.m_type || m_val == nullptr);
246 INLINE_FLATTEN bool operator!=(const wide_storage& o) const {
247 return !operator==(o);
250 INLINE_FLATTEN type_t* type() const {
251 return tv_val_detail::get_ptr(m_type);
253 INLINE_FLATTEN value_t* val() const { return m_val; }
254 INLINE_FLATTEN bool is_set() const { return m_val; }
256 template<typename Tag = tag_t>
257 INLINE_FLATTEN with_tag_t<Tag> tag() const { return m_type.tag(); }
259 private:
260 maybe_tagged_t<type_t> m_type;
261 value_t* m_val;
264 using storage_t = std::conditional_t<wide_tv_val, wide_storage, storage>;
265 storage_t m_s;
269 * TV-lval API for tv_val.
271 template<bool is_const>
272 INLINE_FLATTEN auto& type(const tv_val<is_const>& val) { return val.type(); }
273 template<bool is_const>
274 INLINE_FLATTEN auto& val(const tv_val<is_const>& val) { return val.val(); }
275 template<bool is_const>
276 INLINE_FLATTEN TypedValue as_tv(const tv_val<is_const>& val) {
277 return val.tv();
280 ///////////////////////////////////////////////////////////////////////////////
282 using tv_lval = tv_val<false>;
283 using tv_rval = tv_val<true>;
285 ///////////////////////////////////////////////////////////////////////////////
287 namespace detail {
289 /* representation of a tv_val_offset when wide mode is on */
290 struct tv_val_offset_wide {
291 tv_val_offset_wide(ptrdiff_t tv_offset)
292 : type_offset(tv_offset + offsetof(TypedValue, m_type))
293 , data_offset(tv_offset + offsetof(TypedValue, m_data)) {}
294 tv_val_offset_wide(ptrdiff_t type_offset, ptrdiff_t data_offset)
295 : type_offset(type_offset)
296 , data_offset(data_offset) {}
298 ptrdiff_t typeOffset() const { return type_offset; }
299 ptrdiff_t dataOffset() const { return data_offset; }
301 tv_val_offset_wide shift(ptrdiff_t off) const {
302 return {
303 type_offset + off,
304 data_offset + off
308 /* extract a tv_val from a given base address */
309 tv_lval apply(char* base) const {
310 return tv_lval {
311 reinterpret_cast<DataType*>(base + typeOffset()),
312 reinterpret_cast<Value*>(base + dataOffset())
316 tv_rval apply(const char* base) const {
317 return tv_rval {
318 reinterpret_cast<const DataType*>(base + typeOffset()),
319 reinterpret_cast<const Value*>(base + dataOffset())
323 ptrdiff_t type_offset;
324 ptrdiff_t data_offset;
327 /* representation of a tv_val_offset when wide mode is off */
328 struct tv_val_offset_nonwide {
329 tv_val_offset_nonwide(ptrdiff_t offset) : offset(offset) {}
330 tv_val_offset_nonwide(ptrdiff_t type_offset, ptrdiff_t data_offset)
331 : offset(data_offset) {
332 static_assert(offsetof(TypedValue, m_data) == 0, "");
333 assertx(type_offset == data_offset + offsetof(TypedValue, m_type));
336 ptrdiff_t typeOffset() const { return offset + offsetof(TypedValue, m_type); }
337 ptrdiff_t dataOffset() const { return offset + offsetof(TypedValue, m_data); }
339 /* extract a tv_val from a given base address */
340 tv_lval apply(char* base) {
341 return reinterpret_cast<TypedValue*>(base + dataOffset());
344 tv_rval apply(const char* base) {
345 return reinterpret_cast<const TypedValue*>(base + dataOffset());
348 tv_val_offset_nonwide shift(ptrdiff_t off) const {
349 return {offset + off};
352 ptrdiff_t offset;
355 } // namespace detail
357 using tv_val_offset = std::conditional<wide_tv_val,
358 detail::tv_val_offset_wide,
359 detail::tv_val_offset_nonwide>::type;
363 #include "hphp/runtime/base/tv-val-inl.h"