Remove dead includes in hphp/runtime/vm
[hiphop-php.git] / hphp / runtime / vm / func-emitter.cpp
blob86142c666dcdb5d8c7c5dcd571296d09c85797df
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/ext/extension.h"
20 #include "hphp/runtime/base/unit-cache.h"
21 #include "hphp/runtime/base/rds.h"
23 #include "hphp/runtime/vm/blob-helper.h"
24 #include "hphp/runtime/vm/bytecode.h"
25 #include "hphp/runtime/vm/native.h"
26 #include "hphp/runtime/vm/reified-generics.h"
27 #include "hphp/runtime/vm/repo.h"
28 #include "hphp/runtime/vm/runtime.h"
30 #include "hphp/runtime/vm/jit/types.h"
32 #include "hphp/runtime/vm/verifier/cfg.h"
34 #include "hphp/system/systemlib.h"
36 #include "hphp/util/atomic-vector.h"
37 #include "hphp/util/file.h"
38 #include "hphp/util/trace.h"
40 namespace HPHP {
41 ///////////////////////////////////////////////////////////////////////////////
42 // FuncEmitter.
44 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, Id id, const StringData* n)
45 : m_ue(ue)
46 , m_pce(nullptr)
47 , m_sn(sn)
48 , m_id(id)
49 , name(n)
50 , top(false)
51 , maxStackCells(0)
52 , hniReturnType(folly::none)
53 , retUserType(nullptr)
54 , docComment(nullptr)
55 , originalFilename(nullptr)
56 , memoizePropName(nullptr)
57 , memoizeGuardPropName(nullptr)
58 , memoizeSharedPropIndex(0)
59 , m_numLocals(0)
60 , m_numUnnamedLocals(0)
61 , m_activeUnnamedLocals(0)
62 , m_numIterators(0)
63 , m_nextFreeIterator(0)
64 , m_ehTabSorted(false)
67 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, const StringData* n,
68 PreClassEmitter* pce)
69 : m_ue(ue)
70 , m_pce(pce)
71 , m_sn(sn)
72 , m_id(kInvalidId)
73 , name(n)
74 , top(false)
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_activeUnnamedLocals(0)
86 , m_numIterators(0)
87 , m_nextFreeIterator(0)
88 , m_ehTabSorted(false)
91 FuncEmitter::~FuncEmitter() {
95 ///////////////////////////////////////////////////////////////////////////////
96 // Initialization and execution.
98 void FuncEmitter::init(int l1, int l2, Offset base_, Attr attrs_, bool top_,
99 const StringData* docComment_) {
100 base = base_;
101 line1 = l1;
102 line2 = l2;
103 top = top_;
104 attrs = fix_attrs(attrs_);
105 docComment = docComment_;
107 if (!isPseudoMain()) {
108 if (!SystemLib::s_inited) {
109 assertx(attrs & AttrBuiltin);
114 void FuncEmitter::finish(Offset past_) {
115 past = past_;
116 sortEHTab();
119 void FuncEmitter::commit(RepoTxn& txn) const {
120 Repo& repo = Repo::get();
121 FuncRepoProxy& frp = repo.frp();
122 int repoId = m_ue.m_repoId;
123 int64_t usn = m_ue.m_sn;
125 frp.insertFunc[repoId]
126 .insert(*this, txn, usn, m_sn, m_pce ? m_pce->id() : -1, name, top);
129 Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
130 bool isGenerated = isdigit(name->data()[0]);
132 auto attrs = fix_attrs(this->attrs);
133 if (preClass && preClass->attrs() & AttrInterface) {
134 attrs |= AttrAbstract;
136 if (attrs & AttrIsMethCaller && RuntimeOption::RepoAuthoritative) {
137 attrs |= AttrPersistent | AttrUnique;
139 if (attrs & AttrPersistent && !preClass) {
140 if ((RuntimeOption::EvalJitEnableRenameFunction ||
141 attrs & AttrInterceptable ||
142 (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited))) {
143 if (attrs & AttrBuiltin) {
144 SystemLib::s_anyNonPersistentBuiltins = true;
146 attrs = Attr(attrs & ~AttrPersistent);
148 } else {
149 assertx(preClass || !(attrs & AttrBuiltin) || (attrs & AttrIsMethCaller));
151 if (!RuntimeOption::RepoAuthoritative) {
152 // In non-RepoAuthoritative mode, any function could get a VarEnv because
153 // of evalPHPDebugger.
154 attrs |= AttrMayUseVV;
155 } else if ((attrs & AttrInterceptable) &&
156 !name->empty() &&
157 !Func::isSpecial(name) &&
158 !isClosureBody) {
159 // intercepted functions need to pass all args through
160 // to the interceptee
161 attrs |= AttrMayUseVV;
163 if (isVariadic()) {
164 attrs |= AttrVariadicParam;
166 if (isAsync && !isGenerator) {
167 // Async functions can return results directly.
168 attrs |= AttrSupportsAsyncEagerReturn;
171 assertx(!m_pce == !preClass);
172 auto f = m_ue.newFunc(this, unit, name, attrs, params.size());
174 f->m_isPreFunc = !!preClass;
176 auto const uait = userAttributes.find(s___Reified.get());
177 auto const hasReifiedGenerics = uait != userAttributes.end();
179 bool const needsExtendedSharedData =
180 isNative ||
181 line2 - line1 >= Func::kSmallDeltaLimit ||
182 past - base >= Func::kSmallDeltaLimit ||
183 hasReifiedGenerics;
185 f->m_shared.reset(
186 needsExtendedSharedData
187 ? new Func::ExtendedSharedData(preClass, base, past, line1, line2,
188 top, !containsCalls, docComment)
189 : new Func::SharedData(preClass, base, past,
190 line1, line2, top, !containsCalls, docComment)
193 f->init(params.size());
195 if (auto const ex = f->extShared()) {
196 ex->m_hasExtendedSharedData = true;
197 ex->m_arFuncPtr = nullptr;
198 ex->m_nativeFuncPtr = nullptr;
199 ex->m_line2 = line2;
200 ex->m_past = past;
201 ex->m_returnByValue = false;
202 ex->m_isMemoizeWrapper = false;
203 ex->m_isMemoizeWrapperLSB = false;
206 std::vector<Func::ParamInfo> fParams;
207 for (unsigned i = 0; i < params.size(); ++i) {
208 Func::ParamInfo pi = params[i];
209 if (pi.isVariadic()) {
210 pi.builtinType = RuntimeOption::EvalHackArrDVArrs
211 ? KindOfVec : KindOfArray;
213 f->appendParam(params[i].inout, pi, fParams);
216 auto const originalFullName =
217 (!originalFilename ||
218 !RuntimeOption::RepoAuthoritative ||
219 FileUtil::isAbsolutePath(originalFilename->slice())) ?
220 originalFilename :
221 makeStaticString(RuntimeOption::SourceRoot +
222 originalFilename->toCppString());
224 f->shared()->m_localNames.create(m_localNames);
225 f->shared()->m_numLocals = m_numLocals;
226 f->shared()->m_numIterators = m_numIterators;
227 f->m_maxStackCells = maxStackCells;
228 f->shared()->m_ehtab = ehtab;
229 f->shared()->m_isClosureBody = isClosureBody;
230 f->shared()->m_isAsync = isAsync;
231 f->shared()->m_isGenerator = isGenerator;
232 f->shared()->m_isPairGenerator = isPairGenerator;
233 f->shared()->m_userAttributes = userAttributes;
234 f->shared()->m_retTypeConstraint = retTypeConstraint;
235 f->shared()->m_retUserType = retUserType;
236 f->shared()->m_originalFilename = originalFullName;
237 f->shared()->m_isGenerated = isGenerated;
238 f->shared()->m_repoReturnType = repoReturnType;
239 f->shared()->m_repoAwaitedReturnType = repoAwaitedReturnType;
240 f->shared()->m_isMemoizeWrapper = isMemoizeWrapper;
241 f->shared()->m_isMemoizeWrapperLSB = isMemoizeWrapperLSB;
242 f->shared()->m_hasReifiedGenerics = hasReifiedGenerics;
243 f->shared()->m_isRxDisabled = isRxDisabled;
245 if (hasReifiedGenerics) {
246 auto tv = uait->second;
247 assertx(tvIsVecOrVArray(tv));
248 f->extShared()->m_reifiedGenericsInfo =
249 extractSizeAndPosFromReifiedAttribute(tv.m_data.parr);
252 if (isNative) {
253 auto const ex = f->extShared();
255 ex->m_hniReturnType = hniReturnType;
257 auto const info = getNativeInfo();
259 Attr dummy = AttrNone;
260 auto nativeAttributes = parseNativeAttributes(dummy);
261 Native::getFunctionPointers(
262 info,
263 nativeAttributes,
264 ex->m_arFuncPtr,
265 ex->m_nativeFuncPtr
268 if (ex->m_nativeFuncPtr) {
269 if (info.sig.ret == Native::NativeSig::Type::MixedTV) {
270 ex->m_returnByValue = true;
272 int extra = isMethod() ? 1 : 0;
273 assertx(info.sig.args.size() == params.size() + extra);
274 for (auto i = params.size(); i--; ) {
275 switch (info.sig.args[extra + i]) {
276 case Native::NativeSig::Type::ObjectArg:
277 case Native::NativeSig::Type::StringArg:
278 case Native::NativeSig::Type::ArrayArg:
279 case Native::NativeSig::Type::ResourceArg:
280 case Native::NativeSig::Type::MixedTV:
281 fParams[i].nativeArg = true;
282 break;
283 default:
284 break;
290 f->finishedEmittingParams(fParams);
291 return f;
294 String FuncEmitter::nativeFullname() const {
295 return Native::fullName(name, m_pce ? m_pce->name() : nullptr,
296 (attrs & AttrStatic));
299 Native::NativeFunctionInfo FuncEmitter::getNativeInfo() const {
300 return Native::getNativeFunction(
301 m_ue.m_nativeFuncs,
302 name,
303 m_pce ? m_pce->name() : nullptr,
304 (attrs & AttrStatic)
308 ///////////////////////////////////////////////////////////////////////////////
309 // Locals, iterators, and parameters.
311 void FuncEmitter::allocVarId(const StringData* name) {
312 assertx(name != nullptr);
313 // Unnamed locals are segregated (they all come after the named locals).
314 assertx(m_numUnnamedLocals == 0);
315 UNUSED Id id;
316 if (m_localNames.find(name) == m_localNames.end()) {
317 id = (m_numLocals++);
318 assertx(id == (int)m_localNames.size());
319 m_localNames.add(name, name);
323 Id FuncEmitter::allocIterator() {
324 assertx(m_numIterators >= m_nextFreeIterator);
325 Id id = m_nextFreeIterator++;
326 if (m_numIterators < m_nextFreeIterator) {
327 m_numIterators = m_nextFreeIterator;
329 return id;
332 Id FuncEmitter::allocUnnamedLocal() {
333 ++m_activeUnnamedLocals;
334 if (m_activeUnnamedLocals > m_numUnnamedLocals) {
335 ++m_numLocals;
336 ++m_numUnnamedLocals;
338 return numNamedLocals() - 1 + m_activeUnnamedLocals;
342 ///////////////////////////////////////////////////////////////////////////////
343 // Unit tables.
345 EHEnt& FuncEmitter::addEHEnt() {
346 assertx(!m_ehTabSorted
347 || "should only mark the ehtab as sorted after adding all of them");
348 ehtab.emplace_back();
349 ehtab.back().m_parentIndex = 7777;
350 return ehtab.back();
353 namespace {
356 * Ordering on EHEnts where e1 < e2 iff
358 * a) e1 and e2 do not overlap, and e1 comes first
359 * b) e1 encloses e2
361 struct EHEntComp {
362 bool operator()(const EHEnt& e1, const EHEnt& e2) const {
363 if (e1.m_base == e2.m_base) {
364 return e1.m_past > e2.m_past;
366 return e1.m_base < e2.m_base;
372 void FuncEmitter::sortEHTab() {
373 if (m_ehTabSorted) return;
375 std::sort(ehtab.begin(), ehtab.end(), EHEntComp());
377 for (unsigned int i = 0; i < ehtab.size(); i++) {
378 ehtab[i].m_parentIndex = -1;
379 for (int j = i - 1; j >= 0; j--) {
380 if (ehtab[j].m_past >= ehtab[i].m_past) {
381 // parent EHEnt better enclose this one.
382 assertx(ehtab[j].m_base <= ehtab[i].m_base);
383 ehtab[i].m_parentIndex = j;
384 break;
389 setEHTabIsSorted();
392 void FuncEmitter::setEHTabIsSorted() {
393 m_ehTabSorted = true;
394 if (!debug) return;
396 Offset curBase = 0;
397 for (size_t i = 0; i < ehtab.size(); ++i) {
398 auto& eh = ehtab[i];
400 // Base offsets must be monotonically increasing.
401 always_assert(curBase <= eh.m_base);
402 curBase = eh.m_base;
404 // Parent should come before, and must enclose this guy.
405 always_assert(eh.m_parentIndex == -1 || eh.m_parentIndex < i);
406 if (eh.m_parentIndex != -1) {
407 auto& parent = ehtab[eh.m_parentIndex];
408 always_assert(parent.m_base <= eh.m_base &&
409 parent.m_past >= eh.m_past);
415 ///////////////////////////////////////////////////////////////////////////////
416 // Complex setters.
418 /* <<__Native>> user attribute causes systemlib declarations
419 * to hook internal (C++) implementation of funcs/methods
421 * The Native attribute may have the following sub-options
422 * "NoFCallBuiltin": Prevent FCallBuiltin optimization
423 * Effectively forces functions to generate an ActRec
424 * "NoInjection": Do not include this frame in backtraces
426 * e.g. <<__Native("NoFCallBuiltin")>> function foo():mixed;
428 static const StaticString
429 s_native("__Native"),
430 s_nofcallbuiltin("NoFCallBuiltin"),
431 s_noinjection("NoInjection"),
432 s_opcodeimpl("OpCodeImpl");
434 int FuncEmitter::parseNativeAttributes(Attr& attrs_) const {
435 int ret = Native::AttrNone;
437 auto it = userAttributes.find(s_native.get());
438 assertx(it != userAttributes.end());
439 const TypedValue userAttr = it->second;
440 assertx(isArrayLikeType(userAttr.m_type));
441 for (ArrayIter it(userAttr.m_data.parr); it; ++it) {
442 Variant userAttrVal = it.second();
443 if (userAttrVal.isString()) {
444 String userAttrStrVal = userAttrVal.toString();
445 if (userAttrStrVal.get()->isame(s_nofcallbuiltin.get())) {
446 attrs_ |= AttrNoFCallBuiltin;
447 } else if (userAttrStrVal.get()->isame(s_noinjection.get())) {
448 attrs_ |= AttrNoInjection;
449 } else if (userAttrStrVal.get()->isame(s_opcodeimpl.get())) {
450 ret |= Native::AttrOpCodeImpl;
454 return ret;
457 Attr FuncEmitter::fix_attrs(Attr a) const {
458 if (RuntimeOption::RepoAuthoritative) return a;
460 a = Attr(a & ~AttrInterceptable);
462 if (RuntimeOption::EvalJitEnableRenameFunction) {
463 return a | AttrInterceptable;
466 if (!RuntimeOption::DynamicInvokeFunctions.empty()) {
467 auto const fullName = [&] {
468 if (m_pce) return folly::sformat("{}::{}", m_pce->name(), name);
469 return name->toCppString();
470 }();
471 if (RuntimeOption::DynamicInvokeFunctions.count(fullName)) {
472 return a | AttrInterceptable;
475 return a;
478 ///////////////////////////////////////////////////////////////////////////////
479 // Serialization/Deserialization
481 template<class SerDe>
482 void FuncEmitter::serdeMetaData(SerDe& sd) {
483 // NOTE: name, top, and a few other fields currently handled outside of this.
484 Offset past_delta;
485 Attr a = attrs;
487 if (!SerDe::deserializing) {
488 past_delta = past - base;
489 a = fix_attrs(attrs);
492 sd(line1)
493 (line2)
494 (base)
495 (past_delta)
497 (hniReturnType)
498 (repoReturnType)
499 (repoAwaitedReturnType)
500 (docComment)
501 (m_numLocals)
502 (m_numIterators)
503 (maxStackCells)
504 (m_repoBoolBitset)
506 (params)
507 (m_localNames, [](auto s) { return s; })
508 (ehtab,
509 [&](const EHEnt& prev, EHEnt cur) -> EHEnt {
510 if (!SerDe::deserializing) {
511 cur.m_handler -= cur.m_past;
512 cur.m_past -= cur.m_base;
513 cur.m_base -= prev.m_base;
514 } else {
515 cur.m_base += prev.m_base;
516 cur.m_past += cur.m_base;
517 cur.m_handler += cur.m_past;
519 return cur;
522 (userAttributes)
523 (retTypeConstraint)
524 (retUserType)
525 (originalFilename)
528 if (SerDe::deserializing) {
529 repoReturnType.resolveArray(ue());
530 repoAwaitedReturnType.resolveArray(ue());
531 past = base + past_delta;
532 attrs = fix_attrs(a);
536 ///////////////////////////////////////////////////////////////////////////////
537 // FuncRepoProxy.
539 FuncRepoProxy::FuncRepoProxy(Repo& repo)
540 : RepoProxy(repo),
541 insertFunc{InsertFuncStmt(repo, 0), InsertFuncStmt(repo, 1)},
542 getFuncs{GetFuncsStmt(repo, 0), GetFuncsStmt(repo, 1)}
545 FuncRepoProxy::~FuncRepoProxy() {
548 void FuncRepoProxy::createSchema(int repoId, RepoTxn& txn) {
549 auto createQuery = folly::sformat(
550 "CREATE TABLE {} "
551 "(unitSn INTEGER, funcSn INTEGER, preClassId INTEGER, name TEXT, "
552 " top INTEGER, extraData BLOB, PRIMARY KEY (unitSn, funcSn));",
553 m_repo.table(repoId, "Func"));
554 txn.exec(createQuery);
557 void FuncRepoProxy::InsertFuncStmt
558 ::insert(const FuncEmitter& fe,
559 RepoTxn& txn, int64_t unitSn, int funcSn,
560 Id preClassId, const StringData* name,
561 bool top) {
562 if (!prepared()) {
563 auto insertQuery = folly::sformat(
564 "INSERT INTO {} "
565 "VALUES(@unitSn, @funcSn, @preClassId, @name, @top, @extraData);",
566 m_repo.table(m_repoId, "Func"));
567 txn.prepare(*this, insertQuery);
570 BlobEncoder extraBlob{fe.useGlobalIds()};
571 RepoTxnQuery query(txn, *this);
572 query.bindInt64("@unitSn", unitSn);
573 query.bindInt("@funcSn", funcSn);
574 query.bindId("@preClassId", preClassId);
575 query.bindStaticString("@name", name);
576 query.bindBool("@top", top);
577 const_cast<FuncEmitter&>(fe).serdeMetaData(extraBlob);
578 query.bindBlob("@extraData", extraBlob, /* static */ true);
579 query.exec();
582 void FuncRepoProxy::GetFuncsStmt
583 ::get(UnitEmitter& ue) {
584 auto txn = RepoTxn{m_repo.begin()};
585 if (!prepared()) {
586 auto selectQuery = folly::sformat(
587 "SELECT funcSn, preClassId, name, top, extraData "
588 "FROM {} "
589 "WHERE unitSn == @unitSn ORDER BY funcSn ASC;",
590 m_repo.table(m_repoId, "Func"));
591 txn.prepare(*this, selectQuery);
593 RepoTxnQuery query(txn, *this);
594 query.bindInt64("@unitSn", ue.m_sn);
595 do {
596 query.step();
597 if (query.row()) {
598 int funcSn; /**/ query.getInt(0, funcSn);
599 Id preClassId; /**/ query.getId(1, preClassId);
600 StringData* name; /**/ query.getStaticString(2, name);
601 bool top; /**/ query.getBool(3, top);
602 BlobDecoder extraBlob = /**/ query.getBlob(4, ue.useGlobalIds());
604 FuncEmitter* fe;
605 if (preClassId < 0) {
606 fe = ue.newFuncEmitter(name);
607 } else {
608 PreClassEmitter* pce = ue.pce(preClassId);
609 fe = ue.newMethodEmitter(name, pce);
610 bool added UNUSED = pce->addMethod(fe);
611 assertx(added);
613 assertx(fe->sn() == funcSn);
614 fe->top = top;
615 fe->serdeMetaData(extraBlob);
616 if (!SystemLib::s_inited && !fe->isPseudoMain()) {
617 assertx(fe->attrs & AttrBuiltin);
618 if (preClassId < 0) {
619 assertx(fe->attrs & AttrPersistent);
620 assertx(fe->attrs & AttrUnique);
623 fe->setEHTabIsSorted();
624 fe->finish(fe->past);
626 } while (!query.done());
627 txn.commit();
630 ///////////////////////////////////////////////////////////////////////////////