You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If a method is inlined into a compiler-generated wrapper, calls to recover from that method will return nil even if they should return a panic value. This is only possible with mid-stack inlining enabled because otherwise the call to recover inhibits inlining. For example:
When built without -l=4, the deferred method successfully recovers the panic and the program exits silently. When built with -l=4, T.M gets inlined into the generated wrapper (*T).M. However, since (*T).M is marked with the WRAPPER flag, obj generates prologue code that adjusts the gp.panic.argp to point to where the arguments would be for a call from the wrapper. But because T.M was inlined, it doesn't make that call and recover sees the wrapper's own argp, which doesn't match the warpper-adjusted gp.panic.argp. Hence, recover thinks it was called from the wrong frame and returns nil.
@cherrymui and I discovered this while trying to tease apart the wrapper prologue. We thought of a few possible fixes. One approach would be to teach wrappers and inlining about each other. Inlining could be disabled inside a wrapper. Or, if inlining happens, it could clear the wrapper flag. Another approach would be to handle this in the runtime. The compiler could record the WRAPPER flag in the runtime's funcInfo and runtime.gorecover could walk the stack to find the top-most wrapper's argp (there could be more than one in a row because of reflection). While this makes recover somewhat more complex, it would let us eliminate hundreds of lines of hand-written Progs from all of the obj backends, reducing overall complexity and simpifying a very subtle interaction. It also wouldn't hurt the performance of typical recover uses.
If a method is inlined into a compiler-generated wrapper, calls to
recover
from that method will return nil even if they should return a panic value. This is only possible with mid-stack inlining enabled because otherwise the call torecover
inhibits inlining. For example:When built without
-l=4
, the deferred method successfully recovers the panic and the program exits silently. When built with-l=4
,T.M
gets inlined into the generated wrapper(*T).M
. However, since(*T).M
is marked with the WRAPPER flag, obj generates prologue code that adjusts thegp.panic.argp
to point to where the arguments would be for a call from the wrapper. But becauseT.M
was inlined, it doesn't make that call andrecover
sees the wrapper's own argp, which doesn't match the warpper-adjustedgp.panic.argp
. Hence,recover
thinks it was called from the wrong frame and returns nil.@cherrymui and I discovered this while trying to tease apart the wrapper prologue. We thought of a few possible fixes. One approach would be to teach wrappers and inlining about each other. Inlining could be disabled inside a wrapper. Or, if inlining happens, it could clear the wrapper flag. Another approach would be to handle this in the runtime. The compiler could record the WRAPPER flag in the runtime's
funcInfo
andruntime.gorecover
could walk the stack to find the top-most wrapper's argp (there could be more than one in a row because of reflection). While this makes recover somewhat more complex, it would let us eliminate hundreds of lines of hand-written Progs from all of the obj backends, reducing overall complexity and simpifying a very subtle interaction. It also wouldn't hurt the performance of typical recover uses./cc @randall77 @rsc
The text was updated successfully, but these errors were encountered: