Example #1
0
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));
        }
      }
    }
  }
Example #2
0
void optimizeStores(IRUnit& unit) {
  PassTracer tracer{&unit, Trace::hhir_store, "optimizeStores"};

  // This isn't required for correctness, but it may allow removing stores that
  // otherwise we would leave alone.
  splitCriticalEdges(unit);

  auto incompleteQ = dataflow_worklist<PostOrderId>(unit.numBlocks());

  /*
   * Global state for this pass, visible while processing any block.
   */
  auto genv = Global { unit };
  auto const& poBlockList = genv.poBlockList;
  if (genv.ainfo.locations.size() == 0) {
    FTRACE(1, "no memory accesses to possibly optimize\n");
    return;
  }
  FTRACE(1, "\nLocations:\n{}\n", show(genv.ainfo));

  /*
   * Initialize the block state structures.
   *
   * Important note: every block starts with an empty liveOut set, including
   * blocks that are exiting the region.  When an HHIR region is exited,
   * there's always some instruction we can use to indicate via memory_effects
   * what may be read (e.g. EndCatch, RetCtrl, ReqBindJmp, etc).  When we start
   * iterating, we'll appropriately add things to GEN based on these.
   */
  for (auto poId = uint32_t{0}; poId < poBlockList.size(); ++poId) {
    genv.blockStates[poBlockList[poId]->id()].id = poId;
    incompleteQ.push(poId);
  }

  /*
   * Analyze each block to compute its transfer function.
   *
   * The blockAnalysis vector is indexed by post order id.
   */
  auto const blockAnalysis = [&] () -> jit::vector<BlockAnalysis> {
    auto ret = jit::vector<BlockAnalysis>{};
    ret.reserve(unit.numBlocks());
    for (auto id = uint32_t{0}; id < poBlockList.size(); ++id) {
      ret.push_back(analyze_block(genv, poBlockList[id]));
    }
    return ret;
  }();

  FTRACE(2, "Transfer functions:\n{}\n",
    [&]() -> std::string {
      auto ret = std::string{};
      for (auto poId = uint32_t{0}; poId < poBlockList.size(); ++poId) {
        auto& analysis = blockAnalysis[poId];
        folly::format(
          &ret,
          " B{}\n"
          "   gen:  {}\n"
          "   kill: {}\n",
          poBlockList[poId]->id(),
          show(analysis.gen),
          show(analysis.kill)
        );
      }
      return ret;
    }()
  );

  /*
   * Iterate on the liveOut states until we reach a fixed point.
   */
  FTRACE(4, "Iterating\n");
  while (!incompleteQ.empty()) {
    auto const poId = incompleteQ.pop();
    auto const blk  = poBlockList[poId];

    auto& state         = genv.blockStates[blk->id()];
    auto const transfer = blockAnalysis[poId];
    assertx(state.id == poId);

    state.liveIn = transfer.gen | (state.liveOut & ~transfer.kill);
    FTRACE(4, "  block B{}\n"
              "    live out: {}\n"
              "        gen : {}\n"
              "       kill : {}\n"
              "    live in : {}\n",
              blk->id(),
              show(state.liveOut),
              show(transfer.gen),
              show(transfer.kill),
              show(state.liveIn));

    /*
     * Update predecessors by merging the live in state into the live out state
     * of each predecessor.
     *
     * If anything changes, reschedule the predecessor.
     */
    blk->forEachPred([&] (Block* pred) {
      FTRACE(4, "   -> {}\n", pred->id());
      auto& predState = genv.blockStates[pred->id()];

      auto const oldLiveOut = predState.liveOut;
      predState.liveOut |= state.liveIn;

      if (predState.liveOut != oldLiveOut) {
        incompleteQ.push(predState.id);
      }
    });
  }

  /*
   * We've reached a fixed point.  Now we can act on this information to remove
   * dead stores.
   */
  FTRACE(2, "\nFixed point:\n{}\n",
    [&]() -> std::string {
      auto ret = std::string{};
      for (auto& blk : poBlockList) {
        folly::format(
          &ret,
          " B{: <3}: {}\n",
          blk->id(),
          show(genv.blockStates[blk->id()].liveOut)
        );
      }
      return ret;
    }()
  );
  for (auto& block : poBlockList) {
    optimize_block(genv, block);
  }
}