Example #1
0
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;
}
Example #2
0
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;
}