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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/irgen-builtin.h"
18 #include "hphp/runtime/base/array-init.h"
19 #include "hphp/runtime/vm/jit/analysis.h"
20 #include "hphp/runtime/vm/jit/type-constraint.h"
21 #include "hphp/runtime/vm/jit/type.h"
22 #include "hphp/runtime/ext/ext_collections.h"
24 #include "hphp/runtime/vm/jit/irgen-inlining.h"
25 #include "hphp/runtime/vm/jit/irgen-call.h"
26 #include "hphp/runtime/vm/jit/irgen-exit.h"
27 #include "hphp/runtime/vm/jit/irgen-interpone.h"
28 #include "hphp/runtime/vm/jit/irgen-types.h"
29 #include "hphp/runtime/vm/jit/irgen-internal.h"
31 namespace HPHP
{ namespace jit
{ namespace irgen
{
35 //////////////////////////////////////////////////////////////////////
41 s_in_array("in_array"),
42 s_get_class("get_class"),
43 s_get_called_class("get_called_class"),
51 //////////////////////////////////////////////////////////////////////
53 // Will turn into either an int or a double in zend_convert_scalar_to_number.
54 bool type_converts_to_number(Type ty
) {
55 return ty
.subtypeOfAny(
66 //////////////////////////////////////////////////////////////////////
68 SSATmp
* opt_is_a(HTS
& env
, uint32_t numArgs
) {
69 if (numArgs
!= 3) return nullptr;
71 // The last param of is_a has a default argument of false, which makes it
72 // behave the same as instanceof (which doesn't allow a string as the tested
73 // object). Don't do the conversion if we're not sure this arg is false.
74 auto const allowStringType
= topType(env
, 0);
75 if (!(allowStringType
<= Type::Bool
)
76 || !allowStringType
.isConst()
77 || allowStringType
.boolVal()) {
81 // Unlike InstanceOfD, is_a doesn't support interfaces like Stringish, so e.g.
82 // "is_a('x', 'Stringish')" is false even though "'x' instanceof Stringish" is
83 // true. So if the first arg is not an object, the return is always false.
84 auto const objType
= topType(env
, 2);
85 if (!objType
.maybe(Type::Obj
)) {
86 return cns(env
, false);
89 if (objType
<= Type::Obj
) {
90 auto const classnameType
= topType(env
, 1);
91 if (classnameType
<= Type::StaticStr
&& classnameType
.isConst()) {
92 return implInstanceOfD(
95 top(env
, Type::Str
, 1)->strVal()
100 // The LHS is a strict superset of Obj; bail.
104 SSATmp
* opt_count(HTS
& env
, uint32_t numArgs
) {
105 if (numArgs
!= 2) return nullptr;
107 auto const mode
= topC(env
, 0);
108 auto const val
= topC(env
, 1);
110 // Bail if we're trying to do a recursive count()
111 if (!mode
->isConst(0)) return nullptr;
113 return gen(env
, Count
, val
);
116 SSATmp
* opt_ord(HTS
& env
, uint32_t numArgs
) {
117 if (numArgs
!= 1) return nullptr;
119 auto const arg
= topC(env
, 0);
120 // a static string is passed in, resolve with a constant.
121 if (arg
->type().isConst(Type::Str
)) {
122 unsigned char first
= arg
->strVal()->data()[0];
123 return cns(env
, int64_t(first
));
128 SSATmp
* opt_ini_get(HTS
& env
, uint32_t numArgs
) {
129 if (numArgs
!= 1) return nullptr;
131 // Only generate the optimized version if the argument passed in is a
132 // static string with a constant literal value so we can get the string value
134 auto const argType
= topType(env
, 0);
135 if (!(argType
<= Type::StaticStr
) || !argType
.isConst()) {
139 // We can only optimize settings that are system wide since user level
140 // settings can be overridden during the execution of a request.
141 auto const settingName
= top(env
, Type::Str
, 0)->strVal()->toCppString();
142 IniSetting::Mode mode
= IniSetting::PHP_INI_NONE
;
143 if (!IniSetting::GetMode(settingName
, mode
)) {
146 if (mode
& ~IniSetting::PHP_INI_SYSTEM
) {
149 if (mode
== IniSetting::PHP_INI_ALL
) { /* PHP_INI_ALL has a weird encoding */
154 IniSetting::Get(settingName
, value
);
155 // ini_get() is now enhanced to return more than strings
156 // Only return a string, get out of here if we are something
157 // else like an array
158 if (value
.isString()) {
159 return cns(env
, makeStaticString(value
.toString()));
165 * Transforms in_array with a static haystack argument into an AKExists with the
168 SSATmp
* opt_in_array(HTS
& env
, uint32_t numArgs
) {
169 if (numArgs
!= 3) return nullptr;
171 // We will restrict this optimization to needles that are strings, and
172 // haystacks that have only non-numeric string keys. This avoids a bunch of
173 // complication around numeric-string array-index semantics.
174 if (!(topType(env
, 2) <= Type::Str
)) {
178 auto const haystackType
= topType(env
, 1);
179 if (!(haystackType
<= Type::StaticArr
) || !haystackType
.isConst()) {
180 // Haystack isn't statically known
184 auto const haystack
= haystackType
.arrVal();
185 if (haystack
->size() == 0) {
186 return cns(env
, false);
189 ArrayInit flipped
{haystack
->size(), ArrayInit::Map
{}};
191 for (auto iter
= ArrayIter
{haystack
}; iter
; ++iter
) {
192 auto const& key
= iter
.secondRef();
196 if (!key
.isString() ||
197 key
.asCStrRef().get()
198 ->isNumericWithVal(ignoredInt
, ignoredDbl
, false) != KindOfNull
) {
199 // Numeric strings will complicate matters because the loose comparisons
200 // done with array keys are not quite the same as loose comparisons done
201 // by in_array. For example: in_array('0', array('0000')) is true, but
202 // doing array('0000' => true)['0'] will say "undefined index". This seems
203 // unlikely to affect real-world usage.
207 flipped
.set(key
.asCStrRef(), init_null_variant
);
210 auto const needle
= topC(env
, 2);
211 auto const array
= flipped
.toArray();
215 cns(env
, ArrayData::GetScalarArray(array
.get())),
220 SSATmp
* opt_get_class(HTS
& env
, uint32_t numArgs
) {
221 auto const curCls
= curClass(env
);
222 auto const curName
= [&] {
223 return curCls
!= nullptr ? cns(env
, curCls
->name()) : nullptr;
225 if (numArgs
== 0) return curName();
226 if (numArgs
!= 1) return nullptr;
228 auto const val
= topC(env
, 0);
229 auto const ty
= val
->type();
230 if (ty
<= Type::Null
) return curName();
231 if (ty
<= Type::Obj
) {
232 auto const cls
= gen(env
, LdObjClass
, val
);
233 return gen(env
, LdClsName
, cls
);
239 SSATmp
* opt_get_called_class(HTS
& env
, uint32_t numArgs
) {
240 if (numArgs
!= 0) return nullptr;
241 if (!curClass(env
)) return nullptr;
242 auto const ctx
= ldCtx(env
);
243 auto const cls
= gen(env
, LdClsCtx
, ctx
);
244 return gen(env
, LdClsName
, cls
);
247 SSATmp
* opt_sqrt(HTS
& env
, uint32_t numArgs
) {
248 if (numArgs
!= 1) return nullptr;
250 auto const val
= topC(env
);
251 auto const ty
= val
->type();
252 if (ty
<= Type::Dbl
) return gen(env
, Sqrt
, val
);
253 if (ty
<= Type::Int
) {
254 auto const conv
= gen(env
, ConvIntToDbl
, val
);
255 return gen(env
, Sqrt
, conv
);
260 SSATmp
* opt_ceil(HTS
& env
, uint32_t numArgs
) {
261 if (numArgs
!= 1) return nullptr;
262 if (!folly::CpuId().sse41()) return nullptr;
263 auto const val
= topC(env
);
264 if (!type_converts_to_number(val
->type())) return nullptr;
265 auto const dbl
= gen(env
, ConvCellToDbl
, val
);
266 return gen(env
, Ceil
, dbl
);
269 SSATmp
* opt_floor(HTS
& env
, uint32_t numArgs
) {
270 if (numArgs
!= 1) return nullptr;
271 if (!folly::CpuId().sse41()) return nullptr;
272 auto const val
= topC(env
);
273 if (!type_converts_to_number(val
->type())) return nullptr;
274 auto const dbl
= gen(env
, ConvCellToDbl
, val
);
275 return gen(env
, Floor
, dbl
);
278 SSATmp
* opt_abs(HTS
& env
, uint32_t numArgs
) {
279 if (numArgs
!= 1) return nullptr;
281 auto const value
= topC(env
);
282 if (value
->type() <= Type::Int
) {
283 // compute integer absolute value ((src>>63) ^ src) - (src>>63)
284 auto const t1
= gen(env
, Shr
, value
, cns(env
, 63));
285 auto const t2
= gen(env
, XorInt
, t1
, value
);
286 return gen(env
, SubInt
, t2
, t1
);
289 if (value
->type() <= Type::Dbl
) return gen(env
, AbsDbl
, value
);
290 if (value
->type() <= Type::Arr
) return cns(env
, false);
295 //////////////////////////////////////////////////////////////////////
297 bool optimizedFCallBuiltin(HTS
& env
,
300 uint32_t numNonDefault
) {
301 auto const result
= [&]() -> SSATmp
* {
304 if (func->name()->isame(s_##x.get())) return opt_##x(env, numArgs);
323 if (result
== nullptr) return false;
325 // Decref and free args
326 for (int i
= 0; i
< numArgs
; i
++) {
327 auto const arg
= popR(env
);
328 if (i
>= numArgs
- numNonDefault
) {
329 gen(env
, DecRef
, arg
);
337 //////////////////////////////////////////////////////////////////////
340 * Return the type that a parameter to a builtin function is supposed to be
341 * coerced to. What this means depends on how the builtin is dealing with
342 * parameter coersion: new-style HNI builtins try to do a tvCoerceParamTo*,
343 * while older ones use tvCastTo* semantics.
345 * If the builtin parameter has no type hints to cause coercion, this function
346 * returns Type::Bottom.
348 Type
param_coerce_type(const Func
* callee
, uint32_t paramIdx
) {
349 auto const& pi
= callee
->params()[paramIdx
];
350 auto const& tc
= pi
.typeConstraint
;
351 if (tc
.isNullable() && !callee
->byRef(paramIdx
)) {
352 auto const dt
= tc
.underlyingDataType();
353 if (!dt
) return Type::Bottom
;
354 return Type::Null
| Type(*dt
);
356 return pi
.builtinType
? Type(*pi
.builtinType
) : Type::Bottom
;
359 //////////////////////////////////////////////////////////////////////
362 explicit ParamPrep(size_t count
) : info(count
) {}
365 SSATmp
* value
{nullptr};
366 bool throughStack
{false};
367 bool needsConversion
{false};
370 const Info
& operator[](size_t idx
) const { return info
[idx
]; }
371 Info
& operator[](size_t idx
) { return info
[idx
]; }
372 size_t size() const { return info
.size(); }
374 SSATmp
* thiz
{nullptr}; // may be null if call is not a method
375 jit::vector
<Info
> info
;
376 uint32_t numThroughStack
{0};
380 * Collect parameters for a call to a builtin. Also determine which ones will
381 * need to be passed through the eval stack, and which ones will need
384 template<class LoadParam
>
385 ParamPrep
prepare_params(HTS
& env
,
389 uint32_t numNonDefault
,
390 LoadParam loadParam
) {
391 auto ret
= ParamPrep(numArgs
);
394 // Fill in in reverse order, since they may come from popC's (depending on
395 // what loadParam wants to do).
396 for (auto offset
= uint32_t{numArgs
}; offset
-- > 0;) {
397 ret
[offset
].value
= loadParam(offset
);
399 auto const ty
= param_coerce_type(callee
, offset
);
401 // We do actually mean exact type equality here. We're only capable of
402 // passing the following primitives through registers; everything else goes
404 if (ty
== Type::Bool
|| ty
== Type::Int
|| ty
== Type::Dbl
) {
405 ret
[offset
].throughStack
= false;
406 ret
[offset
].needsConversion
= offset
< numNonDefault
;
410 // If ty > Type::Bottom, it had some kind of type hint.
411 ++ret
.numThroughStack
;
412 ret
[offset
].throughStack
= true;
413 ret
[offset
].needsConversion
= offset
< numNonDefault
&& ty
> Type::Bottom
;
419 //////////////////////////////////////////////////////////////////////
422 * CatchMaker makes catch blocks for calling builtins. There's a fair bit of
423 * complexity here right now, for these reasons:
425 * o Sometimes we're 'logically' inlining a php-level call to a function
426 * that contains a NativeImpl opcode.
428 * But we implement this by generating all the relevant NativeImpl code
429 * after the InlineReturn for the callee, to make it easier for DCE to
430 * eliminate the code that constructs the callee's activation record.
431 * This means the unwinder is going to see our PC as equal to the FCallD
432 * for the call to the function, which will be inside the FPI region for
433 * the call, so it'll try to pop an ActRec, so we'll need to reconstruct
434 * one for it during unwinding.
436 * o HNI-style param coerce modes can force the entire function to return
437 * false or null if the coersions fail. This is implemented via a
438 * TVCoercionException, which is not a user-visible exception. So our
439 * catch blocks are sometimes handling a PHP exception, and sometimes a
442 * o Both of these things may be relevant to the same catch block.
444 * Also, note that the CatchMaker keeps a pointer to the builtin call's
445 * ParamPrep, which will have its values mutated by realize_params as it's
446 * making coersions, so that we can see what's changed so far (and what to
447 * clean up on the offramps). Some values that were refcounted may become
448 * non-refcounted after conversions, and we can't DecRef things twice.
451 enum class Kind
{ NotInlining
, InliningNonCtor
, InliningCtor
};
453 explicit CatchMaker(HTS
& env
,
456 const ParamPrep
* params
)
462 assert(!m_params
.thiz
|| inlining());
465 CatchMaker(const CatchMaker
&) = delete;
466 CatchMaker(CatchMaker
&&) = default;
468 bool inlining() const {
470 case Kind::NotInlining
: return false;
471 case Kind::InliningNonCtor
: return true;
472 case Kind::InliningCtor
: return true;
477 Block
* makeUnusualCatch() const {
478 auto const exit
= env
.unit
.defBlock(Block::Hint::Unlikely
);
479 BlockPusher
bp(*env
.irb
, makeMarker(env
, bcOff(env
)), exit
);
480 gen(env
, BeginCatch
);
483 gen(env
, EndCatch
, StackOffset
{ offsetFromSP(env
, 0) }, fp(env
), sp(env
));
487 Block
* makeParamCoerceCatch() const {
488 auto const exit
= env
.unit
.defBlock(Block::Hint::Unlikely
);
490 BlockPusher
bp(*env
.irb
, makeMarker(env
, bcOff(env
)), exit
);
491 gen(env
, BeginCatch
);
493 // Determine whether we're dealing with a TVCoercionException or a php
494 // exception. If it's a php-exception, we'll go to the taken block.
498 gen(env
, UnwindCheckSideExit
, taken
, fp(env
), sp(env
));
501 hint(env
, Block::Hint::Unused
);
504 gen(env
, EndCatch
, StackOffset
{ offsetFromSP(env
, 0) }, fp(env
),
509 // From here on we're on the side-exit path, due to a failure to coerce.
510 // We need to push the unwinder value and then side-exit to the next
512 hint(env
, Block::Hint::Unlikely
);
514 if (m_params
.thiz
) gen(env
, DecRef
, m_params
.thiz
);
516 auto const val
= gen(env
, LdUnwinderValue
, Type::Cell
);
517 gen(env
, DeleteUnwinderException
);
519 gen(env
, Jmp
, makeExit(env
, nextBcOff(env
)));
525 void prepareForCatch() const {
529 m_params
.thiz
? m_params
.thiz
: cns(env
, Type::Nullptr
),
530 ActRec::encodeNumArgs(m_params
.size(),
531 false /* localsDecRefd */,
533 m_kind
== Kind::InliningCtor
),
536 for (auto i
= uint32_t{0}; i
< m_params
.size(); ++i
) {
537 // TODO(#4313939): it's not actually necessary to push these
539 push(env
, cns(env
, Type::InitNull
));
542 * We're potentially spilling to a different depth than the unwinder
543 * would've expected, so we need an eager sync. Even if we aren't inlining
544 * this can happen, because before doing the CallBuiltin we set the marker
545 * stack offset to only include the passed-through-stack args.
547 * So before we leave, update the marker to placate EndCatch assertions,
548 * which is trying to detect failure to do this properly.
551 gen(env
, AdjustSP
, StackOffset
{ offsetFromSP(env
, 0) }, sp(env
));
552 gen(env
, EagerSyncVMRegs
, fp(env
), sp(env
));
553 updateMarker(env
); // Mark the EndCatch safe, since we're eager syncing.
557 * For consistency with the interpreter, we need to decref things in a
558 * different order depending on whether we are unwinding, or planning to side
561 * In either case, parameters that are not being passed through the stack
562 * still may need to be decref'd, because they may have been a reference
563 * counted type that was going to be converted to a non-reference counted
564 * type that we'd pass in a register. As we do the coersions, params.value
565 * gets updated so whenever we call these catch block creation functions it
566 * will only decref things that weren't yet converted.
569 void decRefForUnwind() const {
570 for (auto i
= uint32_t{0}; i
< m_params
.size(); ++i
) {
571 if (m_params
[i
].throughStack
) {
572 popDecRef(env
, Type::Gen
);
574 gen(env
, DecRef
, m_params
[i
].value
);
579 // Same work as above, but opposite order.
580 void decRefForSideExit() const {
582 auto stackIdx
= m_params
.numThroughStack
;
584 // Make sure we have loads for all of the stack elements. We need to do
585 // this in forward order before we decref in backward order because
586 // extendStack will end up with values that are of type StkElem
588 for (auto i
= 0; i
< stackIdx
; ++i
) {
589 top(env
, Type::Gen
, i
, DataTypeGeneric
);
592 for (auto i
= m_params
.size(); i
-- > 0;) {
593 if (m_params
[i
].throughStack
) {
595 auto const val
= top(env
, Type::Gen
, stackIdx
, DataTypeGeneric
);
596 gen(env
, DecRef
, val
);
598 gen(env
, DecRef
, m_params
[i
].value
);
601 discard(env
, m_params
.numThroughStack
);
607 const Func
* m_callee
;
608 const ParamPrep
& m_params
;
611 //////////////////////////////////////////////////////////////////////
613 SSATmp
* coerce_value(HTS
& env
,
617 const CatchMaker
& maker
) {
618 auto const targetTy
= param_coerce_type(callee
, paramIdx
);
619 if (!callee
->isParamCoerceMode()) {
620 if (targetTy
<= Type::Int
) {
621 return gen(env
, ConvCellToInt
, maker
.makeUnusualCatch(), oldVal
);
623 if (targetTy
<= Type::Dbl
) {
624 return gen(env
, ConvCellToDbl
, maker
.makeUnusualCatch(), oldVal
);
626 always_assert(targetTy
<= Type::Bool
);
627 return gen(env
, ConvCellToBool
, oldVal
);
630 assert(!(Type::Null
< targetTy
));
632 if (targetTy
<= Type::Int
) {
635 CoerceData(callee
, paramIdx
+ 1),
636 maker
.makeParamCoerceCatch(),
639 if (targetTy
<= Type::Dbl
) {
642 CoerceData(callee
, paramIdx
+ 1),
643 maker
.makeParamCoerceCatch(),
646 always_assert(targetTy
<= Type::Bool
);
649 CoerceData(callee
, paramIdx
+ 1),
650 maker
.makeParamCoerceCatch(),
654 void coerce_stack(HTS
& env
,
658 const CatchMaker
& maker
) {
659 auto const mi
= callee
->methInfo();
660 auto const targetTy
= [&]() -> Type
{
661 if (callee
->params()[paramIdx
].builtinType
== KindOfObject
&& mi
&&
662 mi
->parameters
[paramIdx
]->valueLen
> 0) {
663 return Type::NullableObj
;
665 return param_coerce_type(callee
, paramIdx
);
668 if (callee
->isParamCoerceMode()) {
672 CoerceStkData
{ offsetFromSP(env
, offset
), callee
, paramIdx
+ 1 },
673 maker
.makeParamCoerceCatch(),
679 StackOffset
{ offsetFromSP(env
, offset
) },
680 maker
.makeUnusualCatch(),
685 * We can throw after writing to the stack above; inform IRBuilder about it.
686 * This is basically just for assertions right now.
688 env
.irb
->exceptionStackBoundary();
692 * Prepare the actual arguments to the CallBuiltin instruction, by converting a
693 * ParamPrep into a vector of SSATmps to pass to CallBuiltin. If any of the
694 * parameters needed type conversions, we need to do that here too.
696 jit::vector
<SSATmp
*> realize_params(HTS
& env
,
699 const CatchMaker
& maker
) {
700 auto const cbNumArgs
= 2 + params
.size() + (params
.thiz
? 1 : 0);
701 auto ret
= jit::vector
<SSATmp
*>(cbNumArgs
);
702 auto argIdx
= uint32_t{0};
703 ret
[argIdx
++] = fp(env
);
704 ret
[argIdx
++] = sp(env
);
705 if (params
.thiz
) ret
[argIdx
++] = params
.thiz
;
707 auto stackIdx
= uint32_t{0};
708 for (auto paramIdx
= uint32_t{0}; paramIdx
< params
.size(); ++paramIdx
) {
709 if (!params
[paramIdx
].throughStack
) {
710 if (!params
[paramIdx
].needsConversion
) {
711 ret
[argIdx
++] = params
[paramIdx
].value
;
714 auto const oldVal
= params
[paramIdx
].value
;
715 // Heads up on non-local state here: we have to update the values inside
716 // ParamPrep so that the CatchMaker functions know about new potentially
717 // refcounted types to decref, or values that were already decref'd and
718 // replaced with things like ints.
719 params
[paramIdx
].value
= coerce_value(
726 gen(env
, DecRef
, oldVal
);
727 ret
[argIdx
++] = params
[paramIdx
].value
;
731 auto const offset
= params
.numThroughStack
- stackIdx
- 1;
732 if (params
[paramIdx
].needsConversion
) {
733 coerce_stack(env
, callee
, paramIdx
, offset
, maker
);
735 ret
[argIdx
++] = ldStkAddr(env
, offset
);
739 assert(stackIdx
== params
.numThroughStack
);
740 assert(argIdx
== cbNumArgs
);
745 //////////////////////////////////////////////////////////////////////
747 void builtinCall(HTS
& env
,
750 const CatchMaker
& catchMaker
) {
752 * Everything that needs to be on the stack gets spilled now.
754 * If we're not inlining, the reason we do this even when numThroughStack is
755 * zero is to make it so that in either case the stack depth when we enter
756 * our catch blocks is always the same as the numThroughStack value, in all
757 * situations. If we didn't do this, then when we aren't inlining, and
758 * numThroughStack is zero, we'd have the stack depth be the total num params
759 * (the depth before the FCallBuiltin), which would add more cases to handle
760 * in the catch blocks.
762 if (params
.numThroughStack
!= 0 || !catchMaker
.inlining()) {
763 for (auto i
= uint32_t{0}; i
< params
.size(); ++i
) {
764 if (params
[i
].throughStack
) {
765 push(env
, params
[i
].value
);
768 // We're going to do ldStkAddrs on these, so the stack must be
772 * This marker update is to make sure rbx points to the bottom of our stack
773 * if we enter a catch trace. It's also necessary because we might run
774 * destructors as part of parameter coersions, which we don't want to
775 * clobber our spilled stack.
780 // If we're not inlining, we've spilled the stack and are about to do things
781 // that can throw. If we are inlining, we've done various DefInlineFP and
782 // ReDefSP type stuff and possibly also spilled the stack.
783 env
.irb
->exceptionStackBoundary();
785 auto const retType
= [&]() -> Type
{
786 auto const retDT
= callee
->returnType();
787 auto const ret
= retDT
? Type(*retDT
) : Type::Cell
;
788 if (callee
->attrs() & ClassInfo::IsReference
) {
789 return ret
.box() & Type::BoxedInitCell
;
794 // Make the actual call.
795 auto realized
= realize_params(env
, callee
, params
, catchMaker
);
796 SSATmp
** const decayedPtr
= &realized
[0];
797 auto const ret
= gen(
802 offsetFromSP(env
, 0),
804 builtinFuncDestroysLocals(callee
)
806 catchMaker
.makeUnusualCatch(),
807 std::make_pair(realized
.size(), decayedPtr
)
810 // Pop the stack params and push the return value.
811 if (params
.thiz
) gen(env
, DecRef
, params
.thiz
);
812 for (auto i
= uint32_t{0}; i
< params
.numThroughStack
; ++i
) {
813 popDecRef(env
, Type::Gen
);
815 // We don't need to decref the non-state param values, because they are only
816 // non-reference counted types. (At this point we've gotten through all our
817 // coersions, so even if they started refcounted we've already decref'd them
823 * When we're inlining a NativeImpl opcode, we know this is the only opcode in
824 * the callee method body aside from AssertRATs (bytecode invariant). So in
825 * order to make sure we can eliminate the SpillFrame, we do the CallBuiltin
826 * instruction after we've left the inlined frame.
828 * We may need to pass some arguments to the builtin through the stack (e.g. if
829 * it takes const Variant&'s)---these are spilled to the stack after leaving
832 * To make this work, we need to do some weird things in the catch trace. ;)
834 void nativeImplInlined(HTS
& env
) {
835 auto const callee
= curFunc(env
);
836 assert(callee
->nativeFuncPtr());
838 auto const wasInliningConstructor
=
839 fp(env
)->inst()->extra
<DefInlineFP
>()->fromFPushCtor
;
841 bool const instanceMethod
= callee
->isMethod() &&
842 !(callee
->attrs() & AttrStatic
);
845 auto const numArgs
= callee
->numParams();
846 auto const paramThis
= instanceMethod
? ldThis(env
) : nullptr;
848 auto params
= prepare_params(
853 numArgs
, // numNonDefault is equal to numArgs here.
855 auto ret
= ldLoc(env
, i
, nullptr, DataTypeSpecific
);
856 // These IncRefs must be 'inside' the callee: it may own the only
857 // reference to the parameters. Normally they will cancel with the
858 // DecRefs that we'll do in endInlinedCommon.
859 gen(env
, IncRef
, ret
);
864 // For the same reason that we have to IncRef the locals above, we
865 // need to grab one on the $this.
866 if (paramThis
) gen(env
, IncRef
, paramThis
);
868 endInlinedCommon(env
);
870 auto const catcher
= CatchMaker
{
872 wasInliningConstructor
? CatchMaker::Kind::InliningCtor
873 : CatchMaker::Kind::InliningNonCtor
,
878 builtinCall(env
, callee
, params
, catcher
);
881 //////////////////////////////////////////////////////////////////////
885 //////////////////////////////////////////////////////////////////////
887 SSATmp
* optimizedCallIsObject(HTS
& env
, SSATmp
* src
) {
888 if (src
->isA(Type::Obj
) && src
->type().clsSpec()) {
889 auto const cls
= src
->type().clsSpec().cls();
890 if (!env
.irb
->constrainValue(src
, TypeConstraint(cls
).setWeak())) {
891 // If we know the class without having to specialize a guard
892 // any further, use it.
893 return cns(env
, cls
!= SystemLib::s___PHP_Incomplete_ClassClass
);
897 if (!src
->type().maybe(Type::Obj
)) {
898 return cns(env
, false);
901 auto checkClass
= [&] (SSATmp
* obj
) {
902 auto cls
= gen(env
, LdObjClass
, obj
);
903 auto testCls
= SystemLib::s___PHP_Incomplete_ClassClass
;
904 return gen(env
, ClsNeq
, ClsNeqData
{ testCls
}, cls
);
909 0, // references produced
911 auto isObj
= gen(env
, IsType
, Type::Obj
, src
);
912 gen(env
, JmpZero
, taken
, isObj
);
914 [&] { // Next: src is an object
915 auto obj
= gen(env
, AssertType
, Type::Obj
, src
);
916 return checkClass(obj
);
918 [&] { // Taken: src is not an object
919 return cns(env
, false);
924 //////////////////////////////////////////////////////////////////////
926 void emitFCallBuiltin(HTS
& env
,
928 int32_t numNonDefault
,
929 const StringData
* funcName
) {
930 auto const callee
= Unit::lookupFunc(funcName
);
932 if (optimizedFCallBuiltin(env
, callee
, numArgs
, numNonDefault
)) return;
934 auto params
= prepare_params(
937 nullptr, // no $this; FCallBuiltin never happens for methods
940 [&] (uint32_t i
) { return pop(env
, Type::Gen
, DataTypeSpecific
); }
943 auto const catcher
= CatchMaker
{
945 CatchMaker::Kind::NotInlining
,
950 builtinCall(env
, callee
, params
, catcher
);
953 void emitNativeImpl(HTS
& env
) {
954 if (isInlining(env
)) return nativeImplInlined(env
);
956 gen(env
, NativeImpl
, fp(env
), sp(env
));
957 auto const stack
= gen(env
, RetAdjustStk
, fp(env
));
958 auto const retAddr
= gen(env
, LdRetAddr
, fp(env
));
959 auto const frame
= gen(env
, FreeActRec
, fp(env
));
960 gen(env
, RetCtrl
, RetCtrlData(0, false), stack
, frame
, retAddr
);
963 //////////////////////////////////////////////////////////////////////
965 // Helper for doing array-style Idx translations, even if we're dealing with a
966 // collection. The stack will still contain the collection in that case, and
967 // loaded_collection_array will be non-nullptr. If we're really doing
968 // ArrayIdx, it's nullptr.
969 void implArrayIdx(HTS
& env
, SSATmp
* loaded_collection_array
) {
970 // These types are just used to decide what to do; once we know what we're
971 // actually doing we constrain the values with the popC()s later on in this
973 auto const keyType
= topC(env
, 1, DataTypeGeneric
)->type();
975 if (keyType
<= Type::Null
) {
976 auto const def
= popC(env
, DataTypeGeneric
);
977 auto const key
= popC(env
);
978 auto const stack_base
= popC(env
);
980 // if the key is null it will not be found so just return the default
982 gen(env
, DecRef
, stack_base
);
983 gen(env
, DecRef
, key
);
986 if (!(keyType
<= Type::Int
|| keyType
<= Type::Str
)) {
987 interpOne(env
, Type::Cell
, 3);
991 auto const def
= popC(env
, DataTypeGeneric
); // a helper will decref it but
992 // the translated code doesn't
993 // care about the type
994 auto const key
= popC(env
);
995 auto const stack_base
= popC(env
);
996 auto const use_base
= loaded_collection_array
997 ? loaded_collection_array
999 auto const value
= gen(env
, ArrayIdx
, use_base
, key
, def
);
1001 gen(env
, DecRef
, stack_base
);
1002 gen(env
, DecRef
, key
);
1003 gen(env
, DecRef
, def
);
1006 void implGenericIdx(HTS
& env
) {
1007 auto const def
= popC(env
, DataTypeSpecific
);
1008 auto const key
= popC(env
, DataTypeSpecific
);
1009 auto const arr
= popC(env
, DataTypeSpecific
);
1010 push(env
, gen(env
, GenericIdx
, arr
, key
, def
));
1011 gen(env
, DecRef
, arr
);
1012 gen(env
, DecRef
, key
);
1013 gen(env
, DecRef
, def
);
1016 void emitArrayIdx(HTS
& env
) {
1017 auto const arrType
= topC(env
, 2, DataTypeGeneric
)->type();
1018 if (!(arrType
<= Type::Arr
)) {
1020 interpOne(env
, Type::Cell
, 3);
1024 implArrayIdx(env
, nullptr);
1027 void emitIdx(HTS
& env
) {
1028 auto const key
= topC(env
, 1, DataTypeGeneric
);
1029 auto const base
= topC(env
, 2, DataTypeGeneric
);
1030 auto const keyType
= key
->type();
1031 auto const baseType
= base
->type();
1033 auto const simple_key
=
1034 keyType
<= Type::Int
|| keyType
<= Type::Str
;
1037 if (baseType
<= Type::Arr
) {
1042 if (baseType
< Type::Obj
&& baseType
.clsSpec()) {
1043 auto const cls
= baseType
.clsSpec().cls();
1045 // We must require either constant keys or known integer keys for Map,
1046 // because integer-like strings behave differently.
1047 auto const okMap
= cls
== c_Map::classof() &&
1048 (keyType
.isConst() || keyType
<= Type::Int
);
1049 // Similarly, Vector is only usable with int keys, so we can only do this
1050 // for Vector if it's an Int.
1051 auto const okVector
= cls
== c_Vector::classof() &&
1052 keyType
<= Type::Int
;
1054 auto const optimizableCollection
= okMap
|| okVector
;
1055 if (optimizableCollection
) {
1056 env
.irb
->constrainValue(base
, TypeConstraint(cls
));
1057 env
.irb
->constrainValue(key
, DataTypeSpecific
);
1058 auto const arr
= gen(env
, LdColArray
, base
);
1059 implArrayIdx(env
, arr
);
1065 implGenericIdx(env
);
1068 void emitAKExists(HTS
& env
) {
1069 auto const arr
= popC(env
);
1070 auto const key
= popC(env
);
1072 if (!arr
->isA(Type::Arr
) && !arr
->isA(Type::Obj
)) {
1073 PUNT(AKExists_badArray
);
1075 if (!key
->isA(Type::Str
) && !key
->isA(Type::Int
) && !key
->isA(Type::Null
)) {
1076 PUNT(AKExists_badKey
);
1079 push(env
, gen(env
, AKExists
, arr
, key
));
1080 gen(env
, DecRef
, arr
);
1081 gen(env
, DecRef
, key
);
1084 void emitGetMemoKey(HTS
& env
) {
1085 auto const inTy
= topC(env
)->type();
1086 if (inTy
<= Type::Int
) {
1087 // An int is already a valid key. No-op.
1090 if (inTy
<= Type::Null
) {
1091 auto input
= popC(env
);
1092 push(env
, cns(env
, s_empty
.get()));
1093 gen(env
, DecRef
, input
);
1097 auto const obj
= popC(env
);
1098 auto const key
= gen(env
, GetMemoKey
, obj
);
1100 gen(env
, DecRef
, obj
);
1103 void emitStrlen(HTS
& env
) {
1104 auto const inType
= topC(env
)->type();
1106 if (inType
<= Type::Str
) {
1107 auto const input
= popC(env
);
1108 if (input
->isConst()) {
1109 // static string; fold its strlen operation
1110 push(env
, cns(env
, input
->strVal()->size()));
1114 push(env
, gen(env
, LdStrLen
, input
));
1115 gen(env
, DecRef
, input
);
1119 if (inType
<= Type::Null
) {
1121 push(env
, cns(env
, 0));
1125 if (inType
<= Type::Bool
) {
1126 // strlen(true) == 1, strlen(false) == 0.
1127 push(env
, gen(env
, ConvBoolToInt
, popC(env
)));
1131 interpOne(env
, Type::Int
| Type::InitNull
, 1);
1134 void emitSilence(HTS
& env
, Id localId
, SilenceOp subop
) {
1135 // We can't generate direct StLoc and LdLocs in pseudomains (violates an IR
1137 if (curFunc(env
)->isPseudoMain()) PUNT(PseudoMain
-Silence
);
1140 case SilenceOp::Start
:
1141 // We assume that whatever is in the local is dead and doesn't need to be
1142 // refcounted before being overwritten.
1143 gen(env
, AssertLoc
, Type::Uncounted
, LocalId(localId
), fp(env
));
1144 gen(env
, StLoc
, LocalId(localId
), fp(env
), gen(env
, ZeroErrorLevel
));
1146 case SilenceOp::End
:
1148 gen(env
, AssertLoc
, Type::Int
, LocalId(localId
), fp(env
));
1149 auto const level
= ldLoc(env
, localId
, makeExit(env
), DataTypeGeneric
);
1150 gen(env
, RestoreErrorLevel
, level
);
1156 //////////////////////////////////////////////////////////////////////