Example #1
0
/**
 * Create blocks for each entry point as well as ordinary control
 * flow boundaries.  Calls are not treated as basic-block ends.
 */
void GraphBuilder::createBlocks() {
  PC bc = m_unit->entry();
  m_graph->param_count = m_func->params().size();
  m_graph->first_linear = createBlock(m_func->base());
  // DV entry points
  m_graph->entries = new (m_arena) Block*[m_graph->param_count + 1];
  int dv_index = 0;
  for (Range<Func::ParamInfoVec> p(m_func->params()); !p.empty(); ) {
    const Func::ParamInfo& param = p.popFront();
    m_graph->entries[dv_index++] = !param.hasDefaultValue() ? 0 :
                                   createBlock(param.funcletOff());
  }
  // main entry point
  assert(dv_index == m_graph->param_count);
  m_graph->entries[dv_index] = createBlock(m_func->base());
  // ordinary basic block boundaries
  for (InstrRange i = funcInstrs(m_func); !i.empty(); ) {
    PC pc = i.popFront();
    if (isCF(pc) && !i.empty()) createBlock(i.front());
    if (isSwitch(*reinterpret_cast<const Op*>(pc))) {
      foreachSwitchTarget(reinterpret_cast<const Op*>(pc), [&](Offset& o) {
        createBlock(pc + o);
      });
    } else {
      Offset target = instrJumpTarget((Op*)bc, pc - bc);
      if (target != InvalidAbsoluteOffset) createBlock(target);
    }
  }
}
Example #2
0
/**
 * Create blocks for each entry point as well as ordinary control
 * flow boundaries.  Calls are not treated as basic-block ends.
 */
void GraphBuilder::createBlocks() {
  PC bc = m_unit->entry();
  m_graph->param_count = m_func->params().size();
  m_graph->first_linear = createBlock(m_func->base());
  // DV entry points
  m_graph->entries = new (m_arena) Block*[m_graph->param_count + 1];
  int dv_index = 0;
  for (auto& param : m_func->params()) {
    m_graph->entries[dv_index++] = !param.hasDefaultValue() ? 0 :
                                   createBlock(param.funcletOff);
  }
  // main entry point
  assert(dv_index == m_graph->param_count);
  m_graph->entries[dv_index] = createBlock(m_func->base());
  // ordinary basic block boundaries
  for (InstrRange i = funcInstrs(m_func); !i.empty(); ) {
    PC pc = i.popFront();
    if ((isCF(pc) || isTF(pc)) && !i.empty()) createBlock(i.front());
    if (isSwitch(peek_op(pc))) {
      foreachSwitchTarget(pc, [&](Offset o) { createBlock(pc + o); });
    } else {
      Offset target = instrJumpTarget(bc, pc - bc);
      if (target != InvalidAbsoluteOffset) createBlock(target);
    }
  }
}
Example #3
0
/**
 * Link ordinary blocks with ordinary edges and set their last instruction
 * and end offsets
 */
void GraphBuilder::linkBlocks() {
  PC bc = m_unit->entry();
  Block* block = m_graph->first_linear;
  block->id = m_graph->block_count++;
  for (InstrRange i = funcInstrs(m_func); !i.empty(); ) {
    PC pc = i.popFront();
    block->last = pc;
    if (isCF(pc)) {
      if (isSwitch(*reinterpret_cast<const Op*>(pc))) {
        int i = 0;
        foreachSwitchTarget((Op*)pc, [&](Offset& o) {
          succs(block)[i++] = at(pc + o);
        });
      } else {
        Offset target = instrJumpTarget((Op*)bc, pc - bc);
        if (target != InvalidAbsoluteOffset) {
          assert(numSuccBlocks(block) > 0);
          succs(block)[numSuccBlocks(block) - 1] = at(target);
        }
      }
    }
    PC next_pc = !i.empty() ? i.front() : m_unit->at(m_func->past());
    Block* next = at(next_pc);
    if (next) {
      block->next_linear = next;
      block->end = next_pc;
      if (!isTF(pc)) {
        assert(numSuccBlocks(block) > 0);
        succs(block)[0] = next;
      }
      block = next;
      block->id = m_graph->block_count++;
    }
  }
  block->end = m_unit->at(m_func->past());
}
Example #4
0
/*
 * Region-selector that unintelligently takes a whole method at a
 * time.  This is primarily intended for use for debugging and
 * development on the JIT.
 *
 * Adds no type annotations to the region beyond those known from the
 * context.  It will list only the parameter types as guards.
 *
 * If the context is not a method entry point, returns nullptr to fall
 * back to the tracelet compiler.  (This will happen for side-exits
 * from method regions, for example.)
 */
