Skip to content

Exception handling support to MINIMAL_RUNTIME#8040

Merged
juj merged 15 commits into
emscripten-core:incomingfrom
juj:optimize_exception_handling
Jun 8, 2019
Merged

Exception handling support to MINIMAL_RUNTIME#8040
juj merged 15 commits into
emscripten-core:incomingfrom
juj:optimize_exception_handling

Conversation

@juj

@juj juj commented Feb 8, 2019

Copy link
Copy Markdown
Collaborator

This refactors exception handling support to its own file, breaks up the $EXCEPTIONS object, and adds exceptions support to MINIMAL_RUNTIME.

Also adds tracking that the new library_exceptions.js file will only be linked in if -fno-exceptions is not used. However -fno-exceptions is traditionally a compiler flag and not a linker flag, so at link time one should also issue -fno-exceptions to enforce no exceptions getting in, or add the link time flag -s SUPPORT_CPP_THROW_EXCEPTION=0 explicitly. This new flag is convenient when one is linking in multiple libraries and doesn't know if one of them used exceptions or not - with -s SUPPORT_CPP_THROW_EXCEPTION=0 the whole library_exceptions.js file will be omitted, giving immediate linker errors for any exceptions related symbols.

@juj juj added the code size label Feb 8, 2019
@juj
juj force-pushed the optimize_exception_handling branch from dff80e3 to 0e9deb2 Compare February 8, 2019 18:35
@kripken

kripken commented Feb 9, 2019

Copy link
Copy Markdown
Member

Flag-wise, we already have a link-time flag called DISABLE_EXCEPTION_CATCHING (on by default). So users that want exceptions have been turning it off already.

Comment thread src/modules.js Outdated
Comment thread src/parseTools.js

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good, but I think we should use the existing DISABLE_EXCEPTION_CATCHING flag as mentioned, and another minor comment or too.

If we do want another link-time flag, I'm not opposed to that, but tying it to this PR seems risky.

@juj
juj force-pushed the optimize_exception_handling branch from 0e9deb2 to 497b65a Compare February 9, 2019 13:45
@juj

juj commented Feb 9, 2019

Copy link
Copy Markdown
Collaborator Author

Looks mostly good, but I think we should use the existing DISABLE_EXCEPTION_CATCHING flag as mentioned, and another minor comment or too.

The use of DISABLE_EXCEPTION_CATCHING and SUPPORT_CPP_THROW_EXCEPTION are different, and both can have their uses. If I use DISABLE_EXCEPTION_CATCHING=1, it means that throws are supported and compile ok, but they'll fall through to top level scope at runtime, and you won't know at compile/link time if any code actually did use exceptions, whereas SUPPORT_CPP_THROW_EXCEPTION=0 means that we issue an error at link time if any of the C++ exception machinery is used anywhere in the linked program. SUPPORT_CPP_THROW_EXCEPTION=0 could be renamed to a new mode DISABLE_EXCEPTION_CATCHING=3, but that might be a bit more confusing, since it already uses the =1 or =2 setting for "all/select functions only" options.

If I have
a.cpp

int main()
{
	throw 4;
}

and I do

$ em++ a.cpp -o a.bc
$ em++ a.bc -o a.html -fno-exceptions

the above code will compile and run, and the throw will be there, printing

exception thrown: 5247224 - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.

Imagine the two lines may be coming from different stages of the build system, or a.bc might be code that was checked in to a precompiled code repository. After this PR, -fno-exceptions gets also turned into a link flag, and the above code prints

$ em++ a.cpp -o a.bc
$ em++ a.bc -o a.html -fno-exceptions
error: undefined symbol: __cxa_allocate_exception
warning: To disable errors for undefined symbols use `-s ERROR_ON_UNDEFINED_SYMBOLS=0`
error: undefined symbol: __cxa_throw
Error: Aborting compilation due to previous errors

I overloaded this setting onto -fno-exceptions for convenience and to be "idiomatic". The same feature can/could be accessed just with

$ em++ a.bc -o a.html -s SUPPORT_CPP_THROW_EXCEPTION=0
error: undefined symbol: __cxa_allocate_exception
warning: To disable errors for undefined symbols use `-s ERROR_ON_UNDEFINED_SYMBOLS=0`
error: undefined symbol: __cxa_throw
Error: Aborting compilation due to previous errors

to be explicit.

@juj
juj force-pushed the optimize_exception_handling branch from 497b65a to 45ca734 Compare February 9, 2019 14:16
@juj
juj force-pushed the optimize_exception_handling branch 7 times, most recently from 70324cd to a3e62b3 Compare February 10, 2019 16:52
@kripken

kripken commented Feb 11, 2019

Copy link
Copy Markdown
Member

I see the difference between the flags now. But I don't see the benefit. Can the old flag not reduce the throw into an abort(), and then they are basically the same?

@juj

juj commented Feb 11, 2019

Copy link
Copy Markdown
Collaborator Author

