This ensures the panicking code is outlined from borrow_mut for RefCell.
In other words, RefCell::borrow/RefCell::borrow_mut are marked with #[inline], but for best performance I guess, the developer had to extract the panic operation into a dedicated #[cold] #[inline(never)] function, instead of using Result::unwrap or panic! directly.
That made me wonder: why isn't panicking code outlined by default? I mean, panicking path should always be cold, and shouldn't blow up the instruction cache.
I've made a simple comparison on godbolt: using panic! instead of a custom outlined panic wrapper increase my example function assembly by 13 instructions! And I don't see any benefit for this.
Really, I don't understand why inlining is the default behavior. Does someone has an explanation?
Are you asking why this isn't covered by an existing optimization pass, or are you hinting at a possible change in the implementation of panic! itself?
I know neither how panic! is implemented, nor if their is optimization passes after panic! replacement, so I don't have any answer to your question; I'm just asking if this 13 additional assembly instructions are worth it compared to a non-inlined cold function call, as I'm surprised about the comment I've found in the standard library.
So a payload-less call to panic!is cold and inline-never.
Similarly when a payload is provided, panic! delegates to panic_fmt which also appears to be cold and inline-never. However, note that the call to format_args! will be inlined, and that probably explains the code size difference in your godbolt example.
And you can see that if I remove the panic message, it does indeed leave the panic code cold. It's only ever the panic_fmt that's problematic, and that's because the formatting code is inlined, followed by a call to the out-of-line ::core::panicking::panic_fmt.
I don't believe that there's a good way to force the format_args! call to be out-of-line, unfortunately.
I think the comment is leading you slightly astray.
The more important thing that function is doing is not being generic.
You don't want every RefCell<T>::borrow_mut to have its own copy of the panic -- even if it's outlined -- you want one copy of the panicking code, called by all of them.
Right, naively expanding to a closure would break something like
panic!("{}", return 42)
Also, capturing the arguments into a closure ends up doing unnecessary work just moving stuff around into whatever layout and calling convention is chosen for the closure. But that could potentially be improved if such single-local-use closures could have a calling convention of "wherever the captures are in the parent's frame".
I don't think there's any transformation using a closure that will work in all cases, because it makes things inter-procedural, and there are rust analyses that are only intra-procedural.
EDIT: Ok, it seems that's more or less what someone tried to do in #115562, but also taking constness in account. So I guess we just need to wait #115562 to be resolved.