External Publication
Visit Post

Code bloat caused by lack of nounwind attr on `drop_in_place`

Rust Internals [Unofficial] April 16, 2026
Source

I think it's because rustc emits invoke in the IR, which implies unwinding. LLVM's pass for adding nounwind uses:

/// Helper for NoUnwind inference predicate InstrBreaksAttribute.
static bool InstrBreaksNonThrowing(Instruction &I, const SCCNodeSet &SCCNodes) {
  if (!I.mayThrow(/* IncludePhaseOneUnwind */ true))
    return false;
  if (const auto *CI = dyn_cast<CallInst>(&I)) {
    if (Function *Callee = CI->getCalledFunction()) {
      // I is a may-throw call to a function inside our SCC. This doesn't
      // invalidate our current working assumption that the SCC is no-throw; we
      // just have to scan that other function.
      if (SCCNodes.contains(Callee))
        return false;
    }
  }
  return true;
}

which only allows call (CallInst).

This check is relevant for recursive functions. I happen to have Arc that is a recursive type, so Arc::drop_slow calls drop_in_place which may call Arc::drop_slow again. In this case rustc assuming that drop_slow may unwind makes it emit unwind-requiring invoke instruction when called recursively, which then cements its status as an unwinding function.

I don't know if LLVM checking for CallInst is intentional, or was it meant to be CallBase (which would include InstInvoke)

Discussion in the ATmosphere

Loading comments...