2 +----------------------------------------------------------------------+
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 #include "hphp/runtime/vm/func.h"
19 #include "hphp/runtime/base/attr.h"
20 #include "hphp/runtime/ext/extension.h"
21 #include "hphp/runtime/base/autoload-handler.h"
22 #include "hphp/runtime/base/builtin-functions.h"
23 #include "hphp/runtime/base/execution-context.h"
24 #include "hphp/runtime/base/init-fini-node.h"
25 #include "hphp/runtime/base/intercept.h"
26 #include "hphp/runtime/base/runtime-option.h"
27 #include "hphp/runtime/base/static-string-table.h"
28 #include "hphp/runtime/base/string-data.h"
29 #include "hphp/runtime/base/type-string.h"
30 #include "hphp/runtime/server/memory-stats.h"
31 #include "hphp/runtime/vm/as-shared.h"
32 #include "hphp/runtime/vm/class.h"
33 #include "hphp/runtime/vm/cti.h"
34 #include "hphp/runtime/vm/func-emitter.h"
35 #include "hphp/runtime/vm/reified-generics.h"
36 #include "hphp/runtime/vm/repo-file.h"
37 #include "hphp/runtime/vm/repo-global-data.h"
38 #include "hphp/runtime/vm/reverse-data-map.h"
39 #include "hphp/runtime/vm/runtime.h"
40 #include "hphp/runtime/vm/source-location.h"
41 #include "hphp/runtime/vm/treadmill.h"
42 #include "hphp/runtime/vm/type-constraint.h"
43 #include "hphp/runtime/vm/unit.h"
44 #include "hphp/runtime/vm/unit-util.h"
46 #include "hphp/runtime/vm/jit/mcgen.h"
47 #include "hphp/runtime/vm/jit/tc.h"
48 #include "hphp/runtime/vm/jit/types.h"
50 #include "hphp/system/systemlib.h"
52 #include "hphp/util/atomic-vector.h"
53 #include "hphp/util/fixed-vector.h"
54 #include "hphp/util/functional.h"
55 #include "hphp/util/struct-log.h"
56 #include "hphp/util/trace.h"
66 ///////////////////////////////////////////////////////////////////////////////
70 std::atomic
<bool> Func::s_treadmill
;
75 * FuncId high water mark and FuncId -> Func* table.
76 * We can't start with 0 since that's used for special sentinel value
79 static std::atomic
<FuncId::Int
> s_nextFuncId
{1};
81 AtomicLowPtrVector
<const Func
> Func::s_funcVec
{0, nullptr};
82 static InitFiniNode
s_funcVecReinit([]{
83 UnsafeReinitEmptyAtomicLowPtrVector(
84 Func::s_funcVec
, RuntimeOption::EvalFuncCountHint
);
85 }, InitFiniNode::When::PostRuntimeOptions
, "s_funcVec reinit");
89 inline int numProloguesForNumParams(int numParams
) {
90 // The number of prologues is numParams + 2. The extra 2 are needed for
91 // the following cases:
92 // - arguments passed > numParams
93 // - no arguments passed
98 ///////////////////////////////////////////////////////////////////////////////
99 // Creation and destruction.
101 Func::Func(Unit
& unit
, const StringData
* name
, Attr attrs
)
104 , m_hasPrivateAncestor(false)
105 , m_shouldSampleJit(StructuredLog::coinflip(RuntimeOption::EvalJitSampleRate
))
106 , m_hasForeignThis(false)
107 , m_registeredInDataMap(false)
115 Unit
& unit
, const StringData
* name
, Attr attrs
,
116 const StringData
*methCallerCls
, const StringData
*methCallerMeth
)
118 , m_methCallerMethName(to_low(methCallerMeth
, kMethCallerBit
))
121 , m_hasPrivateAncestor(false)
122 , m_shouldSampleJit(StructuredLog::coinflip(RuntimeOption::EvalJitSampleRate
))
123 , m_hasForeignThis(false)
124 , m_registeredInDataMap(false)
129 assertx(methCallerCls
!= nullptr);
130 assertx(methCallerMeth
!= nullptr);
134 // Should've deregistered in Func::destroy() or Func::freeClone()
135 assertx(!m_registeredInDataMap
);
142 void* Func::allocFuncMem(int numParams
) {
143 int numPrologues
= numProloguesForNumParams(numParams
);
145 auto const funcSize
=
146 sizeof(Func
) + numPrologues
* sizeof(m_prologueTable
[0])
147 - sizeof(m_prologueTable
);
149 MemoryStats::LogAlloc(AllocKind::Func
, funcSize
);
150 return lower_malloc(funcSize
);
153 void Func::destroy(Func
* func
) {
154 if (jit::mcgen::initialized() && RuntimeOption::EvalEnableReusableTC
) {
155 // Free TC-space associated with func
156 jit::tc::reclaimFunction(func
);
159 if (func
->m_registeredInDataMap
) {
160 func
->deregisterInDataMap();
164 if (!func
->m_funcId
.isInvalid()) {
165 assertx(s_funcVec
.get(func
->m_funcId
.toInt()) == func
);
166 s_funcVec
.set(func
->m_funcId
.toInt(), nullptr);
167 func
->m_funcId
= {FuncId::Invalid
};
171 if (s_treadmill
.load(std::memory_order_acquire
)) {
172 Treadmill::enqueue([func
](){
183 void Func::freeClone() {
184 assertx(isPreFunc());
185 assertx(m_cloned
.flag
.test_and_set());
187 if (jit::mcgen::initialized() && RuntimeOption::EvalEnableReusableTC
) {
188 // Free TC-space associated with func
189 jit::tc::reclaimFunction(this);
192 if (m_registeredInDataMap
) {
193 deregisterInDataMap();
197 if (!m_funcId
.isInvalid()) {
198 assertx(s_funcVec
.get(m_funcId
.toInt()) == this);
199 s_funcVec
.set(m_funcId
.toInt(), nullptr);
200 m_funcId
= {FuncId::Invalid
};
204 m_cloned
.flag
.clear();
207 Func
* Func::clone(Class
* cls
, const StringData
* name
) const {
208 auto numParams
= this->numParams();
210 // If this is a PreFunc (i.e., a Func on a PreClass) that is not already
211 // being used as a regular Func by a Class, and we aren't trying to change
212 // its name (since the name is part of the template for later clones), we can
213 // reuse this same Func as the clone.
214 bool const can_reuse
=
215 m_isPreFunc
&& !name
&& !m_cloned
.flag
.test_and_set();
218 ? new (allocFuncMem(numParams
)) Func(*this)
219 : const_cast<Func
*>(this);
221 f
->m_cloned
.flag
.test_and_set();
222 f
->initPrologues(numParams
);
223 f
->m_funcEntry
= nullptr;
224 if (name
) f
->m_name
= name
;
226 f
->setFullName(numParams
);
229 f
->m_isPreFunc
= false;
230 f
->m_registeredInDataMap
= false;
234 f
->m_funcId
= {FuncId::Invalid
};
240 void Func::rescope(Class
* ctx
) {
242 setFullName(numParams());
245 ///////////////////////////////////////////////////////////////////////////////
248 void Func::init(int numParams
) {
253 // For methods, we defer setting the full name until m_cls is initialized
255 setFullName(numParams
);
257 m_fullName
= nullptr;
259 if (isSpecial(m_name
)) {
261 * We dont want these compiler generated functions to
262 * appear in backtraces.
264 m_attrs
= m_attrs
| AttrNoInjection
;
267 initPrologues(numParams
);
270 void Func::initPrologues(int numParams
) {
271 int numPrologues
= numProloguesForNumParams(numParams
);
273 if (!jit::mcgen::initialized()) {
274 for (int i
= 0; i
< numPrologues
; i
++) {
275 m_prologueTable
[i
] = nullptr;
280 auto const& stubs
= jit::tc::ustubs();
282 TRACE(4, "initPrologues func %p %d\n", this, numPrologues
);
283 for (int i
= 0; i
< numPrologues
; i
++) {
284 m_prologueTable
[i
] = stubs
.fcallHelperThunk
;
288 void Func::setFullName(int /*numParams*/) {
289 assertx(m_name
->isStatic());
290 Class
*clazz
= cls();
292 m_fullName
= (StringData
*)kNeedsFullName
;
294 m_fullName
= m_name
.get();
296 // A scoped closure may not have a `cls', but we still need to preserve its
297 // `methodSlot', which refers to its slot in its `baseCls' (which still
298 // points to a subclass of Closure).
300 setNamedEntity(NamedEntity::get(m_name
));
305 /* This function is expected to be called after all calls to appendParam
306 * are complete. After, m_paramCounts is initialized such that the least
307 * significant bit of this->m_paramCounts indicates whether the last param
308 * is (non)variadic; and the rest of the bits are the number of params.
310 void Func::finishedEmittingParams(std::vector
<ParamInfo
>& fParams
) {
311 assertx(m_paramCounts
== 0);
312 assertx(m_inoutBits
== 0);
314 // Initialize m_paramCounts.
315 shared()->m_params
= fParams
;
316 m_paramCounts
= fParams
.size() << 1;
317 if (!(m_attrs
& AttrVariadicParam
)) {
320 assertx(numParams() == fParams
.size());
322 // Build m_inoutBits.
323 for (auto i
= 0u; i
< fParams
.size(); ++i
) {
324 if (LIKELY(!fParams
[i
].isInOut())) continue;
325 m_inoutBits
|= 1u << std::min(i
, kInoutFastCheckBits
);
329 void Func::registerInDataMap() {
331 assertx(!m_funcId
.isInvalid());
333 assertx((!m_isPreFunc
|| m_cloned
.flag
.test_and_set()));
334 assertx(!m_registeredInDataMap
);
335 assertx(mallocEnd());
336 data_map::register_start(this);
337 m_registeredInDataMap
= true;
340 void Func::deregisterInDataMap() {
341 assertx(m_registeredInDataMap
);
342 assertx((!m_isPreFunc
|| m_cloned
.flag
.test_and_set()));
344 assertx(!m_funcId
.isInvalid());
346 data_map::deregister(this);
347 m_registeredInDataMap
= false;
350 bool Func::isMemoizeImplName(const StringData
* name
) {
351 return name
->size() > 13 && !memcmp(name
->data() + name
->size() - 13,
352 "$memoize_impl", 13);
355 const StringData
* Func::genMemoizeImplName(const StringData
* origName
) {
356 return makeStaticString(folly::sformat("{}$memoize_impl", origName
->data()));
359 std::pair
<const StringData
*, const StringData
*> Func::getMethCallerNames(
360 const StringData
* name
) {
361 assertx(name
->size() > 11 && !memcmp(name
->data(), "MethCaller$", 11));
362 auto clsMethName
= name
->slice();
363 clsMethName
.uncheckedAdvance(11);
364 auto const sep
= folly::qfind(clsMethName
, folly::StringPiece("$"));
365 assertx(sep
!= std::string::npos
);
366 auto cls
= clsMethName
.uncheckedSubpiece(0, sep
);
367 auto meth
= clsMethName
.uncheckedSubpiece(sep
+ 1);
368 return std::make_pair(makeStaticString(cls
), makeStaticString(meth
));
371 ///////////////////////////////////////////////////////////////////////////////
372 // FuncId manipulation.
374 FuncId::Int
Func::maxFuncIdNum() {
375 return s_nextFuncId
.load(std::memory_order_relaxed
);
379 void Func::setNewFuncId() {
380 s_nextFuncId
.fetch_add(1, std::memory_order_relaxed
);
383 const Func
* Func::fromFuncId(FuncId id
) {
384 auto const func
= id
.getFunc();
389 bool Func::isFuncIdValid(FuncId id
) {
390 return !id
.isInvalid() && !id
.isDummy();
393 void Func::setNewFuncId() {
394 assertx(m_funcId
.isInvalid());
395 m_funcId
= {s_nextFuncId
.fetch_add(1, std::memory_order_relaxed
)};
397 s_funcVec
.ensureSize(m_funcId
.toInt() + 1);
398 assertx(s_funcVec
.get(m_funcId
.toInt()) == nullptr);
399 s_funcVec
.set(m_funcId
.toInt(), this);
402 const Func
* Func::fromFuncId(FuncId id
) {
403 assertx(id
.toInt() < s_nextFuncId
);
404 auto const func
= s_funcVec
.get(id
.toInt());
409 bool Func::isFuncIdValid(FuncId id
) {
410 if (id
.toInt() >= s_nextFuncId
) return false;
411 return s_funcVec
.get(id
.toInt()) != nullptr;
416 ///////////////////////////////////////////////////////////////////////////////
419 bool Func::isEntry(Offset offset
) const {
420 return offset
== 0 || isDVEntry(offset
);
423 bool Func::isDVEntry(Offset offset
) const {
424 auto const nparams
= numNonVariadicParams();
425 for (int i
= 0; i
< nparams
; i
++) {
426 const ParamInfo
& pi
= params()[i
];
427 if (pi
.hasDefaultValue() && pi
.funcletOff
== offset
) return true;
432 int Func::getEntryNumParams(Offset offset
) const {
433 if (offset
== 0) return numNonVariadicParams();
434 return getDVEntryNumParams(offset
);
437 int Func::getDVEntryNumParams(Offset offset
) const {
438 auto const nparams
= numNonVariadicParams();
439 for (int i
= 0; i
< nparams
; i
++) {
440 const ParamInfo
& pi
= params()[i
];
441 if (pi
.hasDefaultValue() && pi
.funcletOff
== offset
) return i
;
446 Offset
Func::getEntryForNumArgs(int numArgsPassed
) const {
447 assertx(numArgsPassed
>= 0);
448 auto const nparams
= numNonVariadicParams();
449 for (unsigned i
= numArgsPassed
; i
< nparams
; i
++) {
450 const Func::ParamInfo
& pi
= params()[i
];
451 if (pi
.hasDefaultValue()) {
452 return pi
.funcletOff
;
459 ///////////////////////////////////////////////////////////////////////////////
462 bool Func::isInOut(int32_t arg
) const {
464 if (LIKELY(arg
< kInoutFastCheckBits
)) {
465 return m_inoutBits
& (1ull << arg
);
468 if (arg
>= numParams()) return false;
469 return params()[arg
].isInOut();
472 bool Func::isReadonly(int32_t arg
) const {
474 if (arg
>= numParams()) return false;
475 return params()[arg
].isReadonly();
478 uint32_t Func::numInOutParams() const {
479 uint32_t count
= folly::popcount(m_inoutBits
);
480 if (LIKELY(static_cast<int32_t>(m_inoutBits
) >= 0)) {
481 // The sign bit is not set, so only args 0..31 can possibly be inout.
485 assertx(numParams() > kInoutFastCheckBits
);
486 for (auto i
= numParams() - 1; i
>= kInoutFastCheckBits
; --i
) {
487 count
+= params()[i
].isInOut() ? 1 : 0;
489 // Minus one for the sign bit.
493 uint32_t Func::numInOutParamsForArgs(int32_t numArgs
) const {
494 if (!takesInOutParams()) return 0;
496 for (int p
= 0; p
< numArgs
; ++p
) i
+= isInOut(p
);
500 ///////////////////////////////////////////////////////////////////////////////
501 // Locals, iterators, and stack.
503 Id
Func::lookupVarId(const StringData
* name
) const {
504 assertx(name
!= nullptr);
505 return shared()->m_localNames
.findIndex(name
);
508 uint32_t Func::numClosureUseLocals() const {
509 assertx(isClosureBody());
510 auto const cls
= implCls();
511 return cls
->numDeclProperties() - (cls
->hasClosureCoeffectsProp() ? 1 : 0);
514 ///////////////////////////////////////////////////////////////////////////////
517 bool Func::isImmutableFrom(const Class
* cls
) const {
518 if (!RuntimeOption::RepoAuthoritative
) return false;
519 assertx(cls
&& cls
->lookupMethod(name()) == this);
520 if (attrs() & AttrNoOverride
) {
523 if (cls
->preClass()->attrs() & AttrNoOverride
) {
529 ///////////////////////////////////////////////////////////////////////////////
532 int Func::numPrologues() const {
533 return numProloguesForNumParams(numParams());
536 void Func::resetPrologue(int numParams
) {
537 auto const& stubs
= jit::tc::ustubs();
538 m_prologueTable
[numParams
] = stubs
.fcallHelperThunk
;
541 void Func::resetFuncEntry() {
542 m_funcEntry
= nullptr;
545 ///////////////////////////////////////////////////////////////////////////////
549 const ReifiedGenericsInfo k_defaultReifiedGenericsInfo
{0, false, 0, {}};
552 const ReifiedGenericsInfo
& Func::getReifiedGenericsInfo() const {
553 if (!shared()->m_allFlags
.m_hasReifiedGenerics
) return k_defaultReifiedGenericsInfo
;
554 auto const ex
= extShared();
556 return ex
->m_reifiedGenericsInfo
;
559 ///////////////////////////////////////////////////////////////////////////////
562 void Func::print_attrs(std::ostream
& out
, Attr attrs
) {
563 if (attrs
& AttrStatic
) { out
<< " static"; }
564 if (attrs
& AttrPublic
) { out
<< " public"; }
565 if (attrs
& AttrProtected
) { out
<< " protected"; }
566 if (attrs
& AttrPrivate
) { out
<< " private"; }
567 if (attrs
& AttrInternal
) { out
<< " internal"; }
568 if (attrs
& AttrAbstract
) { out
<< " abstract"; }
569 if (attrs
& AttrFinal
) { out
<< " final"; }
570 if (attrs
& AttrNoOverride
){ out
<< " (nooverride)"; }
571 if (attrs
& AttrInterceptable
) { out
<< " (interceptable)"; }
572 if (attrs
& AttrPersistent
) { out
<< " (persistent)"; }
573 if (attrs
& AttrBuiltin
) { out
<< " (builtin)"; }
574 if (attrs
& AttrIsFoldable
) { out
<< " (foldable)"; }
575 if (attrs
& AttrNoInjection
) { out
<< " (no_injection)"; }
576 if (attrs
& AttrSupportsAsyncEagerReturn
) { out
<< " (can_async_eager_ret)"; }
577 if (attrs
& AttrDynamicallyCallable
) { out
<< " (dyn_callable)"; }
578 if (attrs
& AttrIsMethCaller
) { out
<< " (is_meth_caller)"; }
581 void Func::prettyPrint(std::ostream
& out
, const PrintOpts
& opts
) const {
583 if (preClass() != nullptr) {
585 print_attrs(out
, m_attrs
);
586 if (isPhpLeafFn()) out
<< " (leaf)";
587 if (isMemoizeWrapper()) out
<< " (memoize_wrapper)";
588 if (isMemoizeWrapperLSB()) out
<< " (memoize_wrapper_lsb)";
589 if (cls() != nullptr) {
590 out
<< ' ' << fullName()->data();
592 out
<< ' ' << preClass()->name()->data() << "::" << m_name
->data();
596 print_attrs(out
, m_attrs
);
597 if (isPhpLeafFn()) out
<< " (leaf)";
598 if (isMemoizeWrapper()) out
<< " (memoize_wrapper)";
599 if (isMemoizeWrapperLSB()) out
<< " (memoize_wrapper_lsb)";
600 out
<< ' ' << m_name
->data();
607 const ParamInfoVec
& params
= shared()->m_params
;
608 for (uint32_t i
= 0; i
< params
.size(); ++i
) {
609 auto const& param
= params
[i
];
610 out
<< " Param: " << localVarName(i
)->data();
611 if (param
.typeConstraint
.hasConstraint()) {
612 out
<< " " << param
.typeConstraint
.displayName(cls(), true);
614 if (param
.userType
) {
615 out
<< " (" << param
.userType
->data() << ")";
617 if (param
.funcletOff
!= kInvalidOffset
) {
618 out
<< " DV" << " at " << param
.funcletOff
;
620 out
<< " = " << param
.phpCode
->data();
626 if (returnTypeConstraint().hasConstraint() ||
627 (returnUserType() && !returnUserType()->empty())) {
629 if (returnTypeConstraint().hasConstraint()) {
630 out
<< " " << returnTypeConstraint().displayName(cls(), true);
632 if (returnUserType() && !returnUserType()->empty()) {
633 out
<< " (" << returnUserType()->data() << ")";
638 if (repoReturnType().tag() != RepoAuthType::Tag::Cell
) {
639 out
<< "repoReturnType: " << show(repoReturnType()) << '\n';
641 if (repoAwaitedReturnType().tag() != RepoAuthType::Tag::Cell
) {
642 out
<< "repoAwaitedReturnType: " << show(repoAwaitedReturnType()) << '\n';
644 out
<< "maxStackCells: " << maxStackCells() << '\n'
645 << "numLocals: " << numLocals() << '\n'
646 << "numIterators: " << numIterators() << '\n';
648 const EHEntVec
& ehtab
= shared()->m_ehtab
;
650 for (auto it
= ehtab
.begin(); it
!= ehtab
.end(); ++it
, ++ehId
) {
651 out
<< " EH " << ehId
<< " Catch for " <<
652 it
->m_base
<< ":" << it
->m_past
;
653 if (it
->m_parentIndex
!= -1) {
654 out
<< " outer EH " << it
->m_parentIndex
;
656 if (it
->m_iterId
!= -1) {
657 out
<< " iterId " << it
->m_iterId
;
659 out
<< " handle at " << it
->m_handler
;
660 if (it
->m_end
!= kInvalidOffset
) {
661 out
<< ":" << it
->m_end
;
663 if (it
->m_parentIndex
!= -1) {
664 out
<< " parentIndex " << it
->m_parentIndex
;
670 if (opts
.startOffset
!= kInvalidOffset
) {
671 auto startOffset
= std::max(0, opts
.startOffset
);
672 auto stopOffset
= std::min(bclen(), opts
.stopOffset
);
674 if (startOffset
>= stopOffset
) {
678 auto it
= at(startOffset
);
679 auto const stop
= at(stopOffset
);
680 int prevLineNum
= -1;
682 if (opts
.showLines
) {
684 SourceLocation::getLineNumber(getOrLoadLineTable(), offsetOf(it
));
685 if (lineNum
!= prevLineNum
) {
686 out
<< " // line " << lineNum
<< std::endl
;
687 prevLineNum
= lineNum
;
691 out
<< std::string(opts
.indentSize
, ' ')
692 << std::setw(4) << offsetOf(it
) << ": "
693 << instrToString(it
, this)
701 ///////////////////////////////////////////////////////////////////////////////
704 Func::SharedData::SharedData(BCPtr bc
, Offset bclen
,
705 PreClass
* preClass
, int sn
, int line1
,
706 int line2
, bool isPhpLeafFn
)
707 : m_bc(bc
.isPtr() ? BCPtr::FromPtr(allocateBCRegion(bc
.ptr(), bclen
)) : bc
)
708 , m_preClass(preClass
)
710 , m_originalFilename(nullptr)
715 m_allFlags
.m_isClosureBody
= false;
716 m_allFlags
.m_isAsync
= false;
717 m_allFlags
.m_isGenerator
= false;
718 m_allFlags
.m_isPairGenerator
= false;
719 m_allFlags
.m_isGenerated
= false;
720 m_allFlags
.m_hasExtendedSharedData
= false;
721 m_allFlags
.m_returnByValue
= false;
722 m_allFlags
.m_isMemoizeWrapper
= false;
723 m_allFlags
.m_isMemoizeWrapperLSB
= false;
724 m_allFlags
.m_memoizeICType
= Func::MemoizeICType::NoIC
;
725 m_allFlags
.m_isPhpLeafFn
= isPhpLeafFn
;
726 m_allFlags
.m_hasReifiedGenerics
= false;
727 m_allFlags
.m_hasParamsWithMultiUBs
= false;
728 m_allFlags
.m_hasReturnWithMultiUBs
= false;
730 m_bclenSmall
= std::min
<uint32_t>(bclen
, kSmallDeltaLimit
);
731 m_line2Delta
= std::min
<uint32_t>(line2
- line1
, kSmallDeltaLimit
);
732 m_sn
= std::min
<uint32_t>(sn
, kSmallDeltaLimit
);
735 Func::SharedData::~SharedData() {
736 if (auto bc
= m_bc
.copy(); bc
.isPtr()) {
737 freeBCRegion(bc
.ptr(), bclen());
739 if (auto table
= m_lineTable
.copy(); table
.isPtr()) {
742 Func::s_extendedLineInfo
.erase(this);
743 if (m_cti_base
) free_cti(m_cti_base
, m_cti_size
);
746 void Func::SharedData::atomicRelease() {
747 if (UNLIKELY(m_allFlags
.m_hasExtendedSharedData
)) {
748 delete (ExtendedSharedData
*)this;
754 Func::ExtendedSharedData::~ExtendedSharedData() {
757 ///////////////////////////////////////////////////////////////////////////////
759 void logFunc(const Func
* func
, StructuredLogEntry
& ent
) {
760 auto const attrs
= attrs_to_vec(AttrContext::Func
, func
->attrs());
761 std::set
<folly::StringPiece
> attrSet(attrs
.begin(), attrs
.end());
763 if (func
->isMemoizeWrapper()) attrSet
.emplace("memoize_wrapper");
764 if (func
->isMemoizeWrapperLSB()) attrSet
.emplace("memoize_wrapper_lsb");
765 if (func
->isMemoizeImpl()) attrSet
.emplace("memoize_impl");
766 if (func
->isAsync()) attrSet
.emplace("async");
767 if (func
->isGenerator()) attrSet
.emplace("generator");
768 if (func
->isClosureBody()) attrSet
.emplace("closure_body");
769 if (func
->isPairGenerator()) attrSet
.emplace("pair_generator");
770 if (func
->hasVariadicCaptureParam()) attrSet
.emplace("variadic_param");
771 if (func
->isPhpLeafFn()) attrSet
.emplace("leaf_function");
772 if (func
->cls() && func
->cls()->isPersistent()) attrSet
.emplace("persistent");
774 ent
.setSet("func_attributes", attrSet
);
776 ent
.setInt("num_params", func
->numNonVariadicParams());
777 ent
.setInt("num_locals", func
->numLocals());
778 ent
.setInt("num_iterators", func
->numIterators());
779 ent
.setInt("frame_cells", func
->numSlotsInFrame());
780 ent
.setInt("max_stack_cells", func
->maxStackCells());
783 ///////////////////////////////////////////////////////////////////////////////
786 const StaticString
s_DebuggerMain("__DebuggerMain");
788 void Func::def(Func
* func
) {
789 assertx(!func
->isMethod());
791 // Don't define the __debugger_main() function
792 DEBUGGER_ATTACHED_ONLY(if (func
->userAttributes().count(s_DebuggerMain
.get())) { return; });
794 auto const ne
= func
->getNamedEntity();
796 Func
* f
= ne
->getCachedFunc();
798 std::unique_lock
<Mutex
> l(g_funcsMutex
);
799 f
= ne
->getCachedFunc();
801 auto const persistent
= func
->isPersistent();
802 assertx(!persistent
|| (RuntimeOption::RepoAuthoritative
|| !SystemLib::s_inited
));
804 if (!ne
->m_cachedFunc
.bound()) {
805 ne
->m_cachedFunc
.bind(
806 persistent
? rds::Mode::Persistent
: rds::Mode::Normal
,
807 rds::LinkName
{"Func", func
->name()}
810 ne
->m_cachedFunc
.initWith(func
);
813 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefFuncHook(func
));
817 // If the function didn't exists before we are good
822 // Otherwise check if we match or show the right error message
824 assertx(!RO::RepoAuthoritative
||
825 (f
->isPersistent() && ne
->m_cachedFunc
.isPersistent()));
828 if (func
->attrs() & AttrIsMethCaller
) return;
829 if (f
->isBuiltin()) {
830 assertx(!func
->isBuiltin());
831 raise_error(Strings::REDECLARE_BUILTIN
, func
->name()->data());
833 raise_error(Strings::FUNCTION_ALREADY_DEFINED
, func
->name()->data());
836 Func
* Func::lookup(const NamedEntity
* ne
) {
837 return ne
->getCachedFunc();
840 Func
* Func::lookup(const StringData
* name
) {
841 const NamedEntity
* ne
= NamedEntity::get(name
);
842 return ne
->getCachedFunc();
845 Func
* Func::lookupBuiltin(const StringData
* name
) {
846 // Builtins are either persistent (the normal case), or defined at the
847 // beginning of every request (if JitEnableRenameFunction or interception is
848 // enabled). In either case, they're unique, so they should be present in the
850 auto const ne
= NamedEntity::get(name
);
851 auto const f
= ne
->getCachedFunc();
852 return (f
&& f
->isUnique() && f
->isBuiltin()) ? f
: nullptr;
855 Func
* Func::load(const NamedEntity
* ne
, const StringData
* name
) {
856 Func
* func
= ne
->getCachedFunc();
857 if (LIKELY(func
!= nullptr)) return func
;
858 if (AutoloadHandler::s_instance
->autoloadFunc(
859 const_cast<StringData
*>(name
))) {
860 func
= ne
->getCachedFunc();
865 Func
* Func::load(const StringData
* name
) {
867 auto ne
= NamedEntity::get(name
, true, &normStr
);
869 // Try to fetch from cache
870 Func
* func_
= ne
->getCachedFunc();
871 if (LIKELY(func_
!= nullptr)) return func_
;
873 // Normalize the namespace
875 name
= normStr
.get();
878 // Autoload the function
879 return AutoloadHandler::s_instance
->autoloadFunc(
880 const_cast<StringData
*>(name
)
881 ) ? ne
->getCachedFunc() : nullptr;
885 void handleModuleBoundaryViolation(const Func
* callee
, const Func
* caller
) {
886 if (!RO::EvalEnforceModules
|| !callee
|| !caller
) return;
887 if (will_symbol_raise_module_boundary_violation(callee
, caller
)) {
888 raiseModuleBoundaryViolation(nullptr, callee
, caller
->moduleName());
893 Func
* Func::resolve(const NamedEntity
* ne
, const StringData
* name
,
894 const Func
* callerFunc
) {
895 Func
* func
= load(ne
, name
);
896 handleModuleBoundaryViolation(func
, callerFunc
);
900 Func
* Func::resolve(const StringData
* name
, const Func
* callerFunc
) {
901 Func
* func
= load(name
);
902 handleModuleBoundaryViolation(func
, callerFunc
);
906 ///////////////////////////////////////////////////////////////////////////////
909 Func::ExtendedLineInfoCache
Func::s_extendedLineInfo
;
911 void Func::setLineTable(LineTable lineTable
) {
912 auto& table
= shared()->m_lineTable
;
913 auto lock
= table
.lock_for_update();
914 assertx(table
.copy().isPtr() && !table
.copy().ptr());
916 LineTablePtr::FromPtr(new LineTable
{std::move(lineTable
)})
920 void Func::setLineTable(LineTablePtr::Token token
) {
921 assertx(RO::RepoAuthoritative
);
922 auto& table
= shared()->m_lineTable
;
923 auto lock
= table
.lock_for_update();
924 assertx(table
.copy().isPtr() && !table
.copy().ptr());
925 lock
.update(LineTablePtr::FromToken(token
));
928 void Func::stashExtendedLineTable(SourceLocTable table
) const {
929 ExtendedLineInfoCache::accessor acc
;
930 if (s_extendedLineInfo
.insert(acc
, shared())) {
931 acc
->second
.sourceLocTable
= std::move(table
);
936 * Return the Unit's SourceLocTable, extracting it from the repo if
939 const SourceLocTable
& Func::getLocTable() const {
940 auto const sharedData
= shared();
942 ExtendedLineInfoCache::const_accessor acc
;
943 if (s_extendedLineInfo
.find(acc
, sharedData
)) {
944 return acc
->second
.sourceLocTable
;
947 static SourceLocTable empty
;
952 * Return a copy of the Func's line to OffsetRangeVec table.
954 LineToOffsetRangeVecMap
Func::getLineToOffsetRangeVecMap() const {
955 auto const sharedData
= shared();
957 ExtendedLineInfoCache::const_accessor acc
;
958 if (s_extendedLineInfo
.find(acc
, sharedData
)) {
959 if (!acc
->second
.lineToOffsetRange
.empty()) {
960 return acc
->second
.lineToOffsetRange
;
965 LineToOffsetRangeVecMap map
;
966 auto const& srcLocTable
= getLocTable();
967 SourceLocation::generateLineToOffsetRangesMap(srcLocTable
, map
);
969 ExtendedLineInfoCache::accessor acc
;
970 if (!s_extendedLineInfo
.find(acc
, sharedData
)) {
971 always_assert_flog(0, "ExtendedLineInfoCache was not found when it should "
974 if (acc
->second
.lineToOffsetRange
.empty()) {
975 acc
->second
.lineToOffsetRange
= std::move(map
);
977 return acc
->second
.lineToOffsetRange
;
980 const LineTable
* Func::getLineTable() const {
981 auto const table
= shared()->m_lineTable
.copy();
983 assertx(table
.ptr());
989 const LineTable
& Func::getOrLoadLineTable() const {
990 if (auto const table
= getLineTable()) return *table
;
992 assertx(RO::RepoAuthoritative
);
994 auto& wrapper
= shared()->m_lineTable
;
995 auto lock
= wrapper
.lock_for_update();
997 auto const table
= wrapper
.copy();
998 if (table
.isPtr()) return *table
.ptr();
1000 auto newTable
= new LineTable
{
1001 FuncEmitter::loadLineTableFromRepo(m_unit
->sn(), table
.token())
1003 lock
.update(LineTablePtr::FromPtr(newTable
));
1007 LineTable
Func::getOrLoadLineTableCopy() const {
1008 auto const table
= shared()->m_lineTable
.copy();
1009 if (table
.isPtr()) {
1010 assertx(table
.ptr());
1011 return *table
.ptr();
1013 assertx(RO::RepoAuthoritative
);
1014 return FuncEmitter::loadLineTableFromRepo(m_unit
->sn(), table
.token());
1017 int Func::getLineNumber(Offset offset
) const {
1018 auto const findLine
= [&] {
1019 // lineMap is an atomically acquired bitwise copy of m_lineMap,
1020 // with no destructor
1021 auto lineMap(shared()->m_lineMap
.get());
1022 if (lineMap
->empty()) return INT_MIN
;
1023 auto const it
= std::upper_bound(
1024 lineMap
->begin(), lineMap
->end(),
1026 [] (Offset info
, const LineInfo
& elm
) {
1027 return info
< elm
.first
.past
;
1030 if (it
!= lineMap
->end() && it
->first
.base
<= offset
) return it
->second
;
1034 auto line
= findLine();
1035 if (line
!= INT_MIN
) return line
;
1037 // Updating m_lineMap while coverage is enabled can cause the
1038 // treadmill to fill with an enormous number of resized maps.
1039 if (UNLIKELY(g_context
&& (m_unit
->isCoverageEnabled() ||
1040 RID().getCoverage()))) {
1041 return SourceLocation::getLineNumber(getOrLoadLineTable(), offset
);
1044 auto lock
= shared()->m_lineMap
.lock_for_update();
1046 if (line
!= INT_MIN
) return line
;
1048 auto const info
= SourceLocation::getLineInfo(getOrLoadLineTable(), offset
);
1049 auto copy
= shared()->m_lineMap
.copy();
1050 auto const it
= std::upper_bound(
1051 copy
.begin(), copy
.end(),
1053 [&] (const LineInfo
& a
, const LineInfo
& b
) {
1054 return a
.first
.base
< b
.first
.past
;
1057 assertx(it
== copy
.end() ||
1058 (it
->first
.past
> offset
&& it
->first
.base
> offset
));
1059 copy
.insert(it
, info
);
1060 auto old
= lock
.update(std::move(copy
));
1061 Treadmill::enqueue([old
= std::move(old
)] () mutable { old
.clear(); });
1065 bool Func::getSourceLoc(Offset offset
, SourceLoc
& sLoc
) const {
1066 auto const& sourceLocTable
= getLocTable();
1067 return SourceLocation::getLoc(sourceLocTable
, offset
, sLoc
);
1070 bool Func::getOffsetRange(Offset offset
, OffsetRange
& range
) const {
1071 auto line
= getLineNumber(offset
);
1072 if (line
== -1) return false;
1074 auto map
= getLineToOffsetRangeVecMap();
1075 auto it
= map
.find(line
);
1076 if (it
!= map
.end()) {
1077 for (auto o
: it
->second
) {
1078 if (offset
>= o
.base
&& offset
< o
.past
) {
1087 ///////////////////////////////////////////////////////////////////////////////
1092 using BytecodeArena
= ReadOnlyArena
<VMColdAllocator
<char>, false, 8>;
1093 static BytecodeArena
& bytecode_arena() {
1094 static BytecodeArena
arena(RuntimeOption::EvalHHBCArenaChunkSize
);
1101 * Export for the admin server.
1103 size_t hhbc_arena_capacity() {
1104 if (!RuntimeOption::RepoAuthoritative
) return 0;
1105 return bytecode_arena().capacity();
1109 allocateBCRegion(const unsigned char* bc
, size_t bclen
) {
1110 g_hhbc_size
->addValue(bclen
);
1111 auto mem
= static_cast<unsigned char*>(
1112 RuntimeOption::RepoAuthoritative
? bytecode_arena().allocate(bclen
)
1114 std::copy(bc
, bc
+ bclen
, mem
);
1118 void freeBCRegion(const unsigned char* bc
, size_t bclen
) {
1119 // Can't free bytecode arena memory.
1120 if (RuntimeOption::RepoAuthoritative
) return;
1123 // poison released bytecode
1124 memset(const_cast<unsigned char*>(bc
), 0xff, bclen
);
1126 free(const_cast<unsigned char*>(bc
));
1127 g_hhbc_size
->addValue(-int64_t(bclen
));
1130 PC
Func::loadBytecode() {
1131 assertx(RO::RepoAuthoritative
);
1132 auto& wrapper
= shared()->m_bc
;
1133 auto lock
= wrapper
.lock_for_update();
1134 auto const bc
= wrapper
.copy();
1135 if (bc
.isPtr()) return bc
.ptr();
1136 auto const length
= bclen();
1137 g_hhbc_size
->addValue(length
);
1138 auto mem
= (unsigned char*)bytecode_arena().allocate(length
);
1139 RepoFile::readRawFromUnit(m_unit
->sn(), bc
.token(), mem
, length
);
1140 lock
.update(BCPtr::FromPtr(mem
));
1144 ///////////////////////////////////////////////////////////////////////////////
1148 RDS_LOCAL(uint32_t, tl_saved_coverage_index
);
1149 RDS_LOCAL_NO_CHECK(Array
, tl_called_functions
);
1150 rds::Link
<uint32_t, rds::Mode::Local
> s_coverage_index
;
1152 using CoverageLinkMap
= tbb::concurrent_hash_map
<
1154 rds::Link
<uint32_t, rds::Mode::Local
>
1157 struct EmbeddedCoverageLinkMap
{
1158 explicit operator bool() const { return inited
; }
1160 CoverageLinkMap
* operator->() {
1162 return reinterpret_cast<CoverageLinkMap
*>(&data
);
1164 CoverageLinkMap
& operator*() { assertx(inited
); return *operator->(); }
1166 void emplace(uint32_t size
) {
1168 new (&data
) CoverageLinkMap(size
);
1174 operator*().~CoverageLinkMap();
1180 typename
std::aligned_storage
<
1181 sizeof(CoverageLinkMap
),
1182 alignof(CoverageLinkMap
)
1187 EmbeddedCoverageLinkMap s_covLinks
;
1188 static InitFiniNode
s_covLinksReinit([]{
1189 if (RO::RepoAuthoritative
|| !RO::EvalEnableFuncCoverage
) return;
1190 s_covLinks
.emplace(RO::EvalFuncCountHint
);
1191 }, InitFiniNode::When::PostRuntimeOptions
, "s_funcVec reinit");
1193 InitFiniNode
s_clear_called_functions([]{
1194 tl_called_functions
.nullOut();
1195 }, InitFiniNode::When::RequestFini
, "tl_called_functions clear");
1198 rds::Handle
Func::GetCoverageIndex() {
1199 if (!s_coverage_index
.bound()) {
1200 s_coverage_index
.bind(rds::Mode::Local
, rds::LinkID
{"FuncCoverageIndex"});
1202 return s_coverage_index
.handle();
1205 rds::Handle
Func::getCoverageHandle() const {
1206 assertx(!RO::RepoAuthoritative
&& RO::EvalEnableFuncCoverage
);
1207 assertx(!isNoInjection() && !isMethCaller());
1209 CoverageLinkMap::const_accessor cnsAcc
;
1210 if (s_covLinks
->find(cnsAcc
, fullName())) {
1211 assertx(cnsAcc
->second
.bound());
1212 return cnsAcc
->second
.handle();
1215 CoverageLinkMap::accessor acc
;
1216 if (s_covLinks
->insert(acc
, fullName())) {
1217 assertx(!acc
->second
.bound());
1219 rds::Mode::Local
, rds::LinkName
{"FuncCoverageFlag", fullName()}
1222 assertx(acc
->second
.bound());
1223 return acc
->second
.handle();
1226 void Func::EnableCoverage() {
1229 if (RO::RepoAuthoritative
) {
1230 SystemLib::throwInvalidOperationExceptionObject(
1231 "Cannot enable function call coverage in repo authoritative mode"
1234 if (!RO::EvalEnableFuncCoverage
) {
1235 SystemLib::throwInvalidOperationExceptionObject(
1236 "Cannot enable function call coverage (you must set "
1237 "Eval.EnableFuncCoverage = true)"
1240 if (!tl_called_functions
.isNull()) {
1241 SystemLib::throwInvalidOperationExceptionObject(
1242 "Function call coverage already enabled"
1246 GetCoverageIndex(); // bind the handle
1247 if (!*tl_saved_coverage_index
) *tl_saved_coverage_index
= 1;
1248 *s_coverage_index
= (*tl_saved_coverage_index
)++;
1249 tl_called_functions
.emplace(Array::CreateDict());
1252 std::string
show(PrologueID pid
) {
1253 auto func
= pid
.func();
1254 return folly::sformat("{}(id 0x{:#x}) # of args: {}",
1255 func
->fullName()->data(),
1256 pid
.funcId().toInt(), pid
.nargs());
1259 Array
Func::GetCoverage() {
1260 if (tl_called_functions
.isNull()) {
1261 SystemLib::throwInvalidOperationExceptionObject(
1262 "Function call coverage not enabled"
1266 auto const ret
= std::move(*tl_called_functions
.get());
1267 *s_coverage_index
= 0;
1268 tl_called_functions
.destroy();
1272 void Func::recordCall() const {
1273 if (RO::RepoAuthoritative
|| !RO::EvalEnableFuncCoverage
) return;
1274 if (tl_called_functions
.isNull()) return;
1275 if (isNoInjection() || isMethCaller()) return;
1277 auto const path
= unit()->isSystemLib()
1279 : StrNR
{unit()->filepath()}.asString();
1281 tl_called_functions
->set(fullNameStr().asString(), std::move(path
), true);
1284 void Func::recordCallNoCheck() const {
1285 assertx(!RO::RepoAuthoritative
&& RO::EvalEnableFuncCoverage
);
1286 assertx(!tl_called_functions
.isNull());
1287 assertx(tl_called_functions
->isDict());
1288 assertx(!isNoInjection() && !isMethCaller());
1289 assertx(!tl_called_functions
->exists(fullNameStr().asString(), true));
1291 auto const path
= unit()->isSystemLib()
1293 : StrNR
{unit()->filepath()}.asString();
1295 tl_called_functions
->set(fullNameStr().asString(), std::move(path
), true);
1298 ///////////////////////////////////////////////////////////////////////////////