Delete StructArray and Shape
[hiphop-php.git] / hphp / runtime / vm / jit / simplify.cpp
blob5f5f30f94bde825007a5be670ebeda9ff4024390
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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/simplify.h"
19 #include <limits>
20 #include <sstream>
21 #include <type_traits>
23 #include "hphp/util/overflow.h"
24 #include "hphp/util/trace.h"
26 #include "hphp/runtime/base/array-data-defs.h"
27 #include "hphp/runtime/base/comparisons.h"
28 #include "hphp/runtime/base/packed-array.h"
29 #include "hphp/runtime/base/repo-auth-type-array.h"
30 #include "hphp/runtime/base/type-conversions.h"
31 #include "hphp/runtime/vm/hhbc.h"
32 #include "hphp/runtime/vm/jit/analysis.h"
33 #include "hphp/runtime/vm/jit/containers.h"
34 #include "hphp/runtime/vm/jit/ir-builder.h"
35 #include "hphp/runtime/vm/jit/minstr-effects.h"
36 #include "hphp/runtime/vm/jit/punt.h"
37 #include "hphp/runtime/vm/jit/type.h"
38 #include "hphp/runtime/vm/runtime.h"
40 namespace HPHP { namespace jit {
42 TRACE_SET_MOD(simplify);
44 //////////////////////////////////////////////////////////////////////
46 namespace {
48 //////////////////////////////////////////////////////////////////////
50 const StaticString s_Array("Array");
51 const StaticString s_isEmpty("isEmpty");
52 const StaticString s_count("count");
53 const StaticString s_1("1");
54 const StaticString s_empty("");
55 const StaticString s_invoke("__invoke");
56 const StaticString s_isFinished("isFinished");
57 const StaticString s_isSucceeded("isSucceeded");
58 const StaticString s_isFailed("isFailed");
59 const StaticString s_WaitHandle("HH\\WaitHandle");
61 //////////////////////////////////////////////////////////////////////
63 struct State {
64 IRUnit& unit;
65 const bool typesMightRelax;
67 // The current instruction being simplified is always at insts.top(). This
68 // has to be a stack instead of just a pointer because simplify is reentrant.
69 jit::stack<const IRInstruction*> insts;
70 jit::vector<IRInstruction*> newInsts;
73 //////////////////////////////////////////////////////////////////////
75 SSATmp* simplifyWork(State&, const IRInstruction*);
77 bool mightRelax(State& env, SSATmp* tmp) {
78 if (!env.typesMightRelax) return false;
79 return irgen::typeMightRelax(tmp);
82 template<class... Args>
83 SSATmp* cns(State& env, Args&&... cns) {
84 return env.unit.cns(std::forward<Args>(cns)...);
87 template<class... Args>
88 SSATmp* gen(State& env, Opcode op, BCMarker marker, Args&&... args) {
89 return makeInstruction(
90 [&] (IRInstruction* inst) -> SSATmp* {
91 auto prevNewCount = env.newInsts.size();
92 auto newDest = simplifyWork(env, inst);
94 // If any simplification happened to this instruction, drop it. We have to
95 // check that nothing was added to newInsts because that's the only way
96 // we can tell simplification happened to a no-dest instruction.
97 if (newDest || env.newInsts.size() != prevNewCount) {
98 return newDest;
99 } else {
100 assertx(inst->isTransient());
101 inst = env.unit.clone(inst);
102 env.newInsts.push_back(inst);
104 return inst->dst(0);
108 marker,
109 std::forward<Args>(args)...
113 template<class... Args>
114 SSATmp* gen(State& env, Opcode op, Args&&... args) {
115 assertx(!env.insts.empty());
116 return gen(env, op, env.insts.top()->marker(), std::forward<Args>(args)...);
119 bool arrayKindNeedsVsize(const ArrayData::ArrayKind kind) {
120 switch (kind) {
121 case ArrayData::kPackedKind:
122 case ArrayData::kMixedKind:
123 case ArrayData::kEmptyKind:
124 case ArrayData::kVecKind:
125 case ArrayData::kApcKind:
126 case ArrayData::kDictKind:
127 return false;
128 default:
129 return true;
133 //////////////////////////////////////////////////////////////////////
135 DEBUG_ONLY bool validate(const State& env,
136 SSATmp* newDst,
137 const IRInstruction* origInst) {
138 // simplify() rules are not allowed to add new uses to SSATmps that aren't
139 // known to be available. All the sources to the original instruction must
140 // be available, and non-reference counted values reachable through the
141 // source chain are also always available. Anything else requires more
142 // complicated analysis than belongs in the simplifier right now.
143 auto known_available = [&] (SSATmp* src) -> bool {
144 if (!src->type().maybe(TCounted)) return true;
145 for (auto& oldSrc : origInst->srcs()) {
146 if (oldSrc == src) return true;
148 return false;
151 // Return early for the no-simplification case.
152 if (env.newInsts.empty() && !newDst) {
153 return true;
156 const IRInstruction* last = nullptr;
158 if (!env.newInsts.empty()) {
159 for (size_t i = 0, n = env.newInsts.size(); i < n; ++i) {
160 auto newInst = env.newInsts[i];
162 for (auto& src : newInst->srcs()) {
163 always_assert_flog(
164 known_available(src),
165 "A simplification rule produced an instruction that used a value "
166 "that wasn't known to be available:\n"
167 " original inst: {}\n"
168 " new inst: {}\n"
169 " src: {}\n",
170 origInst->toString(),
171 newInst->toString(),
172 src->toString()
176 if (i == n - 1) {
177 last = newInst;
178 continue;
181 always_assert_flog(
182 !newInst->isBlockEnd(),
183 "Block-ending instruction produced in the middle of a simplified "
184 "instruction stream:\n"
185 " original inst: {}\n"
186 " new inst: {}\n",
187 origInst->toString(),
188 newInst->toString()
193 if (newDst) {
194 const bool available = known_available(newDst) ||
195 std::any_of(env.newInsts.begin(), env.newInsts.end(),
196 [&] (IRInstruction* inst) { return newDst == inst->dst(); });
198 always_assert_flog(
199 available,
200 "simplify() produced a new destination that wasn't known to be "
201 "available:\n"
202 " original inst: {}\n"
203 " new dst: {}\n",
204 origInst->toString(),
205 newDst->toString()
209 if (!last) return true;
211 auto assert_last = [&] (bool cond, const char* msg) {
212 always_assert_flog(
213 cond,
214 "{}:\n"
215 " original inst: {}\n"
216 " last new inst: {}\n",
217 msg,
218 origInst->toString(),
219 last->toString()
223 assert_last(
224 !origInst->naryDst(),
225 "Nontrivial simplification returned for instruction with NaryDest"
228 assert_last(
229 origInst->hasDst() == (newDst != nullptr),
230 "HasDest mismatch between input and output"
233 assert_last(
234 IMPLIES(last->isBlockEnd(), origInst->isBlockEnd()),
235 "Block-end instruction produced for simplification of non-block-end "
236 "instruction"
239 if (last->hasEdges()) {
240 assert_last(
241 origInst->hasEdges(),
242 "Instruction with edges produced for simplification of edge-free "
243 "instruction"
246 assert_last(
247 IMPLIES(last->next(), last->next() == origInst->next() ||
248 last->next() == origInst->taken()),
249 "Last instruction of simplified stream has next edge not reachable from "
250 "the input instruction"
253 assert_last(
254 IMPLIES(last->taken(), last->taken() == origInst->next() ||
255 last->taken() == origInst->taken()),
256 "Last instruction of simplified stream has taken edge not reachable "
257 "from the input instruction"
261 return true;
264 //////////////////////////////////////////////////////////////////////
267 * Individual simplification routines return nullptr if they don't want to
268 * change anything, or they can call gen any number of times to produce a
269 * different IR sequence, returning the thing gen'd that should be used as the
270 * value of the simplified instruction sequence.
273 SSATmp* mergeBranchDests(State& env, const IRInstruction* inst) {
274 // Replace a conditional branch with a Jmp if both branches go to the same
275 // block. Only work if the instruction does not have side effect.
276 // JmpZero/JmpNZero is handled separately.
277 assertx(inst->is(CheckTypeMem,
278 CheckLoc,
279 CheckStk,
280 CheckInit,
281 CheckInitMem,
282 CheckInitProps,
283 CheckInitSProps,
284 CheckPackedArrayBounds,
285 CheckClosureStaticLocInit,
286 CheckRefInner,
287 CheckCtxThis));
288 if (inst->next() != nullptr && inst->next() == inst->taken()) {
289 return gen(env, Jmp, inst->next());
291 return nullptr;
294 SSATmp* simplifyCheckCtxThis(State& env, const IRInstruction* inst) {
295 auto const func = inst->marker().func();
296 auto const srcTy = inst->src(0)->type();
297 if (srcTy <= TObj) return gen(env, Nop);
298 if (!func->mayHaveThis() || !srcTy.maybe(TObj)) {
299 return gen(env, Jmp, inst->taken());
301 return mergeBranchDests(env, inst);
304 SSATmp* simplifyCastCtxThis(State& env, const IRInstruction* inst) {
305 if (inst->src(0)->type() <= TObj) return inst->src(0);
306 return nullptr;
309 SSATmp* simplifyLdClsCtx(State& env, const IRInstruction* inst) {
310 SSATmp* ctx = inst->src(0);
311 if (ctx->hasConstVal(TCctx)) {
312 return cns(env, ctx->cctxVal().cls());
314 Type ctxType = ctx->type();
315 if (ctxType <= TObj) {
316 // this pointer... load its class ptr
317 return gen(env, LdObjClass, ctx);
319 if (ctxType <= TCctx) {
320 return gen(env, LdClsCctx, ctx);
322 return nullptr;
325 SSATmp* simplifyLdClsMethod(State& env, const IRInstruction* inst) {
326 SSATmp* ctx = inst->src(0);
327 if (ctx->isA(TCls)) {
328 auto const src = ctx->inst();
329 if (src->op() == LdClsCctx) {
330 return gen(env, LdClsMethod, src->src(0), inst->src(1));
333 return nullptr;
336 SSATmp* simplifySpillFrame(State& env, const IRInstruction* inst) {
337 SSATmp* ctx = inst->src(2);
338 if (ctx->isA(TCls)) {
339 auto const src = ctx->inst();
340 if (src->op() == LdClsCctx) {
341 return gen(env, SpillFrame, *inst->extra<SpillFrame>(),
342 inst->src(0), inst->src(1), src->src(0));
345 return nullptr;
348 SSATmp* simplifyLdObjClass(State& env, const IRInstruction* inst) {
349 auto const ty = inst->src(0)->type();
351 if (mightRelax(env, inst->src(0)) || !(ty < TObj)) return nullptr;
353 if (auto const exact = ty.clsSpec().exactCls()) return cns(env, exact);
354 return nullptr;
357 SSATmp* simplifyLdObjInvoke(State& env, const IRInstruction* inst) {
358 auto const src = inst->src(0);
359 if (!src->hasConstVal()) return nullptr;
361 auto const cls = src->clsVal();
362 if (!classHasPersistentRDS(cls)) {
363 return nullptr;
366 auto const meth = cls->getCachedInvoke();
367 return meth == nullptr ? nullptr : cns(env, meth);
370 SSATmp* simplifyGetCtxFwdCall(State& env, const IRInstruction* inst) {
371 auto const srcCtx = inst->src(0);
372 if (srcCtx->isA(TCctx)) return srcCtx;
373 return nullptr;
376 SSATmp* simplifyConvClsToCctx(State& env, const IRInstruction* inst) {
377 auto* srcInst = inst->src(0)->inst();
378 if (srcInst->is(LdClsCctx)) return srcInst->src(0);
379 return nullptr;
382 SSATmp* simplifyMov(State& env, const IRInstruction* inst) {
383 return inst->src(0);
386 SSATmp* simplifyAbsDbl(State& env, const IRInstruction* inst) {
387 auto const src = inst->src(0);
388 if (src->hasConstVal()) return cns(env, fabs(src->dblVal()));
389 return nullptr;
392 template<class Oper>
393 SSATmp* constImpl(State& env, SSATmp* src1, SSATmp* src2, Oper op) {
394 // don't canonicalize to the right, OP might not be commutative
395 if (!src1->hasConstVal() || !src2->hasConstVal()) return nullptr;
397 auto both = [&](Type ty) { return src1->type() <= ty && src2->type() <= ty; };
399 if (both(TBool)) return cns(env, op(src1->boolVal(), src2->boolVal()));
400 if (both(TInt)) return cns(env, op(src1->intVal(), src2->intVal()));
401 if (both(TDbl)) return cns(env, op(src1->dblVal(), src2->dblVal()));
402 return nullptr;
405 template<class Oper>
406 SSATmp* commutativeImpl(State& env,
407 SSATmp* src1,
408 SSATmp* src2,
409 Opcode opcode,
410 Oper op) {
411 if (auto simp = constImpl(env, src1, src2, op)) return simp;
413 // Canonicalize constants to the right.
414 if (src1->hasConstVal() && src2->type().hasConstVal()) {
415 return gen(env, opcode, src2, src1);
418 // Only handle integer operations for now.
419 if (!src1->isA(TInt) || !src2->isA(TInt)) return nullptr;
421 auto const inst1 = src1->inst();
422 auto const inst2 = src2->inst();
423 if (inst1->op() == opcode && inst1->src(1)->hasConstVal()) {
424 // (X + C1) + C2 --> X + C3
425 if (src2->hasConstVal()) {
426 int64_t right = inst1->src(1)->intVal();
427 right = op(right, src2->intVal());
428 return gen(env, opcode, inst1->src(0), cns(env, right));
430 // (X + C1) + (Y + C2) --> X + Y + C3
431 if (inst2->op() == opcode && inst2->src(1)->hasConstVal()) {
432 int64_t right = inst1->src(1)->intVal();
433 right = op(right, inst2->src(1)->intVal());
434 SSATmp* left = gen(env, opcode, inst1->src(0), inst2->src(0));
435 return gen(env, opcode, left, cns(env, right));
438 return nullptr;
442 * Assumes that outop is commutative, don't use with subtract!
444 * Assumes that the values we're going to add new uses to are not reference
445 * counted. (I.e. this is a distributive FooInt opcode.)
447 template<class OutOper, class InOper>
448 SSATmp* distributiveImpl(State& env,
449 SSATmp* src1,
450 SSATmp* src2,
451 Opcode outcode,
452 Opcode incode,
453 OutOper outop,
454 InOper inop) {
455 if (auto simp = commutativeImpl(env, src1, src2, outcode, outop)) {
456 return simp;
459 auto const inst1 = src1->inst();
460 auto const inst2 = src2->inst();
461 auto const op1 = inst1->op();
462 auto const op2 = inst2->op();
463 // all combinations of X * Y + X * Z --> X * (Y + Z)
464 if (op1 == incode && op2 == incode) {
465 if (inst1->src(0) == inst2->src(0)) {
466 SSATmp* fold = gen(env, outcode, inst1->src(1), inst2->src(1));
467 return gen(env, incode, inst1->src(0), fold);
469 if (inst1->src(0) == inst2->src(1)) {
470 SSATmp* fold = gen(env, outcode, inst1->src(1), inst2->src(0));
471 return gen(env, incode, inst1->src(0), fold);
473 if (inst1->src(1) == inst2->src(0)) {
474 SSATmp* fold = gen(env, outcode, inst1->src(0), inst2->src(1));
475 return gen(env, incode, inst1->src(1), fold);
477 if (inst1->src(1) == inst2->src(1)) {
478 SSATmp* fold = gen(env, outcode, inst1->src(0), inst2->src(0));
479 return gen(env, incode, inst1->src(1), fold);
482 return nullptr;
485 SSATmp* simplifyAddInt(State& env, const IRInstruction* inst) {
486 auto const src1 = inst->src(0);
487 auto const src2 = inst->src(1);
489 auto add = std::plus<int64_t>();
490 auto mul = std::multiplies<int64_t>();
491 if (auto simp = distributiveImpl(env, src1, src2, AddInt,
492 MulInt, add, mul)) {
493 return simp;
495 if (src2->hasConstVal()) {
496 int64_t src2Val = src2->intVal();
497 // X + 0 --> X
498 if (src2Val == 0) return src1;
500 // X + -C --> X - C
501 // Weird, but can show up as a result of other simplifications. Don't need
502 // to check for C == INT_MIN, simplifySubInt already checks.
503 if (src2Val < 0) return gen(env, SubInt, src1, cns(env, -src2Val));
505 // X + (0 - Y) --> X - Y
506 auto inst2 = src2->inst();
507 if (inst2->op() == SubInt) {
508 SSATmp* src = inst2->src(0);
509 if (src->hasConstVal() && src->intVal() == 0) {
510 return gen(env, SubInt, src1, inst2->src(1));
513 auto inst1 = src1->inst();
515 // (X - C1) + ...
516 if (inst1->op() == SubInt && inst1->src(1)->hasConstVal()) {
517 auto x = inst1->src(0);
518 auto c1 = inst1->src(1);
520 // (X - C1) + C2 --> X + (C2 - C1)
521 if (src2->hasConstVal()) {
522 auto rhs = gen(env, SubInt, cns(env, src2->intVal()), c1);
523 return gen(env, AddInt, x, rhs);
526 // (X - C1) + (Y +/- C2)
527 if ((inst2->op() == AddInt || inst2->op() == SubInt) &&
528 inst2->src(1)->hasConstVal()) {
529 auto y = inst2->src(0);
530 auto c2 = inst2->src(1);
531 SSATmp* rhs = nullptr;
532 if (inst2->op() == SubInt) {
533 // (X - C1) + (Y - C2) --> X + Y + (-C1 - C2)
534 rhs = gen(env, SubInt, gen(env, SubInt, cns(env, 0), c1), c2);
535 } else {
536 // (X - C1) + (Y + C2) --> X + Y + (C2 - C1)
537 rhs = gen(env, SubInt, c2, c1);
539 auto lhs = gen(env, AddInt, x, y);
540 return gen(env, AddInt, lhs, rhs);
542 // (X - C1) + (Y + C2) --> X + Y + (C2 - C1)
543 if (inst2->op() == AddInt && inst2->src(1)->hasConstVal()) {
544 auto y = inst2->src(0);
545 auto c2 = inst2->src(1);
547 auto lhs = gen(env, AddInt, x, y);
548 auto rhs = gen(env, SubInt, c2, c1);
549 return gen(env, AddInt, lhs, rhs);
553 return nullptr;
556 SSATmp* simplifyAddIntO(State& env, const IRInstruction* inst) {
557 auto const src1 = inst->src(0);
558 auto const src2 = inst->src(1);
559 if (src1->hasConstVal() && src2->hasConstVal()) {
560 int64_t a = src1->intVal();
561 int64_t b = src2->intVal();
562 return add_overflow(a, b)
563 ? cns(env, double(a) + double(b))
564 : cns(env, a + b);
566 return nullptr;
569 SSATmp* simplifySubInt(State& env, const IRInstruction* inst) {
570 auto const src1 = inst->src(0);
571 auto const src2 = inst->src(1);
573 auto sub = std::minus<int64_t>();
574 if (auto simp = constImpl(env, src1, src2, sub)) return simp;
576 // X - X --> 0
577 if (src1 == src2) return cns(env, 0);
579 if (src2->hasConstVal()) {
580 int64_t src2Val = src2->intVal();
581 // X - 0 --> X
582 if (src2Val == 0) return src1;
584 // X - -C --> X + C
585 // Need to check for C == INT_MIN, otherwise we'd infinite loop as
586 // X + -C would send us back here.
587 auto const min = std::numeric_limits<int64_t>::min();
588 if (src2Val > min && src2Val < 0) {
589 return gen(env, AddInt, src1, cns(env, -src2Val));
592 // X - (0 - Y) --> X + Y
593 auto inst2 = src2->inst();
594 if (inst2->op() == SubInt) {
595 SSATmp* src = inst2->src(0);
596 if (src->hasConstVal(0)) return gen(env, AddInt, src1, inst2->src(1));
598 return nullptr;
601 SSATmp* simplifySubIntO(State& env, const IRInstruction* inst) {
602 auto const src1 = inst->src(0);
603 auto const src2 = inst->src(1);
604 if (src1->hasConstVal() && src2->hasConstVal()) {
605 int64_t a = src1->intVal();
606 int64_t b = src2->intVal();
607 return sub_overflow(a, b)
608 ? cns(env, double(a) - double(b))
609 : cns(env, a - b);
611 return nullptr;
614 SSATmp* simplifyMulInt(State& env, const IRInstruction* inst) {
615 auto const src1 = inst->src(0);
616 auto const src2 = inst->src(1);
618 auto mul = std::multiplies<int64_t>();
619 if (auto simp = commutativeImpl(env, src1, src2, MulInt, mul)) return simp;
621 if (!src2->hasConstVal()) return nullptr;
623 int64_t rhs = src2->intVal();
625 // X * (-1) --> -X
626 if (rhs == -1) return gen(env, SubInt, cns(env, 0), src1);
627 // X * 0 --> 0
628 if (rhs == 0) return cns(env, 0);
629 // X * 1 --> X
630 if (rhs == 1) return src1;
631 // X * 2 --> X + X
632 if (rhs == 2) return gen(env, AddInt, src1, src1);
634 auto isPowTwo = [](int64_t a) {
635 return a > 0 && folly::isPowTwo<uint64_t>(a);
637 auto log2 = [](int64_t a) {
638 assertx(a > 0);
639 return folly::findLastSet<uint64_t>(a) - 1;
642 // X * 2^C --> X << C
643 if (isPowTwo(rhs)) return gen(env, Shl, src1, cns(env, log2(rhs)));
645 // X * (2^C + 1) --> ((X << C) + X)
646 if (isPowTwo(rhs - 1)) {
647 auto lhs = gen(env, Shl, src1, cns(env, log2(rhs - 1)));
648 return gen(env, AddInt, lhs, src1);
650 // X * (2^C - 1) --> ((X << C) - X)
651 if (isPowTwo(rhs + 1)) {
652 auto lhs = gen(env, Shl, src1, cns(env, log2(rhs + 1)));
653 return gen(env, SubInt, lhs, src1);
656 return nullptr;
659 SSATmp* simplifyAddDbl(State& env, const IRInstruction* inst) {
660 return constImpl(env, inst->src(0), inst->src(1), std::plus<double>());
663 SSATmp* simplifySubDbl(State& env, const IRInstruction* inst) {
664 return constImpl(env, inst->src(0), inst->src(1), std::minus<double>());
667 SSATmp* simplifyMulDbl(State& env, const IRInstruction* inst) {
668 return constImpl(env, inst->src(0), inst->src(1), std::multiplies<double>());
671 SSATmp* simplifyMulIntO(State& env, const IRInstruction* inst) {
672 auto const src1 = inst->src(0);
673 auto const src2 = inst->src(1);
674 if (src1->hasConstVal() && src2->hasConstVal()) {
675 int64_t a = src1->intVal();
676 int64_t b = src2->intVal();
677 return mul_overflow(a, b)
678 ? cns(env, double(a) * double(b))
679 : cns(env, a * b);
681 return nullptr;
684 SSATmp* simplifyMod(State& env, const IRInstruction* inst) {
685 auto const src1 = inst->src(0);
686 auto const src2 = inst->src(1);
688 if (!src2->hasConstVal()) return nullptr;
690 auto const src2Val = src2->intVal();
691 if (src2Val == 0 || src2Val == -1) {
692 // Undefined behavior, so we might as well constant propagate whatever we
693 // want. If we're being asked to simplify this, it better be dynamically
694 // unreachable code.
695 return cns(env, 0);
698 if (src1->hasConstVal()) return cns(env, src1->intVal() % src2Val);
699 // X % 1 --> 0
700 if (src2Val == 1) return cns(env, 0);
702 return nullptr;
705 SSATmp* simplifyDivDbl(State& env, const IRInstruction* inst) {
706 auto const src1 = inst->src(0);
707 auto const src2 = inst->src(1);
709 if (!src2->hasConstVal()) return nullptr;
711 auto src2Val = src2->dblVal();
713 if (src2Val == 0.0) {
714 // The branch emitted during irgen will deal with this
715 return nullptr;
718 // statically compute X / Y
719 return src1->hasConstVal() ? cns(env, src1->dblVal() / src2Val) : nullptr;
722 SSATmp* simplifyDivInt(State& env, const IRInstruction* inst) {
723 auto const dividend = inst->src(0);
724 auto const divisor = inst->src(1);
726 if (!divisor->hasConstVal()) return nullptr;
728 auto const divisorVal = divisor->intVal();
730 if (divisorVal == 0) {
731 // The branch emitted during irgen will deal with this
732 return nullptr;
735 if (!dividend->hasConstVal()) return nullptr;
737 auto const dividendVal = dividend->intVal();
739 if (dividendVal == LLONG_MIN || dividendVal % divisorVal) {
740 // This should be unreachable
741 return nullptr;
744 return cns(env, dividendVal / divisorVal);
747 SSATmp* simplifyAndInt(State& env, const IRInstruction* inst) {
748 auto const src1 = inst->src(0);
749 auto const src2 = inst->src(1);
750 auto bit_and = [](int64_t a, int64_t b) { return a & b; };
751 auto bit_or = [](int64_t a, int64_t b) { return a | b; };
752 auto simp = distributiveImpl(env, src1, src2, AndInt, OrInt,
753 bit_and, bit_or);
754 if (simp != nullptr) {
755 return simp;
757 // X & X --> X
758 if (src1 == src2) {
759 return src1;
761 if (src2->hasConstVal()) {
762 // X & 0 --> 0
763 if (src2->intVal() == 0) {
764 return cns(env, 0);
766 // X & (~0) --> X
767 if (src2->intVal() == ~0L) {
768 return src1;
771 return nullptr;
774 SSATmp* simplifyOrInt(State& env, const IRInstruction* inst) {
775 auto const src1 = inst->src(0);
776 auto const src2 = inst->src(1);
778 auto bit_and = [](int64_t a, int64_t b) { return a & b; };
779 auto bit_or = [](int64_t a, int64_t b) { return a | b; };
780 auto simp = distributiveImpl(env, src1, src2, OrInt, AndInt,
781 bit_or, bit_and);
782 if (simp != nullptr) {
783 return simp;
785 // X | X --> X
786 if (src1 == src2) {
787 return src1;
789 if (src2->hasConstVal()) {
790 // X | 0 --> X
791 if (src2->intVal() == 0) {
792 return src1;
794 // X | (~0) --> ~0
795 if (src2->intVal() == ~uint64_t(0)) {
796 return cns(env, ~uint64_t(0));
799 return nullptr;
802 SSATmp* simplifyXorInt(State& env, const IRInstruction* inst) {
803 auto const src1 = inst->src(0);
804 auto const src2 = inst->src(1);
805 auto bitxor = [](int64_t a, int64_t b) { return a ^ b; };
806 if (auto simp = commutativeImpl(env, src1, src2, XorInt, bitxor)) {
807 return simp;
809 // X ^ X --> 0
810 if (src1 == src2) return cns(env, 0);
811 // X ^ 0 --> X
812 if (src2->hasConstVal(0)) return src1;
813 return nullptr;
816 SSATmp* xorTrueImpl(State& env, SSATmp* src) {
817 auto const inst = src->inst();
818 auto const op = inst->op();
820 // !(X cmp Y) --> X opposite_cmp Y
821 if (auto negated = negateCmpOp(op)) {
822 auto const s0 = inst->src(0);
823 auto const s1 = inst->src(1);
824 // We can't add new uses to reference counted types without a more
825 // advanced availability analysis.
826 if (!s0->type().maybe(TCounted) && !s1->type().maybe(TCounted)) {
827 return gen(env, *negated, s0, s1);
829 return nullptr;
832 switch (op) {
833 // !!X --> X
834 case XorBool:
835 if (inst->src(1)->hasConstVal(true)) {
836 // This is safe to add a new use to because inst->src(0) is a bool.
837 assertx(inst->src(0)->isA(TBool));
838 return inst->src(0);
840 return nullptr;
841 case InstanceOfBitmask:
842 case NInstanceOfBitmask:
843 // This is safe because instanceofs don't take reference counted
844 // arguments.
845 assertx(!inst->src(0)->type().maybe(TCounted) &&
846 !inst->src(1)->type().maybe(TCounted));
847 return gen(
848 env,
849 (op == InstanceOfBitmask) ?
850 NInstanceOfBitmask :
851 InstanceOfBitmask,
852 inst->src(0),
853 inst->src(1)
855 default:
856 break;
859 return nullptr;
862 SSATmp* simplifyXorBool(State& env, const IRInstruction* inst) {
863 auto const src1 = inst->src(0);
864 auto const src2 = inst->src(1);
866 // Both constants.
867 if (src1->hasConstVal() && src2->hasConstVal()) {
868 return cns(env, bool(src1->boolVal() ^ src2->boolVal()));
871 // Canonicalize constants to the right.
872 if (src1->hasConstVal() && !src2->hasConstVal()) {
873 return gen(env, XorBool, src2, src1);
876 // X^0 => X
877 if (src2->hasConstVal(false)) return src1;
879 // X^1 => simplify "not" logic
880 if (src2->hasConstVal(true)) return xorTrueImpl(env, src1);
881 return nullptr;
884 template<class Oper>
885 SSATmp* shiftImpl(State& env, const IRInstruction* inst, Oper op) {
886 auto const src1 = inst->src(0);
887 auto const src2 = inst->src(1);
889 if (src1->hasConstVal()) {
890 if (src1->intVal() == 0) {
891 return cns(env, 0);
894 if (src2->hasConstVal()) {
895 return cns(env, op(src1->intVal(), src2->intVal()));
899 if (src2->hasConstVal() && src2->intVal() == 0) {
900 return src1;
903 return nullptr;
906 SSATmp* simplifyShl(State& env, const IRInstruction* inst) {
907 return shiftImpl(env, inst, [] (int64_t a, int64_t b) { return a << b; });
910 SSATmp* simplifyShr(State& env, const IRInstruction* inst) {
911 return shiftImpl(env, inst, [] (int64_t a, int64_t b) { return a >> b; });
914 // This function isn't meant to perform the actual comparison at
915 // compile-time. Instead, it performs the matching comparison against a
916 // primitive type (usually bool).
917 template<class T, class U>
918 static bool cmpOp(Opcode opc, T a, U b) {
919 switch (opc) {
920 case GtBool:
921 case GtInt:
922 case GtStr:
923 case GtStrInt:
924 case GtObj:
925 case GtArr:
926 case GtRes: return a > b;
927 case GteBool:
928 case GteInt:
929 case GteStr:
930 case GteStrInt:
931 case GteObj:
932 case GteArr:
933 case GteRes: return a >= b;
934 case LtBool:
935 case LtInt:
936 case LtStr:
937 case LtStrInt:
938 case LtObj:
939 case LtArr:
940 case LtRes: return a < b;
941 case LteBool:
942 case LteInt:
943 case LteStr:
944 case LteStrInt:
945 case LteObj:
946 case LteArr:
947 case LteRes: return a <= b;
948 case SameStr:
949 case SameObj:
950 case SameArr:
951 case EqBool:
952 case EqInt:
953 case EqStr:
954 case EqStrInt:
955 case EqObj:
956 case EqArr:
957 case EqRes: return a == b;
958 case NSameStr:
959 case NSameObj:
960 case NSameArr:
961 case NeqBool:
962 case NeqInt:
963 case NeqStr:
964 case NeqStrInt:
965 case NeqObj:
966 case NeqArr:
967 case NeqRes: return a != b;
968 default:
969 always_assert(false);
973 SSATmp* cmpBoolImpl(State& env,
974 Opcode opc,
975 const IRInstruction* const inst,
976 SSATmp* left,
977 SSATmp* right) {
978 assertx(left->type() <= TBool);
979 assertx(right->type() <= TBool);
981 auto newInst = [&](Opcode op, SSATmp* src1, SSATmp* src2) {
982 return gen(env, op, inst ? inst->taken() : (Block*)nullptr, src1, src2);
985 // Identity optimization
986 if (left == right) {
987 return cns(env, cmpOp(opc, true, true));
990 if (left->hasConstVal()) {
991 // If both operands are constants, constant-fold them. Otherwise, move the
992 // constant over to the right.
993 if (right->hasConstVal()) {
994 return cns(env, cmpOp(opc, left->boolVal(), right->boolVal()));
995 } else {
996 auto newOpc = [](Opcode opc) {
997 switch (opc) {
998 case GtBool: return LtBool;
999 case GteBool: return LteBool;
1000 case LtBool: return GtBool;
1001 case LteBool: return GteBool;
1002 case EqBool: return EqBool;
1003 case NeqBool: return NeqBool;
1004 default: always_assert(false);
1006 }(opc);
1007 return newInst(newOpc, right, left);
1011 if (right->hasConstVal()) {
1012 bool b = right->boolVal();
1014 // The result of the comparison might be independent of the truth
1015 // value of the LHS. If so, then simplify.
1016 if (cmpOp(opc, false, b) == cmpOp(opc, true, b)) {
1017 return cns(env, cmpOp(opc, false, b));
1020 // There are only two distinct booleans - false and true (0 and 1).
1021 // From above, we know that (0 OP b) != (1 OP b).
1022 // Hence exactly one of (0 OP b) and (1 OP b) is true.
1023 // Hence there is exactly one boolean value of "left" that results in the
1024 // overall expression being true.
1025 // Hence we may check for equality with that boolean.
1026 if (opc != EqBool) {
1027 return newInst(EqBool, left, cns(env, !cmpOp(opc, false, b)));
1030 // If we reach here, this is an equality comparison against a
1031 // constant. Testing for equality with true simplifies to just the left
1032 // operand, while equality with false is the negation of the left operand
1033 // (equivalent to XORing with true).
1034 return b ? left : newInst(XorBool, left, cns(env, true));
1037 return nullptr;
1040 SSATmp* cmpIntImpl(State& env,
1041 Opcode opc,
1042 const IRInstruction* const inst,
1043 SSATmp* left,
1044 SSATmp* right) {
1045 assertx(left->type() <= TInt);
1046 assertx(right->type() <= TInt);
1048 auto newInst = [&](Opcode op, SSATmp* src1, SSATmp* src2) {
1049 return gen(env, op, inst ? inst->taken() : (Block*)nullptr, src1, src2);
1052 // Identity optimization
1053 if (left == right) {
1054 return cns(env, cmpOp(opc, true, true));
1057 if (left->hasConstVal()) {
1058 // If both operands are constants, constant-fold them. Otherwise, move the
1059 // constant over to the right.
1060 if (right->hasConstVal()) {
1061 return cns(env, cmpOp(opc, left->intVal(), right->intVal()));
1062 } else {
1063 auto newOpc = [](Opcode opc) {
1064 switch (opc) {
1065 case GtInt: return LtInt;
1066 case GteInt: return LteInt;
1067 case LtInt: return GtInt;
1068 case LteInt: return GteInt;
1069 case EqInt: return EqInt;
1070 case NeqInt: return NeqInt;
1071 default: always_assert(false);
1073 }(opc);
1074 return newInst(newOpc, right, left);
1078 return nullptr;
1081 SSATmp* cmpStrImpl(State& env,
1082 Opcode opc,
1083 const IRInstruction* const inst,
1084 SSATmp* left,
1085 SSATmp* right) {
1086 assertx(left->type() <= TStr);
1087 assertx(right->type() <= TStr);
1089 auto newInst = [&](Opcode op, SSATmp* src1, SSATmp* src2) {
1090 return gen(env, op, inst ? inst->taken() : (Block*)nullptr, src1, src2);
1093 // Identity optimization
1094 if (left == right) {
1095 return cns(env, cmpOp(opc, true, true));
1098 if (left->hasConstVal()) {
1099 // If both operands are constants, constant-fold them. Otherwise, move the
1100 // constant over to the right.
1101 if (right->hasConstVal()) {
1102 if (opc == SameStr || opc == NSameStr) {
1103 return cns(
1104 env,
1105 cmpOp(opc, left->strVal()->same(right->strVal()), true)
1107 } else {
1108 return cns(
1109 env,
1110 cmpOp(opc, left->strVal()->compare(right->strVal()), 0)
1113 } else {
1114 auto newOpc = [](Opcode opc) {
1115 switch (opc) {
1116 case GtStr: return LtStr;
1117 case GteStr: return LteStr;
1118 case LtStr: return GtStr;
1119 case LteStr: return GteStr;
1120 case EqStr: return EqStr;
1121 case NeqStr: return NeqStr;
1122 case SameStr: return SameStr;
1123 case NSameStr: return NSameStr;
1124 default: always_assert(false);
1126 }(opc);
1127 return newInst(newOpc, right, left);
1131 // Comparisons against the empty string can be optimized to checks on the
1132 // string length.
1133 if (right->hasConstVal() && right->strVal()->empty()) {
1134 switch (opc) {
1135 case EqStr:
1136 case SameStr:
1137 case LteStr: return newInst(EqInt, gen(env, LdStrLen, left), cns(env, 0));
1138 case NeqStr:
1139 case NSameStr:
1140 case GtStr: return newInst(NeqInt, gen(env, LdStrLen, left), cns(env, 0));
1141 case LtStr: return cns(env, false);
1142 case GteStr: return cns(env, true);
1143 default: always_assert(false);
1147 return nullptr;
1150 SSATmp* cmpStrIntImpl(State& env,
1151 Opcode opc,
1152 const IRInstruction* const inst,
1153 SSATmp* left,
1154 SSATmp* right) {
1155 assertx(left->type() <= TStr);
1156 assertx(right->type() <= TInt);
1158 auto newInst = [&](Opcode op, SSATmp* src1, SSATmp* src2) {
1159 return gen(env, op, inst ? inst->taken() : (Block*)nullptr, src1, src2);
1162 // If the string operand has a constant value, convert it to the appropriate
1163 // numeric and lower to a numeric comparison.
1164 if (left->hasConstVal()) {
1165 int64_t si;
1166 double sd;
1167 auto type =
1168 left->strVal()->isNumericWithVal(si, sd, true /* allow errors */);
1169 if (type == KindOfDouble) {
1170 auto dblOpc = [](Opcode opc) {
1171 switch (opc) {
1172 case GtStrInt: return GtDbl;
1173 case GteStrInt: return GteDbl;
1174 case LtStrInt: return LtDbl;
1175 case LteStrInt: return LteDbl;
1176 case EqStrInt: return EqDbl;
1177 case NeqStrInt: return NeqDbl;
1178 default: always_assert(false);
1180 }(opc);
1181 return newInst(
1182 dblOpc,
1183 cns(env, sd),
1184 gen(env, ConvIntToDbl, right)
1186 } else {
1187 auto intOpc = [](Opcode opc) {
1188 switch (opc) {
1189 case GtStrInt: return GtInt;
1190 case GteStrInt: return GteInt;
1191 case LtStrInt: return LtInt;
1192 case LteStrInt: return LteInt;
1193 case EqStrInt: return EqInt;
1194 case NeqStrInt: return NeqInt;
1195 default: always_assert(false);
1197 }(opc);
1198 return newInst(
1199 intOpc,
1200 cns(env, type == KindOfNull ? (int64_t)0 : si),
1201 right
1206 return nullptr;
1209 SSATmp* cmpObjImpl(State& env,
1210 Opcode opc,
1211 const IRInstruction* const inst,
1212 SSATmp* left,
1213 SSATmp* right) {
1214 assertx(left->type() <= TObj);
1215 assertx(right->type() <= TObj);
1217 // Identity optimization. Object comparisons can produce arbitrary
1218 // side-effects, so we can only eliminate the comparison if its checking for
1219 // sameness.
1220 if ((opc == SameObj || opc == NSameObj) && left == right) {
1221 return cns(env, cmpOp(opc, true, true));
1224 return nullptr;
1227 SSATmp* cmpArrImpl(State& env,
1228 Opcode opc,
1229 const IRInstruction* const inst,
1230 SSATmp* left,
1231 SSATmp* right) {
1232 assertx(left->type() <= TArr);
1233 assertx(right->type() <= TArr);
1235 // Identity optimization. Array comparisons can produce arbitrary
1236 // side-effects, so we can only eliminate the comparison if its checking for
1237 // sameness.
1238 if ((opc == SameArr || opc == NSameArr) && left == right) {
1239 return cns(env, cmpOp(opc, true, true));
1242 return nullptr;
1246 SSATmp* cmpResImpl(State& env,
1247 Opcode opc,
1248 const IRInstruction* const inst,
1249 SSATmp* left,
1250 SSATmp* right) {
1251 assertx(left->type() <= TRes);
1252 assertx(right->type() <= TRes);
1254 // Identity optimization.
1255 if (left == right) {
1256 return cns(env, cmpOp(opc, true, true));
1259 return nullptr;
1262 #define X(name, type) \
1263 SSATmp* simplify##name(State& env, const IRInstruction* i) { \
1264 return cmp##type##Impl(env, i->op(), i, i->src(0), i->src(1)); \
1267 X(GtBool, Bool)
1268 X(GteBool, Bool)
1269 X(LtBool, Bool)
1270 X(LteBool, Bool)
1271 X(EqBool, Bool)
1272 X(NeqBool, Bool)
1274 X(GtInt, Int)
1275 X(GteInt, Int)
1276 X(LtInt, Int)
1277 X(LteInt, Int)
1278 X(EqInt, Int)
1279 X(NeqInt, Int)
1281 X(GtStr, Str)
1282 X(GteStr, Str)
1283 X(LtStr, Str)
1284 X(LteStr, Str)
1285 X(EqStr, Str)
1286 X(NeqStr, Str)
1287 X(SameStr, Str)
1288 X(NSameStr, Str)
1290 X(GtStrInt, StrInt)
1291 X(GteStrInt, StrInt)
1292 X(LtStrInt, StrInt)
1293 X(LteStrInt, StrInt)
1294 X(EqStrInt, StrInt)
1295 X(NeqStrInt, StrInt)
1297 X(GtObj, Obj)
1298 X(GteObj, Obj)
1299 X(LtObj, Obj)
1300 X(LteObj, Obj)
1301 X(EqObj, Obj)
1302 X(NeqObj, Obj)
1303 X(SameObj, Obj)
1304 X(NSameObj, Obj)
1306 X(GtArr, Arr)
1307 X(GteArr, Arr)
1308 X(LtArr, Arr)
1309 X(LteArr, Arr)
1310 X(EqArr, Arr)
1311 X(NeqArr, Arr)
1312 X(SameArr, Arr)
1313 X(NSameArr, Arr)
1315 X(GtRes, Res)
1316 X(GteRes, Res)
1317 X(LtRes, Res)
1318 X(LteRes, Res)
1319 X(EqRes, Res)
1320 X(NeqRes, Res)
1322 #undef X
1324 SSATmp* simplifyEqCls(State& env, const IRInstruction* inst) {
1325 auto const left = inst->src(0);
1326 auto const right = inst->src(1);
1327 if (left->hasConstVal() && right->hasConstVal()) {
1328 return cns(env, left->clsVal() == right->clsVal());
1330 return nullptr;
1333 SSATmp* simplifyCmpBool(State& env, const IRInstruction* inst) {
1334 auto const left = inst->src(0);
1335 auto const right = inst->src(1);
1336 assertx(left->type() <= TBool);
1337 assertx(right->type() <= TBool);
1338 if (left->hasConstVal() && right->hasConstVal()) {
1339 return cns(env, HPHP::compare(left->boolVal(), right->boolVal()));
1341 return nullptr;
1344 SSATmp* simplifyCmpInt(State& env, const IRInstruction* inst) {
1345 auto const left = inst->src(0);
1346 auto const right = inst->src(1);
1347 assertx(left->type() <= TInt);
1348 assertx(right->type() <= TInt);
1349 if (left->hasConstVal() && right->hasConstVal()) {
1350 return cns(env, HPHP::compare(left->intVal(), right->intVal()));
1352 return nullptr;
1355 SSATmp* simplifyCmpStr(State& env, const IRInstruction* inst) {
1356 auto const left = inst->src(0);
1357 auto const right = inst->src(1);
1359 assertx(left->type() <= TStr);
1360 assertx(right->type() <= TStr);
1362 auto newInst = [&](Opcode op, SSATmp* src1, SSATmp* src2) {
1363 return gen(env, op, inst ? inst->taken() : (Block*)nullptr, src1, src2);
1366 if (left->hasConstVal()) {
1367 if (right->hasConstVal()) {
1368 return cns(env, HPHP::compare(left->strVal(), right->strVal()));
1369 } else if (left->strVal()->empty()) {
1370 // Comparisons against the empty string can be optimized to a comparison
1371 // on the string length.
1372 return newInst(CmpInt, cns(env, 0), gen(env, LdStrLen, right));
1374 } else if (right->hasConstVal() && right->strVal()->empty()) {
1375 return newInst(CmpInt, gen(env, LdStrLen, left), cns(env, 0));
1378 return nullptr;
1381 SSATmp* simplifyCmpStrInt(State& env, const IRInstruction* inst) {
1382 auto const left = inst->src(0);
1383 auto const right = inst->src(1);
1385 assertx(left->type() <= TStr);
1386 assertx(right->type() <= TInt);
1388 auto newInst = [&](Opcode op, SSATmp* src1, SSATmp* src2) {
1389 return gen(env, op, inst ? inst->taken() : (Block*)nullptr, src1, src2);
1392 // If the string operand has a constant value, convert it to the appropriate
1393 // numeric and lower to a numeric comparison.
1394 if (left->hasConstVal()) {
1395 int64_t si;
1396 double sd;
1397 auto type =
1398 left->strVal()->isNumericWithVal(si, sd, true /* allow errors */);
1399 if (type == KindOfDouble) {
1400 return newInst(
1401 CmpDbl,
1402 cns(env, sd),
1403 gen(env, ConvIntToDbl, right)
1405 } else {
1406 return newInst(
1407 CmpInt,
1408 cns(env, type == KindOfNull ? (int64_t)0 : si),
1409 right
1414 return nullptr;
1417 SSATmp* simplifyCmpRes(State& env, const IRInstruction* inst) {
1418 auto const left = inst->src(0);
1419 auto const right = inst->src(1);
1420 assertx(left->type() <= TRes);
1421 assertx(right->type() <= TRes);
1422 return (left == right) ? cns(env, 0) : nullptr;
1425 SSATmp* isTypeImpl(State& env, const IRInstruction* inst) {
1426 bool const trueSense = inst->op() == IsType;
1427 auto const type = inst->typeParam();
1428 auto const src = inst->src(0);
1429 auto const srcType = src->type();
1431 // If mightRelax(src) returns true, we can't generally depend on the src's
1432 // type. However, we always constrain the input to this opcode with at least
1433 // DataTypeSpecific, so we only have to skip the optimization if the
1434 // typeParam is specialized.
1435 if (mightRelax(env, src) && type.isSpecialized()) return nullptr;
1437 // Testing for StaticStr will make you miss out on CountedStr, and vice versa,
1438 // and similarly for arrays. PHP treats both types of string the same, so if
1439 // the distinction matters to you here, be careful.
1440 assertx(IMPLIES(type <= TStr, type == TStr));
1441 assertx(IMPLIES(type <= TArr, type == TArr));
1443 // Specially handle checking if an uninit var's type is null. The Type class
1444 // doesn't fully correctly handle the fact that the earlier stages of the
1445 // compiler consider null to be either initalized or uninitalized, so we need
1446 // to do this check first. Right here is really the only place in the backend
1447 // it seems to matter, especially since manipulating an uninit is kind of
1448 // weird; as of this writing, "$uninitalized_variable ?? 42" is the only
1449 // place it really comes up.
1450 if (srcType <= TUninit && type <= TNull) {
1451 return cns(env, trueSense);
1454 // The types are disjoint; the result must be false.
1455 if (!srcType.maybe(type)) {
1456 return cns(env, !trueSense);
1459 // The src type is a subtype of the tested type; the result must be true.
1460 if (srcType <= type) {
1461 return cns(env, trueSense);
1464 // At this point, either the tested type is a subtype of the src type, or they
1465 // are non-disjoint but neither is a subtype of the other. We can't simplify
1466 // this away.
1467 return nullptr;
1470 SSATmp* instanceOfImpl(State& env, ClassSpec spec1, ClassSpec spec2) {
1471 if (!spec1 || !spec2) return nullptr;
1473 auto const cls1 = spec1.cls();
1474 auto const cls2 = spec2.cls();
1476 if (spec1.exact() && spec2.exact()) {
1477 return cns(env, cls1->classof(cls2));
1480 if (spec2.exact() && cls1->classof(cls2)) {
1481 return cns(env, true);
1484 return nullptr;
1487 SSATmp* simplifyInstanceOf(State& env, const IRInstruction* inst) {
1488 auto const src1 = inst->src(0);
1489 auto const src2 = inst->src(1);
1491 auto const spec2 = src2->type().clsSpec();
1493 if (src2->isA(TNullptr)) {
1494 return cns(env, false);
1497 if (mightRelax(env, src1) || mightRelax(env, src2)) {
1498 return nullptr;
1501 if (auto const cls = spec2.exactCls()) {
1502 if (isNormalClass(cls) && (cls->attrs() & AttrUnique)) {
1503 return gen(env, ExtendsClass, ExtendsClassData{ cls }, src1);
1505 if (isInterface(cls) && (cls->attrs() & AttrUnique)) {
1506 return gen(env, InstanceOfIface, src1, cns(env, cls->name()));
1510 return instanceOfImpl(env, src1->type().clsSpec(), src2->type().clsSpec());
1513 SSATmp* simplifyExtendsClass(State& env, const IRInstruction* inst) {
1514 auto const src1 = inst->src(0);
1515 auto const cls2 = inst->extra<ExtendsClassData>()->cls;
1517 assertx(cls2 && isNormalClass(cls2));
1518 auto const spec2 = ClassSpec{cls2, ClassSpec::ExactTag{}};
1519 return instanceOfImpl(env, src1->type().clsSpec(), spec2);
1522 SSATmp* simplifyInstanceOfIface(State& env, const IRInstruction* inst) {
1523 auto const src1 = inst->src(0);
1524 auto const src2 = inst->src(1);
1526 auto const cls2 = Unit::lookupClassOrUniqueClass(src2->strVal());
1527 assertx(cls2 && isInterface(cls2));
1528 auto const spec2 = ClassSpec{cls2, ClassSpec::ExactTag{}};
1530 return instanceOfImpl(env, src1->type().clsSpec(), spec2);
1533 SSATmp* simplifyIsType(State& env, const IRInstruction* i) {
1534 return isTypeImpl(env, i);
1537 SSATmp* simplifyIsNType(State& env, const IRInstruction* i) {
1538 return isTypeImpl(env, i);
1541 SSATmp* simplifyIsScalarType(State& env, const IRInstruction* inst) {
1542 auto const src = inst->src(0);
1543 if (src->type().isKnownDataType()) {
1544 return cns(env, src->isA(TInt | TDbl | TStr | TBool));
1546 return nullptr;
1549 SSATmp* simplifyMethodExists(State& env, const IRInstruction* inst) {
1550 auto const src1 = inst->src(0);
1551 auto const src2 = inst->src(1);
1553 auto const clsSpec = src1->type().clsSpec();
1555 if (clsSpec.cls() != nullptr && src2->hasConstVal(TStr)) {
1556 // If we don't have an exact type, then we can't say for sure the class
1557 // doesn't have the method.
1558 auto const result = clsSpec.cls()->lookupMethod(src2->strVal()) != nullptr;
1559 return (clsSpec.exact() || result) ? cns(env, result) : nullptr;
1561 return nullptr;
1564 SSATmp* simplifyConcatStrStr(State& env, const IRInstruction* inst) {
1565 auto const src1 = inst->src(0);
1566 auto const src2 = inst->src(1);
1567 if (src1->hasConstVal(TStaticStr) &&
1568 src2->hasConstVal(TStaticStr)) {
1569 auto const str1 = const_cast<StringData*>(src1->strVal());
1570 auto const str2 = const_cast<StringData*>(src2->strVal());
1571 auto const sval = String::attach(concat_ss(str1, str2));
1572 return cns(env, makeStaticString(sval.get()));
1575 return nullptr;
1578 SSATmp* convToArrImpl(State& env, const IRInstruction* inst) {
1579 auto const src = inst->src(0);
1580 if (src->hasConstVal()) {
1581 Array arr = Array::Create(src->variantVal());
1582 return cns(env, ArrayData::GetScalarArray(arr.get()));
1584 return nullptr;
1587 SSATmp* simplifyConvBoolToArr(State& env, const IRInstruction* inst) {
1588 return convToArrImpl(env, inst);
1591 SSATmp* simplifyConvIntToArr(State& env, const IRInstruction* inst) {
1592 return convToArrImpl(env, inst);
1595 SSATmp* simplifyConvDblToArr(State& env, const IRInstruction* inst) {
1596 return convToArrImpl(env, inst);
1599 SSATmp* simplifyConvStrToArr(State& env, const IRInstruction* inst) {
1600 return convToArrImpl(env, inst);
1603 SSATmp* simplifyConvVecToArr(State& env, const IRInstruction* inst) {
1604 auto const src = inst->src(0);
1605 if (!src->hasConstVal()) return nullptr;
1606 auto const* array = src->arrVal();
1607 assertx(array->isVecArray());
1608 return cns(
1609 env,
1610 ArrayData::GetScalarArray(
1611 PackedArray::MakeFromVec(const_cast<ArrayData*>(array), true)
1616 SSATmp* simplifyConvDictToArr(State& env, const IRInstruction* inst) {
1617 auto const src = inst->src(0);
1618 if (!src->hasConstVal()) return nullptr;
1619 auto const* array = src->arrVal();
1620 assertx(array->isDict());
1621 return cns(
1622 env,
1623 ArrayData::GetScalarArray(
1624 MixedArray::MakeFromDict(const_cast<ArrayData*>(array), true)
1629 SSATmp* simplifyConvArrToBool(State& env, const IRInstruction* inst) {
1630 auto const src = inst->src(0);
1631 auto const kind = src->type().arrSpec().kind();
1632 if (src->isA(TStaticArr) || (kind && !arrayKindNeedsVsize(*kind))) {
1633 return gen(env, ConvIntToBool, gen(env, CountArrayFast, src));
1635 return nullptr;
1638 SSATmp* simplifyConvDblToBool(State& env, const IRInstruction* inst) {
1639 auto const src = inst->src(0);
1640 if (src->hasConstVal()) {
1641 bool const bval = src->dblVal();
1642 return cns(env, bval);
1644 return nullptr;
1647 SSATmp* simplifyConvIntToBool(State& env, const IRInstruction* inst) {
1648 auto const src = inst->src(0);
1649 if (src->hasConstVal()) {
1650 bool const bval = src->intVal();
1651 return cns(env, bval);
1653 return nullptr;
1656 SSATmp* simplifyConvStrToBool(State& env, const IRInstruction* inst) {
1657 auto const src = inst->src(0);
1658 if (src->hasConstVal()) {
1659 // only the strings "", and "0" convert to false, all other strings
1660 // are converted to true
1661 auto const str = src->strVal();
1662 return cns(env, !str->empty() && !str->isZero());
1664 return nullptr;
1667 SSATmp* simplifyConvArrToDbl(State& env, const IRInstruction* inst) {
1668 auto const src = inst->src(0);
1669 if (src->hasConstVal()) {
1670 if (src->arrVal()->empty()) {
1671 return cns(env, 0.0);
1674 return nullptr;
1677 SSATmp* simplifyConvBoolToDbl(State& env, const IRInstruction* inst) {
1678 auto const src = inst->src(0);
1679 if (src->hasConstVal()) {
1680 double const dval = src->boolVal();
1681 return cns(env, dval);
1683 return nullptr;
1686 SSATmp* simplifyConvIntToDbl(State& env, const IRInstruction* inst) {
1687 auto const src = inst->src(0);
1688 if (src->hasConstVal()) {
1689 double const dval = src->intVal();
1690 return cns(env, dval);
1692 if (src->inst()->is(ConvBoolToInt)) {
1693 // This is safe, because the bool src is not reference counted.
1694 return gen(env, ConvBoolToDbl, src->inst()->src(0));
1696 return nullptr;
1699 SSATmp* simplifyConvStrToDbl(State& env, const IRInstruction* inst) {
1700 auto const src = inst->src(0);
1701 return src->hasConstVal() ? cns(env, src->strVal()->toDouble()) : nullptr;
1704 SSATmp* simplifyConvArrToInt(State& env, const IRInstruction* inst) {
1705 auto const src = inst->src(0);
1706 if (src->hasConstVal()) {
1707 if (src->arrVal()->empty()) return cns(env, 0);
1708 return cns(env, 1);
1710 return nullptr;
1713 SSATmp* simplifyConvBoolToInt(State& env, const IRInstruction* inst) {
1714 auto const src = inst->src(0);
1715 if (src->hasConstVal()) return cns(env, static_cast<int>(src->boolVal()));
1716 return nullptr;
1719 SSATmp* simplifyConvDblToInt(State& env, const IRInstruction* inst) {
1720 auto const src = inst->src(0);
1721 if (src->hasConstVal()) return cns(env, toInt64(src->dblVal()));
1722 return nullptr;
1725 SSATmp* simplifyConvStrToInt(State& env, const IRInstruction* inst) {
1726 auto const src = inst->src(0);
1727 return src->hasConstVal() ? cns(env, src->strVal()->toInt64()) : nullptr;
1730 SSATmp* simplifyConvBoolToStr(State& env, const IRInstruction* inst) {
1731 auto const src = inst->src(0);
1732 if (src->hasConstVal()) {
1733 if (src->boolVal()) return cns(env, s_1.get());
1734 return cns(env, s_empty.get());
1736 return nullptr;
1739 SSATmp* simplifyConvDblToStr(State& env, const IRInstruction* inst) {
1740 auto const src = inst->src(0);
1741 if (src->hasConstVal()) {
1742 auto dblStr = String::attach(buildStringData(src->dblVal()));
1743 return cns(env, makeStaticString(dblStr));
1745 return nullptr;
1748 SSATmp* simplifyConvIntToStr(State& env, const IRInstruction* inst) {
1749 auto const src = inst->src(0);
1750 if (src->hasConstVal()) {
1751 return cns(env,
1752 makeStaticString(folly::to<std::string>(src->intVal()))
1755 return nullptr;
1758 SSATmp* simplifyConvCellToBool(State& env, const IRInstruction* inst) {
1759 auto const src = inst->src(0);
1760 auto const srcType = src->type();
1762 if (srcType <= TBool) return src;
1763 if (srcType <= TNull) return cns(env, false);
1764 if (srcType <= TArr) return gen(env, ConvArrToBool, src);
1765 if (srcType <= TDbl) return gen(env, ConvDblToBool, src);
1766 if (srcType <= TInt) return gen(env, ConvIntToBool, src);
1767 if (srcType <= TStr) return gen(env, ConvStrToBool, src);
1768 if (srcType <= TObj) {
1769 if (auto cls = srcType.clsSpec().cls()) {
1770 // We need to exclude interfaces like ConstSet. For now, just
1771 // skip anything that's an interface.
1772 if (!(cls->attrs() & AttrInterface)) {
1773 // t3429711 we should test cls->m_ODAttr
1774 // here, but currently it doesnt have all
1775 // the flags set.
1776 if (!cls->instanceCtor()) {
1777 return cns(env, true);
1781 return gen(env, ConvObjToBool, src);
1783 if (srcType <= TRes) return cns(env, true);
1785 return nullptr;
1788 SSATmp* simplifyConvCellToStr(State& env, const IRInstruction* inst) {
1789 auto const src = inst->src(0);
1790 auto const srcType = src->type();
1791 auto const catchTrace = inst->taken();
1793 if (srcType <= TBool) return gen(env, ConvBoolToStr, src);
1794 if (srcType <= TNull) return cns(env, s_empty.get());
1795 if (srcType <= TArr) {
1796 gen(env, RaiseNotice, catchTrace,
1797 cns(env, makeStaticString("Array to string conversion")));
1798 return cns(env, s_Array.get());
1800 if (srcType <= TDbl) return gen(env, ConvDblToStr, src);
1801 if (srcType <= TInt) return gen(env, ConvIntToStr, src);
1802 if (srcType <= TStr) {
1803 gen(env, IncRef, src);
1804 return src;
1806 if (srcType <= TObj) return gen(env, ConvObjToStr, catchTrace, src);
1807 if (srcType <= TRes) return gen(env, ConvResToStr, catchTrace, src);
1809 return nullptr;
1812 SSATmp* simplifyConvCellToInt(State& env, const IRInstruction* inst) {
1813 auto const src = inst->src(0);
1814 auto const srcType = src->type();
1816 if (srcType <= TInt) return src;
1817 if (srcType <= TNull) return cns(env, 0);
1818 if (srcType <= TArr) return gen(env, ConvArrToInt, src);
1819 if (srcType <= TBool) return gen(env, ConvBoolToInt, src);
1820 if (srcType <= TDbl) return gen(env, ConvDblToInt, src);
1821 if (srcType <= TStr) return gen(env, ConvStrToInt, src);
1822 if (srcType <= TObj) return gen(env, ConvObjToInt, inst->taken(), src);
1823 if (srcType <= TRes) return gen(env, ConvResToInt, src);
1825 return nullptr;
1828 SSATmp* simplifyConvCellToDbl(State& env, const IRInstruction* inst) {
1829 auto const src = inst->src(0);
1830 auto const srcType = src->type();
1832 if (srcType <= TDbl) return src;
1833 if (srcType <= TNull) return cns(env, 0.0);
1834 if (srcType <= TArr) return gen(env, ConvArrToDbl, src);
1835 if (srcType <= TBool) return gen(env, ConvBoolToDbl, src);
1836 if (srcType <= TInt) return gen(env, ConvIntToDbl, src);
1837 if (srcType <= TStr) return gen(env, ConvStrToDbl, src);
1838 if (srcType <= TObj) return gen(env, ConvObjToDbl, inst->taken(), src);
1839 if (srcType <= TRes) return gen(env, ConvResToDbl, src);
1841 return nullptr;
1844 SSATmp* simplifyConvObjToBool(State& env, const IRInstruction* inst) {
1845 auto const ty = inst->src(0)->type();
1847 if (!irgen::typeMightRelax(inst->src(0)) &&
1848 ty < TObj &&
1849 ty.clsSpec().cls() &&
1850 ty.clsSpec().cls()->isCollectionClass()) {
1851 return gen(env, ColIsNEmpty, inst->src(0));
1853 return nullptr;
1856 SSATmp* simplifyConvCellToObj(State& env, const IRInstruction* inst) {
1857 if (inst->src(0)->isA(TObj)) return inst->src(0);
1858 return nullptr;
1861 SSATmp* simplifyCoerceCellToBool(State& env, const IRInstruction* inst) {
1862 auto const src = inst->src(0);
1863 auto const srcType = src->type();
1865 if (srcType.subtypeOfAny(TBool, TNull, TDbl,
1866 TInt, TStr)) {
1867 return gen(env, ConvCellToBool, src);
1870 // We actually know that any other type will fail causing us to side exit
1871 // but there's no easy way to optimize for that
1873 return nullptr;
1876 SSATmp* simplifyCoerceCellToInt(State& env, const IRInstruction* inst) {
1877 auto const src = inst->src(0);
1878 auto const srcType = src->type();
1880 if (srcType.subtypeOfAny(TInt, TBool, TNull, TDbl,
1881 TBool)) {
1882 return gen(env, ConvCellToInt, inst->taken(), src);
1885 if (srcType <= TStr) return gen(env, CoerceStrToInt, inst->taken(),
1886 *inst->extra<CoerceCellToInt>(), src);
1888 // We actually know that any other type will fail causing us to side exit
1889 // but there's no easy way to optimize for that
1891 return nullptr;
1894 SSATmp* simplifyCoerceCellToDbl(State& env, const IRInstruction* inst) {
1895 auto const src = inst->src(0);
1896 auto const srcType = src->type();
1898 if (srcType.subtypeOfAny(TInt, TBool, TNull, TDbl,
1899 TBool)) {
1900 return gen(env, ConvCellToDbl, inst->taken(), src);
1903 if (srcType <= TStr) return gen(env, CoerceStrToDbl, inst->taken(),
1904 *inst->extra<CoerceCellToDbl>(), src);
1906 // We actually know that any other type will fail causing us to side exit
1907 // but there's no easy way to optimize for that
1909 return nullptr;
1912 SSATmp* roundImpl(State& env, const IRInstruction* inst, double (*op)(double)) {
1913 auto const src = inst->src(0);
1915 if (src->hasConstVal()) {
1916 return cns(env, op(src->dblVal()));
1919 auto srcInst = src->inst();
1920 if (srcInst->op() == ConvIntToDbl || srcInst->op() == ConvBoolToDbl) {
1921 return src;
1924 return nullptr;
1927 SSATmp* simplifyFloor(State& env, const IRInstruction* inst) {
1928 return roundImpl(env, inst, floor);
1931 SSATmp* simplifyCeil(State& env, const IRInstruction* inst) {
1932 return roundImpl(env, inst, ceil);
1935 SSATmp* simplifyUnboxPtr(State& env, const IRInstruction* inst) {
1936 if (inst->src(0)->isA(TPtrToCell)) {
1937 return inst->src(0);
1939 return nullptr;
1942 SSATmp* simplifyBoxPtr(State& env, const IRInstruction* inst) {
1943 if (inst->src(0)->isA(TPtrToBoxedCell)) {
1944 return inst->src(0);
1946 return nullptr;
1949 SSATmp* simplifyCheckInit(State& env, const IRInstruction* inst) {
1950 auto const srcType = inst->src(0)->type();
1951 assertx(!srcType.maybe(TPtrToGen));
1952 assertx(inst->taken());
1953 if (!srcType.maybe(TUninit)) return gen(env, Nop);
1954 return mergeBranchDests(env, inst);
1957 SSATmp* simplifyCheckInitMem(State& env, const IRInstruction* inst) {
1958 return mergeBranchDests(env, inst);
1961 SSATmp* simplifyCheckInitProps(State& env, const IRInstruction* inst) {
1962 return mergeBranchDests(env, inst);
1965 SSATmp* simplifyCheckInitSProps(State& env, const IRInstruction* inst) {
1966 return mergeBranchDests(env, inst);
1969 SSATmp* simplifyInitObjProps(State& env, const IRInstruction* inst) {
1970 auto const cls = inst->extra<InitObjProps>()->cls;
1971 if (cls->getODAttrs() == 0 && cls->numDeclProperties() == 0) {
1972 return gen(env, Nop);
1974 return nullptr;
1977 SSATmp* simplifyCheckType(State& env, const IRInstruction* inst) {
1978 auto const typeParam = inst->typeParam();
1979 auto const srcType = inst->src(0)->type();
1981 if (!srcType.maybe(typeParam) || inst->next() == inst->taken()) {
1983 * Convert the check into a Jmp. The dest of the CheckType (which would've
1984 * been Bottom) is now never going to be defined, so we return a Bottom.
1986 gen(env, Jmp, inst->taken());
1987 return cns(env, TBottom);
1990 auto const newType = srcType & typeParam;
1991 if (srcType <= newType) {
1992 // The type of the src is the same or more refined than type, so the guard
1993 // is unnecessary.
1994 return inst->src(0);
1997 return nullptr;
2000 SSATmp* simplifyCheckTypeMem(State& env, const IRInstruction* inst) {
2001 if (inst->next() == inst->taken() ||
2002 inst->typeParam() == TBottom) {
2003 return gen(env, Jmp, inst->taken());
2006 return nullptr;
2009 SSATmp* simplifyAssertType(State& env, const IRInstruction* inst) {
2010 auto const src = inst->src(0);
2012 return canSimplifyAssertType(inst, src->type(), mightRelax(env, src))
2013 ? src
2014 : nullptr;
2017 SSATmp* simplifyCheckLoc(State& env, const IRInstruction* inst) {
2018 return mergeBranchDests(env, inst);
2021 SSATmp* simplifyCheckStk(State& env, const IRInstruction* inst) {
2022 return mergeBranchDests(env, inst);
2025 SSATmp* simplifyCheckClosureStaticLocInit(State& env,
2026 const IRInstruction* inst) {
2027 return mergeBranchDests(env, inst);
2030 SSATmp* simplifyCheckRefInner(State& env, const IRInstruction* inst) {
2031 // Ref inner cells are at worst InitCell, so don't bother checking for that.
2032 if (TInitCell <= inst->typeParam()) {
2033 return gen(env, Nop);
2035 return mergeBranchDests(env, inst);
2038 SSATmp* simplifyDefLabel(State& env, const IRInstruction* inst) {
2039 if (inst->numDsts() == 0) {
2040 return gen(env, Nop);
2042 return nullptr;
2045 SSATmp* decRefImpl(State& env, const IRInstruction* inst) {
2046 auto const src = inst->src(0);
2047 if (!mightRelax(env, src) && !src->type().maybe(TCounted)) {
2048 return gen(env, Nop);
2050 return nullptr;
2053 SSATmp* simplifyDecRef(State& env, const IRInstruction* inst) {
2054 return decRefImpl(env, inst);
2057 SSATmp* simplifyDecRefNZ(State& env, const IRInstruction* inst) {
2058 return decRefImpl(env, inst);
2061 SSATmp* simplifyIncRef(State& env, const IRInstruction* inst) {
2062 auto const src = inst->src(0);
2063 if (!mightRelax(env, src) && !src->type().maybe(TCounted)) {
2064 return gen(env, Nop);
2066 return nullptr;
2069 SSATmp* simplifyIncRefCtx(State& env, const IRInstruction* inst) {
2070 auto const ctx = inst->src(0);
2071 if (ctx->isA(TObj)) {
2072 return gen(env, IncRef, ctx);
2073 } else if (!mightRelax(env, ctx) && !ctx->type().maybe(TCounted)) {
2074 return gen(env, Nop);
2077 return nullptr;
2080 SSATmp* condJmpImpl(State& env, const IRInstruction* inst) {
2081 assertx(inst->is(JmpZero, JmpNZero));
2082 // Both ways go to the same block.
2083 if (inst->taken() == inst->next()) {
2084 assertx(inst->taken() != nullptr);
2085 return gen(env, Jmp, inst->taken());
2088 auto const src = inst->src(0);
2089 auto const srcInst = src->inst();
2091 // Constant propagate.
2092 if (src->hasConstVal()) {
2093 bool val = src->isA(TBool) ? src->boolVal()
2094 : static_cast<bool>(src->intVal());
2095 if (val == inst->is(JmpNZero)) {
2096 // always taken
2097 assertx(inst->taken());
2098 return gen(env, Jmp, inst->taken());
2100 // Never taken. Since simplify() is also run when building the IR,
2101 // inst->next() could be nullptr at this moment.
2102 return gen(env, Nop);
2105 auto absorb = [&](){
2106 return gen(env, inst->op(), inst->taken(), srcInst->src(0));
2108 auto absorbOpp = [&](){
2109 return gen(env,
2110 inst->op() == JmpZero ? JmpNZero : JmpZero,
2111 inst->taken(),
2112 srcInst->src(0)
2116 // Absorb negations.
2117 if (srcInst->is(XorBool) && srcInst->src(1)->hasConstVal(true)) {
2118 return absorbOpp();
2121 // Absorb ConvIntToBool.
2122 if (srcInst->is(ConvIntToBool)) {
2123 return absorb();
2126 // Absorb boolean comparisons.
2127 if (srcInst->is(EqBool) && srcInst->src(1)->hasConstVal()) {
2128 return srcInst->src(1)->boolVal() ? absorb() : absorbOpp();
2130 if (srcInst->is(NeqBool) && srcInst->src(1)->hasConstVal()) {
2131 return srcInst->src(1)->boolVal() ? absorbOpp() : absorb();
2134 // Absorb integer comparisons against constant zero.
2135 if (srcInst->is(EqInt) && srcInst->src(1)->hasConstVal(0)) {
2136 return absorbOpp();
2138 if (srcInst->is(NeqInt) && srcInst->src(1)->hasConstVal(0)) {
2139 return absorb();
2142 return nullptr;
2145 SSATmp* simplifyJmpZero(State& env, const IRInstruction* i) {
2146 return condJmpImpl(env, i);
2149 SSATmp* simplifyJmpNZero(State& env, const IRInstruction* i) {
2150 return condJmpImpl(env, i);
2154 SSATmp* simplifyAssertNonNull(State& env, const IRInstruction* inst) {
2155 if (!inst->src(0)->type().maybe(TNullptr)) {
2156 return inst->src(0);
2158 return nullptr;
2161 SSATmp* simplifyCheckPackedArrayBounds(State& env, const IRInstruction* inst) {
2162 auto const array = inst->src(0);
2163 auto const idx = inst->src(1);
2164 if (!idx->hasConstVal()) return mergeBranchDests(env, inst);
2166 auto const idxVal = idx->intVal();
2167 switch (packedArrayBoundsStaticCheck(array->type(), idxVal)) {
2168 case PackedBounds::In: return gen(env, Nop);
2169 case PackedBounds::Out: return gen(env, Jmp, inst->taken());
2170 case PackedBounds::Unknown: break;
2173 return mergeBranchDests(env, inst);
2176 SSATmp* arrIntKeyImpl(State& env, const IRInstruction* inst) {
2177 auto const arr = inst->src(0);
2178 auto const idx = inst->src(1);
2179 assertx(arr->hasConstVal(TArr));
2180 assertx(idx->hasConstVal(TInt));
2181 assertx(!arr->arrVal()->isDict());
2182 assertx(!arr->arrVal()->isVecArray());
2183 auto const value = arr->arrVal()->nvGet(idx->intVal());
2184 return value ? cns(env, *value) : nullptr;
2187 SSATmp* arrStrKeyImpl(State& env, const IRInstruction* inst) {
2188 auto const arr = inst->src(0);
2189 auto const idx = inst->src(1);
2190 assertx(arr->hasConstVal(TArr));
2191 assertx(idx->hasConstVal(TStr));
2192 assertx(!arr->arrVal()->isDict());
2193 assertx(!arr->arrVal()->isVecArray());
2194 auto const value = [&] {
2195 int64_t val;
2196 if (arr->arrVal()->convertKey(idx->strVal(), val)) {
2197 return arr->arrVal()->nvGet(val);
2199 return arr->arrVal()->nvGet(idx->strVal());
2200 }();
2201 return value ? cns(env, *value) : nullptr;
2204 SSATmp* hackArrayGetImpl(State& env, const ArrayData* arr, SSATmp* key) {
2205 assertx(key->hasConstVal(TStr) || key->hasConstVal(TInt));
2206 assertx(arr->isDict() || arr->isVecArray());
2207 if (key->type() <= TInt) {
2208 auto r = arr->nvGet(key->intVal());
2209 return r ? cns(env, *r) : nullptr;
2211 if (key->type() <= TStr) {
2212 auto r = arr->nvGet(key->strVal());
2213 return r ? cns(env, *r) : nullptr;
2215 return nullptr;
2218 SSATmp* simplifyArrayGet(State& env, const IRInstruction* inst) {
2219 if (inst->src(0)->hasConstVal() && inst->src(1)->hasConstVal()) {
2220 auto const arr = inst->src(0)->arrVal();
2221 if (arr->isDict() || arr->isVecArray()) {
2222 return hackArrayGetImpl(env, arr, inst->src(1));
2224 if (inst->src(1)->type() <= TInt) {
2225 if (auto result = arrIntKeyImpl(env, inst)) {
2226 return result;
2228 gen(env, RaiseArrayIndexNotice, inst->taken(), inst->src(1));
2229 return cns(env, TInitNull);
2231 if (inst->src(1)->type() <= TStr) {
2232 if (auto result = arrStrKeyImpl(env, inst)) {
2233 return result;
2235 gen(env, RaiseArrayKeyNotice, inst->taken(), inst->src(1));
2236 return cns(env, TInitNull);
2239 return nullptr;
2242 SSATmp* simplifyMixedArrayGetK(State& env, const IRInstruction* inst) {
2243 if (inst->src(0)->hasConstVal() && inst->src(1)->hasConstVal()) {
2244 if (inst->src(1)->type() <= TInt) {
2245 if (auto result = arrIntKeyImpl(env, inst)) {
2246 return result;
2249 if (inst->src(1)->type() <= TStr) {
2250 if (auto result = arrStrKeyImpl(env, inst)) {
2251 return result;
2255 return nullptr;
2258 SSATmp* simplifyArrayIdx(State& env, const IRInstruction* inst) {
2259 if (inst->src(0)->hasConstVal() && inst->src(1)->hasConstVal()) {
2260 auto const arr = inst->src(0)->arrVal();
2261 if (arr->isDict() || arr->isVecArray()) {
2262 auto r = hackArrayGetImpl(env, arr, inst->src(1));
2263 return r ? r : inst->src(2);
2265 if (inst->src(1)->isA(TInt)) {
2266 if (auto result = arrIntKeyImpl(env, inst)) {
2267 return result;
2269 return inst->src(2);
2271 if (inst->src(1)->isA(TStr)) {
2272 if (auto result = arrStrKeyImpl(env, inst)) {
2273 return result;
2275 return inst->src(2);
2278 return nullptr;
2281 SSATmp* simplifyAKExistsArr(State& env, const IRInstruction* inst) {
2282 if (inst->src(0)->hasConstVal() && inst->src(1)->hasConstVal()) {
2283 auto const arr = inst->src(0)->arrVal();
2284 if (arr->isDict() || arr->isVecArray()) {
2285 return cns(env, hackArrayGetImpl(env, arr, inst->src(1)) != nullptr);
2287 if (inst->src(1)->isA(TInt)) {
2288 if (arrIntKeyImpl(env, inst)) {
2289 return cns(env, true);
2291 } else if (inst->src(1)->isA(TStr)) {
2292 if (arrStrKeyImpl(env, inst)) {
2293 return cns(env, true);
2296 return cns(env, false);
2298 return nullptr;
2301 SSATmp* simplifyCount(State& env, const IRInstruction* inst) {
2302 auto const val = inst->src(0);
2303 auto const ty = val->type();
2305 if (ty <= TNull) return cns(env, 0);
2307 auto const oneTy = TBool | TInt | TDbl | TStr | TRes;
2308 if (ty <= oneTy) return cns(env, 1);
2310 if (ty <= TArr) return gen(env, CountArray, val);
2312 if (ty < TObj) {
2313 auto const cls = ty.clsSpec().cls();
2314 if (!mightRelax(env, val) && cls != nullptr && cls->isCollectionClass()) {
2315 return gen(env, CountCollection, val);
2318 return nullptr;
2321 SSATmp* simplifyCountArrayFast(State& env, const IRInstruction* inst) {
2322 auto const src = inst->src(0);
2323 if (inst->src(0)->hasConstVal(TArr)) return cns(env, src->arrVal()->size());
2324 auto const arrSpec = src->type().arrSpec();
2325 auto const at = arrSpec.type();
2326 if (!at) return nullptr;
2327 using A = RepoAuthType::Array;
2328 switch (at->tag()) {
2329 case A::Tag::Packed:
2330 if (at->emptiness() == A::Empty::No) {
2331 return cns(env, at->size());
2333 break;
2334 case A::Tag::PackedN:
2335 break;
2337 return nullptr;
2340 SSATmp* simplifyCountArray(State& env, const IRInstruction* inst) {
2341 auto const src = inst->src(0);
2342 auto const ty = src->type();
2344 if (src->hasConstVal()) return cns(env, src->arrVal()->size());
2346 auto const kind = ty.arrSpec().kind();
2348 if (kind && !mightRelax(env, src) && !arrayKindNeedsVsize(*kind))
2349 return gen(env, CountArrayFast, src);
2350 else
2351 return nullptr;
2354 SSATmp* simplifyLdClsName(State& env, const IRInstruction* inst) {
2355 auto const src = inst->src(0);
2356 return src->hasConstVal(TCls) ? cns(env, src->clsVal()->name()) : nullptr;
2359 SSATmp* simplifyLdStrLen(State& env, const IRInstruction* inst) {
2360 auto const src = inst->src(0);
2361 return src->hasConstVal(TStr) ? cns(env, src->strVal()->size()) : nullptr;
2364 SSATmp* simplifyCallBuiltin(State& env, const IRInstruction* inst) {
2365 auto const callee = inst->extra<CallBuiltin>()->callee;
2366 auto const args = inst->srcs();
2369 bool const arg2IsCollection = args.size() == 3 &&
2370 args[2]->isA(TObj) &&
2371 args[2]->type().clsSpec() &&
2372 args[2]->type().clsSpec().cls()->isCollectionClass() &&
2373 !mightRelax(env, args[2]);
2375 if (arg2IsCollection) {
2376 if (callee->name()->isame(s_isEmpty.get())) {
2377 FTRACE(3, "simplifying collection: {}\n", callee->name()->data());
2378 return gen(env, ColIsEmpty, args[2]);
2380 if (callee->name()->isame(s_count.get())) {
2381 FTRACE(3, "simplifying collection: {}\n", callee->name()->data());
2382 return gen(env, CountCollection, args[2]);
2386 bool const arg2IsWaitHandle = !arg2IsCollection &&
2387 args.size() == 3 &&
2388 args[2]->isA(TObj) &&
2389 args[2]->type().clsSpec() &&
2390 args[2]->type().clsSpec().cls()->classof(c_WaitHandle::classof()) &&
2391 !mightRelax(env, args[2]);
2393 if (arg2IsWaitHandle) {
2394 const auto genState = [&] (Opcode op, int64_t whstate) -> SSATmp* {
2395 // these methods all spring from the base class
2396 assert(callee->cls()->name()->isame(s_WaitHandle.get()));
2397 const auto state = gen(env, LdWHState, args[2]);
2398 return gen(env, op, state, cns(env, whstate));
2400 const auto methName = callee->name();
2401 if (methName->isame(s_isFinished.get())) {
2402 return genState(LteInt, int64_t{c_WaitHandle::STATE_FAILED});
2404 if (methName->isame(s_isSucceeded.get())) {
2405 return genState(EqInt, int64_t{c_WaitHandle::STATE_SUCCEEDED});
2407 if (methName->isame(s_isFailed.get())) {
2408 return genState(EqInt, int64_t{c_WaitHandle::STATE_FAILED});
2412 return nullptr;
2415 SSATmp* simplifyIsWaitHandle(State& env, const IRInstruction* inst) {
2416 if (mightRelax(env, inst->src(0))) return nullptr;
2418 bool baseIsWaitHandle = inst->src(0)->isA(TObj) &&
2419 inst->src(0)->type().clsSpec() &&
2420 inst->src(0)->type().clsSpec().cls()->classof(c_WaitHandle::classof());
2421 if (baseIsWaitHandle) {
2422 return cns(env, true);
2424 return nullptr;
2427 SSATmp* simplifyIsCol(State& env, const IRInstruction* inst) {
2428 auto const ty = inst->src(0)->type();
2430 if (!irgen::typeMightRelax(inst->src(0)) &&
2431 ty < TObj &&
2432 ty.clsSpec().cls()) {
2433 return cns(env, ty.clsSpec().cls()->isCollectionClass());
2435 return nullptr;
2438 SSATmp* simplifyHasToString(State& env, const IRInstruction* inst) {
2439 auto const src = inst->src(0);
2441 if (!mightRelax(env, src) &&
2442 src->isA(TObj) &&
2443 src->type().clsSpec()) {
2444 return cns(
2445 env,
2446 src->type().clsSpec().cls()->getToString() != nullptr
2449 return nullptr;
2452 SSATmp* simplifyOrdStr(State& env, const IRInstruction* inst) {
2453 const auto src = inst->src(0);
2454 if (src->hasConstVal(TStr)) {
2455 // a static string is passed in, resolve with a constant.
2456 unsigned char first = src->strVal()->data()[0];
2457 return cns(env, int64_t{first});
2459 return nullptr;
2462 SSATmp* ldImpl(State& env, const IRInstruction* inst) {
2463 if (env.typesMightRelax) return nullptr;
2465 auto const t = inst->typeParam();
2467 return t.hasConstVal() ||
2468 t.subtypeOfAny(TUninit, TInitNull, TNullptr)
2469 ? cns(env, t)
2470 : nullptr;
2473 SSATmp* simplifyLdLoc(State& env, const IRInstruction* inst) {
2474 return ldImpl(env, inst);
2477 SSATmp* simplifyLdStk(State& env, const IRInstruction* inst) {
2478 return ldImpl(env, inst);
2481 SSATmp* simplifyJmpSwitchDest(State& env, const IRInstruction* inst) {
2482 auto const index = inst->src(0);
2483 if (!index->hasConstVal(TInt)) return nullptr;
2485 auto indexVal = index->intVal();
2486 auto const sp = inst->src(1);
2487 auto const fp = inst->src(2);
2488 auto const& extra = *inst->extra<JmpSwitchDest>();
2490 if (indexVal < 0 || indexVal >= extra.cases) {
2491 // Instruction is unreachable.
2492 return gen(env, Halt);
2495 auto const newExtra = ReqBindJmpData{extra.targets[indexVal], extra.invSPOff,
2496 extra.irSPOff, TransFlags{}};
2497 return gen(env, ReqBindJmp, newExtra, sp, fp);
2500 SSATmp* simplifyCheckRange(State& env, const IRInstruction* inst) {
2501 auto val = inst->src(0);
2502 auto limit = inst->src(1);
2504 // CheckRange returns (0 <= val < limit).
2505 if (val->hasConstVal(TInt)) {
2506 if (val->intVal() < 0) return cns(env, false);
2508 if (limit->hasConstVal(TInt)) {
2509 return cns(env, val->intVal() < limit->intVal());
2513 if (limit->hasConstVal(TInt) && limit->intVal() <= 0) return cns(env, false);
2515 return nullptr;
2518 //////////////////////////////////////////////////////////////////////
2520 SSATmp* simplifyWork(State& env, const IRInstruction* inst) {
2521 env.insts.push(inst);
2522 SCOPE_EXIT {
2523 assertx(env.insts.top() == inst);
2524 env.insts.pop();
2527 #define X(x) case x: return simplify##x(env, inst);
2528 switch (inst->op()) {
2529 X(Shl)
2530 X(Shr)
2531 X(AbsDbl)
2532 X(AssertNonNull)
2533 X(BoxPtr)
2534 X(CallBuiltin)
2535 X(Ceil)
2536 X(CheckInit)
2537 X(CheckInitMem)
2538 X(CheckInitProps)
2539 X(CheckInitSProps)
2540 X(CheckLoc)
2541 X(CheckRefInner)
2542 X(CheckStk)
2543 X(CheckClosureStaticLocInit)
2544 X(CheckType)
2545 X(CheckTypeMem)
2546 X(AssertType)
2547 X(CheckPackedArrayBounds)
2548 X(CoerceCellToBool)
2549 X(CoerceCellToDbl)
2550 X(CoerceCellToInt)
2551 X(ConcatStrStr)
2552 X(ConvArrToBool)
2553 X(ConvArrToDbl)
2554 X(ConvArrToInt)
2555 X(ConvBoolToArr)
2556 X(ConvBoolToDbl)
2557 X(ConvBoolToInt)
2558 X(ConvBoolToStr)
2559 X(ConvCellToBool)
2560 X(ConvCellToDbl)
2561 X(ConvCellToInt)
2562 X(ConvCellToObj)
2563 X(ConvCellToStr)
2564 X(ConvClsToCctx)
2565 X(ConvDblToArr)
2566 X(ConvDblToBool)
2567 X(ConvDblToInt)
2568 X(ConvDblToStr)
2569 X(ConvIntToArr)
2570 X(ConvIntToBool)
2571 X(ConvIntToDbl)
2572 X(ConvIntToStr)
2573 X(ConvObjToBool)
2574 X(ConvStrToArr)
2575 X(ConvVecToArr)
2576 X(ConvDictToArr)
2577 X(ConvStrToBool)
2578 X(ConvStrToDbl)
2579 X(ConvStrToInt)
2580 X(Count)
2581 X(CountArray)
2582 X(CountArrayFast)
2583 X(DecRef)
2584 X(DecRefNZ)
2585 X(DefLabel)
2586 X(DivDbl)
2587 X(DivInt)
2588 X(ExtendsClass)
2589 X(Floor)
2590 X(GetCtxFwdCall)
2591 X(IncRef)
2592 X(IncRefCtx)
2593 X(InitObjProps)
2594 X(InstanceOf)
2595 X(InstanceOfIface)
2596 X(IsNType)
2597 X(IsScalarType)
2598 X(IsType)
2599 X(IsWaitHandle)
2600 X(IsCol)
2601 X(HasToString)
2602 X(LdClsCtx)
2603 X(LdClsName)
2604 X(LdClsMethod)
2605 X(LdStrLen)
2606 X(MethodExists)
2607 X(CheckCtxThis)
2608 X(CastCtxThis)
2609 X(LdObjClass)
2610 X(LdObjInvoke)
2611 X(Mov)
2612 X(UnboxPtr)
2613 X(JmpZero)
2614 X(JmpNZero)
2615 X(OrInt)
2616 X(AddInt)
2617 X(SubInt)
2618 X(MulInt)
2619 X(AddDbl)
2620 X(SubDbl)
2621 X(MulDbl)
2622 X(Mod)
2623 X(AndInt)
2624 X(XorInt)
2625 X(XorBool)
2626 X(AddIntO)
2627 X(SubIntO)
2628 X(MulIntO)
2629 X(GtBool)
2630 X(GteBool)
2631 X(LtBool)
2632 X(LteBool)
2633 X(EqBool)
2634 X(NeqBool)
2635 X(CmpBool)
2636 X(GtInt)
2637 X(GteInt)
2638 X(LtInt)
2639 X(LteInt)
2640 X(EqInt)
2641 X(NeqInt)
2642 X(CmpInt)
2643 X(GtStr)
2644 X(GteStr)
2645 X(LtStr)
2646 X(LteStr)
2647 X(EqStr)
2648 X(NeqStr)
2649 X(SameStr)
2650 X(NSameStr)
2651 X(CmpStr)
2652 X(GtStrInt)
2653 X(GteStrInt)
2654 X(LtStrInt)
2655 X(LteStrInt)
2656 X(EqStrInt)
2657 X(NeqStrInt)
2658 X(CmpStrInt)
2659 X(GtObj)
2660 X(GteObj)
2661 X(LtObj)
2662 X(LteObj)
2663 X(EqObj)
2664 X(NeqObj)
2665 X(SameObj)
2666 X(NSameObj)
2667 X(GtArr)
2668 X(GteArr)
2669 X(LtArr)
2670 X(LteArr)
2671 X(EqArr)
2672 X(NeqArr)
2673 X(SameArr)
2674 X(NSameArr)
2675 X(GtRes)
2676 X(GteRes)
2677 X(LtRes)
2678 X(LteRes)
2679 X(EqRes)
2680 X(NeqRes)
2681 X(CmpRes)
2682 X(EqCls)
2683 X(ArrayGet)
2684 X(MixedArrayGetK)
2685 X(ArrayIdx)
2686 X(AKExistsArr)
2687 X(OrdStr)
2688 X(LdLoc)
2689 X(LdStk)
2690 X(JmpSwitchDest)
2691 X(CheckRange)
2692 X(SpillFrame)
2693 default: break;
2695 #undef X
2696 return nullptr;
2699 //////////////////////////////////////////////////////////////////////
2703 //////////////////////////////////////////////////////////////////////
2705 bool canSimplifyAssertType(const IRInstruction* inst,
2706 const Type srcType,
2707 bool srcMightRelax) {
2708 assert(inst->is(AssertType, AssertLoc, AssertStk));
2710 auto const typeParam = inst->typeParam();
2712 if (!srcType.maybe(typeParam)) {
2713 // If both types are boxed, this is okay and even expected as a means to
2714 // update the hint for the inner type.
2715 if (srcType <= TBoxedCell &&
2716 typeParam <= TBoxedCell) {
2717 return false;
2720 // We got external information (probably from static analysis) that
2721 // conflicts with what we've built up so far. There's no reasonable way to
2722 // continue here: we can't properly fatal the request because we can't make
2723 // a catch trace or SpillStack without IRGS, and we can't punt on
2724 // just this instruction because we might not be in the initial translation
2725 // phase, and we can't just plow on forward since we'll probably generate
2726 // malformed IR. Since this case is very rare, just punt on the whole
2727 // trace so it gets interpreted.
2728 TRACE_PUNT("Invalid AssertTypeOp");
2731 // Asserting in these situations doesn't add any information.
2732 if (typeParam == TCls && srcType <= TCls) return true;
2733 if (typeParam == TGen && srcType <= TGen) return true;
2735 auto const newType = srcType & typeParam;
2737 if (srcType <= newType) {
2738 // The src type is at least as good as the new type. Eliminate this
2739 // AssertType if the src type won't relax. We do this to avoid eliminating
2740 // apparently redundant assert opcodes that may become useful after prior
2741 // guards are relaxed.
2742 if (!srcMightRelax) return true;
2744 if (srcType < newType) {
2745 // This can happen because of limitations in how Type::operator& handles
2746 // specialized types: sometimes it returns a Type that's wider than it
2747 // needs to be. It shouldn't affect correctness but it can cause us to
2748 // miss out on some perf.
2749 FTRACE_MOD(Trace::hhir, 1,
2750 "Suboptimal AssertTypeOp: refineType({}, {}) -> {} in {}\n",
2751 srcType, typeParam, newType, *inst);
2753 // We don't currently support intersecting RepoAuthType::Arrays
2754 // (t4473238), so we might be here because srcType and typeParam have
2755 // different RATArrays. If that's the case, and if typeParam provides no
2756 // other useful information, we can unconditionally eliminate this
2757 // instruction: RATArrays never come from guards so we can't miss out on
2758 // anything by doing so.
2759 if (srcType < TArr &&
2760 srcType.arrSpec().type() &&
2761 typeParam < TArr &&
2762 typeParam.arrSpec().type() &&
2763 !typeParam.arrSpec().kind()) {
2764 return true;
2769 return false;
2772 //////////////////////////////////////////////////////////////////////
2774 SimplifyResult simplify(IRUnit& unit,
2775 const IRInstruction* origInst,
2776 bool typesMightRelax) {
2777 auto env = State { unit, typesMightRelax };
2778 auto const newDst = simplifyWork(env, origInst);
2780 assertx(validate(env, newDst, origInst));
2782 return SimplifyResult { std::move(env.newInsts), newDst };
2785 void simplify(IRUnit& unit, IRInstruction* origInst) {
2786 assertx(!origInst->isTransient());
2788 copyProp(origInst);
2789 auto res = simplify(unit, origInst, false);
2791 // No simplification occurred; nothing to do.
2792 if (res.instrs.empty() && !res.dst) return;
2794 FTRACE(1, "simplifying: {}\n", origInst->toString());
2796 if (origInst->isBlockEnd()) {
2797 auto const next = origInst->block()->next();
2799 if (res.instrs.empty() || !res.instrs.back()->isBlockEnd()) {
2800 // Our block-end instruction was eliminated (most likely a Jmp* converted
2801 // to a Nop). Replace it with a Jmp to the next block.
2802 res.instrs.push_back(unit.gen(Jmp, origInst->marker(), next));
2805 auto last = res.instrs.back();
2806 assertx(last->isBlockEnd());
2808 if (!last->isTerminal() && !last->next()) {
2809 // We converted the block-end instruction to a different one. Set its
2810 // next block appropriately.
2811 last->setNext(next);
2815 size_t out_size = 0;
2816 bool need_mov = res.dst;
2817 IRInstruction* last = nullptr;
2819 for (auto inst : res.instrs) {
2820 if (inst->is(Nop)) continue;
2822 ++out_size;
2823 last = inst;
2825 if (res.dst && res.dst == inst->dst()) {
2826 // One of the new instructions produced the new dst. Since we're going
2827 // to drop `origInst', just use origInst->dst() instead.
2828 inst->setDst(origInst->dst());
2829 inst->dst()->setInstruction(inst);
2830 need_mov = false;
2834 auto const block = origInst->block();
2835 auto const pos = ++block->iteratorTo(origInst);
2837 if (need_mov) {
2839 * In `killed_edge_defining' we have the case that an instruction defining
2840 * a temp on an edge (like CheckType) determined it can never define that
2841 * tmp. In this situation we just Nop out the instruction and leave the
2842 * old tmp dangling. The reason this is ok is that one of the following
2843 * two things are happening:
2845 * o The old next() block is becoming unreachable. It's ok not to make
2846 * a new definition of this tmp, because the code running simplify is
2847 * going to have to track unreachable blocks and avoid looking at
2848 * them. It will also have to remove unreachable blocks when it's
2849 * finished to maintain IR invariants (e.g. through DCE::Minimal),
2850 * which will mean the uses of the no-longer-defined tmp will go away.
2852 * o The old next() block is still reachable (e.g. if we're removing a
2853 * CheckType because it had next == taken). But in this case, the
2854 * next() edge must have been a critical edge, and therefore nothing
2855 * could have any use of the old destination of the CheckType, or the
2856 * program would already not have been in SSA, because it was only
2857 * defined in blocks dominated by the next edge.
2859 auto const killed_edge_defining = res.dst->type() <= TBottom &&
2860 origInst->isBlockEnd();
2861 if (killed_edge_defining) {
2862 origInst->convertToNop();
2863 } else {
2864 unit.replace(origInst, Mov, res.dst);
2865 // Force the existing dst type to match that of `res.dst'.
2866 origInst->dst()->setType(res.dst->type());
2868 } else {
2869 if (out_size == 1) {
2870 assertx(origInst->dst() == last->dst());
2871 FTRACE(1, " {}\n", last->toString());
2873 // We only have a single instruction, so just become it.
2874 origInst->become(unit, last);
2876 // Make sure to reset our dst's inst pointer, if we have one. (It may
2877 // have been set to `last'.
2878 if (origInst->dst()) {
2879 origInst->dst()->setInstruction(origInst);
2882 // And we also need to kill `last', to update preds.
2883 last->convertToNop();
2884 return;
2887 origInst->convertToNop();
2890 FTRACE(1, " {}\n", origInst->toString());
2892 for (auto const inst : res.instrs) {
2893 if (inst->is(Nop)) continue;
2894 block->insert(pos, inst);
2895 FTRACE(1, " {}\n", inst->toString());
2899 //////////////////////////////////////////////////////////////////////
2901 void simplifyPass(IRUnit& unit) {
2902 auto reachable = boost::dynamic_bitset<>(unit.numBlocks());
2903 reachable.set(unit.entry()->id());
2905 for (auto block : rpoSortCfg(unit)) {
2906 if (!reachable.test(block->id())) continue;
2908 for (auto& inst : *block) simplify(unit, &inst);
2910 if (auto const b = block->next()) reachable.set(b->id());
2911 if (auto const b = block->taken()) reachable.set(b->id());
2915 //////////////////////////////////////////////////////////////////////
2917 void copyProp(IRInstruction* inst) {
2918 for (auto& src : inst->srcs()) {
2919 while (src->inst()->is(Mov)) src = src->inst()->src(0);
2923 PackedBounds packedArrayBoundsStaticCheck(Type arrayType, int64_t idxVal) {
2924 if (idxVal < 0 || idxVal > PackedArray::MaxSize) return PackedBounds::Out;
2926 if (arrayType.hasConstVal()) {
2927 return idxVal < arrayType.arrVal()->size()
2928 ? PackedBounds::In
2929 : PackedBounds::Out;
2932 auto const at = arrayType.arrSpec().type();
2933 if (!at) return PackedBounds::Unknown;
2935 using A = RepoAuthType::Array;
2936 switch (at->tag()) {
2937 case A::Tag::Packed:
2938 if (idxVal < at->size() && at->emptiness() == A::Empty::No) {
2939 return PackedBounds::In;
2941 // fallthrough
2942 case A::Tag::PackedN:
2943 if (idxVal == 0 && at->emptiness() == A::Empty::No) {
2944 return PackedBounds::In;
2947 return PackedBounds::Unknown;
2950 Type packedArrayElemType(SSATmp* arr, SSATmp* idx) {
2951 assertx(arr->isA(TArr) &&
2952 arr->type().arrSpec().kind() == ArrayData::kPackedKind &&
2953 idx->isA(TInt));
2955 if (arr->hasConstVal() && idx->hasConstVal()) {
2956 auto const idxVal = idx->intVal();
2957 if (idxVal >= 0 && idxVal < arr->arrVal()->size()) {
2958 return Type(arr->arrVal()->nvGet(idxVal)->m_type);
2960 return TInitNull;
2963 Type t = arr->isA(TPersistentArr) ? TInitCell : TGen;
2965 auto const at = arr->type().arrSpec().type();
2966 if (!at) return t;
2968 switch (at->tag()) {
2969 case RepoAuthType::Array::Tag::Packed:
2971 if (idx->hasConstVal(TInt)) {
2972 auto const idxVal = idx->intVal();
2973 if (idxVal >= 0 && idxVal < at->size()) {
2974 return typeFromRAT(at->packedElem(idxVal)) & t;
2976 return TInitNull;
2978 Type elemType = TBottom;
2979 for (uint32_t i = 0; i < at->size(); ++i) {
2980 elemType |= typeFromRAT(at->packedElem(i));
2982 return elemType & t;
2984 case RepoAuthType::Array::Tag::PackedN:
2985 return typeFromRAT(at->elemType()) & t;
2987 not_reached();
2990 //////////////////////////////////////////////////////////////////////