remove operator-> from String
[hiphop-php.git] / hphp / runtime / vm / jit / simplifier.cpp
blobef6002b3647545ead13fd091bfe5a9e2a90911dd
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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/simplifier.h"
19 #include <sstream>
20 #include <type_traits>
21 #include <limits>
23 #include "hphp/runtime/base/smart-containers.h"
24 #include "hphp/runtime/base/type-conversions.h"
25 #include "hphp/runtime/vm/jit/guard-relaxation.h"
26 #include "hphp/runtime/vm/jit/ir-builder.h"
27 #include "hphp/runtime/vm/hhbc.h"
28 #include "hphp/runtime/vm/runtime.h"
30 namespace HPHP {
31 namespace JIT {
33 TRACE_SET_MOD(hhir);
35 //////////////////////////////////////////////////////////////////////
37 StackValueInfo getStackValue(SSATmp* sp, uint32_t index) {
38 FTRACE(5, "getStackValue: idx = {}, {}\n", index, sp->inst()->toString());
39 assert(sp->isA(Type::StkPtr));
40 IRInstruction* inst = sp->inst();
42 switch (inst->op()) {
43 case DefInlineSP:
44 case DefSP:
45 return StackValueInfo { inst, Type::StackElem };
47 case ReDefGeneratorSP: {
48 auto const extra = inst->extra<ReDefGeneratorSP>();
49 auto info = getStackValue(inst->src(0), index);
50 if (extra->spansCall) info.spansCall = true;
51 return info;
54 case StashGeneratorSP:
55 return getStackValue(inst->src(1), index);
57 case ReDefSP: {
58 auto const extra = inst->extra<ReDefSP>();
59 auto info = getStackValue(inst->src(0), index);
60 if (extra->spansCall) info.spansCall = true;
61 return info;
64 case PassSP:
65 case ExceptionBarrier:
66 return getStackValue(inst->src(0), index);
68 case SideExitGuardStk:
69 if (inst->extra<SideExitGuardData>()->checkedSlot == index) {
70 return StackValueInfo { inst, inst->typeParam() };
72 return getStackValue(inst->src(0), index);
74 case AssertStk:
75 // fallthrough
76 case CastStk:
77 // fallthrough
78 case CoerceStk:
79 // fallthrough
80 case GuardStk:
81 // We don't have a value, but we may know the type due to guarding
82 // on it.
83 if (inst->extra<StackOffset>()->offset == index) {
84 return StackValueInfo { inst, inst->typeParam() };
86 return getStackValue(inst->src(0), index);
88 case CheckStk:
89 // CheckStk's resulting type is the intersection of its typeParam
90 // with whatever type preceded it.
91 if (inst->extra<StackOffset>()->offset == index) {
92 Type prevType = getStackValue(inst->src(0), index).knownType;
93 return StackValueInfo { inst, inst->typeParam() & prevType};
95 return getStackValue(inst->src(0), index);
97 case CallArray: {
98 if (index == 0) {
99 // return value from call
100 return StackValueInfo { inst, Type::Gen };
102 auto info =
103 getStackValue(inst->src(0),
104 // Pushes a return value, pops an ActRec and args Array
105 index -
106 (1 /* pushed */ - (kNumActRecCells + 1) /* popped */));
107 info.spansCall = true;
108 return info;
111 case Call: {
112 if (index == 0) {
113 // return value from call
114 return StackValueInfo { inst, Type::Gen };
116 auto info =
117 getStackValue(inst->src(0),
118 index -
119 (1 /* pushed */ - kNumActRecCells /* popped */));
120 info.spansCall = true;
121 return info;
124 case SpillStack: {
125 int64_t numPushed = 0;
126 int32_t numSpillSrcs = inst->numSrcs() - 2;
128 for (int i = 0; i < numSpillSrcs; ++i) {
129 SSATmp* tmp = inst->src(i + 2);
130 if (index == numPushed) {
131 return StackValueInfo { tmp };
133 ++numPushed;
136 // This is not one of the values pushed onto the stack by this
137 // spillstack instruction, so continue searching.
138 SSATmp* prevSp = inst->src(0);
139 int64_t numPopped = inst->src(1)->intVal();
140 return getStackValue(prevSp,
141 // pop values pushed by spillstack
142 index - (numPushed - numPopped));
145 case InterpOne:
146 case InterpOneCF: {
147 SSATmp* prevSp = inst->src(0);
148 auto const& extra = *inst->extra<InterpOneData>();
149 int64_t spAdjustment = extra.cellsPopped - extra.cellsPushed;
150 switch (extra.opcode) {
151 // some instructions are kinda funny and mess with the stack
152 // in places other than the top
153 case Op::CGetL2:
154 if (index == 1) return StackValueInfo { inst, inst->typeParam() };
155 if (index == 0) return getStackValue(prevSp, index);
156 break;
157 case Op::CGetL3:
158 if (index == 2) return StackValueInfo { inst, inst->typeParam() };
159 if (index < 2) return getStackValue(prevSp, index);
160 break;
161 case Op::UnpackCont:
162 if (index == 0) return StackValueInfo { inst, Type::Int };
163 if (index == 1) return StackValueInfo { inst, Type::Cell };
164 break;
165 case Op::FPushCufSafe:
166 if (index == kNumActRecCells) return StackValueInfo { inst, Type::Bool };
167 if (index == kNumActRecCells + 1) return getStackValue(prevSp, 0);
168 break;
169 case Op::FPushCtor:
170 case Op::FPushCtorD:
171 if (index == kNumActRecCells) return StackValueInfo { inst, Type::Obj };
172 if (index == kNumActRecCells + 1) return getStackValue(prevSp, 0);
173 case Op::AsyncAwait:
174 if (index == 0) return StackValueInfo { inst, Type::Bool };
175 if (index == 1) return StackValueInfo { inst, Type::Cell };
176 break;
178 default:
179 if (index == 0 && inst->hasTypeParam()) {
180 return StackValueInfo { inst, inst->typeParam() };
182 break;
185 // If the index we're looking for is a cell pushed by the InterpOne (other
186 // than top of stack), we know nothing about its type.
187 if (index < extra.cellsPushed) {
188 return StackValueInfo{ inst, Type::StackElem };
190 return getStackValue(prevSp, index + spAdjustment);
193 case SpillFrame:
194 case CufIterSpillFrame:
195 // pushes an ActRec
196 if (index < kNumActRecCells) {
197 return StackValueInfo { inst, Type::StackElem };
199 return getStackValue(inst->src(0), index - kNumActRecCells);
201 default:
203 // Assume it's a vector instruction. This will assert in
204 // minstrBaseIdx if not.
205 auto const base = inst->src(minstrBaseIdx(inst));
206 assert(base->inst()->op() == LdStackAddr);
207 if (base->inst()->extra<LdStackAddr>()->offset == index) {
208 MInstrEffects effects(inst);
209 assert(effects.baseTypeChanged || effects.baseValChanged);
210 return StackValueInfo { inst, effects.baseType.derefIfPtr() };
212 return getStackValue(base->inst()->src(0), index);
216 not_reached();
219 smart::vector<SSATmp*> collectStackValues(SSATmp* sp, uint32_t stackDepth) {
220 smart::vector<SSATmp*> ret;
221 ret.reserve(stackDepth);
222 for (uint32_t i = 0; i < stackDepth; ++i) {
223 auto const value = getStackValue(sp, i).value;
224 if (value) {
225 ret.push_back(value);
228 return ret;
231 //////////////////////////////////////////////////////////////////////
233 void copyProp(IRInstruction* inst) {
234 for (uint32_t i = 0; i < inst->numSrcs(); i++) {
235 auto tmp = inst->src(i);
236 auto srcInst = tmp->inst();
238 if (srcInst->is(Mov, PassSP, PassFP)) {
239 inst->setSrc(i, srcInst->src(0));
242 // We're assuming that all of our src instructions have already been
243 // copyPropped.
244 assert(!inst->src(i)->inst()->is(Mov, PassSP, PassFP));
248 const SSATmp* canonical(const SSATmp* val) {
249 return canonical(const_cast<SSATmp*>(val));
252 SSATmp* canonical(SSATmp* value) {
253 auto inst = value->inst();
255 while (inst->isPassthrough()) {
256 value = inst->getPassthroughValue();
257 inst = value->inst();
259 return value;
262 IRInstruction* findSpillFrame(SSATmp* sp) {
263 auto inst = sp->inst();
264 while (!inst->is(SpillFrame)) {
265 assert(inst->dst()->isA(Type::StkPtr));
266 assert(!inst->is(RetAdjustStack, GenericRetDecRefs));
267 if (inst->is(DefSP)) return nullptr;
268 if (inst->is(InterpOne) && isFPush(inst->extra<InterpOne>()->opcode)) {
269 // A non-punted translation of this bytecode would contain a SpillFrame.
270 return nullptr;
273 // M-instr support opcodes have the previous sp in varying sources.
274 if (inst->modifiesStack()) inst = inst->previousStkPtr()->inst();
275 else inst = inst->src(0)->inst();
278 return inst;
281 IRInstruction* findPassFP(IRInstruction* fpInst) {
282 while (!fpInst->is(DefFP, DefInlineFP, PassFP)) {
283 assert(fpInst->dst()->isA(Type::FramePtr));
284 fpInst = fpInst->src(0)->inst();
286 return fpInst->is(PassFP) ? fpInst : nullptr;
289 const IRInstruction* frameRoot(const IRInstruction* fpInst) {
290 return frameRoot(const_cast<IRInstruction*>(fpInst));
293 IRInstruction* frameRoot(IRInstruction* fpInst) {
294 while (!fpInst->is(DefFP, DefInlineFP)) {
295 assert(fpInst->dst()->isA(Type::FramePtr));
296 fpInst = fpInst->src(0)->inst();
298 return fpInst;
301 //////////////////////////////////////////////////////////////////////
303 template<class... Args> SSATmp* Simplifier::cns(Args&&... cns) {
304 return m_irb.cns(std::forward<Args>(cns)...);
307 template<class... Args> SSATmp* Simplifier::gen(Opcode op, Args&&... args) {
308 assert(!m_insts.empty());
309 return m_irb.gen(op, m_insts.top()->marker(), std::forward<Args>(args)...);
312 template<class... Args> SSATmp* Simplifier::gen(Opcode op, BCMarker marker,
313 Args&&... args) {
314 return m_irb.gen(op, marker, std::forward<Args>(args)...);
317 //////////////////////////////////////////////////////////////////////
319 SSATmp* Simplifier::simplify(IRInstruction* inst) {
320 m_insts.push(inst);
321 SCOPE_EXIT {
322 assert(m_insts.top() == inst);
323 m_insts.pop();
326 SSATmp* src1 = inst->numSrcs() < 1 ? nullptr : inst->src(0);
327 SSATmp* src2 = inst->numSrcs() < 2 ? nullptr : inst->src(1);
329 Opcode opc = inst->op();
330 switch (opc) {
331 case AbsDbl: return simplifyAbsDbl(inst);
333 case AddInt: return simplifyAddInt(src1, src2);
334 case SubInt: return simplifySubInt(src1, src2);
335 case MulInt: return simplifyMulInt(src1, src2);
336 case AddDbl: return simplifyAddDbl(src1, src2);
337 case SubDbl: return simplifySubDbl(src1, src2);
338 case MulDbl: return simplifyMulDbl(src1, src2);
340 case DivDbl: return simplifyDivDbl(inst);
341 case Mod: return simplifyMod(src1, src2);
342 case AndInt: return simplifyAndInt(src1, src2);
343 case OrInt: return simplifyOrInt(src1, src2);
344 case XorInt: return simplifyXorInt(src1, src2);
345 case XorBool: return simplifyXorBool(src1, src2);
346 case Shl: return simplifyShl(inst);
347 case Shr: return simplifyShr(inst);
349 case Gt:
350 case Gte:
351 case Lt:
352 case Lte:
353 case Eq:
354 case Neq:
355 case GtInt:
356 case GteInt:
357 case LtInt:
358 case LteInt:
359 case EqInt:
360 case NeqInt:
361 case Same:
362 case NSame:
363 return simplifyCmp(opc, inst, src1, src2);
365 case ConcatCellCell: return simplifyConcatCellCell(inst);
366 case ConcatStrStr: return simplifyConcatStrStr(src1, src2);
367 case Mov: return simplifyMov(src1);
368 case ConvBoolToArr: return simplifyConvToArr(inst);
369 case ConvDblToArr: return simplifyConvToArr(inst);
370 case ConvIntToArr: return simplifyConvToArr(inst);
371 case ConvStrToArr: return simplifyConvToArr(inst);
372 case ConvArrToBool: return simplifyConvArrToBool(inst);
373 case ConvDblToBool: return simplifyConvDblToBool(inst);
374 case ConvIntToBool: return simplifyConvIntToBool(inst);
375 case ConvStrToBool: return simplifyConvStrToBool(inst);
376 case ConvArrToDbl: return simplifyConvArrToDbl(inst);
377 case ConvBoolToDbl: return simplifyConvBoolToDbl(inst);
378 case ConvIntToDbl: return simplifyConvIntToDbl(inst);
379 case ConvStrToDbl: return simplifyConvStrToDbl(inst);
380 case ConvArrToInt: return simplifyConvArrToInt(inst);
381 case ConvBoolToInt: return simplifyConvBoolToInt(inst);
382 case ConvDblToInt: return simplifyConvDblToInt(inst);
383 case ConvStrToInt: return simplifyConvStrToInt(inst);
384 case ConvBoolToStr: return simplifyConvBoolToStr(inst);
385 case ConvDblToStr: return simplifyConvDblToStr(inst);
386 case ConvIntToStr: return simplifyConvIntToStr(inst);
387 case ConvCellToBool:return simplifyConvCellToBool(inst);
388 case ConvCellToStr: return simplifyConvCellToStr(inst);
389 case ConvCellToInt: return simplifyConvCellToInt(inst);
390 case ConvCellToDbl: return simplifyConvCellToDbl(inst);
391 case Floor: return simplifyFloor(inst);
392 case Ceil: return simplifyCeil(inst);
393 case Unbox: return simplifyUnbox(inst);
394 case UnboxPtr: return simplifyUnboxPtr(inst);
395 case BoxPtr: return simplifyBoxPtr(inst);
396 case IsType:
397 case IsNType: return simplifyIsType(inst);
398 case IsScalarType: return simplifyIsScalarType(inst);
399 case CheckInit: return simplifyCheckInit(inst);
401 case JmpZero:
402 case JmpNZero:
403 return simplifyCondJmp(inst);
405 case JmpGt:
406 case JmpGte:
407 case JmpLt:
408 case JmpLte:
409 case JmpEq:
410 case JmpNeq:
411 case JmpGtInt:
412 case JmpGteInt:
413 case JmpLtInt:
414 case JmpLteInt:
415 case JmpEqInt:
416 case JmpNeqInt:
417 case JmpSame:
418 case JmpNSame:
419 return simplifyQueryJmp(inst);
421 case DecRef:
422 case DecRefNZ: return simplifyDecRef(inst);
423 case IncRef: return simplifyIncRef(inst);
424 case IncRefCtx: return simplifyIncRefCtx(inst);
425 case CheckType: return simplifyCheckType(inst);
426 case AssertType: return simplifyAssertType(inst);
427 case CheckStk: return simplifyCheckStk(inst);
428 case AssertNonNull:return simplifyAssertNonNull(inst);
430 case LdCls: return simplifyLdCls(inst);
431 case LdCtx: return simplifyLdCtx(inst);
432 case LdClsCtx: return simplifyLdClsCtx(inst);
433 case GetCtxFwdCall:return simplifyGetCtxFwdCall(inst);
434 case ConvClsToCctx: return simplifyConvClsToCctx(inst);
436 case SpillStack: return simplifySpillStack(inst);
437 case CastStk: return simplifyCastStk(inst);
438 case CoerceStk: return simplifyCoerceStk(inst);
439 case AssertStk: return simplifyAssertStk(inst);
440 case LdStack: return simplifyLdStack(inst);
441 case TakeStack: return simplifyTakeStack(inst);
442 case LdStackAddr: return simplifyLdStackAddr(inst);
443 case DecRefStack: return simplifyDecRefStack(inst);
444 case DecRefLoc: return simplifyDecRefLoc(inst);
445 case LdLoc: return simplifyLdLoc(inst);
447 case ExitOnVarEnv: return simplifyExitOnVarEnv(inst);
449 case CheckPackedArrayBounds: return simplifyCheckPackedArrayBounds(inst);
450 case LdPackedArrayElem: return simplifyLdPackedArrayElem(inst);
452 default:
453 return nullptr;
457 SSATmp* Simplifier::simplifySpillStack(IRInstruction* inst) {
458 auto const sp = inst->src(0);
459 auto const spDeficit = inst->src(1)->intVal();
460 auto const numSpillSrcs = inst->srcs().subpiece(2).size();
462 // If there's nothing to spill, and no stack adjustment, we don't
463 // need the instruction; the old stack is still accurate.
464 if (!numSpillSrcs && spDeficit == 0) return sp;
466 return nullptr;
469 // We never inline functions that could have a VarEnv, so an
470 // ExitOnVarEnv that has a frame based on DefInlineFP can be removed.
471 SSATmp* Simplifier::simplifyExitOnVarEnv(IRInstruction* inst) {
472 auto const frameInst = inst->src(0)->inst();
473 if (frameInst->op() == DefInlineFP) {
474 inst->convertToNop();
476 return nullptr;
479 SSATmp* Simplifier::simplifyLdCtx(IRInstruction* inst) {
480 auto const func = inst->extra<LdCtx>()->func;
481 if (func->isStatic()) {
482 // ActRec->m_cls of a static function is always a valid class pointer with
483 // the bottom bit set
484 return gen(LdCctx, inst->src(0));
486 return nullptr;
489 SSATmp* Simplifier::simplifyLdClsCtx(IRInstruction* inst) {
490 SSATmp* ctx = inst->src(0);
491 Type ctxType = ctx->type();
492 if (ctxType <= Type::Obj) {
493 // this pointer... load its class ptr
494 return gen(LdObjClass, ctx);
496 if (ctxType <= Type::Cctx) {
497 return gen(LdClsCctx, ctx);
499 return nullptr;
502 SSATmp* Simplifier::simplifyGetCtxFwdCall(IRInstruction* inst) {
503 SSATmp* srcCtx = inst->src(0);
504 if (srcCtx->isA(Type::Cctx)) {
505 return srcCtx;
507 return nullptr;
510 SSATmp* Simplifier::simplifyConvClsToCctx(IRInstruction* inst) {
511 auto* srcInst = inst->src(0)->inst();
512 if (srcInst->is(LdClsCctx)) return srcInst->src(0);
514 return nullptr;
517 SSATmp* Simplifier::simplifyLdCls(IRInstruction* inst) {
518 SSATmp* clsName = inst->src(0);
519 if (clsName->isConst()) {
520 const Class* cls = Unit::lookupClass(clsName->strVal());
521 if (cls) {
522 if (RDS::isPersistentHandle(cls->classHandle())) {
523 // the class is always defined
524 return cns(cls);
526 const Class* ctx = inst->src(1)->clsVal();
527 if (ctx && ctx->classof(cls)) {
528 // the class of the current function being compiled is the
529 // same as or derived from cls, so cls must be defined and
530 // cannot change the next time we execute this same code
531 return cns(cls);
534 return gen(LdClsCached, inst->taken(), clsName);
536 return nullptr;
539 SSATmp* Simplifier::simplifyCheckType(IRInstruction* inst) {
540 SSATmp* src = inst->src(0);
541 auto const oldType = src->type();
542 auto const newType = inst->typeParam();
544 if (oldType.not(newType)) {
545 if (oldType.isBoxed() && newType.isBoxed()) {
546 /* This CheckType serves to update the inner type hint for a boxed
547 * value, which requires no runtime work. This depends on the type being
548 * boxed, and constraining it with DataTypeCountness will do it. */
549 m_irb.constrainValue(src, DataTypeCountness);
550 return gen(AssertType, newType, src);
552 /* This check will always fail. It's probably due to an incorrect
553 * prediction. Generate a Jmp, and return src because
554 * following instructions may depend on the output of CheckType
555 * (they'll be DCEd later). Note that we can't use convertToJmp
556 * because the return value isn't nullptr, so the original
557 * instruction won't be inserted into the stream. */
558 gen(Jmp, inst->taken());
559 return src;
562 if (newType >= oldType) {
564 * The type of the src is the same or more refined than type, so the guard
565 * is unnecessary.
567 return src;
569 if (newType < oldType) {
570 assert(!src->isConst());
571 return nullptr;
574 return nullptr;
577 SSATmp* Simplifier::simplifyAssertType(IRInstruction* inst) {
578 auto const src = inst->src(0);
580 return simplifyAssertTypeOp(inst, src->type(), [&](TypeConstraint tc) {
581 m_irb.constrainValue(src, tc);
585 SSATmp* Simplifier::simplifyCheckStk(IRInstruction* inst) {
586 auto const newType = inst->typeParam();
587 auto sp = inst->src(0);
588 auto offset = inst->extra<CheckStk>()->offset;
590 auto stkVal = getStackValue(sp, offset);
591 auto const oldType = stkVal.knownType;
593 if (newType < oldType) {
594 // The new type is strictly better than the old type.
595 return nullptr;
598 if (newType >= oldType) {
599 // The new type isn't better than the old type.
600 return sp;
603 if (newType.not(oldType)) {
604 if (oldType.isBoxed() && newType.isBoxed()) {
605 /* This CheckStk serves to update the inner type hint for a boxed
606 * value, which requires no runtime work. This depends on the type being
607 * boxed, and constraining it with DataTypeCountness will do it. */
608 m_irb.constrainStack(sp, offset, DataTypeCountness);
609 return gen(AssertStk, newType, sp);
611 /* This check will always fail. It's probably due to an incorrect
612 * prediction. Generate a Jmp, and return the source because
613 * following instructions may depend on the output of CheckStk
614 * (they'll be DCEd later). Note that we can't use convertToJmp
615 * because the return value isn't nullptr, so the original
616 * instruction won't be inserted into the stream. */
617 gen(Jmp, inst->taken());
618 return sp;
621 return nullptr;
624 SSATmp* Simplifier::simplifyQueryJmp(IRInstruction* inst) {
625 SSATmp* src1 = inst->src(0);
626 SSATmp* src2 = inst->src(1);
627 Opcode opc = inst->op();
628 // reuse the logic in simplifyCmp.
629 SSATmp* newCmp = simplifyCmp(queryJmpToQueryOp(opc), nullptr, src1, src2);
630 if (!newCmp) return nullptr;
632 // Become an equivalent conditional jump and reuse that logic.
633 m_irb.unit().replace(
634 inst,
635 JmpNZero,
636 inst->taken(),
637 newCmp
640 return simplifyCondJmp(inst);
643 SSATmp* Simplifier::simplifyMov(SSATmp* src) {
644 return src;
647 SSATmp* Simplifier::simplifyAbsDbl(IRInstruction* inst) {
648 auto src = inst->src(0);
650 if (src->isConst()) {
651 double val = src->dblVal();
652 return cns(fabs(val));
655 return nullptr;
658 template <class Oper>
659 SSATmp* Simplifier::simplifyConst(SSATmp* src1, SSATmp* src2, Oper op) {
660 // don't canonicalize to the right, OP might not be commutative
661 if (!src1->isConst() || !src2->isConst()) return nullptr;
663 auto both = [&](Type ty) { return src1->type() <= ty && src2->type() <= ty; };
665 if (both(Type::Bool)) return cns(op(src1->boolVal(), src2->boolVal()));
666 if (both(Type::Int)) return cns(op(src1->intVal(), src2->intVal()));
667 if (both(Type::Dbl)) return cns(op(src1->dblVal(), src2->dblVal()));
668 return nullptr;
671 template <class Oper>
672 SSATmp* Simplifier::simplifyCommutative(SSATmp* src1,
673 SSATmp* src2,
674 Opcode opcode,
675 Oper op) {
676 if (auto simp = simplifyConst(src1, src2, op)) return simp;
678 // Canonicalize constants to the right.
679 if (src1->isConst() && !src2->isConst()) {
680 return gen(opcode, src2, src1);
683 // Only handle integer operations for now.
684 if (!src1->isA(Type::Int) || !src2->isA(Type::Int)) return nullptr;
686 auto inst1 = src1->inst();
687 auto inst2 = src2->inst();
688 if (inst1->op() == opcode && inst1->src(1)->isConst()) {
689 // (X + C1) + C2 --> X + C3
690 if (src2->isConst()) {
691 int64_t right = inst1->src(1)->intVal();
692 right = op(right, src2->intVal());
693 return gen(opcode, inst1->src(0), cns(right));
695 // (X + C1) + (Y + C2) --> X + Y + C3
696 if (inst2->op() == opcode && inst2->src(1)->isConst()) {
697 int64_t right = inst1->src(1)->intVal();
698 right = op(right, inst2->src(1)->intVal());
699 SSATmp* left = gen(opcode, inst1->src(0), inst2->src(0));
700 return gen(opcode, left, cns(right));
703 return nullptr;
706 template <class OutOper, class InOper>
707 SSATmp* Simplifier::simplifyDistributive(SSATmp* src1,
708 SSATmp* src2,
709 Opcode outcode,
710 Opcode incode,
711 OutOper outop,
712 InOper inop) {
713 // assumes that outop is commutative, don't use with subtract!
714 if (auto simp = simplifyCommutative(src1, src2, outcode, outop)) return simp;
716 auto inst1 = src1->inst();
717 auto inst2 = src2->inst();
718 Opcode op1 = inst1->op();
719 Opcode op2 = inst2->op();
720 // all combinations of X * Y + X * Z --> X * (Y + Z)
721 if (op1 == incode && op2 == incode) {
722 if (inst1->src(0) == inst2->src(0)) {
723 SSATmp* fold = gen(outcode, inst1->src(1), inst2->src(1));
724 return gen(incode, inst1->src(0), fold);
726 if (inst1->src(0) == inst2->src(1)) {
727 SSATmp* fold = gen(outcode, inst1->src(1), inst2->src(0));
728 return gen(incode, inst1->src(0), fold);
730 if (inst1->src(1) == inst2->src(0)) {
731 SSATmp* fold = gen(outcode, inst1->src(0), inst2->src(1));
732 return gen(incode, inst1->src(1), fold);
734 if (inst1->src(1) == inst2->src(1)) {
735 SSATmp* fold = gen(outcode, inst1->src(0), inst2->src(0));
736 return gen(incode, inst1->src(1), fold);
739 return nullptr;
742 SSATmp* Simplifier::simplifyAddInt(SSATmp* src1, SSATmp* src2) {
743 auto add = std::plus<int64_t>();
744 auto mul = std::multiplies<int64_t>();
745 if (auto simp = simplifyDistributive(src1, src2, AddInt, MulInt, add, mul)) {
746 return simp;
748 if (src2->isConst()) {
749 int64_t src2Val = src2->intVal();
750 // X + 0 --> X
751 if (src2Val == 0) return src1;
753 // X + -C --> X - C
754 // Weird, but can show up as a result of other simplifications. Don't need
755 // to check for C == INT_MIN, simplifySubInt already checks.
756 if (src2Val < 0) return gen(SubInt, src1, cns(-src2Val));
758 // X + (0 - Y) --> X - Y
759 auto inst2 = src2->inst();
760 if (inst2->op() == SubInt) {
761 SSATmp* src = inst2->src(0);
762 if (src->isConst() && src->intVal() == 0) {
763 return gen(SubInt, src1, inst2->src(1));
766 auto inst1 = src1->inst();
768 // (X - C1) + ...
769 if (inst1->op() == SubInt && inst1->src(1)->isConst()) {
770 auto x = inst1->src(0);
771 auto c1 = inst1->src(1);
773 // (X - C1) + C2 --> X + (C2 - C1)
774 if (src2->isConst()) {
775 auto rhs = gen(SubInt, cns(src2->intVal()), c1);
776 return gen(AddInt, x, rhs);
779 // (X - C1) + (Y +/- C2)
780 if ((inst2->op() == AddInt || inst2->op() == SubInt) &&
781 inst2->src(1)->isConst()) {
782 auto y = inst2->src(0);
783 auto c2 = inst2->src(1);
784 SSATmp* rhs = nullptr;
785 if (inst2->op() == SubInt) {
786 // (X - C1) + (Y - C2) --> X + Y + (-C1 - C2)
787 rhs = gen(SubInt, gen(SubInt, cns(0), c1), c2);
788 } else {
789 // (X - C1) + (Y + C2) --> X + Y + (C2 - C1)
790 rhs = gen(SubInt, c2, c1);
792 auto lhs = gen(AddInt, x, y);
793 return gen(AddInt, lhs, rhs);
795 // (X - C1) + (Y + C2) --> X + Y + (C2 - C1)
796 if (inst2->op() == AddInt && inst2->src(1)->isConst()) {
797 auto y = inst2->src(0);
798 auto c2 = inst2->src(1);
800 auto lhs = gen(AddInt, x, y);
801 auto rhs = gen(SubInt, c2, c1);
802 return gen(AddInt, lhs, rhs);
806 return nullptr;
809 SSATmp* Simplifier::simplifySubInt(SSATmp* src1, SSATmp* src2) {
810 auto sub = std::minus<int64_t>();
811 if (auto simp = simplifyConst(src1, src2, sub)) return simp;
813 // X - X --> 0
814 if (src1 == src2) return cns(0);
816 if (src2->isConst()) {
817 int64_t src2Val = src2->intVal();
818 // X - 0 --> X
819 if (src2Val == 0) return src1;
821 // X - -C --> X + C
822 // Need to check for C == INT_MIN, otherwise we'd infinite loop as
823 // X + -C would send us back here.
824 auto const min = std::numeric_limits<int64_t>::min();
825 if (src2Val > min && src2Val < 0) return gen(AddInt, src1, cns(-src2Val));
827 // X - (0 - Y) --> X + Y
828 auto inst2 = src2->inst();
829 if (inst2->op() == SubInt) {
830 SSATmp* src = inst2->src(0);
831 if (src->isConst(0)) return gen(AddInt, src1, inst2->src(1));
833 return nullptr;
836 SSATmp* Simplifier::simplifyMulInt(SSATmp* src1, SSATmp* src2) {
837 auto mul = std::multiplies<int64_t>();
838 if (auto simp = simplifyCommutative(src1, src2, MulInt, mul)) return simp;
840 if (!src2->isConst()) return nullptr;
842 int64_t rhs = src2->intVal();
844 // X * (-1) --> -X
845 if (rhs == -1) return gen(SubInt, cns(0), src1);
846 // X * 0 --> 0
847 if (rhs == 0) return cns(0);
848 // X * 1 --> X
849 if (rhs == 1) return src1;
850 // X * 2 --> X + X
851 if (rhs == 2) return gen(AddInt, src1, src1);
853 auto isPowTwo = [](int64_t a) {
854 return a > 0 && folly::isPowTwo<uint64_t>(a);
856 auto log2 = [](int64_t a) {
857 assert(a > 0);
858 return folly::findLastSet<uint64_t>(a) - 1;
861 // X * 2^C --> X << C
862 if (isPowTwo(rhs)) return gen(Shl, src1, cns(log2(rhs)));
864 // X * (2^C + 1) --> ((X << C) + X)
865 if (isPowTwo(rhs - 1)) {
866 auto lhs = gen(Shl, src1, cns(log2(rhs - 1)));
867 return gen(AddInt, lhs, src1);
869 // X * (2^C - 1) --> ((X << C) - X)
870 if (isPowTwo(rhs + 1)) {
871 auto lhs = gen(Shl, src1, cns(log2(rhs + 1)));
872 return gen(SubInt, lhs, src1);
875 return nullptr;
878 SSATmp* Simplifier::simplifyAddDbl(SSATmp* src1, SSATmp* src2) {
879 if (auto simp = simplifyConst(src1, src2, std::plus<double>())) {
880 return simp;
882 return nullptr;
885 SSATmp* Simplifier::simplifySubDbl(SSATmp* src1, SSATmp* src2) {
886 if (auto c = simplifyConst(src1, src2, std::minus<double>())) {
887 return c;
889 return nullptr;
892 SSATmp* Simplifier::simplifyMulDbl(SSATmp* src1, SSATmp* src2) {
893 auto mul = std::multiplies<double>();
894 if (auto simp = simplifyConst(src1, src2, mul)) return simp;
895 return nullptr;
898 SSATmp* Simplifier::simplifyMod(SSATmp* src1, SSATmp* src2) {
899 if (!src2->isConst()) return nullptr;
901 int64_t src2Val = src2->intVal();
902 auto const min = std::numeric_limits<int64_t>::min();
904 // refrain from generating undefined IR
905 assert(src2Val != 0);
906 // simplify const
907 if (src1->isConst()) {
908 // still don't want undefined IR
909 assert(src1->intVal() != min || src2Val != -1);
910 return cns(src1->intVal() % src2Val);
912 // X % 1, X % -1 --> 0
913 if (src2Val == 1 || src2Val == -1) return cns(0);
915 // X % LONG_MIN = X (largest magnitude possible as rhs)
916 return src2Val == min ? src1 : nullptr;
919 SSATmp* Simplifier::simplifyDivDbl(IRInstruction* inst) {
920 auto src1 = inst->src(0);
921 auto src2 = inst->src(1);
923 if (!src2->isConst()) return nullptr;
925 // not supporting integers (#2570625)
926 double src2Val = src2->dblVal();
928 // X / 0 -> bool(false)
929 if (src2Val == 0.0) {
930 // Ideally we'd generate a RaiseWarning and return false here, but we need
931 // a catch trace for that and we can't make a catch trace without
932 // HhbcTranslator.
933 return nullptr;
936 // statically compute X / Y
937 return src1->isConst() ? cns(src1->dblVal() / src2Val) : nullptr;
940 SSATmp* Simplifier::simplifyAndInt(SSATmp* src1, SSATmp* src2) {
941 auto bit_and = [](int64_t a, int64_t b) { return a & b; };
942 auto bit_or = [](int64_t a, int64_t b) { return a | b; };
943 auto simp = simplifyDistributive(src1, src2, AndInt, OrInt, bit_and, bit_or);
944 if (simp != nullptr) {
945 return simp;
947 // X & X --> X
948 if (src1 == src2) {
949 return src1;
951 if (src2->isConst()) {
952 // X & 0 --> 0
953 if (src2->intVal() == 0) {
954 return cns(0);
956 // X & (~0) --> X
957 if (src2->intVal() == ~0L) {
958 return src1;
961 return nullptr;
964 SSATmp* Simplifier::simplifyOrInt(SSATmp* src1, SSATmp* src2) {
965 auto bit_and = [](int64_t a, int64_t b) { return a & b; };
966 auto bit_or = [](int64_t a, int64_t b) { return a | b; };
967 auto simp = simplifyDistributive(src1, src2, OrInt, AndInt, bit_or, bit_and);
968 if (simp != nullptr) {
969 return simp;
971 // X | X --> X
972 if (src1 == src2) {
973 return src1;
975 if (src2->isConst()) {
976 // X | 0 --> X
977 if (src2->intVal() == 0) {
978 return src1;
980 // X | (~0) --> ~0
981 if (src2->intVal() == ~uint64_t(0)) {
982 return cns(~uint64_t(0));
985 return nullptr;
988 SSATmp* Simplifier::simplifyXorInt(SSATmp* src1, SSATmp* src2) {
989 auto bitxor = [](int64_t a, int64_t b) { return a ^ b; };
990 if (auto simp = simplifyCommutative(src1, src2, XorInt, bitxor)) {
991 return simp;
993 // X ^ X --> 0
994 if (src1 == src2) return cns(0);
995 // X ^ 0 --> X
996 if (src2->isConst(0)) return src1;
997 return nullptr;
1000 SSATmp* Simplifier::simplifyXorTrue(SSATmp* src) {
1001 IRInstruction* inst = src->inst();
1002 Opcode op = inst->op();
1004 switch (op) {
1005 // !!X --> X
1006 case XorBool:
1007 if (inst->src(1)->isConst(true)) return inst->src(0);
1008 return nullptr;
1010 // !(X cmp Y) --> X opposite_cmp Y
1011 case Lt:
1012 case Lte:
1013 case Gt:
1014 case Gte:
1015 case Eq:
1016 case Neq:
1017 case Same:
1018 case NSame: {
1019 auto s0 = inst->src(0);
1020 auto s1 = inst->src(1);
1021 // Not for Dbl: (x < NaN) != !(x >= NaN)
1022 if (!s0->isA(Type::Dbl) && !s1->isA(Type::Dbl)) {
1023 return gen(negateQueryOp(op), s0, s1);
1025 break;
1027 case InstanceOfBitmask:
1028 case NInstanceOfBitmask:
1029 // TODO: combine this with the above check and use isQueryOp or
1030 // add an isNegatable.
1031 return gen(
1032 negateQueryOp(op),
1033 std::make_pair(inst->numSrcs(), inst->srcs().begin())
1035 return nullptr;
1036 // TODO !(X | non_zero) --> 0
1037 default: (void)op;
1039 return nullptr;
1042 SSATmp* Simplifier::simplifyXorBool(SSATmp* src1, SSATmp* src2) {
1043 // Both constants.
1044 if (src1->isConst() && src2->isConst()) {
1045 return cns(bool(src1->boolVal() ^ src2->boolVal()));
1048 // Canonicalize constants to the right.
1049 if (src1->isConst() && !src2->isConst()) {
1050 return gen(XorBool, src2, src1);
1053 // X^0 => X
1054 if (src2->isConst(false)) return src1;
1056 // X^1 => simplify "not" logic
1057 if (src2->isConst(true)) return simplifyXorTrue(src1);
1058 return nullptr;
1061 template<class Oper>
1062 SSATmp* Simplifier::simplifyShift(SSATmp* src1, SSATmp* src2, Oper op) {
1063 if (src1->isConst()) {
1064 if (src1->intVal() == 0) {
1065 return cns(0);
1068 if (src2->isConst()) {
1069 return cns(op(src1->intVal(), src2->intVal()));
1073 if (src2->isConst() && src2->intVal() == 0) {
1074 return src1;
1077 return nullptr;
1080 SSATmp* Simplifier::simplifyShl(IRInstruction* inst) {
1081 auto src1 = inst->src(0);
1082 auto src2 = inst->src(1);
1084 return simplifyShift(src1, src2, [] (int64_t a, int64_t b) {
1085 return a << b; });
1088 SSATmp* Simplifier::simplifyShr(IRInstruction* inst) {
1089 auto src1 = inst->src(0);
1090 auto src2 = inst->src(1);
1092 return simplifyShift(src1, src2, [] (int64_t a, int64_t b) {
1093 return a >> b; });
1096 template<class T, class U>
1097 static typename std::common_type<T,U>::type cmpOp(Opcode opName, T a, U b) {
1098 switch (opName) {
1099 case GtInt:
1100 case Gt: return a > b;
1101 case GteInt:
1102 case Gte: return a >= b;
1103 case LtInt:
1104 case Lt: return a < b;
1105 case LteInt:
1106 case Lte: return a <= b;
1107 case Same:
1108 case EqInt:
1109 case Eq: return a == b;
1110 case NSame:
1111 case NeqInt:
1112 case Neq: return a != b;
1113 default:
1114 not_reached();
1118 SSATmp* Simplifier::simplifyCmp(Opcode opName, IRInstruction* inst,
1119 SSATmp* src1, SSATmp* src2) {
1120 auto newInst = [inst, this](Opcode op, SSATmp* src1, SSATmp* src2) {
1121 return gen(op, inst ? inst->taken() : (Block*)nullptr, src1, src2);
1123 // ---------------------------------------------------------------------
1124 // Perform some execution optimizations immediately
1125 // ---------------------------------------------------------------------
1127 auto const type1 = src1->type();
1128 auto const type2 = src2->type();
1130 // Identity optimization
1131 if (src1 == src2 && type1.not(Type::Dbl)) {
1132 // (val1 == val1) does not simplify to true when val1 is a NaN
1133 return cns(bool(cmpOp(opName, 0, 0)));
1136 // need both types to be unboxed to simplify, and the code below assumes the
1137 // types are known DataTypes.
1138 if (!type1.isKnownUnboxedDataType() || !type2.isKnownUnboxedDataType()) {
1139 return nullptr;
1142 // ---------------------------------------------------------------------
1143 // OpSame and OpNSame have some special rules
1144 // ---------------------------------------------------------------------
1146 if (opName == Same || opName == NSame) {
1147 // OpSame and OpNSame do not perform type juggling
1148 if (type1.toDataType() != type2.toDataType() &&
1149 !(type1 <= Type::Str && type2 <= Type::Str)) {
1150 return cns(opName == NSame);
1153 // src1 and src2 are same type, treating Str and StaticStr as the same
1155 // OpSame and OpNSame have special rules for string, array, object, and
1156 // resource. Other types may simplify to OpEq and OpNeq, respectively
1157 if (type1 <= Type::Str && type2 <= Type::Str) {
1158 if (src1->isConst() && src2->isConst()) {
1159 auto str1 = src1->strVal();
1160 auto str2 = src2->strVal();
1161 bool same = str1->same(str2);
1162 return cns(bool(cmpOp(opName, same, 1)));
1163 } else {
1164 return nullptr;
1168 auto const badTypes = Type::Obj | Type::Res | Type::Arr;
1169 if (type1.maybe(badTypes) || type2.maybe(badTypes)) {
1170 return nullptr;
1173 // Type is a primitive type - simplify to Eq/Neq
1174 return newInst(opName == Same ? Eq : Neq, src1, src2);
1177 // ---------------------------------------------------------------------
1178 // We may now perform constant-constant optimizations
1179 // ---------------------------------------------------------------------
1181 // Null cmp Null
1182 if (type1 <= Type::Null && type2 <= Type::Null) {
1183 return cns(bool(cmpOp(opName, 0, 0)));
1185 // const cmp const
1186 // TODO this list is incomplete - feel free to add more
1187 // TODO: can simplify const arrays when sizes are different or both 0
1188 if (src1->isConst() && src2->isConst()) {
1189 // StaticStr cmp StaticStr
1190 if (src1->isA(Type::StaticStr) &&
1191 src2->isA(Type::StaticStr)) {
1192 int cmp = src1->strVal()->compare(src2->strVal());
1193 return cns(bool(cmpOp(opName, cmp, 0)));
1195 // ConstInt cmp ConstInt
1196 if (src1->isA(Type::Int) && src2->isA(Type::Int)) {
1197 return cns(bool(
1198 cmpOp(opName, src1->intVal(), src2->intVal())));
1200 // ConstBool cmp ConstBool
1201 if (src1->isA(Type::Bool) && src2->isA(Type::Bool)) {
1202 return cns(bool(
1203 cmpOp(opName, src1->boolVal(), src2->boolVal())));
1207 // ---------------------------------------------------------------------
1208 // Constant bool comparisons can be strength-reduced
1209 // NOTE: Comparisons with bools get juggled to bool.
1210 // ---------------------------------------------------------------------
1212 // Perform constant-bool optimizations
1213 if (src2->isA(Type::Bool) && src2->isConst()) {
1214 bool b = src2->boolVal();
1216 // The result of the comparison might be independent of the truth
1217 // value of the LHS. If so, then simplify.
1218 // E.g. `some-int > true`. some-int may juggle to false or true
1219 // (0 or 1), but `0 > true` and `1 > true` are both false, so we can
1220 // simplify to false immediately.
1221 if (cmpOp(opName, false, b) == cmpOp(opName, true, b)) {
1222 return cns(bool(cmpOp(opName, false, b)));
1225 // There are only two distinct booleans - false and true (0 and 1).
1226 // From above, we know that (0 OP b) != (1 OP b).
1227 // Hence exactly one of (0 OP b) and (1 OP b) is true.
1228 // Hence there is exactly one boolean value of src1 that results in the
1229 // overall expression being true (after type-juggling).
1230 // Hence we may check for equality with that boolean.
1231 // E.g. `some-int > false` is equivalent to `some-int == true`
1232 if (opName != Eq) {
1233 if (cmpOp(opName, false, b)) {
1234 return newInst(Eq, src1, cns(false));
1235 } else {
1236 return newInst(Eq, src1, cns(true));
1241 // Lower to int-comparison if possible.
1242 if (!isIntQueryOp(opName) && type1 <= Type::Int && type2 <= Type::Int) {
1243 return newInst(queryToIntQueryOp(opName), src1, src2);
1246 // ---------------------------------------------------------------------
1247 // For same-type cmps, canonicalize any constants to the right
1248 // Then stop - there are no more simplifications left
1249 // ---------------------------------------------------------------------
1251 if (type1.toDataType() == type2.toDataType() ||
1252 (type1 <= Type::Str && type2 <= Type::Str)) {
1253 if (src1->isConst() && !src2->isConst()) {
1254 return newInst(commuteQueryOp(opName), src2, src1);
1256 return nullptr;
1259 // ---------------------------------------------------------------------
1260 // Perform type juggling and type canonicalization for different types
1261 // see http://www.php.net/manual/en/language.operators.comparison.php
1262 // ---------------------------------------------------------------------
1264 // nulls get canonicalized to the right
1265 if (type1 <= Type::Null) {
1266 return newInst(commuteQueryOp(opName), src2, src1);
1269 // case 1a: null cmp string. Convert null to ""
1270 if (type1 <= Type::Str && type2 <= Type::Null) {
1271 return newInst(opName, src1, cns(makeStaticString("")));
1274 // case 1b: null cmp object. Convert null to false and the object to true
1275 if (type1 <= Type::Obj && type2 <= Type::Null) {
1276 return newInst(opName, cns(true), cns(false));
1279 // case 2a: null cmp anything. Convert null to false
1280 if (type2 <= Type::Null) {
1281 return newInst(opName, src1, cns(false));
1284 // bools get canonicalized to the right
1285 if (src1->isA(Type::Bool)) {
1286 return newInst(commuteQueryOp(opName), src2, src1);
1289 // case 2b: bool cmp anything. Convert anything to bool
1290 if (src2->isA(Type::Bool)) {
1291 if (src1->isConst()) {
1292 if (src1->isA(Type::Int)) {
1293 return newInst(opName, cns(bool(src1->intVal())), src2);
1294 } else if (src1->isA(Type::Str)) {
1295 auto str = src1->strVal();
1296 return newInst(opName, cns(str->toBoolean()), src2);
1300 // Optimize comparison between int and const bool
1301 if (src1->isA(Type::Int) && src2->isConst()) {
1302 // Based on the const bool optimization (above) opName should be OpEq
1303 always_assert(opName == Eq);
1305 if (src2->boolVal()) {
1306 return newInst(Neq, src1, cns(0));
1307 } else {
1308 return newInst(Eq, src1, cns(0));
1312 // Nothing fancy to do - perform juggling as normal.
1313 return newInst(opName, gen(ConvCellToBool, src1), src2);
1316 // From here on, we must be careful of how Type::Obj gets dealt with,
1317 // since Type::Obj can refer to an object or to a resource.
1319 // case 3: object cmp object. No juggling to do
1320 // same-type simplification is performed above
1322 // strings get canonicalized to the left
1323 if (type2 <= Type::Str) {
1324 return newInst(commuteQueryOp(opName), src2, src1);
1327 // ints get canonicalized to the right
1328 if (src1->isA(Type::Int)) {
1329 return newInst(commuteQueryOp(opName), src2, src1);
1332 // case 4: number/string/resource cmp. Convert to number (int OR double)
1333 // NOTE: The following if-test only checks for some of the SRON-SRON
1334 // cases (specifically, string-int). Other cases (like string-string)
1335 // are dealt with earlier, while other cases (like number-resource)
1336 // are not caught at all (and end up exiting this macro at the bottom).
1337 if (src1->isConst(Type::Str) && src2->isA(Type::Int)) {
1338 auto str = src1->strVal();
1339 int64_t si; double sd;
1340 auto st = str->isNumericWithVal(si, sd, true /* allow errors */);
1341 if (st == KindOfDouble) {
1342 return newInst(opName, cns(sd), src2);
1344 if (st == KindOfNull) {
1345 si = 0;
1347 return newInst(opName, cns(si), src2);
1350 // case 5: array cmp array. No juggling to do
1351 // same-type simplification is performed above
1353 // case 6: array cmp anything. Array is greater
1354 if (src1->isA(Type::Arr)) {
1355 return cns(bool(cmpOp(opName, 1, 0)));
1357 if (src2->isA(Type::Arr)) {
1358 return cns(bool(cmpOp(opName, 0, 1)));
1361 // case 7: object cmp anything. Object is greater
1362 // ---------------------------------------------------------------------
1363 // Unfortunately, we are unsure of whether Type::Obj is an object or a
1364 // resource, so this code cannot be applied.
1365 // ---------------------------------------------------------------------
1366 return nullptr;
1369 SSATmp* Simplifier::simplifyIsType(IRInstruction* inst) {
1370 bool trueSense = inst->op() == IsType;
1371 auto type = inst->typeParam();
1372 auto src = inst->src(0);
1373 auto srcType = src->type();
1375 if (m_irb.typeMightRelax(src)) return nullptr;
1377 // The comparisons below won't work for these cases covered by this
1378 // assert, and we currently don't generate these types.
1379 assert(type.isKnownUnboxedDataType());
1381 // Testing for StaticStr will make you miss out on CountedStr, and vice versa,
1382 // and similarly for arrays. PHP treats both types of string the same, so if
1383 // the distinction matters to you here, be careful.
1384 assert(IMPLIES(type <= Type::Str, type == Type::Str));
1385 assert(IMPLIES(type <= Type::Arr, type == Type::Arr));
1387 // The types are disjoint; the result must be false.
1388 if (srcType.not(type)) {
1389 return cns(!trueSense);
1392 // The src type is a subtype of the tested type; the result must be true.
1393 if (srcType <= type) {
1394 return cns(trueSense);
1397 // At this point, either the tested type is a subtype of the src type, or they
1398 // are non-disjoint but neither is a subtype of the other. We can't simplify
1399 // this away.
1400 return nullptr;
1403 SSATmp* Simplifier::simplifyIsScalarType(IRInstruction* inst) {
1404 SSATmp* src = inst->src(0);
1405 if (src->type().isKnownDataType()) {
1406 if (src->isA(Type::Int | Type::Dbl | Type::Str | Type::Bool)) {
1407 return cns(true);
1408 } else {
1409 return cns(false);
1412 return nullptr;
1415 SSATmp* Simplifier::simplifyConcatCellCell(IRInstruction* inst) {
1416 SSATmp* src1 = inst->src(0);
1417 SSATmp* src2 = inst->src(1);
1418 auto catchBlock = inst->taken();
1420 if (src1->isA(Type::Str) && src2->isA(Type::Str)) { // StrStr
1421 return gen(ConcatStrStr, catchBlock, src1, src2);
1423 if (src1->isA(Type::Int) && src2->isA(Type::Str)) { // IntStr
1424 return gen(ConcatIntStr, catchBlock, src1, src2);
1426 if (src1->isA(Type::Str) && src2->isA(Type::Int)) { // StrInt
1427 return gen(ConcatStrInt, catchBlock, src1, src2);
1430 // XXX: t3770157. All the cases below need two different catch blocks but we
1431 // only have access to one here.
1432 return nullptr;
1434 if (src1->isA(Type::Int)) { // IntCell
1435 auto* asStr = gen(ConvCellToStr, catchBlock, src2);
1436 auto* result = gen(ConcatIntStr, src1, asStr);
1437 // ConcatIntStr doesn't consume its second input so we have to decref it
1438 // here.
1439 gen(DecRef, asStr);
1440 return result;
1442 if (src2->isA(Type::Int)) { // CellInt
1443 auto const asStr = gen(ConvCellToStr, catchBlock, src1);
1444 // concat promises to decref its first argument. we need to do it here
1445 gen(DecRef, src1);
1446 return gen(ConcatStrInt, asStr, src2);
1448 if (src1->isA(Type::Str)) { // StrCell
1449 auto* asStr = gen(ConvCellToStr, catchBlock, src2);
1450 auto* result = gen(ConcatStrStr, src1, asStr);
1451 // ConcatStrStr doesn't consume its second input so we have to decref it
1452 // here.
1453 gen(DecRef, asStr);
1454 return result;
1456 if (src2->isA(Type::Str)) { // CellStr
1457 auto const asStr = gen(ConvCellToStr, catchBlock, src1);
1458 // concat promises to decref its first argument. we need to do it here
1459 gen(DecRef, src1);
1460 return gen(ConcatStrStr, asStr, src2);
1463 return nullptr;
1466 SSATmp* Simplifier::simplifyConcatStrStr(SSATmp* src1, SSATmp* src2) {
1467 if (src1->isConst() && src1->isA(Type::StaticStr) &&
1468 src2->isConst() && src2->isA(Type::StaticStr)) {
1469 StringData* str1 = const_cast<StringData *>(src1->strVal());
1470 StringData* str2 = const_cast<StringData *>(src2->strVal());
1471 StringData* merge = makeStaticString(concat_ss(str1, str2));
1472 return cns(merge);
1475 return nullptr;
1478 SSATmp* Simplifier::simplifyConvToArr(IRInstruction* inst) {
1479 SSATmp* src = inst->src(0);
1480 if (src->isConst()) {
1481 Array arr = Array::Create(src->variantVal());
1482 return cns(ArrayData::GetScalarArray(arr.get()));
1484 return nullptr;
1487 SSATmp* Simplifier::simplifyConvArrToBool(IRInstruction* inst) {
1488 SSATmp* src = inst->src(0);
1489 if (src->isConst()) {
1490 if (src->arrVal()->empty()) {
1491 return cns(false);
1493 return cns(true);
1495 return nullptr;
1498 SSATmp* Simplifier::simplifyConvDblToBool(IRInstruction* inst) {
1499 SSATmp* src = inst->src(0);
1500 if (src->isConst()) {
1501 return cns(bool(src->dblVal()));
1504 return nullptr;
1507 SSATmp* Simplifier::simplifyConvIntToBool(IRInstruction* inst) {
1508 SSATmp* src = inst->src(0);
1509 if (src->isConst()) {
1510 return cns(bool(src->intVal()));
1512 return nullptr;
1515 SSATmp* Simplifier::simplifyConvStrToBool(IRInstruction* inst) {
1516 SSATmp* src = inst->src(0);
1517 if (src->isConst()) {
1518 // only the strings "", and "0" convert to false, all other strings
1519 // are converted to true
1520 const StringData* str = src->strVal();
1521 return cns(!str->empty() && !str->isZero());
1523 return nullptr;
1526 SSATmp* Simplifier::simplifyConvArrToDbl(IRInstruction* inst) {
1527 SSATmp* src = inst->src(0);
1528 if (src->isConst()) {
1529 if (src->arrVal()->empty()) {
1530 return cns(0.0);
1533 return nullptr;
1536 SSATmp* Simplifier::simplifyConvBoolToDbl(IRInstruction* inst) {
1537 SSATmp* src = inst->src(0);
1538 if (src->isConst()) {
1539 return cns(double(src->boolVal()));
1541 return nullptr;
1544 SSATmp* Simplifier::simplifyConvIntToDbl(IRInstruction* inst) {
1545 SSATmp* src = inst->src(0);
1546 if (src->isConst()) {
1547 return cns(double(src->intVal()));
1549 if (src->inst()->is(ConvBoolToInt)) {
1550 return gen(ConvBoolToDbl, src->inst()->src(0));
1552 return nullptr;
1555 SSATmp* Simplifier::simplifyConvStrToDbl(IRInstruction* inst) {
1556 SSATmp* src = inst->src(0);
1557 if (src->isConst()) {
1558 const StringData *str = src->strVal();
1559 int64_t lval;
1560 double dval;
1561 DataType ret = str->isNumericWithVal(lval, dval, 1);
1562 if (ret == KindOfInt64) {
1563 dval = (double)lval;
1564 } else if (ret != KindOfDouble) {
1565 dval = 0.0;
1567 return cns(dval);
1569 return nullptr;
1572 SSATmp* Simplifier::simplifyConvArrToInt(IRInstruction* inst) {
1573 SSATmp* src = inst->src(0);
1574 if (src->isConst()) {
1575 if (src->arrVal()->empty()) {
1576 return cns(0);
1578 return cns(1);
1580 return nullptr;
1583 SSATmp* Simplifier::simplifyConvBoolToInt(IRInstruction* inst) {
1584 SSATmp* src = inst->src(0);
1585 if (src->isConst()) {
1586 return cns(int(src->boolVal()));
1588 return nullptr;
1591 SSATmp* Simplifier::simplifyConvDblToInt(IRInstruction* inst) {
1592 SSATmp* src = inst->src(0);
1593 if (src->isConst()) {
1594 return cns(toInt64(src->dblVal()));
1596 return nullptr;
1599 SSATmp* Simplifier::simplifyConvStrToInt(IRInstruction* inst) {
1600 SSATmp* src = inst->src(0);
1601 if (src->isConst()) {
1602 const StringData *str = src->strVal();
1603 int64_t lval;
1604 double dval;
1605 DataType ret = str->isNumericWithVal(lval, dval, 1);
1606 if (ret == KindOfDouble) {
1607 lval = (int64_t)dval;
1608 } else if (ret != KindOfInt64) {
1609 lval = 0;
1611 return cns(lval);
1613 return nullptr;
1616 SSATmp* Simplifier::simplifyConvBoolToStr(IRInstruction* inst) {
1617 SSATmp* src = inst->src(0);
1618 if (src->isConst()) {
1619 if (src->boolVal()) {
1620 return cns(makeStaticString("1"));
1622 return cns(makeStaticString(""));
1624 return nullptr;
1627 SSATmp* Simplifier::simplifyConvDblToStr(IRInstruction* inst) {
1628 SSATmp* src = inst->src(0);
1629 if (src->isConst()) {
1630 String dblStr(buildStringData(src->dblVal()));
1631 return cns(makeStaticString(dblStr));
1633 return nullptr;
1636 SSATmp* Simplifier::simplifyConvIntToStr(IRInstruction* inst) {
1637 SSATmp* src = inst->src(0);
1638 if (src->isConst()) {
1639 return cns(
1640 makeStaticString(folly::to<std::string>(src->intVal()))
1643 return nullptr;
1646 SSATmp* Simplifier::simplifyConvCellToBool(IRInstruction* inst) {
1647 auto const src = inst->src(0);
1648 auto const srcType = src->type();
1650 if (srcType <= Type::Bool) return src;
1651 if (srcType <= Type::Null) return cns(false);
1652 if (srcType <= Type::Arr) return gen(ConvArrToBool, src);
1653 if (srcType <= Type::Dbl) return gen(ConvDblToBool, src);
1654 if (srcType <= Type::Int) return gen(ConvIntToBool, src);
1655 if (srcType <= Type::Str) return gen(ConvStrToBool, src);
1656 if (srcType <= Type::Obj) {
1657 if (auto cls = srcType.getClass()) {
1658 // t3429711 we should test cls->m_ODAttr
1659 // here, but currently it doesnt have all
1660 // the flags set.
1661 if (!cls->instanceCtor()) {
1662 return cns(true);
1665 return gen(ConvObjToBool, src);
1667 if (srcType <= Type::Res) return nullptr; // No specialization yet
1669 return nullptr;
1672 SSATmp* Simplifier::simplifyConvCellToStr(IRInstruction* inst) {
1673 auto const src = inst->src(0);
1674 auto const srcType = src->type();
1675 auto const catchTrace = inst->taken();
1677 if (srcType <= Type::Bool) return gen(ConvBoolToStr, src);
1678 if (srcType <= Type::Null) return cns(makeStaticString(""));
1679 if (srcType <= Type::Arr) {
1680 gen(RaiseNotice, catchTrace,
1681 cns(makeStaticString("Array to string conversion")));
1682 return cns(makeStaticString("Array"));
1684 if (srcType <= Type::Dbl) return gen(ConvDblToStr, src);
1685 if (srcType <= Type::Int) return gen(ConvIntToStr, src);
1686 if (srcType <= Type::Str) {
1687 gen(IncRef, src);
1688 return src;
1690 if (srcType <= Type::Obj) return gen(ConvObjToStr, catchTrace, src);
1691 if (srcType <= Type::Res) return gen(ConvResToStr, catchTrace, src);
1693 return nullptr;
1696 SSATmp* Simplifier::simplifyConvCellToInt(IRInstruction* inst) {
1697 auto const src = inst->src(0);
1698 auto const srcType = src->type();
1700 if (srcType <= Type::Int) return src;
1701 if (srcType <= Type::Null) return cns(0);
1702 if (srcType <= Type::Arr) return gen(ConvArrToInt, src);
1703 if (srcType <= Type::Bool) return gen(ConvBoolToInt, src);
1704 if (srcType <= Type::Dbl) return gen(ConvDblToInt, src);
1705 if (srcType <= Type::Str) return gen(ConvStrToInt, src);
1706 if (srcType <= Type::Obj) return gen(ConvObjToInt, inst->taken(), src);
1707 if (srcType <= Type::Res) return nullptr; // No specialization yet
1709 return nullptr;
1712 SSATmp* Simplifier::simplifyConvCellToDbl(IRInstruction* inst) {
1713 auto const src = inst->src(0);
1714 auto const srcType = src->type();
1716 if (srcType <= Type::Dbl) return src;
1717 if (srcType <= Type::Null) return cns(0.0);
1718 if (srcType <= Type::Arr) return gen(ConvArrToDbl, src);
1719 if (srcType <= Type::Bool) return gen(ConvBoolToDbl, src);
1720 if (srcType <= Type::Int) return gen(ConvIntToDbl, src);
1721 if (srcType <= Type::Str) return gen(ConvStrToDbl, src);
1722 if (srcType <= Type::Obj) return gen(ConvObjToDbl, inst->taken(), src);
1723 if (srcType <= Type::Res) return nullptr; // No specialization yet
1725 return nullptr;
1728 template<class Oper>
1729 SSATmp* Simplifier::simplifyRoundCommon(IRInstruction* inst, Oper op) {
1730 auto const src = inst->src(0);
1732 if (src->isConst()) {
1733 return cns(op(src->dblVal()));
1736 auto srcInst = src->inst();
1737 if (srcInst->op() == ConvIntToDbl || srcInst->op() == ConvBoolToDbl) {
1738 return src;
1741 return nullptr;
1744 SSATmp* Simplifier::simplifyFloor(IRInstruction* inst) {
1745 return simplifyRoundCommon(inst, floor);
1748 SSATmp* Simplifier::simplifyCeil(IRInstruction* inst) {
1749 return simplifyRoundCommon(inst, ceil);
1752 SSATmp* Simplifier::simplifyUnbox(IRInstruction* inst) {
1753 auto* src = inst->src(0);
1754 auto type = outputType(inst);
1756 Type srcType = src->type();
1757 if (srcType.notBoxed()) {
1758 assert(type.equals(srcType));
1759 return src;
1761 if (srcType.isBoxed()) {
1762 srcType = srcType.innerType();
1763 assert(type.equals(srcType));
1764 return gen(LdRef, type, inst->taken(), src);
1766 return nullptr;
1769 SSATmp* Simplifier::simplifyUnboxPtr(IRInstruction* inst) {
1770 if (inst->src(0)->isA(Type::PtrToCell)) {
1771 return inst->src(0);
1773 return nullptr;
1776 SSATmp* Simplifier::simplifyBoxPtr(IRInstruction* inst) {
1777 if (inst->src(0)->isA(Type::PtrToBoxedCell)) {
1778 return inst->src(0);
1780 return nullptr;
1783 SSATmp* Simplifier::simplifyCheckInit(IRInstruction* inst) {
1784 auto const srcType = inst->src(0)->type();
1785 assert(srcType.notPtr());
1786 assert(inst->taken());
1787 if (srcType.not(Type::Uninit)) inst->convertToNop();
1788 return nullptr;
1791 SSATmp* Simplifier::simplifyDecRef(IRInstruction* inst) {
1792 auto src = inst->src(0);
1793 if (!m_irb.typeMightRelax(src) && !isRefCounted(src)) {
1794 inst->convertToNop();
1796 return nullptr;
1799 SSATmp* Simplifier::simplifyIncRef(IRInstruction* inst) {
1800 SSATmp* src = inst->src(0);
1801 if (!m_irb.typeMightRelax(src) && !isRefCounted(src)) {
1802 inst->convertToNop();
1804 return nullptr;
1807 SSATmp* Simplifier::simplifyIncRefCtx(IRInstruction* inst) {
1808 auto* ctx = inst->src(0);
1809 if (ctx->isA(Type::Obj)) {
1810 inst->setOpcode(IncRef);
1811 } else if (!m_irb.typeMightRelax(ctx) && ctx->type().notCounted()) {
1812 inst->convertToNop();
1815 return nullptr;
1818 SSATmp* Simplifier::simplifyCondJmp(IRInstruction* inst) {
1819 SSATmp* const src = inst->src(0);
1820 IRInstruction* const srcInst = src->inst();
1821 const Opcode srcOpcode = srcInst->op();
1823 // After other simplifications below (isConvIntOrPtrToBool), we can
1824 // end up with a non-Bool input. Nothing more to do in this case.
1825 if (!src->isA(Type::Bool)) {
1826 return nullptr;
1829 // Constant propagate.
1830 if (src->isConst()) {
1831 bool val = src->boolVal();
1832 if (inst->op() == JmpZero) {
1833 val = !val;
1835 if (val) {
1836 inst->convertToJmp();
1837 } else {
1838 inst->convertToNop();
1840 return nullptr;
1843 // Pull negations into the jump.
1844 if (srcOpcode == XorBool && srcInst->src(1)->isConst(true)) {
1845 inst->setOpcode(inst->op() == JmpZero ? JmpNZero : JmpZero);
1846 inst->setSrc(0, srcInst->src(0));
1847 return nullptr;
1851 * Try to combine the src inst with the Jmp. We can't do any
1852 * combinations of the src instruction with the jump if the src's
1853 * are refcounted, since we may have dec refs between the src
1854 * instruction and the jump.
1856 for (auto& src : srcInst->srcs()) {
1857 if (isRefCounted(src)) return nullptr;
1860 // If the source is conversion of an int or pointer to boolean, we
1861 // can test the int/ptr value directly.
1862 auto isConvIntOrPtrToBool = [&](IRInstruction* instr) {
1863 switch (instr->op()) {
1864 case ConvIntToBool:
1865 return true;
1866 case ConvCellToBool:
1867 return instr->src(0)->type().subtypeOfAny(
1868 Type::Func, Type::Cls, Type::VarEnv, Type::TCA);
1869 default:
1870 return false;
1873 if (isConvIntOrPtrToBool(srcInst)) {
1874 // We can just check the int or ptr directly. Borrow the Conv's src.
1875 inst->setSrc(0, srcInst->src(0));
1876 return nullptr;
1879 auto canCompareFused = [&]() {
1880 auto src1Type = srcInst->src(0)->type();
1881 auto src2Type = srcInst->src(1)->type();
1882 return ((src1Type <= Type::Int && src2Type <= Type::Int) ||
1883 ((src1Type <= Type::Int || src1Type <= Type::Dbl) &&
1884 (src2Type <= Type::Int || src2Type <= Type::Dbl)) ||
1885 (src1Type <= Type::Bool && src2Type <= Type::Bool) ||
1886 (src1Type <= Type::Cls && src2Type <= Type::Cls));
1889 // Fuse jumps with query operators.
1890 if (isFusableQueryOp(srcOpcode) && canCompareFused()) {
1891 auto opc = queryToJmpOp(inst->op() == JmpZero
1892 ? negateQueryOp(srcOpcode) : srcOpcode);
1893 SrcRange ssas = srcInst->srcs();
1895 m_irb.unit().replace(
1896 inst,
1897 opc,
1898 inst->maybeTypeParam(),
1899 std::make_pair(ssas.size(), ssas.begin())
1903 return nullptr;
1906 SSATmp* Simplifier::simplifyCastStk(IRInstruction* inst) {
1907 auto const info = getStackValue(inst->src(0),
1908 inst->extra<CastStk>()->offset);
1909 if (info.knownType <= inst->typeParam()) {
1910 // No need to cast---the type was as good or better.
1911 inst->convertToNop();
1913 return nullptr;
1916 SSATmp* Simplifier::simplifyCoerceStk(IRInstruction* inst) {
1917 auto const info = getStackValue(inst->src(0),
1918 inst->extra<CoerceStk>()->offset);
1919 if (info.knownType <= inst->typeParam()) {
1920 // No need to cast---the type was as good or better.
1921 inst->convertToNop();
1923 return nullptr;
1926 SSATmp* Simplifier::simplifyAssertStk(IRInstruction* inst) {
1927 auto const idx = inst->extra<AssertStk>()->offset;
1928 auto const info = getStackValue(inst->src(0), idx);
1930 return simplifyAssertTypeOp(inst, info.knownType,
1931 [&](TypeConstraint tc) {
1932 m_irb.constrainStack(inst->src(0), idx, tc);
1937 SSATmp* Simplifier::simplifyLdStack(IRInstruction* inst) {
1938 auto const info = getStackValue(inst->src(0),
1939 inst->extra<LdStack>()->offset);
1941 // We don't want to extend live ranges of tmps across calls, so we
1942 // don't get the value if spansCall is true; however, we can use
1943 // any type information known.
1944 auto* value = info.value;
1945 if (value && (!info.spansCall || info.value->inst()->is(DefConst))) {
1946 // The refcount optimizations depend on reliably tracking refcount
1947 // producers and consumers. LdStack and other raw loads are special cased
1948 // during the analysis, so if we're going to replace this LdStack with a
1949 // value that isn't from another raw load, we need to leave something in
1950 // its place to preserve that information.
1951 if (!value->inst()->isRawLoad() &&
1952 (value->type().maybeCounted() || m_irb.typeMightRelax(info.value))) {
1953 gen(TakeStack, info.value);
1955 return info.value;
1957 inst->setTypeParam(std::min(inst->typeParam(), info.knownType));
1958 return nullptr;
1961 SSATmp* Simplifier::simplifyTakeStack(IRInstruction* inst) {
1962 if (inst->src(0)->type().notCounted() &&
1963 !m_irb.typeMightRelax(inst->src(0))) {
1964 inst->convertToNop();
1967 return nullptr;
1970 SSATmp* Simplifier::simplifyDecRefLoc(IRInstruction* inst) {
1971 auto const localValue = m_irb.localValue(inst->extra<DecRefLoc>()->locId,
1972 DataTypeGeneric);
1973 if (!m_irb.typeMightRelax(localValue) && inst->typeParam().notCounted()) {
1974 inst->convertToNop();
1976 return nullptr;
1979 SSATmp* Simplifier::simplifyLdLoc(IRInstruction* inst) {
1980 // Ideally we'd replace LdLoc<Null,...> with a constant value of that type,
1981 // but that prevents the guard relaxation code from tracing the source of
1982 // values.
1983 return nullptr;
1986 SSATmp* Simplifier::simplifyLdStackAddr(IRInstruction* inst) {
1987 auto const info = getStackValue(inst->src(0),
1988 inst->extra<StackOffset>()->offset);
1989 inst->setTypeParam(std::min(inst->typeParam(), info.knownType.ptr()));
1990 return nullptr;
1993 SSATmp* Simplifier::simplifyDecRefStack(IRInstruction* inst) {
1994 auto const info = getStackValue(inst->src(0),
1995 inst->extra<StackOffset>()->offset);
1996 if (info.value && !info.spansCall) {
1997 if (info.value->type().maybeCounted() || m_irb.typeMightRelax(info.value)) {
1998 gen(TakeStack, info.value);
2000 inst->convertToNop();
2001 return gen(DecRef, info.value);
2003 if (m_irb.typeMightRelax(info.value)) {
2004 return nullptr;
2007 inst->setTypeParam(std::min(inst->typeParam(), info.knownType));
2008 if (inst->typeParam().notCounted()) {
2009 inst->convertToNop();
2011 return nullptr;
2014 SSATmp* Simplifier::simplifyAssertNonNull(IRInstruction* inst) {
2015 if (inst->src(0)->type().not(Type::Nullptr)) {
2016 return inst->src(0);
2018 return nullptr;
2021 SSATmp* Simplifier::simplifyCheckPackedArrayBounds(IRInstruction* inst) {
2022 auto* array = inst->src(0);
2023 auto* idx = inst->src(1);
2025 if (idx->isConst()) {
2026 auto const idxVal = (uint64_t)idx->intVal();
2027 if (idxVal >= 0xffffffffull) {
2028 // ArrayData can't hold more than 2^32 - 1 elements, so this is always
2029 // going to fail.
2030 inst->convertToJmp();
2031 } else if (array->isConst()) {
2032 if (idxVal >= array->arrVal()->size()) {
2033 inst->convertToJmp();
2034 } else {
2035 // We should convert inst to a nop here but that exposes t3626113
2040 return nullptr;
2043 SSATmp* Simplifier::simplifyLdPackedArrayElem(IRInstruction* inst) {
2044 auto* arrayTmp = inst->src(0);
2045 auto* idxTmp = inst->src(1);
2046 if (arrayTmp->isConst() && idxTmp->isConst()) {
2047 auto* value = arrayTmp->arrVal()->nvGet(idxTmp->intVal());
2048 if (!value) {
2049 // The index doesn't exist. This code should be unreachable at runtime.
2050 return nullptr;
2053 if (value->m_type == KindOfRef) value = value->m_data.pref->tv();
2054 return cns(*value);
2057 return nullptr;
2060 SSATmp* Simplifier::simplifyAssertTypeOp(IRInstruction* inst, Type oldType,
2061 ConstraintFunc constrain) const {
2062 auto const newType = inst->typeParam();
2064 if (oldType.not(newType)) {
2065 // If both types are boxed this is ok and even expected as a means to
2066 // update the hint for the inner type.
2067 if (oldType.isBoxed() && newType.isBoxed()) return nullptr;
2069 // We got external information (probably from static analysis) that
2070 // conflicts with what we've built up so far. There's no reasonable way to
2071 // continue here: we can't properly fatal the request because we can't make
2072 // a catch trace or SpillStack without HhbcTranslator, we can't punt on
2073 // just this instruction because we might not be in the initial translation
2074 // phase, and we can't just plow on forward since we'll probably generate
2075 // malformed IR. Since this case is very rare, just punt on the whole trace
2076 // so it gets interpreted.
2077 TRACE_PUNT("Invalid AssertTypeOp");
2080 // Asserting in these situations doesn't add any information.
2081 if (oldType <= Type::Cls || newType == Type::Gen) return inst->src(0);
2083 // We're asserting a strict subtype of the old type, so keep the assert
2084 // around.
2085 if (newType < oldType) return nullptr;
2087 // oldType is at least as good as the new type. Kill this assert op but
2088 // preserve the type we were asserting in case the source type gets relaxed
2089 // past it.
2090 if (newType >= oldType) {
2091 constrain({DataTypeGeneric, newType});
2092 return inst->src(0);
2095 // Now we're left with cases where neither type is a subtype of the other but
2096 // they have some nonzero intersection. We want to end up asserting the
2097 // intersection, but we have to constrain the input to avoid reintroducing
2098 // types that were removed from the original typeParam.
2099 auto const intersect = newType & oldType;
2100 inst->setTypeParam(intersect);
2102 TypeConstraint tc;
2103 if (intersect != newType) {
2104 auto increment = [](DataTypeCategory& cat) {
2105 always_assert(cat != DataTypeSpecialized);
2106 cat = static_cast<DataTypeCategory>(static_cast<uint8_t>(cat) + 1);
2109 Type relaxed;
2110 // Find the most general constraint that doesn't modify the type being
2111 // asserted.
2112 while ((relaxed = newType & relaxType(oldType, tc)) != intersect) {
2113 if (tc.category > DataTypeGeneric &&
2114 relaxed.maybeBoxed() && intersect.maybeBoxed() &&
2115 (relaxed & Type::Cell) == (intersect & Type::Cell)) {
2116 // If the inner type is why we failed, constrain that a level.
2117 increment(tc.innerCat);
2118 } else {
2119 increment(tc.category);
2123 constrain(tc);
2125 return nullptr;
2128 //////////////////////////////////////////////////////////////////////