The old -s DISABLE_EXCEPTION_CATCHING=1 flag still allows one to write a global JS window.onerror() handler where one decodes std::exception::what() to a human readable string for debugging or similar. One example here: https://stackoverflow.com/a/33743704 . So being able to throw C++ exceptions can still be useful. If we turned -s DISABLE_EXCEPTION_CATCHING=1 into a throw 'abort';, they'll lose that benefit.

@kripken

kripken commented Feb 12, 2019

Copy link
Copy Markdown
Member

I see, thanks.

It seems more natural to have DISABLE_EXCEPTION_THROWING to parallel the _CATCHING flag, then. But the argument that there's an existing compiler flag also has merit. I don't have a strong intuition here, maybe best to ask people with more experience in the matter - @sbc100 @dschuff, thoughts?

@juj

juj commented Feb 12, 2019

Copy link
Copy Markdown
Collaborator Author

The PR does latch this onto -fno-exceptions, but the reason why there's also a dedicated option is that I don't know if some build systems might not let one specify -fno-exceptions at link time, in which case a dedicated option would be useful. (also that -s setting is internally used to route the information from frontend to linker, so making it publicly visible was trivial, nicer than marking it internal)

@sbc100 sbc100 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like splitting the exceptions library stuff into its own file and disabling it completely when exceptions are not requested.

Is it worth being more consistent about forcing using to opt into exception handling though? IIUC, at the moment we disable exceptions by default at compile time but allow them by default at link time?

