-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Store exceptions metadata in wasm memory (#11503). #11518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Store exceptions metadata in wasm memory (#11503). #11518
Conversation
Thank you for submitting a pull request! If this is your first PR, make sure to add yourself to AUTHORS. |
Can you explain why this useful/better as part of the PR descrption? Also include |
This fixes #11503. |
In general I think this is a good direction! First step is to get it passing the test suite. I see a bunch of existing failures, many of them seem to be minor things like unresolved symbols that can be easily fixed. The bigger issue is that as I think I mentioned on the other issue, the adjustment/deadjustment is needed for virtual inheritance. There are tests for that so after you fix other things eventually I think you'll end up with a broken test that shows an example of the code that needs that adjustment stuff. |
src/library_exceptions.js
Outdated
// A rethrown exception can reach refcount 0; it must not be discarded | ||
// Its next handler will clear the rethrown flag and addRef it, prior to | ||
// final decRef and destruction here | ||
if (info.refcount === 0 && !info.rethrown) { | ||
if (info.destructor) { | ||
if (info.release_ref() && !info.rethrown) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops.
< info.rethrown
info.get_rethrown()
OK, I will try to sort out tests a bit later. |
src/library_exceptions.js
Outdated
@@ -265,52 +309,53 @@ var LibraryExceptions = { | |||
// functionality boils down to picking a suitable 'catch' block. | |||
// We'll do that here, instead, to keep things simpler. | |||
|
|||
__cxa_find_matching_catch__deps: ['__exception_last', '__exception_infos', '__resumeException'], | |||
__cxa_find_matching_catch__deps: ['__exception_last', 'ExceptionInfo', '__resumeException'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found that this declarations actually had no effect because addCxaCatch() function below did not handle it.
I have reworked exceptions handling to (hopefully) proper way. New entity introduced - CatchInfo which is used as catching context and returned by __cxa_find_matching_catch(). |
Now several tests are failed and seems to be unrelated. No idea how to proceed. |
Looking at the first of the errors, I see
Investigating it, it looks like a bug in fastcomp. While the tests run with exceptions disabled, some JS exceptions code is included somehow, and it wants We'll be removing fastcomp soon, so not sure it makes sense to debug this much. As a workaround, you can ifdef out that code, this.get_exception_ptr = function() {
#if !WASM_BACKEND && DISABLE_EXCEPTION_CATCHING != 1 // work around a fastcomp bug
var isPointer = {{{ exportedAsmFunc('___cxa_is_pointer_type') }}}
(this.get_exception_info().get_type());
if (isPointer) {
return {{{ makeGetValue('this.get_base_ptr()', '0', '*') }}};
}
var adjusted = this.get_adjusted_ptr();
if (adjusted !== 0) return adjusted;
return this.get_base_ptr();
#else
abort("no exceptions support");
#endif
} |
Have you meant |
Hmm, no, I think that code was right? We want to emit that logic if fastcomp (so "not wasm backend") and if exceptions are enabled ("so "disabled != 1). What error do you see? |
Oh, wait, yeah, that's not right... it should be an or with flipped backend. So
|
Could not it be that |
(function from exception library is taken in build without exception support).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work!
First set of review comments. I don't see anything worrying, but I didn't read all the details in each function yet, still trying to properly understand the big picture.
src/library_exceptions.js
Outdated
RETHROWN_OFFSET: Runtime.POINTER_SIZE * 2 + 5, | ||
|
||
// Should be multiple of allocation alignment. | ||
SIZE: alignMemory(Runtime.POINTER_SIZE * 2 + 6, 16) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
16 is the default alignment, so the last parameter is not needed.
src/library_exceptions.js
Outdated
__ExceptionInfoAttrs: { | ||
TYPE_OFFSET: 0, | ||
DESTRUCTOR_OFFSET: Runtime.POINTER_SIZE, | ||
REFCOUNT_OFFSET: Runtime.POINTER_SIZE * 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the refcount take 8 bytes? Based on the next field's offset it seems like it is, but based on the operations on it it seems otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refcount offset is Runtime.POINTER_SIZE * 2
, next field offset is Runtime.POINTER_SIZE * 2 + 4
so it takes 4 bytes. The only question is type field which takes POINTER_SIZE
I do not remember why, but probably because of alignment (pointers indeed should be aligned on pointer size). I will change fields order, it is common practice to sort them in descending order by size to minimize paddings.
src/library_exceptions.js
Outdated
#if EXCEPTION_DEBUG | ||
err('de-adjusted exception ptr ' + adjusted + ' to ' + ptr); | ||
|
||
__ExceptionInfoAttrs: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add a comment here explaining what an ExceptionInfo is in general, and maybe some notes on where it's allocated and freed, etc. - the big picture of ExceptionInfo and CatchInfo and their relationship would be good to describe.
src/library_exceptions.js
Outdated
} | ||
|
||
this.set_refcount = function(refcount) { | ||
{{{ makeSetValue('this.ptr', '___ExceptionInfoAttrs.REFCOUNT_OFFSET', 'refcount', '*') }}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this isn't a pointer, so it should be 'i32'
for the type?
src/library_exceptions.js
Outdated
|
||
this.set_type = function(type) { | ||
{{{ makeSetValue('this.ptr', '___ExceptionInfoAttrs.TYPE_OFFSET', 'type', '*') }}}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add ;
after these assignments.
src/library_exceptions.js
Outdated
{{{ makeSetValue('this.ptr', '___ExceptionInfoAttrs.REFCOUNT_OFFSET', 'prev - 1', 'i32') }}}; | ||
#endif | ||
if (prev === 0) { | ||
err('Exception reference counter underflow'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these 3 lines can be like they were before,
#if ASSERTIONS
assert(prev > 0);
#endif
In general we shouldn't emit logging code in a normal build. In a build with assertions we can assert on things, and if we want logging, that should be behind EXCEPTION_DEBUG
.
src/library_exceptions.js
Outdated
this.get_exception_ptr = function() { | ||
#if WASM_BACKEND || DISABLE_EXCEPTION_CATCHING != 1 // work around a fastcomp bug | ||
var isPointer = {{{ exportedAsmFunc('___cxa_is_pointer_type') }}} | ||
(this.get_exception_info().get_type()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please put the (
on the previous line. (I'm actually unsure what JS ASI does on something like this, but it worries me...)
rethrown: false | ||
}; | ||
var info = new _ExceptionInfo(ptr); | ||
info.init(type, destructor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me why init
is called here but not in other places. Can we add the two parameters to the constructor? Or if there is a fundamental reason, perhaps init
needs a more meaningful name or comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it is first created and initialized, all the other places just wrap the pointer to previously created ExceptionInfo instance. Will add the parameters to the constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or may be leave it as is with some comment? Otherwise it will look like new _ExceptionInfo(ptr, type, destructor);
thus relying purely on constructor side effects which does not look nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, interesting.
How about the constructor has no side effects, and after it we can either create(..)
(instead of init(..)
) to create a new underlying instance, or wrap(..)
to wrap an existing one? That seems like it would be clear.
src/library_exceptions.js
Outdated
#if WASM_BACKEND == 0 | ||
, 'setThrew' | ||
#endif | ||
], | ||
__cxa_end_catch: function() { | ||
// Clear state flag. | ||
_setThrew(0); | ||
if (!___exception_caught.length) { | ||
err("Exceptions stack underflow"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same as earlier - please make this an assertion)
Either I don't think this PR needs to change anything with the stubs one, though, unless I'm missing something? |
Hi @vagran , is there something I can do to help move this forward? Please let me know if some of the last comments were not clear. |
Hi, sorry, I had several weeks vacation. Now I checked it, and seems the only thing left is this one:
I tried to make factory methods in |
I see, thanks @vagran - It could be you're hitting some bug in the JS preprocessor. Anyhow, let's leave that for later refactoring perhaps, I agree the current approach is good enough as a starting point. Please merge in latest master to resolve the conflicts. |
I have merged the master. There was some names refactoring there, so I have discarded changes in this file in the merge, and made same names refactoring in a separate commit to not mess the things up. |
Reading through And looking at the deadjustment code itself, the old code had an array of adjustments, |
The old code messed thrown object pointer and catch context, both were represented by exception pointer. When these entities are separated, increment and decrement methods are never called for adjusted pointer. As I remember, I checked this approach with libcxxabi implementation.
Yes, the pointer can be adjusted only once per catch context. |
I think I see. Then it probably doesn't make sense to split this up, as it's tied together with the splitting up of the two things (thrown pointers and catch contexts), IIUC. Re-reading the old code, yes, that was definitely mixed up and incorrect. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, this looks good I think, thanks @vagran !
I wish we had more tests for this, but we do have quite a bit, so I'm not very worried. Also I just verified this happens to fix #11776 which was filed recently, which is a very good sign.
The code style is not 100% consistent with the rest of our code, but we can improve that separately, and also this is something we hope to rewrite eventually in C probably.
I don't think this has any interactions with the new wasm backend exceptions support, but just to make sure, ping @aheejin before landing.
All files changed here are not used for wasm exception handling, so I don't think it will interact with it. |
Thanks @aheejin , landing. |
This patch is accepted in our private fork we use in our project and it seems to be working fine.
(I have switched to my private account).