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 #ifndef incl_HPHP_PACKED_BLOCK_H_
17 #define incl_HPHP_PACKED_BLOCK_H_
21 #include <sys/types.h>
23 #include "hphp/runtime/base/array-common.h"
24 #include "hphp/runtime/base/array-data.h"
25 #include "hphp/runtime/base/data-walker.h"
26 #include "hphp/runtime/base/header-kind.h"
27 #include "hphp/runtime/base/memory-manager.h"
28 #include "hphp/runtime/base/sort-flags.h"
29 #include "hphp/runtime/base/tv-val.h"
30 #include "hphp/runtime/base/typed-value.h"
31 #include "hphp/runtime/base/vanilla-vec.h"
33 #include "hphp/util/type-scan.h"
37 //////////////////////////////////////////////////////////////////////
40 * If VanillaVec::stores_unaligned_typed_values is false, the entries of a VanillaVec
41 * will be stored in a PackedBlock format. Each block consists of 8 DataTypes
42 * followed by 8 Values, for a total size of 72 bytes.
44 * The extra types and values in the last PackedBlock are uninitialized.
46 * The easiest way to access entries in this layout is to use LvalAt. However,
47 * if you are iterating over the array, you may want to use a pair of nested
48 * loops to iterate first over blocks and second over items in the block.
50 struct PackedBlock final
{
51 static_assert(sizeof(Value
) == 8);
52 static_assert(sizeof(DataType
) == 1);
53 static constexpr size_t kNumItems
= 8;
54 static constexpr size_t kByteSize
= kNumItems
* 9;
56 // Each block contains up to 8 items, so i must be less than 8 here.
57 tv_lval
operator[](size_t i
);
58 bool operator<(const PackedBlock
& other
);
60 // Advance to the next block.
61 PackedBlock
& operator++();
62 PackedBlock
operator+(int64_t i
);
63 PackedBlock
& operator+=(int64_t i
);
65 // For the fast type predicates, n must be the number of items in the block.
66 // In particular, it must be greater than 0 and less than or equal to 8.
67 bool AllTypesEqual(size_t n
);
68 bool AllTypesMatch(size_t n
, DataType expected
);
69 bool AllTypesAreUncounted(size_t n
);
71 // Get a pointer to a given block by index in a packed array.
72 static PackedBlock
BlockAt(ArrayData
* ad
, size_t b
);
73 static tv_lval
LvalAt(ArrayData
* ad
, size_t k
);
75 // Get the type and data offsets for an known index. Used by the JIT.
76 static VanillaVec::EntryOffset
EntryOffset(size_t i
);
78 // Turn a pointer in an array into the index it points to. -1 on failure.
79 static int64_t PointerToIndex(const ArrayData
* ad
, const void* ptr
);
81 // Always points to the 8-byte type block at the start of a block.
85 //////////////////////////////////////////////////////////////////////
87 constexpr uint64_t kTypecheckEqualityMasks
[8] = {
98 constexpr uint64_t kTypecheckRefcountMasks
[8] = {
109 ALWAYS_INLINE tv_lval
PackedBlock::operator[](size_t i
) {
110 assertx(i
< kNumItems
);
111 auto* type
= reinterpret_cast<DataType
*>(this->m_base
);
112 return tv_lval(type
+ i
, this->m_base
+ i
+ 1);
115 ALWAYS_INLINE
bool PackedBlock::operator<(const PackedBlock
& other
) {
116 return this->m_base
< other
.m_base
;
119 ALWAYS_INLINE PackedBlock
& PackedBlock::operator++() {
123 ALWAYS_INLINE PackedBlock
PackedBlock::operator+(int64_t i
) {
124 return PackedBlock
{ this->m_base
+ 9 * i
};
127 ALWAYS_INLINE PackedBlock
& PackedBlock::operator+=(int64_t i
) {
128 this->m_base
+= 9 * i
;
132 ALWAYS_INLINE
bool PackedBlock::AllTypesEqual(size_t n
) {
133 assertx(0 < n
&& n
<= kNumItems
);
134 auto const types
= *reinterpret_cast<uint64_t*>(this->m_base
);
135 return ((types
^ (types
>> 8)) & kTypecheckEqualityMasks
[n
- 1]) == 0;
138 ALWAYS_INLINE
bool PackedBlock::AllTypesMatch(size_t n
, DataType type
) {
139 assertx(0 < n
&& n
<= kNumItems
);
140 return AllTypesEqual(n
) && *reinterpret_cast<DataType
*>(m_base
) == type
;
143 ALWAYS_INLINE
bool PackedBlock::AllTypesAreUncounted(size_t n
) {
144 assertx(0 < n
&& n
<= kNumItems
);
145 auto const types
= *reinterpret_cast<uint64_t*>(this->m_base
);
146 return (types
& kTypecheckRefcountMasks
[n
- 1]) == 0;
149 ALWAYS_INLINE PackedBlock
PackedBlock::BlockAt(ArrayData
* ad
, size_t b
) {
150 assertx(!VanillaVec::stores_unaligned_typed_values
);
151 return PackedBlock
{ reinterpret_cast<Value
*>(ad
+ 1) } + b
;
154 ALWAYS_INLINE tv_lval
PackedBlock::LvalAt(ArrayData
* ad
, size_t k
) {
155 // Given an index k = x + y, where y = k % 8, we want to index into ad at:
156 // type: ad + VanillaVec::entriesOffset + 9 * x + y
157 // data: ad + VanillaVec::entriesOffset + 9 * x + 8 * y + 8
159 // One way to compute these offsets is to compute the "base block pointer"
160 // at ad + VanillaVec::entriesOffset + 9 * x, then use that to compute the
161 // two pointers. This approach uses 6 arithmetic instructions:
165 // z = x + 8 * x # LEA
166 // w = ad + z + eo # LEA
168 // d = w + 8 * y + 8 # LEA
170 // We can save an instruction by avoiding computing y, as follows:
173 // a = ad + 8 * x + eo # LEA
174 // b = ad + 8 * k + eo # LEA
176 // d = b + x + 8 # LEA
178 // This method implements the optimization above and checks it against the
179 // simpler computation using the PackedBlock API.
181 assertx(!VanillaVec::stores_unaligned_typed_values
);
182 auto const x
= k
& size_t(-8);
183 auto const a
= reinterpret_cast<char*>(ad
+ 1) + 8 * x
;
184 auto const b
= reinterpret_cast<char*>(ad
+ 1) + 8 * k
;
185 auto const lval
= tv_lval(
186 reinterpret_cast<DataType
*>(a
+ k
),
187 reinterpret_cast<Value
*>(b
+ x
+ 8)
189 assertx(lval
== PackedBlock::BlockAt(ad
, k
/ 8)[k
& 7]);
193 VanillaVec::EntryOffset
PackedBlock::EntryOffset(size_t i
) {
194 assertx(!VanillaVec::stores_unaligned_typed_values
);
195 auto const div
= i
/ kNumItems
;
196 auto const mod
= i
% kNumItems
;
197 auto const base
= sizeof(ArrayData
) + kByteSize
* div
;
198 return {ptrdiff_t(base
+ sizeof(DataType
) * mod
),
199 ptrdiff_t(base
+ sizeof(DataType
) * kNumItems
+ sizeof(Value
) * mod
)};
202 int64_t PackedBlock::PointerToIndex(const ArrayData
* ad
, const void* ptr
) {
203 // We have three cases to handle here:
204 // 1. ptr is before the first array entry.
205 // 2. ptr points into some block's 8-byte DataType prefix.
206 // 2. ptr points into some block's 64-byte Value suffix.
207 assertx(!VanillaVec::stores_unaligned_typed_values
);
208 auto const base
= reinterpret_cast<const char*>(ad
+ 1);
209 auto const diff
= reinterpret_cast<const char*>(ptr
) - base
;
210 auto const div
= diff
/ kByteSize
;
211 auto const mod
= diff
% kByteSize
;
212 return diff
< 0 ? -1 : mod
< 8 ? 8 * div
+ mod
: 8 * div
+ mod
/ 8 - 1;