Exemple #1
0
static void recordActRecPush(const SrcKey sk,
                             const StringData* name,
                             const StringData* clsName,
                             bool staticCall) {
  auto unit = sk.unit();
  FTRACE(2, "annotation: recordActRecPush: {}@{} {}{}{} ({}static)\n",
         unit->filepath()->data(),
         sk.offset(),
         clsName ? clsName->data() : "",
         clsName ? "::" : "",
         name,
         !staticCall ? "non" : "");

  SrcKey next(sk);
  next.advance(unit);
  const FPIEnt *fpi = sk.func()->findFPI(next.offset());
  assert(fpi);
  assert(name->isStatic());
  assert(sk.offset() == fpi->m_fpushOff);
  auto const fcall = SrcKey { sk.func(), fpi->m_fcallOff, sk.resumed() };
  assert(isFCallStar(*reinterpret_cast<const Op*>(unit->at(fcall.offset()))));
  auto const func = lookupDirectFunc(sk, name, clsName, staticCall);
  if (func) {
    recordFunc(fcall, func);
  }
}
Exemple #2
0
bool InliningDecider::canInlineAt(SrcKey callSK, const Func* callee) const {
  if (!callee ||
      !RuntimeOption::EvalHHIREnableGenTimeInlining ||
      RuntimeOption::EvalJitEnableRenameFunction ||
      callee->attrs() & AttrInterceptable) {
    return false;
  }

  if (callee->cls()) {
    if (!classHasPersistentRDS(callee->cls())) {
      // if the callee's class is not persistent, its still ok
      // to use it if we're jitting into a method of a subclass
      auto ctx = callSK.func()->cls();
      if (!ctx || !ctx->classof(callee->cls())) {
        return false;
      }
    }
  } else {
    auto const handle = callee->funcHandle();
    if (handle == rds::kInvalidHandle || !rds::isPersistentHandle(handle)) {
      // if the callee isn't persistent, its still ok to
      // use it if its defined at the top level in the same
      // unit as the caller
      if (callee->unit() != callSK.unit() || !callee->top()) {
        return false;
      }
    }
  }

  // If inlining was disabled... don't inline.
  if (m_disabled) return false;

  // TODO(#3331014): We have this hack until more ARM codegen is working.
  if (arch() == Arch::ARM) return false;

  // We can only inline at normal FCalls.
  if (callSK.op() != Op::FCall &&
      callSK.op() != Op::FCallD) {
    return false;
  }

  // Don't inline from resumed functions.  The inlining mechanism doesn't have
  // support for these---it has no way to redefine stack pointers relative to
  // the frame pointer, because in a resumed function the frame pointer points
  // into the heap instead of into the eval stack.
  if (callSK.resumed()) return false;

  // TODO(#4238160): Inlining into pseudomain callsites is still buggy.
  if (callSK.func()->isPseudoMain()) return false;

  if (!isCalleeInlinable(callSK, callee) || !checkNumArgs(callSK, callee)) {
    return false;
  }

  return true;
}
Exemple #3
0
bool InliningDecider::canInlineAt(SrcKey callSK, const Func* callee) const {
  if (m_disabled ||
      !callee ||
      !RuntimeOption::EvalHHIREnableGenTimeInlining ||
      RuntimeOption::EvalJitEnableRenameFunction ||
      callee->attrs() & AttrInterceptable) {
    return false;
  }

  // We can only inline at normal FCalls.
  if (callSK.op() != Op::FCall &&
      callSK.op() != Op::FCallD) {
    return false;
  }

  // Don't inline from resumed functions.  The inlining mechanism doesn't have
  // support for these---it has no way to redefine stack pointers relative to
  // the frame pointer, because in a resumed function the frame pointer points
  // into the heap instead of into the eval stack.
  if (callSK.resumed()) return false;

  // TODO(#4238160): Inlining into pseudomain callsites is still buggy.
  if (callSK.func()->isPseudoMain()) return false;

  if (!isCalleeInlinable(callSK, callee) || !checkNumArgs(callSK, callee)) {
    return false;
  }

  return true;
}
Exemple #4
0
void prepareForNextHHBC(IRGS& env,
                        const NormalizedInstruction* ni,
                        SrcKey newSk,
                        bool lastBcInst) {
  FTRACE(1, "------------------- prepareForNextHHBC ------------------\n");
  env.currentNormalizedInstruction = ni;

  always_assert_flog(
    IMPLIES(isInlining(env), !env.lastBcInst),
    "Tried to end trace while inlining."
  );

  always_assert_flog(
    IMPLIES(isInlining(env), !env.firstBcInst),
    "Inlining while still at the first region instruction."
  );

  always_assert(env.bcStateStack.size() >= env.inlineLevel + 1);
  auto pops = env.bcStateStack.size() - 1 - env.inlineLevel;
  while (pops--) env.bcStateStack.pop_back();

  always_assert_flog(env.bcStateStack.back().func() == newSk.func(),
                     "Tried to update current SrcKey with a different func");

  env.bcStateStack.back().setOffset(newSk.offset());
  updateMarker(env);
  env.lastBcInst = lastBcInst;
  env.catchCreator = nullptr;
  env.irb->prepareForNextHHBC();
}
Exemple #5
0
TransRec::TransRec(SrcKey                      _src,
                   TransID                     transID,
                   TransKind                   _kind,
                   TCA                         _aStart,
                   uint32_t                    _aLen,
                   TCA                         _acoldStart,
                   uint32_t                    _acoldLen,
                   TCA                         _afrozenStart,
                   uint32_t                    _afrozenLen,
                   RegionDescPtr               region,
                   std::vector<TransBCMapping> _bcMapping,
                   Annotations&&               _annotations,
                   bool                        _hasLoop)
  : bcMapping(_bcMapping)
  , annotations(std::move(_annotations))
  , funcName(_src.func()->fullName()->data())
  , src(_src)
  , md5(_src.func()->unit()->md5())
  , aStart(_aStart)
  , acoldStart(_acoldStart)
  , afrozenStart(_afrozenStart)
  , aLen(_aLen)
  , acoldLen(_acoldLen)
  , afrozenLen(_afrozenLen)
  , bcStart(_src.offset())
  , id(transID)
  , kind(_kind)
  , hasLoop(_hasLoop)
{
  if (funcName.empty()) funcName = "Pseudo-main";

  if (!region) return;

  assertx(!region->empty());
  for (auto& block : region->blocks()) {
    auto sk = block->start();
    blocks.emplace_back(Block{sk.unit()->md5(), sk.offset(),
                              block->last().advanced().offset()});
  }

  auto& firstBlock = *region->blocks().front();
  for (auto const& pred : firstBlock.typePreConditions()) {
    guards.emplace_back(show(pred));
  }
}
Exemple #6
0
TransRec::TransRec(SrcKey                      _src,
                   TransKind                   _kind,
                   TCA                         _aStart,
                   uint32_t                    _aLen,
                   TCA                         _acoldStart,
                   uint32_t                    _acoldLen,
                   TCA                         _afrozenStart,
                   uint32_t                    _afrozenLen,
                   RegionDescPtr               region,
                   std::vector<TransBCMapping> _bcMapping,
                   bool                        _isLLVM)
  : bcMapping(_bcMapping)
  , funcName(_src.func()->fullName()->data())
  , src(_src)
  , md5(_src.func()->unit()->md5())
  , aStart(_aStart)
  , acoldStart(_acoldStart)
  , afrozenStart(_afrozenStart)
  , aLen(_aLen)
  , acoldLen(_acoldLen)
  , afrozenLen(_afrozenLen)
  , bcStart(_src.offset())
  , id(0)
  , kind(_kind)
  , isLLVM(_isLLVM)
{
  if (funcName.empty()) funcName = "Pseudo-main";

  if (!region) return;

  assertx(!region->empty());
  for (auto& block : region->blocks()) {
    auto sk = block->start();
    blocks.emplace_back(Block{sk.unit()->md5(), sk.offset(),
                              block->last().advanced().offset()});
  }

  auto& firstBlock = *region->blocks().front();
  auto guardRange = firstBlock.typePreds().equal_range(firstBlock.start());
  for (; guardRange.first != guardRange.second; ++guardRange.first) {
    guards.emplace_back(show(guardRange.first->second));
  }
}
Exemple #7
0
std::string showShort(SrcKey sk) {
    if (!sk.valid()) return "<invalid SrcKey>";
    return folly::format(
               "{}(id {:#x})@{}{}",
               sk.func()->fullName(),
               sk.funcID(),
               sk.offset(),
               sk.resumed() ? "r" : ""
           ).str();
}
Exemple #8
0
std::string show(SrcKey sk) {
  auto func = sk.func();
  auto unit = sk.unit();
  const char *filepath = "*anonFile*";
  if (unit->filepath()->data() && unit->filepath()->size()) {
    filepath = unit->filepath()->data();
  }
  return folly::format("{}:{} in {}(id 0x{:#x})@{: >6}",
                       filepath, unit->getLineNumber(sk.offset()),
                       func->isPseudoMain() ? "pseudoMain"
                                            : func->fullName()->data(),
                       (unsigned long long)sk.getFuncId(), sk.offset()).str();
}
Exemple #9
0
std::string show(SrcKey sk) {
  auto func = sk.func();
  auto unit = sk.unit();
  const char *filepath = "*anonFile*";
  if (unit->filepath()->data() && unit->filepath()->size()) {
    filepath = unit->filepath()->data();
  }
  return folly::sformat("{}:{} in {}(id 0x{:#x})@{: >6}{}{}",
                        filepath, unit->getLineNumber(sk.offset()),
                        func->isPseudoMain() ? "pseudoMain"
                                             : func->fullName()->data(),
                        (uint32_t)sk.funcID(), sk.offset(),
                        sk.resumed()  ? "r" : "",
                        sk.hasThis()  ? "t" : "",
                        sk.prologue() ? "p" : "");
}
Exemple #10
0
bool profileSrcKey(SrcKey sk) {
  if (!shouldPGOFunc(*sk.func())) return false;
  if (profData()->optimized(sk.funcID())) return false;
  if (profData()->profiling(sk.funcID())) return true;

  // Don't start profiling new functions if the size of either main or
  // prof is already above Eval.JitAMaxUsage and we already filled hot.
  auto tcUsage = std::max(code().main().used(), code().prof().used());
  if (tcUsage >= CodeCache::AMaxUsage && !code().hotEnabled()) {
    return false;
  }

  // We have two knobs to control the number of functions we're allowed to
  // profile: Eval.JitProfileRequests and Eval.JitProfileBCSize. We profile new
  // functions until either of these limits is exceeded. In practice we expect
  // to hit the bytecode size limit first but we keep the request limit around
  // as a safety net.
  if (RuntimeOption::EvalJitProfileBCSize > 0 &&
      profData()->profilingBCSize() >= RuntimeOption::EvalJitProfileBCSize) {
    return false;
  }

  return requestCount() <= RuntimeOption::EvalJitProfileRequests;
}
Exemple #11
0
bool InliningDecider::canInlineAt(SrcKey callSK, const Func* callee,
                                  const RegionDesc& region) const {
  if (!RuntimeOption::RepoAuthoritative ||
      !RuntimeOption::EvalHHIREnableGenTimeInlining) {
    return false;
  }

  // If inlining was disabled... don't inline.
  if (m_disabled) return false;

  // TODO(#3331014): We have this hack until more ARM codegen is working.
  if (arch() == Arch::ARM) return false;

  // We can only inline at normal FCalls.
  if (callSK.op() != Op::FCall &&
      callSK.op() != Op::FCallD) {
    return false;
  }

  // Don't inline from resumed functions.  The inlining mechanism doesn't have
  // support for these---it has no way to redefine stack pointers relative to
  // the frame pointer, because in a resumed function the frame pointer points
  // into the heap instead of into the eval stack.
  if (callSK.resumed()) return false;

  // TODO(#4238160): Inlining into pseudomain callsites is still buggy.
  if (callSK.func()->isPseudoMain()) return false;

  if (!isCalleeInlinable(callSK, callee) ||
      !checkNumArgs(callSK, callee) ||
      !checkFPIRegion(callSK, callee, region)) {
    return false;
  }

  return true;
}
/*
 * Checks if the given region is well-formed, which entails the
 * following properties:
 *
 *   1) The region has at least one block.
 *
 *   2) Each block in the region has a different id.
 *
 *   3) All arcs involve blocks within the region.
 *
 *   4) For each arc, the bytecode offset of the dst block must
 *      possibly follow the execution of the src block.
 *
 *   5) Each block contains at most one successor corresponding to a
 *      given SrcKey.
 *
 *   6) The region doesn't contain any loops, unless JitLoops is
 *      enabled.
 *
 *   7) All blocks are reachable from the entry block.
 *
 *   8) For each block, there must be a path from the entry to it that
 *      includes only earlier blocks in the region.
 *
 *   9) The region is topologically sorted unless loops are enabled.
 *
 *  10) The block-retranslation chains cannot have cycles.
 *
 */