Comment thread src/modules.js Outdated
Comment thread tests/test_exception.cpp
Comment thread tests/test_core.py Outdated
Comment thread tests/test_other.py Outdated
(['-O2'], 31, [], ['waka'], 226582, 20, 33, 566), # noqa
(['-O2', '-s', 'EMULATED_FUNCTION_POINTERS=1'],
32, [], ['waka'], 226582, 20, 32, 565), # noqa
31, [], ['waka'], 226582, 20, 33, 566), # noqa

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like two functions moved from JS exported functions to native functions here.. At least for wasm backend. Does this make sense? Do you know which functions moved?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know the details on what changed here, this was done blind against the CI. Updated the code a little, not sure what CI says on this now. The major change is the unpacking of the $EXCEPTIONS object, so more functions are now tracked as JS functions; and the deps info mechanism was revised, not sure which function it is pulling in to these (if I had to guess, it'd be free()) I don't have upstream wasm LLVM installed at the moment to verify though.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good: what happened is we have _ZSt18uncaught_exceptionv in library.js, as a hack. With the more proper deps here, we get it from the system library. So an import is replaced by an export.

We should remove _ZSt18uncaught_exceptionv from the new src/library_exceptions.js file, but can leave that to a followup.

Comment thread src/library.js

terminate: '__cxa_call_unexpected',

__gxx_personality_v0__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where these deps simply wrong?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were taking a shortcut, i.e. __gxx_personality_v0 depended on something that depended on something that eventually depended on these. I don't remember any more what the exact chain here was to start with, but adjusted to be more precise.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, now I remember. This is due to limitation of deps tracking, a JS function can't depend on a C++ function, then depend back on JS function, and again to C++ function. This seems to be due to JS->C++ function dependencies being resolved once first, and then all JS->JS dependencies second. This moved over to deps_info.json to help around that limitation. (it can safely be added to both, put it back here to be clearer)

@sbc100

sbc100 commented Feb 12, 2019

Copy link
Copy Markdown
Collaborator

Just to be clear, if the symbol dependency system works correctly the exception handling library function will only be included if they are used by the program?

And this new link time flag lets you know if any of the code you are linking references any of these symbol, because it generated a link error in such cases.

And we are not changing the emscipten default here which is to allow exception thrown at link time by default but disallow throwing at compile time default. Seems reasonable, if a little confusing.

@juj

juj commented Feb 12, 2019

Copy link
Copy Markdown
Collaborator Author

Just to be clear, if the symbol dependency system works correctly the exception handling library function will only be included if they are used by the program?

Correct, the deps system does track through when to emit exceptions related code. Although I am not sure if the current deps chain is 100% accurate. One example is that we have something like this:

  __gxx_personality_v0__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'],
  __gxx_personality_v0: function() {
  },

that is, a no-op function that is marked to depend on exceptions. I am not versed on the details of gxx_personality_v0's semantics, but I notice if I remove those deps, then test suite did not pass, so that is necessary.

And this new link time flag lets you know if any of the code you are linking references any of these symbol, because it generated a link error in such cases.

That's correct.

And we are not changing the emscipten default here which is to allow exception thrown at link time by default but disallow throwing at compile time default. Seems reasonable, if a little confusing.

That is right. It is important to allow throwing by default, since that will make exceptions raise all the way up, which lets one do a convenient global error handler to track aborts from C++ throws.

@juj
juj force-pushed the optimize_exception_handling branch from 0121fd9 to 7ec178f Compare February 13, 2019 23:01
@juj

juj commented Feb 13, 2019

Copy link
Copy Markdown
Collaborator Author

It seems more natural to have DISABLE_EXCEPTION_THROWING to parallel the _CATCHING flag, then. But the argument that there's an existing compiler flag also has merit. I don't have a strong intuition here, maybe best to ask people with more experience in the matter - @sbc100 @dschuff, thoughts?

Renamed SUPPORT_CPP_THROW_EXCEPTION to DISABLE_EXCEPTION_THROWING, that seems like a more symmetric name.

@juj
juj force-pushed the optimize_exception_handling branch 2 times, most recently from dd32beb to 8f87dd2 Compare February 17, 2019 16:40
@juj
juj force-pushed the optimize_exception_handling branch from 8f87dd2 to 46ac093 Compare February 25, 2019 15:11
@juj
juj force-pushed the optimize_exception_handling branch from 6097aaf to ec02c5c Compare February 26, 2019 13:07
@juj
juj force-pushed the optimize_exception_handling branch from ec02c5c to 62fe6d8 Compare May 25, 2019 12:11
@juj

juj commented May 25, 2019

Copy link
Copy Markdown
Collaborator Author

Rebased this PR to latest, not sure if this will pass everything, but let's see how things look on the CI.

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

almost lgtm, but I do have questions about all those _1,2,3,.. etc. functions, that worry me.

Comment thread src/deps_info.json
"emscripten_get_preloaded_image_data_from_FILE": ["fileno"],
"__gxx_personality_v0": ["_ZSt18uncaught_exceptionv", "__cxa_find_matching_catch"],
"__cxa_find_matching_catch": ["__cxa_is_pointer_type", "__cxa_can_catch"],
"__cxa_find_matching_catch_0": ["__cxa_is_pointer_type", "__cxa_can_catch"],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are all these 1,2,3 etc. versions needed? and why just 10?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change to explicitly list out __cxa_find_matching_catch_<number> is a change to migrate these functions to be defined as JS library functions (in src/library_exceptions.js ), instead of the symbols being defined ad hoc in JS linker (used to be in src/jsifier.js at

// special logic
). See #8046 that discusses this aspect in more detail.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, sounds good - if we have a nice error if we need more than 10. What would happen in that case?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there are more than 10, there will likely be an undefined symbol error, since the linker cannot find a function that LLVM emitted a dependence to

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry that's not super-clear, but if it comes up we can look into improving it I guess.

___cxa_decrement_exception_refcount
___cxa_end_catch
___cxa_find_matching_catch
___cxa_find_matching_catch_0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are all these added?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't really know how the JS exceptions machinery works in dynamic linking across main module and side module - before this PR, what would happen if a side module throws an exception with say 3 arguments, but main module did not have a need to generate ___cxa_find_matching_catch_3 in code?

That is, superficially I get an impression that main module would need to emit all these variants to prepare for all possibilities in the side module?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I think exceptions probably did not work in all cases in dynamic linking, we only have very basic testing for that.

@juj
juj force-pushed the optimize_exception_handling branch from 62fe6d8 to 2700a46 Compare June 1, 2019 10:02
___cxa_find_matching_catch_29
___cxa_find_matching_catch_3
___cxa_find_matching_catch_30
___cxa_find_matching_catch_31

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this go up to 31, if the limit is 10?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to update the test - earlier I had it go up to 31, but that was excessive. Updated.

@kripken

kripken commented Jun 3, 2019

Copy link
Copy Markdown
Member

lgtm with those last comments addressed.

@juj
juj force-pushed the optimize_exception_handling branch from a50025b to 65fd1e1 Compare June 7, 2019 17:56
@juj
juj merged commit bcf60ce into emscripten-core:incoming Jun 8, 2019
belraquib pushed a commit to belraquib/emscripten that referenced this pull request Dec 23, 2020
* Move exceptions test to its own file

* Move exception handling support to its own JS library file, and optimize it by tearing away the  object that does not Closure well

* Add exceptions support to MINIMAL_RUNTIME.

* More exception deps fixes, also fix emscripten-core#8046

* Fix tests

* Fix __cxa_find_matching_catch to be found under wasmobj0 build mode

* Update review comments

* Rename SUPPORT_CPP_THROW_EXCEPTION to DISABLE_EXCEPTION_THROWING

* Fix test_emcc_s_typo

* Fix tests

* Fix JS-JS dependency to uncaught_exception from cxa_begin_catch

* Limit __cxa_find_matching_catch to 10 arguments, and update tests

* Fix wasm metadce test

* Update tests

* Update tests
sbc100 added a commit that referenced this pull request Jan 24, 2023
It looks like this was originally added back in #8040, but was likely
not needed.

At least in of the of three remaining places this function is used
we don't need to go via the `Module` object and these symbols can/should
simply be referenced via the current scope.

Split out from #18540
sbc100 added a commit that referenced this pull request Jan 24, 2023
It looks like this was originally added back in #8040, but was likely
not needed.

At least in each the of three remaining places this function is used
we don't need to go via the `Module` object and these symbols can/should
simply be referenced via the current scope.

Split out from #18540
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants