When we insert a call to the personality function wrapper
(_Unwind_CallPersonality) for a catch pad, we store some necessary
info in __wasm_lpad_context struct and pass it. One of the info is the
LSDA address for the function. For this, we insert a call to
wasm.lsda(), which will be lowered down to the address of LSDA, and
store it in a field in __wasm_lpad_context.
There are exceptions to this personality call insertion: catchpads for
catch (...) and cleanuppads (for destructors) don't need personality
function calls, because we don't need to figure out whether the current
exception should be caught or not. (They always should.)
There was a little optimization to wasm.lsda() call insertion. Because
the LSDA address is the same throughout a function, we don't need to
insert a store of wasm.lsda() return value in every catchpad. For
example:
try { foo(); } catch (int) { // wasm.lsda() call and a store are inserted here, like, in // pseudocode, // %lsda = wasm.lsda(); // store %lsda to a field in __wasm_lpad_context try { foo(); } catch (int) { // We don't need to insert the wasm.lsda() and store again, because // to arrive here, we have already stored the LSDA address to // __wasm_lpad_context in the outer catch. } }
So the previous algorithm checked if the current catch has a parent EH
pad, we didn't insert a call to wasm.lsda() and its store.
But this was incorrect, because what if the outer catch is catch (...)
or a cleanuppad?
try { foo(); } catch (...) { // wasm.lsda() call and a store are NOT inserted here try { foo(); } catch (int) { // We need wasm.lsda() here! } }
In this case we need to insert wasm.lsda() in the inner catchpad,
because the outer catchpad does not have one.
To minimize the number of inserted wasm.lsda() calls and stores, we
need a way to figure out whether we have encountered wasm.lsda() call
in any of EH pads that dominates the current EH pad. To figure that
out, we now visit EH pads in BFS order in the dominator tree so that we
visit parent BBs first before visiting its child BBs in the domtree.
We keep a set named ExecutedLSDA, which basically means "Do we have
wasm.lsda() either in the current EH pad or any of its parent EH
pads in the dominator tree?". This is to prevent scanning the domtree up
to the root in the worst case every time we examine an EH pad: each EH
pad only needs to examine its immediate parent EH pad.
- If any of its parent EH pads in the domtree has wasm.lsda(), this means we don't need wasm.lsda() in the current EH pad. We also insert the current EH pad in ExecutedLSDA set.
- If none of its parent EH pad has wasm.lsda()
- If the current EH pad is a catch (...) or a cleanuppad, done.
- If the current EH pad is neither a catch (...) nor a cleanuppad, add wasm.lsda() and the store in the current EH pad, and add the current EH pad to ExecutedLSDA set.
Did you want to move this comment too?