std::unique_ptr<Unit> UnitEmitter::create() { auto u = folly::make_unique<Unit>(); u->m_repoId = m_repoId; u->m_sn = m_sn; u->m_bc = allocateBCRegion(m_bc, m_bclen); u->m_bclen = m_bclen; u->m_filepath = m_filepath; u->m_mainReturn = m_mainReturn; u->m_mergeOnly = m_mergeOnly; u->m_isHHFile = m_isHHFile; u->m_useStrictTypes = m_useStrictTypes; { const std::string& dirname = FileUtil::safe_dirname(m_filepath->data(), m_filepath->size()); u->m_dirpath = makeStaticString(dirname); } u->m_md5 = m_md5; for (unsigned i = 0; i < m_litstrs.size(); ++i) { NamedEntityPair np; np.first = m_litstrs[i]; np.second = nullptr; u->m_namedInfo.push_back(np); } u->m_arrays = [&]() -> std::vector<const ArrayData*> { auto ret = std::vector<const ArrayData*>{}; for (unsigned i = 0; i < m_arrays.size(); ++i) { ret.push_back(m_arrays[i]); } return ret; }(); for (auto const& pce : m_pceVec) { u->m_preClasses.push_back(PreClassPtr(pce->create(*u))); } u->m_typeAliases = m_typeAliases; size_t ix = m_fes.size() + m_hoistablePceIdList.size(); if (m_mergeOnly && !m_allClassesHoistable) { size_t extra = 0; for (auto& mergeable : m_mergeableStmts) { extra++; if (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited) { if (mergeable.first != MergeKind::Class) { extra = 0; u->m_mergeOnly = false; break; } } else { switch (mergeable.first) { case MergeKind::PersistentDefine: case MergeKind::Define: case MergeKind::Global: extra += sizeof(TypedValueAux) / sizeof(void*); break; default: break; } } } ix += extra; } Unit::MergeInfo *mi = Unit::MergeInfo::alloc(ix); u->m_mergeInfo = mi; ix = 0; for (auto& fe : m_fes) { Func* func = fe->create(*u); if (func->top()) { if (!mi->m_firstHoistableFunc) { mi->m_firstHoistableFunc = ix; } } else { assert(!mi->m_firstHoistableFunc); } mi->mergeableObj(ix++) = func; } assert(u->getMain()->isPseudoMain()); if (!mi->m_firstHoistableFunc) { mi->m_firstHoistableFunc = ix; } mi->m_firstHoistablePreClass = ix; assert(m_fes.size()); for (auto& id : m_hoistablePceIdList) { mi->mergeableObj(ix++) = u->m_preClasses[id].get(); } mi->m_firstMergeablePreClass = ix; if (u->m_mergeOnly && !m_allClassesHoistable) { for (auto& mergeable : m_mergeableStmts) { switch (mergeable.first) { case MergeKind::Class: mi->mergeableObj(ix++) = u->m_preClasses[mergeable.second].get(); break; case MergeKind::TypeAlias: mi->mergeableObj(ix++) = (void*)((intptr_t(mergeable.second) << 3) + (int)mergeable.first); break; case MergeKind::ReqDoc: { assert(RuntimeOption::RepoAuthoritative); void* name = u->lookupLitstrId(mergeable.second); mi->mergeableObj(ix++) = (char*)name + (int)mergeable.first; break; } case MergeKind::Define: case MergeKind::Global: assert(RuntimeOption::RepoAuthoritative); case MergeKind::PersistentDefine: { void* name = u->lookupLitstrId (m_mergeableValues[mergeable.second].first); mi->mergeableObj(ix++) = (char*)name + (int)mergeable.first; auto& tv = m_mergeableValues[mergeable.second].second; auto* tva = (TypedValueAux*)mi->mergeableData(ix); tva->m_data = tv.m_data; tva->m_type = tv.m_type; // leave tva->m_aux uninitialized ix += sizeof(*tva) / sizeof(void*); assert(sizeof(*tva) % sizeof(void*) == 0); break; } case MergeKind::Done: case MergeKind::UniqueDefinedClass: not_reached(); } } } assert(ix == mi->m_mergeablesSize); mi->mergeableObj(ix) = (void*)MergeKind::Done; /* * What's going on is we're going to have a m_lineTable if this UnitEmitter * was loaded from the repo, and no m_sourceLocTab (it's demand-loaded by * unit.cpp because it's only used for the debugger). Don't bother creating * the line table here, because we can retrieve it from the repo later. * * On the other hand, if this unit was just created by parsing a php file (or * whatnot) which was not committed to the repo, we'll have a m_sourceLocTab. * In this case we should populate m_lineTable (otherwise we might lose line * info altogether, since it may not be backed by a repo). */ if (m_sourceLocTab.size() != 0) { stashLineTable(u.get(), createLineTable(m_sourceLocTab, m_bclen)); } for (size_t i = 0; i < m_feTab.size(); ++i) { assert(m_feTab[i].second->past == m_feTab[i].first); assert(m_fMap.find(m_feTab[i].second) != m_fMap.end()); u->m_funcTable.push_back( FuncEntry(m_feTab[i].first, m_fMap.find(m_feTab[i].second)->second)); } // Funcs can be recorded out of order when loading them from the // repo currently. So sort 'em here. std::sort(u->m_funcTable.begin(), u->m_funcTable.end()); m_fMap.clear(); if (RuntimeOption::EvalDumpBytecode) { // Dump human-readable bytecode. Trace::traceRelease("%s", u->toString().c_str()); } if (RuntimeOption::EvalDumpHhas && SystemLib::s_inited) { std::printf("%s", disassemble(u.get()).c_str()); std::fflush(stdout); _Exit(0); } static const bool kVerify = debug || getenv("HHVM_VERIFY"); static const bool kVerifyVerboseSystem = getenv("HHVM_VERIFY_VERBOSE_SYSTEM"); static const bool kVerifyVerbose = kVerifyVerboseSystem || getenv("HHVM_VERIFY_VERBOSE"); const bool isSystemLib = u->filepath()->empty() || boost::contains(u->filepath()->data(), "systemlib"); const bool doVerify = kVerify || boost::ends_with(u->filepath()->data(), "hhas"); if (doVerify) { auto const verbose = isSystemLib ? kVerifyVerboseSystem : kVerifyVerbose; auto const ok = Verifier::checkUnit(u.get(), verbose); if (!ok && !verbose) { std::cerr << folly::format( "Verification failed for unit {}. Re-run with HHVM_VERIFY_VERBOSE{}=1 " "to see more details.\n", u->filepath()->data(), isSystemLib ? "_SYSTEM" : "" ); } } return u; }
std::unique_ptr<Unit> UnitEmitter::create(bool saveLineTable) { INC_TPC(unit_load); std::unique_ptr<Unit> u { RuntimeOption::RepoAuthoritative && !RuntimeOption::SandboxMode && m_litstrs.empty() && m_arrayTypeTable.empty() ? new Unit : new UnitExtended }; u->m_repoId = saveLineTable ? RepoIdInvalid : m_repoId; u->m_sn = m_sn; u->m_bc = allocateBCRegion(m_bc, m_bclen); u->m_bclen = m_bclen; u->m_filepath = m_filepath; u->m_mainReturn = m_mainReturn; u->m_mergeOnly = m_mergeOnly; u->m_isHHFile = m_isHHFile; u->m_useStrictTypes = m_useStrictTypes; u->m_useStrictTypesForBuiltins = m_useStrictTypesForBuiltins; u->m_dirpath = makeStaticString(FileUtil::dirname(StrNR{m_filepath})); u->m_md5 = m_md5; u->m_arrays = m_arrays; for (auto const& pce : m_pceVec) { u->m_preClasses.push_back(PreClassPtr(pce->create(*u))); } u->m_typeAliases = m_typeAliases; u->m_metaData = m_metaData; size_t ix = m_fes.size() + m_hoistablePceIdList.size(); if (m_mergeOnly && !m_allClassesHoistable) { size_t extra = 0; for (auto& mergeable : m_mergeableStmts) { extra++; if (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited) { if (mergeable.first != MergeKind::Class) { extra = 0; u->m_mergeOnly = false; break; } } else { switch (mergeable.first) { case MergeKind::PersistentDefine: case MergeKind::Define: case MergeKind::Global: extra += sizeof(TypedValueAux) / sizeof(void*); break; default: break; } } } ix += extra; } Unit::MergeInfo *mi = Unit::MergeInfo::alloc(ix); u->m_mergeInfo.store(mi, std::memory_order_relaxed); ix = 0; for (auto& fe : m_fes) { Func* func = fe->create(*u); if (func->top()) { if (!mi->m_firstHoistableFunc) { mi->m_firstHoistableFunc = ix; } } else { assertx(!mi->m_firstHoistableFunc); } assertx(ix == fe->id()); mi->mergeableObj(ix++) = func; } assertx(u->getMain(nullptr)->isPseudoMain()); if (!mi->m_firstHoistableFunc) { mi->m_firstHoistableFunc = ix; } mi->m_firstHoistablePreClass = ix; assertx(m_fes.size()); for (auto& id : m_hoistablePceIdList) { mi->mergeableObj(ix++) = u->m_preClasses[id].get(); } mi->m_firstMergeablePreClass = ix; if (u->m_mergeOnly && !m_allClassesHoistable) { for (auto& mergeable : m_mergeableStmts) { switch (mergeable.first) { case MergeKind::Class: mi->mergeableObj(ix++) = u->m_preClasses[mergeable.second].get(); break; case MergeKind::TypeAlias: mi->mergeableObj(ix++) = (void*)((intptr_t(mergeable.second) << 3) + (int)mergeable.first); break; case MergeKind::ReqDoc: { assertx(RuntimeOption::RepoAuthoritative); void* name = u->lookupLitstrId(mergeable.second); mi->mergeableObj(ix++) = (char*)name + (int)mergeable.first; break; } case MergeKind::Define: case MergeKind::Global: assertx(RuntimeOption::RepoAuthoritative); case MergeKind::PersistentDefine: { void* name = u->lookupLitstrId (m_mergeableValues[mergeable.second].first); mi->mergeableObj(ix++) = (char*)name + (int)mergeable.first; auto& tv = m_mergeableValues[mergeable.second].second; auto* tva = (TypedValueAux*)mi->mergeableData(ix); tva->m_data = tv.m_data; tva->m_type = tv.m_type; // leave tva->m_aux uninitialized ix += sizeof(*tva) / sizeof(void*); assertx(sizeof(*tva) % sizeof(void*) == 0); break; } case MergeKind::Done: case MergeKind::UniqueDefinedClass: not_reached(); } } } assertx(ix == mi->m_mergeablesSize); mi->mergeableObj(ix) = (void*)MergeKind::Done; /* * What's going on is we're going to have a m_lineTable if this UnitEmitter * was loaded from the repo, and no m_sourceLocTab (it's demand-loaded by * unit.cpp because it's only used for the debugger). Don't bother creating * the line table here, because we can retrieve it from the repo later. * * On the other hand, if this unit was just created by parsing a php file (or * whatnot) which was not committed to the repo, we'll have a m_sourceLocTab. * In this case we should populate m_lineTable (otherwise we might lose line * info altogether, since it may not be backed by a repo). */ if (m_sourceLocTab.size() != 0) { stashLineTable(u.get(), createLineTable(m_sourceLocTab, m_bclen)); // If we plan to dump hhas we will need the extended line table information // in the output, and if we're not writing the repo, stashing it here is // necessary for it to make it through. if (RuntimeOption::RepoDebugInfo && RuntimeOption::EvalDumpHhas && SystemLib::s_inited) { stashExtendedLineTable(u.get(), createSourceLocTable()); } } else if (saveLineTable) { stashLineTable(u.get(), m_lineTable); } if (u->m_extended) { auto ux = u->getExtended(); for (auto s : m_litstrs) { ux->m_namedInfo.push_back(s); } ux->m_arrayTypeTable = m_arrayTypeTable; for (auto const fe : m_feTab) { assertx(m_fMap.find(fe) != m_fMap.end()); auto const func = m_fMap.find(fe)->second; ux->m_funcTable.push_back(func); } // Funcs can be recorded out of order when loading them from the // repo currently. So sort 'em here. std::sort(ux->m_funcTable.begin(), ux->m_funcTable.end(), [] (const Func* a, const Func* b) { return a->past() < b->past(); }); m_fMap.clear(); } else { assertx(!m_litstrs.size()); assertx(m_arrayTypeTable.empty()); } static const bool kVerify = debug || RuntimeOption::EvalVerify || RuntimeOption::EvalVerifyOnly; static const bool kVerifyVerboseSystem = getenv("HHVM_VERIFY_VERBOSE_SYSTEM"); static const bool kVerifyVerbose = kVerifyVerboseSystem || getenv("HHVM_VERIFY_VERBOSE"); const bool isSystemLib = u->filepath()->empty() || boost::contains(u->filepath()->data(), "systemlib"); const bool doVerify = kVerify || boost::ends_with(u->filepath()->data(), "hhas"); if (doVerify) { auto const verbose = isSystemLib ? kVerifyVerboseSystem : kVerifyVerbose; auto const ok = Verifier::checkUnit( u.get(), verbose ? Verifier::kVerbose : Verifier::kStderr ); if (!ok && !verbose) { std::cerr << folly::format( "Verification failed for unit {}. Re-run with HHVM_VERIFY_VERBOSE{}=1 " "to see more details.\n", u->filepath()->data(), isSystemLib ? "_SYSTEM" : "" ); } } if (RuntimeOption::EvalVerifyOnly) { std::fflush(stdout); _Exit(0); } if (RuntimeOption::EvalDumpHhas > 1 || (SystemLib::s_inited && RuntimeOption::EvalDumpHhas == 1)) { auto const& hhaspath = RuntimeOption::EvalDumpHhasToFile; if (!hhaspath.empty()) { static std::atomic<bool> first_unit{true}; auto const flags = O_WRONLY | O_CREAT | (first_unit ? O_TRUNC : O_APPEND); if (!folly::writeFile(disassemble(u.get()), hhaspath.c_str(), flags)) { Logger::Error("Failed to write hhas to %s", hhaspath.c_str()); _Exit(1); } first_unit = false; } else { std::printf("%s", disassemble(u.get()).c_str()); std::fflush(stdout); } if (SystemLib::s_inited) { _Exit(0); } } if (RuntimeOption::EvalDumpBytecode) { // Dump human-readable bytecode. Trace::traceRelease("%s", u->toString().c_str()); } return u; }