Code bloat caused by lack of nounwind attr on `drop_in_place`
Rust Internals [Unofficial]
April 16, 2026
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