Refine to bottom
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-builtin.cpp
blob415ec1c631d1bc513929db1f3334174b09cb575f
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
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 {
33 namespace {
35 //////////////////////////////////////////////////////////////////////
37 const StaticString
38 s_is_a("is_a"),
39 s_count("count"),
40 s_ini_get("ini_get"),
41 s_in_array("in_array"),
42 s_get_class("get_class"),
43 s_get_called_class("get_called_class"),
44 s_sqrt("sqrt"),
45 s_ceil("ceil"),
46 s_floor("floor"),
47 s_abs("abs"),
48 s_ord("ord"),
49 s_empty("");
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(
56 Type::Dbl,
57 Type::Int,
58 Type::Null,
59 Type::Obj,
60 Type::Res,
61 Type::Str,
62 Type::Bool
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()) {
78 return nullptr;
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(
93 env,
94 topC(env, 2),
95 top(env, Type::Str, 1)->strVal()
100 // The LHS is a strict superset of Obj; bail.
101 return nullptr;
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));
125 return nullptr;
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
133 // at JIT time.
134 auto const argType = topType(env, 0);
135 if (!(argType <= Type::StaticStr) || !argType.isConst()) {
136 return nullptr;
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)) {
144 return nullptr;
146 if (mode & ~IniSetting::PHP_INI_SYSTEM) {
147 return nullptr;
149 if (mode == IniSetting::PHP_INI_ALL) { /* PHP_INI_ALL has a weird encoding */
150 return nullptr;
153 Variant value;
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()));
161 return nullptr;
165 * Transforms in_array with a static haystack argument into an AKExists with the
166 * haystack flipped.
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)) {
175 return nullptr;
178 auto const haystackType = topType(env, 1);
179 if (!(haystackType <= Type::StaticArr) || !haystackType.isConst()) {
180 // Haystack isn't statically known
181 return nullptr;
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();
193 int64_t ignoredInt;
194 double ignoredDbl;
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.
204 return nullptr;
207 flipped.set(key.asCStrRef(), init_null_variant);
210 auto const needle = topC(env, 2);
211 auto const array = flipped.toArray();
212 return gen(
213 env,
214 AKExists,
215 cns(env, ArrayData::GetScalarArray(array.get())),
216 needle
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);
236 return nullptr;
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);
257 return nullptr;
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);
292 return nullptr;
295 //////////////////////////////////////////////////////////////////////
297 bool optimizedFCallBuiltin(HTS& env,
298 const Func* func,
299 uint32_t numArgs,
300 uint32_t numNonDefault) {
301 auto const result = [&]() -> SSATmp* {
303 #define X(x) \
304 if (func->name()->isame(s_##x.get())) return opt_##x(env, numArgs);
306 X(get_called_class);
307 X(get_class);
308 X(in_array);
309 X(ini_get);
310 X(count);
311 X(is_a);
312 X(sqrt);
313 X(ceil);
314 X(floor);
315 X(abs);
316 X(ord);
318 #undef X
320 return nullptr;
321 }();
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);
333 push(env, result);
334 return true;
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 //////////////////////////////////////////////////////////////////////
361 struct ParamPrep {
362 explicit ParamPrep(size_t count) : info(count) {}
364 struct Info {
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
382 * conversions.
384 template<class LoadParam>
385 ParamPrep prepare_params(HTS& env,
386 const Func* callee,
387 SSATmp* thiz,
388 uint32_t numArgs,
389 uint32_t numNonDefault,
390 LoadParam loadParam) {
391 auto ret = ParamPrep(numArgs);
392 ret.thiz = thiz;
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
403 // on the stack.
404 if (ty == Type::Bool || ty == Type::Int || ty == Type::Dbl) {
405 ret[offset].throughStack = false;
406 ret[offset].needsConversion = offset < numNonDefault;
407 continue;
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;
416 return ret;
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
440 * failure to coerce.
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.
450 struct CatchMaker {
451 enum class Kind { NotInlining, InliningNonCtor, InliningCtor };
453 explicit CatchMaker(HTS& env,
454 Kind kind,
455 const Func* callee,
456 const ParamPrep* params)
457 : env(env)
458 , m_kind(kind)
459 , m_callee(callee)
460 , m_params(*params)
462 assert(!m_params.thiz || inlining());
465 CatchMaker(const CatchMaker&) = delete;
466 CatchMaker(CatchMaker&&) = default;
468 bool inlining() const {
469 switch (m_kind) {
470 case Kind::NotInlining: return false;
471 case Kind::InliningNonCtor: return true;
472 case Kind::InliningCtor: return true;
474 not_reached();
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);
481 decRefForUnwind();
482 prepareForCatch();
483 gen(env, EndCatch, StackOffset { offsetFromSP(env, 0) }, fp(env), sp(env));
484 return exit;
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.
495 ifThen(
496 env,
497 [&] (Block* taken) {
498 gen(env, UnwindCheckSideExit, taken, fp(env), sp(env));
500 [&] {
501 hint(env, Block::Hint::Unused);
502 decRefForUnwind();
503 prepareForCatch();
504 gen(env, EndCatch, StackOffset { offsetFromSP(env, 0) }, fp(env),
505 sp(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
511 // instruction.
512 hint(env, Block::Hint::Unlikely);
513 decRefForSideExit();
514 if (m_params.thiz) gen(env, DecRef, m_params.thiz);
516 auto const val = gen(env, LdUnwinderValue, Type::Cell);
517 gen(env, DeleteUnwinderException);
518 push(env, val);
519 gen(env, Jmp, makeExit(env, nextBcOff(env)));
521 return exit;
524 private:
525 void prepareForCatch() const {
526 if (inlining()) {
527 fpushActRec(env,
528 cns(env, m_callee),
529 m_params.thiz ? m_params.thiz : cns(env, Type::Nullptr),
530 ActRec::encodeNumArgs(m_params.size(),
531 false /* localsDecRefd */,
532 false /* resumed */,
533 m_kind == Kind::InliningCtor),
534 nullptr);
536 for (auto i = uint32_t{0}; i < m_params.size(); ++i) {
537 // TODO(#4313939): it's not actually necessary to push these
538 // nulls.
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.
550 spillStack(env);
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
559 * exit.
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);
573 } else {
574 gen(env, DecRef, m_params[i].value);
579 // Same work as above, but opposite order.
580 void decRefForSideExit() const {
581 spillStack(env);
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
587 // TODO(#6156498).
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) {
594 --stackIdx;
595 auto const val = top(env, Type::Gen, stackIdx, DataTypeGeneric);
596 gen(env, DecRef, val);
597 } else {
598 gen(env, DecRef, m_params[i].value);
601 discard(env, m_params.numThroughStack);
604 private:
605 HTS& env;
606 Kind const m_kind;
607 const Func* m_callee;
608 const ParamPrep& m_params;
611 //////////////////////////////////////////////////////////////////////
613 SSATmp* coerce_value(HTS& env,
614 const Func* callee,
615 SSATmp* oldVal,
616 uint32_t paramIdx,
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) {
633 return gen(env,
634 CoerceCellToInt,
635 CoerceData(callee, paramIdx + 1),
636 maker.makeParamCoerceCatch(),
637 oldVal);
639 if (targetTy <= Type::Dbl) {
640 return gen(env,
641 CoerceCellToDbl,
642 CoerceData(callee, paramIdx + 1),
643 maker.makeParamCoerceCatch(),
644 oldVal);
646 always_assert(targetTy <= Type::Bool);
647 return gen(env,
648 CoerceCellToBool,
649 CoerceData(callee, paramIdx + 1),
650 maker.makeParamCoerceCatch(),
651 oldVal);
654 void coerce_stack(HTS& env,
655 const Func* callee,
656 uint32_t paramIdx,
657 uint32_t offset,
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);
666 }();
668 if (callee->isParamCoerceMode()) {
669 gen(env,
670 CoerceStk,
671 targetTy,
672 CoerceStkData { offsetFromSP(env, offset), callee, paramIdx + 1 },
673 maker.makeParamCoerceCatch(),
674 sp(env));
675 } else {
676 gen(env,
677 CastStk,
678 targetTy,
679 StackOffset { offsetFromSP(env, offset) },
680 maker.makeUnusualCatch(),
681 sp(env));
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,
697 const Func* callee,
698 ParamPrep& params,
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;
712 continue;
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(
720 env,
721 callee,
722 oldVal,
723 paramIdx,
724 maker
726 gen(env, DecRef, oldVal);
727 ret[argIdx++] = params[paramIdx].value;
728 continue;
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);
736 ++stackIdx;
739 assert(stackIdx == params.numThroughStack);
740 assert(argIdx == cbNumArgs);
742 return ret;
745 //////////////////////////////////////////////////////////////////////
747 void builtinCall(HTS& env,
748 const Func* callee,
749 ParamPrep& params,
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
769 // materialized:
770 spillStack(env);
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.
777 updateMarker(env);
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;
791 return ret;
792 }();
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(
798 env,
799 CallBuiltin,
800 retType,
801 CallBuiltinData {
802 offsetFromSP(env, 0),
803 callee,
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
818 // as appropriate.)
819 push(env, ret);
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
830 * the callee.
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(
849 env,
850 callee,
851 paramThis,
852 numArgs,
853 numArgs, // numNonDefault is equal to numArgs here.
854 [&] (uint32_t i) {
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);
860 return 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 {
871 env,
872 wasInliningConstructor ? CatchMaker::Kind::InliningCtor
873 : CatchMaker::Kind::InliningNonCtor,
874 callee,
875 &params
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);
907 return cond(
908 env,
909 0, // references produced
910 [&] (Block* taken) {
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,
927 int32_t numArgs,
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(
935 env,
936 callee,
937 nullptr, // no $this; FCallBuiltin never happens for methods
938 numArgs,
939 numNonDefault,
940 [&] (uint32_t i) { return pop(env, Type::Gen, DataTypeSpecific); }
943 auto const catcher = CatchMaker {
944 env,
945 CatchMaker::Kind::NotInlining,
946 callee,
947 &params
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
972 // function.
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
981 push(env, def);
982 gen(env, DecRef, stack_base);
983 gen(env, DecRef, key);
984 return;
986 if (!(keyType <= Type::Int || keyType <= Type::Str)) {
987 interpOne(env, Type::Cell, 3);
988 return;
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
998 : stack_base;
999 auto const value = gen(env, ArrayIdx, use_base, key, def);
1000 push(env, value);
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)) {
1019 // raise fatal
1020 interpOne(env, Type::Cell, 3);
1021 return;
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;
1036 if (simple_key) {
1037 if (baseType <= Type::Arr) {
1038 emitArrayIdx(env);
1039 return;
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);
1060 return;
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.
1088 return;
1090 if (inTy <= Type::Null) {
1091 auto input = popC(env);
1092 push(env, cns(env, s_empty.get()));
1093 gen(env, DecRef, input);
1094 return;
1097 auto const obj = popC(env);
1098 auto const key = gen(env, GetMemoKey, obj);
1099 push(env, key);
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()));
1111 return;
1114 push(env, gen(env, LdStrLen, input));
1115 gen(env, DecRef, input);
1116 return;
1119 if (inType <= Type::Null) {
1120 popC(env);
1121 push(env, cns(env, 0));
1122 return;
1125 if (inType <= Type::Bool) {
1126 // strlen(true) == 1, strlen(false) == 0.
1127 push(env, gen(env, ConvBoolToInt, popC(env)));
1128 return;
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
1136 // invariant).
1137 if (curFunc(env)->isPseudoMain()) PUNT(PseudoMain-Silence);
1139 switch (subop) {
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));
1145 break;
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);
1152 break;
1156 //////////////////////////////////////////////////////////////////////