void reclaimFunction(const Func* func) {
  BlockingLeaseHolder writer(Translator::WriteLease());

  auto it = s_funcTCData.find(func);
  if (it == s_funcTCData.end()) return;

  ITRACE(1, "Tearing down func {} (id={})\n",
         func->fullName()->data(), func->getFuncId());

  Trace::Indent _i;

  auto& data = it->second;
  auto& us = mcg->ustubs();

  ITRACE(1, "Smashing prologues\n");
  clobberFuncGuards(func);

  for (auto& caller : data.callers) {
    ITRACE(1, "Unsmashing call @ {} (guard = {})\n",
           caller.first, caller.second.isGuard);

    // It should be impossible to reach a prologue that has been reclaimed
    // through an immutable stub, as this would imply the function is still
    // reachable.
    auto addr = caller.second.isGuard ? us.bindCallStub
                                      : nullptr;
    smashCall(caller.first, addr);
    s_smashedCalls.erase(caller.first);
  }

  auto movedData = folly::makeMoveWrapper(std::move(data));
  auto fname = func->fullName()->data();
  auto fid   = func->getFuncId();
  // We just smashed all of those callers-- treadmill the free to avoid a
  // race (threads executing callers may end up inside the guard even though
  // the function is now unreachable). Once the following block runs the guards
  // should be unreachable.
  Treadmill::enqueue([fname, fid, movedData] {
    BlockingLeaseHolder writer(Translator::WriteLease());

    ITRACE(1, "Reclaiming func {} (id={})\n", fname, fid);
    Trace::Indent _i;
    {
      ITRACE(1, "Reclaiming Prologues\n");
      Trace::Indent _i;
      for (auto& loc : movedData->prologues) {
        reclaimTranslation(loc);
      }
    }

    for (auto* rec : movedData->srcRecs) {
      reclaimSrcRec(rec);
    }
  });

  s_funcTCData.erase(it);
}
Exemple #2
0
folly::Optional<std::pair<SrcKey,TransID>>
updateFuncPrologue(TCA start, ProfTransRec* rec) {
  auto func = rec->func();
  auto nArgs = rec->prologueArgs();

  auto codeLock = lockCode();

  // Smash callers of the old prologue with the address of the new one.
  for (auto toSmash : rec->mainCallers()) {
    smashCall(toSmash, start);
  }

  // If the prologue has a matching guard, then smash its guard-callers as
  // well.
  auto const guard = funcGuardFromPrologue(start, func);
  if (funcGuardMatches(guard, func)) {
    for (auto toSmash : rec->guardCallers()) {
      smashCall(toSmash, guard);
    }
  }
  rec->clearAllCallers();

  // If this prologue has a DV funclet, then invalidate it and return its SrcKey
  // and TransID
  if (nArgs < func->numNonVariadicParams()) {
    auto paramInfo = func->params()[nArgs];
    if (paramInfo.hasDefaultValue()) {
      SrcKey funcletSK(func, paramInfo.funcletOff, false);
      auto funcletTransId = profData()->dvFuncletTransId(func, nArgs);
      if (funcletTransId != kInvalidTransID) {
        invalidateSrcKey(funcletSK);
        return std::make_pair(funcletSK, funcletTransId);
      }
    }
  }

  return folly::none;
}
Exemple #3
0
void bindCall(TCA toSmash, TCA start, Func* callee, int nArgs, bool immutable) {
  auto codeLock = lockCode();

  if (!start || smashableCallTarget(toSmash) == start) return;
  assertx(smashableCallTarget(toSmash));
  TRACE(2, "bindCall smash %p -> %p\n", toSmash, start);
  smashCall(toSmash, start);

  bool is_profiled = false;
  // For functions to be PGO'ed, if their current prologues are still
  // profiling ones (living in code.prof()), then save toSmash as a
  // caller to the prologue, so that it can later be smashed to call a
  // new prologue when it's generated.
  int calleeNumParams = callee->numNonVariadicParams();
  int calledPrologNumArgs = (nArgs <= calleeNumParams ?
                             nArgs :  calleeNumParams + 1);
  auto const profData = jit::profData();
  if (profData != nullptr && code().prof().contains(start)) {
    auto rec = profData->prologueTransRec(
      callee,
      calledPrologNumArgs
    );
    if (immutable) {
      rec->addMainCaller(toSmash);
    } else {
      rec->addGuardCaller(toSmash);
    }
    is_profiled = true;
  }

  // We need to be able to reclaim the function prologues once the unit
  // associated with this function is treadmilled-- so record all of the
  // callers that will need to be re-smashed
  //
  // Additionally for profiled calls we need to remove them from the main
  // and guard caller maps.
  if (RuntimeOption::EvalEnableReusableTC) {
    if (debug || is_profiled || !immutable) {
      auto metaLock = lockMetadata();
      recordFuncCaller(callee, toSmash, immutable, is_profiled,
                           calledPrologNumArgs);
    }
  }
}