Optimize struct element initialization
[hiphop-php.git] / hphp / runtime / vm / func-inl.h
blob4fda9618fc548b9bf17e48cad4eddb906253ce83
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 #ifndef incl_HPHP_VM_FUNC_INL_H_
18 #error "func-inl.h should only be included by func.h"
19 #endif
21 #include "hphp/runtime/vm/unit-util.h"
23 namespace HPHP {
24 ///////////////////////////////////////////////////////////////////////////////
25 // EH table.
27 template<class SerDe>
28 void EHEnt::serde(SerDe& sd) {
29 folly::Optional<Offset> end;
30 if (!SerDe::deserializing) {
31 end = (m_end == kInvalidOffset) ? folly::none : folly::make_optional(m_end);
34 sd(m_base)
35 (m_past)
36 (m_iterId)
37 (m_handler)
38 (end)
39 (m_parentIndex)
42 if (SerDe::deserializing) {
43 m_end = end.value_or(kInvalidOffset);
47 ///////////////////////////////////////////////////////////////////////////////
48 // ParamInfo.
50 inline Func::ParamInfo::ParamInfo()
51 : defaultValue(make_tv<KindOfUninit>())
54 template<class SerDe>
55 inline void Func::ParamInfo::serde(SerDe& sd) {
56 sd(builtinType)
57 (funcletOff)
58 (defaultValue)
59 (phpCode)
60 (typeConstraint)
61 (flags)
62 (userAttributes)
63 (userType)
67 inline bool Func::ParamInfo::hasDefaultValue() const {
68 return funcletOff != kInvalidOffset;
71 inline bool Func::ParamInfo::hasScalarDefaultValue() const {
72 return hasDefaultValue() && defaultValue.m_type != KindOfUninit;
75 inline bool Func::ParamInfo::isInOut() const {
76 return flags & (1 << static_cast<int32_t>(Flags::InOut));
79 inline bool Func::ParamInfo::isVariadic() const {
80 return flags & (1 << static_cast<int32_t>(Flags::Variadic));
83 inline bool Func::ParamInfo::isNativeArg() const {
84 return flags & (1 << static_cast<int32_t>(Flags::NativeArg));
87 inline bool Func::ParamInfo::isTakenAsVariant() const {
88 return flags & (1 << static_cast<int32_t>(Flags::AsVariant));
91 inline bool Func::ParamInfo::isTakenAsTypedValue() const {
92 return flags & (1 << static_cast<int32_t>(Flags::AsTypedValue));
95 inline void Func::ParamInfo::setFlag(Func::ParamInfo::Flags flag) {
96 flags |= 1 << static_cast<int32_t>(flag);
99 ///////////////////////////////////////////////////////////////////////////////
100 // Func.
102 inline const void* Func::mallocEnd() const {
103 return reinterpret_cast<const char*>(this)
104 + Func::prologueTableOff()
105 + numPrologues() * sizeof(m_prologueTable[0]);
108 inline bool Func::validate() const {
109 #ifndef NDEBUG
110 assertx(m_magic == kMagic);
111 #endif
112 assertx(m_name != nullptr);
113 return true;
116 ///////////////////////////////////////////////////////////////////////////////
117 // FuncId manipulation.
119 inline FuncId Func::getFuncId() const {
120 assertx(!m_funcId.isInvalid());
121 assertx(fromFuncId(m_funcId) == this);
122 #ifdef USE_LOWPTR
123 return FuncId{this};
124 #else
125 return m_funcId;
126 #endif
129 ///////////////////////////////////////////////////////////////////////////////
130 // Basic info.
132 inline Unit* Func::unit() const {
133 return m_unit;
136 inline Class* Func::cls() const {
137 return !isMethCaller() ? m_u.cls() : nullptr;
140 inline PreClass* Func::preClass() const {
141 return shared()->m_preClass;
144 inline Class* Func::baseCls() const {
145 return !(m_baseCls & kMethCallerBit) ?
146 reinterpret_cast<Class*>(m_baseCls) : nullptr;
149 inline Class* Func::implCls() const {
150 return isClosureBody() ? baseCls() : cls();
153 inline const StringData* Func::name() const {
154 assertx(m_name != nullptr);
155 return m_name;
158 inline String Func::nameWithClosureName() const {
159 if (!isClosureBody()) return String(const_cast<StringData*>(name()));
160 // Strip the file hash from the closure name.
161 String name{const_cast<StringData*>(baseCls()->name())};
162 return name.substr(0, name.find(';'));
165 inline StrNR Func::nameStr() const {
166 assertx(m_name != nullptr);
167 return StrNR(m_name);
170 inline size_t Func::stableHash() const {
171 return folly::hash::hash_combine(
172 name()->hashStatic(),
173 cls() ? cls()->name()->hashStatic() : 0,
174 unit()->sn()
178 inline const StringData* Func::fullName() const {
179 if (m_fullName == nullptr) return m_name;
180 if (UNLIKELY((intptr_t)m_fullName.get() == kNeedsFullName)) {
181 m_fullName = makeStaticString(
182 std::string(cls()->name()->data()) + "::" + m_name->data());
184 return m_fullName;
187 inline String Func::fullNameWithClosureName() const {
188 if (!isClosureBody()) return String(const_cast<StringData*>(fullName()));
189 return nameWithClosureName();
192 inline StrNR Func::fullNameStr() const {
193 assertx(m_fullName != nullptr);
194 return StrNR(fullName());
197 inline void invalidFuncConversion(const char* type) {
198 SystemLib::throwInvalidOperationExceptionObject(folly::sformat(
199 "Cannot convert func to {}", type
203 inline NamedEntity* Func::getNamedEntity() {
204 assertx(!shared()->m_preClass);
205 return *reinterpret_cast<LowPtr<NamedEntity>*>(&m_namedEntity);
208 inline const NamedEntity* Func::getNamedEntity() const {
209 assertx(!shared()->m_preClass);
210 return *reinterpret_cast<const LowPtr<const NamedEntity>*>(&m_namedEntity);
213 inline void Func::setNamedEntity(const NamedEntity* e) {
214 *reinterpret_cast<LowPtr<const NamedEntity>*>(&m_namedEntity) = e;
217 inline const StringData* Func::methCallerClsName() const {
218 assertx(isMethCaller() && isBuiltin());
219 return m_u.name();
222 inline const StringData* Func::methCallerMethName() const {
223 assertx(isMethCaller() && isBuiltin() &&
224 (m_methCallerMethName & kMethCallerBit));
225 return reinterpret_cast<StringData*>(m_methCallerMethName - kMethCallerBit);
228 ///////////////////////////////////////////////////////////////////////////////
229 // File info.
231 inline const StringData* Func::originalFilename() const {
232 return shared()->m_originalFilename;
235 inline const StringData* Func::filename() const {
236 // Builtins don't have filenames
237 if (isBuiltin()) {
238 return staticEmptyString();
241 // Use the original filename if it exists, otherwise grab the filename from
242 // the unit
243 const StringData* name = originalFilename();
244 if (!name) {
245 assertx(m_unit);
246 name = m_unit->filepath();
247 assertx(name);
249 return name;
252 inline int Func::sn() const {
253 auto const sd = shared();
254 auto small = sd->m_sn;
255 if (UNLIKELY(small == kSmallDeltaLimit)) {
256 assertx(extShared());
257 return static_cast<const ExtendedSharedData*>(sd)->m_sn;
259 return small;
262 inline int Func::line1() const {
263 return shared()->m_line1;
266 inline int Func::line2() const {
267 auto const sd = shared();
268 auto const delta = sd->m_line2Delta;
269 if (UNLIKELY(delta == kSmallDeltaLimit)) {
270 assertx(extShared());
271 return static_cast<const ExtendedSharedData*>(sd)->m_line2;
273 return line1() + delta;
276 inline const StringData* Func::docComment() const {
277 auto const ex = extShared();
278 return ex ? ex->m_docComment : staticEmptyString();
281 ///////////////////////////////////////////////////////////////////////////////
282 // Bytecode.
284 inline PC Func::entry() const {
285 const auto pc = shared()->m_bc.load(std::memory_order_relaxed);
286 if (pc != nullptr) {
287 return pc;
289 return const_cast<Func*>(this)->loadBytecode();
292 inline Offset Func::bclen() const {
293 return shared()->bclen();
296 inline Offset Func::SharedData::bclen() const {
297 auto const len = this->m_bclenSmall;
298 if (UNLIKELY(len == kSmallDeltaLimit)) {
299 assertx(m_allFlags.m_hasExtendedSharedData);
300 return static_cast<const ExtendedSharedData*>(this)->m_bclen;
302 return len;
305 inline bool Func::contains(PC pc) const {
306 return uintptr_t(pc - entry()) < bclen();
309 inline bool Func::contains(Offset offset) const {
310 assertx(offset >= 0);
311 return offset < bclen();
314 inline PC Func::at(Offset off) const {
315 // We don't use contains because we want to allow past becase it is often
316 // used in loops
317 assertx(off >= 0 && off <= bclen());
318 return entry() + off;
321 inline Offset Func::offsetOf(PC pc) const {
322 assertx(contains(pc));
323 return pc - entry();
326 inline Op Func::getOp(Offset instrOffset) const {
327 assertx(contains(instrOffset));
328 return peek_op(entry() + instrOffset);
331 inline Offset Func::ctiEntry() const {
332 return shared()->m_cti_base.load(std::memory_order_acquire);
335 inline void Func::setCtiFunclet(int i, Offset cti_funclet) {
336 shared()->m_params[i].ctiFunclet = cti_funclet;
339 inline void Func::setCtiEntry(Offset base, uint32_t size) {
340 auto sd = shared();
341 sd->m_cti_size = size;
342 sd->m_cti_base.store(base, std::memory_order_release);
345 ///////////////////////////////////////////////////////////////////////////////
346 // Return type.
348 inline MaybeDataType Func::hniReturnType() const {
349 auto const ex = extShared();
350 return ex ? ex->m_hniReturnType : folly::none;
353 inline RepoAuthType Func::repoReturnType() const {
354 return shared()->m_repoReturnType;
357 inline RepoAuthType Func::repoAwaitedReturnType() const {
358 return shared()->m_repoAwaitedReturnType;
361 inline bool Func::isReturnByValue() const {
362 return shared()->m_allFlags.m_returnByValue;
365 inline const TypeConstraint& Func::returnTypeConstraint() const {
366 return shared()->m_retTypeConstraint;
369 inline const StringData* Func::returnUserType() const {
370 return shared()->m_retUserType;
373 inline bool Func::hasReturnWithMultiUBs() const {
374 return shared()->m_allFlags.m_hasReturnWithMultiUBs;
377 inline const Func::UpperBoundVec& Func::returnUBs() const {
378 assertx(hasReturnWithMultiUBs());
379 return extShared()->m_returnUBs;
382 ///////////////////////////////////////////////////////////////////////////////
383 // Parameters.
385 inline const Func::ParamInfoVec& Func::params() const {
386 return shared()->m_params;
389 inline uint32_t Func::numParams() const {
390 assertx(bool(m_attrs & AttrVariadicParam) != bool(m_paramCounts & 1));
391 assertx((m_paramCounts >> 1) == params().size());
392 return (m_paramCounts) >> 1;
395 inline uint32_t Func::numNonVariadicParams() const {
396 assertx(bool(m_attrs & AttrVariadicParam) != bool(m_paramCounts & 1));
397 assertx((m_paramCounts >> 1) == params().size());
398 return (m_paramCounts - 1) >> 1;
401 inline uint32_t Func::numRequiredParams() const {
402 for (auto i = numNonVariadicParams(); i > 0; --i) {
403 if (!params()[i - 1].hasDefaultValue()) return i;
405 return 0;
408 inline bool Func::hasVariadicCaptureParam() const {
409 #ifndef NDEBUG
410 assertx(bool(m_attrs & AttrVariadicParam) ==
411 (numParams() && params()[numParams() - 1].isVariadic()));
412 #endif
413 return m_attrs & AttrVariadicParam;
416 inline bool Func::hasParamsWithMultiUBs() const {
417 return shared()->m_allFlags.m_hasParamsWithMultiUBs;
420 inline const Func::ParamUBMap& Func::paramUBs() const {
421 assertx(hasParamsWithMultiUBs());
422 return extShared()->m_paramUBs;
425 ///////////////////////////////////////////////////////////////////////////////
426 // Locals, iterators, and stack.
428 inline int Func::numLocals() const {
429 return shared()->m_numLocals;
432 inline int Func::numIterators() const {
433 return shared()->m_numIterators;
436 inline Id Func::numNamedLocals() const {
437 return shared()->m_localNames.size();
440 inline Id Func::coeffectsLocalId() const {
441 assertx(hasCoeffectsLocal());
442 return numParams() + (hasReifiedGenerics() ? 1 : 0);
445 inline Id Func::reifiedGenericsLocalId() const {
446 assertx(hasReifiedGenerics());
447 return numParams();
450 inline const StringData* Func::localVarName(Id id) const {
451 assertx(id >= 0);
452 return id < numNamedLocals() ? shared()->m_localNames[id] : nullptr;
455 inline LowStringPtr const* Func::localNames() const {
456 return shared()->m_localNames.accessList();
459 inline int Func::maxStackCells() const {
460 return m_maxStackCells;
463 inline int Func::numSlotsInFrame() const {
464 return
465 shared()->m_numLocals +
466 shared()->m_numIterators * (sizeof(Iter) / sizeof(TypedValue));
469 inline bool Func::hasForeignThis() const {
470 return m_hasForeignThis;
473 inline void Func::setHasForeignThis(bool hasForeignThis) {
474 m_hasForeignThis = hasForeignThis;
477 inline void Func::setGenerated(bool isGenerated) {
478 shared()->m_allFlags.m_isGenerated = isGenerated;
481 ///////////////////////////////////////////////////////////////////////////////
482 // Definition context.
484 inline bool Func::isMethod() const {
485 return (bool)baseCls();
488 inline bool Func::isFromTrait() const {
489 return m_attrs & AttrTrait;
492 inline bool Func::isPublic() const {
493 return m_attrs & AttrPublic;
496 inline bool Func::isStatic() const {
497 return m_attrs & AttrStatic;
500 inline bool Func::isStaticInPrologue() const {
501 return isStatic() && !isClosureBody();
504 inline bool Func::hasThisInBody() const {
505 return cls() && !isStatic();
508 inline bool Func::hasNoContextAttr() const {
509 return m_attrs & AttrNoContext;
512 inline bool Func::isAbstract() const {
513 return m_attrs & AttrAbstract;
516 inline bool Func::isPreFunc() const {
517 return m_isPreFunc;
520 inline bool Func::isMemoizeWrapper() const {
521 return shared()->m_allFlags.m_isMemoizeWrapper;
524 inline bool Func::isMemoizeWrapperLSB() const {
525 return shared()->m_allFlags.m_isMemoizeWrapperLSB;
528 inline bool Func::isMemoizeImpl() const {
529 return isMemoizeImplName(name());
532 inline const StringData* Func::memoizeImplName() const {
533 assertx(isMemoizeWrapper());
534 return genMemoizeImplName(name());
537 ///////////////////////////////////////////////////////////////////////////////
538 // Builtins.
540 inline bool Func::isBuiltin() const {
541 return m_attrs & AttrBuiltin;
544 inline bool Func::isCPPBuiltin() const {
545 auto const ex = extShared();
546 return UNLIKELY(!!ex) && ex->m_arFuncPtr;
549 inline ArFunction Func::arFuncPtr() const {
550 if (auto const ex = extShared()) return ex->m_arFuncPtr;
551 return nullptr;
554 inline NativeFunction Func::nativeFuncPtr() const {
555 if (auto const ex = extShared()) return ex->m_nativeFuncPtr;
556 return nullptr;
559 ///////////////////////////////////////////////////////////////////////////////
560 // Closures.
562 inline bool Func::isClosureBody() const {
563 return shared()->m_allFlags.m_isClosureBody;
566 ///////////////////////////////////////////////////////////////////////////////
567 // Resumables.
569 inline bool Func::isAsync() const {
570 return shared()->m_allFlags.m_isAsync;
573 inline bool Func::isGenerator() const {
574 return shared()->m_allFlags.m_isGenerator;
577 inline bool Func::isPairGenerator() const {
578 return shared()->m_allFlags.m_isPairGenerator;
581 inline bool Func::isAsyncFunction() const {
582 return isAsync() && !isGenerator();
585 inline bool Func::isNonAsyncGenerator() const {
586 return !isAsync() && isGenerator();
589 inline bool Func::isAsyncGenerator() const {
590 return isAsync() && isGenerator();
593 inline bool Func::isResumable() const {
594 return isAsync() || isGenerator();
597 ///////////////////////////////////////////////////////////////////////////////
598 // Coeffects.
600 inline RuntimeCoeffects Func::requiredCoeffects() const {
601 return m_requiredCoeffects;
604 inline RuntimeCoeffects Func::shallowCoeffectsWithLocals() const {
605 return extShared() ? extShared()->m_shallowCoeffectsWithLocals
606 : RuntimeCoeffects::none();
609 inline void Func::setRequiredCoeffects(RuntimeCoeffects c) {
610 m_requiredCoeffects = c;
613 inline StaticCoeffectNamesMap Func::staticCoeffectNames() const {
614 return shared()->m_staticCoeffectNames;
617 inline bool Func::isRxDisabled() const {
618 return shared()->m_allFlags.m_isRxDisabled;
621 inline bool Func::hasCoeffectsLocal() const {
622 return hasCoeffectRules() &&
623 !(getCoeffectRules().size() == 1 &&
624 getCoeffectRules()[0].isGeneratorThis());
627 inline bool Func::hasCoeffectRules() const {
628 return attrs() & AttrHasCoeffectRules;
631 inline const Func::CoeffectRules& Func::getCoeffectRules() const {
632 assertx(extShared());
633 assertx(hasCoeffectRules());
634 return extShared()->m_coeffectRules;
637 ///////////////////////////////////////////////////////////////////////////////
638 // Methods.
640 inline Slot Func::methodSlot() const {
641 assertx(isMethod());
642 return m_methodSlot;
645 inline bool Func::hasPrivateAncestor() const {
646 return m_hasPrivateAncestor;
649 ///////////////////////////////////////////////////////////////////////////////
650 // Magic methods.
652 inline bool Func::isGenerated() const {
653 return shared()->m_allFlags.m_isGenerated;
656 inline bool Func::isSpecial(const StringData* name) {
657 return strncmp("86", name->data(), 2) == 0;
660 ///////////////////////////////////////////////////////////////////////////////
661 // Other attributes.
663 inline Attr Func::attrs() const {
664 return m_attrs;
667 inline const UserAttributeMap& Func::userAttributes() const {
668 return shared()->m_userAttributes;
671 inline bool Func::isUnique() const {
672 return m_attrs & AttrUnique;
675 inline bool Func::isPersistent() const {
676 return m_attrs & AttrPersistent;
679 inline bool Func::isInterceptable() const {
680 return m_attrs & AttrInterceptable;
683 inline bool Func::isNoInjection() const {
684 return m_attrs & AttrNoInjection;
687 inline bool Func::isSkipFrame() const {
688 return isCPPBuiltin() || (isBuiltin() && !isMethod());
691 inline bool Func::isProvenanceSkipFrame() const {
692 return m_attrs & AttrProvenanceSkipFrame;
695 inline bool Func::isFoldable() const {
696 return m_attrs & AttrIsFoldable;
699 inline bool Func::supportsAsyncEagerReturn() const {
700 return m_attrs & AttrSupportsAsyncEagerReturn;
703 inline bool Func::isDynamicallyCallable() const {
704 return m_attrs & AttrDynamicallyCallable;
707 inline folly::Optional<int64_t> Func::dynCallSampleRate() const {
708 if (auto const ex = extShared()) {
709 if (ex->m_dynCallSampleRate >= 0) return ex->m_dynCallSampleRate;
711 return folly::none;
714 inline bool Func::isMethCaller() const {
715 return m_attrs & AttrIsMethCaller;
718 inline bool Func::isPhpLeafFn() const {
719 return shared()->m_allFlags.m_isPhpLeafFn;
722 inline bool Func::hasReifiedGenerics() const {
723 return shared()->m_allFlags.m_hasReifiedGenerics;
726 ///////////////////////////////////////////////////////////////////////////////
727 // Unit table entries.
729 inline const Func::EHEntVec& Func::ehtab() const {
730 return shared()->m_ehtab;
733 inline const EHEnt* Func::findEH(Offset o) const {
734 assertx(o >= 0 && o < bclen());
735 return findEH(shared()->m_ehtab, o);
738 template<class Container>
739 const typename Container::value_type*
740 Func::findEH(const Container& ehtab, Offset o) {
741 const typename Container::value_type* eh = nullptr;
743 for (uint32_t i = 0, sz = ehtab.size(); i < sz; ++i) {
744 if (ehtab[i].m_base <= o && o < ehtab[i].m_past) {
745 eh = &ehtab[i];
748 return eh;
751 ///////////////////////////////////////////////////////////////////////////////
752 // JIT data.
754 inline rds::Handle Func::funcHandle() const {
755 return m_cachedFunc.handle();
758 inline unsigned char* Func::getFuncBody() const {
759 return m_funcBody;
762 inline void Func::setFuncBody(unsigned char* fb) {
763 m_funcBody = fb;
766 inline uint8_t* Func::getPrologue(int index) const {
767 return m_prologueTable[index];
770 inline void Func::setPrologue(int index, unsigned char* tca) {
771 m_prologueTable[index] = tca;
774 ///////////////////////////////////////////////////////////////////////////////
775 // Other methods.
777 inline int8_t& Func::maybeIntercepted() const {
778 return m_maybeIntercepted;
781 ///////////////////////////////////////////////////////////////////////////////
782 // Public setters.
784 inline void Func::setAttrs(Attr attrs) {
785 m_attrs = attrs;
788 inline void Func::setBaseCls(Class* baseCls) {
789 m_baseCls = to_low(baseCls);
792 inline void Func::setFuncHandle(rds::Link<LowPtr<Func>,
793 rds::Mode::NonLocal> l) {
794 // TODO(#2950356): This assertion fails for create_function with an existing
795 // declared function named __lambda_func.
796 //assertx(!m_cachedFunc.valid());
797 m_cachedFunc = l;
800 inline void Func::setHasPrivateAncestor(bool b) {
801 m_hasPrivateAncestor = b;
804 inline void Func::setMethodSlot(Slot s) {
805 assertx(isMethod());
806 m_methodSlot = s;
809 inline bool Func::serialize() const {
810 if (m_serialized) return false;
811 const_cast<Func*>(this)->m_serialized = true;
812 return true;
815 //////////////////////////////////////////////////////////////////////
817 inline const Func::ExtendedSharedData* Func::extShared() const {
818 return const_cast<Func*>(this)->extShared();
821 inline Func::ExtendedSharedData* Func::extShared() {
822 auto const s = shared();
823 return UNLIKELY(s->m_allFlags.m_hasExtendedSharedData)
824 ? static_cast<ExtendedSharedData*>(s)
825 : nullptr;
828 ///////////////////////////////////////////////////////////////////////////////