Ejemplo n.º 1
0
bool InliningDecider::shouldInline(const Func* callee,
                                   const RegionDesc& region,
                                   uint32_t maxTotalCost) {
  auto sk = region.empty() ? SrcKey() : region.start();
  assertx(callee);
  assertx(sk.func() == callee);

  // Tracing return lambdas.
  auto refuse = [&] (const char* why) {
    FTRACE(1, "shouldInline: rejecting callee region: {}", show(region));
    return traceRefusal(m_topFunc, callee, why);
  };

  auto accept = [&, this] (const char* kind) {
    FTRACE(1, "InliningDecider: inlining {}() <- {}()\t<reason: {}>\n",
           m_topFunc->fullName()->data(), callee->fullName()->data(), kind);
    return true;
  };

  // Check inlining depths.
  if (m_callDepth + 1 >= RuntimeOption::EvalHHIRInliningMaxDepth) {
    return refuse("inlining call depth limit exceeded");
  }
  if (m_stackDepth + callee->maxStackCells() >= kStackCheckLeafPadding) {
    return refuse("inlining stack depth limit exceeded");
  }

  // Even if the func contains NativeImpl we may have broken the trace before
  // we hit it.
  auto containsNativeImpl = [&] {
    for (auto block : region.blocks()) {
      if (!block->empty() && block->last().op() == OpNativeImpl) return true;
    }
    return false;
  };

  // Try to inline CPP builtin functions.
  // The NativeImpl opcode may appear later in the function because of Asserts
  // generated in hhbbc
  if (callee->isCPPBuiltin() && containsNativeImpl()) {
    if (isInlinableCPPBuiltin(callee)) {
      return accept("inlinable CPP builtin");
    }
    return refuse("non-inlinable CPP builtin");
  }

  // If the function may use a VarEnv (which is stored in the ActRec) or may be
  // variadic, we restrict inlined callees to certain whitelisted instructions
  // which we know won't actually require these features.
  const bool needsCheckVVSafe = callee->attrs() & AttrMayUseVV;

  int numRets = 0;
  int numExits = 0;

  // Iterate through the region, checking its suitability for inlining.
  for (auto const& block : region.blocks()) {
    sk = block->start();

    for (auto i = 0, n = block->length(); i < n; ++i, sk.advance()) {
      auto op = sk.op();

      // We don't allow inlined functions in the region.  The client is
      // expected to disable inlining for the region it gives us to peek.
      if (sk.func() != callee) {
        return refuse("got region with inlined calls");
      }

      // Restrict to VV-safe opcodes if necessary.
      if (needsCheckVVSafe && !isInliningVVSafe(op)) {
        return refuse(folly::format("{} may use dynamic environment",
                                    opcodeToName(op)).str().c_str());
      }

      // Count the returns.
      if (isReturnish(op)) {
        if (++numRets > RuntimeOption::EvalHHIRInliningMaxReturns) {
          return refuse("region has too many returns");
        }
        continue;
      }

      // We can't inline FCallArray.  XXX: Why?
      if (op == Op::FCallArray) {
        return refuse("can't inline FCallArray");
      }
    }

    if (region.isExit(block->id())) {
      if (++numExits > RuntimeOption::EvalHHIRInliningMaxBindJmps + numRets) {
        return refuse("region has too many non return exits");
      }
    }
  }

  // Refuse if the cost exceeds our thresholds.
  // We measure the cost of inlining each callstack and stop when it exceeds a
  // certain threshold.  (Note that we do not measure the total cost of all the
  // inlined calls for a given caller---just the cost of each nested stack.)
  const int maxCost = maxTotalCost - m_cost;
  const int cost = computeCost(region);
  if (cost > maxCost) {
    return refuse("too expensive");
  }

  if (numRets == 0) {
    return refuse("region has no returns");
  }
  return accept("small region with single return");
}
Ejemplo n.º 2
0
FuncAnalysis do_analyze_collect(const Index& index,
                                Context const inputCtx,
                                CollectedInfo& collect,
                                ClassAnalysis* clsAnalysis,
                                const std::vector<Type>* knownArgs) {
  auto const ctx = adjust_closure_context(inputCtx);
  FuncAnalysis ai(ctx);

  Trace::Bump bumper{Trace::hhbbc, kTraceFuncBump,
    is_trace_function(ctx.cls, ctx.func)};
  FTRACE(2, "{:-^70}\n-- {}\n", "Analyze", show(ctx));

  /*
   * Set of RPO ids that still need to be visited.
   *
   * Initially, we need each entry block in this list.  As we visit
   * blocks, we propagate states to their successors and across their
   * back edges---when state merges cause a change to the block
   * stateIn, we will add it to this queue so it gets visited again.
   */
  auto incompleteQ = prepare_incompleteQ(index, ai, clsAnalysis, knownArgs);

  /*
   * There are potentially infinitely growing types when we're using
   * union_of to merge states, so occasonially we need to apply a
   * widening operator.
   *
   * Currently this is done by having a straight-forward hueristic: if
   * you visit a block too many times, we'll start doing all the
   * merges with the widening operator until we've had a chance to
   * visit the block again.  We must then continue iterating in case
   * the actual fixed point is higher than the result of widening.
   *
   * Terminiation is guaranteed because the widening operator has only
   * finite chains in the type lattice.
   */
  auto nonWideVisits = std::vector<uint32_t>(ctx.func->nextBlockId);

  // For debugging, count how many times basic blocks get interpreted.
  auto interp_counter = uint32_t{0};

  /*
   * Iterate until a fixed point.
   *
   * Each time a stateIn for a block changes, we re-insert the block's
   * rpo ID in incompleteQ.  Since incompleteQ is ordered, we'll
   * always visit blocks with earlier RPO ids first, which hopefully
   * means less iterations.
   */
  while (!incompleteQ.empty()) {
    auto const blk = ai.rpoBlocks[incompleteQ.pop()];

    if (nonWideVisits[blk->id]++ > options.analyzeFuncWideningLimit) {
      nonWideVisits[blk->id] = 0;
    }

    FTRACE(2, "block #{}\nin {}{}", blk->id,
      state_string(*ctx.func, ai.bdata[blk->id].stateIn),
      property_state_string(collect.props));
    ++interp_counter;

    auto propagate = [&] (php::Block& target, const State& st) {
      auto const needsWiden =
        nonWideVisits[target.id] >= options.analyzeFuncWideningLimit;

      // We haven't optimized the widening operator much, because it
      // doesn't happen in practice right now.  We want to know when
      // it starts happening:
      if (needsWiden) {
        std::fprintf(stderr, "widening in %s on %s\n",
          ctx.unit->filename->data(),
          ctx.func->name->data());
      }

      FTRACE(2, "     {}-> {}\n", needsWiden ? "widening " : "", target.id);
      FTRACE(4, "target old {}",
        state_string(*ctx.func, ai.bdata[target.id].stateIn));

      auto const changed =
        needsWiden ? widen_into(ai.bdata[target.id].stateIn, st)
                   : merge_into(ai.bdata[target.id].stateIn, st);
      if (changed) {
        incompleteQ.push(rpoId(ai, &target));
      }
      FTRACE(4, "target new {}",
        state_string(*ctx.func, ai.bdata[target.id].stateIn));
    };

    auto stateOut = ai.bdata[blk->id].stateIn;
    auto interp   = Interp { index, ctx, collect, blk, stateOut };
    auto flags    = run(interp, propagate);
    if (flags.returned) {
      ai.inferredReturn = union_of(std::move(ai.inferredReturn),
                                   std::move(*flags.returned));
    }
  }

  ai.closureUseTypes = std::move(collect.closureUseTypes);

  if (ctx.func->isGenerator) {
    if (ctx.func->isAsync) {
      // Async generators always return AsyncGenerator object.
      ai.inferredReturn = objExact(index.builtin_class(s_AsyncGenerator.get()));
    } else {
      // Non-async generators always return Generator object.
      ai.inferredReturn = objExact(index.builtin_class(s_Generator.get()));
    }
  } else if (ctx.func->isAsync) {
    // Async functions always return WaitH<T>, where T is the type returned
    // internally.
    ai.inferredReturn = wait_handle(index, ai.inferredReturn);
  }

  /*
   * If inferredReturn is TBottom, the callee didn't execute a return
   * at all.  (E.g. it unconditionally throws, or is an abstract
   * function body.)
   *
   * In this case, we leave the return type as TBottom, to indicate
   * the same to callers.
   */
  assert(ai.inferredReturn.subtypeOf(TGen));

  // For debugging, print the final input states for each block.
  FTRACE(2, "{}", [&] {
    auto const bsep = std::string(60, '=') + "\n";
    auto const sep = std::string(60, '-') + "\n";
    auto ret = folly::format(
      "{}function {} ({} block interps):\n{}",
      bsep,
      show(ctx),
      interp_counter,
      bsep
    ).str();
    for (auto& bd : ai.bdata) {
      ret += folly::format(
        "{}block {}:\nin {}",
        sep,
        ai.rpoBlocks[bd.rpoId]->id,
        state_string(*ctx.func, bd.stateIn)
      ).str();
    }
    ret += sep + bsep;
    folly::format(&ret,
      "Inferred return type: {}\n", show(ai.inferredReturn));
    ret += bsep;
    return ret;
  }());

  return ai;
}
Ejemplo n.º 3
0
RegionDescPtr selectHotTrace(TransID triggerId,
                             const ProfData* profData,
                             TransCFG& cfg,
                             TransIDSet& selectedSet,
                             TransIDVec* selectedVec) {
  auto region = std::make_shared<RegionDesc>();
  TransID tid    = triggerId;
  TransID prevId = kInvalidTransID;
  selectedSet.clear();
  if (selectedVec) selectedVec->clear();

  PostConditions accumPostConds;
  // Maps BlockIds to the set of BC offsets for its successor blocks.
  // Used to prevent multiple successors with the same SrcKey for now.
  // This can go away once task #4157613 is done.
  hphp_hash_map<RegionDesc::BlockId, SrcKeySet> succSKSet;

  // Maps from BlockIds to accumulated post conditions for that block.
  // Used to determine if we can add branch-over edges by checking the
  // pre-conditions of the successor block.
  hphp_hash_map<RegionDesc::BlockId, PostConditions> blockPostConds;

  while (!selectedSet.count(tid)) {

    RegionDescPtr blockRegion = profData->transRegion(tid);
    if (blockRegion == nullptr) break;

    // If the debugger is attached, only allow single-block regions.
    if (prevId != kInvalidTransID && isDebuggerAttachedProcess()) {
      FTRACE(2, "selectHotTrace: breaking region at Translation {} "
             "because of debugger is attached\n", tid);
      break;
    }

    // Break if block is not the first and requires reffiness checks.
    // Task #2589970: fix translateRegion to support mid-region reffiness checks
    if (prevId != kInvalidTransID) {
      auto nRefDeps = blockRegion->entry()->reffinessPreds().size();
      if (nRefDeps > 0) {
        FTRACE(2, "selectHotTrace: breaking region because of refDeps ({}) at "
               "Translation {}\n", nRefDeps, tid);
        break;
      }
    }

    // Break if block is not the first and it corresponds to the main
    // function body entry.  This is to prevent creating multiple
    // large regions containing the function body (starting at various
    // DV funclets).
    if (prevId != kInvalidTransID) {
      const Func* func = profData->transFunc(tid);
      Offset  bcOffset = profData->transStartBcOff(tid);
      if (func->base() == bcOffset) {
        FTRACE(2, "selectHotTrace: breaking region because reached the main "
               "function body entry at Translation {} (BC offset {})\n",
               tid, bcOffset);
        break;
      }
    }

    if (prevId != kInvalidTransID) {
      auto sk = profData->transSrcKey(tid);
      if (profData->optimized(sk)) {
        FTRACE(2, "selectHotTrace: breaking region because next sk already "
               "optimized, for Translation {}\n", tid);
        break;
      }
    }

    // Break trace if translation tid cannot follow the execution of
    // the entire translation prevId.  This can only happen if the
    // execution of prevId takes a side exit that leads to the
    // execution of tid.
    if (prevId != kInvalidTransID) {
      Op* lastInstr = profData->transLastInstr(prevId);
      const Unit* unit = profData->transFunc(prevId)->unit();
      OffsetSet succOffs = instrSuccOffsets(lastInstr, unit);
      if (!succOffs.count(profData->transSrcKey(tid).offset())) {
        if (HPHP::Trace::moduleEnabled(HPHP::Trace::pgo, 2)) {
          FTRACE(2, "selectHotTrace: WARNING: Breaking region @: {}\n",
                 show(*region));
          FTRACE(2, "selectHotTrace: next translation selected: tid = {}\n{}\n",
                 tid, show(*blockRegion));
          FTRACE(2, "\nsuccOffs = {}\n", folly::join(", ", succOffs));
        }
        break;
      }
    }

    bool hasPredBlock = !region->empty();
    RegionDesc::BlockId predBlockId = (hasPredBlock ?
                                       region->blocks().back().get()->id() : 0);
    auto const& newFirstBlock = blockRegion->entry();
    auto newFirstBlockId = newFirstBlock->id();
    auto newFirstBlockSk = newFirstBlock->start();
    auto newLastBlockId  = blockRegion->blocks().back()->id();

    // Make sure we don't end up with multiple successors for the same
    // SrcKey. Task #4157613 will allow the following check to go away.
    // This needs to be done before we insert blockRegion into region,
    // to avoid creating unreachable blocks.
    if (RuntimeOption::EvalHHIRBytecodeControlFlow && hasPredBlock &&
        succSKSet[predBlockId].count(newFirstBlockSk)) {
      break;
    }

    // Add blockRegion's blocks and arcs to region.
    region->append(*blockRegion);

    if (hasPredBlock) {
      if (RuntimeOption::EvalHHIRBytecodeControlFlow) {
        // This is checked above.
        assert(succSKSet[predBlockId].count(newFirstBlockSk) == 0);
        succSKSet[predBlockId].insert(newFirstBlockSk);
      }
      region->addArc(predBlockId, newFirstBlockId);
    }

    // With bytecode control-flow, we add all forward arcs in the TransCFG
    // that are induced by the blocks in the region, as a simple way
    // to expose control-flow for now.
    // This can go away once Task #4075822 is done.
    if (RuntimeOption::EvalHHIRBytecodeControlFlow) {
      assert(hasTransId(newFirstBlockId));
      auto newTransId = getTransId(newFirstBlockId);
      auto& blocks = region->blocks();
      for (auto iOther = 0; iOther < blocks.size(); iOther++) {
        auto other = blocks[iOther];
        auto otherFirstBlockId = other.get()->id();
        if (!hasTransId(otherFirstBlockId)) continue;
        auto otherTransId = getTransId(otherFirstBlockId);
        auto otherFirstBlockSk = other.get()->start();
        auto otherRegion = profData->transRegion(otherTransId);
        auto otherLastBlockId = otherRegion->blocks().back()->id();
        // When loops are off, stop once we hit the newTransId we just inserted.
        if (!RuntimeOption::EvalJitLoops && otherTransId == newTransId) break;
        if (cfg.hasArc(otherTransId, newTransId) &&
            // Task #4157613 will allow the following check to go away
            !succSKSet[otherLastBlockId].count(newFirstBlockSk) &&
            preCondsAreSatisfied(newFirstBlock,
                                 blockPostConds[otherLastBlockId])) {
          region->addArc(otherLastBlockId, newFirstBlockId);
          succSKSet[otherLastBlockId].insert(newFirstBlockSk);
        }
        // When Eval.JitLoops is set, insert back-edges in the
        // region if they exist in the TransCFG.
        if (RuntimeOption::EvalJitLoops &&
            cfg.hasArc(newTransId, otherTransId) &&
            // Task #4157613 will allow the following check to go away
            !succSKSet[newLastBlockId].count(otherFirstBlockSk)) {
          region->addArc(newLastBlockId, otherFirstBlockId);
          succSKSet[newLastBlockId].insert(otherFirstBlockSk);
        }
      }
    }

    if (cfg.outArcs(tid).size() > 1) {
      region->setSideExitingBlock(blockRegion->entry()->id());
    }
    selectedSet.insert(tid);
    if (selectedVec) selectedVec->push_back(tid);

    Op lastOp = *(profData->transLastInstr(tid));
    if (breaksRegion(lastOp)) {
      FTRACE(2, "selectHotTrace: breaking region because of last instruction "
             "in Translation {}: {}\n", tid, opcodeToName(lastOp));
      break;
    }

    auto outArcs = cfg.outArcs(tid);
    if (outArcs.size() == 0) {
      FTRACE(2, "selectHotTrace: breaking region because there's no successor "
             "for Translation {}\n", tid);
      break;
    }

    auto newLastBlock = blockRegion->blocks().back();
    discardPoppedTypes(accumPostConds,
                       blockRegion->entry()->initialSpOffset());
    mergePostConds(accumPostConds, newLastBlock->postConds());
    blockPostConds[newLastBlock->id()] = accumPostConds;

    TransCFG::ArcPtrVec possibleOutArcs;
    for (auto arc : outArcs) {
      RegionDesc::BlockPtr possibleNext =
        profData->transRegion(arc->dst())->entry();
      if (preCondsAreSatisfied(possibleNext, accumPostConds)) {
        possibleOutArcs.emplace_back(arc);
      }
    }

    if (possibleOutArcs.size() == 0) {
      FTRACE(2, "selectHotTrace: breaking region because postcondition check "
             "pruned all successors of Translation {}\n", tid);
      break;
    }

    auto maxWeight = std::numeric_limits<int64_t>::min();
    TransCFG::Arc* maxArc = nullptr;
    for (auto arc : possibleOutArcs) {
      if (arc->weight() >= maxWeight) {
        maxWeight = arc->weight();
        maxArc = arc;
      }
    }
    assert(maxArc != nullptr);
    prevId = tid;
    tid = maxArc->dst();
  }

  return region;
}
Ejemplo n.º 4
0
void FrameState::update(const IRInstruction* inst) {
  FTRACE(3, "FrameState::update processing {}\n", *inst);

  if (auto* taken = inst->taken()) {
    // When we're building the IR, we append a conditional jump after
    // generating its target block: see emitJmpCondHelper, where we
    // call makeExit() before gen(JmpZero).  It doesn't make sense to
    // update the target block state at this point, so don't.  The
    // state doesn't have this problem during optimization passes,
    // because we'll always process the jump before the target block.
    if (!m_building || taken->empty()) save(taken);
  }

  auto const opc = inst->op();

  getLocalEffects(inst, *this);

  switch (opc) {
  case DefInlineFP:    trackDefInlineFP(inst);  break;
  case InlineReturn:   trackInlineReturn(inst); break;

  case Call:
    m_spValue = inst->dst();
    m_frameSpansCall = true;
    // A call pops the ActRec and pushes a return value.
    m_spOffset -= kNumActRecCells;
    m_spOffset += 1;
    assert(m_spOffset >= 0);
    clearCse();
    break;

  case CallArray:
    m_spValue = inst->dst();
    m_frameSpansCall = true;
    // A CallArray pops the ActRec an array arg and pushes a return value.
    m_spOffset -= kNumActRecCells;
    assert(m_spOffset >= 0);
    clearCse();
    break;

  case ContEnter:
    clearCse();
    break;

  case DefFP:
  case FreeActRec:
    m_fpValue = inst->dst();
    break;

  case ReDefResumableSP:
    m_spValue = inst->dst();
    break;

  case ReDefSP:
    m_spValue = inst->dst();
    m_spOffset = inst->extra<ReDefSP>()->spOffset;
    break;

  case DefInlineSP:
  case DefSP:
    m_spValue = inst->dst();
    m_spOffset = inst->extra<StackOffset>()->offset;
    break;

  case AssertStk:
  case CastStk:
  case CoerceStk:
  case CheckStk:
  case GuardStk:
  case ExceptionBarrier:
    m_spValue = inst->dst();
    break;

  case SpillStack: {
    m_spValue = inst->dst();
    // Push the spilled values but adjust for the popped values
    int64_t stackAdjustment = inst->src(1)->intVal();
    m_spOffset -= stackAdjustment;
    m_spOffset += spillValueCells(inst);
    break;
  }

  case SpillFrame:
  case CufIterSpillFrame:
    m_spValue = inst->dst();
    m_spOffset += kNumActRecCells;
    break;

  case InterpOne:
  case InterpOneCF: {
    m_spValue = inst->dst();
    auto const& extra = *inst->extra<InterpOneData>();
    int64_t stackAdjustment = extra.cellsPopped - extra.cellsPushed;
    // push the return value if any and adjust for the popped values
    m_spOffset -= stackAdjustment;
    break;
  }

  case AssertLoc:
  case GuardLoc:
  case CheckLoc:
    m_fpValue = inst->dst();
    break;

  case LdThis:
    m_thisAvailable = true;
    break;

  default:
    break;
  }

  if (inst->modifiesStack()) {
    m_spValue = inst->modifiedStkPtr();
  }

  // update the CSE table
  if (m_enableCse && inst->canCSE()) {
    cseInsert(inst);
  }

  // if the instruction kills any of its sources, remove them from the
  // CSE table
  if (inst->killsSources()) {
    for (int i = 0; i < inst->numSrcs(); ++i) {
      if (inst->killsSource(i)) {
        cseKill(inst->src(i));
      }
    }
  }

  // Save state for each block at the end.
  if (inst->isTerminal()) {
    save(inst->block());
  }
}
Ejemplo n.º 5
0
// fill out the icon with the stop symbol from app_server
void
ConflictView::_FillSavedIcon()
{
    // return if the fSavedIcon has already been filled out
    if (fSavedIcon != NULL && fSavedIcon->InitCheck() == B_OK)
        return;

    BPath path;
    status_t status = find_directory(B_BEOS_SERVERS_DIRECTORY, &path);
    if (status < B_OK) {
        FTRACE((stderr,
                "_FillWarningIcon() - find_directory failed: %s\n",
                strerror(status)));
        delete fSavedIcon;
        fSavedIcon = NULL;
        return;
    }

    path.Append("app_server");
    BFile file;
    status = file.SetTo(path.Path(), B_READ_ONLY);
    if (status < B_OK) {
        FTRACE((stderr,
                "_FillWarningIcon() - BFile init failed: %s\n",
                strerror(status)));
        delete fSavedIcon;
        fSavedIcon = NULL;
        return;
    }

    BResources resources;
    status = resources.SetTo(&file);
    if (status < B_OK) {
        FTRACE((stderr,
                "_WarningIcon() - BResources init failed: %s\n",
                strerror(status)));
        delete fSavedIcon;
        fSavedIcon = NULL;
        return;
    }

    // Allocate the fSavedIcon bitmap
    fSavedIcon = new(std::nothrow) BBitmap(BRect(0, 0, 15, 15), 0, B_RGBA32);
    if (fSavedIcon->InitCheck() < B_OK) {
        FTRACE((stderr, "_WarningIcon() - No memory for warning bitmap\n"));
        delete fSavedIcon;
        fSavedIcon = NULL;
        return;
    }

    // Load the raw stop icon data
    size_t size = 0;
    const uint8* rawIcon;
    rawIcon = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE,
              "stop", &size);

    // load vector warning icon into fSavedIcon
    if (rawIcon == NULL
            || BIconUtils::GetVectorIcon(rawIcon, size, fSavedIcon) < B_OK) {
        delete fSavedIcon;
        fSavedIcon = NULL;
    }
}
Ejemplo n.º 6
0
/*
-------------------------------------------------------------------------------

Class: CSimpleTimeout

Method: CSimpleTimeout

Description: Default constructor

C++ default constructor can NOT contain any code, that
might leave.

Parameters: None

Return Values: None

Errors/Exceptions: None

Status: Approved

-------------------------------------------------------------------------------
*/
CSimpleTimeout::CSimpleTimeout() : CActive (CActive::EPriorityStandard)
{
    FTRACE(FPrint(_L("CSimpleTimeout::CSimpleTimeout")));
}
Ejemplo n.º 7
0
/*
 * reoptimize() runs a trace through a second pass of TraceBuilder
 * 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 TraceBuilder::reoptimize() {
  FTRACE(5, "ReOptimize:vvvvvvvvvvvvvvvvvvvv\n");
  SCOPE_EXIT { FTRACE(5, "ReOptimize:^^^^^^^^^^^^^^^^^^^^\n"); };
  assert(m_curTrace->isMain());
  assert(m_savedTraces.empty());

  m_state.setEnableCse(RuntimeOption::EvalHHIRCse);
  m_enableSimplification = RuntimeOption::EvalHHIRSimplification;
  if (!m_state.enableCse() && !m_enableSimplification) return;
  always_assert(!m_inReoptimize);
  m_inReoptimize = true;

  BlockList sortedBlocks = rpoSortCfg(m_unit);
  auto const idoms = findDominators(m_unit, sortedBlocks);
  m_state.clear();

  auto blocks = std::move(m_curTrace->blocks());
  assert(m_curTrace->blocks().empty());
  while (!blocks.empty()) {
    Block* block = blocks.front();
    blocks.pop_front();
    assert(block->trace() == m_curTrace);
    FTRACE(5, "Block: {}\n", block->id());

    assert(m_curTrace->isMain());
    m_state.startBlock(block);
    m_curTrace->push_back(block);

    auto instructions = std::move(block->instrs());
    assert(block->empty());
    while (!instructions.empty()) {
      auto *inst = &instructions.front();
      instructions.pop_front();
      m_state.setMarker(inst->marker());

      // 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, block);
        m_state.update(inst);
        continue;
      }
      SSATmp* dst = inst->dst();
      if (dst->type() != Type::None && 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());
        IRInstruction* mov = m_unit.mov(dst, tmp, inst->marker());
        appendInstruction(mov, block);
        m_state.update(mov);
      }
      // Not re-adding inst; remove the inst->taken edge
      if (inst->taken()) inst->setTaken(nullptr);
    }

    if (block->empty()) {
      // If all the instructions in the block were optimized away, remove it
      // from the trace.
      auto it = m_curTrace->blocks().end();
      --it;
      assert(*it == block);
      m_curTrace->unlink(it);
    } else {
      if (block->back().isTerminal()) {
        // Could have converted a conditional branch to Jmp; clear next.
        block->setNext(nullptr);
      }
      m_state.finishBlock(block);
    }
  }
}
Ejemplo n.º 8
0
void IRTranslator::interpretInstr(const NormalizedInstruction& i) {
  FTRACE(5, "HHIR: BC Instr {}\n",  i.toString());
  m_hhbcTrans.emitInterpOne(i);
}
Ejemplo n.º 9
0
bool shouldIRInline(const Func* caller, const Func* callee, RegionIter& iter) {
  if (!RuntimeOption::EvalHHIREnableGenTimeInlining) {
    return false;
  }
  if (arch() == Arch::ARM) {
    // TODO(#3331014): hack until more ARM codegen is working.
    return false;
  }
  if (caller->isPseudoMain()) {
    // TODO(#4238160): Hack inlining into pseudomain callsites is still buggy
    return false;
  }

  auto refuse = [&](const char* why) -> bool {
    FTRACE(1, "shouldIRInline: refusing {} <reason: {}> [NI = {}]\n",
           callee->fullName()->data(), why,
           iter.finished() ? "<end>" : iter.sk().showInst());
    return false;
  };
  auto accept = [&](const char* kind) -> bool {
    FTRACE(1, "shouldIRInline: inlining {} <kind: {}>\n",
           callee->fullName()->data(), kind);
    return true;
  };

  if (callee->numIterators() != 0) {
    return refuse("iterators");
  }
  if (callee->isMagic() || Func::isSpecial(callee->name())) {
    return refuse("special or magic function");
  }
  if (callee->attrs() & AttrMayUseVV) {
    return refuse("may use dynamic environment");
  }
  if (callee->isResumable()) {
    return refuse("resumables");
  }
  if (callee->numSlotsInFrame() + callee->maxStackCells() >=
      kStackCheckLeafPadding) {
    return refuse("function stack depth too deep");
  }

  ////////////

  assert(!iter.finished() && "shouldIRInline given empty region");
  bool hotCallingCold = !(callee->attrs() & AttrHot) &&
                         (caller->attrs() & AttrHot);
  uint64_t cost = 0;
  int inlineDepth = 0;
  Op op = OpLowInvalid;
  smart::vector<const Func*> funcs;
  const Func* func = callee;
  funcs.push_back(func);

  for (; !iter.finished(); iter.advance()) {
    // If func has changed after an FCall, we've started an inlined call. This
    // will have to change when we support inlining recursive calls.
    if (func != iter.sk().func()) {
      assert(isRet(op) || op == Op::FCall || op == Op::FCallD);
      if (op == Op::FCall || op == Op::FCallD) {
        funcs.push_back(iter.sk().func());
        int totalDepth = 0;
        for (auto* f : funcs) {
          totalDepth += f->numSlotsInFrame() + f->maxStackCells();
        }
        if (totalDepth >= kStackCheckLeafPadding) {
          return refuse("stack too deep after nested inlining");
        }
        ++inlineDepth;
      }
    }
    op = iter.sk().op();
    func = iter.sk().func();

    // If we hit a RetC/V while inlining, leave that level and
    // continue. Otherwise, accept the tracelet.
    if (isRet(op)) {
      if (inlineDepth > 0) {
        --inlineDepth;
        funcs.pop_back();
        continue;
      } else {
        assert(inlineDepth == 0);
        return accept("entire function fits in one region");
      }
    }

    if (op == Op::FCallArray) return refuse("FCallArray");

    // These opcodes don't indicate any additional work in the callee,
    // so they shouldn't count toward the inlining cost.
    if (op == Op::AssertRATL || op == Op::AssertRATStk) {
      continue;
    }

    cost += 1;

    // Check for an immediate vector, and if it's present add its size to the
    // cost.
    auto const pc = reinterpret_cast<const Op*>(iter.sk().pc());
    if (hasMVector(op)) {
      cost += getMVector(pc).size();
    } else if (hasImmVector(op)) {
      cost += getImmVector(pc).size();
    }

    if (cost > RuntimeOption::EvalHHIRInliningMaxCost) {
      return refuse("too expensive");
    }

    if (cost > RuntimeOption::EvalHHIRAlwaysInlineMaxCost &&
        hotCallingCold) {
      return refuse("inlining sizeable cold func into hot func");
    }

    if (JIT::opcodeBreaksBB(op)) {
      return refuse("breaks tracelet");
    }
  }

  return refuse("region doesn't end in RetC/RetV");
}
Ejemplo n.º 10
0
/**
 * Sorts the regions vector in a linear order to be used for
 * translation.  The goal is to obtain an order that improves locality
 * when the function is executed.
 */
