Skip to content

[EH] Remove !rethrown check when destroying exception - #28012

Open
aheejin wants to merge 3 commits into
mainfrom
remove_rethrown
Open

aheejin wants to merge 3 commits into
mainfrom
remove_rethrown

Conversation

@aheejin

@aheejin aheejin commented Oct 19, 2026

Copy link
Copy Markdown
Member

In Emscripten EH, #25988 changed where we increment refcount to fix a refcounting inconsistency between Emscripten vs. Wasm EH (#17115). Before #25988, it was incremented in __cxa_begin_catch, but after
#25988, it was in __cxa_throw and __cxa_rethrow, more inline with
Wasm EH.

__cxa_end_catch calls __cxa_decrement_exception_refcount, which decrements the refcount and destroys/frees the exception. But when an exception is rethrown, we should not destroy it in __cxa_end_catch. The below illutrates how a rethrown exception is (correctly) not destroyed in __cxa_end_catch:

// Before #25988 (refcount incremented in __cxa_begin_catch):
try {
  throw MyEx(); // __cxa_throw:       refcount = 0, rethrown = false
} catch (...) { // __cxa_begin_catch: refcount = 1 (0 -> 1), rethrown = false
  throw;        // __cxa_rethrow:     refcount = 1 (unchanged), rethrown = true
}               // __cxa_end_catch:   refcount = 0 (1 -> 0); not destroyed/freed because rethrown == true

// After #25988 (refcount incremented in __cxa_throw and __cxa_rethrow):
try {
  throw MyEx(); // __cxa_throw:       refcount = 1 (0 -> 1)
} catch (...) { // __cxa_begin_catch: refcount = 1 (unchanged)
  throw;        // __cxa_rethrow:     refcount = 2 (1 -> 2)
}               // __cxa_end_catch:   refcount = 1 (2 -> 1); not destroyed/freed because refcount > 0

So after #25988 we were supposed to solely rely on the refcount to determine whether to destroy an exception. But the !exception_header->rethrown check was incorrectly still left in __cxa_decrement_exception_refcount. This didn't affect C++-only programs, but this prevented an exception from being destroyed when an exception was rethrown to JS and decrementExceptionRefcount was called from there, as in the test added here.

This removes the incorrect rethrown flag check. After this and #26518, no code uses the rethrown field, so this also removes it and its related methods.

This is a part of fixes for #27705.

@aheejin
aheejin requested a review from sbc100 October 19, 2026 13:36
In Emscripten EH, #25988 changed where we increment refcount to fix a
refcounting inconsistency between Emscripten vs. Wasm EH (#17115).
Before #25988, it was incremented in `__cxa_begin_catch`, but after
 #25988, it was in `__cxa_throw` and `__cxa_rethrow`, more inline with
Wasm EH.

`__cxa_end_catch` calls `__cxa_decrement_exception_refcount`, which
decrements the refcount and destroys/frees the exception. But when an
exception is rethrown, we should not destroy it in `__cxa_end_catch`.
The below illutrates how a rethrown exception is (correctly) not
destroyed in `__cxa_end_catch`:
```cpp
// Before #25988 (refcount incremented in __cxa_begin_catch):
try {
  throw MyEx(); // __cxa_throw:       refcount = 0, rethrown = false
} catch (...) { // __cxa_begin_catch: refcount = 1 (0 -> 1), rethrown = false
  throw;        // __cxa_rethrow:     refcount = 1 (unchanged), rethrown = true
}               // __cxa_end_catch:   refcount = 0 (1 -> 0); not destroyed/freed because rethrown == true

// After #25988 (refcount incremented in __cxa_throw and __cxa_rethrow):
try {
  throw MyEx(); // __cxa_throw:       refcount = 1 (0 -> 1)
} catch (...) { // __cxa_begin_catch: refcount = 1 (unchanged)
  throw;        // __cxa_rethrow:     refcount = 2 (1 -> 2)
}               // __cxa_end_catch:   refcount = 1 (2 -> 1); not destroyed/freed because refcount > 0
```

So after #25988 we were supposed to solely rely on the refcount to
determine whether to destroy an exception. But the
`!exception_header->rethrown` check was incorrectly still left in
`__cxa_decrement_exception_refcount`. This didn't affect C++-only
programs, but this prevented an exception from being destroyed when an
exception was rethrown to JS and `decrementExceptionRefcount` was called
from there, as in the test added here.

This removes the incorrect `rethrown` flag check. After this and #26518,
no code uses the `rethrown` field, so this also removes it and its
related methods.

This is a part of fixes for #27705.
This is an automatic change generated by tools/maint/rebaseline_tests.py.

The following (2) test expectation files were updated by
running the tests with `--rebaseline`:

```
codesize/test_codesize_cxx_except.json: 199227 => 199193 [-34 bytes / -0.02%]
codesize/test_codesize_cxx_mangle.json: 265550 => 265516 [-34 bytes / -0.01%]

Average change: -0.01% (-0.02% - -0.01%)
```

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant