Remove tvScratch, take 2
[hiphop-php.git] / hphp / runtime / vm / jit / translator-runtime.cpp
blob9fbdc3ad943f3e03d339a442f1bda2225e63bed7
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 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/translator-runtime.h"
19 #include "hphp/runtime/base/autoload-handler.h"
20 #include "hphp/runtime/base/collections.h"
21 #include "hphp/runtime/base/packed-array.h"
22 #include "hphp/runtime/base/stats.h"
23 #include "hphp/runtime/base/zend-functions.h"
24 #include "hphp/runtime/ext/ext_closure.h"
25 #include "hphp/runtime/ext/collections/ext_collections-idl.h"
26 #include "hphp/runtime/ext/hh/ext_hh.h"
27 #include "hphp/runtime/ext/std/ext_std_function.h"
28 #include "hphp/runtime/vm/jit/mc-generator-internal.h"
29 #include "hphp/runtime/vm/jit/mc-generator.h"
30 #include "hphp/runtime/vm/jit/target-profile.h"
31 #include "hphp/runtime/vm/jit/translator-inline.h"
32 #include "hphp/runtime/vm/jit/unwind-x64.h"
33 #include "hphp/runtime/vm/member-operations.h"
34 #include "hphp/runtime/vm/minstr-state.h"
35 #include "hphp/runtime/vm/type-constraint.h"
36 #include "hphp/runtime/vm/unit-util.h"
37 #include "hphp/runtime/vm/unwind.h"
39 namespace HPHP {
41 TRACE_SET_MOD(runtime);
43 //////////////////////////////////////////////////////////////////////
45 const StaticString s_staticPrefix("86static_");
47 // Defined here so it can be inlined below.
48 RefData* lookupStaticFromClosure(ObjectData* closure,
49 StringData* name,
50 bool& inited) {
51 assertx(closure->instanceof(c_Closure::classof()));
52 auto str = String::attach(
53 StringData::Make(s_staticPrefix.slice(), name->slice())
55 auto const cls = closure->getVMClass();
56 auto const slot = cls->lookupDeclProp(str.get());
57 assertx(slot != kInvalidSlot);
58 auto const val = static_cast<c_Closure*>(closure)->getStaticVar(slot);
60 if (val->m_type == KindOfUninit) {
61 inited = false;
62 val->m_type = KindOfNull;
63 tvBox(val);
64 return val->m_data.pref;
66 inited = true;
67 assertx(val->m_type == KindOfRef);
68 return val->m_data.pref;
71 namespace jit {
73 //////////////////////////////////////////////////////////////////////
75 ArrayData* addNewElemHelper(ArrayData* a, TypedValue value) {
76 ArrayData* r = a->append(tvAsCVarRef(&value), a->getCount() != 1);
77 if (UNLIKELY(r != a)) {
78 decRefArr(a);
80 tvRefcountedDecRef(value);
81 return r;
84 ArrayData* addElemIntKeyHelper(ArrayData* ad,
85 int64_t key,
86 TypedValue value) {
87 // this does not re-enter
88 // set will decRef any old value that may have been overwritten
89 // if appropriate
90 ArrayData* retval = ad->set(key, tvAsCVarRef(&value),
91 ad->hasMultipleRefs());
92 // TODO Task #1970153: It would be great if there were set()
93 // methods that didn't bump up the refcount so that we didn't
94 // have to decrement it here
95 tvRefcountedDecRef(&value);
96 return arrayRefShuffle<false>(ad, retval, nullptr);
99 ArrayData* addElemStringKeyHelper(ArrayData* ad,
100 StringData* key,
101 TypedValue value) {
102 // this does not re-enter
103 bool copy = ad->hasMultipleRefs();
104 // set will decRef any old value that may have been overwritten
105 // if appropriate
106 int64_t intkey;
107 ArrayData* retval = UNLIKELY(key->isStrictlyInteger(intkey)) ?
108 ad->set(intkey, tvAsCVarRef(&value), copy) :
109 ad->set(key, tvAsCVarRef(&value), copy);
110 // TODO Task #1970153: It would be great if there were set()
111 // methods that didn't bump up the refcount so that we didn't
112 // have to decrement it here
113 decRefStr(key);
114 tvRefcountedDecRef(&value);
115 return arrayRefShuffle<false>(ad, retval, nullptr);
118 ArrayData* arrayAdd(ArrayData* a1, ArrayData* a2) {
119 if (!a2->empty()) {
120 if (a1->empty()) {
121 // We consume refs on a2 and also produce references, so there's
122 // no need to inc/dec a2.
123 decRefArr(a1);
124 return a2;
126 if (a1 != a2) {
127 auto const escalated = a1->plusEq(a2);
128 if (escalated != a1) {
129 decRefArr(a2);
130 decRefArr(a1);
131 return escalated;
135 decRefArr(a2);
136 return a1;
139 void setNewElem(TypedValue* base, Cell val) {
140 HPHP::SetNewElem<false>(base, &val);
143 void setNewElemArray(TypedValue* base, Cell val) {
144 HPHP::SetNewElemArray(base, &val);
147 TypedValue setOpElem(TypedValue* base, TypedValue key,
148 Cell val, MInstrState* mis, SetOpOp op) {
149 auto result = HPHP::SetOpElem(mis->tvRef, op, base, key, &val);
151 Cell ret;
152 cellDup(*tvToCell(result), ret);
153 return ret;
156 TypedValue incDecElem(
157 TypedValue* base,
158 TypedValue key,
159 MInstrState* mis,
160 IncDecOp op
162 TypedValue result;
163 HPHP::IncDecElem<true>(mis->tvRef, op, base, key, result);
164 assertx(result.m_type != KindOfRef);
165 return result;
168 void bindNewElemIR(TypedValue* base, RefData* val, MInstrState* mis) {
169 auto elem = HPHP::NewElem<true>(mis->tvRef, base);
170 tvBindRef(val, elem);
173 RefData* boxValue(TypedValue tv) {
174 assertx(tv.m_type != KindOfRef);
175 if (tv.m_type == KindOfUninit) tv = make_tv<KindOfNull>();
176 return RefData::Make(tv);
179 inline int64_t reinterpretDblAsInt(double d) {
180 union {
181 int64_t intval;
182 double dblval;
183 } u;
184 u.dblval = d;
185 return u.intval;
188 inline double reinterpretIntAsDbl(int64_t i) {
189 union {
190 int64_t intval;
191 double dblval;
192 } u;
193 u.intval = i;
194 return u.dblval;
197 ArrayData* convCellToArrHelper(TypedValue tv) {
198 // Note: the call sites of this function all assume that
199 // no user code will run and no recoverable exceptions will
200 // occur while running this code. This seems trivially true
201 // in all cases but converting objects to arrays. It also
202 // seems true for that case as well, since the resulting array
203 // is essentially metadata for the object. If that is not true,
204 // you might end up looking at this code in a debugger and now
205 // you know why.
206 tvCastToArrayInPlace(&tv); // consumes a ref on counted values
207 return tv.m_data.parr;
210 int64_t convObjToDblHelper(const ObjectData* o) {
211 return reinterpretDblAsInt(o->toDouble());
214 int64_t convArrToDblHelper(ArrayData* a) {
215 return reinterpretDblAsInt(a->empty() ? 0 : 1);
218 int64_t convStrToDblHelper(const StringData* s) {
219 return reinterpretDblAsInt(s->toDouble());
222 int64_t convResToDblHelper(const ResourceHdr* r) {
223 return reinterpretDblAsInt(r->getId());
226 int64_t convCellToDblHelper(TypedValue tv) {
227 return reinterpretDblAsInt(tvCastToDouble(&tv));
230 int64_t convArrToIntHelper(ArrayData* a) {
231 return a->empty() ? 0 : 1;
234 ObjectData* convCellToObjHelper(TypedValue tv) {
235 // Note: the call sites of this function all assume that
236 // no user code will run and no recoverable exceptions will
237 // occur while running this code. This seems trivially true
238 // in all cases but converting arrays to objects. It also
239 // seems true for that case as well, since the source array
240 // is essentially metadata for the object. If that is not true,
241 // you might end up looking at this code in a debugger and now
242 // you know why.
243 tvCastToObjectInPlace(&tv); // consumes a ref on counted values
244 return tv.m_data.pobj;
247 StringData* convDblToStrHelper(int64_t i) {
248 double d = reinterpretIntAsDbl(i);
249 return buildStringData(d);
252 StringData* convIntToStrHelper(int64_t i) {
253 return buildStringData(i);
256 StringData* convObjToStrHelper(ObjectData* o) {
257 // toString() returns a counted String; detach() it to move ownership
258 // of the count to the caller
259 return o->invokeToString().detach();
262 StringData* convResToStrHelper(ResourceHdr* r) {
263 // toString() returns a counted String; detach() it to move ownership
264 // of the count to the caller
265 return r->data()->o_toString().detach();
268 TypedValue getMemoKeyHelper(TypedValue tv) {
269 auto var = HHVM_FN(serialize_memoize_param)(tvAsCVarRef(&tv));
270 auto res = var.asTypedValue();
271 tvRefcountedIncRef(res);
272 return *res;
275 inline void coerceCellFail(DataType expected, DataType actual, int64_t argNum,
276 const Func* func) {
277 raise_param_type_warning(func->name()->data(), argNum, expected, actual);
279 throw TVCoercionException(func, argNum, actual, expected);
282 bool coerceCellToBoolHelper(TypedValue tv, int64_t argNum, const Func* func) {
283 assertx(cellIsPlausible(tv));
285 DataType type = tv.m_type;
286 if (type == KindOfArray || type == KindOfObject || type == KindOfResource) {
287 coerceCellFail(KindOfBoolean, type, argNum, func);
288 not_reached();
291 return cellToBool(tv);
294 int64_t coerceStrToDblHelper(StringData* sd, int64_t argNum, const Func* func) {
295 DataType type = is_numeric_string(sd->data(), sd->size(), nullptr, nullptr);
297 if (type != KindOfDouble && type != KindOfInt64) {
298 coerceCellFail(KindOfDouble, KindOfString, argNum, func);
299 not_reached();
302 return reinterpretDblAsInt(sd->toDouble());
305 int64_t coerceCellToDblHelper(Cell tv, int64_t argNum, const Func* func) {
306 assertx(cellIsPlausible(tv));
308 switch (tv.m_type) {
309 case KindOfNull:
310 case KindOfBoolean:
311 case KindOfInt64:
312 case KindOfDouble:
313 return convCellToDblHelper(tv);
315 case KindOfStaticString:
316 case KindOfString:
317 return coerceStrToDblHelper(tv.m_data.pstr, argNum, func);
319 case KindOfUninit:
320 case KindOfArray:
321 case KindOfObject:
322 case KindOfResource:
323 coerceCellFail(KindOfDouble, tv.m_type, argNum, func);
324 break;
326 case KindOfRef:
327 case KindOfClass:
328 break;
330 not_reached();
333 int64_t coerceStrToIntHelper(StringData* sd, int64_t argNum, const Func* func) {
334 DataType type = is_numeric_string(sd->data(), sd->size(), nullptr, nullptr);
336 if (type != KindOfDouble && type != KindOfInt64) {
337 coerceCellFail(KindOfInt64, KindOfString, argNum, func);
338 not_reached();
341 return sd->toInt64();
344 int64_t coerceCellToIntHelper(TypedValue tv, int64_t argNum, const Func* func) {
345 assertx(cellIsPlausible(tv));
347 switch (tv.m_type) {
348 case KindOfNull:
349 case KindOfBoolean:
350 case KindOfInt64:
351 case KindOfDouble:
352 return cellToInt(tv);
354 case KindOfStaticString:
355 case KindOfString:
356 return coerceStrToIntHelper(tv.m_data.pstr, argNum, func);
358 case KindOfUninit:
359 case KindOfArray:
360 case KindOfObject:
361 case KindOfResource:
362 coerceCellFail(KindOfInt64, tv.m_type, argNum, func);
363 break;
365 case KindOfRef:
366 case KindOfClass:
367 break;
369 not_reached();
372 const StaticString
373 s_empty(""),
374 s_1("1");
376 StringData* convCellToStrHelper(TypedValue tv) {
377 switch (tv.m_type) {
378 case KindOfUninit:
379 case KindOfNull: return s_empty.get();
380 case KindOfBoolean: return tv.m_data.num ? s_1.get() : s_empty.get();
381 case KindOfInt64: return convIntToStrHelper(tv.m_data.num);
382 case KindOfDouble: return convDblToStrHelper(tv.m_data.num);
383 case KindOfString: tv.m_data.pstr->incRefCount();
384 /* fallthrough */
385 case KindOfStaticString:
386 return tv.m_data.pstr;
387 case KindOfArray: raise_notice("Array to string conversion");
388 return array_string.get();
389 case KindOfObject: return convObjToStrHelper(tv.m_data.pobj);
390 case KindOfResource: return convResToStrHelper(tv.m_data.pres);
391 case KindOfRef:
392 case KindOfClass: break;
394 not_reached();
397 void raiseUndefProp(ObjectData* base, const StringData* name) {
398 base->raiseUndefProp(name);
401 void raiseUndefVariable(StringData* nm) {
402 raise_notice(Strings::UNDEFINED_VARIABLE, nm->data());
403 decRefStr(nm);
406 void raise_error_sd(const StringData *msg) {
407 raise_error("%s", msg->data());
410 ALWAYS_INLINE
411 static bool VerifyTypeSlowImpl(const Class* cls,
412 const Class* constraint,
413 const HPHP::TypeConstraint* expected) {
414 // This helper should only be called for the Object, Self, and Parent cases
415 assertx(expected->isObject() || expected->isSelf() || expected->isParent());
416 // For the Self and Parent cases, we must always have a resolved class for
417 // the constraint
418 assertx(IMPLIES(
419 expected->isSelf() || expected->isParent(), constraint != nullptr));
420 // If we have a resolved class for the constraint, all we have to do is
421 // check if the value's class is compatible with it
422 if (LIKELY(constraint != nullptr)) {
423 return cls->classof(constraint);
425 // The Self and Parent cases should never reach here because they were
426 // handled above
427 assertx(expected->isObject());
428 // Handle the case where the constraint is a type alias
429 return expected->checkTypeAliasObj(cls);
432 void VerifyParamTypeSlow(const Class* cls,
433 const Class* constraint,
434 const HPHP::TypeConstraint* expected,
435 int param) {
436 if (!VerifyTypeSlowImpl(cls, constraint, expected)) {
437 VerifyParamTypeFail(param);
441 void VerifyParamTypeCallable(TypedValue value, int param) {
442 if (UNLIKELY(!is_callable(tvAsCVarRef(&value)))) {
443 VerifyParamTypeFail(param);
447 void VerifyParamTypeFail(int paramNum) {
448 VMRegAnchor _;
449 const ActRec* ar = liveFrame();
450 const Func* func = ar->m_func;
451 auto const& tc = func->params()[paramNum].typeConstraint;
452 TypedValue* tv = frame_local(ar, paramNum);
453 assertx(!tc.check(tv, func));
454 tc.verifyParamFail(func, tv, paramNum);
457 void VerifyRetTypeSlow(const Class* cls,
458 const Class* constraint,
459 const HPHP::TypeConstraint* expected,
460 const TypedValue tv) {
461 if (!VerifyTypeSlowImpl(cls, constraint, expected)) {
462 VerifyRetTypeFail(tv);
466 void VerifyRetTypeCallable(TypedValue value) {
467 if (UNLIKELY(!is_callable(tvAsCVarRef(&value)))) {
468 VerifyRetTypeFail(value);
472 void VerifyRetTypeFail(TypedValue tv) {
473 VMRegAnchor _;
474 const ActRec* ar = liveFrame();
475 const Func* func = ar->m_func;
476 const HPHP::TypeConstraint& tc = func->returnTypeConstraint();
477 assertx(!tc.check(&tv, func));
478 tc.verifyReturnFail(func, &tv);
481 RefData* closureStaticLocInit(StringData* name, ActRec* fp, TypedValue val) {
482 auto const func = fp->m_func;
483 assertx(func->isClosureBody());
484 auto const closureLoc = frame_local(fp, func->numParams());
486 bool inited;
487 auto const refData = lookupStaticFromClosure(
488 closureLoc->m_data.pobj, name, inited);
489 if (!inited) {
490 cellCopy(val, *refData->tv());
492 return refData;
495 ALWAYS_INLINE
496 static bool ak_exist_string_impl(ArrayData* arr, StringData* key) {
497 int64_t n;
498 if (key->isStrictlyInteger(n)) {
499 return arr->exists(n);
501 return arr->exists(key);
504 bool ak_exist_string(ArrayData* arr, StringData* key) {
505 return ak_exist_string_impl(arr, key);
508 bool ak_exist_string_obj(ObjectData* obj, StringData* key) {
509 if (obj->isCollection()) {
510 return collections::contains(obj, Variant{key});
512 auto arr = obj->toArray();
513 return ak_exist_string_impl(arr.get(), key);
516 bool ak_exist_int_obj(ObjectData* obj, int64_t key) {
517 if (obj->isCollection()) {
518 return collections::contains(obj, key);
520 auto arr = obj->toArray();
521 return arr.get()->exists(key);
524 namespace {
525 ALWAYS_INLINE
526 TypedValue getDefaultIfNullCell(const TypedValue* tv, TypedValue& def) {
527 if (UNLIKELY(nullptr == tv)) {
528 // DecRef of def is done unconditionally by the IR, since there's
529 // a good chance it will be paired with an IncRef and optimized
530 // away. So we need to IncRef here if it is being returned.
531 tvRefcountedIncRef(&def);
532 return def;
534 auto const ret = tvToCell(tv);
535 tvRefcountedIncRef(ret);
536 return *ret;
540 TypedValue arrayIdxS(ArrayData* a, StringData* key, TypedValue def) {
541 return getDefaultIfNullCell(a->nvGet(key), def);
544 TypedValue arrayIdxSi(ArrayData* a, StringData* key, TypedValue def) {
545 int64_t i;
546 return UNLIKELY(key->isStrictlyInteger(i)) ?
547 getDefaultIfNullCell(a->nvGet(i), def) :
548 getDefaultIfNullCell(a->nvGet(key), def);
551 TypedValue arrayIdxI(ArrayData* a, int64_t key, TypedValue def) {
552 return getDefaultIfNullCell(a->nvGet(key), def);
555 TypedValue arrayIdxIc(ArrayData* a, int64_t key, TypedValue def) {
556 return arrayIdxI(a, key, def);
559 const StaticString s_idx("hh\\idx");
561 TypedValue genericIdx(TypedValue obj, TypedValue key, TypedValue def) {
562 static auto func = Unit::loadFunc(s_idx.get());
563 assertx(func != nullptr);
564 TypedValue args[] = {
565 obj,
566 key,
569 TypedValue ret;
570 g_context->invokeFuncFew(&ret, func, nullptr, nullptr, 3, &args[0]);
571 return ret;
574 TypedValue mapIdx(ObjectData* mapOD, StringData* key, TypedValue def) {
575 assert(collections::isType(mapOD->getVMClass(), CollectionType::Map) ||
576 collections::isType(mapOD->getVMClass(), CollectionType::ImmMap));
577 return getDefaultIfNullCell(static_cast<BaseMap*>(mapOD)->get(key), def);
580 int32_t arrayVsize(ArrayData* ad) {
581 return ad->vsize();
584 TypedValue* getSPropOrNull(const Class* cls,
585 const StringData* name,
586 Class* ctx) {
587 auto const lookup = cls->getSProp(ctx, name);
589 if (UNLIKELY(!lookup.prop || !lookup.accessible)) return nullptr;
591 return lookup.prop;
594 TypedValue* getSPropOrRaise(const Class* cls,
595 const StringData* name,
596 Class* ctx) {
597 auto sprop = getSPropOrNull(cls, name, ctx);
598 if (UNLIKELY(!sprop)) {
599 raise_error("Invalid static property access: %s::%s",
600 cls->name()->data(), name->data());
602 return sprop;
605 TypedValue* ldGblAddrHelper(StringData* name) {
606 return g_context->m_globalVarEnv->lookup(name);
609 TypedValue* ldGblAddrDefHelper(StringData* name) {
610 return g_context->m_globalVarEnv->lookupAdd(name);
613 template <typename T>
614 static int64_t switchBoundsCheck(T v, int64_t base, int64_t nTargets) {
615 // I'm relying on gcc to be smart enough to optimize away the next
616 // two lines when T is int64.
617 if (int64_t(v) == v) {
618 int64_t ival = v;
619 if (ival >= base && ival < (base + nTargets)) {
620 return ival - base;
623 return nTargets + 1;
626 int64_t switchDoubleHelper(int64_t val, int64_t base, int64_t nTargets) {
627 union {
628 int64_t intbits;
629 double dblval;
630 } u;
631 u.intbits = val;
632 return switchBoundsCheck(u.dblval, base, nTargets);
635 int64_t switchStringHelper(StringData* s, int64_t base, int64_t nTargets) {
636 int64_t ival;
637 double dval;
639 [&] {
640 switch (s->isNumericWithVal(ival, dval, 1)) {
641 case KindOfNull:
642 ival = switchBoundsCheck(0, base, nTargets);
643 return;
644 case KindOfInt64:
645 ival = switchBoundsCheck(ival, base, nTargets);
646 return;
647 case KindOfDouble:
648 ival = switchBoundsCheck(dval, base, nTargets);
649 return;
651 case KindOfUninit:
652 case KindOfBoolean:
653 case KindOfStaticString:
654 case KindOfString:
655 case KindOfArray:
656 case KindOfObject:
657 case KindOfResource:
658 case KindOfRef:
659 case KindOfClass:
660 break;
662 not_reached();
663 }();
665 decRefStr(s);
666 return ival;
669 int64_t switchObjHelper(ObjectData* o, int64_t base, int64_t nTargets) {
670 auto const ival = o->toInt64();
671 decRefObj(o);
672 return switchBoundsCheck(ival, base, nTargets);
675 TCA sswitchHelperFast(const StringData* val,
676 const SSwitchMap* table,
677 TCA* def) {
678 TCA* dest = table->find(val);
679 return dest ? *dest : *def;
682 // TODO(#2031980): clear these out
683 void tv_release_generic(TypedValue* tv) {
684 assertx(vmRegStateIsDirty());
685 assertx(tv->m_type == KindOfString || tv->m_type == KindOfArray ||
686 tv->m_type == KindOfObject || tv->m_type == KindOfResource ||
687 tv->m_type == KindOfRef);
688 g_destructors[typeToDestrIdx(tv->m_type)](tv->m_data.pref);
691 Cell lookupCnsHelper(const TypedValue* tv,
692 StringData* nm,
693 bool error) {
694 assertx(tv->m_type == KindOfUninit);
696 // Deferred constants such as SID
697 if (UNLIKELY(tv->m_data.pref != nullptr)) {
698 auto callback = (Unit::SystemConstantCallback)(tv->m_data.pref);
699 const Cell* cns = callback().asTypedValue();
700 if (LIKELY(cns->m_type != KindOfUninit)) {
701 Cell c1;
702 cellDup(*cns, c1);
703 return c1;
707 const Cell* cns = nullptr;
708 if (UNLIKELY(rds::s_constants().get() != nullptr)) {
709 cns = rds::s_constants()->nvGet(nm);
711 if (!cns) {
712 cns = Unit::loadCns(const_cast<StringData*>(nm));
714 if (LIKELY(cns != nullptr)) {
715 Cell c1;
716 cellDup(*cns, c1);
717 return c1;
720 // Undefined constants
721 if (error) {
722 raise_error("Undefined constant '%s'", nm->data());
723 } else {
724 raise_notice(Strings::UNDEFINED_CONSTANT, nm->data(), nm->data());
725 Cell c1;
726 c1.m_data.pstr = const_cast<StringData*>(nm);
727 c1.m_type = KindOfStaticString;
728 return c1;
730 not_reached();
733 void lookupClsMethodHelper(Class* cls,
734 StringData* meth,
735 ActRec* ar,
736 ActRec* fp) {
737 try {
738 const Func* f;
739 ObjectData* obj = fp->hasThis() ? fp->getThis() : nullptr;
740 Class* ctx = fp->m_func->cls();
741 LookupResult res =
742 g_context->lookupClsMethod(f, cls, meth, obj, ctx, true);
743 if (res == LookupResult::MethodFoundNoThis ||
744 res == LookupResult::MagicCallStaticFound) {
745 ar->setClass(cls);
746 } else {
747 assertx(obj);
748 assertx(res == LookupResult::MethodFoundWithThis ||
749 res == LookupResult::MagicCallFound);
750 obj->incRefCount();
751 ar->setThis(obj);
753 ar->m_func = f;
754 if (res == LookupResult::MagicCallFound ||
755 res == LookupResult::MagicCallStaticFound) {
756 ar->setMagicDispatch(meth);
757 meth->incRefCount();
759 } catch (...) {
760 *arPreliveOverwriteCells(ar) = make_tv<KindOfString>(meth);
761 throw;
765 void profileObjClassHelper(ClassProfile* profile, ObjectData* obj) {
766 profile->reportClass(obj->getVMClass());
769 Cell lookupCnsUHelper(const TypedValue* tv,
770 StringData* nm,
771 StringData* fallback) {
772 const Cell* cns = nullptr;
773 Cell c1;
775 // lookup qualified name in thread-local constants
776 bool cacheConsts = rds::s_constants().get() != nullptr;
777 if (UNLIKELY(cacheConsts)) {
778 cns = rds::s_constants()->nvGet(nm);
780 if (!cns) {
781 cns = Unit::loadCns(const_cast<StringData*>(nm));
784 // try cache handle for unqualified name
785 if (UNLIKELY(!cns && tv->m_type != KindOfUninit)) {
786 cns = const_cast<Cell*>(tv);
789 // lookup unqualified name in thread-local constants
790 if (UNLIKELY(!cns)) {
791 if (UNLIKELY(cacheConsts)) {
792 cns = rds::s_constants()->nvGet(fallback);
794 if (!cns) {
795 cns = Unit::loadCns(const_cast<StringData*>(fallback));
797 if (UNLIKELY(!cns)) {
798 raise_notice(Strings::UNDEFINED_CONSTANT,
799 fallback->data(), fallback->data());
800 c1.m_data.pstr = const_cast<StringData*>(fallback);
801 c1.m_type = KindOfStaticString;
802 return c1;
805 tvDup(*cns, c1);
806 return c1;
809 //////////////////////////////////////////////////////////////////////
811 void checkFrame(ActRec* fp, Cell* sp, bool fullCheck, Offset bcOff) {
812 const Func* func = fp->m_func;
813 func->validate();
814 if (func->cls()) {
815 assertx(!func->cls()->isZombie());
817 if ((func->attrs() & AttrMayUseVV) && fp->hasVarEnv()) {
818 assertx(fp->getVarEnv()->getFP() == fp);
820 int numLocals = func->numLocals();
821 assertx(sp <= (Cell*)fp - func->numSlotsInFrame() || fp->resumed());
823 if (!fullCheck) return;
825 int numParams = func->numParams();
826 for (int i = 0; i < numLocals; i++) {
827 if (i >= numParams && fp->resumed() && i < func->numNamedLocals()) {
828 continue;
830 assertx(tvIsPlausible(*frame_local(fp, i)));
833 visitStackElems(
834 fp, sp, bcOff,
835 [](const ActRec* ar) {
836 ar->func()->validate();
838 [](const TypedValue* tv) {
839 assertx(tv->m_type == KindOfClass || tvIsPlausible(*tv));
844 void traceCallback(ActRec* fp, Cell* sp, Offset pcOff) {
845 if (Trace::moduleEnabled(Trace::hhirTracelets)) {
846 FTRACE(0, "{} {} {} {} {}\n",
847 fp->m_func->fullName()->data(), pcOff, fp, sp,
848 __builtin_return_address(0));
850 checkFrame(fp, sp, /*fullCheck*/true, pcOff);
853 enum class OnFail { Warn, Fatal };
855 template<OnFail FailBehavior, class FooNR>
856 void loadFuncContextImpl(FooNR callableNR, ActRec* preLiveAR, ActRec* fp) {
857 static_assert(
858 std::is_same<FooNR,ArrNR>::value ||
859 std::is_same<FooNR,StrNR>::value,
860 "check loadFuncContextImpl for a new FooNR"
863 ObjectData* inst = nullptr;
864 Class* cls = nullptr;
865 StringData* invName = nullptr;
867 auto func = vm_decode_function(
868 VarNR(callableNR),
870 false, // forward
871 inst,
872 cls,
873 invName,
874 FailBehavior == OnFail::Warn
876 if (UNLIKELY(func == nullptr)) {
877 if (FailBehavior == OnFail::Fatal) {
878 raise_error("Invalid callable (array)");
880 func = SystemLib::s_nullFunc;
883 preLiveAR->m_func = func;
884 if (inst) {
885 inst->incRefCount();
886 preLiveAR->setThis(inst);
887 } else {
888 preLiveAR->setClass(cls);
890 if (UNLIKELY(invName != nullptr)) {
891 preLiveAR->setMagicDispatch(invName);
895 void loadArrayFunctionContext(ArrayData* arr, ActRec* preLiveAR, ActRec* fp) {
896 try {
897 loadFuncContextImpl<OnFail::Fatal>(ArrNR(arr), preLiveAR, fp);
898 } catch (...) {
899 *arPreliveOverwriteCells(preLiveAR) = make_tv<KindOfArray>(arr);
900 throw;
904 NEVER_INLINE
905 static void fpushCufHelperArraySlowPath(ArrayData* arr,
906 ActRec* preLiveAR,
907 ActRec* fp) {
908 loadFuncContextImpl<OnFail::Warn>(ArrNR(arr), preLiveAR, fp);
911 ALWAYS_INLINE
912 static bool strHasColon(StringData* sd) {
913 auto const sl = sd->slice();
914 auto const e = sl.end();
915 for (auto p = sl.begin(); p != e; ++p) {
916 if (*p == ':') return true;
918 return false;
921 void fpushCufHelperArray(ArrayData* arr, ActRec* preLiveAR, ActRec* fp) {
922 try {
923 if (UNLIKELY(!arr->isPacked() || arr->getSize() != 2)) {
924 return fpushCufHelperArraySlowPath(arr, preLiveAR, fp);
927 auto const elem0 = tvToCell(PackedArray::NvGetInt(arr, 0));
928 auto const elem1 = tvToCell(PackedArray::NvGetInt(arr, 1));
930 if (UNLIKELY(elem0->m_type != KindOfObject ||
931 !isStringType(elem1->m_type))) {
932 return fpushCufHelperArraySlowPath(arr, preLiveAR, fp);
935 // If the string contains a class name (e.g. Foo::bar), all kinds
936 // of weird junk happens (wrt forwarding class contexts and
937 // things). We just do a quick loop to try to bail out of this
938 // case.
939 if (UNLIKELY(strHasColon(elem1->m_data.pstr))) {
940 return fpushCufHelperArraySlowPath(arr, preLiveAR, fp);
943 auto const inst = elem0->m_data.pobj;
944 auto const func = g_context->lookupMethodCtx(
945 inst->getVMClass(),
946 elem1->m_data.pstr,
947 fp->m_func->cls(),
948 CallType::ObjMethod
950 if (UNLIKELY(!func || (func->attrs() & AttrStatic))) {
951 return fpushCufHelperArraySlowPath(arr, preLiveAR, fp);
954 preLiveAR->m_func = func;
955 inst->incRefCount();
956 preLiveAR->setThis(inst);
957 } catch (...) {
958 *arPreliveOverwriteCells(preLiveAR) = make_tv<KindOfArray>(arr);
959 throw;
963 NEVER_INLINE
964 static void fpushCufHelperStringSlowPath(StringData* sd,
965 ActRec* preLiveAR,
966 ActRec* fp) {
967 loadFuncContextImpl<OnFail::Warn>(StrNR(sd), preLiveAR, fp);
970 NEVER_INLINE
971 static void fpushStringFail(const StringData* sd, ActRec* preLiveAR) {
972 throw_invalid_argument("function: method '%s' not found", sd->data());
973 preLiveAR->m_func = SystemLib::s_nullFunc;
976 void fpushCufHelperString(StringData* sd, ActRec* preLiveAR, ActRec* fp) {
977 try {
978 if (UNLIKELY(strHasColon(sd))) {
979 return fpushCufHelperStringSlowPath(sd, preLiveAR, fp);
982 auto const func = Unit::loadFunc(sd);
983 preLiveAR->m_func = func;
984 if (UNLIKELY(!func)) {
985 return fpushStringFail(sd, preLiveAR);
987 } catch (...) {
988 *arPreliveOverwriteCells(preLiveAR) = make_tv<KindOfString>(sd);
989 throw;
993 const Func* loadClassCtor(Class* cls) {
994 const Func* f = cls->getCtor();
995 if (UNLIKELY(!(f->attrs() & AttrPublic))) {
996 VMRegAnchor _;
997 UNUSED LookupResult res =
998 g_context->lookupCtorMethod(f, cls, true /*raise*/);
999 assertx(res == LookupResult::MethodFoundWithThis);
1001 return f;
1004 const Func* lookupUnknownFunc(const StringData* name) {
1005 VMRegAnchor _;
1006 auto const func = Unit::loadFunc(name);
1007 if (UNLIKELY(!func)) {
1008 raise_error("Call to undefined function %s()", name->data());
1010 return func;
1013 const Func* lookupFallbackFunc(const StringData* name,
1014 const StringData* fallback) {
1015 VMRegAnchor _;
1016 // Try to load the first function
1017 auto func = Unit::loadFunc(name);
1018 if (LIKELY(!func)) {
1019 // Then try to load the fallback function
1020 func = Unit::loadFunc(fallback);
1021 if (UNLIKELY(!func)) {
1022 raise_error("Call to undefined function %s()", name->data());
1025 return func;
1028 Class* lookupKnownClass(Class** cache, const StringData* clsName) {
1029 Class* cls = *cache;
1030 assertx(!cls); // the caller should already have checked
1032 AutoloadHandler::s_instance->autoloadClass(
1033 StrNR(const_cast<StringData*>(clsName)));
1034 cls = *cache;
1036 if (UNLIKELY(!cls)) raise_error(Strings::UNKNOWN_CLASS, clsName->data());
1037 return cls;
1040 Cell lookupClassConstantTv(TypedValue* cache,
1041 const NamedEntity* ne,
1042 const StringData* cls,
1043 const StringData* cns) {
1044 Cell clsCns = g_context->lookupClsCns(ne, cls, cns);
1045 cellDup(clsCns, *cache);
1046 return clsCns;
1049 //////////////////////////////////////////////////////////////////////
1051 ObjectData* colAddNewElemCHelper(ObjectData* coll, TypedValue value) {
1052 collections::initElem(coll, &value);
1053 // If we specialized this on Vector we could use a DecRefNZ here (since we
1054 // could assume that initElem has incref'd the value). Right now, HH\Set
1055 // goes through this code path also, though, and it might fail to add the new
1056 // element.
1057 tvRefcountedDecRef(value);
1058 return coll;
1061 ObjectData* colAddElemCHelper(ObjectData* coll, TypedValue key,
1062 TypedValue value) {
1063 collections::initMapElem(coll, &key, &value);
1064 // consume the input value. the collection setter either threw or created a
1065 // reference to value, so we can use a cheaper decref.
1066 tvRefcountedDecRefNZ(value);
1067 return coll;
1070 //////////////////////////////////////////////////////////////////////
1073 * The standard VMRegAnchor treatment won't work for some cases called
1074 * during function preludes.
1076 * The fp sync machinery is fundamentally based on the notion that
1077 * instruction pointers in the TC are uniquely associated with source
1078 * HHBC instructions, and that source HHBC instructions are in turn
1079 * uniquely associated with SP->FP deltas.
1081 * trimExtraArgs is called from the prologue of the callee.
1082 * The prologue is 1) still in the caller frame for now,
1083 * and 2) shared across multiple call sites. 1 means that we have the
1084 * fp from the caller's frame, and 2 means that this fp is not enough
1085 * to figure out sp.
1087 * However, the prologue passes us the callee actRec, whose predecessor
1088 * has to be the caller. So we can sync sp and fp by ourselves here.
1089 * Geronimo!
1091 static void sync_regstate_to_caller(ActRec* preLive) {
1092 assertx(tl_regState == VMRegState::DIRTY);
1093 auto const ec = g_context.getNoCheck();
1094 auto& regs = vmRegsUnsafe();
1095 regs.stack.top() = (TypedValue*)preLive - preLive->numArgs();
1096 ActRec* fp = preLive == vmFirstAR() ?
1097 ec->m_nestedVMs.back().fp : preLive->m_sfp;
1098 regs.fp = fp;
1099 regs.pc = fp->m_func->unit()->at(fp->m_func->base() + preLive->m_soff);
1100 tl_regState = VMRegState::CLEAN;
1103 #define SHUFFLE_EXTRA_ARGS_PRELUDE() \
1104 const Func* f = ar->m_func; \
1105 int numParams = f->numNonVariadicParams(); \
1106 int numArgs = ar->numArgs(); \
1107 assertx(numArgs > numParams); \
1108 int numExtra = numArgs - numParams; \
1109 TRACE(1, "extra args: %d args, function %s takes only %d, ar %p\n", \
1110 numArgs, f->name()->data(), numParams, ar); \
1111 auto tvArgs = reinterpret_cast<TypedValue*>(ar) - numArgs; \
1112 /* end SHUFFLE_EXTRA_ARGS_PRELUDE */
1114 NEVER_INLINE
1115 static void trimExtraArgsMayReenter(ActRec* ar,
1116 TypedValue* tvArgs,
1117 TypedValue* limit
1119 sync_regstate_to_caller(ar);
1120 do {
1121 tvRefcountedDecRef(tvArgs); // may reenter for __destruct
1122 ++tvArgs;
1123 } while (tvArgs != limit);
1124 ar->setNumArgs(ar->m_func->numParams());
1126 // go back to dirty (see the comments of sync_regstate_to_caller)
1127 tl_regState = VMRegState::DIRTY;
1130 void trimExtraArgs(ActRec* ar) {
1131 SHUFFLE_EXTRA_ARGS_PRELUDE()
1132 assertx(!f->hasVariadicCaptureParam());
1133 assertx(!(f->attrs() & AttrMayUseVV));
1135 TypedValue* limit = tvArgs + numExtra;
1136 do {
1137 if (UNLIKELY(tvDecRefWillCallHelper(tvArgs))) {
1138 trimExtraArgsMayReenter(ar, tvArgs, limit);
1139 return;
1141 tvDecRefOnly(tvArgs);
1142 ++tvArgs;
1143 } while (tvArgs != limit);
1145 assertx(f->numParams() == (numArgs - numExtra));
1146 assertx(f->numParams() == numParams);
1147 ar->setNumArgs(numParams);
1150 void shuffleExtraArgsMayUseVV(ActRec* ar) {
1151 SHUFFLE_EXTRA_ARGS_PRELUDE()
1152 assertx(!f->hasVariadicCaptureParam());
1153 assertx(f->attrs() & AttrMayUseVV);
1155 ar->setExtraArgs(ExtraArgs::allocateCopy(tvArgs, numExtra));
1158 void shuffleExtraArgsVariadic(ActRec* ar) {
1159 SHUFFLE_EXTRA_ARGS_PRELUDE()
1160 assertx(f->hasVariadicCaptureParam());
1161 assertx(!(f->attrs() & AttrMayUseVV));
1164 auto varArgsArray = Array::attach(MixedArray::MakePacked(numExtra, tvArgs));
1165 // write into the last (variadic) param
1166 auto tv = reinterpret_cast<TypedValue*>(ar) - numParams - 1;
1167 tv->m_type = KindOfArray;
1168 tv->m_data.parr = varArgsArray.detach();
1169 assertx(tv->m_data.parr->hasExactlyOneRef());
1171 // no incref is needed, since extra values are being transferred
1172 // from the stack to the last local
1173 assertx(f->numParams() == (numArgs - numExtra + 1));
1174 assertx(f->numParams() == (numParams + 1));
1175 ar->setNumArgs(numParams + 1);
1179 void shuffleExtraArgsVariadicAndVV(ActRec* ar) {
1180 SHUFFLE_EXTRA_ARGS_PRELUDE()
1181 assertx(f->hasVariadicCaptureParam());
1182 assertx(f->attrs() & AttrMayUseVV);
1185 ar->setExtraArgs(ExtraArgs::allocateCopy(tvArgs, numExtra));
1187 auto varArgsArray = Array::attach(MixedArray::MakePacked(numExtra, tvArgs));
1188 auto tvIncr = tvArgs; uint32_t i = 0;
1189 // an incref is needed to compensate for discarding from the stack
1190 for (; i < numExtra; ++i, ++tvIncr) { tvRefcountedIncRef(tvIncr); }
1191 // write into the last (variadic) param
1192 auto tv = reinterpret_cast<TypedValue*>(ar) - numParams - 1;
1193 tv->m_type = KindOfArray;
1194 tv->m_data.parr = varArgsArray.detach();
1195 assertx(tv->m_data.parr->hasExactlyOneRef());
1196 // Before, for each arg: refcount = n + 1 (stack)
1197 // After, for each arg: refcount = n + 2 (ExtraArgs, varArgsArray)
1201 #undef SHUFFLE_EXTRA_ARGS_PRELUDE
1203 void raiseMissingArgument(const Func* func, int got) {
1204 const auto total = func->numNonVariadicParams();
1205 const auto variadic = func->hasVariadicCaptureParam();
1206 const Func::ParamInfoVec& params = func->params();
1207 int expected = 0;
1208 // We subtract the number of parameters with default value at the end
1209 for (size_t i = total; i--; ) {
1210 if (!params[i].hasDefaultValue()) {
1211 expected = i + 1;
1212 break;
1215 bool lessNeeded = (variadic || expected < total);
1216 if (expected == 1) {
1217 raise_warning(Strings::MISSING_ARGUMENT, func->name()->data(),
1218 lessNeeded ? "at least" : "exactly", got);
1219 } else {
1220 raise_warning(Strings::MISSING_ARGUMENTS, func->name()->data(),
1221 lessNeeded ? "at least" : "exactly", expected, got);
1225 rds::Handle lookupClsRDSHandle(const StringData* name) {
1226 return NamedEntity::get(name)->getClassHandle();
1229 void registerLiveObj(ObjectData* obj) {
1230 assertx(RuntimeOption::EnableObjDestructCall && obj->getVMClass()->getDtor());
1231 g_context->m_liveBCObjs.insert(obj);
1234 void throwSwitchMode() {
1235 // This is only called right after dispatchBB, so the VM regs really are
1236 // clean.
1237 tl_regState = VMRegState::CLEAN;
1238 throw VMSwitchMode();
1241 namespace MInstrHelpers {
1243 StringData* stringGetI(StringData* base, uint64_t x) {
1244 if (LIKELY(x < base->size())) {
1245 return base->getChar(x);
1247 if (RuntimeOption::EnableHipHopSyntax) {
1248 raise_warning("Out of bounds");
1250 return staticEmptyString();
1253 uint64_t pairIsset(c_Pair* pair, int64_t index) {
1254 auto result = pair->get(index);
1255 return result ? !cellIsNull(result) : false;
1258 uint64_t vectorIsset(c_Vector* vec, int64_t index) {
1259 auto result = vec->get(index);
1260 return result ? !cellIsNull(result) : false;
1263 void bindElemC(TypedValue* base, TypedValue key, RefData* val,
1264 MInstrState* mis) {
1265 auto elem = HPHP::ElemD<false, true>(mis->tvRef, base, key);
1266 tvBindRef(val, elem);
1269 void setWithRefElemC(TypedValue* base, TypedValue keyTV, TypedValue val,
1270 MInstrState* mis) {
1271 auto const keyC = tvToCell(&keyTV);
1272 auto elem = HPHP::ElemD<false, false>(mis->tvRef, base, *keyC);
1273 tvDup(val, *elem);
1276 void setWithRefNewElem(TypedValue* base, TypedValue val, MInstrState* mis) {
1277 auto elem = NewElem<false>(mis->tvRef, base);
1278 tvDup(val, *elem);
1283 //////////////////////////////////////////////////////////////////////
1285 uintptr_t tlsBaseNoInline() {
1286 return tlsBase();
1289 //////////////////////////////////////////////////////////////////////