static void sortRegion(RegionVec&                  regions,
                       const Func*                 func,
                       const TransCFG&             cfg,
                       const ProfData*             profData,
                       const TransIDToRegionMap&   headToRegion,
                       const RegionToTransIDsMap&  regionToTransIds) {
  RegionVec sorted;
  RegionSet selected;

  if (regions.size() == 0) return;

  // First, pick the region starting at the lowest bytecode offset.
  // This will normally correspond to the main function entry (for
  // normal, regular bytecode), but it may not be for irregular
  // functions written in hhas (like array_map and array_filter).  If
  // there multiple regions starting at the lowest bytecode offset,
  // pick the one with the largest profile weight.
  RegionDescPtr entryRegion = nullptr;
  int64_t    maxEntryWeight = -1;
  Offset     lowestOffset   = kInvalidOffset;
  for (const auto& pair : regionToTransIds) {
    auto  r    = pair.first;
    auto& tids = pair.second;
    TransID firstTid = tids[0];
    Offset firstOffset = profData->transSrcKey(firstTid).offset();
    int64_t weight = cfg.weight(firstTid);
    if (lowestOffset == kInvalidOffset || firstOffset < lowestOffset ||
        (firstOffset == lowestOffset && weight > maxEntryWeight)) {
      entryRegion    = r;
      maxEntryWeight = weight;
      lowestOffset   = firstOffset;
    }
  }

  assert(entryRegion);
  sorted.push_back(entryRegion);
  selected.insert(entryRegion);

  RegionDescPtr region = entryRegion;
  // Select the remaining regions, iteratively picking the most likely
  // region to execute next.
  for (auto i = 1; i < regions.size(); i++) {
    int64_t      maxWeight = -1;
    int64_t  maxHeadWeight = -1;
    RegionDescPtr bestNext = nullptr;
    auto    regionTransIds = getRegionTransIDVec(regionToTransIds, region);
    for (auto next : regions) {
      if (setContains(selected, next)) continue;
      auto nextTransIds = getRegionTransIDVec(regionToTransIds, next);
      int64_t weight = interRegionWeight(regionTransIds, nextTransIds[0], cfg);
      int64_t headWeight = cfg.weight(nextTransIds[0]);
      if ((weight >  maxWeight) ||
          (weight == maxWeight && headWeight > maxHeadWeight)) {
        maxWeight     = weight;
        maxHeadWeight = headWeight;
        bestNext      = next;
      }
    }
    assert(bestNext);
    sorted.push_back(bestNext);
    selected.insert(bestNext);
    region = bestNext;
  }

  assert(sorted.size() == regions.size());
  regions = sorted;

  if (debug && Trace::moduleEnabled(HPHP::Trace::pgo, 5)) {
    for (size_t i = 0; i < regions.size(); i++) {
      auto r = regions[i];
      auto tids = getRegionTransIDVec(regionToTransIds, r);
      std::string transIds = folly::join(", ", tids);
      FTRACE(6, "sortRegion: region[{}]: {}\n", i, transIds);
    }
  }
}
Ejemplo n.º 11
0
/**
 * Regionize a func, so that each node and each arc in its TransCFG is
 * "covered".  A node is covered if any region contains it.  An arc T1->T2
 * is covered if either:
 *
 *   a) T1 and T2 are in the same region R and T2 immediately follows
 *      T1 in R.
 *   b) T2 is the head (first translation) of a region.
 *
 * Basic algorithm:
 *
 *   1) sort nodes in decreasing weight order
 *   2) for each node N:
 *      2.1) if N and all its incoming arcs are covered, then continue
 *      2.2) select a region starting at this node and mark nodes/arcs as
 *           covered appropriately
 */
