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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/array-layout.h"
19 #include "hphp/runtime/base/bespoke/layout.h"
20 #include "hphp/runtime/base/bespoke/logging-array.h"
21 #include "hphp/runtime/base/bespoke/logging-profile.h"
22 #include "hphp/runtime/base/bespoke/monotype-dict.h"
23 #include "hphp/runtime/base/bespoke/monotype-vec.h"
24 #include "hphp/runtime/base/bespoke/struct-dict.h"
25 #include "hphp/runtime/base/bespoke-array.h"
26 #include "hphp/runtime/vm/jit/irgen-internal.h"
27 #include "hphp/runtime/vm/jit/prof-data-serialize.h"
28 #include "hphp/runtime/vm/jit/ssa-tmp.h"
30 namespace HPHP
{ namespace jit
{
32 //////////////////////////////////////////////////////////////////////////////
36 using Sort
= ArrayLayout::Sort
;
38 auto constexpr kBasicSortMask
= 0b11;
39 auto constexpr kBasicSortShift
= 0b11;
40 auto constexpr kBasicSortUnshift
= 0b01;
42 // A "basic sort" is just one of the four named Sort enum values. If `sort`
43 // is non-basic, then Sort::Bottom < sort < Sort::Bespoke.
44 constexpr bool isBasicSort(Sort sort
) {
45 return sort
<= Sort::Bespoke
;
48 // Converts non-basic sorts (which are subtypes of Bespoke) to Bespoke.
49 constexpr Sort
toBasicSort(Sort sort
) {
50 return std::min(sort
, Sort::Bespoke
);
53 // If we mask a basic sort, we'll get a value such that | and & bit ops on
54 // that value correspond to | and & type operations on the original sort.
55 constexpr int maskBasicSort(Sort sort
) {
56 assertx(isBasicSort(sort
));
57 return kBasicSortMask
& (int(sort
) + kBasicSortShift
);
60 static_assert(maskBasicSort(Sort::Top
) == 0b11);
61 static_assert(maskBasicSort(Sort::Vanilla
) == 0b01);
62 static_assert(maskBasicSort(Sort::Bespoke
) == 0b10);
63 static_assert(maskBasicSort(Sort::Bottom
) == 0b00);
65 // This operation is the inverse of the maskBasicSort operation above.
66 constexpr Sort
unmaskBasicSort(int masked
) {
67 auto const result
= Sort(kBasicSortMask
& (masked
+ kBasicSortUnshift
));
68 assertx(isBasicSort(result
));
72 static_assert(unmaskBasicSort(maskBasicSort(Sort::Top
)) == Sort::Top
);
73 static_assert(unmaskBasicSort(maskBasicSort(Sort::Vanilla
)) == Sort::Vanilla
);
74 static_assert(unmaskBasicSort(maskBasicSort(Sort::Bespoke
)) == Sort::Bespoke
);
75 static_assert(unmaskBasicSort(maskBasicSort(Sort::Bottom
)) == Sort::Bottom
);
77 // Returns the basic sort that is the intersection of the given basic sorts.
78 constexpr Sort
intersectBasicSort(Sort a
, Sort b
) {
79 return unmaskBasicSort(maskBasicSort(a
) & maskBasicSort(b
));
82 // Returns the basic sort that is the union of the given basic sorts.
83 constexpr Sort
unionBasicSort(Sort a
, Sort b
) {
84 return unmaskBasicSort(maskBasicSort(a
) | maskBasicSort(b
));
87 // Returns the sort (either Bespoke, or non-basic) for this bespoke layout.
88 Sort
sortFromLayoutIndex(bespoke::LayoutIndex index
) {
89 return Sort(index
.raw
+ int(Sort::Bespoke
));
92 const bespoke::Layout
& assertBespoke(ArrayLayout layout
) {
93 auto const result
= layout
.bespokeLayout();
94 assertx(result
!= nullptr);
100 //////////////////////////////////////////////////////////////////////////////
102 ArrayLayout::ArrayLayout(bespoke::LayoutIndex index
)
103 : sort(sortFromLayoutIndex(index
))
105 assertx(bespoke::Layout::FromIndex(*layoutIndex()));
108 ArrayLayout::ArrayLayout(const bespoke::Layout
* layout
)
109 : sort(sortFromLayoutIndex(layout
->index()))
111 assertx(bespoke::Layout::FromIndex(*layoutIndex()));
114 bool ArrayLayout::operator<=(const ArrayLayout
& o
) const {
115 if (*this == o
) return true;
116 if (o
== Top()) return true;
117 if (*this == Bottom()) return true;
119 // The max chain length on basic sorts alone is three:
121 // Bottom < {Vanilla,Bespoke} < Top
123 // We took care of the Bottom, Top, and equality cases above. Further, if o
124 // is non-basic, it's a strict subtype of Bespoke. So we can return here.
125 if (isBasicSort(sort
)) return false;
127 if (isBasicSort(o
.sort
)) return o
== Bespoke();
128 return assertBespoke(*this) <= assertBespoke(o
);
131 ArrayLayout
ArrayLayout::operator|(const ArrayLayout
& o
) const {
132 if (*this == o
) return o
;
133 if (o
== Bottom()) return *this;
134 if (*this == Bottom()) return o
;
136 // If either side is captured as a basic sort, then the result is, too.
137 if (isBasicSort(sort
) || isBasicSort(o
.sort
)) {
138 return ArrayLayout(unionBasicSort(toBasicSort(sort
), toBasicSort(o
.sort
)));
141 return ArrayLayout(assertBespoke(*this) | assertBespoke(o
));
144 ArrayLayout
ArrayLayout::operator&(const ArrayLayout
& o
) const {
145 if (*this == o
) return o
;
146 if (o
== Top()) return *this;
147 if (*this == Top()) return o
;
149 // We only intersect bespoke layouts if toBasicSort is Bespoke for both.
150 auto const meet
= intersectBasicSort(toBasicSort(sort
), toBasicSort(o
.sort
));
151 if (meet
!= Sort::Bespoke
) return ArrayLayout(meet
);
153 // If either type is Bespoke (i.e. "bespoke top"), return the other type.
154 if (o
== Bespoke()) return *this;
155 if (*this == Bespoke()) return o
;
156 auto const result
= assertBespoke(*this) & assertBespoke(o
);
157 return result
? ArrayLayout(result
) : Bottom();
160 bool ArrayLayout::logging() const {
161 auto const index
= layoutIndex();
162 return index
&& *index
== bespoke::LoggingArray::GetLayoutIndex();
165 bool ArrayLayout::monotype() const {
166 auto const index
= layoutIndex();
167 if (!index
) return false;
168 return bespoke::isMonotypeVecLayout(*index
) ||
169 bespoke::isMonotypeDictLayout(*index
);
172 bool ArrayLayout::is_struct() const {
173 auto const index
= layoutIndex();
174 return index
&& bespoke::StructLayout::IsStructLayout(*index
);
177 bool ArrayLayout::is_concrete() const {
178 auto const layout
= bespokeLayout();
179 return layout
&& layout
->isConcrete();
182 const bespoke::Layout
* ArrayLayout::bespokeLayout() const {
183 auto const index
= layoutIndex();
184 if (!index
) return nullptr;
185 return bespoke::Layout::FromIndex(*index
);
188 Optional
<bespoke::LayoutIndex
> ArrayLayout::layoutIndex() const {
189 auto const index
= int(sort
) - int(Sort::Bespoke
);
190 if (index
< 0) return {};
191 return bespoke::LayoutIndex
{ safe_cast
<uint16_t>(index
) };
194 LayoutTest
ArrayLayout::bespokeLayoutTest() const {
195 assertx(!isBasicSort(sort
));
196 auto const& layout
= assertBespoke(*this);
197 return layout
.getLayoutTest();
200 const bespoke::Layout
* ArrayLayout::irgenLayout() const {
201 auto const index
= std::max(int(sort
) - int(Sort::Bespoke
), 0);
202 return bespoke::Layout::FromIndex({safe_cast
<uint16_t>(index
)});
205 std::string
ArrayLayout::describe() const {
206 if (isBasicSort(sort
)) {
208 case Sort::Top
: return "Top";
209 case Sort::Vanilla
: return "Vanilla";
210 case Sort::Bespoke
: return "Bespoke";
211 case Sort::Bottom
: return "Bottom";
214 return assertBespoke(*this).describe();
217 ArrayData
* ArrayLayout::apply(ArrayData
* ad
) const {
218 assertx(ad
->isStatic());
219 assertx(ad
->isVanilla());
221 auto const result
= [&]() -> ArrayData
* {
222 if (vanilla() || logging()) return ad
;
223 return bespokeLayout()->coerce(ad
);
226 SCOPE_ASSERT_DETAIL("ArrayLayout::apply") { return describe(); };
227 always_assert(result
!= nullptr);
231 //////////////////////////////////////////////////////////////////////////////
233 ArrayLayout
ArrayLayout::appendType(Type val
) const {
234 if (vanilla()) return ArrayLayout::Vanilla();
235 if (isBasicSort(sort
)) return ArrayLayout::Top();
236 return bespokeLayout()->appendType(val
);
239 ArrayLayout
ArrayLayout::removeType(Type key
) const {
240 if (vanilla()) return ArrayLayout::Vanilla();
241 if (isBasicSort(sort
)) return ArrayLayout::Top();
242 return bespokeLayout()->removeType(key
);
245 ArrayLayout
ArrayLayout::setType(Type key
, Type val
) const {
246 if (vanilla()) return ArrayLayout::Vanilla();
247 if (isBasicSort(sort
)) return ArrayLayout::Top();
248 return bespokeLayout()->setType(key
, val
);
251 std::pair
<Type
, bool> ArrayLayout::elemType(Type key
) const {
252 if (isBasicSort(sort
)) return {TInitCell
, false};
253 return bespokeLayout()->elemType(key
);
256 std::pair
<Type
, bool> ArrayLayout::firstLastType(
257 bool isFirst
, bool isKey
) const {
258 if (isBasicSort(sort
)) return {isKey
? (TInt
| TStr
) : TInitCell
, false};
259 return bespokeLayout()->firstLastType(isFirst
, isKey
);
262 Type
ArrayLayout::iterPosType(Type pos
, bool isKey
) const {
263 if (isBasicSort(sort
)) return isKey
? (TInt
| TStr
) : TInitCell
;
264 return bespokeLayout()->iterPosType(pos
, isKey
);
267 //////////////////////////////////////////////////////////////////////////////
271 using bespoke::LoggingProfileKey
;
272 using bespoke::SinkProfileKey
;
273 using bespoke::StructLayout
;
274 using FieldVector
= bespoke::StructLayout::FieldVector
;
276 void write_field_vector(ProfDataSerializer
& ser
, const StructLayout
* layout
) {
277 auto const num
= layout
->numFields();
279 for (auto slot
= 0; slot
< num
; ++slot
) {
280 auto const& f
= layout
->field(slot
);
281 write_string(ser
, f
.key
);
282 write_raw(ser
, f
.required
);
283 write_raw(ser
, f
.type_mask
);
287 FieldVector
read_field_vector(ProfDataDeserializer
& des
) {
288 auto data
= FieldVector
{};
289 auto const num
= read_raw
<size_t>(des
);
290 for (auto i
= 0; i
< num
; i
++) {
291 auto const key
= read_string(des
);
292 auto const required
= read_raw
<bool>(des
);
293 auto const type_mask
= read_raw
<uint8_t>(des
);
294 data
.push_back({key
, required
, type_mask
});
299 void write_source_key(ProfDataSerializer
& ser
, const LoggingProfileKey
& key
) {
300 write_raw(ser
, key
.locationType
);
301 switch (key
.locationType
) {
302 case bespoke::LocationType::SrcKey
:
303 write_srckey(ser
, key
.sk
);
305 case bespoke::LocationType::APCKey
:
306 write_raw(ser
, key
.ak
);
308 case bespoke::LocationType::InstanceProperty
:
309 case bespoke::LocationType::StaticProperty
:
310 write_class(ser
, key
.cls
);
311 write_raw(ser
, key
.slot
);
313 case bespoke::LocationType::Runtime
:
314 write_string(ser
, key
.runtimeStruct
->getStableIdentifier());
316 default: always_assert(false);
320 LoggingProfileKey
read_source_key(ProfDataDeserializer
& des
) {
321 LoggingProfileKey
key(SrcKey
{});
322 read_raw(des
, key
.locationType
);
323 switch (key
.locationType
) {
324 case bespoke::LocationType::SrcKey
:
325 key
.sk
= read_srckey(des
);
327 case bespoke::LocationType::APCKey
:
328 read_raw(des
, key
.ak
);
330 case bespoke::LocationType::InstanceProperty
:
331 case bespoke::LocationType::StaticProperty
:
332 key
.cls
= read_class(des
);
333 read_raw(des
, key
.slot
);
335 case bespoke::LocationType::Runtime
: {
336 auto const stableIdentifier
= read_string(des
);
337 key
.runtimeStruct
= RuntimeStruct::findById(stableIdentifier
);
340 default: always_assert(false);
345 void write_sink_key(ProfDataSerializer
& ser
, const SinkProfileKey
& key
) {
346 write_raw(ser
, key
.first
);
347 write_srckey(ser
, key
.second
);
350 SinkProfileKey
read_sink_key(ProfDataDeserializer
& des
) {
351 auto const trans
= read_raw
<TransID
>(des
);
352 return SinkProfileKey(trans
, read_srckey(des
));
355 void write_sink_layout(ProfDataSerializer
& ser
, bespoke::SinkLayout sl
) {
356 write_raw(ser
, sl
.layout
);
357 write_raw(ser
, sl
.sideExit
);
360 bespoke::SinkLayout
read_sink_layout(ProfDataDeserializer
& des
) {
361 auto result
= bespoke::SinkLayout
{};
362 result
.layout
= read_layout(des
);
363 read_raw(des
, result
.sideExit
);
369 struct RuntimeStructSerde
{
370 static void write_runtime_struct(
371 ProfDataSerializer
& ser
, const RuntimeStruct
* runtimeStruct
) {
372 write_string(ser
, runtimeStruct
->m_stableIdentifier
);
374 write_raw(ser
, runtimeStruct
->m_fields
.size());
375 for (auto const key
: runtimeStruct
->m_fields
) {
377 write_raw(ser
, true);
378 write_string(ser
, key
);
380 write_raw(ser
, false);
384 auto const layout
= runtimeStruct
->m_assignedLayout
.load();
386 write_raw(ser
, true);
387 write_raw(ser
, layout
->index());
389 write_raw(ser
, false);
393 static void deserialize_runtime_struct(ProfDataDeserializer
& des
) {
394 auto const stableIdentifier
= read_string(des
);
395 auto const fieldSize
= read_raw
<size_t>(des
);
397 auto fields
= RuntimeStruct::FieldKeys(fieldSize
, nullptr);
398 for (int i
= 0; i
< fieldSize
; i
++) {
399 auto const present
= read_raw
<bool>(des
);
400 if (present
) fields
[i
] = read_string(des
);
403 auto const runtimeStruct
=
404 RuntimeStruct::deserialize(stableIdentifier
, std::move(fields
));
406 auto const hasLayout
= read_raw
<bool>(des
);
408 auto const layoutIdx
= read_raw
<bespoke::LayoutIndex
>(des
);
409 auto const layout
= bespoke::Layout::FromIndex(layoutIdx
);
410 runtimeStruct
->applyLayout(bespoke::StructLayout::As(layout
));
415 void serializeBespokeLayouts(ProfDataSerializer
& ser
) {
416 // For now, we only need to serialize and deserialize StructLayouts,
417 // because they are the only dynamically-constructed layouts.
418 std::vector
<const StructLayout
*> layouts
;
419 bespoke::eachLayout([&](auto const& layout
) {
420 if (!layout
.isConcrete() || !ArrayLayout(&layout
).is_struct()) return;
421 layouts
.push_back(StructLayout::As(&layout
));
423 write_raw(ser
, layouts
.size());
424 for (auto const layout
: layouts
) {
425 write_raw(ser
, layout
->index());
426 write_field_vector(ser
, layout
);
429 // Serialize the runtime sources.
430 std::vector
<const RuntimeStruct
*> runtimeStructs
;
431 RuntimeStruct::eachRuntimeStruct([&](RuntimeStruct
* runtimeStruct
) {
432 runtimeStructs
.push_back(runtimeStruct
);
434 write_raw(ser
, runtimeStructs
.size());
435 for (auto const runtimeStruct
: runtimeStructs
) {
436 RuntimeStructSerde::write_runtime_struct(ser
, runtimeStruct
);
439 // Serialize the decisions we made at all sources and sinks.
440 write_raw(ser
, bespoke::countSources());
441 bespoke::eachSource([&](auto const& profile
) {
442 write_source_key(ser
, profile
.key
);
443 write_raw(ser
, profile
.getLayout());
445 write_raw(ser
, bespoke::countSinks());
446 bespoke::eachSink([&](auto const& profile
) {
447 write_sink_key(ser
, profile
.key
);
448 write_sink_layout(ser
, profile
.getLayout());
452 void deserializeBespokeLayouts(ProfDataDeserializer
& des
) {
453 always_assert(bespoke::countSources() == 0);
454 always_assert(bespoke::countSinks() == 0);
455 bespoke::setLoggingEnabled(false);
456 always_assert(bespoke::countSources() == 0);
457 always_assert(bespoke::countSinks() == 0);
459 auto const layouts
= read_raw
<size_t>(des
);
460 for (auto i
= 0; i
< layouts
; i
++) {
461 auto const index
= read_raw
<bespoke::LayoutIndex
>(des
);
462 auto const fields
= read_field_vector(des
);
463 auto const layout
= StructLayout::Deserialize(index
, fields
);
464 layout
->createColoringHashMap();
466 auto const runtimeStructs
= read_raw
<size_t>(des
);
467 for (auto i
= 0; i
< runtimeStructs
; i
++) {
468 RuntimeStructSerde::deserialize_runtime_struct(des
);
470 auto const sources
= read_raw
<size_t>(des
);
471 for (auto i
= 0; i
< sources
; i
++) {
472 assertx(bespoke::countSources() == i
);
473 auto const key
= read_source_key(des
);
474 bespoke::deserializeSource(key
, read_layout(des
));
475 assertx(bespoke::countSources() == i
+ 1);
477 auto const sinks
= read_raw
<size_t>(des
);
478 for (auto i
= 0; i
< sinks
; i
++) {
479 assertx(bespoke::countSinks() == i
);
480 auto const key
= read_sink_key(des
);
481 bespoke::deserializeSink(key
, read_sink_layout(des
));
482 assertx(bespoke::countSinks() == i
+ 1);
484 bespoke::Layout::FinalizeHierarchy();
487 //////////////////////////////////////////////////////////////////////////////