bool Unit::compileTimeFatal(const StringData*& msg, int& line) const { // A compile-time fatal is encoded as a pseudomain that contains precisely: // // String <id>; Fatal; // // Decode enough of pseudomain to determine whether it contains a // compile-time fatal, and if so, extract the error message and line number. const Opcode* entry = getMain()->getEntry(); const Opcode* pc = entry; // String <id>; Fatal; // ^^^^^^ if (*pc != OpString) { return false; } pc++; // String <id>; Fatal; // ^^^^ Id id = *(Id*)pc; pc += sizeof(Id); // String <id>; Fatal; // ^^^^^ if (*pc != OpFatal) { return false; } msg = lookupLitstrId(id); line = getLineNumber(Offset(pc - entry)); return true; }
void IRTranslator::translateNewStructArray(const NormalizedInstruction& i) { auto numArgs = i.immVec.size(); auto ids = i.immVec.vec32(); auto unit = m_hhbcTrans.curUnit(); StringData* keys[MixedArray::MaxMakeSize]; for (size_t i = 0; i < numArgs; i++) { keys[i] = unit->lookupLitstrId(ids[i]); } HHIR_EMIT(NewStructArray, numArgs, keys); }
void inlSingletonSProp(IRGS& env, const Func* func, const Op* clsOp, const Op* propOp) { assertx(*clsOp == Op::String); assertx(*propOp == Op::String); TransFlags trflags; trflags.noinlineSingleton = true; auto exitBlock = makeExit(env, trflags); // Pull the class and property names. auto const unit = func->unit(); auto const clsName = unit->lookupLitstrId(getImmPtr(clsOp, 0)->u_SA); auto const propName = unit->lookupLitstrId(getImmPtr(propOp, 0)->u_SA); // Make sure we have a valid class. auto const cls = Unit::lookupClass(clsName); if (UNLIKELY(!classHasPersistentRDS(cls))) { PUNT(SingletonSProp-Persistent); } // Make sure the sprop is accessible from the singleton method's context. auto const lookup = cls->findSProp(func->cls(), propName); if (UNLIKELY(lookup.prop == kInvalidSlot || !lookup.accessible)) { PUNT(SingletonSProp-Accessibility); } // Look up the static property. auto const sprop = ldClsPropAddrKnown(env, cls, propName); auto const unboxed = gen(env, UnboxPtr, sprop); auto const value = gen(env, LdMem, unboxed->type().deref(), unboxed); // Side exit if the static property is null. auto isnull = gen(env, IsType, TNull, value); gen(env, JmpNZero, exitBlock, isnull); // Return the singleton. pushIncRef(env, value); }
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; }
bool IRTranslator::tryTranslateSingletonInline(const NormalizedInstruction& i, const Func* funcd) { using Atom = BCPattern::Atom; using Captures = BCPattern::CaptureVec; if (!funcd) return false; // Make sure we have an acceptable FPush and non-null callee. assert(i.op() == Op::FPushFuncD || i.op() == Op::FPushClsMethodD); auto fcall = i.nextSk(); // Check if the next instruction is an acceptable FCall. if ((fcall.op() != Op::FCall && fcall.op() != Op::FCallD) || funcd->isResumable() || funcd->isReturnRef()) { return false; } // First, check for the static local singleton pattern... // Lambda to check if CGetL and StaticLocInit refer to the same local. auto has_same_local = [] (PC pc, const Captures& captures) { if (captures.size() == 0) return false; auto cgetl = (const Op*)pc; auto sli = (const Op*)captures[0]; assert(*cgetl == Op::CGetL); assert(*sli == Op::StaticLocInit); return (getImm(sli, 0).u_IVA == getImm(cgetl, 0).u_IVA); }; auto cgetl = Atom(Op::CGetL).onlyif(has_same_local); auto retc = Atom(Op::RetC); // Look for a static local singleton pattern. auto result = BCPattern { Atom(Op::Null), Atom(Op::StaticLocInit).capture(), Atom(Op::IsTypeL), Atom::alt( Atom(Op::JmpZ).taken({cgetl, retc}), Atom::seq(Atom(Op::JmpNZ), cgetl, retc) ) }.ignore( {Op::AssertRATL, Op::AssertRATStk} ).matchAnchored(funcd); if (result.found()) { try { hhbcTrans().emitSingletonSLoc( funcd, (const Op*)result.getCapture(0) ); } catch (const FailedIRGen& e) { return false; } catch (const FailedCodeGen& e) { return false; } TRACE(1, "[singleton-sloc] %s <- %s\n", funcd->fullName()->data(), fcall.func()->fullName()->data()); return true; } // Not found; check for the static property pattern. // Factory for String atoms that are required to match another captured // String opcode. auto same_string_as = [&] (int i) { return Atom(Op::String).onlyif([=] (PC pc, const Captures& captures) { auto string1 = (const Op*)pc; auto string2 = (const Op*)captures[i]; assert(*string1 == Op::String); assert(*string2 == Op::String); auto const unit = funcd->unit(); auto sd1 = unit->lookupLitstrId(getImmPtr(string1, 0)->u_SA); auto sd2 = unit->lookupLitstrId(getImmPtr(string2, 0)->u_SA); return (sd1 && sd1 == sd2); }); }; auto stringProp = same_string_as(0); auto stringCls = same_string_as(1); auto agetc = Atom(Op::AGetC); auto cgets = Atom(Op::CGetS); // Look for a class static singleton pattern. result = BCPattern { Atom(Op::String).capture(), Atom(Op::String).capture(), Atom(Op::AGetC), Atom(Op::CGetS), Atom(Op::IsTypeC), Atom::alt( Atom(Op::JmpZ).taken({stringProp, stringCls, agetc, cgets, retc}), Atom::seq(Atom(Op::JmpNZ), stringProp, stringCls, agetc, cgets, retc) ) }.ignore( {Op::AssertRATL, Op::AssertRATStk} ).matchAnchored(funcd); if (result.found()) { try { hhbcTrans().emitSingletonSProp( funcd, (const Op*)result.getCapture(1), (const Op*)result.getCapture(0) ); } catch (const FailedIRGen& e) { return false; } catch (const FailedCodeGen& e) { return false; } TRACE(1, "[singleton-sprop] %s <- %s\n", funcd->fullName()->data(), fcall.func()->fullName()->data()); return true; } return false; }