void regionizeFunc(const Func*         func,
                   JIT::TranslatorX64* tx64,
                   RegionVec&          regions) {
  assert(RuntimeOption::EvalJitPGO);
  FuncId funcId = func->getFuncId();
  ProfData* profData = tx64->profData();
  TransCFG cfg(funcId, profData, tx64->getSrcDB(), tx64->getJmpToTransIDMap());

  if (Trace::moduleEnabled(HPHP::Trace::pgo, 5)) {
    string dotFileName = folly::to<string>("/tmp/func-cfg-", funcId, ".dot");
    cfg.print(dotFileName, funcId, profData, nullptr);
    FTRACE(5, "regionizeFunc: initial CFG for func {} saved to file {}\n",
           funcId, dotFileName);
  }

  TransCFG::ArcPtrVec arcs = cfg.arcs();
  vector<TransID>    nodes = cfg.nodes();

  std::sort(nodes.begin(), nodes.end(),
            [&](TransID tid1, TransID tid2) -> bool {
              if (cfg.weight(tid1) != cfg.weight(tid2)) {
                return cfg.weight(tid1) > cfg.weight(tid2);
              }
              // In case of ties, pick older translations first, in an
              // attempt to start loops at their headers.
              return tid1 < tid2;
            });

  TransCFG::ArcPtrSet coveredArcs;
  TransIDSet          coveredNodes;
  TransIDSet          heads;
  TransIDToRegionMap  headToRegion;
  RegionToTransIDsMap regionToTransIds;
  regions.clear();

  for (auto node : nodes) {
    if (!setContains(coveredNodes, node) ||
        !allArcsCovered(cfg.inArcs(node),  coveredArcs)) {
      TransID newHead = node;
      FTRACE(6, "regionizeFunc: selecting trace to cover node {}\n", newHead);
      TransIDSet selectedSet;
      TransIDVec selectedVec;
      RegionDescPtr region = selectHotTrace(newHead, profData, cfg,
                                            selectedSet, &selectedVec);
      profData->setOptimized(profData->transSrcKey(newHead));
      assert(selectedVec.size() > 0 && selectedVec[0] == newHead);
      regions.push_back(region);
      heads.insert(newHead);
      markCovered(cfg, selectedVec, heads, coveredNodes, coveredArcs);
      regionToTransIds[region] = selectedVec;
      headToRegion[newHead] = region;

      FTRACE(6, "regionizeFunc: selected trace: {}\n",
             folly::join(", ", selectedVec));
    }
  }

  assert(coveredNodes.size() == cfg.nodes().size());
  assert(coveredArcs.size() == arcs.size());

  sortRegion(regions, func, cfg, profData, headToRegion, regionToTransIds);

  if (debug && Trace::moduleEnabled(HPHP::Trace::pgo, 5)) {
    FTRACE(5, "\n--------------------------------------------\n"
           "regionizeFunc({}): computed regions:\n", funcId);
    for (auto region : regions) {
      FTRACE(5, "{}\n\n", show(*region));
    }
  }
}
Ejemplo n.º 12
0
TInt CIlbcEncoderIntfcTestClass::CreateInputStreamInstance( CStifItemParser& /*aItem */)
	{
	FTRACE(FPrint(_L("CIlbcEncoderIntfcTestClass::CreateInputStreamInstance")));
	iLog->Log(_L("CIlbcEncoderIntfcTestClass::CreateInputStreamInstance"));
	TInt error = KErrNone;
    TRAP(error, iAudioInputStream = CMdaAudioInputStream::NewL(*this););
Ejemplo n.º 13
0
FuncAnalysis do_analyze_collect(const Index& index,
                                Context const ctx,
                                CollectedInfo& collect,
                                ClassAnalysis* clsAnalysis,
                                const std::vector<Type>* knownArgs) {
  assertx(ctx.cls == adjust_closure_context(ctx).cls);
  FuncAnalysis ai{ctx};

  auto const bump = trace_bump_for(ctx.cls, ctx.func);
  Trace::Bump bumper1{Trace::hhbbc, bump};
  Trace::Bump bumper2{Trace::hhbbc_cfg, bump};

  if (knownArgs) {
    FTRACE(2, "{:.^70}\n", "Inline Interp");
  }
  SCOPE_EXIT {
    if (knownArgs) {
      FTRACE(2, "{:.^70}\n", "End Inline Interp");
    }
  };

  FTRACE(2, "{:-^70}\n-- {}\n", "Analyze", show(ctx));

  /*
   * Set of RPO ids that still need to be visited.
   *
   * Initially, we need each entry block in this list.  As we visit
   * blocks, we propagate states to their successors and across their
   * back edges---when state merges cause a change to the block
   * stateIn, we will add it to this queue so it gets visited again.
   */
  auto incompleteQ = prepare_incompleteQ(index, ai, clsAnalysis, knownArgs);

  /*
   * There are potentially infinitely growing types when we're using union_of to
   * merge states, so occasionally we need to apply a widening operator.
   *
   * Currently this is done by having a straight-forward hueristic: if you visit
   * a block too many times, we'll start doing all the merges with the widening
   * operator. We must then continue iterating in case the actual fixed point is
   * higher than the result of widening. Likewise if we loop too much because of
   * local static types changing, we'll widen those.
   *
   * Termination is guaranteed because the widening operator has only finite
   * chains in the type lattice.
   */
  auto totalVisits = std::vector<uint32_t>(ctx.func->blocks.size());
  auto totalLoops = uint32_t{0};

  // For debugging, count how many times basic blocks get interpreted.
  auto interp_counter = uint32_t{0};

  // Used to force blocks that depended on the types of local statics
  // to be re-analyzed when the local statics change.
  std::unordered_map<borrowed_ptr<const php::Block>, std::map<LocalId, Type>>
    usedLocalStatics;

  /*
   * Iterate until a fixed point.
   *
   * Each time a stateIn for a block changes, we re-insert the block's
   * rpo ID in incompleteQ.  Since incompleteQ is ordered, we'll
   * always visit blocks with earlier RPO ids first, which hopefully
   * means less iterations.
   */
  do {
    while (!incompleteQ.empty()) {
      auto const blk = ai.rpoBlocks[incompleteQ.pop()];

      totalVisits[blk->id]++;

      FTRACE(2, "block #{}\nin {}{}", blk->id,
             state_string(*ctx.func, ai.bdata[blk->id].stateIn, collect),
             property_state_string(collect.props));
      ++interp_counter;

      auto propagate = [&] (BlockId target, const State* st) {
        if (!st) {
          FTRACE(2, "     Force reprocess: {}\n", target);
          incompleteQ.push(rpoId(ai, target));
          return;
        }

        auto const needsWiden =
          totalVisits[target] >= options.analyzeFuncWideningLimit;

        FTRACE(2, "     {}-> {}\n", needsWiden ? "widening " : "", target);
        FTRACE(4, "target old {}",
               state_string(*ctx.func, ai.bdata[target].stateIn, collect));

        auto const changed =
          needsWiden ? widen_into(ai.bdata[target].stateIn, *st)
                     : merge_into(ai.bdata[target].stateIn, *st);
        if (changed) {
          incompleteQ.push(rpoId(ai, target));
        }
        FTRACE(4, "target new {}",
               state_string(*ctx.func, ai.bdata[target].stateIn, collect));
      };

      auto stateOut = ai.bdata[blk->id].stateIn;
      auto interp   = Interp { index, ctx, collect, blk, stateOut };
      auto flags    = run(interp, propagate);
      if (any(collect.opts & CollectionOpts::EffectFreeOnly) &&
          !collect.effectFree) {
        break;
      }
      // We only care about the usedLocalStatics from the last visit
      if (flags.usedLocalStatics) {
        usedLocalStatics[blk] = std::move(*flags.usedLocalStatics);
      } else {
        usedLocalStatics.erase(blk);
      }

      if (flags.returned) {
        ai.inferredReturn |= std::move(*flags.returned);
      }
    }

    if (any(collect.opts & CollectionOpts::EffectFreeOnly) &&
        !collect.effectFree) {
      break;
    }

    // maybe some local statics changed type since the last time their
    // blocks were visited.

    if (totalLoops++ >= options.analyzeFuncWideningLimit) {
      // If we loop too many times because of static locals, widen them to
      // ensure termination.
      for (auto& t : collect.localStaticTypes) {
        t = widen_type(std::move(t));
      }
    }

    for (auto const& elm : usedLocalStatics) {
      for (auto const& ls : elm.second) {
        if (collect.localStaticTypes[ls.first] != ls.second) {
          incompleteQ.push(rpoId(ai, elm.first->id));
          break;
        }
      }
    }
  } while (!incompleteQ.empty());

  ai.closureUseTypes = std::move(collect.closureUseTypes);
  ai.cnsMap = std::move(collect.cnsMap);
  ai.readsUntrackedConstants = collect.readsUntrackedConstants;
  ai.mayUseVV = collect.mayUseVV;
  ai.effectFree = collect.effectFree;
  ai.unfoldableFuncs = collect.unfoldableFuncs;

  index.fixup_return_type(ctx.func, ai.inferredReturn);

  /*
   * If inferredReturn is TBottom, the callee didn't execute a return
   * at all.  (E.g. it unconditionally throws, or is an abstract
   * function body.)
   *
   * In this case, we leave the return type as TBottom, to indicate
   * the same to callers.
   */
  assert(ai.inferredReturn.subtypeOf(TGen));

  // For debugging, print the final input states for each block.
  FTRACE(2, "{}", [&] {
    auto const bsep = std::string(60, '=') + "\n";
    auto const sep = std::string(60, '-') + "\n";
    auto ret = folly::format(
      "{}function {} ({} block interps):\n{}",
      bsep,
      show(ctx),
      interp_counter,
      bsep
    ).str();
    for (auto& bd : ai.bdata) {
      folly::format(
        &ret,
        "{}block {}:\nin {}",
        sep,
        ai.rpoBlocks[bd.rpoId]->id,
        state_string(*ctx.func, bd.stateIn, collect)
      );
    }
    ret += sep + bsep;
    folly::format(&ret, "Inferred return type: {}\n", show(ai.inferredReturn));
    ret += bsep;
    return ret;
  }());

  // Do this after the tracing above
  ai.localStaticTypes = std::move(collect.localStaticTypes);
  return ai;
}
// -----------------------------------------------------------------------------
// CBTServiceDelayedDestroyer::RunError()
// -----------------------------------------------------------------------------
//      
TInt CBTServiceDelayedDestroyer::RunError(TInt aError)
    {
    FTRACE(FPrint(_L("[BTSU]\t CBTServiceStarter::RunError() aError = %d"), aError) );
    (void) aError;
    return KErrNone;
    } 
Ejemplo n.º 15
0
/*
-------------------------------------------------------------------------------

Class: CSimpleTimeout

Method: ~CSimpleTimeout

Description: Destructor.

Cancel request

Parameters: None

Return Values: None

Errors/Exceptions: None

Status: Approved

-------------------------------------------------------------------------------
*/
CSimpleTimeout::~CSimpleTimeout()
{
    FTRACE(FPrint(_L("CSimpleTimeout::~CSimpleTimeout")));
    Cancel();
    iTimer.Close();
}
Ejemplo n.º 16
0
TransCFG::TransCFG(FuncId funcId,
                   const ProfData* profData,
                   const SrcDB& srcDB,
                   const TcaTransIDMap& jmpToTransID) {
  assert(profData);

  // add nodes
  for (auto tid : profData->funcProfTransIDs(funcId)) {
    assert(profData->transRegion(tid) != nullptr);
    // This will skip DV Funclets if they were already
    // retranslated w/ the prologues:
    if (!profData->optimized(profData->transSrcKey(tid))) {
      int64_t counter = profData->transCounter(tid);
      int64_t weight  = RuntimeOption::EvalJitPGOThreshold - counter;
      addNode(tid, weight);
    }
  }

  // add arcs
  for (TransID dstId : nodes()) {
    SrcKey dstSK = profData->transSrcKey(dstId);
    RegionDesc::BlockPtr dstBlock = profData->transRegion(dstId)->blocks[0];
    const SrcRec* dstSR = srcDB.find(dstSK);
    FTRACE(5, "TransCFG: adding incoming arcs in dstId = {}\n", dstId);
    TransIDSet predIDs = findPredTrans(dstSR, jmpToTransID);
    for (auto predId : predIDs) {
      if (hasNode(predId)) {
        auto predPostConds =
          profData->transRegion(predId)->blocks.back()->postConds();
        SrcKey predSK = profData->transSrcKey(predId);
        if (preCondsAreSatisfied(dstBlock, predPostConds) &&
            predSK.resumed() == dstSK.resumed()) {
          FTRACE(5, "TransCFG: adding arc {} -> {} ({} -> {})\n",
                 predId, dstId, showShort(predSK), showShort(dstSK));
          addArc(predId, dstId, TransCFG::Arc::kUnknownWeight);
        }
      }
    }
  }

  // infer arc weights
  bool changed;
  do {
    changed = false;
    for (TransID tid : nodes()) {
      int64_t nodeWeight = weight(tid);
      if (inferredArcWeight(inArcs(tid),  nodeWeight)) changed = true;
      if (inferredArcWeight(outArcs(tid), nodeWeight)) changed = true;
    }
  } while (changed);

  // guess weight or non-inferred arcs
  for (TransID tid : nodes()) {
    for (auto arc : outArcs(tid)) {
      if (arc->weight() == Arc::kUnknownWeight) {
        arc->setGuessed();
        int64_t arcWgt = std::min(weight(arc->src()), weight(arc->dst())) / 2;
        arc->setWeight(arcWgt);
      }
    }
  }
}
Ejemplo n.º 17
0
/*
-------------------------------------------------------------------------------

Class: CSimpleTimeout

Method: DoCancel

Description: Cancel active request

Parameters: None

Return Values: None

Errors/Exceptions: None

Status: Approved

-------------------------------------------------------------------------------
*/
void CSimpleTimeout::DoCancel()
{
    FTRACE(FPrint(_L("CSimpleTimeout::DoCancel")));
    iTimer.Cancel();
}
Ejemplo n.º 18
0
/*
 * reoptimize() runs a trace through a second pass of TraceBuilder
 * 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 TraceBuilder::reoptimize() {
  FTRACE(5, "ReOptimize:vvvvvvvvvvvvvvvvvvvv\n");
  SCOPE_EXIT { FTRACE(5, "ReOptimize:^^^^^^^^^^^^^^^^^^^^\n"); };
  assert(m_savedBlocks.empty());
  assert(!m_curWhere);

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

  BlockList sortedBlocks = rpoSortCfg(m_unit);
  auto const idoms = findDominators(m_unit, sortedBlocks);
  m_state.clear();

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

    m_state.startBlock(block);
    m_curBlock = block;

    auto instructions = std::move(block->instrs());
    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->type() != Type::None && 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());
        IRInstruction* mov = m_unit.mov(dst, tmp, inst->marker());
        appendInstruction(mov);
      }

      if (inst->isBlockEnd()) {
        // Not re-adding inst; replace it with a jump to the next block.
        auto next = inst->next();
        appendInstruction(m_unit.gen(Jmp, inst->marker(), next));
        inst->setTaken(nullptr);
        inst->setNext(nullptr);
      }
    }

    assert(!block->empty());
    m_state.finishBlock(block);
  }
}
Ejemplo n.º 19
0
SSATmp* TraceBuilder::optimizeWork(IRInstruction* inst,
                                   const folly::Optional<IdomVector>& idoms) {
  // Since some of these optimizations inspect tracked state, we don't
  // perform any of them on non-main traces.
  if (m_savedTraces.size() > 0) return nullptr;

  static DEBUG_ONLY __thread int instNest = 0;
  if (debug) ++instNest;
  SCOPE_EXIT { if (debug) --instNest; };
  DEBUG_ONLY auto indent = [&] { return std::string(instNest * 2, ' '); };

  FTRACE(1, "optimizing {}{}\n", indent(), inst->toString());

  // First pass of tracebuilder optimizations try to replace an
  // instruction based on tracked state before we do anything else.
  // May mutate the IRInstruction in place (and return nullptr) or
  // return an SSATmp*.
  if (SSATmp* preOpt = preOptimize(inst)) {
    FTRACE(1, "  {}preOptimize returned: {}\n",
           indent(), preOpt->inst()->toString());
    return preOpt;
  }
  if (inst->op() == Nop) return nullptr;

  // copy propagation on inst source operands
  copyProp(inst);

  SSATmp* result = nullptr;

  if (m_enableSimplification) {
    result = m_simplifier.simplify(inst);
    if (result) {
      inst = result->inst();
      if (inst->producesReference(0)) {
        // This effectively prevents CSE from kicking in below, which
        // would replace the instruction with an IncRef.  That is
        // correct if the simplifier morphed the instruction, but it's
        // incorrect if the simplifier returned one of original
        // instruction sources.  We currently have no way to
        // distinguish the two cases, so we prevent CSE completely for
        // now.
        return result;
      }
    }
  }

  if (m_state.enableCse() && inst->canCSE()) {
    SSATmp* cseResult = m_state.cseLookup(inst, idoms);
    if (cseResult) {
      // Found a dominating instruction that can be used instead of inst
      FTRACE(1, "  {}cse found: {}\n",
             indent(), cseResult->inst()->toString());

      assert(!inst->consumesReferences());
      if (inst->producesReference(0)) {
        // Replace with an IncRef
        FTRACE(1, "  {}cse of refcount-producing instruction\n", indent());
        gen(IncRef, cseResult);
      }
      return cseResult;
    }
  }

  return result;
}
Ejemplo n.º 20
0
// ---------------------------------------------------------------------------
// Destructor.
// ---------------------------------------------------------------------------
//
CDunBtPlugin::~CDunBtPlugin()
    {
    FTRACE(FPrint( _L( "CDunBtPlugin::~CDunBtPlugin()" ) ));
    Uninitialize();
    FTRACE(FPrint( _L( "CDunBtPlugin::~CDunBtPlugin() complete" ) ));
    }
Ejemplo n.º 21
0
/**
 * Trace back to the guard that provided the type of val, if
 * any. Constrain it so its type will not be relaxed beyond the given
 * DataTypeCategory. Returns true iff one or more guard instructions
 * were constrained.
 */
bool TraceBuilder::constrainValue(SSATmp* const val,
                                  TypeConstraint tc) {
  if (!shouldConstrainGuards()) return false;

  if (!val) {
    FTRACE(1, "constrainValue(nullptr, {}), bailing\n", tc);
    return false;
  }

  FTRACE(1, "constrainValue({}, {})\n", *val->inst(), tc);

  auto inst = val->inst();
  if (inst->is(LdLoc, LdLocAddr)) {
    // We've hit a LdLoc(Addr). If the source of the value is non-null and not
    // a FramePtr, it's a real value that was killed by a Call. The value won't
    // be live but it's ok to use it to track down the guard.

    auto source = inst->extra<LocalData>()->valSrc;
    if (!source) {
      // val was newly created in this trace. Nothing to constrain.
      FTRACE(2, "  - valSrc is null, bailing\n");
      return false;
    }

    // If valSrc is a FramePtr, it represents the frame the value was
    // originally loaded from. Look for the guard for this local.
    if (source->isA(Type::FramePtr)) {
      return constrainLocal(inst->extra<LocalId>()->locId, source, tc,
                            "constrainValue");
    }

    // Otherwise, keep chasing down the source of val.
    return constrainValue(source, tc);
  } else if (inst->is(LdStack, LdStackAddr)) {
    return constrainStack(inst->src(0), inst->extra<StackOffset>()->offset,
                          tc);
  } else if (inst->is(CheckType, AssertType)) {
    // If the dest type of the instruction fits the constraint we want, we can
    // stop here without constraining any further. Otherwise, continue through
    // to the source.
    auto changed = false;
    if (inst->is(CheckType)) changed = constrainGuard(inst, tc) || changed;

    auto dstType = inst->typeParam();
    if (!typeFitsConstraint(dstType, tc.category)) {
      changed = constrainValue(inst->src(0), tc) || changed;
    }
    return changed;
  } else if (inst->is(StRef, StRefNT, Box, BoxPtr)) {
    // If our caller cares about the inner type, propagate that through.
    // Otherwise we're done.
    if (tc.innerCat) {
      auto src = inst->src(inst->is(StRef, StRefNT) ? 1 : 0);
      tc.innerCat.reset();
      return constrainValue(src, tc);
    }
    return false;
  } else if (inst->is(LdRef, Unbox, UnboxPtr)) {
    // Pass through to the source of the box, remembering that we care about
    // the inner type of the box.
    assert(!tc.innerCat);
    tc.innerCat = tc.category;
    return constrainValue(inst->src(0), tc);
  } else if (inst->isPassthrough()) {
    return constrainValue(inst->getPassthroughValue(), tc);
  } else {
    // Any instructions not special cased above produce a new value, so
    // there's no guard for us to constrain.
    FTRACE(2, "  - value is new in this trace, bailing\n");
    return false;
  }
  // TODO(t2598894): Should be able to do something with LdMem<T> here
}
void RegionDesc::Block::addPredicted(SrcKey sk, TypePred pred) {
    FTRACE(2, "Block::addPredicted({}, {})\n", showShort(sk), show(pred));
    assert(pred.type.subtypeOf(Type::Gen | Type::Cls));
    assert(contains(sk));
    m_typePreds.insert(std::make_pair(sk, pred));
}
Ejemplo n.º 23
0
/*
 * reoptimize() runs a trace through a second pass of TraceBuilder
 * 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 TraceBuilder::reoptimize() {
    FTRACE(5, "ReOptimize:vvvvvvvvvvvvvvvvvvvv\n");
    SCOPE_EXIT { FTRACE(5, "ReOptimize:^^^^^^^^^^^^^^^^^^^^\n"); };
    assert(m_curTrace == m_mainTrace.get());
    assert(m_savedTraces.size() == 0);

    m_enableCse = RuntimeOption::EvalHHIRCse;
    m_enableSimplification = RuntimeOption::EvalHHIRSimplification;
    if (!m_enableCse && !m_enableSimplification) return;
    if (m_mainTrace->blocks().size() >
            RuntimeOption::EvalHHIRSimplificationMaxBlocks) {
        // TODO CSEHash::filter is very slow for large block sizes
        // t2135219 should address that
        return;
    }

    BlockList sortedBlocks = rpoSortCfg(m_mainTrace.get(), m_irFactory);
    auto const idoms = findDominators(sortedBlocks);
    clearTrackedState();

    auto blocks = std::move(m_mainTrace->blocks());
    assert(m_mainTrace->blocks().empty());
    while (!blocks.empty()) {
        Block* block = blocks.front();
        blocks.pop_front();
        assert(block->trace() == m_mainTrace.get());
        FTRACE(5, "Block: {}\n", block->id());

        m_mainTrace->push_back(block);
        if (m_snapshots[block]) {
            useState(block);
        }

        auto instructions = std::move(block->instrs());
        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, block);
                updateTrackedState(inst);
                continue;
            }
            SSATmp* dst = inst->dst();
            if (dst->type() != Type::None && 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());
                IRInstruction* mov = m_irFactory.mov(dst, tmp, inst->marker());
                appendInstruction(mov, block);
                updateTrackedState(mov);
            }
            // Not re-adding inst; remove the inst->taken edge
            if (inst->taken()) inst->setTaken(nullptr);
        }
        if (block->back()->isTerminal()) {
            // Could have converted a conditional branch to Jmp; clear next.
            block->setNext(nullptr);
        } else {
            // if the last instruction was a branch, we already saved state
            // for the target in updateTrackedState().  Now save state for
            // the fall-through path.
            saveState(block->next());
        }
    }
}
void RegionDesc::Block::addReffinessPred(SrcKey sk, const ReffinessPred& pred) {
    FTRACE(2, "Block::addReffinessPred({}, {})\n", showShort(sk), show(pred));
    assert(contains(sk));
    m_refPreds.insert(std::make_pair(sk, pred));
}
Ejemplo n.º 25
0
void region_prune_arcs(RegionDesc& region, std::vector<Type>* input) {
  FTRACE(4, "region_prune_arcs\n");

  region.sortBlocks();
  auto const sortedBlocks = region.blocks();

  // Maps region block ids to their RPO ids.
  auto blockToRPO = std::unordered_map<RegionDesc::BlockId,uint32_t>{};

  auto blockInfos = std::vector<BlockInfo>(sortedBlocks.size());
  auto workQ = dataflow_worklist<uint32_t>(sortedBlocks.size());
  for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) {
    auto const& b = sortedBlocks[rpoID];
    auto& binfo = blockInfos[rpoID];
    binfo.blockID = b->id();
    blockToRPO[binfo.blockID] = rpoID;
  }
  workQ.push(0);
  blockInfos[0].in = entry_state(region, input);

  FTRACE(4, "Iterating:\n");
  do {
    auto const rpoID = workQ.pop();
    auto& binfo = blockInfos[rpoID];
    FTRACE(4, "B{}\n", binfo.blockID);

    binfo.out = binfo.in;
    apply_transfer_function(
      binfo.out,
      region.block(binfo.blockID)->postConds()
    );

    for (auto& succ : region.succs(binfo.blockID)) {
      auto const succRPO = blockToRPO.find(succ);
      assertx(succRPO != end(blockToRPO));
      auto& succInfo = blockInfos[succRPO->second];
      if (preconds_may_pass(*region.block(succInfo.blockID), binfo.out)) {
        if (merge_into(succInfo.in, binfo.out)) {
          FTRACE(5, "  -> {}\n", succInfo.blockID);
          workQ.push(succRPO->second);
        }
      }
    }
  } while (!workQ.empty());

  FTRACE(2, "\nPostConds fixed point:\n{}\n",
    [&] () -> std::string {
      auto ret = std::string{};
      for (auto& s : blockInfos) {
        folly::format(&ret, "B{}:\n{}", s.blockID, show(s.in));
      }
      return ret;
    }()
  );

  // Now remove any edge that looks like it will unconditionally fail type
  // predictions, and completely remove any block that can't be reached.
  using ArcIDs = std::pair<RegionDesc::BlockId,RegionDesc::BlockId>;
  auto toRemove = std::vector<ArcIDs>{};
  for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) {
    auto const& binfo = blockInfos[rpoID];

    for (auto& succ : region.succs(binfo.blockID)) {
      auto const succRPO = blockToRPO.find(succ);
      assertx(succRPO != end(blockToRPO));
      auto const& succInfo = blockInfos[succRPO->second];
      if (!binfo.in.initialized ||
          !succInfo.in.initialized ||
          !preconds_may_pass(*region.block(succInfo.blockID), binfo.out)) {
        FTRACE(2, "Pruning arc: B{} -> B{}\n",
               binfo.blockID,
               succInfo.blockID);
        toRemove.emplace_back(binfo.blockID, succInfo.blockID);
      }
    }

    for (auto& r : toRemove) region.removeArc(r.first, r.second);
    toRemove.clear();
  }

  // Get rid of the completely unreachable blocks, now that any arcs to/from
  // them are gone.
  for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) {
    auto const& binfo = blockInfos[rpoID];
    if (!binfo.in.initialized) {
      FTRACE(2, "Pruning block: B{}\n", binfo.blockID);
      region.deleteBlock(binfo.blockID);
    }
  }
  FTRACE(2, "\n");
}
RegionDescPtr selectTraceletLegacy(const Transl::Tracelet& tlet) {
    typedef RegionDesc::Block Block;

    auto region = std::make_shared<RegionDesc>();
    SrcKey sk(tlet.m_sk);
    auto unit = tlet.func()->unit();

    const Func* topFunc = nullptr;
    Block* curBlock = nullptr;
    auto newBlock = [&](const Func* func, SrcKey start) {
        assert(curBlock == nullptr || curBlock->length() > 0);
        region->blocks.push_back(
            std::make_shared<Block>(func, start.offset(), 0));
        curBlock = region->blocks.back().get();
    };
    newBlock(tlet.func(), sk);

    for (auto ni = tlet.m_instrStream.first; ni; ni = ni->next) {
        assert(sk == ni->source);
        assert(ni->unit() == unit);

        curBlock->addInstruction();
        if ((curBlock->length() == 1 && ni->funcd != nullptr) ||
                ni->funcd != topFunc) {
            topFunc = ni->funcd;
            curBlock->setKnownFunc(sk, topFunc);
        }

        if (ni->calleeTrace && !ni->calleeTrace->m_inliningFailed) {
            assert(ni->op() == OpFCall);
            assert(ni->funcd == ni->calleeTrace->func());
            // This should be translated as an inlined call. Insert the blocks of the
            // callee in the region.
            auto const& callee = *ni->calleeTrace;
            curBlock->setInlinedCallee(ni->funcd);
            SrcKey cSk = callee.m_sk;
            Unit* cUnit = callee.func()->unit();

            newBlock(callee.func(), cSk);

            for (auto cni = callee.m_instrStream.first; cni; cni = cni->next) {
                assert(cSk == cni->source);
                assert(cni->op() == OpRetC ||
                       cni->op() == OpContRetC ||
                       cni->op() == OpNativeImpl ||
                       !instrIsNonCallControlFlow(cni->op()));

                curBlock->addInstruction();
                cSk.advance(cUnit);
            }

            if (ni->next) {
                sk.advance(unit);
                newBlock(tlet.func(), sk);
            }
            continue;
        }

        if (!ni->noOp && isFPassStar(ni->op())) {
            curBlock->setParamByRef(sk, ni->preppedByRef);
        }

        if (ni->next && ni->op() == OpJmp) {
            // A Jmp that isn't the final instruction in a Tracelet means we traced
            // through a forward jump in analyze. Update sk to point to the next NI
            // in the stream.
            auto dest = ni->offset() + ni->imm[0].u_BA;
            assert(dest > sk.offset()); // We only trace for forward Jmps for now.
            sk.setOffset(dest);

            // The Jmp terminates this block.
            newBlock(tlet.func(), sk);
        } else {
            sk.advance(unit);
        }
    }

    auto& frontBlock = *region->blocks.front();

    // Add tracelet guards as predictions on the first instruction. Predictions
    // and known types from static analysis will be applied by
    // Translator::translateRegion.
    for (auto const& dep : tlet.m_dependencies) {
        if (dep.second->rtt.isVagueValue() ||
                dep.second->location.isThis()) continue;

        typedef RegionDesc R;
        auto addPred = [&](const R::Location& loc) {
            auto type = Type::fromRuntimeType(dep.second->rtt);
            frontBlock.addPredicted(tlet.m_sk, {loc, type});
        };

        switch (dep.first.space) {
        case Transl::Location::Stack:
            addPred(R::Location::Stack{uint32_t(-dep.first.offset - 1)});
            break;

        case Transl::Location::Local:
            addPred(R::Location::Local{uint32_t(dep.first.offset)});
            break;

        default:
            not_reached();
        }
    }

    // Add reffiness dependencies as predictions on the first instruction.
    for (auto const& dep : tlet.m_refDeps.m_arMap) {
        RegionDesc::ReffinessPred pred{dep.second.m_mask,
                                       dep.second.m_vals,
                                       dep.first};
        frontBlock.addReffinessPred(tlet.m_sk, pred);
    }

    FTRACE(2, "Converted Tracelet:\n{}\nInto RegionDesc:\n{}\n",
           tlet.toString(), show(*region));
    return region;
}
Ejemplo n.º 27
0
void
FontManager::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case B_NODE_MONITOR:
		{
			// TODO: support removing fonts!

			int32 opcode;
			if (message->FindInt32("opcode", &opcode) != B_OK)
				return;

			switch (opcode) {
				case B_ENTRY_CREATED:
				{
					const char* name;
					node_ref nodeRef;
					if (message->FindInt32("device", &nodeRef.device) != B_OK
						|| message->FindInt64("directory", &nodeRef.node) != B_OK
						|| message->FindString("name", &name) != B_OK)
						break;

					// TODO: make this better (possible under Haiku)
					snooze(100000);
						// let the font be written completely before trying to open it

					BEntry entry;
					if (set_entry(nodeRef, name, entry) != B_OK)
						break;

					if (entry.IsDirectory()) {
						// a new directory to watch for us
						_AddPath(entry);
					} else {
						// a new font
						font_directory* directory = _FindDirectory(nodeRef);
						if (directory == NULL) {
							// unknown directory? how come?
							break;
						}

						_AddFont(*directory, entry);
					}
					break;
				}

				case B_ENTRY_MOVED:
				{
					// has the entry been moved into a monitored directory or has
					// it been removed from one?
					const char* name;
					node_ref nodeRef;
					uint64 fromNode;
					uint64 node;
					if (message->FindInt32("device", &nodeRef.device) != B_OK
						|| message->FindInt64("to directory", &nodeRef.node) != B_OK
						|| message->FindInt64("from directory", (int64 *)&fromNode) != B_OK
						|| message->FindInt64("node", (int64 *)&node) != B_OK
						|| message->FindString("name", &name) != B_OK)
						break;

					font_directory* directory = _FindDirectory(nodeRef);

					BEntry entry;
					if (set_entry(nodeRef, name, entry) != B_OK)
						break;

					if (directory != NULL) {
						// something has been added to our watched font directories

						// test, if the source directory is one of ours as well
						nodeRef.node = fromNode;
						font_directory* fromDirectory = _FindDirectory(nodeRef);

						if (entry.IsDirectory()) {
							if (fromDirectory == NULL) {
								// there is a new directory to watch for us
								_AddPath(entry);
								FTRACE("new directory moved in");
							} else {
								// A directory from our watched directories has
								// been renamed or moved within the watched
								// directories - we only need to update the
								// path names of the styles in that directory
								nodeRef.node = node;
								directory = _FindDirectory(nodeRef);
								if (directory != NULL) {
									for (int32 i = 0; i < directory->styles.CountItems(); i++) {
										FontStyle* style = directory->styles.ItemAt(i);
										style->UpdatePath(directory->directory);
									}
								}
								FTRACE("directory renamed");
							}
						} else {
							if (fromDirectory != NULL) {
								// find style in source and move it to the target
								nodeRef.node = node;
								FontStyle* style = fromDirectory->FindStyle(nodeRef);
								if (style != NULL) {
									fromDirectory->styles.RemoveItem(style, false);
									directory->styles.AddItem(style);
									style->UpdatePath(directory->directory);
								}
								FTRACE(("font moved"));
							} else {
								FTRACE(("font added: %s\n", name));
								_AddFont(*directory, entry);
							}
						}
					} else {
						// and entry has been removed from our font directories
						if (entry.IsDirectory()) {
							if (entry.GetNodeRef(&nodeRef) == B_OK
								&& (directory = _FindDirectory(nodeRef)) != NULL)
								_RemoveDirectory(directory);
						} else {
							// remove font style from directory
							_RemoveStyle(nodeRef.device, fromNode, node);
						}
					}
					break;
				}

				case B_ENTRY_REMOVED:
				{
					node_ref nodeRef;
					uint64 directoryNode;
					if (message->FindInt32("device", &nodeRef.device) != B_OK
						|| message->FindInt64("directory", (int64 *)&directoryNode) != B_OK
						|| message->FindInt64("node", &nodeRef.node) != B_OK)
						break;

					font_directory* directory = _FindDirectory(nodeRef);
					if (directory != NULL) {
						// the directory has been removed, so we remove it as well
						_RemoveDirectory(directory);
					} else {
						// remove font style from directory
						_RemoveStyle(nodeRef.device, directoryNode, nodeRef.node);
					}
					break;
				}
			}
			break;
		}
	}
}
Ejemplo n.º 28
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));
        }
      }
    }
  }
Ejemplo n.º 29
0
void MemoryManager::resetStatsImpl(bool isInternalCall) {
#ifdef USE_JEMALLOC
  FTRACE(1, "resetStatsImpl({}) pre:\n", isInternalCall);
  FTRACE(1, "usage: {}\nalloc: {}\npeak usage: {}\npeak alloc: {}\n",
    m_stats.usage, m_stats.alloc, m_stats.peakUsage, m_stats.peakAlloc);
  FTRACE(1, "total alloc: {}\nje alloc: {}\nje dealloc: {}\n",
    m_stats.totalAlloc, m_prevAllocated, m_prevDeallocated);
  FTRACE(1, "je debt: {}\n\n", m_stats.jemallocDebt);
#else
  FTRACE(1, "resetStatsImpl({}) pre:\n"
    "usage: {}\nalloc: {}\npeak usage: {}\npeak alloc: {}\n\n",
    isInternalCall,
    m_stats.usage, m_stats.alloc, m_stats.peakUsage, m_stats.peakAlloc);
#endif
  if (isInternalCall) {
    m_statsIntervalActive = false;
    m_stats.usage = 0;
    m_stats.alloc = 0;
    m_stats.peakUsage = 0;
    m_stats.peakAlloc = 0;
    m_stats.totalAlloc = 0;
    m_stats.peakIntervalUsage = 0;
    m_stats.peakIntervalAlloc = 0;
#ifdef USE_JEMALLOC
    m_enableStatsSync = false;
#endif
  } else {
    // This is only set by the jemalloc stats sync which we don't enable until
    // after this has been called.
    assert(m_stats.totalAlloc == 0);
    // We expect some thread local initialization to have been done already.
    assert(m_slabs.size() > 0);
#ifdef USE_JEMALLOC
    assert(m_stats.jemallocDebt >= m_stats.alloc);
#endif

    // The effect of this call is simply to ignore anything we've done *outside*
    // the smart allocator after we initialized to avoid attributing shared
    // structure initialization that happens during init_thread_locals() to this
    // session.

    // We don't want to clear the other values because we do already have some
    // sized smart allocator usage and live slabs and wiping now will result in
    // negative values when we try to reconcile our accounting with jemalloc.
#ifdef USE_JEMALLOC
    // Anything that was definitively allocated by the smart allocator should
    // be counted in this number even if we're otherwise zeroing out the count
    // for each thread.
    m_stats.totalAlloc = s_statsEnabled ? m_stats.jemallocDebt : 0;

    // Ignore any attempt to sync jemalloc stats before this point since if we
    // do before this it is impossible to tell what the former usage was before
    // the sync.
    assert(!m_enableStatsSync);
    m_enableStatsSync = s_statsEnabled;
#else
    m_stats.totalAlloc = 0;
#endif
  }
#ifdef USE_JEMALLOC
  if (s_statsEnabled) {
    m_stats.jemallocDebt = 0;
    m_prevDeallocated = *m_deallocated;
    m_prevAllocated = *m_allocated;
  }
#endif
#ifdef USE_JEMALLOC
  FTRACE(1, "resetStatsImpl({}) post:\n", isInternalCall);
  FTRACE(1, "usage: {}\nalloc: {}\npeak usage: {}\npeak alloc: {}\n",
    m_stats.usage, m_stats.alloc, m_stats.peakUsage, m_stats.peakAlloc);
  FTRACE(1, "total alloc: {}\nje alloc: {}\nje dealloc: {}\n",
    m_stats.totalAlloc, m_prevAllocated, m_prevDeallocated);
  FTRACE(1, "je debt: {}\n\n", m_stats.jemallocDebt);
#else
  FTRACE(1, "resetStatsImpl({}) post:\n"
    "usage: {}\nalloc: {}\npeak usage: {}\npeak alloc: {}\n\n",
    isInternalCall,
    m_stats.usage, m_stats.alloc, m_stats.peakUsage, m_stats.peakAlloc);
#endif
}
Ejemplo n.º 30
0
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CRequestManager::ExecuteStartJobRequestL()
	{
	FLOG(_L("[IMAGEPRINTUI]<<< CRequestManager, ExecuteStartJobRequestL "));
	iStart.iReqParam.Reset();
	            
    TUint layout = iCapabilityManager->Layout();
	TUint quality = iCapabilityManager->Quality();
	TUint paperSize = iCapabilityManager->PaperSize();
    
    //fill request parameter by retrieved values
    TDpsArgsInt req_quality,req_papersize, req_layout;
    
    req_quality.iElement = EDpsArgQuality;
    req_quality.iContent = quality;
    iStart.iReqParam.iJobConfig.AppendL(req_quality);
    
    req_papersize.iElement = EDpsArgPaperSize;
    req_papersize.iContent = paperSize;
    iStart.iReqParam.iJobConfig.AppendL(req_papersize);

    req_layout.iElement = EDpsArgLayout;
    req_layout.iContent = layout;
    iStart.iReqParam.iJobConfig.AppendL(req_layout);
              
   
    // retrieve  images
    FLOG(_L("[IMAGEPRINTUI]<<< CRequestManager, Get Images"));
    iImageArrayFlat = iAppUi->ImagesToPrint(); 	// not taking ownership
    
   
    iNumberOfImages = iImageArrayFlat->Count();
	TDpsPrintInfo* helpTDpsPrintInfo = new (ELeave) TDpsPrintInfo[iNumberOfImages];
	CleanupStack::PushL(helpTDpsPrintInfo);          
    // go through the list of images and add info for start job request 
    
    for(int i=0; i<iNumberOfImages; i++)
    	{
    	FLOG(_L("[IMAGEPRINTUI]<<< CRequestManager, Start job, append image"));
    	// check if file really exist, use may have delete it after choose print
    	// that will set printer unstable state
    	iFileExist = ConeUtils::FileExists(iImageArrayFlat->operator[](i));
    	if(iFileExist)
    		{
    		FLOG(_L("[IMAGEPRINTUI]<<< CRequestManager, Start job, file exist"));
    		helpTDpsPrintInfo[i].iFile.Copy(iImageArrayFlat->operator[](i));
        	iStart.iReqParam.iPrintInfo.AppendL(helpTDpsPrintInfo[i]);
    		}
    	}
    
    FTRACE(FPrint(_L("[IMAGEPRINTUI]\t CRequestManager iNumberOfImages  is %d"), iNumberOfImages));	        
    iAppUi->NumberOfImages(iNumberOfImages); 
    if(!iFileExist)
    	{
    	FLOG(_L("[IMAGEPRINTUI]>>> CRequestManager, ExecuteStartJobRequestL, file not exist "));
        iAppUi->Notes()->ShowErrorMsgL(R_ERROR_FILE_NOT_FOUND); 
    	}
    else
    	{
    	iDpsEngine->DoDpsRequestL(&iStart, iStatus);
    	}
    
	CleanupStack::PopAndDestroy(helpTDpsPrintInfo);

	
	FLOG(_L("[IMAGEPRINTUI]>>> CRequestManager, ExecuteStartJobRequestL "));
	}