enabling <coroutine> from std library for folly when c++ version is 20
[hiphop-php.git] / hphp / runtime / test / alias-class.cpp
blob0ecc525288e58ec5ff69b0b2140004d8b9bfb550
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 #include <gtest/gtest.h>
18 #include <boost/range/join.hpp>
19 #include <vector>
21 #include "hphp/runtime/test/test-context.h"
23 #include "hphp/runtime/vm/jit/alias-class.h"
24 #include "hphp/runtime/vm/jit/ir-unit.h"
25 #include "hphp/runtime/vm/jit/memory-effects.h"
26 #include "hphp/runtime/vm/jit/ssa-tmp.h"
28 namespace HPHP::jit {
30 namespace {
32 //////////////////////////////////////////////////////////////////////
34 std::vector<AliasClass> generic_classes() {
35 return {
36 ALocalAny,
37 APropAny,
38 AHeapAny,
39 AStackAny,
40 AElemIAny,
41 AElemSAny,
42 AElemAny,
43 AIterAny,
44 AFContextAny,
45 AFFuncAny,
46 AFMetaAny,
47 AActRecAny
51 std::vector<AliasClass> specialized_classes(IRUnit& unit) {
52 auto const bcctx = BCContext { BCMarker::Dummy(), 0 };
54 // Specialized test cases need some SSATmp*'s and similar things, so let's
55 // make some instructions.
56 auto const mainFP = unit.gen(DefFP, bcctx, DefFPData { std::nullopt })->dst();
58 return {
59 // Frame locals.
60 ALocal { mainFP, 1 },
61 ALocal { mainFP, 2 },
62 ALocal { mainFP, 6 },
64 // Some stack locations.
65 AStack::at(IRSPRelOffset { -1 }),
66 AStack::range(IRSPRelOffset { -4 }, IRSPRelOffset { -1 }),
67 AStack::below(IRSPRelOffset { -11 }),
68 AStack::range(IRSPRelOffset { -15 }, IRSPRelOffset { -11 }),
69 AStack::range(IRSPRelOffset { -13 }, IRSPRelOffset { -10 }),
70 AStack::range(IRSPRelOffset { -61 }, IRSPRelOffset { -51 }),
72 // ActRec locations
73 AFContext { mainFP },
74 AFFunc { mainFP },
75 AFMeta { mainFP },
76 AActRec { mainFP },
82 //////////////////////////////////////////////////////////////////////
84 TEST(AliasClass, AliasIdSet) {
85 constexpr auto Max = AliasIdSet::Max;
86 constexpr auto BitsetMax = AliasIdSet::BitsetMax;
88 EXPECT_TRUE(BitsetMax < 64);
90 AliasIdSet big = BitsetMax + 100;
91 EXPECT_EQ(big.size(), 1);
92 EXPECT_TRUE(big.isBigInteger());
93 EXPECT_FALSE(big.empty());
95 big.unset(BitsetMax);
96 EXPECT_EQ(big.size(), 1);
97 EXPECT_TRUE(big.isBigInteger());
98 EXPECT_FALSE(big.empty());
100 big.set(BitsetMax + 100);
101 EXPECT_EQ(big.size(), 1);
102 EXPECT_TRUE(big.isBigInteger());
103 EXPECT_FALSE(big.empty());
104 EXPECT_TRUE(big.hasSingleValue());
106 big.unset(BitsetMax + 100);
107 EXPECT_EQ(big.size(), 0);
108 EXPECT_TRUE(big.isBitset());
109 EXPECT_TRUE(big.empty());
110 EXPECT_FALSE(big.hasSingleValue());
112 AliasIdSet ids { 0u, 3u, IdRange { 6, 9 }, IdRange { 15, 12 }, BitsetMax };
114 EXPECT_EQ(ids.size(), 6);
115 EXPECT_TRUE(ids.isBitset());
116 EXPECT_FALSE(ids.empty());
117 EXPECT_FALSE(ids.isBigInteger());
118 EXPECT_FALSE(ids.hasUpperRange());
120 EXPECT_TRUE(ids.test(0));
121 EXPECT_TRUE(ids.test(3));
122 EXPECT_FALSE(ids.test(5));
123 EXPECT_TRUE(ids.test(6));
124 EXPECT_TRUE(ids.test(8));
125 EXPECT_FALSE(ids.test(9));
126 EXPECT_FALSE(ids.test(12));
127 EXPECT_FALSE(ids.test(14));
128 EXPECT_FALSE(ids.test(15));
129 EXPECT_TRUE(ids.test(BitsetMax));
130 EXPECT_FALSE(ids.test(BitsetMax + 1));
131 EXPECT_FALSE(ids.test(63));
133 EXPECT_TRUE(ids == (ids | AliasIdSet{}));
134 EXPECT_TRUE(ids == (ids | 6));
135 EXPECT_TRUE(ids == (ids | BitsetMax));
136 EXPECT_TRUE((1000 | ids).test(1000));
138 AliasIdSet unbounded = IdRange { 4 };
140 EXPECT_EQ(unbounded.size(), Max);
141 EXPECT_TRUE(unbounded.isBitset());
142 EXPECT_FALSE(unbounded.empty());
143 EXPECT_FALSE(unbounded.isBigInteger());
144 EXPECT_TRUE(unbounded.hasUpperRange());
146 EXPECT_TRUE(unbounded.test(4));
147 EXPECT_TRUE(unbounded.test(12));
148 EXPECT_TRUE(unbounded.test(61));
149 EXPECT_TRUE(unbounded.test(62));
150 EXPECT_TRUE(unbounded.test(BitsetMax));
151 EXPECT_TRUE(unbounded.test(BitsetMax + 1));
152 EXPECT_TRUE(unbounded.test(64));
153 EXPECT_TRUE(unbounded.test(100));
155 EXPECT_TRUE(ids.maybe(IdRange { 5, 7 }));
156 EXPECT_TRUE(ids.maybe(unbounded));
158 EXPECT_TRUE((ids | unbounded).hasUpperRange());
161 //////////////////////////////////////////////////////////////////////
163 TEST(AliasClass, Basic) {
164 IRUnit unit{test_context};
165 auto const specialized = specialized_classes(unit);
166 auto const generic = generic_classes();
167 auto const joined = boost::join(generic, specialized);
169 for (auto cls : joined) {
170 // Everything is a subclass of AUnknown and intersects AUnknown.
171 EXPECT_TRUE(cls.maybe(AUnknown));
172 EXPECT_TRUE(cls <= AUnknown);
174 // Everything contains AEmpty, but AEmpty can't be anything.
175 EXPECT_TRUE(AEmpty <= cls);
176 EXPECT_FALSE(AEmpty.maybe(cls));
179 // maybe() is a symmetric relation
180 for (auto c1 : joined) {
181 EXPECT_EQ(c1.maybe(AUnknown), AUnknown.maybe(c1));
182 EXPECT_EQ(c1.maybe(AEmpty), AEmpty.maybe(c1));
183 for (auto c2 : joined) {
184 EXPECT_EQ(c1.maybe(c2), c2.maybe(c1));
188 // If one class is a subclass of another, and they aren't equal, the other
189 // isn't a subclass of it.
190 for (auto c1 : joined) {
191 for (auto c2 : joined) {
192 if (c1 != c2) {
193 if (c1 <= c2) {
194 EXPECT_FALSE(c2 <= c1);
200 // == implies <=, and <= implies maybe
201 for (auto c1 : joined) {
202 for (auto c2 : joined) {
203 EXPECT_TRUE(!(c1 == c2) || c1 <= c2);
204 EXPECT_TRUE(!(c1 <= c2) || c1.maybe(c2));
208 // Arguments to operator| are subclasses of the result, and operator| is
209 // commutative.
210 for (auto c1 : joined) {
211 for (auto c2 : joined) {
212 auto const res = c1 | c2;
213 EXPECT_EQ(res, c2 | c1);
214 EXPECT_TRUE(c1 <= res);
215 EXPECT_TRUE(c2 <= res);
220 TEST(AliasClass, StackBasics) {
221 IRUnit unit{test_context};
223 // Some basic canonicalization and maybe.
225 AliasClass const stk = AStack::at(IRSPRelOffset { 0 });
227 EXPECT_TRUE(stk <= AStackAny);
228 EXPECT_TRUE(stk != AStackAny);
230 EXPECT_EQ(stk, stk);
231 EXPECT_TRUE(stk.maybe(stk));
232 EXPECT_TRUE(stk <= stk);
235 // Stack ranges, with subtype and maybe.
237 AliasClass const stk1 = AStack::at(IRSPRelOffset { -10 });
238 AliasClass const stk2 = AStack::range(IRSPRelOffset { -11 },
239 IRSPRelOffset { -9 });
240 EXPECT_TRUE(stk1 <= stk2);
241 EXPECT_TRUE(stk1.maybe(stk2));
243 AliasClass const stk3 = AStack::range(IRSPRelOffset { -14 },
244 IRSPRelOffset { -9 });
245 EXPECT_TRUE(stk1 <= stk3);
246 EXPECT_TRUE(stk1.maybe(stk3));
247 AliasClass const stk4 = AStack::at(IRSPRelOffset { -15 });
248 AliasClass const stk5 = AStack::at(IRSPRelOffset { -14 });
249 // stk4's slot is immediately below stk3's range, but stk5 is the last slot
250 // of its range.
251 EXPECT_FALSE(stk3.maybe(stk4));
252 EXPECT_FALSE(stk3 <= stk4);
253 EXPECT_TRUE(stk5.maybe(stk3));
254 EXPECT_TRUE(stk5 <= stk3);
255 EXPECT_FALSE(stk5.maybe(stk4));
256 EXPECT_FALSE(stk5 <= stk4);
260 TEST(AliasClass, SpecializedUnions) {
261 IRUnit unit{test_context};
263 AliasClass const stk = AStack::range(IRSPRelOffset { -12 },
264 IRSPRelOffset { -9 });
265 AliasClass const unrelated_stk = AStack::at(IRSPRelOffset { -14 });
266 AliasClass const related_stk = AStack::range(IRSPRelOffset { -12 },
267 IRSPRelOffset { -10 });
269 auto const stk_and_frame = stk | ALocalAny;
270 EXPECT_TRUE(!stk_and_frame.is_stack());
271 EXPECT_TRUE(ALocalAny <= stk_and_frame);
272 EXPECT_TRUE(stk <= stk_and_frame);
273 EXPECT_TRUE(AStackAny.maybe(stk_and_frame));
274 EXPECT_TRUE(ALocalAny.maybe(stk_and_frame));
275 EXPECT_FALSE(unrelated_stk <= stk_and_frame);
276 EXPECT_FALSE(stk_and_frame.maybe(unrelated_stk));
278 auto const stk_and_prop = stk | APropAny;
279 EXPECT_TRUE(stk_and_prop.maybe(stk_and_frame));
280 EXPECT_TRUE(stk_and_frame.maybe(stk_and_prop));
281 EXPECT_FALSE(stk_and_prop <= stk_and_frame);
282 EXPECT_FALSE(stk_and_frame <= stk_and_prop);
283 EXPECT_TRUE(APropAny.maybe(stk_and_prop));
284 EXPECT_TRUE(AStackAny.maybe(stk_and_prop));
286 auto const unrelated_stk_and_prop = unrelated_stk | APropAny;
287 EXPECT_FALSE(stk_and_frame.maybe(unrelated_stk_and_prop));
288 EXPECT_FALSE(unrelated_stk_and_prop.maybe(stk_and_frame));
289 EXPECT_TRUE(unrelated_stk_and_prop.maybe(stk_and_prop)); // because of prop
290 EXPECT_FALSE(unrelated_stk_and_prop <= stk_and_prop);
291 EXPECT_FALSE(stk_and_prop <= unrelated_stk_and_prop);
292 EXPECT_FALSE(unrelated_stk_and_prop <= stk_and_frame);
293 EXPECT_FALSE(stk_and_frame <= unrelated_stk_and_prop);
295 EXPECT_FALSE(stk_and_prop <= AHeapAny);
296 EXPECT_TRUE(stk_and_prop.maybe(AHeapAny));
297 EXPECT_FALSE(stk_and_frame <= AHeapAny);
298 EXPECT_FALSE(stk_and_frame.maybe(AHeapAny));
300 auto const rel_stk_and_frame = related_stk | ALocalAny;
301 EXPECT_TRUE(stk_and_frame.maybe(rel_stk_and_frame));
302 EXPECT_TRUE(rel_stk_and_frame.maybe(stk_and_frame));
303 EXPECT_TRUE(related_stk <= stk);
304 EXPECT_TRUE(rel_stk_and_frame <= stk_and_frame);
305 EXPECT_FALSE(stk_and_frame <= rel_stk_and_frame);
306 EXPECT_TRUE(rel_stk_and_frame.maybe(stk_and_prop));
307 EXPECT_TRUE(stk_and_prop.maybe(rel_stk_and_frame));
308 EXPECT_FALSE(rel_stk_and_frame <= stk_and_prop);
310 auto const some_mis = AMIStateTempBase;
312 auto const some_heap = AElemIAny;
313 auto const u1 = some_heap | some_mis;
314 auto const u2 = ALocalAny | u1;
315 EXPECT_TRUE((AHeapAny | some_heap) == AHeapAny);
316 EXPECT_TRUE(AHeapAny <= (AHeapAny | u1));
317 EXPECT_TRUE(AHeapAny <= (AHeapAny | u2));
320 auto const mis_stk = some_mis | stk;
321 auto const mis_stk_any = AStackAny | mis_stk;
323 EXPECT_EQ(some_mis, AliasClass{*mis_stk_any.mis()});
324 EXPECT_NE(mis_stk_any, AStackAny | AMIStateAny);
326 auto const other_mis = AMIStateBase;
328 EXPECT_LE(some_mis, some_mis | other_mis);
329 EXPECT_LE(other_mis, some_mis | other_mis);
331 EXPECT_NE(some_mis, some_mis | other_mis);
332 EXPECT_NE(other_mis, some_mis | other_mis);
335 TEST(AliasClass, StackUnions) {
336 IRUnit unit{test_context};
339 AliasClass const stk1 = AStack::at(IRSPRelOffset { -3 });
340 AliasClass const stk2 = AStack::at(IRSPRelOffset { -4 });
341 AliasClass const stk3 = AStack::at(IRSPRelOffset { -5 });
342 AliasClass const stk12 = AStack::range(IRSPRelOffset { -4 },
343 IRSPRelOffset { -2 });
344 AliasClass const stk23 = AStack::range(IRSPRelOffset { -5 },
345 IRSPRelOffset { -3 });
346 AliasClass const stk13 = AStack::range(IRSPRelOffset { -5 },
347 IRSPRelOffset { -2 });
348 EXPECT_EQ(stk1 | stk2, stk12);
349 EXPECT_EQ(stk2 | stk3, stk23);
350 EXPECT_EQ(stk1 | stk3, stk13);
353 // Same as above but with some other bits.
355 AliasClass const stk1 = AHeapAny | AStack::at(IRSPRelOffset { -3 });
356 AliasClass const stk2 = AHeapAny | AStack::at(IRSPRelOffset { -4 });
357 AliasClass const stk3 = AHeapAny | AStack::at(IRSPRelOffset { -5 });
358 AliasClass const stk12 = AHeapAny | AStack::range(IRSPRelOffset { -4 },
359 IRSPRelOffset { -2 });
360 AliasClass const stk23 = AHeapAny | AStack::range(IRSPRelOffset { -5 },
361 IRSPRelOffset { -3 });
362 AliasClass const stk13 = AHeapAny | AStack::range(IRSPRelOffset { -5 },
363 IRSPRelOffset { -2 });
364 EXPECT_EQ(stk1 | stk2, stk12);
365 EXPECT_EQ(stk2 | stk3, stk23);
366 EXPECT_EQ(stk1 | stk3, stk13);
370 AliasClass const stk1 = AStack::at(IRSPRelOffset { 0 });
371 AliasClass const stk2 = AStack::at(IRSPRelOffset { -2 });
372 AliasClass const true_union = AStack::range(IRSPRelOffset { -2 },
373 IRSPRelOffset { 1 });
374 EXPECT_NE(stk1 | stk2, AStackAny);
375 EXPECT_EQ(stk1 | stk2, true_union);
379 AliasClass const deep_stk1 = AStack::below(IRSPRelOffset { -9 });
380 AliasClass const deep_stk2 = AStack::below(IRSPRelOffset { -13 });
381 EXPECT_EQ(deep_stk1 | deep_stk2, deep_stk1);
385 TEST(AliasClass, IterUnion) {
386 IRUnit unit{test_context};
387 auto const bcctx = BCContext { BCMarker::Dummy(), 0 };
388 auto const FP = unit.gen(DefFP, bcctx, DefFPData { std::nullopt })->dst();
391 AliasClass const iterP0 = aiter_pos(FP, 0);
392 AliasClass const iterP1 = aiter_pos(FP, 1);
393 auto const u1 = iterP0 | iterP1;
394 EXPECT_TRUE(u1 <= AIterAny);
395 EXPECT_TRUE(iterP0 <= u1);
396 EXPECT_TRUE(iterP1 <= u1);
400 AliasClass const iterP0 = aiter_pos(FP, 0);
401 AliasClass const iterE0 = aiter_end(FP, 0);
402 AliasClass const iterP1 = aiter_pos(FP, 1);
404 // All the alias classes should be distinct to start.
405 auto const classes = std::vector{iterP0, iterE0, iterP1};
406 for (auto const cls1 : classes) {
407 for (auto const cls2 : classes) {
408 if (cls1 == cls2) continue;
409 EXPECT_FALSE(cls1 <= cls2);
413 auto const u1 = iterP0 | iterE0;
414 EXPECT_TRUE(iterP0 <= u1);
415 EXPECT_TRUE(iterE0 <= u1);
416 EXPECT_FALSE(iterP1 <= u1);
417 EXPECT_TRUE(u1 <= AIterAny);
419 EXPECT_TRUE(u1.iter());
420 EXPECT_TRUE(u1.is_iter());
422 auto const u2 = iterE0 | iterP1;
423 EXPECT_FALSE(iterP0 <= u2);
424 EXPECT_TRUE(iterE0 <= u2);
425 EXPECT_TRUE(iterP1 <= u2);
426 EXPECT_TRUE(u2 <= AIterAny);
428 EXPECT_TRUE(u1.iter());
429 EXPECT_TRUE(u1.is_iter());
433 AliasClass const local = ALocal { FP, 0 };
434 AliasClass const iter = aiter_pos(FP, 0);
435 auto const u1 = local | iter;
436 EXPECT_TRUE(local <= u1);
437 EXPECT_TRUE(iter <= u1);
438 EXPECT_FALSE(!!u1.is_iter());
439 EXPECT_FALSE(!!u1.is_local());
440 EXPECT_TRUE(!!u1.local()); // locals are preferred in unions to iters
441 EXPECT_FALSE(!!u1.iter());
445 AliasClass const iterP0 = aiter_pos(FP, 0);
446 AliasClass const iterE0 = aiter_end(FP, 0);
447 AliasClass const iterP1 = aiter_pos(FP, 1);
448 AliasClass const iterE1 = aiter_end(FP, 1);
450 EXPECT_FALSE(iterP0.maybe(iterP1));
451 EXPECT_FALSE(iterE0.maybe(iterE1));
453 auto const u1 = iterP0 | iterE0;
454 auto const u2 = iterP1 | iterE1;
455 EXPECT_FALSE(u1 == u2);
456 EXPECT_FALSE(u1.maybe(u2));
457 EXPECT_FALSE(u1 <= u2);
458 EXPECT_FALSE(u2 <= u1);
460 EXPECT_TRUE(iterE1 <= u2);
461 EXPECT_TRUE(iterP1 <= u2);
462 EXPECT_FALSE(iterP0 <= u2);
463 EXPECT_FALSE(iterE0 <= u2);
465 auto const u3 = u1 | iterP1;
466 EXPECT_TRUE(u3.iter());
467 EXPECT_TRUE(iterP1 <= u3);
468 EXPECT_TRUE(iterP0 <= u3);
469 EXPECT_TRUE(iterE0 <= u3);
470 EXPECT_TRUE(u1 <= u3);
471 EXPECT_TRUE(u2.maybe(u3));
473 // u2 <= u3 isn't 'really' true, but operator| is conservative and makes u3
474 // too big for that right now.
475 EXPECT_TRUE(!u1.precise_union(iterP1));
479 TEST(AliasClass, FrameUnion) {
480 IRUnit unit{test_context};
481 auto const bcctx = BCContext { BCMarker::Dummy(), 0 };
482 auto const dsData = DefStackData { SBInvOffset { 0 }, SBInvOffset { 0 } };
483 auto const biData = BeginInliningData {
484 IRSPRelOffset { 0 }, nullptr, 1, SrcKey {}, IRSPRelOffset { 0 },
485 SBInvOffset { 0 }, 0
487 auto const SP = unit.gen(DefRegSP, bcctx, dsData)->dst();
488 auto const FP1 = unit.gen(DefFP, bcctx, DefFPData { std::nullopt })->dst();
489 auto const FP2 = unit.gen(BeginInlining, bcctx, biData, SP)->dst();
491 AliasClass const local1 = ALocal { FP1, 0 };
492 AliasClass const localR = ALocal { FP1, AliasIdSet::IdRange(0, 2) };
493 AliasClass const fctx1 = AFContext { FP1 };
494 AliasClass const fctx2 = AFContext { FP2 };
496 EXPECT_TRUE(local1.maybe(localR));
497 EXPECT_TRUE(local1 <= localR);
498 EXPECT_FALSE(local1.maybe(fctx1));
499 EXPECT_FALSE(localR.maybe(fctx1));
500 EXPECT_TRUE(fctx1.maybe(AFContextAny));
501 EXPECT_TRUE(fctx1.maybe(AActRecAny));
502 EXPECT_TRUE(fctx1.maybe(AActRec{FP1}));
504 auto const u1 = local1 | fctx1;
505 auto const u2 = localR | fctx1;
506 auto const u3 = local1 | fctx2;
507 auto const u4 = u3 | fctx1;
508 auto const u5 = u4 | AMIStateAny;
509 EXPECT_TRUE(u1 <= u2);
510 EXPECT_TRUE(u1.maybe(u2));
511 EXPECT_FALSE(u1 <= localR);
512 EXPECT_FALSE(u1.maybe(fctx2));
513 EXPECT_FALSE(u2.maybe(fctx2));
514 EXPECT_TRUE(u3.local().has_value());
515 EXPECT_FALSE(u3.fcontext().has_value());
516 EXPECT_EQ(u3, u4);
517 EXPECT_TRUE(u5.local().has_value());
518 EXPECT_EQ(u5.local()->frameIdx, local1.local()->frameIdx);
519 EXPECT_EQ(u5.local()->ids, local1.local()->ids);
522 TEST(AliasClass, Pointees) {
523 IRUnit unit{test_context};
524 auto const bcctx = BCContext { BCMarker::Dummy(), 0 };
525 auto ptr = unit.gen(LdMBase, bcctx, TLval, AliasClassData{ ALocalAny | APropAny })->dst();
526 auto const acls = pointee(ptr);
527 EXPECT_EQ(ALocalAny | APropAny, acls);
530 //////////////////////////////////////////////////////////////////////