Esempio n. 1
0
/*
 * Algorithm for reflow:
 * 1. for each block in reverse postorder:
 * 2.   compute dest types of each instruction in forwards order
 * 3.   if the block ends with a jmp that passes types to a label,
 *      and the jmp is a loop edge,
 *      and any passed types cause the target label's type to widen,
 *      then set again=true
 * 4. if again==true, goto step 1
 */
void reflowTypes(IRUnit& unit) {
  auto blocklist = rpoSortCfgWithIds(unit);
  auto isBackEdge = [&](Block* from, Block* to) {
    return blocklist.ids[from] > blocklist.ids[to];
  };
  for (bool again = true; again;) {
    again = false;
    for (auto* block : blocklist.blocks) {
      FTRACE(5, "reflowTypes: visiting block {}\n", block->id());
      for (auto& inst : *block) retypeDests(&inst, &unit);
      auto& jmp = block->back();
      auto n = jmp.numSrcs();
      if (!again && jmp.is(Jmp) && n > 0 && isBackEdge(block, jmp.taken())) {
        // if we pass a widening type to a label, loop again.
        auto srcs = jmp.srcs();
        auto dsts = jmp.taken()->front().dsts();
        for (unsigned i = 0; i < n; ++i) {
          if (srcs[i]->type() <= dsts[i].type()) continue;
          again = true;
          break;
        }
      }
    }
  }
}
Esempio n. 2
0
/*
 * Build the CFG, then the dominator tree, then use it to validate SSA.
 * 1. Each src must be defined by some other instruction, and each dst must
 *    be defined by the current instruction.
 * 2. Each src must be defined earlier in the same block or in a dominator.
 * 3. Each dst must not be previously defined.
 * 4. Treat tmps defined by DefConst as always defined.
 * 5. Each predecessor of a reachable block must be reachable (deleted
 *    blocks must not have out-edges to reachable blocks).
 * 6. The entry block must not have any predecessors.
 */
bool checkCfg(const IRUnit& unit) {
  auto const blocksIds = rpoSortCfgWithIds(unit);
  auto const& blocks = blocksIds.blocks;
  jit::hash_set<const Edge*> edges;

  // Entry block can't have predecessors.
  assert(unit.entry()->numPreds() == 0);

  // Check valid successor/predecessor edges.
  for (Block* b : blocks) {
    auto checkEdge = [&] (const Edge* e) {
      assert(e->from() == b);
      edges.insert(e);
      for (auto& p : e->to()->preds()) if (&p == e) return;
      assert(false); // did not find edge.
    };
    checkBlock(b);
    if (auto *e = b->nextEdge())  checkEdge(e);
    if (auto *e = b->takenEdge()) checkEdge(e);
  }
  for (Block* b : blocks) {
    for (DEBUG_ONLY auto const &e : b->preds()) {
      assert(&e == e.inst()->takenEdge() || &e == e.inst()->nextEdge());
      assert(e.to() == b);
    }
  }

  // visit dom tree in preorder, checking all tmps
  auto const children = findDomChildren(unit, blocksIds);
  StateVector<SSATmp, bool> defined0(unit, false);
  forPreorderDoms(blocks.front(), children, defined0,
                  [] (Block* block, StateVector<SSATmp, bool>& defined) {
    for (IRInstruction& inst : *block) {
      for (DEBUG_ONLY SSATmp* src : inst.srcs()) {
        assert(src->inst() != &inst);
        assert_log(src->inst()->op() == DefConst ||
                   defined[src],
                   [&]{ return folly::format(
                       "src '{}' in '{}' came from '{}', which is not a "
                       "DefConst and is not defined at this use site",
                       src->toString(), inst.toString(),
                       src->inst()->toString()).str();
                   });
      }
      for (SSATmp& dst : inst.dsts()) {
        assert(dst.inst() == &inst && inst.op() != DefConst);
        assert(!defined[dst]);
        defined[dst] = true;
      }
    }
  });
  return true;
}
Esempio n. 3
0
void gvn(IRUnit& unit) {
  auto rpoBlocksWithIds = rpoSortCfgWithIds(unit);
  auto& rpoBlocks = rpoBlocksWithIds.blocks;
  auto dominators = findDominators(unit, rpoBlocksWithIds);
  ValueNumberTable vnTable(unit, ValueNumberMetadata { nullptr, nullptr });

  // This is an implementation of the RPO version of the global value numbering
  // algorithm presented in the 1996 paper "SCC-based Value Numbering" by
  // Cooper and Simpson.
  runAnalysis(unit, rpoBlocks, vnTable);
  replaceRedundantComputations(unit, dominators, rpoBlocks, vnTable);
}
Esempio n. 4
0
/*
 * Build the CFG, then the dominator tree, then use it to validate SSA.
 * 1. Each src must be defined by some other instruction, and each dst must
 *    be defined by the current instruction.
 * 2. Each src must be defined earlier in the same block or in a dominator.
 * 3. Each dst must not be previously defined.
 * 4. Treat tmps defined by DefConst as always defined.
 * 5. Each predecessor of a reachable block must be reachable (deleted
 *    blocks must not have out-edges to reachable blocks).
 * 6. The entry block must not have any predecessors.
 * 7. The entry block starts with a DefFP instruction.
 */
