Allow early initialization of classes if they don't have any [sp]init methods
[hiphop-php.git] / hphp / runtime / vm / jit / simplifier.cpp
blob54e8bf3f9d3dfafaeeda4a6b2d803db3bccae063
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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>
22 #include "hphp/runtime/base/memory/smart_containers.h"
23 #include "hphp/runtime/base/type_conversions.h"
24 #include "hphp/runtime/vm/jit/tracebuilder.h"
25 #include "hphp/runtime/vm/runtime.h"
27 namespace HPHP {
28 namespace JIT {
30 TRACE_SET_MOD(hhir);
32 //////////////////////////////////////////////////////////////////////
34 StackValueInfo getStackValue(SSATmp* sp, uint32_t index) {
35 assert(sp->isA(Type::StkPtr));
36 IRInstruction* inst = sp->inst();
38 switch (inst->op()) {
39 case DefSP:
40 return {};
42 case ReDefGeneratorSP:
43 case StashGeneratorSP:
44 return getStackValue(inst->src(0), index);
46 case ReDefSP:
47 return getStackValue(inst->src(1), index);
49 case ExceptionBarrier:
50 return getStackValue(inst->src(0), index);
52 case SideExitGuardStk:
53 always_assert(0 && "simplifier is not tested for running after jumpopts");
55 case AssertStk:
56 // fallthrough
57 case CastStk:
58 // fallthrough
59 case CheckStk:
60 // fallthrough
61 case GuardStk:
62 // We don't have a value, but we may know the type due to guarding
63 // on it.
64 if (inst->extra<StackOffset>()->offset == index) {
65 return StackValueInfo { inst->typeParam() };
67 return getStackValue(inst->src(0), index);
69 case CallArray: {
70 if (index == 0) {
71 // return value from call
72 return StackValueInfo { nullptr };
74 auto info =
75 getStackValue(inst->src(0),
76 // Pushes a return value, pops an ActRec and args Array
77 index -
78 (1 /* pushed */ - kNumActRecCells + 1 /* popped */));
79 info.spansCall = true;
80 return info;
83 case Call: {
84 if (index == 0) {
85 // return value from call
86 return StackValueInfo { nullptr };
88 auto info =
89 getStackValue(inst->src(0),
90 index -
91 (1 /* pushed */ - kNumActRecCells /* popped */));
92 info.spansCall = true;
93 return info;
96 case SpillStack: {
97 int64_t numPushed = 0;
98 int32_t numSpillSrcs = inst->numSrcs() - 2;
100 for (int i = 0; i < numSpillSrcs; ++i) {
101 SSATmp* tmp = inst->src(i + 2);
102 if (index == numPushed) {
103 if (tmp->inst()->op() == IncRef) {
104 tmp = tmp->inst()->src(0);
106 if (!tmp->type().equals(Type::None)) {
107 return StackValueInfo { tmp };
110 ++numPushed;
113 // This is not one of the values pushed onto the stack by this
114 // spillstack instruction, so continue searching.
115 SSATmp* prevSp = inst->src(0);
116 int64_t numPopped = inst->src(1)->getValInt();
117 return getStackValue(prevSp,
118 // pop values pushed by spillstack
119 index - (numPushed - numPopped));
122 case InterpOne: {
123 SSATmp* prevSp = inst->src(1);
124 int64_t spAdjustment = inst->src(3)->getValInt(); // # popped - # pushed
125 Type resultType = inst->typeParam();
126 if (index == 0 && !resultType.equals(Type::None)) {
127 return StackValueInfo { resultType };
129 return getStackValue(prevSp, index + spAdjustment);
132 case SpillFrame:
133 case CufIterSpillFrame:
134 return getStackValue(inst->src(0),
135 // pushes an ActRec
136 index - kNumActRecCells);
138 default:
140 // Assume it's a vector instruction. This will assert in
141 // vectorBaseIdx if not.
142 auto const base = inst->src(vectorBaseIdx(inst));
143 assert(base->inst()->op() == LdStackAddr);
144 if (base->inst()->extra<LdStackAddr>()->offset == index) {
145 VectorEffects ve(inst);
146 assert(ve.baseTypeChanged || ve.baseValChanged);
147 return StackValueInfo { ve.baseType.derefIfPtr() };
149 return getStackValue(base->inst()->src(0), index);
153 // Should not get here!
154 not_reached();
157 smart::vector<SSATmp*> collectStackValues(SSATmp* sp, uint32_t stackDepth) {
158 smart::vector<SSATmp*> ret;
159 ret.reserve(stackDepth);
160 for (uint32_t i = 0; i < stackDepth; ++i) {
161 auto const value = getStackValue(sp, i).value;
162 if (value) {
163 ret.push_back(value);
166 return ret;
169 //////////////////////////////////////////////////////////////////////
171 static void copyPropSrc(IRInstruction* inst, int index) {
172 auto tmp = inst->src(index);
173 auto srcInst = tmp->inst();
175 switch (srcInst->op()) {
176 case Mov:
177 inst->setSrc(index, srcInst->src(0));
178 break;
180 case IncRef:
181 if (!isRefCounted(srcInst->src(0))) {
182 srcInst->setOpcode(Mov);
183 inst->setSrc(index, srcInst->src(0));
185 break;
187 default:
188 return;
192 void copyProp(IRInstruction* inst) {
193 for (uint32_t i = 0; i < inst->numSrcs(); i++) {
194 copyPropSrc(inst, i);
199 * Checks if property propName of class clsTmp, called from context class ctx,
200 * can be accessed via the static property cache.
201 * Right now, this returns true for two cases:
202 * (a) the property is accessed from within the class containing it
203 * (b) the property belongs to a persistent class and it's accessible from ctx
205 bool canUseSPropCache(SSATmp* clsTmp,
206 const StringData* propName,
207 const Class* ctx) {
208 if (propName == nullptr) return false;
210 const StringData* clsName = findClassName(clsTmp);
211 if (ctx) {
212 const StringData* ctxName = ctx->preClass()->name();;
213 if (clsName && ctxName && clsName->isame(ctxName)) return true;
216 if (!clsTmp->isConst()) return false;
218 const Class* cls = clsTmp->getValClass();
220 if (!Transl::TargetCache::classIsPersistent(cls)) return false;
222 // If the class requires initialization, it might not have been
223 // initialized yet. getSProp() below will trigger initialization,
224 // but that's only valid to do earlier if it doesn't require any
225 // property initializer ([sp]init methods).
226 if (cls->hasInitMethods()) return false;
228 bool visible, accessible;
229 cls->getSProp(const_cast<Class*>(ctx), propName, visible, accessible);
231 return visible && accessible;
234 //////////////////////////////////////////////////////////////////////
236 template<class... Args> SSATmp* Simplifier::cns(Args&&... cns) {
237 return m_tb->cns(std::forward<Args>(cns)...);
240 template<class... Args> SSATmp* Simplifier::gen(Args&&... args) {
241 return m_tb->gen(std::forward<Args>(args)...);
244 //////////////////////////////////////////////////////////////////////
246 SSATmp* Simplifier::simplify(IRInstruction* inst) {
247 SSATmp* src1 = inst->src(0);
248 SSATmp* src2 = inst->src(1);
250 Opcode opc = inst->op();
251 switch (opc) {
252 case OpAdd: return simplifyAdd(src1, src2);
253 case OpSub: return simplifySub(src1, src2);
254 case OpMul: return simplifyMul(src1, src2);
255 case OpBitAnd: return simplifyBitAnd(src1, src2);
256 case OpBitOr: return simplifyBitOr(src1, src2);
257 case OpBitXor: return simplifyBitXor(src1, src2);
258 case OpLogicXor: return simplifyLogicXor(src1, src2);
260 case OpGt:
261 case OpGte:
262 case OpLt:
263 case OpLte:
264 case OpEq:
265 case OpNeq:
266 case OpSame:
267 case OpNSame:
268 return simplifyCmp(opc, inst, src1, src2);
270 case Concat: return simplifyConcat(src1, src2);
271 case Mov: return simplifyMov(src1);
272 case OpNot: return simplifyNot(src1);
273 case LdClsPropAddr: return simplifyLdClsPropAddr(inst);
274 case ConvBoolToArr: return simplifyConvToArr(inst);
275 case ConvDblToArr: return simplifyConvToArr(inst);
276 case ConvIntToArr: return simplifyConvToArr(inst);
277 case ConvStrToArr: return simplifyConvToArr(inst);
278 case ConvArrToBool: return simplifyConvArrToBool(inst);
279 case ConvDblToBool: return simplifyConvDblToBool(inst);
280 case ConvIntToBool: return simplifyConvIntToBool(inst);
281 case ConvStrToBool: return simplifyConvStrToBool(inst);
282 case ConvArrToDbl: return simplifyConvArrToDbl(inst);
283 case ConvBoolToDbl: return simplifyConvBoolToDbl(inst);
284 case ConvIntToDbl: return simplifyConvIntToDbl(inst);
285 case ConvStrToDbl: return simplifyConvStrToDbl(inst);
286 case ConvArrToInt: return simplifyConvArrToInt(inst);
287 case ConvBoolToInt: return simplifyConvBoolToInt(inst);
288 case ConvDblToInt: return simplifyConvDblToInt(inst);
289 case ConvStrToInt: return simplifyConvStrToInt(inst);
290 case ConvBoolToStr: return simplifyConvBoolToStr(inst);
291 case ConvDblToStr: return simplifyConvDblToStr(inst);
292 case ConvIntToStr: return simplifyConvIntToStr(inst);
293 case ConvCellToBool:return simplifyConvCellToBool(inst);
294 case Unbox: return simplifyUnbox(inst);
295 case UnboxPtr: return simplifyUnboxPtr(inst);
296 case IsType:
297 case IsNType: return simplifyIsType(inst);
298 case CheckInit:
299 case CheckInitMem: return simplifyCheckInit(inst);
301 case JmpZero:
302 case JmpNZero:
303 return simplifyCondJmp(inst);
305 case JmpGt:
306 case JmpGte:
307 case JmpLt:
308 case JmpLte:
309 case JmpEq:
310 case JmpNeq:
311 case JmpSame:
312 case JmpNSame:
313 return simplifyQueryJmp(inst);
315 case JmpIsType:
316 case JmpIsNType:
317 return simplifyJmpIsType(inst);
319 case PrintStr:
320 case PrintInt:
321 case PrintBool: return simplifyPrint(inst);
322 case DecRef:
323 case DecRefNZOrBranch:
324 case DecRefNZ: return simplifyDecRef(inst);
325 case IncRef: return simplifyIncRef(inst);
326 case CheckType: return simplifyCheckType(inst);
327 case AssertNonNull:return simplifyAssertNonNull(inst);
329 case LdCls: return simplifyLdCls(inst);
330 case LdThis: return simplifyLdThis(inst);
331 case LdCtx: return simplifyLdCtx(inst);
332 case LdClsCtx: return simplifyLdClsCtx(inst);
333 case GetCtxFwdCall:return simplifyGetCtxFwdCall(inst);
335 case SpillStack: return simplifySpillStack(inst);
336 case Call: return simplifyCall(inst);
337 case CastStk: return simplifyCastStk(inst);
338 case AssertStk: return simplifyAssertStk(inst);
339 case LdStack: return simplifyLdStack(inst);
340 case LdStackAddr: return simplifyLdStackAddr(inst);
341 case DecRefStack: return simplifyDecRefStack(inst);
342 case DecRefLoc: return simplifyDecRefLoc(inst);
343 case LdLoc: return simplifyLdLoc(inst);
344 case StRef: return simplifyStRef(inst);
346 case ExitOnVarEnv: return simplifyExitOnVarEnv(inst);
348 default:
349 return nullptr;
353 SSATmp* Simplifier::simplifySpillStack(IRInstruction* inst) {
354 auto const sp = inst->src(0);
355 auto const spDeficit = inst->src(1)->getValInt();
356 auto spillVals = inst->srcs().subpiece(2);
357 auto const numSpillSrcs = spillVals.size();
358 auto const spillCells = spillValueCells(inst);
359 int64_t adjustment = spDeficit - spillCells;
361 // If there's nothing to spill, and no stack adjustment, we don't
362 // need the instruction; the old stack is still accurate.
363 if (!numSpillSrcs && spDeficit == 0) return sp;
365 // If our value came from a LdStack on the same sp and offset,
366 // we don't need to spill it.
367 for (uint32_t i = 0, cellOff = 0; i < numSpillSrcs; i++) {
368 const int64_t offset = cellOff + adjustment;
369 auto* srcInst = spillVals[i]->inst();
370 if (srcInst->op() == LdStack && srcInst->src(0) == sp &&
371 srcInst->extra<LdStack>()->offset == offset) {
372 spillVals[i] = m_tb->genDefNone();
374 cellOff++;
377 // Note: although the instruction might have been modified above, we still
378 // need to return nullptr so that it gets cloned later if it's stack-allocated
379 return nullptr;
382 SSATmp* Simplifier::simplifyCall(IRInstruction* inst) {
383 auto spillVals = inst->srcs().subpiece(3);
384 auto const spillStack = inst->src(0)->inst();
385 if (spillStack->op() != SpillStack) {
386 return nullptr;
389 SSATmp* sp = spillStack->src(0);
390 int baseOffset = spillStack->src(1)->getValInt() -
391 spillValueCells(spillStack);
392 auto const numSpillSrcs = spillVals.size();
393 for (int32_t i = 0; i < numSpillSrcs; i++) {
394 const int64_t offset = -(i + 1) + baseOffset;
395 assert(spillVals[i]->type() != Type::ActRec);
396 IRInstruction* srcInst = spillVals[i]->inst();
397 // If our value came from a LdStack on the same sp and offset,
398 // we don't need to spill it.
399 if (srcInst->op() == LdStack && srcInst->src(0) == sp &&
400 srcInst->extra<LdStack>()->offset == offset) {
401 spillVals[i] = m_tb->genDefNone();
405 // Note: although the instruction might have been modified above, we still
406 // need to return nullptr so that it gets cloned later if it's stack-allocated
407 return nullptr;
410 // We never inline functions that could have a VarEnv, so an
411 // ExitOnVarEnv that has a frame based on DefInlineFP can be removed.
412 SSATmp* Simplifier::simplifyExitOnVarEnv(IRInstruction* inst) {
413 auto const frameInst = inst->src(0)->inst();
414 if (frameInst->op() == DefInlineFP) {
415 inst->convertToNop();
417 return nullptr;
420 SSATmp* Simplifier::simplifyLdCtx(IRInstruction* inst) {
421 const Func* func = inst->src(1)->getValFunc();
422 if (func->isStatic()) {
423 // ActRec->m_cls of a static function is always a valid class pointer with
424 // the bottom bit set
425 return gen(LdCctx, inst->src(0));
427 return nullptr;
430 SSATmp* Simplifier::simplifyLdClsCtx(IRInstruction* inst) {
431 SSATmp* ctx = inst->src(0);
432 Type ctxType = ctx->type();
433 if (ctxType.equals(Type::Obj)) {
434 // this pointer... load its class ptr
435 return gen(LdObjClass, ctx);
437 if (ctxType.equals(Type::Cctx)) {
438 return gen(LdClsCctx, ctx);
440 return nullptr;
443 SSATmp* Simplifier::simplifyGetCtxFwdCall(IRInstruction* inst) {
444 SSATmp* srcCtx = inst->src(0);
445 if (srcCtx->isA(Type::Cctx)) {
446 return srcCtx;
448 return nullptr;
451 SSATmp* Simplifier::simplifyLdCls(IRInstruction* inst) {
452 SSATmp* clsName = inst->src(0);
453 if (clsName->isConst()) {
454 const Class* cls = Unit::lookupClass(clsName->getValStr());
455 if (cls) {
456 if (Transl::TargetCache::isPersistentHandle(cls->m_cachedOffset)) {
457 // the class is always defined
458 return cns(cls);
460 const Class* ctx = inst->src(1)->getValClass();
461 if (ctx && ctx->classof(cls)) {
462 // the class of the current function being compiled is the
463 // same as or derived from cls, so cls must be defined and
464 // cannot change the next time we execute this same code
465 return cns(cls);
468 return gen(LdClsCached, clsName);
470 return nullptr;
473 SSATmp* Simplifier::simplifyCheckType(IRInstruction* inst) {
474 Type type = inst->typeParam();
475 SSATmp* src = inst->src(0);
476 Type srcType = src->type();
478 if (srcType.subtypeOf(type)) {
480 * The type of the src is the same or more refined than type, so the
481 * guard is unnecessary.
483 return src;
485 if (type.strictSubtypeOf(srcType)) {
486 return nullptr;
489 if (type.equals(Type::Str) && srcType.maybe(Type::Str)) {
491 * If we're guarding against Str and srcType has StaticStr or CountedStr
492 * in it, refine the output type. This can happen when we have a
493 * KindOfString guard from Translator but internally we know a more
494 * specific subtype of Str.
496 FTRACE(1, "CheckType: refining {} to {}\n", srcType.toString(),
497 type.toString());
498 inst->setTypeParam(type & srcType);
499 return nullptr;
503 * We got a predicted type that is wrong -- it's incompatible with
504 * the tracked type. So throw the prediction away, since it would
505 * always fail.
507 FTRACE(1, "WARNING: CheckType: removed incorrect prediction that {} is {}\n",
508 srcType.toString(), type.toString());
509 return src;
512 SSATmp* Simplifier::simplifyQueryJmp(IRInstruction* inst) {
513 SSATmp* src1 = inst->src(0);
514 SSATmp* src2 = inst->src(1);
515 Opcode opc = inst->op();
516 // reuse the logic in simplifyCmp.
517 SSATmp* newCmp = simplifyCmp(queryJmpToQueryOp(opc), nullptr, src1, src2);
518 if (!newCmp) return nullptr;
520 SSATmp* newQueryJmp = makeInstruction(
521 [=] (IRInstruction* condJmp) -> SSATmp* {
522 SSATmp* newCondJmp = simplifyCondJmp(condJmp);
523 if (newCondJmp) return newCondJmp;
524 if (condJmp->op() == Nop) {
525 // simplifyCondJmp folded the branch into a nop
526 inst->convertToNop();
528 // Couldn't fold condJmp or combine it with newCmp
529 return nullptr;
531 JmpNZero,
532 inst->taken(),
533 newCmp);
534 if (!newQueryJmp) return nullptr;
535 return newQueryJmp;
538 SSATmp* Simplifier::simplifyMov(SSATmp* src) {
539 return src;
542 SSATmp* Simplifier::simplifyNot(SSATmp* src) {
543 if (src->isConst()) {
544 return cns(!src->getValBool());
547 IRInstruction* inst = src->inst();
548 Opcode op = inst->op();
550 switch (op) {
551 // !!X --> X
552 case OpNot:
553 return inst->src(0);
555 // !(X cmp Y) --> X opposite_cmp Y
556 case OpLt:
557 case OpLte:
558 case OpGt:
559 case OpGte:
560 case OpEq:
561 case OpNeq:
562 case OpSame:
563 case OpNSame:
564 // Not for Dbl: (x < NaN) != !(x >= NaN)
565 if (!inst->src(0)->isA(Type::Dbl) &&
566 !inst->src(1)->isA(Type::Dbl)) {
567 return gen(negateQueryOp(op), inst->src(0), inst->src(1));
569 break;
571 case InstanceOfBitmask:
572 case NInstanceOfBitmask:
573 // TODO: combine this with the above check and use isQueryOp or
574 // add an isNegatable.
575 return gen(
576 negateQueryOp(op),
577 std::make_pair(inst->numSrcs(), inst->srcs().begin())
579 return nullptr;
580 // TODO !(X | non_zero) --> 0
581 default: (void)op;
583 return nullptr;
586 #define SIMPLIFY_CONST(OP) do { \
587 /* don't canonicalize to the right, OP might not be commutative */ \
588 if (src1->isConst() && src2->isConst()) { \
589 if (src1->type().isNull()) { \
590 /* Null op Null */ \
591 if (src2->type().isNull()) { \
592 return cns(int64_t(0 OP 0)); \
594 /* Null op ConstInt */ \
595 if (src2->type() == Type::Int) { \
596 return cns(int64_t(0 OP src2->getValInt())); \
598 /* Null op ConstBool */ \
599 if (src2->type() == Type::Bool) { \
600 return cns(int64_t(0 OP src2->getValBool())); \
602 /* Null op StaticStr */ \
603 if (src2->type() == Type::StaticStr) { \
604 const StringData* str = src2->getValStr(); \
605 if (str->isInteger()) { \
606 return cns(int64_t(0 OP str->toInt64())); \
608 return cns(int64_t(0 OP 0)); \
611 if (src1->type() == Type::Int) { \
612 /* ConstInt op Null */ \
613 if (src2->type().isNull()) { \
614 return cns(int64_t(src1->getValInt()) OP 0); \
616 /* ConstInt op ConstInt */ \
617 if (src2->type() == Type::Int) { \
618 return cns(int64_t(src1->getValInt() OP \
619 src2->getValInt())); \
621 /* ConstInt op ConstBool */ \
622 if (src2->type() == Type::Bool) { \
623 return cns(int64_t(src1->getValInt() OP \
624 int(src2->getValBool()))); \
626 /* ConstInt op StaticStr */ \
627 if (src2->type() == Type::StaticStr) { \
628 const StringData* str = src2->getValStr(); \
629 if (str->isInteger()) { \
630 return cns(int64_t(src1->getValInt() OP str->toInt64())); \
632 return cns(int64_t(src1->getValInt() OP 0)); \
635 if (src1->type() == Type::Bool) { \
636 /* ConstBool op Null */ \
637 if (src2->type().isNull()) { \
638 return cns(int64_t(src1->getValBool() OP 0)); \
640 /* ConstBool op ConstInt */ \
641 if (src2->type() == Type::Int) { \
642 return cns(int64_t(int(src1->getValBool()) OP \
643 src2->getValInt())); \
645 /* ConstBool op ConstBool */ \
646 if (src2->type() == Type::Bool) { \
647 return cns(int64_t(src1->getValBool() OP \
648 src2->getValBool())); \
650 /* ConstBool op StaticStr */ \
651 if (src2->type() == Type::StaticStr) { \
652 const StringData* str = src2->getValStr(); \
653 if (str->isInteger()) { \
654 return cns(int64_t(int(src1->getValBool()) OP str->toInt64())); \
656 return cns(int64_t(int(src1->getValBool()) OP 0)); \
659 if (src1->type() == Type::StaticStr) { \
660 const StringData* str = src1->getValStr(); \
661 int64_t strInt = 0; \
662 if (str->isInteger()) { \
663 strInt = str->toInt64(); \
665 /* StaticStr op Null */ \
666 if (src2->type().isNull()) { \
667 return cns(int64_t(strInt OP 0)); \
669 /* StaticStr op ConstInt */ \
670 if (src2->type() == Type::Int) { \
671 return cns(int64_t(strInt OP src2->getValInt())); \
673 /* StaticStr op ConstBool */ \
674 if (src2->type() == Type::Bool) { \
675 return cns(int64_t(strInt OP int(src2->getValBool()))); \
677 /* StaticStr op StaticStr */ \
678 if (src2->type() == Type::StaticStr) { \
679 const StringData* str2 = src2->getValStr(); \
680 if (str2->isInteger()) { \
681 return cns(int64_t(strInt OP str2->toInt64())); \
683 return cns(int64_t(strInt OP 0)); \
687 } while (0)
689 #define SIMPLIFY_COMMUTATIVE(OP, NAME) do { \
690 SIMPLIFY_CONST(OP); \
691 if (src1->isConst() && !src2->isConst()) { \
692 return gen(Op##NAME, src2, src1); \
694 if (src1->isA(Type::Int) && src2->isA(Type::Int)) { \
695 IRInstruction* inst1 = src1->inst(); \
696 IRInstruction* inst2 = src2->inst(); \
697 if (inst1->op() == Op##NAME && inst1->src(1)->isConst()) { \
698 /* (X + C1) + C2 --> X + C3 */ \
699 if (src2->isConst()) { \
700 int64_t right = inst1->src(1)->getValInt(); \
701 right OP##= src2->getValInt(); \
702 return gen(Op##NAME, inst1->src(0), cns(right)); \
704 /* (X + C1) + (Y + C2) --> X + Y + C3 */ \
705 if (inst2->op() == Op##NAME && inst2->src(1)->isConst()) { \
706 int64_t right = inst1->src(1)->getValInt(); \
707 right OP##= inst2->src(1)->getValInt(); \
708 SSATmp* left = gen(Op##NAME, inst1->src(0), inst2->src(0)); \
709 return gen(Op##NAME, left, cns(right)); \
713 } while (0)
715 #define SIMPLIFY_DISTRIBUTIVE(OUTOP, INOP, OUTNAME, INNAME) do { \
716 /* assumes that OUTOP is commutative, don't use with subtract! */ \
717 SIMPLIFY_COMMUTATIVE(OUTOP, OUTNAME); \
718 IRInstruction* inst1 = src1->inst(); \
719 IRInstruction* inst2 = src2->inst(); \
720 Opcode op1 = inst1->op(); \
721 Opcode op2 = inst2->op(); \
722 /* all combinations of X * Y + X * Z --> X * (Y + Z) */ \
723 if (op1 == Op##INNAME && op2 == Op##INNAME) { \
724 if (inst1->src(0) == inst2->src(0)) { \
725 SSATmp* fold = gen(Op##OUTNAME, inst1->src(1), inst2->src(1)); \
726 return gen(Op##INNAME, inst1->src(0), fold); \
728 if (inst1->src(0) == inst2->src(1)) { \
729 SSATmp* fold = gen(Op##OUTNAME, inst1->src(1), inst2->src(0)); \
730 return gen(Op##INNAME, inst1->src(0), fold); \
732 if (inst1->src(1) == inst2->src(0)) { \
733 SSATmp* fold = gen(Op##OUTNAME, inst1->src(0), inst2->src(1)); \
734 return gen(Op##INNAME, inst1->src(1), fold); \
736 if (inst1->src(1) == inst2->src(1)) { \
737 SSATmp* fold = gen(Op##OUTNAME, inst1->src(0), inst2->src(0)); \
738 return gen(Op##INNAME, inst1->src(1), fold); \
741 } while (0)
743 SSATmp* Simplifier::simplifyAdd(SSATmp* src1, SSATmp* src2) {
744 SIMPLIFY_DISTRIBUTIVE(+, *, Add, Mul);
745 if (src2->isConst() && src2->type() == Type::Int) {
746 int64_t src2Val = src2->getValInt();
747 // X + 0 --> X
748 if (src2Val == 0) {
749 if (src1->type() == Type::Bool) {
750 return gen(ConvBoolToInt, src1);
752 return src1;
754 // X + -C --> X - C
755 if (src2Val < 0) {
756 return gen(OpSub, src1, cns(-src2Val));
759 // X + (0 - Y) --> X - Y
760 IRInstruction* inst2 = src2->inst();
761 Opcode op2 = inst2->op();
762 if (op2 == OpSub) {
763 SSATmp* src = inst2->src(0);
764 if (src->isConst() && src->type() == Type::Int) {
765 if (src->getValInt() == 0) {
766 return gen(OpSub, src1, inst2->src(1));
770 return nullptr;
773 SSATmp* Simplifier::simplifySub(SSATmp* src1, SSATmp* src2) {
774 SIMPLIFY_CONST(-);
775 // X - X --> 0
776 if (src1 == src2) {
777 return cns(0);
779 if (src2->isConst() && src2->type() == Type::Int) {
780 int64_t src2Val = src2->getValInt();
781 // X - 0 --> X
782 if (src2Val == 0) {
783 if (src1->type() == Type::Bool) {
784 return gen(ConvBoolToInt, src1);
786 return src1;
788 // X - -C --> X + C
789 if (src2Val < 0 && src2Val > std::numeric_limits<int64_t>::min()) {
790 return gen(OpAdd, src1, cns(-src2Val));
793 // X - (0 - Y) --> X + Y
794 IRInstruction* inst2 = src2->inst();
795 Opcode op2 = inst2->op();
796 if (op2 == OpSub) {
797 SSATmp* src = inst2->src(0);
798 if (src->isConst() && src->type() == Type::Int) {
799 if (src->getValInt() == 0) {
800 return gen(OpAdd, src1, inst2->src(1));
804 // TODO patterns in the form of:
805 // (X - C1) + (X - C2)
806 // (X - C1) + C2
807 // (X - C1) + (X + C2)
808 return nullptr;
811 SSATmp* Simplifier::simplifyMul(SSATmp* src1, SSATmp* src2) {
812 SIMPLIFY_COMMUTATIVE(*, Mul);
813 if (src2->isConst() && src2->type() == Type::Int) {
814 // X * (-1) --> -X
815 if (src2->getValInt() == -1) {
816 return gen(OpSub, cns(0), src1);
818 // X * 0 --> 0
819 if (src2->getValInt() == 0) {
820 return cns(0);
822 // X * 1 --> X
823 if (src2->getValInt() == 1) {
824 if (src1->type() == Type::Bool) {
825 return gen(ConvBoolToInt, src1);
827 return src1;
829 // X * 2 --> X + X
830 if (src2->getValInt() == 2) {
831 return gen(OpAdd, src1, src1);
833 // TODO once IR has shifts
834 // X * 2^C --> X << C
835 // X * (2^C + 1) --> ((X << C) + X)
836 // X * (2^C - 1) --> ((X << C) - X)
838 return nullptr;
841 SSATmp* Simplifier::simplifyBitAnd(SSATmp* src1, SSATmp* src2) {
842 SIMPLIFY_DISTRIBUTIVE(&, |, BitAnd, BitOr);
843 // X & X --> X
844 if (src1 == src2) {
845 return src1;
847 if (src2->isConst()) {
848 // X & 0 --> 0
849 if (src2->getValInt() == 0) {
850 return cns(0);
852 // X & (~0) --> X
853 if (src2->getValInt() == ~0L) {
854 return src1;
857 return nullptr;
860 SSATmp* Simplifier::simplifyBitOr(SSATmp* src1, SSATmp* src2) {
861 SIMPLIFY_DISTRIBUTIVE(|, &, BitOr, BitAnd);
862 // X | X --> X
863 if (src1 == src2) {
864 return src1;
866 if (src2->isConst()) {
867 // X | 0 --> X
868 if (src2->getValInt() == 0) {
869 return src1;
871 // X | (~0) --> ~0
872 if (src2->getValInt() == ~uint64_t(0)) {
873 return cns(~uint64_t(0));
876 return nullptr;
879 SSATmp* Simplifier::simplifyBitXor(SSATmp* src1, SSATmp* src2) {
880 SIMPLIFY_COMMUTATIVE(^, BitXor);
881 // X ^ X --> 0
882 if (src1 == src2)
883 return cns(0);
884 // X ^ 0 --> X; X ^ -1 --> ~X
885 if (src2->isConst()) {
886 if (src2->getValInt() == 0) {
887 return src1;
889 if (src2->getValInt() == -1) {
890 return gen(OpBitNot, src1);
893 return nullptr;
896 SSATmp* Simplifier::simplifyLogicXor(SSATmp* src1, SSATmp* src2) {
897 SIMPLIFY_COMMUTATIVE(^, LogicXor);
898 if (src1 == src2) {
899 return cns(false);
902 // SIMPLIFY_COMMUTATIVE takes care of the both-sides-const case, and
903 // canonicalizes a single const to the right
904 if (src2->isConst()) {
905 if (src2->getValBool()) {
906 return gen(OpNot, src1);
907 } else {
908 return src1;
911 return nullptr;
914 static SSATmp* chaseIncRefs(SSATmp* tmp) {
915 while (tmp->inst()->op() == IncRef) {
916 tmp = tmp->inst()->src(0);
918 return tmp;
921 template<class T, class U>
922 static typename std::common_type<T,U>::type cmpOp(Opcode opName, T a, U b) {
923 switch (opName) {
924 case OpGt: return a > b;
925 case OpGte: return a >= b;
926 case OpLt: return a < b;
927 case OpLte: return a <= b;
928 case OpSame:
929 case OpEq: return a == b;
930 case OpNSame:
931 case OpNeq: return a != b;
932 default:
933 not_reached();
937 SSATmp* Simplifier::simplifyCmp(Opcode opName, IRInstruction* inst,
938 SSATmp* src1, SSATmp* src2) {
939 auto newInst = [inst, this](Opcode op, SSATmp* src1, SSATmp* src2) {
940 return gen(op, inst ? inst->taken() : (Block*)nullptr, src1, src2);
942 // ---------------------------------------------------------------------
943 // Perform some execution optimizations immediately
944 // ---------------------------------------------------------------------
946 // Identity optimization
947 if ((src1 == src2 || chaseIncRefs(src1) == chaseIncRefs(src2)) &&
948 src1->type() != Type::Dbl) {
949 // (val1 == val1) does not simplify to true when val1 is a NaN
950 return cns(bool(cmpOp(opName, 0, 0)));
953 // need both types to be unboxed and known to simplify
954 if (!src1->type().notBoxed() || src1->type() == Type::Cell ||
955 !src2->type().notBoxed() || src2->type() == Type::Cell) {
956 return nullptr;
959 // ---------------------------------------------------------------------
960 // OpSame and OpNSame have some special rules
961 // ---------------------------------------------------------------------
963 if (opName == OpSame || opName == OpNSame) {
964 // OpSame and OpNSame do not perform type juggling
965 if (src1->type() != src2->type()) {
966 if (!(src1->type().isString() && src2->type().isString())) {
967 return cns(opName == OpNSame);
971 // src1 and src2 are same type, treating Str and StaticStr as the same
973 // OpSame and OpNSame have special rules for string and object
974 // Other types may simplify to OpEq and OpNeq, respectively
975 if (src1->type().isString() && src2->type().isString()) {
976 if (src1->isConst() && src2->isConst()) {
977 auto str1 = src1->getValStr();
978 auto str2 = src2->getValStr();
979 bool same = str1->same(str2);
980 return cns(bool(cmpOp(opName, same, 1)));
981 } else {
982 return nullptr;
985 if (src1->type() == Type::Obj && src2->type() == Type::Obj) {
986 return nullptr;
988 // for arrays, don't simplify Same to Eq
989 if (src1->type() == Type::Arr && src2->type() == Type::Arr) {
990 return nullptr;
992 // Type is neither a string nor an object - simplify to OpEq/OpNeq
993 if (opName == OpSame) {
994 return newInst(OpEq, src1, src2);
996 return newInst(OpNeq, src1, src2);
999 // ---------------------------------------------------------------------
1000 // We may now perform constant-constant optimizations
1001 // ---------------------------------------------------------------------
1003 // Null cmp Null
1004 if (src1->type().isNull() && src2->type().isNull()) {
1005 return cns(bool(cmpOp(opName, 0, 0)));
1007 // const cmp const
1008 // TODO this list is incomplete - feel free to add more
1009 // TODO: can simplify const arrays when sizes are different or both 0
1010 if (src1->isConst() && src2->isConst()) {
1011 // StaticStr cmp StaticStr
1012 if (src1->type() == Type::StaticStr &&
1013 src2->type() == Type::StaticStr) {
1014 int cmp = src1->getValStr()->compare(src2->getValStr());
1015 return cns(bool(cmpOp(opName, cmp, 0)));
1017 // ConstInt cmp ConstInt
1018 if (src1->type() == Type::Int && src2->type() == Type::Int) {
1019 return cns(bool(
1020 cmpOp(opName, src1->getValInt(), src2->getValInt())));
1022 // ConstBool cmp ConstBool
1023 if (src1->type() == Type::Bool && src2->type() == Type::Bool) {
1024 return cns(bool(
1025 cmpOp(opName, src1->getValBool(), src2->getValBool())));
1029 // ---------------------------------------------------------------------
1030 // Constant bool comparisons can be strength-reduced
1031 // NOTE: Comparisons with bools get juggled to bool.
1032 // ---------------------------------------------------------------------
1034 // Perform constant-bool optimizations
1035 if (src2->type() == Type::Bool && src2->isConst()) {
1036 bool b = src2->getValBool();
1038 // The result of the comparison might be independent of the truth
1039 // value of the LHS. If so, then simplify.
1040 // E.g. `some-int > true`. some-int may juggle to false or true
1041 // (0 or 1), but `0 > true` and `1 > true` are both false, so we can
1042 // simplify to false immediately.
1043 if (cmpOp(opName, false, b) == cmpOp(opName, true, b)) {
1044 return cns(bool(cmpOp(opName, false, b)));
1047 // There are only two distinct booleans - false and true (0 and 1).
1048 // From above, we know that (0 OP b) != (1 OP b).
1049 // Hence exactly one of (0 OP b) and (1 OP b) is true.
1050 // Hence there is exactly one boolean value of src1 that results in the
1051 // overall expression being true (after type-juggling).
1052 // Hence we may check for equality with that boolean.
1053 // E.g. `some-int > false` is equivalent to `some-int == true`
1054 if (opName != OpEq) {
1055 if (cmpOp(opName, false, b)) {
1056 return newInst(OpEq, src1, cns(false));
1057 } else {
1058 return newInst(OpEq, src1, cns(true));
1063 // ---------------------------------------------------------------------
1064 // For same-type cmps, canonicalize any constants to the right
1065 // Then stop - there are no more simplifications left
1066 // ---------------------------------------------------------------------
1068 if (src1->type() == src2->type() ||
1069 (src1->type().isString() && src2->type().isString())) {
1070 if (src1->isConst() && !src2->isConst()) {
1071 return newInst(commuteQueryOp(opName), src2, src1);
1073 return nullptr;
1076 // ---------------------------------------------------------------------
1077 // Perform type juggling and type canonicalization for different types
1078 // see http://www.php.net/manual/en/language.operators.comparison.php
1079 // ---------------------------------------------------------------------
1081 // nulls get canonicalized to the right
1082 if (src1->type().isNull()) {
1083 return newInst(commuteQueryOp(opName), src2, src1);
1086 // case 1: null cmp string. Convert null to ""
1087 if (src1->type().isString() && src2->type().isNull()) {
1088 return newInst(opName, src1, cns(StringData::GetStaticString("")));
1091 // case 2a: null cmp anything. Convert null to false
1092 if (src2->type().isNull()) {
1093 return newInst(opName, src1, cns(false));
1096 // bools get canonicalized to the right
1097 if (src1->type() == Type::Bool) {
1098 return newInst(commuteQueryOp(opName), src2, src1);
1101 // case 2b: bool cmp anything. Convert anything to bool
1102 if (src2->type() == Type::Bool) {
1103 if (src1->isConst()) {
1104 if (src1->type() == Type::Int) {
1105 return newInst(opName, cns(bool(src1->getValInt())), src2);
1106 } else if (src1->type().isString()) {
1107 auto str = src1->getValStr();
1108 return newInst(opName, cns(str->toBoolean()), src2);
1112 // Optimize comparison between int and const bool
1113 if (src1->type() == Type::Int && src2->isConst()) {
1114 // Based on the const bool optimization (above) opName should be OpEq
1115 always_assert(opName == OpEq);
1117 if (src2->getValBool()) {
1118 return newInst(OpNeq, src1, cns(0));
1119 } else {
1120 return newInst(OpEq, src1, cns(0));
1124 // Nothing fancy to do - perform juggling as normal.
1125 return newInst(opName, gen(ConvCellToBool, src1), src2);
1128 // From here on, we must be careful of how Type::Obj gets dealt with,
1129 // since Type::Obj can refer to an object or to a resource.
1131 // case 3: object cmp object. No juggling to do
1132 // same-type simplification is performed above
1134 // strings get canonicalized to the left
1135 if (src2->type().isString()) {
1136 return newInst(commuteQueryOp(opName), src2, src1);
1139 // ints get canonicalized to the right
1140 if (src1->type() == Type::Int) {
1141 return newInst(commuteQueryOp(opName), src2, src1);
1144 // case 4: number/string/resource cmp. Convert to number (int OR double)
1145 // NOTE: The following if-test only checks for some of the SRON-SRON
1146 // cases (specifically, string-int). Other cases (like string-string)
1147 // are dealt with earlier, while other cases (like number-resource)
1148 // are not caught at all (and end up exiting this macro at the bottom).
1149 if (src1->type().isString() && src1->isConst() &&
1150 src2->type() == Type::Int) {
1151 auto str = src1->getValStr();
1152 int64_t si; double sd;
1153 auto st = str->isNumericWithVal(si, sd, true /* allow errors */);
1154 if (st == KindOfDouble) {
1155 return newInst(opName, cns(sd), src2);
1157 if (st == KindOfNull) {
1158 si = 0;
1160 return newInst(opName, cns(si), src2);
1163 // case 5: array cmp array. No juggling to do
1164 // same-type simplification is performed above
1166 // case 6: array cmp anything. Array is greater
1167 if (src1->isArray()) {
1168 return cns(bool(cmpOp(opName, 1, 0)));
1170 if (src2->isArray()) {
1171 return cns(bool(cmpOp(opName, 0, 1)));
1174 // case 7: object cmp anything. Object is greater
1175 // ---------------------------------------------------------------------
1176 // Unfortunately, we are unsure of whether Type::Obj is an object or a
1177 // resource, so this code cannot be applied.
1178 // ---------------------------------------------------------------------
1179 return nullptr;
1182 SSATmp* Simplifier::simplifyJmpIsType(IRInstruction* inst) {
1183 SSATmp* res = simplifyIsType(inst);
1184 if (res == nullptr) return nullptr;
1185 assert(res->isConst());
1186 if (res->getValBool()) {
1187 // Taken jump
1188 return gen(Jmp_, inst->taken());
1189 } else {
1190 // Not taken jump; turn jump into a nop
1191 inst->convertToNop();
1193 return nullptr;
1196 SSATmp* Simplifier::simplifyIsType(IRInstruction* inst) {
1197 bool trueSense =
1198 inst->op() == IsType || inst->op() == JmpIsType;
1199 auto type = inst->typeParam();
1200 auto src = inst->src(0);
1201 auto srcType = src->type();
1203 // The comparisons below won't work for these cases covered by this
1204 // assert, and we currently don't generate these types.
1205 assert(type.isKnownUnboxedDataType() && type != Type::StaticStr);
1207 // CountedStr and StaticStr are disjoint, but compatible for this purpose.
1208 if (type.isString() && srcType.isString()) {
1209 return cns(trueSense);
1212 // The types are disjoint; the result must be false.
1213 if ((srcType & type).equals(Type::Bottom)) {
1214 return cns(!trueSense);
1217 // The src type is a subtype of the tested type. You'd think the result would
1218 // always be true, but it's not for is_object.
1219 if (!type.subtypeOf(Type::Obj) && srcType.subtypeOf(type)) {
1220 return cns(trueSense);
1223 // At this point, either the tested type is a subtype of the src type, or they
1224 // are non-disjoint but neither is a subtype of the other. (Or it's the weird
1225 // Obj case.) We can't simplify this away.
1226 return nullptr;
1229 SSATmp* Simplifier::simplifyConcat(SSATmp* src1, SSATmp* src2) {
1230 if (src1->isConst() && src1->type() == Type::StaticStr &&
1231 src2->isConst() && src2->type() == Type::StaticStr) {
1232 StringData* str1 = const_cast<StringData *>(src1->getValStr());
1233 StringData* str2 = const_cast<StringData *>(src2->getValStr());
1234 StringData* merge = StringData::GetStaticString(concat_ss(str1, str2));
1235 return cns(merge);
1237 return nullptr;
1240 SSATmp* Simplifier::simplifyConvToArr(IRInstruction* inst) {
1241 SSATmp* src = inst->src(0);
1242 if (src->isConst()) {
1243 Array arr = Array::Create(src->getValVariant());
1244 return cns(ArrayData::GetScalarArray(arr.get()));
1246 return nullptr;
1249 SSATmp* Simplifier::simplifyConvArrToBool(IRInstruction* inst) {
1250 SSATmp* src = inst->src(0);
1251 if (src->isConst()) {
1252 if (src->getValArr()->empty()) {
1253 return cns(false);
1255 return cns(true);
1257 return nullptr;
1260 SSATmp* Simplifier::simplifyConvDblToBool(IRInstruction* inst) {
1261 SSATmp* src = inst->src(0);
1262 if (src->isConst()) {
1263 return cns(bool(src->getValDbl()));
1265 return nullptr;
1268 SSATmp* Simplifier::simplifyConvIntToBool(IRInstruction* inst) {
1269 SSATmp* src = inst->src(0);
1270 if (src->isConst()) {
1271 return cns(bool(src->getValInt()));
1273 return nullptr;
1276 SSATmp* Simplifier::simplifyConvStrToBool(IRInstruction* inst) {
1277 SSATmp* src = inst->src(0);
1278 if (src->isConst()) {
1279 // only the strings "", and "0" convert to false, all other strings
1280 // are converted to true
1281 const StringData* str = src->getValStr();
1282 return cns(!str->empty() && !str->isZero());
1284 return nullptr;
1287 SSATmp* Simplifier::simplifyConvArrToDbl(IRInstruction* inst) {
1288 SSATmp* src = inst->src(0);
1289 if (src->isConst()) {
1290 if (src->getValArr()->empty()) {
1291 return cns(0.0);
1294 return nullptr;
1297 SSATmp* Simplifier::simplifyConvBoolToDbl(IRInstruction* inst) {
1298 SSATmp* src = inst->src(0);
1299 if (src->isConst()) {
1300 return cns(double(src->getValBool()));
1302 return nullptr;
1305 SSATmp* Simplifier::simplifyConvIntToDbl(IRInstruction* inst) {
1306 SSATmp* src = inst->src(0);
1307 if (src->isConst()) {
1308 return cns(double(src->getValInt()));
1310 return nullptr;
1313 SSATmp* Simplifier::simplifyConvStrToDbl(IRInstruction* inst) {
1314 SSATmp* src = inst->src(0);
1315 if (src->isConst()) {
1316 const StringData *str = src->getValStr();
1317 int64_t lval;
1318 double dval;
1319 DataType ret = str->isNumericWithVal(lval, dval, 1);
1320 if (ret == KindOfInt64) {
1321 dval = (double)lval;
1322 } else if (ret != KindOfDouble) {
1323 dval = 0.0;
1325 return cns(dval);
1327 return nullptr;
1330 SSATmp* Simplifier::simplifyConvArrToInt(IRInstruction* inst) {
1331 SSATmp* src = inst->src(0);
1332 if (src->isConst()) {
1333 if (src->getValArr()->empty()) {
1334 return cns(0);
1336 return cns(1);
1338 return nullptr;
1341 SSATmp* Simplifier::simplifyConvBoolToInt(IRInstruction* inst) {
1342 SSATmp* src = inst->src(0);
1343 if (src->isConst()) {
1344 return cns(int(src->getValBool()));
1346 return nullptr;
1349 SSATmp* Simplifier::simplifyConvDblToInt(IRInstruction* inst) {
1350 SSATmp* src = inst->src(0);
1351 if (src->isConst()) {
1352 return cns(toInt64(src->getValDbl()));
1354 return nullptr;
1357 SSATmp* Simplifier::simplifyConvStrToInt(IRInstruction* inst) {
1358 SSATmp* src = inst->src(0);
1359 if (src->isConst()) {
1360 const StringData *str = src->getValStr();
1361 int64_t lval;
1362 double dval;
1363 DataType ret = str->isNumericWithVal(lval, dval, 1);
1364 if (ret == KindOfDouble) {
1365 lval = (int64_t)dval;
1366 } else if (ret != KindOfInt64) {
1367 lval = 0;
1369 return cns(lval);
1371 return nullptr;
1374 SSATmp* Simplifier::simplifyConvBoolToStr(IRInstruction* inst) {
1375 SSATmp* src = inst->src(0);
1376 if (src->isConst()) {
1377 if (src->getValBool()) {
1378 return cns(StringData::GetStaticString("1"));
1380 return cns(StringData::GetStaticString(""));
1382 return nullptr;
1385 SSATmp* Simplifier::simplifyConvDblToStr(IRInstruction* inst) {
1386 SSATmp* src = inst->src(0);
1387 if (src->isConst()) {
1388 return cns(
1389 StringData::convert_double_helper(src->getValDbl()));
1391 return nullptr;
1394 SSATmp* Simplifier::simplifyConvIntToStr(IRInstruction* inst) {
1395 SSATmp* src = inst->src(0);
1396 if (src->isConst()) {
1397 return cns(
1398 StringData::convert_integer_helper(src->getValInt()));
1400 return nullptr;
1403 SSATmp* Simplifier::simplifyConvCellToBool(IRInstruction* inst) {
1404 auto const src = inst->src(0);
1405 auto const srcType = src->type();
1407 if (srcType.isBool()) return src;
1408 if (srcType.isNull()) return cns(false);
1409 if (srcType.isArray()) return gen(ConvArrToBool, src);
1410 if (srcType.isDbl()) return gen(ConvDblToBool, src);
1411 if (srcType.isInt()) return gen(ConvIntToBool, src);
1412 if (srcType.isString()) return gen(ConvStrToBool, src);
1413 if (srcType.isObj()) return cns(true);
1415 return nullptr;
1418 SSATmp* Simplifier::simplifyLdClsPropAddr(IRInstruction* inst) {
1419 SSATmp* propName = inst->src(1);
1420 if (!propName->isConst()) return nullptr;
1422 SSATmp* cls = inst->src(0);
1423 auto ctxCls = inst->src(2)->getValClass();
1425 if (canUseSPropCache(cls, propName->getValStr(), ctxCls)) {
1427 const StringData* clsNameStr = findClassName(cls);
1429 return gen(LdClsPropAddrCached,
1430 inst->taken(),
1431 cls,
1432 propName,
1433 cns(clsNameStr),
1434 inst->src(2));
1437 return nullptr;
1441 * If we're in an inlined frame, use the this that we put in the
1442 * inlined ActRec. (This could chase more intervening SpillStack
1443 * instructions to find the SpillFrame, but for now we don't inline
1444 * calls that will have that.)
1446 SSATmp* Simplifier::simplifyLdThis(IRInstruction* inst) {
1447 auto fpInst = inst->src(0)->inst();
1448 if (fpInst->op() == DefInlineFP) {
1449 auto spInst = fpInst->src(0)->inst();
1450 if (spInst->op() == SpillFrame &&
1451 spInst->src(3)->isA(Type::Obj)) {
1452 return spInst->src(3);
1454 return nullptr;
1457 return nullptr;
1460 SSATmp* Simplifier::simplifyUnbox(IRInstruction* inst) {
1461 auto* src = inst->src(0);
1462 auto type = outputType(inst);
1464 Type srcType = src->type();
1465 if (srcType.notBoxed()) {
1466 assert(type.equals(srcType));
1467 return src;
1469 if (srcType.isBoxed()) {
1470 srcType = srcType.innerType();
1471 assert(type.equals(srcType));
1472 return gen(LdRef, type, inst->taken()->trace(), src);
1474 return nullptr;
1477 SSATmp* Simplifier::simplifyUnboxPtr(IRInstruction* inst) {
1478 if (inst->src(0)->isA(Type::PtrToCell)) {
1479 // Nothing to unbox
1480 return inst->src(0);
1482 return nullptr;
1485 SSATmp* Simplifier::simplifyCheckInit(IRInstruction* inst) {
1486 Type srcType = inst->src(0)->type();
1487 srcType = inst->op() == CheckInitMem ? srcType.deref() : srcType;
1488 assert(srcType.notPtr());
1489 assert(inst->taken());
1490 if (srcType.isInit()) {
1491 inst->convertToNop();
1493 return nullptr;
1496 SSATmp* Simplifier::simplifyPrint(IRInstruction* inst) {
1497 if (inst->src(0)->type().isNull()) {
1498 inst->convertToNop();
1500 return nullptr;
1503 SSATmp* Simplifier::simplifyDecRef(IRInstruction* inst) {
1504 if (!isRefCounted(inst->src(0))) {
1505 inst->convertToNop();
1507 return nullptr;
1510 SSATmp* Simplifier::simplifyIncRef(IRInstruction* inst) {
1511 SSATmp* src = inst->src(0);
1512 if (!isRefCounted(src)) {
1513 return src;
1515 return nullptr;
1518 SSATmp* Simplifier::simplifyCondJmp(IRInstruction* inst) {
1519 SSATmp* const src = inst->src(0);
1520 IRInstruction* const srcInst = src->inst();
1521 const Opcode srcOpcode = srcInst->op();
1523 // After other simplifications below (isConvIntOrPtrToBool), we can
1524 // end up with a non-Bool input. Nothing more to do in this case.
1525 if (src->type() != Type::Bool) {
1526 return nullptr;
1529 // Constant propagate.
1530 if (src->isConst()) {
1531 bool val = src->getValBool();
1532 if (inst->op() == JmpZero) {
1533 val = !val;
1535 if (val) {
1536 return gen(Jmp_, inst->taken());
1538 inst->convertToNop();
1539 return nullptr;
1542 // Pull negations into the jump.
1543 if (src->inst()->op() == OpNot) {
1544 return gen(inst->op() == JmpZero ? JmpNZero : JmpZero,
1545 inst->taken(),
1546 srcInst->src(0));
1550 * Try to combine the src inst with the Jmp. We can't do any
1551 * combinations of the src instruction with the jump if the src's
1552 * are refcounted, since we may have dec refs between the src
1553 * instruction and the jump.
1555 for (auto& src : srcInst->srcs()) {
1556 if (isRefCounted(src)) return nullptr;
1559 // If the source is conversion of an int or pointer to boolean, we
1560 // can test the int/ptr value directly.
1561 if (isConvIntOrPtrToBool(srcInst)) {
1562 return gen(inst->op(), inst->taken(), srcInst->src(0));
1565 // Fuse jumps with query operators.
1566 if (isQueryOp(srcOpcode)) {
1567 SrcRange ssas = srcInst->srcs();
1568 return gen(
1569 queryToJmpOp(
1570 inst->op() == JmpZero
1571 ? negateQueryOp(srcOpcode)
1572 : srcOpcode),
1573 srcInst->typeParam(), // if it had a type param
1574 inst->taken(),
1575 std::make_pair(ssas.size(), ssas.begin())
1579 return nullptr;
1582 SSATmp* Simplifier::simplifyCastStk(IRInstruction* inst) {
1583 auto const info = getStackValue(inst->src(0),
1584 inst->extra<CastStk>()->offset);
1585 if (info.knownType.subtypeOf(inst->typeParam())) {
1586 // No need to cast---the type was as good or better.
1587 inst->convertToNop();
1589 return nullptr;
1592 SSATmp* Simplifier::simplifyAssertStk(IRInstruction* inst) {
1593 auto const info = getStackValue(inst->src(0),
1594 inst->extra<AssertStk>()->offset);
1596 // AssertStk indicated that we knew the type from static analysis,
1597 // so this assert just double checks.
1598 if (info.value) assert(info.value->isA(inst->typeParam()));
1600 if (info.knownType.subtypeOf(inst->typeParam())) {
1601 inst->convertToNop();
1603 return nullptr;
1606 SSATmp* Simplifier::simplifyLdStack(IRInstruction* inst) {
1607 auto const info = getStackValue(inst->src(0),
1608 inst->extra<LdStack>()->offset);
1610 // We don't want to extend live ranges of tmps across calls, so we
1611 // don't get the value if spansCall is true; however, we can use
1612 // any type information known.
1613 if (info.value && (!info.spansCall ||
1614 info.value->inst()->op() == DefConst)) {
1615 return info.value;
1617 if (!info.knownType.equals(Type::None)) {
1618 inst->setTypeParam(
1619 Type::mostRefined(inst->typeParam(), info.knownType)
1622 return nullptr;
1625 SSATmp* Simplifier::simplifyDecRefLoc(IRInstruction* inst) {
1626 if (inst->typeParam().notCounted()) {
1627 inst->convertToNop();
1629 return nullptr;
1632 SSATmp* Simplifier::simplifyLdLoc(IRInstruction* inst) {
1633 if (inst->typeParam().isNull()) {
1634 return cns(inst->typeParam());
1636 return nullptr;
1639 // Replace StRef with StRefNT when we know we aren't going to change
1640 // its m_type field.
1641 SSATmp* Simplifier::simplifyStRef(IRInstruction* inst) {
1642 auto const oldUnbox = inst->src(0)->type().unbox();
1643 auto const newType = inst->src(1)->type();
1644 if (oldUnbox.isKnownDataType() &&
1645 oldUnbox.equals(newType) && !oldUnbox.isString()) {
1646 inst->setOpcode(StRefNT);
1648 return nullptr;
1651 SSATmp* Simplifier::simplifyLdStackAddr(IRInstruction* inst) {
1652 auto const info = getStackValue(inst->src(0),
1653 inst->extra<StackOffset>()->offset);
1654 if (!info.knownType.equals(Type::None)) {
1655 inst->setTypeParam(
1656 Type::mostRefined(inst->typeParam(), info.knownType.ptr())
1659 return nullptr;
1662 SSATmp* Simplifier::simplifyDecRefStack(IRInstruction* inst) {
1663 if (inst->typeParam().notCounted()) {
1664 inst->convertToNop();
1665 return nullptr;
1667 auto const info = getStackValue(inst->src(0),
1668 inst->extra<StackOffset>()->offset);
1669 if (info.value && !info.spansCall) {
1670 inst->convertToNop();
1671 return gen(DecRef, info.knownType, info.value);
1673 if (!info.knownType.equals(Type::None)) {
1674 inst->setTypeParam(
1675 Type::mostRefined(inst->typeParam(), info.knownType)
1678 return nullptr;
1681 SSATmp* Simplifier::simplifyAssertNonNull(IRInstruction* inst) {
1682 auto t = inst->typeParam();
1683 assert(t.maybe(Type::Nullptr));
1684 if (t.subtypeOf(Type::Nullptr)) {
1685 return inst->src(0);
1687 return nullptr;
1690 //////////////////////////////////////////////////////////////////////