Use struct bit packing
[hiphop-php.git] / hphp / runtime / vm / func.cpp
blobecf1ff3f3db5304940ce1218e2ea92cca33d665d
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 #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"
58 #include <algorithm>
59 #include <atomic>
60 #include <iomanip>
61 #include <ostream>
62 #include <string>
63 #include <vector>
65 namespace HPHP {
66 ///////////////////////////////////////////////////////////////////////////////
68 TRACE_SET_MOD(hhbc);
70 std::atomic<bool> Func::s_treadmill;
72 Mutex g_funcsMutex;
75 * FuncId high water mark and FuncId -> Func* table.
76 * We can't start with 0 since that's used for special sentinel value
77 * in TreadHashMap
79 static std::atomic<FuncId::Int> s_nextFuncId{1};
80 #ifndef USE_LOWPTR
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");
86 #endif
88 namespace {
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
94 return numParams + 2;
98 ///////////////////////////////////////////////////////////////////////////////
99 // Creation and destruction.
101 Func::Func(Unit& unit, const StringData* name, Attr attrs)
102 : m_name(name)
103 , m_isPreFunc(false)
104 , m_hasPrivateAncestor(false)
105 , m_shouldSampleJit(StructuredLog::coinflip(RuntimeOption::EvalJitSampleRate))
106 , m_hasForeignThis(false)
107 , m_registeredInDataMap(false)
108 , m_unit(&unit)
109 , m_shared(nullptr)
110 , m_attrs(attrs)
114 Func::Func(
115 Unit& unit, const StringData* name, Attr attrs,
116 const StringData *methCallerCls, const StringData *methCallerMeth)
117 : m_name(name)
118 , m_methCallerMethName(to_low(methCallerMeth, kMethCallerBit))
119 , m_u(methCallerCls)
120 , m_isPreFunc(false)
121 , m_hasPrivateAncestor(false)
122 , m_shouldSampleJit(StructuredLog::coinflip(RuntimeOption::EvalJitSampleRate))
123 , m_hasForeignThis(false)
124 , m_registeredInDataMap(false)
125 , m_unit(&unit)
126 , m_shared(nullptr)
127 , m_attrs(attrs)
129 assertx(methCallerCls != nullptr);
130 assertx(methCallerMeth != nullptr);
133 Func::~Func() {
134 // Should've deregistered in Func::destroy() or Func::freeClone()
135 assertx(!m_registeredInDataMap);
136 #ifndef NDEBUG
137 validate();
138 m_magic = ~m_magic;
139 #endif
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();
163 #ifndef USE_LOWPTR
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};
169 #endif
171 if (s_treadmill.load(std::memory_order_acquire)) {
172 Treadmill::enqueue([func](){
173 func->~Func();
174 lower_free(func);
176 return;
179 func->~Func();
180 lower_free(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();
196 #ifndef USE_LOWPTR
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};
202 #endif
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();
217 Func* f = !can_reuse
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;
225 f->m_u.setCls(cls);
226 f->setFullName(numParams);
228 if (f != this) {
229 f->m_isPreFunc = false;
230 f->m_registeredInDataMap = false;
233 #ifndef USE_LOWPTR
234 f->m_funcId = {FuncId::Invalid};
235 #endif
236 f->setNewFuncId();
237 return f;
240 void Func::rescope(Class* ctx) {
241 m_u.setCls(ctx);
242 setFullName(numParams());
245 ///////////////////////////////////////////////////////////////////////////////
246 // Initialization.
248 void Func::init(int numParams) {
249 #ifndef NDEBUG
250 m_magic = kMagic;
251 #endif
252 setNewFuncId();
253 // For methods, we defer setting the full name until m_cls is initialized
254 if (!preClass()) {
255 setFullName(numParams);
256 } else {
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;
266 assertx(m_name);
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;
277 return;
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();
291 if (clazz) {
292 m_fullName = (StringData*)kNeedsFullName;
293 } else {
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).
299 if (!isMethod()) {
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)) {
318 m_paramCounts |= 1;
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() {
330 #ifndef USE_LOWPTR
331 assertx(!m_funcId.isInvalid());
332 #endif
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()));
343 #ifndef USE_LOWPTR
344 assertx(!m_funcId.isInvalid());
345 #endif
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);
378 #ifdef USE_LOWPTR
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();
385 func->validate();
386 return func;
389 bool Func::isFuncIdValid(FuncId id) {
390 return !id.isInvalid() && !id.isDummy();
392 #else
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());
405 func->validate();
406 return func;
409 bool Func::isFuncIdValid(FuncId id) {
410 if (id.toInt() >= s_nextFuncId) return false;
411 return s_funcVec.get(id.toInt()) != nullptr;
413 #endif
416 ///////////////////////////////////////////////////////////////////////////////
417 // Bytecode.
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;
429 return false;
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;
443 return -1;
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;
455 return 0;
459 ///////////////////////////////////////////////////////////////////////////////
460 // Parameters.
462 bool Func::isInOut(int32_t arg) const {
463 assertx(arg >= 0);
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 {
473 assertx(arg >= 0);
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.
482 return count;
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.
490 return count - 1;
493 uint32_t Func::numInOutParamsForArgs(int32_t numArgs) const {
494 if (!takesInOutParams()) return 0;
495 uint32_t i = 0;
496 for (int p = 0; p < numArgs; ++p) i += isInOut(p);
497 return i;
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 ///////////////////////////////////////////////////////////////////////////////
515 // Persistence.
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) {
521 return true;
523 if (cls->preClass()->attrs() & AttrNoOverride) {
524 return true;
526 return false;
529 ///////////////////////////////////////////////////////////////////////////////
530 // JIT data.
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 ///////////////////////////////////////////////////////////////////////////////
546 // Reified Generics
548 namespace {
549 const ReifiedGenericsInfo k_defaultReifiedGenericsInfo{0, false, 0, {}};
550 } // namespace
552 const ReifiedGenericsInfo& Func::getReifiedGenericsInfo() const {
553 if (!shared()->m_allFlags.m_hasReifiedGenerics) return k_defaultReifiedGenericsInfo;
554 auto const ex = extShared();
555 assertx(ex);
556 return ex->m_reifiedGenericsInfo;
559 ///////////////////////////////////////////////////////////////////////////////
560 // Pretty printer.
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 {
582 if (opts.name) {
583 if (preClass() != nullptr) {
584 out << "Method";
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();
591 } else {
592 out << ' ' << preClass()->name()->data() << "::" << m_name->data();
594 } else {
595 out << "Function";
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();
603 out << std::endl;
606 if (opts.metadata) {
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;
619 if (param.phpCode) {
620 out << " = " << param.phpCode->data();
623 out << std::endl;
626 if (returnTypeConstraint().hasConstraint() ||
627 (returnUserType() && !returnUserType()->empty())) {
628 out << " Ret: ";
629 if (returnTypeConstraint().hasConstraint()) {
630 out << " " << returnTypeConstraint().displayName(cls(), true);
632 if (returnUserType() && !returnUserType()->empty()) {
633 out << " (" << returnUserType()->data() << ")";
635 out << std::endl;
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;
649 size_t ehId = 0;
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;
666 out << std::endl;
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) {
675 return;
678 auto it = at(startOffset);
679 auto const stop = at(stopOffset);
680 int prevLineNum = -1;
681 while (it < stop) {
682 if (opts.showLines) {
683 auto const lineNum =
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)
694 << std::endl;
695 it += instrLen(it);
701 ///////////////////////////////////////////////////////////////////////////////
702 // SharedData.
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)
709 , m_line1(line1)
710 , m_originalFilename(nullptr)
711 , m_cti_base(0)
712 , m_numLocals(0)
713 , m_numIterators(0)
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()) {
740 delete table.ptr();
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;
749 } else {
750 delete 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 ///////////////////////////////////////////////////////////////////////////////
784 // Lookup
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();
797 if (f == nullptr) {
798 std::unique_lock<Mutex> l(g_funcsMutex);
799 f = ne->getCachedFunc();
800 if (f == nullptr) {
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);
811 l.unlock();
813 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefFuncHook(func));
817 // If the function didn't exists before we are good
818 if (f == nullptr) {
819 return;
822 // Otherwise check if we match or show the right error message
823 if (f == func) {
824 assertx(!RO::RepoAuthoritative ||
825 (f->isPersistent() && ne->m_cachedFunc.isPersistent()));
826 return;
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
849 // NamedEntity.
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();
862 return func;
865 Func* Func::load(const StringData* name) {
866 String normStr;
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
874 if (normStr) {
875 name = normStr.get();
878 // Autoload the function
879 return AutoloadHandler::s_instance->autoloadFunc(
880 const_cast<StringData*>(name)
881 ) ? ne->getCachedFunc() : nullptr;
884 namespace {
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());
891 } // namespace
893 Func* Func::resolve(const NamedEntity* ne, const StringData* name,
894 const Func* callerFunc) {
895 Func* func = load(ne, name);
896 handleModuleBoundaryViolation(func, callerFunc);
897 return func;
900 Func* Func::resolve(const StringData* name, const Func* callerFunc) {
901 Func* func = load(name);
902 handleModuleBoundaryViolation(func, callerFunc);
903 return func;
906 ///////////////////////////////////////////////////////////////////////////////
907 // Code locations.
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());
915 lock.update(
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
937 * necessary.
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;
948 return 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 "
972 "have been");
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();
982 if (table.isPtr()) {
983 assertx(table.ptr());
984 return table.ptr();
986 return nullptr;
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));
1004 return *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(),
1025 offset,
1026 [] (Offset info, const LineInfo& elm) {
1027 return info < elm.first.past;
1030 if (it != lineMap->end() && it->first.base <= offset) return it->second;
1031 return INT_MIN;
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();
1045 line = findLine();
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(),
1052 info,
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(); });
1062 return info.second;
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) {
1079 range = o;
1080 return true;
1084 return false;
1087 ///////////////////////////////////////////////////////////////////////////////
1088 // Bytecode
1090 namespace {
1092 using BytecodeArena = ReadOnlyArena<VMColdAllocator<char>, false, 8>;
1093 static BytecodeArena& bytecode_arena() {
1094 static BytecodeArena arena(RuntimeOption::EvalHHBCArenaChunkSize);
1095 return arena;
1101 * Export for the admin server.
1103 size_t hhbc_arena_capacity() {
1104 if (!RuntimeOption::RepoAuthoritative) return 0;
1105 return bytecode_arena().capacity();
1108 unsigned char*
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)
1113 : malloc(bclen));
1114 std::copy(bc, bc + bclen, mem);
1115 return mem;
1118 void freeBCRegion(const unsigned char* bc, size_t bclen) {
1119 // Can't free bytecode arena memory.
1120 if (RuntimeOption::RepoAuthoritative) return;
1122 if (debug) {
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));
1141 return mem;
1144 ///////////////////////////////////////////////////////////////////////////////
1145 // Coverage
1147 namespace {
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<
1153 const StringData*,
1154 rds::Link<uint32_t, rds::Mode::Local>
1157 struct EmbeddedCoverageLinkMap {
1158 explicit operator bool() const { return inited; }
1160 CoverageLinkMap* operator->() {
1161 assertx(inited);
1162 return reinterpret_cast<CoverageLinkMap*>(&data);
1164 CoverageLinkMap& operator*() { assertx(inited); return *operator->(); }
1166 void emplace(uint32_t size) {
1167 assertx(!inited);
1168 new (&data) CoverageLinkMap(size);
1169 inited = true;
1172 void clear() {
1173 if (inited) {
1174 operator*().~CoverageLinkMap();
1175 inited = false;
1179 private:
1180 typename std::aligned_storage<
1181 sizeof(CoverageLinkMap),
1182 alignof(CoverageLinkMap)
1183 >::type data;
1184 bool inited;
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());
1218 acc->second.bind(
1219 rds::Mode::Local, rds::LinkName{"FuncCoverageFlag", fullName()}
1222 assertx(acc->second.bound());
1223 return acc->second.handle();
1226 void Func::EnableCoverage() {
1227 assertx(g_context);
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();
1269 return ret;
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()
1278 ? empty_string()
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()
1292 ? empty_string()
1293 : StrNR{unit()->filepath()}.asString();
1295 tl_called_functions->set(fullNameStr().asString(), std::move(path), true);
1298 ///////////////////////////////////////////////////////////////////////////////