Reland D23318594 and D23318592 add recordbasenativesp instr
[hiphop-php.git] / hphp / runtime / vm / func-emitter.cpp
blob3629c643d8139d02193f4fab07873cbce6895118
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/unit-cache.h"
21 #include "hphp/runtime/base/rds.h"
23 #include "hphp/runtime/ext/extension.h"
25 #include "hphp/runtime/vm/blob-helper.h"
26 #include "hphp/runtime/vm/bytecode.h"
27 #include "hphp/runtime/vm/native.h"
28 #include "hphp/runtime/vm/reified-generics.h"
29 #include "hphp/runtime/vm/repo.h"
30 #include "hphp/runtime/vm/repo-autoload-map-builder.h"
31 #include "hphp/runtime/vm/runtime.h"
33 #include "hphp/runtime/vm/jit/types.h"
35 #include "hphp/runtime/vm/verifier/cfg.h"
37 #include "hphp/system/systemlib.h"
39 #include "hphp/util/atomic-vector.h"
40 #include "hphp/util/file.h"
41 #include "hphp/util/trace.h"
43 namespace HPHP {
44 ///////////////////////////////////////////////////////////////////////////////
45 // FuncEmitter.
47 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, Id id, const StringData* n)
48 : m_ue(ue)
49 , m_pce(nullptr)
50 , m_sn(sn)
51 , m_id(id)
52 , name(n)
53 , maxStackCells(0)
54 , hniReturnType(folly::none)
55 , retUserType(nullptr)
56 , docComment(nullptr)
57 , originalFilename(nullptr)
58 , memoizePropName(nullptr)
59 , memoizeGuardPropName(nullptr)
60 , memoizeSharedPropIndex(0)
61 , m_numLocals(0)
62 , m_numUnnamedLocals(0)
63 , m_numIterators(0)
64 , m_nextFreeIterator(0)
65 , m_ehTabSorted(false)
68 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, const StringData* n,
69 PreClassEmitter* pce)
70 : m_ue(ue)
71 , m_pce(pce)
72 , m_sn(sn)
73 , m_id(kInvalidId)
74 , name(n)
75 , maxStackCells(0)
76 , hniReturnType(folly::none)
77 , retUserType(nullptr)
78 , docComment(nullptr)
79 , originalFilename(nullptr)
80 , memoizePropName(nullptr)
81 , memoizeGuardPropName(nullptr)
82 , memoizeSharedPropIndex(0)
83 , m_numLocals(0)
84 , m_numUnnamedLocals(0)
85 , m_numIterators(0)
86 , m_nextFreeIterator(0)
87 , m_ehTabSorted(false)
90 FuncEmitter::~FuncEmitter() {
94 ///////////////////////////////////////////////////////////////////////////////
95 // Initialization and execution.
97 void FuncEmitter::init(int l1, int l2, Offset base_, Attr attrs_,
98 const StringData* docComment_) {
99 base = base_;
100 line1 = l1;
101 line2 = l2;
102 attrs = fix_attrs(attrs_);
103 docComment = docComment_;
105 if (!SystemLib::s_inited) assertx(attrs & AttrBuiltin);
108 void FuncEmitter::finish(Offset past_) {
109 past = past_;
110 sortEHTab();
113 void FuncEmitter::commit(RepoTxn& txn) const {
114 Repo& repo = Repo::get();
115 FuncRepoProxy& frp = repo.frp();
116 int repoId = m_ue.m_repoId;
117 int64_t usn = m_ue.m_sn;
119 frp.insertFunc[repoId]
120 .insert(*this, txn, usn, m_sn, m_pce ? m_pce->id() : -1, name);
123 const StaticString s_DynamicallyCallable("__DynamicallyCallable");
125 Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
126 bool isGenerated = isdigit(name->data()[0]);
128 auto attrs = fix_attrs(this->attrs);
129 if (preClass && preClass->attrs() & AttrInterface) {
130 attrs |= AttrAbstract;
132 if (attrs & AttrIsMethCaller && RuntimeOption::RepoAuthoritative) {
133 attrs |= AttrPersistent | AttrUnique;
135 if (attrs & AttrPersistent && !preClass) {
136 if ((RuntimeOption::EvalJitEnableRenameFunction ||
137 attrs & AttrInterceptable ||
138 (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited))) {
139 if (attrs & AttrBuiltin) {
140 SystemLib::s_anyNonPersistentBuiltins = true;
142 attrs = Attr(attrs & ~AttrPersistent);
144 } else {
145 assertx(preClass || !(attrs & AttrBuiltin) || (attrs & AttrIsMethCaller));
147 if (isVariadic()) {
148 attrs |= AttrVariadicParam;
150 if (isAsync && !isGenerator) {
151 // Async functions can return results directly.
152 attrs |= AttrSupportsAsyncEagerReturn;
155 auto const dynCallSampleRate = [&] () -> folly::Optional<int64_t> {
156 if (!(attrs & AttrDynamicallyCallable)) return {};
158 auto const uattr = userAttributes.find(s_DynamicallyCallable.get());
159 if (uattr == userAttributes.end()) return {};
161 auto const tv = uattr->second;
162 assertx(isArrayLikeType(type(tv)));
163 auto const rate = val(tv).parr->get(int64_t(0));
164 if (!isIntType(type(rate)) || val(rate).num < 0) return {};
166 attrs = Attr(attrs & ~AttrDynamicallyCallable);
167 return val(rate).num;
168 }();
170 assertx(!m_pce == !preClass);
171 auto f = m_ue.newFunc(this, unit, name, attrs, params.size());
173 f->m_isPreFunc = !!preClass;
175 auto const uait = userAttributes.find(s___Reified.get());
176 auto const hasReifiedGenerics = uait != userAttributes.end();
178 bool const needsExtendedSharedData =
179 isNative ||
180 line2 - line1 >= Func::kSmallDeltaLimit ||
181 past - base >= Func::kSmallDeltaLimit ||
182 hasReifiedGenerics ||
183 hasParamsWithMultiUBs ||
184 hasReturnWithMultiUBs ||
185 dynCallSampleRate;
187 f->m_shared.reset(
188 needsExtendedSharedData
189 ? new Func::ExtendedSharedData(preClass, base, past, line1, line2,
190 !containsCalls, docComment)
191 : new Func::SharedData(preClass, base, past,
192 line1, line2, !containsCalls, docComment)
195 f->init(params.size());
197 if (auto const ex = f->extShared()) {
198 ex->m_allFlags.m_hasExtendedSharedData = true;
199 ex->m_arFuncPtr = nullptr;
200 ex->m_nativeFuncPtr = nullptr;
201 ex->m_line2 = line2;
202 ex->m_past = past;
203 ex->m_dynCallSampleRate = dynCallSampleRate.value_or(-1);
204 ex->m_allFlags.m_returnByValue = false;
205 ex->m_allFlags.m_isMemoizeWrapper = false;
206 ex->m_allFlags.m_isMemoizeWrapperLSB = false;
209 std::vector<Func::ParamInfo> fParams;
210 for (unsigned i = 0; i < params.size(); ++i) {
211 Func::ParamInfo pi = params[i];
212 if (pi.isVariadic()) {
213 pi.builtinType = RuntimeOption::EvalHackArrDVArrs
214 ? KindOfVec : KindOfVArray;
216 f->appendParam(params[i].isInOut(), pi, fParams);
217 auto const& fromUBs = params[i].upperBounds;
218 if (!fromUBs.empty()) {
219 auto& ub = f->extShared()->m_paramUBs[i];
220 ub.resize(fromUBs.size());
221 std::copy(fromUBs.begin(), fromUBs.end(), ub.begin());
222 f->shared()->m_allFlags.m_hasParamsWithMultiUBs = true;
226 auto const originalFullName =
227 (!originalFilename ||
228 !RuntimeOption::RepoAuthoritative ||
229 FileUtil::isAbsolutePath(originalFilename->slice())) ?
230 originalFilename :
231 makeStaticString(RuntimeOption::SourceRoot +
232 originalFilename->toCppString());
234 f->shared()->m_localNames.create(m_localNames);
235 f->shared()->m_numLocals = m_numLocals;
236 f->shared()->m_numIterators = m_numIterators;
237 f->m_maxStackCells = maxStackCells;
238 f->shared()->m_ehtab = ehtab;
239 f->shared()->m_allFlags.m_isClosureBody = isClosureBody;
240 f->shared()->m_allFlags.m_isAsync = isAsync;
241 f->shared()->m_allFlags.m_isGenerator = isGenerator;
242 f->shared()->m_allFlags.m_isPairGenerator = isPairGenerator;
243 f->shared()->m_userAttributes = userAttributes;
244 f->shared()->m_retTypeConstraint = retTypeConstraint;
245 f->shared()->m_retUserType = retUserType;
246 if (!retUpperBounds.empty()) {
247 f->extShared()->m_returnUBs.resize(retUpperBounds.size());
248 std::copy(retUpperBounds.begin(), retUpperBounds.end(),
249 f->extShared()->m_returnUBs.begin());
250 f->shared()->m_allFlags.m_hasReturnWithMultiUBs = true;
252 f->shared()->m_originalFilename = originalFullName;
253 f->shared()->m_allFlags.m_isGenerated = isGenerated;
254 f->shared()->m_repoReturnType = repoReturnType;
255 f->shared()->m_repoAwaitedReturnType = repoAwaitedReturnType;
256 f->shared()->m_allFlags.m_isMemoizeWrapper = isMemoizeWrapper;
257 f->shared()->m_allFlags.m_isMemoizeWrapperLSB = isMemoizeWrapperLSB;
258 f->shared()->m_allFlags.m_hasReifiedGenerics = hasReifiedGenerics;
259 f->shared()->m_allFlags.m_isRxDisabled = isRxDisabled;
261 if (hasReifiedGenerics) {
262 auto tv = uait->second;
263 assertx(tvIsHAMSafeVArray(tv));
264 f->extShared()->m_reifiedGenericsInfo =
265 extractSizeAndPosFromReifiedAttribute(tv.m_data.parr);
268 if (isNative) {
269 auto const ex = f->extShared();
271 ex->m_hniReturnType = hniReturnType;
273 auto const info = getNativeInfo();
275 Attr dummy = AttrNone;
276 auto nativeAttributes = parseNativeAttributes(dummy);
277 Native::getFunctionPointers(
278 info,
279 nativeAttributes,
280 ex->m_arFuncPtr,
281 ex->m_nativeFuncPtr
284 if (ex->m_nativeFuncPtr) {
285 if (info.sig.ret == Native::NativeSig::Type::MixedTV) {
286 ex->m_allFlags.m_returnByValue = true;
288 int extra = isMethod() ? 1 : 0;
289 assertx(info.sig.args.size() == params.size() + extra);
290 for (auto i = params.size(); i--; ) {
291 switch (info.sig.args[extra + i]) {
292 case Native::NativeSig::Type::ObjectArg:
293 case Native::NativeSig::Type::StringArg:
294 case Native::NativeSig::Type::ArrayArg:
295 case Native::NativeSig::Type::ResourceArg:
296 fParams[i].setFlag(Func::ParamInfo::Flags::NativeArg);
297 break;
298 case Native::NativeSig::Type::MixedTV:
299 fParams[i].setFlag(Func::ParamInfo::Flags::NativeArg);
300 fParams[i].setFlag(Func::ParamInfo::Flags::AsTypedValue);
301 break;
302 case Native::NativeSig::Type::Mixed:
303 fParams[i].setFlag(Func::ParamInfo::Flags::AsVariant);
304 break;
305 default:
306 break;
312 f->finishedEmittingParams(fParams);
313 return f;
316 String FuncEmitter::nativeFullname() const {
317 return Native::fullName(name, m_pce ? m_pce->name() : nullptr,
318 (attrs & AttrStatic));
321 Native::NativeFunctionInfo FuncEmitter::getNativeInfo() const {
322 return Native::getNativeFunction(
323 m_ue.m_nativeFuncs,
324 name,
325 m_pce ? m_pce->name() : nullptr,
326 (attrs & AttrStatic)
330 ///////////////////////////////////////////////////////////////////////////////
331 // Locals, iterators, and parameters.
333 void FuncEmitter::allocVarId(const StringData* name, bool slotless) {
334 assertx(name != nullptr);
335 UNUSED Id id;
336 if (m_localNames.find(name) == m_localNames.end()) {
337 // Slotless locals must come after all locals with slots.
338 if (!slotless) {
339 id = (m_numLocals++);
340 assertx(id == (int)m_localNames.size());
342 m_localNames.add(name, name);
346 Id FuncEmitter::allocIterator() {
347 assertx(m_numIterators >= m_nextFreeIterator);
348 Id id = m_nextFreeIterator++;
349 if (m_numIterators < m_nextFreeIterator) {
350 m_numIterators = m_nextFreeIterator;
352 return id;
355 Id FuncEmitter::allocUnnamedLocal() {
356 m_localNames.addUnnamed(nullptr);
357 ++m_numUnnamedLocals;
358 return m_numLocals++;
361 ///////////////////////////////////////////////////////////////////////////////
362 // Unit tables.
364 EHEnt& FuncEmitter::addEHEnt() {
365 assertx(!m_ehTabSorted
366 || "should only mark the ehtab as sorted after adding all of them");
367 ehtab.emplace_back();
368 ehtab.back().m_parentIndex = 7777;
369 return ehtab.back();
372 namespace {
375 * Ordering on EHEnts where e1 < e2 iff
377 * a) e1 and e2 do not overlap, and e1 comes first
378 * b) e1 encloses e2
380 struct EHEntComp {
381 bool operator()(const EHEnt& e1, const EHEnt& e2) const {
382 if (e1.m_base == e2.m_base) {
383 return e1.m_past > e2.m_past;
385 return e1.m_base < e2.m_base;
391 void FuncEmitter::sortEHTab() {
392 if (m_ehTabSorted) return;
394 std::sort(ehtab.begin(), ehtab.end(), EHEntComp());
396 for (unsigned int i = 0; i < ehtab.size(); i++) {
397 ehtab[i].m_parentIndex = -1;
398 for (int j = i - 1; j >= 0; j--) {
399 if (ehtab[j].m_past >= ehtab[i].m_past) {
400 // parent EHEnt better enclose this one.
401 assertx(ehtab[j].m_base <= ehtab[i].m_base);
402 ehtab[i].m_parentIndex = j;
403 break;
408 setEHTabIsSorted();
411 void FuncEmitter::setEHTabIsSorted() {
412 m_ehTabSorted = true;
413 if (!debug) return;
415 Offset curBase = 0;
416 for (size_t i = 0; i < ehtab.size(); ++i) {
417 auto& eh = ehtab[i];
419 // Base offsets must be monotonically increasing.
420 always_assert(curBase <= eh.m_base);
421 curBase = eh.m_base;
423 // Parent should come before, and must enclose this guy.
424 always_assert(eh.m_parentIndex == -1 || eh.m_parentIndex < i);
425 if (eh.m_parentIndex != -1) {
426 auto& parent = ehtab[eh.m_parentIndex];
427 always_assert(parent.m_base <= eh.m_base &&
428 parent.m_past >= eh.m_past);
434 ///////////////////////////////////////////////////////////////////////////////
435 // Complex setters.
437 /* <<__Native>> user attribute causes systemlib declarations
438 * to hook internal (C++) implementation of funcs/methods
440 * The Native attribute may have the following sub-options
441 * "NoFCallBuiltin": Prevent FCallBuiltin optimization
442 * Effectively forces functions to generate an ActRec
443 * "NoInjection": Do not include this frame in backtraces
445 * e.g. <<__Native("NoFCallBuiltin")>> function foo():mixed;
447 static const StaticString
448 s_native("__Native"),
449 s_nofcallbuiltin("NoFCallBuiltin"),
450 s_noinjection("NoInjection"),
451 s_opcodeimpl("OpCodeImpl");
453 int FuncEmitter::parseNativeAttributes(Attr& attrs_) const {
454 int ret = Native::AttrNone;
456 auto it = userAttributes.find(s_native.get());
457 assertx(it != userAttributes.end());
458 const TypedValue userAttr = it->second;
459 assertx(isArrayLikeType(userAttr.m_type));
460 for (ArrayIter it(userAttr.m_data.parr); it; ++it) {
461 Variant userAttrVal = it.second();
462 if (userAttrVal.isString()) {
463 String userAttrStrVal = userAttrVal.toString();
464 if (userAttrStrVal.get()->isame(s_nofcallbuiltin.get())) {
465 attrs_ |= AttrNoFCallBuiltin;
466 } else if (userAttrStrVal.get()->isame(s_noinjection.get())) {
467 attrs_ |= AttrNoInjection;
468 } else if (userAttrStrVal.get()->isame(s_opcodeimpl.get())) {
469 ret |= Native::AttrOpCodeImpl;
473 return ret;
476 Attr FuncEmitter::fix_attrs(Attr a) const {
477 if (RuntimeOption::RepoAuthoritative) return a;
479 a = Attr(a & ~AttrInterceptable);
481 if (RuntimeOption::EvalJitEnableRenameFunction) {
482 return a | AttrInterceptable;
484 return a;
487 ///////////////////////////////////////////////////////////////////////////////
488 // Serialization/Deserialization
490 template<class SerDe>
491 void FuncEmitter::serdeMetaData(SerDe& sd) {
492 // NOTE: name and a few other fields currently handled outside of this.
493 Offset past_delta;
494 Attr a = attrs;
496 if (!SerDe::deserializing) {
497 past_delta = past - base;
498 a = fix_attrs(attrs);
501 sd(line1)
502 (line2)
503 (base)
504 (past_delta)
506 (hniReturnType)
507 (repoReturnType)
508 (repoAwaitedReturnType)
509 (docComment)
510 (m_numLocals)
511 (m_numIterators)
512 (maxStackCells)
513 (m_repoBoolBitset)
515 (params)
516 (m_localNames, [](auto s) { return s; })
517 (ehtab,
518 [&](const EHEnt& prev, EHEnt cur) -> EHEnt {
519 if (!SerDe::deserializing) {
520 cur.m_handler -= cur.m_past;
521 cur.m_past -= cur.m_base;
522 cur.m_base -= prev.m_base;
523 } else {
524 cur.m_base += prev.m_base;
525 cur.m_past += cur.m_base;
526 cur.m_handler += cur.m_past;
528 return cur;
531 (userAttributes)
532 (retTypeConstraint)
533 (retUserType)
534 (retUpperBounds)
535 (originalFilename)
538 if (SerDe::deserializing) {
539 repoReturnType.resolveArray(ue());
540 repoAwaitedReturnType.resolveArray(ue());
541 past = base + past_delta;
542 attrs = fix_attrs(a);
546 ///////////////////////////////////////////////////////////////////////////////
547 // FuncRepoProxy.
549 FuncRepoProxy::FuncRepoProxy(Repo& repo)
550 : RepoProxy(repo),
551 insertFunc{InsertFuncStmt(repo, 0), InsertFuncStmt(repo, 1)},
552 getFuncs{GetFuncsStmt(repo, 0), GetFuncsStmt(repo, 1)}
555 FuncRepoProxy::~FuncRepoProxy() {
558 void FuncRepoProxy::createSchema(int repoId, RepoTxn& txn) {
559 auto createQuery = folly::sformat(
560 "CREATE TABLE {} "
561 "(unitSn INTEGER, funcSn INTEGER, preClassId INTEGER, name TEXT, "
562 " extraData BLOB, PRIMARY KEY (unitSn, funcSn));",
563 m_repo.table(repoId, "Func"));
564 txn.exec(createQuery);
567 void FuncRepoProxy::InsertFuncStmt
568 ::insert(const FuncEmitter& fe,
569 RepoTxn& txn, int64_t unitSn, int funcSn,
570 Id preClassId, const StringData* name) {
571 if (!prepared()) {
572 auto insertQuery = folly::sformat(
573 "INSERT INTO {} "
574 "VALUES(@unitSn, @funcSn, @preClassId, @name, @extraData);",
575 m_repo.table(m_repoId, "Func"));
576 txn.prepare(*this, insertQuery);
579 BlobEncoder extraBlob{fe.useGlobalIds()};
580 RepoTxnQuery query(txn, *this);
581 query.bindInt64("@unitSn", unitSn);
582 query.bindInt("@funcSn", funcSn);
583 query.bindId("@preClassId", preClassId);
584 query.bindStaticString("@name", name);
585 const_cast<FuncEmitter&>(fe).serdeMetaData(extraBlob);
586 query.bindBlob("@extraData", extraBlob, /* static */ true);
587 query.exec();
590 void FuncRepoProxy::GetFuncsStmt
591 ::get(UnitEmitter& ue) {
592 auto txn = RepoTxn{m_repo.begin()};
593 if (!prepared()) {
594 auto selectQuery = folly::sformat(
595 "SELECT funcSn, preClassId, name, extraData "
596 "FROM {} "
597 "WHERE unitSn == @unitSn ORDER BY funcSn ASC;",
598 m_repo.table(m_repoId, "Func"));
599 txn.prepare(*this, selectQuery);
601 RepoTxnQuery query(txn, *this);
602 query.bindInt64("@unitSn", ue.m_sn);
603 do {
604 query.step();
605 if (query.row()) {
606 int funcSn; /**/ query.getInt(0, funcSn);
607 Id preClassId; /**/ query.getId(1, preClassId);
608 StringData* name; /**/ query.getStaticString(2, name);
609 BlobDecoder extraBlob = /**/ query.getBlob(3, ue.useGlobalIds());
611 FuncEmitter* fe;
612 if (preClassId < 0) {
613 fe = ue.newFuncEmitter(name);
614 } else {
615 PreClassEmitter* pce = ue.pce(preClassId);
616 fe = ue.newMethodEmitter(name, pce);
617 bool added UNUSED = pce->addMethod(fe);
618 assertx(added);
620 assertx(fe->sn() == funcSn);
621 fe->serdeMetaData(extraBlob);
622 if (!SystemLib::s_inited) {
623 assertx(fe->attrs & AttrBuiltin);
624 if (preClassId < 0) {
625 assertx(fe->attrs & AttrPersistent);
626 assertx(fe->attrs & AttrUnique);
629 fe->setEHTabIsSorted();
630 fe->finish(fe->past);
632 } while (!query.done());
633 txn.commit();
636 ///////////////////////////////////////////////////////////////////////////////