2 +----------------------------------------------------------------------+
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"
20 #include <type_traits>
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"
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();
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;
54 case StashGeneratorSP
:
55 return getStackValue(inst
->src(1), index
);
58 auto const extra
= inst
->extra
<ReDefSP
>();
59 auto info
= getStackValue(inst
->src(0), index
);
60 if (extra
->spansCall
) info
.spansCall
= true;
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
);
81 // We don't have a value, but we may know the type due to guarding
83 if (inst
->extra
<StackOffset
>()->offset
== index
) {
84 return StackValueInfo
{ inst
, inst
->typeParam() };
86 return getStackValue(inst
->src(0), index
);
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
);
99 // return value from call
100 return StackValueInfo
{ inst
, Type::Gen
};
103 getStackValue(inst
->src(0),
104 // Pushes a return value, pops an ActRec and args Array
106 (1 /* pushed */ - (kNumActRecCells
+ 1) /* popped */));
107 info
.spansCall
= true;
113 // return value from call
114 return StackValueInfo
{ inst
, Type::Gen
};
117 getStackValue(inst
->src(0),
119 (1 /* pushed */ - kNumActRecCells
/* popped */));
120 info
.spansCall
= true;
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
};
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
));
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
154 if (index
== 1) return StackValueInfo
{ inst
, inst
->typeParam() };
155 if (index
== 0) return getStackValue(prevSp
, index
);
158 if (index
== 2) return StackValueInfo
{ inst
, inst
->typeParam() };
159 if (index
< 2) return getStackValue(prevSp
, index
);
162 if (index
== 0) return StackValueInfo
{ inst
, Type::Int
};
163 if (index
== 1) return StackValueInfo
{ inst
, Type::Cell
};
165 case Op::FPushCufSafe
:
166 if (index
== kNumActRecCells
) return StackValueInfo
{ inst
, Type::Bool
};
167 if (index
== kNumActRecCells
+ 1) return getStackValue(prevSp
, 0);
171 if (index
== kNumActRecCells
) return StackValueInfo
{ inst
, Type::Obj
};
172 if (index
== kNumActRecCells
+ 1) return getStackValue(prevSp
, 0);
174 if (index
== 0) return StackValueInfo
{ inst
, Type::Bool
};
175 if (index
== 1) return StackValueInfo
{ inst
, Type::Cell
};
179 if (index
== 0 && inst
->hasTypeParam()) {
180 return StackValueInfo
{ inst
, inst
->typeParam() };
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
);
194 case CufIterSpillFrame
:
196 if (index
< kNumActRecCells
) {
197 return StackValueInfo
{ inst
, Type::StackElem
};
199 return getStackValue(inst
->src(0), index
- kNumActRecCells
);
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
);
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
;
225 ret
.push_back(value
);
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
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();
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.
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();
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();
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
,
314 return m_irb
.gen(op
, marker
, std::forward
<Args
>(args
)...);
317 //////////////////////////////////////////////////////////////////////
319 SSATmp
* Simplifier::simplify(IRInstruction
* inst
) {
322 assert(m_insts
.top() == inst
);
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();
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
);
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
);
397 case IsNType
: return simplifyIsType(inst
);
398 case IsScalarType
: return simplifyIsScalarType(inst
);
399 case CheckInit
: return simplifyCheckInit(inst
);
403 return simplifyCondJmp(inst
);
419 return simplifyQueryJmp(inst
);
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
);
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
;
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();
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));
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
);
502 SSATmp
* Simplifier::simplifyGetCtxFwdCall(IRInstruction
* inst
) {
503 SSATmp
* srcCtx
= inst
->src(0);
504 if (srcCtx
->isA(Type::Cctx
)) {
510 SSATmp
* Simplifier::simplifyConvClsToCctx(IRInstruction
* inst
) {
511 auto* srcInst
= inst
->src(0)->inst();
512 if (srcInst
->is(LdClsCctx
)) return srcInst
->src(0);
517 SSATmp
* Simplifier::simplifyLdCls(IRInstruction
* inst
) {
518 SSATmp
* clsName
= inst
->src(0);
519 if (clsName
->isConst()) {
520 const Class
* cls
= Unit::lookupClass(clsName
->strVal());
522 if (RDS::isPersistentHandle(cls
->classHandle())) {
523 // the class is always defined
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
534 return gen(LdClsCached
, inst
->taken(), clsName
);
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());
562 if (newType
>= oldType
) {
564 * The type of the src is the same or more refined than type, so the guard
569 if (newType
< oldType
) {
570 assert(!src
->isConst());
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.
598 if (newType
>= oldType
) {
599 // The new type isn't better than the old type.
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());
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(
640 return simplifyCondJmp(inst
);
643 SSATmp
* Simplifier::simplifyMov(SSATmp
* 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
));
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()));
671 template <class Oper
>
672 SSATmp
* Simplifier::simplifyCommutative(SSATmp
* src1
,
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
));
706 template <class OutOper
, class InOper
>
707 SSATmp
* Simplifier::simplifyDistributive(SSATmp
* src1
,
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
);
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
)) {
748 if (src2
->isConst()) {
749 int64_t src2Val
= src2
->intVal();
751 if (src2Val
== 0) return src1
;
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();
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
);
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
);
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
;
814 if (src1
== src2
) return cns(0);
816 if (src2
->isConst()) {
817 int64_t src2Val
= src2
->intVal();
819 if (src2Val
== 0) return src1
;
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));
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();
845 if (rhs
== -1) return gen(SubInt
, cns(0), src1
);
847 if (rhs
== 0) return cns(0);
849 if (rhs
== 1) return src1
;
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
) {
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
);
878 SSATmp
* Simplifier::simplifyAddDbl(SSATmp
* src1
, SSATmp
* src2
) {
879 if (auto simp
= simplifyConst(src1
, src2
, std::plus
<double>())) {
885 SSATmp
* Simplifier::simplifySubDbl(SSATmp
* src1
, SSATmp
* src2
) {
886 if (auto c
= simplifyConst(src1
, src2
, std::minus
<double>())) {
892 SSATmp
* Simplifier::simplifyMulDbl(SSATmp
* src1
, SSATmp
* src2
) {
893 auto mul
= std::multiplies
<double>();
894 if (auto simp
= simplifyConst(src1
, src2
, mul
)) return simp
;
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);
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
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) {
951 if (src2
->isConst()) {
953 if (src2
->intVal() == 0) {
957 if (src2
->intVal() == ~0L) {
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) {
975 if (src2
->isConst()) {
977 if (src2
->intVal() == 0) {
981 if (src2
->intVal() == ~uint64_t(0)) {
982 return cns(~uint64_t(0));
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
)) {
994 if (src1
== src2
) return cns(0);
996 if (src2
->isConst(0)) return src1
;
1000 SSATmp
* Simplifier::simplifyXorTrue(SSATmp
* src
) {
1001 IRInstruction
* inst
= src
->inst();
1002 Opcode op
= inst
->op();
1007 if (inst
->src(1)->isConst(true)) return inst
->src(0);
1010 // !(X cmp Y) --> X opposite_cmp Y
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
);
1027 case InstanceOfBitmask
:
1028 case NInstanceOfBitmask
:
1029 // TODO: combine this with the above check and use isQueryOp or
1030 // add an isNegatable.
1033 std::make_pair(inst
->numSrcs(), inst
->srcs().begin())
1036 // TODO !(X | non_zero) --> 0
1042 SSATmp
* Simplifier::simplifyXorBool(SSATmp
* src1
, SSATmp
* src2
) {
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
);
1054 if (src2
->isConst(false)) return src1
;
1056 // X^1 => simplify "not" logic
1057 if (src2
->isConst(true)) return simplifyXorTrue(src1
);
1061 template<class Oper
>
1062 SSATmp
* Simplifier::simplifyShift(SSATmp
* src1
, SSATmp
* src2
, Oper op
) {
1063 if (src1
->isConst()) {
1064 if (src1
->intVal() == 0) {
1068 if (src2
->isConst()) {
1069 return cns(op(src1
->intVal(), src2
->intVal()));
1073 if (src2
->isConst() && src2
->intVal() == 0) {
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
) {
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
) {
1096 template<class T
, class U
>
1097 static typename
std::common_type
<T
,U
>::type
cmpOp(Opcode opName
, T a
, U b
) {
1100 case Gt
: return a
> b
;
1102 case Gte
: return a
>= b
;
1104 case Lt
: return a
< b
;
1106 case Lte
: return a
<= b
;
1109 case Eq
: return a
== b
;
1112 case Neq
: return a
!= b
;
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()) {
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)));
1168 auto const badTypes
= Type::Obj
| Type::Res
| Type::Arr
;
1169 if (type1
.maybe(badTypes
) || type2
.maybe(badTypes
)) {
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 // ---------------------------------------------------------------------
1182 if (type1
<= Type::Null
&& type2
<= Type::Null
) {
1183 return cns(bool(cmpOp(opName
, 0, 0)));
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
)) {
1198 cmpOp(opName
, src1
->intVal(), src2
->intVal())));
1200 // ConstBool cmp ConstBool
1201 if (src1
->isA(Type::Bool
) && src2
->isA(Type::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`
1233 if (cmpOp(opName
, false, b
)) {
1234 return newInst(Eq
, src1
, cns(false));
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
);
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));
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
) {
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 // ---------------------------------------------------------------------
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
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
)) {
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.
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
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
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
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
1460 return gen(ConcatStrStr
, asStr
, src2
);
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
));
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()));
1487 SSATmp
* Simplifier::simplifyConvArrToBool(IRInstruction
* inst
) {
1488 SSATmp
* src
= inst
->src(0);
1489 if (src
->isConst()) {
1490 if (src
->arrVal()->empty()) {
1498 SSATmp
* Simplifier::simplifyConvDblToBool(IRInstruction
* inst
) {
1499 SSATmp
* src
= inst
->src(0);
1500 if (src
->isConst()) {
1501 return cns(bool(src
->dblVal()));
1507 SSATmp
* Simplifier::simplifyConvIntToBool(IRInstruction
* inst
) {
1508 SSATmp
* src
= inst
->src(0);
1509 if (src
->isConst()) {
1510 return cns(bool(src
->intVal()));
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());
1526 SSATmp
* Simplifier::simplifyConvArrToDbl(IRInstruction
* inst
) {
1527 SSATmp
* src
= inst
->src(0);
1528 if (src
->isConst()) {
1529 if (src
->arrVal()->empty()) {
1536 SSATmp
* Simplifier::simplifyConvBoolToDbl(IRInstruction
* inst
) {
1537 SSATmp
* src
= inst
->src(0);
1538 if (src
->isConst()) {
1539 return cns(double(src
->boolVal()));
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));
1555 SSATmp
* Simplifier::simplifyConvStrToDbl(IRInstruction
* inst
) {
1556 SSATmp
* src
= inst
->src(0);
1557 if (src
->isConst()) {
1558 const StringData
*str
= src
->strVal();
1561 DataType ret
= str
->isNumericWithVal(lval
, dval
, 1);
1562 if (ret
== KindOfInt64
) {
1563 dval
= (double)lval
;
1564 } else if (ret
!= KindOfDouble
) {
1572 SSATmp
* Simplifier::simplifyConvArrToInt(IRInstruction
* inst
) {
1573 SSATmp
* src
= inst
->src(0);
1574 if (src
->isConst()) {
1575 if (src
->arrVal()->empty()) {
1583 SSATmp
* Simplifier::simplifyConvBoolToInt(IRInstruction
* inst
) {
1584 SSATmp
* src
= inst
->src(0);
1585 if (src
->isConst()) {
1586 return cns(int(src
->boolVal()));
1591 SSATmp
* Simplifier::simplifyConvDblToInt(IRInstruction
* inst
) {
1592 SSATmp
* src
= inst
->src(0);
1593 if (src
->isConst()) {
1594 return cns(toInt64(src
->dblVal()));
1599 SSATmp
* Simplifier::simplifyConvStrToInt(IRInstruction
* inst
) {
1600 SSATmp
* src
= inst
->src(0);
1601 if (src
->isConst()) {
1602 const StringData
*str
= src
->strVal();
1605 DataType ret
= str
->isNumericWithVal(lval
, dval
, 1);
1606 if (ret
== KindOfDouble
) {
1607 lval
= (int64_t)dval
;
1608 } else if (ret
!= KindOfInt64
) {
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(""));
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
));
1636 SSATmp
* Simplifier::simplifyConvIntToStr(IRInstruction
* inst
) {
1637 SSATmp
* src
= inst
->src(0);
1638 if (src
->isConst()) {
1640 makeStaticString(folly::to
<std::string
>(src
->intVal()))
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
1661 if (!cls
->instanceCtor()) {
1665 return gen(ConvObjToBool
, src
);
1667 if (srcType
<= Type::Res
) return nullptr; // No specialization yet
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
) {
1690 if (srcType
<= Type::Obj
) return gen(ConvObjToStr
, catchTrace
, src
);
1691 if (srcType
<= Type::Res
) return gen(ConvResToStr
, catchTrace
, src
);
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
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
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
) {
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
));
1761 if (srcType
.isBoxed()) {
1762 srcType
= srcType
.innerType();
1763 assert(type
.equals(srcType
));
1764 return gen(LdRef
, type
, inst
->taken(), src
);
1769 SSATmp
* Simplifier::simplifyUnboxPtr(IRInstruction
* inst
) {
1770 if (inst
->src(0)->isA(Type::PtrToCell
)) {
1771 return inst
->src(0);
1776 SSATmp
* Simplifier::simplifyBoxPtr(IRInstruction
* inst
) {
1777 if (inst
->src(0)->isA(Type::PtrToBoxedCell
)) {
1778 return inst
->src(0);
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();
1791 SSATmp
* Simplifier::simplifyDecRef(IRInstruction
* inst
) {
1792 auto src
= inst
->src(0);
1793 if (!m_irb
.typeMightRelax(src
) && !isRefCounted(src
)) {
1794 inst
->convertToNop();
1799 SSATmp
* Simplifier::simplifyIncRef(IRInstruction
* inst
) {
1800 SSATmp
* src
= inst
->src(0);
1801 if (!m_irb
.typeMightRelax(src
) && !isRefCounted(src
)) {
1802 inst
->convertToNop();
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();
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
)) {
1829 // Constant propagate.
1830 if (src
->isConst()) {
1831 bool val
= src
->boolVal();
1832 if (inst
->op() == JmpZero
) {
1836 inst
->convertToJmp();
1838 inst
->convertToNop();
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));
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()) {
1866 case ConvCellToBool
:
1867 return instr
->src(0)->type().subtypeOfAny(
1868 Type::Func
, Type::Cls
, Type::VarEnv
, Type::TCA
);
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));
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(
1898 inst
->maybeTypeParam(),
1899 std::make_pair(ssas
.size(), ssas
.begin())
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();
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();
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
);
1957 inst
->setTypeParam(std::min(inst
->typeParam(), info
.knownType
));
1961 SSATmp
* Simplifier::simplifyTakeStack(IRInstruction
* inst
) {
1962 if (inst
->src(0)->type().notCounted() &&
1963 !m_irb
.typeMightRelax(inst
->src(0))) {
1964 inst
->convertToNop();
1970 SSATmp
* Simplifier::simplifyDecRefLoc(IRInstruction
* inst
) {
1971 auto const localValue
= m_irb
.localValue(inst
->extra
<DecRefLoc
>()->locId
,
1973 if (!m_irb
.typeMightRelax(localValue
) && inst
->typeParam().notCounted()) {
1974 inst
->convertToNop();
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
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()));
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
)) {
2007 inst
->setTypeParam(std::min(inst
->typeParam(), info
.knownType
));
2008 if (inst
->typeParam().notCounted()) {
2009 inst
->convertToNop();
2014 SSATmp
* Simplifier::simplifyAssertNonNull(IRInstruction
* inst
) {
2015 if (inst
->src(0)->type().not(Type::Nullptr
)) {
2016 return inst
->src(0);
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
2030 inst
->convertToJmp();
2031 } else if (array
->isConst()) {
2032 if (idxVal
>= array
->arrVal()->size()) {
2033 inst
->convertToJmp();
2035 // We should convert inst to a nop here but that exposes t3626113
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());
2049 // The index doesn't exist. This code should be unreachable at runtime.
2053 if (value
->m_type
== KindOfRef
) value
= value
->m_data
.pref
->tv();
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
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
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
);
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);
2110 // Find the most general constraint that doesn't modify the type being
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
);
2119 increment(tc
.category
);
2128 //////////////////////////////////////////////////////////////////////