Use struct bit packing
[hiphop-php.git] / hphp / runtime / vm / func-emitter.cpp
blobc5b15aedd381ad2e23d89f64dfb1ed809c0e8c16
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-emitter.h"
19 #include "hphp/runtime/base/array-iterator.h"
20 #include "hphp/runtime/base/coeffects-config.h"
21 #include "hphp/runtime/base/unit-cache.h"
22 #include "hphp/runtime/base/rds.h"
24 #include "hphp/runtime/ext/extension.h"
26 #include "hphp/runtime/vm/bytecode.h"
27 #include "hphp/runtime/vm/native.h"
28 #include "hphp/runtime/vm/preclass-emitter.h"
29 #include "hphp/runtime/vm/reified-generics.h"
30 #include "hphp/runtime/vm/repo-autoload-map-builder.h"
31 #include "hphp/runtime/vm/repo-file.h"
32 #include "hphp/runtime/vm/runtime.h"
34 #include "hphp/runtime/vm/jit/types.h"
36 #include "hphp/runtime/vm/verifier/cfg.h"
38 #include "hphp/system/systemlib.h"
40 #include "hphp/util/atomic-vector.h"
41 #include "hphp/util/blob-encoder.h"
42 #include "hphp/util/file.h"
43 #include "hphp/util/trace.h"
45 namespace HPHP {
47 ///////////////////////////////////////////////////////////////////////////////
48 // FuncEmitter.
50 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, Id id, const StringData* n)
51 : m_ue(ue)
52 , m_pce(nullptr)
53 , m_sn(sn)
54 , m_id(id)
55 , m_bc()
56 , m_bclen(0)
57 , m_bcmax(0)
58 , name(n)
59 , maxStackCells(0)
60 , retUserType(nullptr)
61 , docComment(nullptr)
62 , originalFilename(nullptr)
63 , memoizePropName(nullptr)
64 , memoizeGuardPropName(nullptr)
65 , memoizeSharedPropIndex(0)
66 , m_numLocals(0)
67 , m_numUnnamedLocals(0)
68 , m_numIterators(0)
69 , m_nextFreeIterator(0)
70 , m_ehTabSorted(false)
73 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, const StringData* n,
74 PreClassEmitter* pce)
75 : m_ue(ue)
76 , m_pce(pce)
77 , m_sn(sn)
78 , m_id(kInvalidId)
79 , m_bc()
80 , m_bclen(0)
81 , m_bcmax(0)
82 , name(n)
83 , maxStackCells(0)
84 , retUserType(nullptr)
85 , docComment(nullptr)
86 , originalFilename(nullptr)
87 , memoizePropName(nullptr)
88 , memoizeGuardPropName(nullptr)
89 , memoizeSharedPropIndex(0)
90 , m_numLocals(0)
91 , m_numUnnamedLocals(0)
92 , m_numIterators(0)
93 , m_nextFreeIterator(0)
94 , m_ehTabSorted(false)
97 FuncEmitter::~FuncEmitter() {
98 if (m_bc.isPtr()) {
99 if (auto const p = m_bc.ptr()) free(p);
101 if (m_lineTable.isPtr()) {
102 if (auto const p = m_lineTable.ptr()) delete p;
107 ///////////////////////////////////////////////////////////////////////////////
108 // Source locations.
110 SourceLocTable FuncEmitter::createSourceLocTable() const {
111 assertx(m_sourceLocTab.size() != 0);
112 SourceLocTable locations;
113 for (size_t i = 0; i < m_sourceLocTab.size(); ++i) {
114 Offset endOff = i < m_sourceLocTab.size() - 1
115 ? m_sourceLocTab[i + 1].first
116 : m_bclen;
117 locations.push_back(SourceLocEntry(endOff, m_sourceLocTab[i].second));
119 return locations;
122 void FuncEmitter::setLineTable(LineTable table) {
123 if (m_lineTable.isPtr()) {
124 if (auto p = m_lineTable.ptr()) {
125 *p = std::move(table);
126 return;
129 m_lineTable = Func::LineTablePtr::FromPtr(new LineTable{std::move(table)});
132 namespace {
134 using SrcLoc = std::vector<std::pair<Offset, SourceLoc>>;
137 * Create a LineTable from `srcLoc'.
139 LineTable createLineTable(const SrcLoc& srcLoc, Offset bclen) {
140 LineTable lines;
141 if (srcLoc.empty()) {
142 return lines;
145 auto prev = srcLoc.begin();
146 for (auto it = prev + 1; it != srcLoc.end(); ++it) {
147 if (prev->second.line1 != it->second.line1) {
148 lines.push_back(LineEntry(it->first, prev->second.line1));
149 prev = it;
153 lines.push_back(LineEntry(bclen, prev->second.line1));
154 return lines;
159 void FuncEmitter::recordSourceLocation(const Location::Range& sLoc,
160 Offset start) {
161 // Some byte codes, such as for the implicit "return 0" at the end of a
162 // a source file do not have valid source locations. This check makes
163 // sure we don't record a (dummy) source location in this case.
164 if (start > 0 && sLoc.line0 == -1) return;
165 SourceLoc newLoc(sLoc);
166 if (!m_sourceLocTab.empty()) {
167 if (m_sourceLocTab.back().second == newLoc) {
168 // Combine into the interval already at the back of the vector.
169 assertx(start >= m_sourceLocTab.back().first);
170 return;
172 assertx(m_sourceLocTab.back().first < start &&
173 "source location offsets must be added to UnitEmitter in "
174 "increasing order");
175 } else {
176 // First record added should be for bytecode offset zero or very rarely one
177 // when the source starts with a label and a Nop is inserted.
178 assertx(start == 0 || start == 1);
180 m_sourceLocTab.push_back(std::make_pair(start, newLoc));
183 ///////////////////////////////////////////////////////////////////////////////
184 // Initialization and execution.
186 void FuncEmitter::init(int l1, int l2, Attr attrs_,
187 const StringData* docComment_) {
188 line1 = l1;
189 line2 = l2;
190 attrs = fix_attrs(attrs_);
191 docComment = docComment_;
193 if (!SystemLib::s_inited) assertx(attrs & AttrBuiltin);
196 void FuncEmitter::finish() {
197 sortEHTab();
200 const StaticString
201 s_construct("__construct"),
202 s_DynamicallyCallable("__DynamicallyCallable"),
203 s_Memoize("__Memoize"),
204 s_MemoizeLSB("__MemoizeLSB"),
205 s_KeyedByIC("KeyedByIC"),
206 s_MakeICInaccessible("MakeICInaccessible"),
207 s_SoftMakeICInaccessible("SoftMakeICInaccessible");
209 Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
210 bool isGenerated = isdigit(name->data()[0]);
212 auto attrs = fix_attrs(this->attrs);
213 if (preClass && preClass->attrs() & AttrInterface) {
214 attrs |= AttrAbstract;
216 if (attrs & AttrIsMethCaller && RuntimeOption::RepoAuthoritative) {
217 attrs |= AttrPersistent | AttrUnique;
219 if (attrs & (AttrPersistent | AttrUnique) && !preClass) {
220 if ((RuntimeOption::EvalJitEnableRenameFunction ||
221 attrs & AttrInterceptable ||
222 (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited))) {
223 if (attrs & AttrBuiltin) {
224 SystemLib::s_anyNonPersistentBuiltins = true;
226 attrs = Attr(attrs & ~(AttrPersistent | AttrUnique));
228 } else {
229 assertx(preClass || !(attrs & AttrBuiltin) || (attrs & AttrIsMethCaller));
231 if (isVariadic()) {
232 attrs |= AttrVariadicParam;
234 if (isAsync && !isGenerator) {
235 // Async functions can return results directly.
236 attrs |= AttrSupportsAsyncEagerReturn;
238 if (!coeffectRules.empty()) attrs |= AttrHasCoeffectRules;
240 auto const dynCallSampleRate = [&] () -> Optional<int64_t> {
241 if (!(attrs & AttrDynamicallyCallable)) return {};
243 auto const uattr = userAttributes.find(s_DynamicallyCallable.get());
244 if (uattr == userAttributes.end()) return {};
246 auto const tv = uattr->second;
247 assertx(isArrayLikeType(type(tv)));
248 auto const rate = val(tv).parr->get(int64_t(0));
249 if (!isIntType(type(rate)) || val(rate).num < 0) return {};
251 attrs = Attr(attrs & ~AttrDynamicallyCallable);
252 return val(rate).num;
253 }();
255 assertx(!m_pce == !preClass);
256 auto f = m_ue.newFunc(this, unit, name, attrs, params.size());
258 f->m_isPreFunc = !!preClass;
260 auto const uait = userAttributes.find(s___Reified.get());
261 auto const hasReifiedGenerics = uait != userAttributes.end();
263 // Returns (static coeffects, escapes)
264 auto const coeffectsInfo = getCoeffectsInfoFromList(
265 staticCoeffects,
266 preClass && name == s_construct.get());
267 f->m_requiredCoeffects = coeffectsInfo.first.toRequired();
269 bool const needsExtendedSharedData =
270 isNative ||
271 line2 - line1 >= Func::kSmallDeltaLimit ||
272 m_bclen >= Func::kSmallDeltaLimit ||
273 m_sn >= Func::kSmallDeltaLimit ||
274 hasReifiedGenerics ||
275 hasParamsWithMultiUBs ||
276 hasReturnWithMultiUBs ||
277 dynCallSampleRate ||
278 coeffectsInfo.second.value() != 0 ||
279 !coeffectRules.empty() ||
280 (docComment && !docComment->empty());
282 f->m_shared.reset(
283 needsExtendedSharedData
284 ? new Func::ExtendedSharedData(m_bc, m_bclen, preClass, m_sn, line1, line2,
285 !containsCalls)
286 : new Func::SharedData(m_bc, m_bclen, preClass, m_sn, line1, line2,
287 !containsCalls)
290 f->init(params.size());
292 if (auto const ex = f->extShared()) {
293 ex->m_allFlags.m_hasExtendedSharedData = true;
294 ex->m_arFuncPtr = nullptr;
295 ex->m_nativeFuncPtr = nullptr;
296 ex->m_bclen = m_bclen;
297 ex->m_sn = m_sn;
298 ex->m_line2 = line2;
299 ex->m_dynCallSampleRate = dynCallSampleRate.value_or(-1);
300 ex->m_allFlags.m_returnByValue = false;
301 ex->m_allFlags.m_isMemoizeWrapper = false;
302 ex->m_allFlags.m_isMemoizeWrapperLSB = false;
303 ex->m_allFlags.m_memoizeICType = Func::MemoizeICType::NoIC;
305 if (!coeffectRules.empty()) ex->m_coeffectRules = coeffectRules;
306 ex->m_coeffectEscapes = coeffectsInfo.second;
307 ex->m_docComment = docComment;
310 std::vector<Func::ParamInfo> fParams;
311 for (unsigned i = 0; i < params.size(); ++i) {
312 Func::ParamInfo pi = params[i];
313 if (pi.isVariadic()) {
314 pi.builtinType = KindOfVec;
316 fParams.push_back(pi);
317 auto const& fromUBs = params[i].upperBounds;
318 if (!fromUBs.empty()) {
319 auto& ub = f->extShared()->m_paramUBs[i];
320 ub.resize(fromUBs.size());
321 std::copy(fromUBs.begin(), fromUBs.end(), ub.begin());
322 f->shared()->m_allFlags.m_hasParamsWithMultiUBs = true;
326 auto const originalFullName =
327 (!originalFilename ||
328 !RuntimeOption::RepoAuthoritative ||
329 FileUtil::isAbsolutePath(originalFilename->slice())) ?
330 originalFilename :
331 makeStaticString(RuntimeOption::SourceRoot +
332 originalFilename->toCppString());
334 f->shared()->m_localNames.create(m_localNames);
335 f->shared()->m_numLocals = m_numLocals;
336 f->shared()->m_numIterators = m_numIterators;
337 f->m_maxStackCells = maxStackCells;
338 f->shared()->m_ehtab = ehtab;
339 f->shared()->m_allFlags.m_isClosureBody = isClosureBody;
340 f->shared()->m_allFlags.m_isAsync = isAsync;
341 f->shared()->m_allFlags.m_isGenerator = isGenerator;
342 f->shared()->m_allFlags.m_isPairGenerator = isPairGenerator;
343 f->shared()->m_userAttributes = userAttributes;
344 f->shared()->m_retTypeConstraint = retTypeConstraint;
345 f->shared()->m_retUserType = retUserType;
346 if (!retUpperBounds.empty()) {
347 f->extShared()->m_returnUBs.resize(retUpperBounds.size());
348 std::copy(retUpperBounds.begin(), retUpperBounds.end(),
349 f->extShared()->m_returnUBs.begin());
350 f->shared()->m_allFlags.m_hasReturnWithMultiUBs = true;
352 f->shared()->m_originalFilename = originalFullName;
353 f->shared()->m_allFlags.m_isGenerated = isGenerated;
354 f->shared()->m_repoReturnType = repoReturnType;
355 f->shared()->m_repoAwaitedReturnType = repoAwaitedReturnType;
356 f->shared()->m_allFlags.m_isMemoizeWrapper = isMemoizeWrapper;
357 f->shared()->m_allFlags.m_isMemoizeWrapperLSB = isMemoizeWrapperLSB;
358 f->shared()->m_allFlags.m_hasReifiedGenerics = hasReifiedGenerics;
360 if (isMemoizeWrapper || isMemoizeWrapperLSB) {
361 auto const getICType = [&] (TypedValue tv) {
362 assertx(tvIsVec(tv));
363 auto type = Func::MemoizeICType::NoIC;
364 IterateV(tv.m_data.parr, [&](TypedValue elem) {
365 if (tvIsString(elem)) {
366 if (elem.m_data.pstr->same(s_KeyedByIC.get())) {
367 type = Func::MemoizeICType::KeyedByIC;
368 } else if (elem.m_data.pstr->same(s_MakeICInaccessible.get())) {
369 type = Func::MemoizeICType::MakeICInaccessible;
370 } else if (elem.m_data.pstr->same(s_SoftMakeICInaccessible.get())) {
371 type = Func::MemoizeICType::SoftMakeICInaccessible;
375 return type;
377 auto const attrName = isMemoizeWrapperLSB ? s_MemoizeLSB : s_Memoize;
378 auto const it = userAttributes.find(attrName.get());
379 if (it != userAttributes.end()) {
380 auto const ic_type = getICType(it->second);
381 assertx((ic_type & 0x3) == ic_type);
382 f->shared()->m_allFlags.m_memoizeICType = ic_type;
386 for (auto const& name : staticCoeffects) {
387 f->shared()->m_staticCoeffectNames.push_back(name);
390 if (hasReifiedGenerics) {
391 auto const tv = uait->second;
392 assertx(tvIsVec(tv));
393 f->extShared()->m_reifiedGenericsInfo =
394 extractSizeAndPosFromReifiedAttribute(tv.m_data.parr);
398 * If we have a m_sourceLocTab, use that to create the line
399 * table. Otherwise use the line table we loaded out of the repo, or
400 * a token to lazy load it.
402 if (m_sourceLocTab.size() != 0) {
403 f->setLineTable(createLineTable(m_sourceLocTab, m_bclen));
404 // If the debugger is enabled, or we plan to dump hhas we will
405 // need the extended line table information in the output, and if
406 // we're not writing the repo, stashing it here is necessary for
407 // it to make it through.
408 if (needs_extended_line_table()) {
409 f->stashExtendedLineTable(createSourceLocTable());
411 } else if (m_lineTable.isPtr()) {
412 f->setLineTable(*m_lineTable.ptr());
413 } else {
414 assertx(RO::RepoAuthoritative);
415 f->setLineTable(m_lineTable.token());
418 if (isNative) {
419 auto const ex = f->extShared();
421 auto const info = getNativeInfo();
423 Attr dummy = AttrNone;
424 auto nativeAttributes = parseNativeAttributes(dummy);
425 Native::getFunctionPointers(
426 info,
427 nativeAttributes,
428 ex->m_arFuncPtr,
429 ex->m_nativeFuncPtr
432 if (ex->m_nativeFuncPtr) {
433 if (info.sig.ret == Native::NativeSig::Type::MixedTV) {
434 ex->m_allFlags.m_returnByValue = true;
436 int extra = isMethod() ? 1 : 0;
437 assertx(info.sig.args.size() == params.size() + extra);
438 for (auto i = params.size(); i--; ) {
439 switch (info.sig.args[extra + i]) {
440 case Native::NativeSig::Type::ObjectArg:
441 case Native::NativeSig::Type::StringArg:
442 case Native::NativeSig::Type::ArrayArg:
443 case Native::NativeSig::Type::ResourceArg:
444 fParams[i].setFlag(Func::ParamInfo::Flags::NativeArg);
445 break;
446 case Native::NativeSig::Type::MixedTV:
447 fParams[i].setFlag(Func::ParamInfo::Flags::NativeArg);
448 fParams[i].setFlag(Func::ParamInfo::Flags::AsTypedValue);
449 break;
450 case Native::NativeSig::Type::Mixed:
451 fParams[i].setFlag(Func::ParamInfo::Flags::AsVariant);
452 break;
453 default:
454 break;
460 f->finishedEmittingParams(fParams);
462 if (RuntimeOption::EvalEnableReverseDataMap && !preClass) {
463 f->registerInDataMap();
465 return f;
468 String FuncEmitter::nativeFullname() const {
469 return Native::fullName(name, m_pce ? m_pce->name() : nullptr,
470 (attrs & AttrStatic));
473 Native::NativeFunctionInfo FuncEmitter::getNativeInfo() const {
474 return Native::getNativeFunction(
475 m_ue.m_nativeFuncs,
476 name,
477 m_pce ? m_pce->name() : nullptr,
478 (attrs & AttrStatic)
482 ///////////////////////////////////////////////////////////////////////////////
483 // Locals, iterators, and parameters.
485 void FuncEmitter::allocVarId(const StringData* name, bool slotless) {
486 assertx(name != nullptr);
487 UNUSED Id id;
488 if (m_localNames.find(name) == m_localNames.end()) {
489 // Slotless locals must come after all locals with slots.
490 if (!slotless) {
491 id = (m_numLocals++);
492 assertx(id == (int)m_localNames.size());
494 m_localNames.add(name, name);
498 Id FuncEmitter::allocIterator() {
499 assertx(m_numIterators >= m_nextFreeIterator);
500 Id id = m_nextFreeIterator++;
501 if (m_numIterators < m_nextFreeIterator) {
502 m_numIterators = m_nextFreeIterator;
504 return id;
507 Id FuncEmitter::allocUnnamedLocal() {
508 m_localNames.addUnnamed(nullptr);
509 ++m_numUnnamedLocals;
510 return m_numLocals++;
513 ///////////////////////////////////////////////////////////////////////////////
514 // Unit tables.
516 EHEnt& FuncEmitter::addEHEnt() {
517 assertx(!m_ehTabSorted
518 || "should only mark the ehtab as sorted after adding all of them");
519 ehtab.emplace_back();
520 ehtab.back().m_parentIndex = 7777;
521 return ehtab.back();
524 namespace {
527 * Ordering on EHEnts where e1 < e2 iff
529 * a) e1 and e2 do not overlap, and e1 comes first
530 * b) e1 encloses e2
532 struct EHEntComp {
533 bool operator()(const EHEnt& e1, const EHEnt& e2) const {
534 if (e1.m_base == e2.m_base) {
535 return e1.m_past > e2.m_past;
537 return e1.m_base < e2.m_base;
543 void FuncEmitter::sortEHTab() {
544 if (m_ehTabSorted) return;
546 std::sort(ehtab.begin(), ehtab.end(), EHEntComp());
548 for (unsigned int i = 0; i < ehtab.size(); i++) {
549 ehtab[i].m_parentIndex = -1;
550 for (int j = i - 1; j >= 0; j--) {
551 if (ehtab[j].m_past >= ehtab[i].m_past) {
552 // parent EHEnt better enclose this one.
553 assertx(ehtab[j].m_base <= ehtab[i].m_base);
554 ehtab[i].m_parentIndex = j;
555 break;
560 setEHTabIsSorted();
563 void FuncEmitter::setEHTabIsSorted() {
564 m_ehTabSorted = true;
565 if (!debug) return;
567 Offset curBase = 0;
568 for (size_t i = 0; i < ehtab.size(); ++i) {
569 auto& eh = ehtab[i];
571 // Base offsets must be monotonically increasing.
572 always_assert(curBase <= eh.m_base);
573 curBase = eh.m_base;
575 // Parent should come before, and must enclose this guy.
576 always_assert(eh.m_parentIndex == -1 || eh.m_parentIndex < i);
577 if (eh.m_parentIndex != -1) {
578 auto& parent = ehtab[eh.m_parentIndex];
579 always_assert(parent.m_base <= eh.m_base &&
580 parent.m_past >= eh.m_past);
585 ///////////////////////////////////////////////////////////////////////////////
586 // Bytecode
588 void FuncEmitter::setBc(const unsigned char* bc, size_t bclen) {
589 if (m_bc.isPtr()) {
590 if (auto const p = m_bc.ptr()) free(p);
592 auto const p = (unsigned char*)malloc(bclen);
593 m_bcmax = bclen;
594 if (bclen) memcpy(p, bc, bclen);
595 m_bc = Func::BCPtr::FromPtr(p);
596 m_bclen = bclen;
599 void FuncEmitter::setBcToken(Func::BCPtr::Token token, size_t bclen) {
600 if (m_bc.isPtr()) {
601 if (auto const p = m_bc.ptr()) free(p);
603 m_bcmax = bclen;
604 m_bclen = bclen;
605 m_bc = Func::BCPtr::FromToken(token);
608 Optional<Func::BCPtr::Token> FuncEmitter::loadBc() {
609 if (m_bc.isPtr()) return std::nullopt;
610 assertx(RO::RepoAuthoritative);
611 auto const old = m_bc.token();
612 auto bc = (unsigned char*)malloc(m_bclen);
613 RepoFile::readRawFromUnit(m_ue.m_sn, old, bc, m_bclen);
614 m_bc = Func::BCPtr::FromPtr(bc);
615 return old;
618 ///////////////////////////////////////////////////////////////////////////////
619 // Complex setters.
621 /* <<__Native>> user attribute causes systemlib declarations
622 * to hook internal (C++) implementation of funcs/methods
624 * The Native attribute may have the following sub-options
625 * "NoFCallBuiltin": Prevent FCallBuiltin optimization
626 * Effectively forces functions to generate an ActRec
627 * "NoInjection": Do not include this frame in backtraces
629 * e.g. <<__Native("NoFCallBuiltin")>> function foo():mixed;
631 static const StaticString
632 s_native("__Native"),
633 s_nofcallbuiltin("NoFCallBuiltin"),
634 s_noinjection("NoInjection"),
635 s_opcodeimpl("OpCodeImpl");
637 int FuncEmitter::parseNativeAttributes(Attr& attrs_) const {
638 int ret = Native::AttrNone;
640 auto it = userAttributes.find(s_native.get());
641 assertx(it != userAttributes.end());
642 const TypedValue userAttr = it->second;
643 assertx(isArrayLikeType(userAttr.m_type));
644 for (ArrayIter it(userAttr.m_data.parr); it; ++it) {
645 Variant userAttrVal = it.second();
646 if (userAttrVal.isString()) {
647 String userAttrStrVal = userAttrVal.toString();
648 if (userAttrStrVal.get()->isame(s_nofcallbuiltin.get())) {
649 attrs_ |= AttrNoFCallBuiltin;
650 } else if (userAttrStrVal.get()->isame(s_noinjection.get())) {
651 attrs_ |= AttrNoInjection;
652 } else if (userAttrStrVal.get()->isame(s_opcodeimpl.get())) {
653 ret |= Native::AttrOpCodeImpl;
657 return ret;
660 Attr FuncEmitter::fix_attrs(Attr a) const {
661 if (RuntimeOption::RepoAuthoritative) return a;
663 a = Attr(a & ~AttrInterceptable);
665 if (RuntimeOption::EvalJitEnableRenameFunction) {
666 return a | AttrInterceptable;
668 return a;
671 ///////////////////////////////////////////////////////////////////////////////
672 // Serialization/Deserialization
674 template<class SerDe>
675 void FuncEmitter::serdeMetaData(SerDe& sd) {
676 // NOTE: name and a few other fields currently handled outside of this.
677 Attr a = attrs;
679 if (!SerDe::deserializing) {
680 a = fix_attrs(attrs);
683 sd(line1)
684 (line2)
686 (m_bclen)
687 (staticCoeffects)
688 (repoReturnType)
689 (repoAwaitedReturnType)
690 (docComment)
691 (m_numLocals)
692 (m_numIterators)
693 (maxStackCells)
694 (m_repoBoolBitset)
696 (params)
697 (m_localNames, [](auto s) { return s; })
698 (ehtab,
699 [&](const EHEnt& prev, EHEnt cur) -> EHEnt {
700 if (!SerDe::deserializing) {
701 cur.m_handler -= cur.m_past;
702 cur.m_past -= cur.m_base;
703 cur.m_base -= prev.m_base;
704 } else {
705 cur.m_base += prev.m_base;
706 cur.m_past += cur.m_base;
707 cur.m_handler += cur.m_past;
709 return cur;
712 (userAttributes)
713 (retTypeConstraint)
714 (retUserType)
715 (retUpperBounds)
716 (originalFilename)
717 (coeffectRules)
720 if (SerDe::deserializing) attrs = fix_attrs(a);
723 template void FuncEmitter::serdeMetaData<>(BlobDecoder&);
724 template void FuncEmitter::serdeMetaData<>(BlobEncoder&);
726 template<class SerDe>
727 void FuncEmitter::serde(SerDe& sd, bool lazy) {
728 assertx(IMPLIES(lazy, RO::RepoAuthoritative));
729 assertx(IMPLIES(!SerDe::deserializing, !lazy));
731 serdeMetaData(sd);
733 // Never lazy load any builtins (this avoids issues with HHBBC
734 // trying to load data after we've shutdown the repo).
735 if (attrs & AttrBuiltin) lazy = false;
737 if constexpr (SerDe::deserializing) {
738 if (lazy) {
739 m_lineTable = Func::LineTablePtr::FromToken(sd.advanced());
740 sd.skipWithSize();
741 } else {
742 LineTable lineTable;
743 deserializeLineTable(sd, lineTable);
744 setLineTable(std::move(lineTable));
746 } else {
747 auto const& lines = m_sourceLocTab.empty()
748 ? *m_lineTable.ptr()
749 : createLineTable(m_sourceLocTab, m_bclen);
750 sd.withSize(
751 [&] {
753 lines,
754 [&] (const LineEntry& prev, const LineEntry& curDelta) {
755 return LineEntry {
756 curDelta.pastOffset() - prev.pastOffset(),
757 curDelta.val() - prev.val()
765 if constexpr (SerDe::deserializing) {
766 sd(m_sourceLocTab);
767 } else {
768 sd(RO::RepoDebugInfo ? m_sourceLocTab : decltype(m_sourceLocTab){});
771 // Bytecode
772 if constexpr (SerDe::deserializing) {
773 assertx(sd.remaining() >= m_bclen);
774 if (lazy) {
775 setBcToken(sd.advanced(), m_bclen);
776 } else {
777 setBc(sd.data(), m_bclen);
779 sd.advance(m_bclen);
780 } else {
781 sd.writeRaw((const char*)m_bc.ptr(), m_bclen);
785 template void FuncEmitter::serde<>(BlobDecoder&, bool);
786 template void FuncEmitter::serde<>(BlobEncoder&, bool);
788 void FuncEmitter::deserializeLineTable(BlobDecoder& decoder,
789 LineTable& lineTable) {
790 decoder.withSize(
791 [&] {
792 decoder(
793 lineTable,
794 [&] (const LineEntry& prev, const LineEntry& curDelta) {
795 return LineEntry {
796 curDelta.pastOffset() + prev.pastOffset(),
797 curDelta.val() + prev.val()
805 LineTable FuncEmitter::loadLineTableFromRepo(int64_t unitSn,
806 RepoFile::Token token) {
807 assertx(RO::RepoAuthoritative);
809 auto const remaining = RepoFile::remainingSizeOfUnit(unitSn, token);
810 always_assert(remaining >= sizeof(uint64_t));
812 size_t actualSize;
814 // We encoded the size of the line table along with the table. So,
815 // peek its size and bail if the decoder doesn't have enough data
816 // remaining.
817 auto const size = std::min<size_t>(remaining, 128);
818 auto const data = std::make_unique<unsigned char[]>(size);
819 RepoFile::readRawFromUnit(unitSn, token, data.get(), size);
820 BlobDecoder decoder{data.get(), size};
821 actualSize = decoder.peekSize();
822 if (actualSize <= decoder.remaining()) {
823 LineTable lineTable;
824 deserializeLineTable(decoder, lineTable);
825 return lineTable;
829 constexpr const size_t kLineTableSizeLimit = 1ull << 32;
830 always_assert(actualSize <= kLineTableSizeLimit);
831 always_assert(actualSize <= remaining);
833 LineTable lineTable;
834 auto const data = std::make_unique<unsigned char[]>(actualSize);
835 RepoFile::readRawFromUnit(unitSn, token, data.get(), actualSize);
836 BlobDecoder decoder{data.get(), actualSize};
837 deserializeLineTable(decoder, lineTable);
838 decoder.assertDone();
839 return lineTable;
842 ///////////////////////////////////////////////////////////////////////////////