コード例 #1
0
ファイル: cmd_flow_control.cpp プロジェクト: 2bj/hhvm
// Place internal breakpoints to get out of the current function. This may place
// multiple internal breakpoints, and it may place them more than one frame up.
// Some instructions can cause PHP to be invoked without an explicit call. A set
// which causes a destructor to run, a iteration init which causes an object's
// next() method to run, a RetC which causes destructors to run, etc. This
// recgonizes such cases and ensures we have internal breakpoints to cover the
// destination(s) of such instructions.
void CmdFlowControl::setupStepOuts() {
  // Existing step outs should be cleaned up before making new ones.
  assert(!hasStepOuts());
  auto fp = g_context->getFP();
  if (!fp) return; // No place to step out to!
  Offset returnOffset;
  bool fromVMEntry;
  while (!hasStepOuts()) {
    fp = g_context->getPrevVMState(fp, &returnOffset, nullptr, &fromVMEntry);
    // If we've run off the top of the stack, just return having setup no
    // step outs. This will cause cmds like Next and Out to just let the program
    // run, which is appropriate.
    if (!fp) break;
    Unit* returnUnit = fp->m_func->unit();
    PC returnPC = returnUnit->at(returnOffset);
    TRACE(2, "CmdFlowControl::setupStepOuts: at '%s' offset %d opcode %s\n",
          fp->m_func->fullName()->data(), returnOffset,
          opcodeToName(*reinterpret_cast<const Op*>(returnPC)));
    // Don't step out to generated functions, keep looking.
    if (fp->m_func->line1() == 0) continue;
    if (fromVMEntry) {
      TRACE(2, "CmdFlowControl::setupStepOuts: VM entry\n");
      // We only execute this for opcodes which invoke more PHP, and that does
      // not include switches. Thus, we'll have at most two destinations.
      assert(!isSwitch(*reinterpret_cast<const Op*>(returnPC)) &&
        (numSuccs(reinterpret_cast<const Op*>(returnPC)) <= 2));
      // Set an internal breakpoint after the instruction if it can fall thru.
      if (instrAllowsFallThru(*reinterpret_cast<const Op*>(returnPC))) {
        Offset nextOffset = returnOffset + instrLen((Op*)returnPC);
        TRACE(2, "CmdFlowControl: step out to '%s' offset %d (fall-thru)\n",
              fp->m_func->fullName()->data(), nextOffset);
        m_stepOut1 = StepDestination(returnUnit, nextOffset);
      }
      // Set an internal breakpoint at the target of a control flow instruction.
      // A good example of a control flow op that invokes PHP is IterNext.
      if (instrIsControlFlow(*reinterpret_cast<const Op*>(returnPC))) {
        Offset target =
          instrJumpTarget(reinterpret_cast<const Op*>(returnPC), 0);
        if (target != InvalidAbsoluteOffset) {
          Offset targetOffset = returnOffset + target;
          TRACE(2, "CmdFlowControl: step out to '%s' offset %d (jump target)\n",
                fp->m_func->fullName()->data(), targetOffset);
          m_stepOut2 = StepDestination(returnUnit, targetOffset);
        }
      }
      // If we have no place to step out to, then unwind another frame and try
      // again. The most common case that leads here is Ret*, which does not
      // fall-thru and has no encoded target.
    } else {
      TRACE(2, "CmdFlowControl: step out to '%s' offset %d\n",
            fp->m_func->fullName()->data(), returnOffset);
      m_stepOut1 = StepDestination(returnUnit, returnOffset);
    }
  }
}
コード例 #2
0
ファイル: cfg.cpp プロジェクト: 191919/hhvm
TEST(CFG, InsertPreHeaders_MultiBackEdge) {
  IRUnit unit{test_context};
  auto const dummy = BCMarker::Dummy();

  // Multiple back-edges to the same block.

  /*
    digraph G {
    B0 -> B1; B0 -> B2
    B1 -> B3
    B2 -> B3
    B3 -> B3; B3 -> B4
    B4 -> B3
    }
  */

  auto b0 = unit.entry();
  auto b1 = unit.defBlock();
  auto b2 = unit.defBlock();
  auto b3 = unit.defBlock();
  auto b4 = unit.defBlock();

  auto val1 = unit.gen(Conjure, dummy, TBool);
  auto val2 = unit.gen(Conjure, dummy, TBool);

  b0->push_back(unit.gen(JmpZero, dummy, b1, val1->dst()));
  b0->back().setNext(b2);

  b1->push_back(unit.gen(Jmp, dummy, b3));
  b2->push_back(unit.gen(Jmp, dummy, b3));

  b3->push_back(unit.gen(JmpNZero, dummy, b3, val2->dst()));
  b3->back().setNext(b4);

  b4->push_back(unit.gen(Jmp, dummy, b3));

  auto oldSize = unit.numBlocks();
  auto res = insertLoopPreHeaders(unit);
  auto newSize = unit.numBlocks();

  EXPECT_TRUE(res);
  EXPECT_EQ(newSize, oldSize + 1);

  // b5 is the new pre-header.
  auto b5 = b1->taken();

  EXPECT_NE(b3, b4);
  EXPECT_EQ(b5->numSuccs(), 1);
  EXPECT_EQ(b2->taken(), b5);
  EXPECT_EQ(b5->taken(), b3);

  EXPECT_EQ(b3->taken(), b3);
  EXPECT_EQ(b4->taken(), b3);
}
コード例 #3
0
ファイル: gvn.cpp プロジェクト: swtaarrs/hhvm
void insertIncRefs(PrcEnv& env) {
  auto antQ =
    dataflow_worklist<uint32_t, std::less<uint32_t>>(env.rpoBlocks.size());
  auto avlQ =
    dataflow_worklist<uint32_t, std::greater<uint32_t>>(env.rpoBlocks.size());

  env.states.resize(env.unit.numBlocks());
  for (uint32_t i = 0; i < env.rpoBlocks.size(); i++) {
    auto blk = env.rpoBlocks[i];
    auto& state = env.states[blk->id()];
    state.rpoId = i;
    if (blk->numSuccs()) state.antOut.set();
    if (blk->numPreds()) state.avlIn.set();
    antQ.push(i);
    avlQ.push(i);
  }

  auto id = 0;
  for (auto& v : env.insertMap) {
    for (auto const tmp : v) {
      auto const blk = tmp->inst()->block();
      auto& state = env.states[blk->id()];
      if (!state.local.test(id)) {
        state.local.set(id);
        continue;
      }
    }
    id++;
  }

  using Bits = PrcState::Bits;
  // compute anticipated
  do {
    auto const blk = env.rpoBlocks[antQ.pop()];
    auto& state = env.states[blk->id()];
    state.antIn = state.antOut | state.local;
    state.pantIn = state.pantOut | state.local;
    blk->forEachPred(
      [&] (Block* b) {
        auto& s = env.states[b->id()];
        auto const antOut = s.antOut & state.antIn;
        auto const pantOut = s.pantOut | state.pantIn;
        if (antOut != s.antOut || pantOut != s.pantOut) {
          s.antOut = antOut;
          s.pantOut = pantOut;
          antQ.push(s.rpoId);
        }
      }
    );
  } while (!antQ.empty());

  // compute available
  do {
    auto const blk = env.rpoBlocks[avlQ.pop()];
    auto& state = env.states[blk->id()];
    state.avlOut = state.avlIn | state.local;
    blk->forEachSucc(
      [&] (Block* b) {
        auto& s = env.states[b->id()];
        auto const avlIn = s.avlIn & state.avlOut;
        if (avlIn != s.avlIn) {
          s.avlIn = avlIn;
          avlQ.push(s.rpoId);
        }
      });
  } while (!avlQ.empty());

  for (auto blk : env.rpoBlocks) {
    auto& state = env.states[blk->id()];
    FTRACE(4,
           "InsertIncDecs: Blk(B{}) <- {}\n"
           "{}"
           "  ->{}\n",
           blk->id(),
           [&] {
             std::string ret;
             blk->forEachPred([&] (Block* pred) {
                 folly::format(&ret, " B{}", pred->id());
               });
             return ret;
           }(),
           show(state),
           [&] {
             std::string ret;
             blk->forEachSucc([&] (Block* succ) {
                 folly::format(&ret, " B{}", succ->id());
               });
             return ret;
           }());

    auto inc = state.local;
    for (auto inc_id = 0; inc.any(); inc >>= 1, inc_id++) {
      if (inc.test(0)) {
        auto const& tmps = env.insertMap[inc_id];
        auto insert = [&] (IRInstruction* inst) {
          FTRACE(3, "Inserting IncRef into B{}\n", blk->id());
          auto const iter = std::next(blk->iteratorTo(inst));
          blk->insert(iter, env.unit.gen(IncRef, inst->bcctx(), tmps[0]));
        };
        SSATmp* last = nullptr;
        // Insert an IncRef after every candidate in this block except
        // the last one (since we know for all but the last that its
        // successor is anticipated). Note that entries in tmps from
        // the same block are guaranteed to be in program order.
        for (auto const tmp : tmps) {
          if (tmp->inst()->block() != blk) continue;
          if (last) insert(last->inst());
          last = tmp;
        }
        // If it's partially anticipated out, insert an inc after the
        // last one too.
        always_assert(last);
        if (state.pantOut.test(inc_id)) insert(last->inst());
      }
    }
    auto dec = state.avlIn & ~state.pantIn;
    if (dec.any()) {
      blk->forEachPred(
        [&] (Block* pred) {
          auto& pstate = env.states[pred->id()];
          dec &= pstate.pantOut;
        });

      for (auto dec_id = 0; dec.any(); dec >>= 1, dec_id++) {
        if (dec.test(0)) {
          FTRACE(3, "Inserting DecRef into B{}\n", blk->id());
          auto const tmp = env.insertMap[dec_id][0];
          blk->prepend(env.unit.gen(DecRef, tmp->inst()->bcctx(),
                                    DecRefData{}, tmp));
        }
      }
    }
  }