bool checkCfg(const IRUnit& unit) {
  auto const blocksIds = rpoSortCfgWithIds(unit);
  auto const& blocks = blocksIds.blocks;
  jit::hash_set<const Edge*> edges;

  // Entry block can't have predecessors.
  always_assert(unit.entry()->numPreds() == 0);

  // Entry block starts with DefFP
  always_assert(!unit.entry()->empty() &&
                unit.entry()->begin()->op() == DefFP);

  // Check valid successor/predecessor edges.
  for (Block* b : blocks) {
    auto checkEdge = [&] (const Edge* e) {
      always_assert(e->from() == b);
      edges.insert(e);
      for (auto& p : e->to()->preds()) if (&p == e) return;
      always_assert(false); // did not find edge.
    };
    checkBlock(b);
    if (auto *e = b->nextEdge())  checkEdge(e);
    if (auto *e = b->takenEdge()) checkEdge(e);
  }
  for (Block* b : blocks) {
    for (auto const &e : b->preds()) {
      always_assert(&e == e.inst()->takenEdge() || &e == e.inst()->nextEdge());
      always_assert(e.to() == b);
    }
  }

  // Visit every instruction and make sure their sources are defined in a block
  // that dominates the block containing the instruction.
  auto const idoms = findDominators(unit, blocksIds);
  forEachInst(blocks, [&] (const IRInstruction* inst) {
    for (auto src : inst->srcs()) {
      if (src->inst()->is(DefConst)) continue;
      auto const dom = findDefiningBlock(src);
      always_assert_flog(
        dom && dominates(dom, inst->block(), idoms),
        "src '{}' in '{}' came from '{}', which is not a "
        "DefConst and is not defined at this use site",
        src->toString(), inst->toString(),
        src->inst()->toString()
      );
    }
  });

  return true;
}
Esempio n. 5
0
/*
 * reoptimize() runs a trace through a second pass of IRBuilder
 * optimizations, like this:
 *
 *   reset state.
 *   move all blocks to a temporary list.
 *   compute immediate dominators.
 *   for each block in trace order:
 *     if we have a snapshot state for this block:
 *       clear cse entries that don't dominate this block.
 *       use snapshot state.
 *     move all instructions to a temporary list.
 *     for each instruction:
 *       optimizeWork - do CSE and simplify again
 *       if not simplified:
 *         append existing instruction and update state.
 *       else:
 *         if the instruction has a result, insert a mov from the
 *         simplified tmp to the original tmp and discard the instruction.
 *     if the last conditional branch was turned into a jump, remove the
 *     fall-through edge to the next block.
 */
void IRBuilder::reoptimize() {
  Timer _t("optimize_reoptimize");
  FTRACE(5, "ReOptimize:vvvvvvvvvvvvvvvvvvvv\n");
  SCOPE_EXIT { FTRACE(5, "ReOptimize:^^^^^^^^^^^^^^^^^^^^\n"); };
  always_assert(m_savedBlocks.empty());
  always_assert(!m_curWhere);
  always_assert(m_state.inlineDepth() == 0);

  m_state.setEnableCse(RuntimeOption::EvalHHIRCse);
  m_enableSimplification = RuntimeOption::EvalHHIRSimplification;
  if (!m_state.enableCse() && !m_enableSimplification) return;
  setConstrainGuards(false);

  auto blocksIds = rpoSortCfgWithIds(m_unit);
  auto const idoms = findDominators(m_unit, blocksIds);
  m_state.clear();

  for (auto* block : blocksIds.blocks) {
    FTRACE(5, "Block: {}\n", block->id());

    m_state.startBlock(block);
    m_curBlock = block;

    auto nextBlock = block->next();
    auto backMarker = block->back().marker();
    auto instructions = block->moveInstrs();
    assert(block->empty());
    while (!instructions.empty()) {
      auto* inst = &instructions.front();
      instructions.pop_front();

      // merging state looks at the current marker, and optimizeWork
      // below may create new instructions. Use the marker from this
      // instruction.
      assert(inst->marker().valid());
      setMarker(inst->marker());

      auto const tmp = optimizeWork(inst, idoms); // Can generate new instrs!
      if (!tmp) {
        // Could not optimize; keep the old instruction
        appendInstruction(inst);
        continue;
      }

      SSATmp* dst = inst->dst();
      if (dst != tmp) {
        // The result of optimization has a different destination than the inst.
        // Generate a mov(tmp->dst) to get result into dst. If we get here then
        // assume the last instruction in the block isn't a guard. If it was,
        // we would have to insert the mov on the fall-through edge.
        assert(block->empty() || !block->back().isBlockEnd());
        appendInstruction(m_unit.mov(dst, tmp, inst->marker()));
      }

      if (inst->isBlockEnd()) {
        // We're not re-adding the block-end instruction. Unset its edges.
        inst->setTaken(nullptr);
        inst->setNext(nullptr);
      }
    }

    if (block->empty() || !block->back().isBlockEnd()) {
      // Our block-end instruction was eliminated (most likely a Jmp* converted
      // to a nop). Replace it with a jump to the next block.
      appendInstruction(m_unit.gen(Jmp, backMarker, nextBlock));
    }

    m_state.finishBlock(block);
  }
}