-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Ensure function table indexes are unique #10741
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
Conversation
sbc100
left a 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.
Maybe make separate function for this behavior? getAddressForFunction?
| } | ||
| if (functionsInTableMap[func]) { | ||
| return functionsInTableMap[func]; | ||
| } |
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.
One downside of doing this is that it will mask the current bug we are trying to investigate with @awtcode...
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.
If this fixes a bug, then it's a proper fix for that bug, isn't it? That is, this isn't doing a random thing that might fix a function pointer comparison - it is literally making function pointers properly unique, which should fix bugs.
Maybe I don't see what you mean here?
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.
The problem is that the module in which the bug occurs seem to be confused about how to find the address of a certain function.
For internal (dll-local) functions the compiler just puts the function in the elem section and should never call the fp$ function. For external (non-dll-local) function the compiler should not put the function in the elem section and only call the fp$ function.
This fix will allow such confused modules to silently work.
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, I don't see that as silent. I think this PR changes the model entirely to what should have been the proper model all along: the Table has unique elements. That's more efficient and guaranteed to be correct.
As a bonus, it means we don't need to worry about coordinating things like different ways to add things to the Table. You're right that this might fix a bug in coordinating such things, but it does so by allowing us to remove that code! That's essentially what the corresponding binaryen PR does.
| var freeTableIndexes = []; | ||
|
|
||
| #if WASM_BACKEND | ||
| // Weak map of functions in the table to their indexes, created on first use. |
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.
Why wasm backend only? if this behaviour change is going to be made why of make it apply to fastcomp too?
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.
Well, I only really understand how function pointers work in upstream, after spending the day on that. So this might work on fastcomp, but it's risky - as it's deprecated I figured safest to avoid any changes?
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.
But all this change is going is duplicating the results of adding functions to the wasm table. Surly this will trivially work in both backends and if the test passes.. why not just remove the ifdef. At the very least there is a readability cost to adding more ifdefs.
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.
If you think the risk is worth it I can remove the ifdef, sure.
|
I did some benchmarking to validate that this actually helps (together with the binaryen PR), and doesn't hit some weird slow case in scanning the Table from JS, or in WeakMap handling. On
|
|
Thanks for doing this @kripken :) I can't wait to try it out :) |
tests/interop/test_add_function.cpp
Outdated
| assert(wasmTable.length >= 3); | ||
| assert(addFunction(wasmTable.get(1)) === 1); | ||
| assert(addFunction(wasmTable.get(2)) === 2); | ||
| }, &foo, &bar, &baz); |
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.
Are foo, baz, bar even used here ?
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.
They are not used inside the JS, but sending their addresses into JS ensures that their addresses are taken (in a way LLVM can't optimize out) and therefore that the Table is at least of that size.
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.
Can we do that more explicitly?
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 at least add a comment.
sbc100
left a 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.
Lgtm with comment
This reverts commit e620ae9.
Depends on emscripten-core/emscripten#10741 which ensures that table indexes are unique. With that guarantee, a main module can just add its function pointers into the table, and use them based on that index. The loader will then see them in the table and then give other modules the identical function pointer for a function, ensuring function pointer equality. This avoids calling fp$ functions during startup for the main module's own functions (which are slow). We do still call fp$s of things we import from outside, as we don't have anything to put in the table for them, we depend on the loader for that. I suspect this can also be done with SIDE_MODULES, but did not want to try too much at once.
… pointer indexes in fastcomp because we are doing a matching the on the stubs instead of the wasm function wrapper. In this change, we get the actual function wrapper and pass it into addFunction() so that addFunction can use this to compare with the function wrapper gotten from the wasm table.
… pointer indexes in fastcomp because we are doing a matching the on the stubs instead of the wasm function wrapper. In this change, we get the actual function wrapper and pass it into addFunction() so that addFunction can use this to compare with the function wrapper gotten from the wasm table.
… pointer indexes in fastcomp because we are doing a matching the on the stubs instead of the wasm function wrapper. In this change, we get the actual function wrapper and pass it into addFunction() so that addFunction can use this to compare with the function wrapper gotten from the wasm table.
… pointer indexes in fastcomp because we are doing a matching the on the stubs instead of the wasm function wrapper. In this change, we get the actual function wrapper and pass it into addFunction() so that addFunction can use this to compare with the function wrapper gotten from the wasm table.
This ensures that our table contains unique functions, at least
as far as
addFunctionis concerned: it checks if there is alreadyan index for the function, and if so, returns that index.
This is good for avoiding wasted table indexes, and also is part
of a solution for more efficient MAIN_MODULE function pointer
index handling, which I will open in Binaryen. Basically, that PR
will just let main modules add their own functions to the table,
and not call any
fp$accessors; the risk is that while doingso we need to tell the loader to use those same indexes for
other modules that want function pointers to the same functions.
By always having unique indexes in the table thanks to this PR
we achieve that without any special effort.