bool check(const RegionDesc& region, std::string& error) {

  auto bad = [&](const std::string& errorMsg) {
    error = errorMsg;
    return false;
  };

  // 1) The region has at least one block.
  if (region.empty()) return bad("empty region");

  RegionDesc::BlockIdSet blockSet;
  for (auto b : region.blocks()) {
    auto bid = b->id();
    // 2) Each block in the region has a different id.
    if (blockSet.count(bid)) {
      return bad(folly::sformat("many blocks with id {}", bid));
    }
    blockSet.insert(bid);
  }

  for (auto b : region.blocks()) {
    auto bid = b->id();
    SrcKey    lastSk = region.block(bid)->last();
    OffsetSet validSuccOffsets = lastSk.succOffsets();
    OffsetSet succOffsets;

    for (auto succ : region.succs(bid)) {
      SrcKey succSk = region.block(succ)->start();
      Offset succOffset = succSk.offset();

      // 3) All arcs involve blocks within the region.
      if (blockSet.count(succ) == 0) {
        return bad(folly::sformat("arc with dst not in the region: {} -> {}",
                                  bid, succ));
      }

      // Checks 4) and 5) below don't make sense for arcs corresponding
      // to inlined calls and returns, so skip them in such cases.
      // This won't be possible once task #4076399 is done.
      if (lastSk.func() != succSk.func()) continue;

      // 4) For each arc, the bytecode offset of the dst block must
      //    possibly follow the execution of the src block.
      if (validSuccOffsets.count(succOffset) == 0) {
        return bad(folly::sformat("arc with impossible control flow: {} -> {}",
                                  bid, succ));
      }

      // 5) Each block contains at most one successor corresponding to a
      //    given SrcKey.
      if (succOffsets.count(succOffset) > 0) {
        return bad(folly::sformat("block {} has multiple successors with SK {}",
                                  bid, show(succSk)));
      }
      succOffsets.insert(succOffset);
    }
    for (auto pred : region.preds(bid)) {
      if (blockSet.count(pred) == 0) {
        return bad(folly::sformat("arc with src not in the region: {} -> {}",
                                  pred, bid));
      }
    }
  }

  // 6) is checked by dfsCheck.
  DFSChecker dfsCheck(region);
  if (!dfsCheck.check(region.entry()->id())) {
    return bad("region is cyclic");
  }

  // 7) All blocks are reachable from the entry (first) block.
  if (dfsCheck.numVisited() != blockSet.size()) {
    return bad("region has unreachable blocks");
  }

  // 8) and 9) are checked below.
  RegionDesc::BlockIdSet visited;
  auto& blocks = region.blocks();
  for (unsigned i = 0; i < blocks.size(); i++) {
    auto bid = blocks[i]->id();
    unsigned nVisited = 0;
    for (auto pred : region.preds(bid)) {
      nVisited += visited.count(pred);
    }
    // 8) For each block, there must be a path from the entry to it that
    //    includes only earlier blocks in the region.
    if (nVisited == 0 && i != 0) {
      return bad(folly::sformat("block {} appears before all its predecessors",
                                bid));
    }
    // 9) The region is topologically sorted unless loops are enabled.
    if (!RuntimeOption::EvalJitLoops && nVisited != region.preds(bid).size()) {
      return bad(folly::sformat("non-topological order (bid: {})", bid));
    }
    visited.insert(bid);
  }

  // 10) The block-retranslation chains cannot have cycles.
  for (auto b : blocks) {
    auto bid = b->id();
    RegionDesc::BlockIdSet chainSet;
    chainSet.insert(bid);
    while (auto next = region.nextRetrans(bid)) {
      auto nextId = next.value();
      if (chainSet.count(nextId)) {
        return bad(folly::sformat("cyclic retranslation chain for block {}",
                                  bid));
      }
      chainSet.insert(nextId);
      bid = nextId;
    }
  }

  return true;
}
Exemple #13
0
std::string showShort(SrcKey sk) {
  return folly::format("{}(id 0x{:#x})@{}",
                       sk.func()->fullName()->data(), sk.getFuncId(),
                       sk.offset()).str();
}
Exemple #14
0
std::string showShort(SrcKey sk) {
  if (!sk.valid()) return "<invalid SrcKey>";
  return folly::format("{}(id {:#x})@{}",
                       sk.func()->fullName()->data(), sk.getFuncId(),
                       sk.offset()).str();
}