RegionDescPtr selectMethod(const RegionContext& context) {
  using namespace HPHP::Verifier;

  if (!isFuncEntry(context.func, context.bcOffset)) return nullptr;
  if (context.func->isPseudoMain()) return nullptr;
  FTRACE(1, "function entry for {}: using selectMethod\n",
         context.func->fullName()->data());

  auto ret = std::make_shared<RegionDesc>();

  Arena arena;
  GraphBuilder gb(arena, context.func);
  auto const graph = gb.build();
  auto const unit = context.func->unit();

  jit::hash_map<Block*,RegionDesc::BlockId> blockMap;

  /*
   * Spit out the blocks in a RPO, but skip DV-initializer blocks
   * (i.e. start with graph->first_linear.  We don't handle those in
   * our method regions for now---they'll get handled by the tracelet
   * compiler and then may branch to the main entry point.
   */
  sortRpo(graph);
  {
    auto spOffset = context.spOffset;
    for (Block* b = graph->first_linear; b != nullptr; b = b->next_rpo) {
      auto const start  = unit->offsetOf(b->start);
      auto const length = numInstrs(b->start, b->end);
      SrcKey sk{context.func, start, context.resumed};
      auto const rblock = ret->addBlock(sk, length, spOffset, 0);
      blockMap[b] = rblock->id();
      // flag SP offset as unknown for all but the first block
      spOffset = FPInvOffset::invalid();
    }
  }

  // Add all the ARCs.
  for (Block* b = graph->first_linear; b != nullptr; b = b->next_rpo) {
    auto const myId = blockMap[b];
    auto const numSuccs = numSuccBlocks(b);
    for (auto i = uint32_t{0}; i < numSuccs; ++i) {
      auto const succIt = blockMap.find(b->succs[i]);
      if (succIt != end(blockMap)) {
        ret->addArc(myId, succIt->second);
      }
    }
  }

  // Compute stack depths for each block.
  for (Block* b = graph->first_linear; b != nullptr; b = b->next_rpo) {
    auto const myId = blockMap[b];
    auto rblock = ret->block(myId);
    auto sp = rblock->initialSpOffset();

    // Don't add unreachable blocks to the region.
    if (!sp.isValid()) {
      ret->deleteBlock(myId);
      continue;
    }

    for (InstrRange inst = blockInstrs(b); !inst.empty();) {
      auto const pc   = inst.popFront();
      auto const info = instrStackTransInfo(reinterpret_cast<const Op*>(pc));
      switch (info.kind) {
      case StackTransInfo::Kind::InsertMid:
        ++sp;
        break;
      case StackTransInfo::Kind::PushPop:
        sp += info.numPushes - info.numPops;
        break;
      }
    }

    for (auto idx = uint32_t{0}; idx < numSuccBlocks(b); ++idx) {
      if (!b->succs[idx]) continue;
      auto const succ = ret->block(blockMap[b->succs[idx]]);
      if (succ->initialSpOffset().isValid()) {
        always_assert_flog(
          succ->initialSpOffset() == sp,
          "Stack depth mismatch in region method on {}\n"
          "  srcblkoff={}, dstblkoff={}, src={}, target={}",
          context.func->fullName()->data(),
          context.func->unit()->offsetOf(b->start),
          context.func->unit()->offsetOf(b->succs[idx]->start),
          sp.offset,
          succ->initialSpOffset().offset
        );
        continue;
      }
      succ->setInitialSpOffset(sp);
      FTRACE(2,
        "spOff for {} -> {}\n",
        context.func->unit()->offsetOf(b->succs[idx]->start),
        sp.offset
      );
    }
  }

  /*
   * Fill the first block predictions with the live types.
   */
  assertx(!ret->empty());
  auto const startSK = ret->start();
  for (auto& lt : context.liveTypes) {
    typedef RegionDesc::Location::Tag LTag;

    switch (lt.location.tag()) {
    case LTag::Stack:
      break;
    case LTag::Local:
      if (lt.location.localId() < context.func->numParams()) {
        // Only predict objectness, not the specific class type.
        auto const type = lt.type < TObj ? TObj : lt.type;
        ret->entry()->addPreCondition(startSK, {lt.location, type});
      }
      break;
    }
  }

  return ret;
}