Commit 854e9562 authored by Ehud Katz's avatar Ehud Katz
Browse files

[InlineCost] Fix infinite loop in indirect call evaluation

Currently every time we encounter an indirect call of a known function,
we try to evaluate the inline cost of that function. In case of a
recursion, that evaluation never stops.

The solution presented is to evaluate only the indirect call of the
function, while any further indirect calls (of a known function) will be
treated just as direct function calls, which, actually, never tries to
evaluate the call.

Fixes PR35469.

Differential Revision: https://reviews.llvm.org/D69349
parent 7af53d75
Loading
Loading
Loading
Loading
+91 −84
Original line number Diff line number Diff line
@@ -149,6 +149,9 @@ class CallAnalyzer : public InstVisitor<CallAnalyzer, bool> {
  bool HasUninlineableIntrinsic = false;
  bool InitsVargArgs = false;

  /// Attempt to evaluate indirect calls to boost its inline cost.
  bool BoostIndirectCalls;

  /// Number of bytes allocated statically by the callee.
  uint64_t AllocatedSize = 0;
  unsigned NumInstructions = 0;
@@ -295,13 +298,14 @@ public:
               std::function<AssumptionCache &(Function &)> &GetAssumptionCache,
               Optional<function_ref<BlockFrequencyInfo &(Function &)>> &GetBFI,
               ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE,
               Function &Callee, CallBase &Call, const InlineParams &Params)
               Function &Callee, CallBase &Call, const InlineParams &Params,
               bool BoostIndirect = true)
      : TTI(TTI), GetAssumptionCache(GetAssumptionCache), GetBFI(GetBFI),
        PSI(PSI), F(Callee), DL(F.getParent()->getDataLayout()), ORE(ORE),
        CandidateCall(Call), Params(Params), Threshold(Params.DefaultThreshold),
        ComputeFullInlineCost(OptComputeFullInlineCost ||
                              Params.ComputeFullInlineCost || ORE),
        EnableLoadElimination(true) {}
        BoostIndirectCalls(BoostIndirect), EnableLoadElimination(true) {}

  InlineResult analyzeCall(CallBase &Call);

@@ -1239,7 +1243,31 @@ bool CallAnalyzer::visitCallBase(CallBase &Call) {
  if (isa<CallInst>(Call) && cast<CallInst>(Call).cannotDuplicate())
    ContainsNoDuplicateCall = true;

  if (Function *F = Call.getCalledFunction()) {
  Value *Callee = Call.getCalledOperand();
  Function *F = dyn_cast_or_null<Function>(Callee);
  bool IsIndirectCall = !F;
  if (IsIndirectCall) {
    // Check if this happens to be an indirect function call to a known function
    // in this inline context. If not, we've done all we can.
    F = dyn_cast_or_null<Function>(SimplifiedValues.lookup(Callee));
    if (!F) {
      // Pay the price of the argument setup. We account for the average 1
      // instruction per call argument setup here.
      addCost(Call.arg_size() * InlineConstants::InstrCost);

      // Everything other than inline ASM will also have a significant cost
      // merely from making the call.
      if (!isa<InlineAsm>(Callee))
        addCost(InlineConstants::CallPenalty);

      if (!Call.onlyReadsMemory())
        disableLoadElimination();
      return Base::visitCallBase(Call);
    }
  }

  assert(F && "Expected a call to a known function");

  // When we have a concrete function, first try to simplify it directly.
  if (simplifyCallSite(F, Call))
    return true;
@@ -1282,54 +1310,33 @@ bool CallAnalyzer::visitCallBase(CallBase &Call) {
  }

  if (TTI.isLoweredToCall(F)) {
      // We account for the average 1 instruction per call argument setup
      // here.
      addCost(Call.arg_size() * InlineConstants::InstrCost);

      // Everything other than inline ASM will also have a significant cost
      // merely from making the call.
      if (!isa<InlineAsm>(Call.getCalledValue()))
        addCost(InlineConstants::CallPenalty);
    }

    if (!Call.onlyReadsMemory())
      disableLoadElimination();
    return Base::visitCallBase(Call);
  }

  // Otherwise we're in a very special case -- an indirect function call. See
  // if we can be particularly clever about this.
  Value *Callee = Call.getCalledValue();

  // First, pay the price of the argument setup. We account for the average
  // 1 instruction per call argument setup here.
    // We account for the average 1 instruction per call argument setup here.
    addCost(Call.arg_size() * InlineConstants::InstrCost);

  // Next, check if this happens to be an indirect function call to a known
  // function in this inline context. If not, we've done all we can.
  Function *F = dyn_cast_or_null<Function>(SimplifiedValues.lookup(Callee));
  if (!F) {
    if (!Call.onlyReadsMemory())
      disableLoadElimination();
    return Base::visitCallBase(Call);
  }

    // If we have a constant that we are calling as a function, we can peer
    // through it and see the function target. This happens not infrequently
    // during devirtualization and so we want to give it a hefty bonus for
  // inlining, but cap that bonus in the event that inlining wouldn't pan
  // out. Pretend to inline the function, with a custom threshold.
    // inlining, but cap that bonus in the event that inlining wouldn't pan out.
    // Pretend to inline the function, with a custom threshold.
    if (IsIndirectCall && BoostIndirectCalls) {
      auto IndirectCallParams = Params;
  IndirectCallParams.DefaultThreshold = InlineConstants::IndirectCallThreshold;
      IndirectCallParams.DefaultThreshold =
          InlineConstants::IndirectCallThreshold;
      CallAnalyzer CA(TTI, GetAssumptionCache, GetBFI, PSI, ORE, *F, Call,
                  IndirectCallParams);
                      IndirectCallParams, false);
      if (CA.analyzeCall(Call)) {
        // We were able to inline the indirect call! Subtract the cost from the
        // threshold to get the bonus we want to apply, but don't go below zero.
        Cost -= std::max(0, CA.getThreshold() - CA.getCost());
      } else
        // Otherwise simply add the cost for merely making the call.
        addCost(InlineConstants::CallPenalty);
    } else
      // Otherwise simply add the cost for merely making the call.
      addCost(InlineConstants::CallPenalty);
  }

  if (!F->onlyReadsMemory())
  if (!Call.onlyReadsMemory() || (IsIndirectCall && !F->onlyReadsMemory()))
    disableLoadElimination();
  return Base::visitCallBase(Call);
}
+55 −0
Original line number Diff line number Diff line
; RUN: opt -inline -early-cse < %s
; This test used to crash (PR35469).

define void @func1() {
  %t = bitcast void ()* @func2 to void ()*
  tail call void %t()
  ret void
}

define void @func2() {
  %t = bitcast void ()* @func3 to void ()*
  tail call void %t()
  ret void
}

define void @func3() {
  %t = bitcast void ()* @func4 to void ()*
  tail call void %t()
  ret void
}

define void @func4() {
  br i1 undef, label %left, label %right

left:
  %t = bitcast void ()* @func5 to void ()*
  tail call void %t()
  ret void

right:
  ret void
}

define void @func5() {
  %t = bitcast void ()* @func6 to void ()*
  tail call void %t()
  ret void
}

define void @func6() {
  %t = bitcast void ()* @func2 to void ()*
  tail call void %t()
  ret void
}

define void @func7() {
  %t = bitcast void ()* @func3 to void ()*
  tail call void @func8(void()* %t)
  ret void
}

define void @func8(void()* %f) {
  tail call void %f()
  ret void
}