Skip to content

Part two of dynCall removal #12059

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

Merged
merged 3 commits into from
Aug 31, 2020
Merged

Part two of dynCall removal #12059

merged 3 commits into from
Aug 31, 2020

Conversation

sbc100
Copy link
Collaborator

@sbc100 sbc100 commented Aug 26, 2020

This change now passes flags to binaryen to limit the creation
of the dynCall functions and removes more internal use of the
legacy dynCall functions.

See #12002

@sbc100 sbc100 requested a review from kripken August 26, 2020 22:38
"a.wasm.gz": 6935,
"total": 16029,
"total_gz": 9552
"a.js": 4651,
Copy link
Member

Choose a reason for hiding this comment

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

what causes the increase here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think this is the JS side code in dynCall and makeDynCaller. The wasm gets smaller in proportion to the number of signatures and the JS grows by a fixed amount I think.

In summary:

  1. Small JS size increase.
  2. Only paid by programs that use dynCalls.
  3. Should be removable once we can switch 100% to table calls.

Copy link
Collaborator

Choose a reason for hiding this comment

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

-1 for this change: the old code is both smaller and more performance, as it avoids dynamic signature binding. I think we should switch to this only after we have step 3. from above in-place in this same PR. That way we will not have to accept a regression on a promise of things improving back to old form later on.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK that makes sense. I'll keep going with this until its all done rather then try to land this incremental change.

@@ -340,6 +340,7 @@ mergeInto(LibraryManager.library, {
});
},

emscripten_scan_registers__deps: ['$getDynCaller'],
Copy link
Member

Choose a reason for hiding this comment

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

Why is this dep added? I don't see the function used in the body. I'd guess it's expanded from {{{ makeDynCall }}}? It seems confusing to not see the function in the dep actually used, and could make us forget deps later on. Could the one in the body be changed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, any function with a makeDynCall need to depend on getDynCaller for now.

getDynCaller then selects between the legacy native thunks and the direct table access. Once we can remove the legacy thunks completely we can look at making this more efficient.

@@ -2656,6 +2656,7 @@ var LibraryJSEvents = {
cancelAnimationFrame(id);
},

emscripten_request_animation_frame_loop__deps: ['$getDynCaller'],
Copy link
Collaborator

Choose a reason for hiding this comment

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

This kind of deps mechanism is a bit awkward since it leaks the need to know internal names from makeDynCall out to the JS code. Can we align the naming so that one would type the same string into the __deps field that one puts inside the {{{ }}} block. That way there is at least a visual hint to suggest where the dependency comes from. (otherwise it can be hard to discover)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I manged to remove the need for these.

@sbc100 sbc100 force-pushed the dyn_call_part2 branch 5 times, most recently from 5a7eb1e to 122c607 Compare August 27, 2020 15:22
@sbc100
Copy link
Collaborator Author

sbc100 commented Aug 27, 2020

OK I went all the way to managed to get a codeside win for both wasm and js sides!

We still have three different cases where we need to use the legacy system:

  • WASM2C - needs to be modified not to rely on the native dynCall_xx functions
  • ASYNCIFY - seems to depend on the dynCall entry points being asyncified whereas perhaps the table entries are not? Is not clear to me yet.

Both of these cases needs to be investigated and fixed so that we can completely remove the non-i64 dynCalls from binaryen.

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Nice progress!

}
return Module['dynCall_' + sig].call(null, ptr);
},
$dynCall__deps: ['$dynCallLegacy'],
Copy link
Member

Choose a reason for hiding this comment

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

it's odd to have the deps not near the function - why is this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Because the dependency is conditional in the exact same way the this functions existence is conditions.

Copy link
Member

Choose a reason for hiding this comment

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

I guess I don't feel strongly but I believe this is the one place in the entire codebase not to have the two adjacent. Maybe it's worth the benefit though.

@kripken
Copy link
Member

kripken commented Aug 27, 2020

And good idea about the internal API change, yes, that's a bunch of work to update all the places as you've done, but I think it's the right thing.

@sbc100
Copy link
Collaborator Author

sbc100 commented Aug 27, 2020

And good idea about the internal API change, yes, that's a bunch of work to update all the places as you've done, but I think it's the right thing.

I manged to make vim do it for me after doing about 10 by hand: :%s/makeDynCall('\(.*\)') }}}(\([^,)]*\), /makeDynCall('\1', '\2') }}}(/c

@sbc100
Copy link
Collaborator Author

sbc100 commented Aug 27, 2020

Add two bugs for followup on ASYNCIFY and WASM2C

@sbc100 sbc100 force-pushed the dyn_call_part2 branch 2 times, most recently from 0ccdcbc to c60a031 Compare August 28, 2020 08:53
kripken added a commit that referenced this pull request Aug 28, 2020
Instead, this calls the Table directly.

This takes a little more effort at compile time because we need to find
the index of the function type for the signature we are calling with
(so that we trap if the target function has the wrong type). To do that,
scan the wasm2c output - which is at risk of breaking if that output
changes, sadly (another option might be to disassemble the wasm
and scan that, which would at least be a stable format - but this
seems fine for now).

Helps #12059

Fixes #12065
Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

lgtm % last comment on always including $dynCall.

@@ -90,7 +90,7 @@ def clear(ports, settings, shared):


def process_dependencies(settings):
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$autoResumeAudioContext']
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$autoResumeAudioContext', '$dynCall']
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be safer to always do this for dynCall in all (non-minimal runtime) builds - that is, to do this in emcc.py. That would make this change less noticeable to users.

Separately, removing it from DEFAULT_LIBRARY_FUNCS_TO_INCLUDE later might make sense, but it would be good at that later time to document that in the changelog + make sure it gives a clear error to users if they need to add it manually from then onwards.

@kripken
Copy link
Member

kripken commented Aug 29, 2020

I did some benchmarking of this and see no issues, in fact it may be a slight bit faster.

sbc100 added 2 commits August 31, 2020 10:03
This change now passes flags to binaryen to limit the creation
of the dynCall functions and removes more internal use of the
legacy dynCall functions.

See #12002
kripken added a commit that referenced this pull request Aug 31, 2020
This removes callStackIdToFunc, the map of ids to functions. Instead it
makes us always use the map of ids to function names. That removes one
of the two mechanisms and makes the code simpler, which will help in a
followup PR that adds support for direct table calls, which we need for
#12059.

I think we kept both mechanisms because I was worried about performance,
but I think I was wrong. After debugging for #12059 I think I have a better
understanding of things and it seems fine to use names - it's just a single
JS map lookup for asyncify operation, which is small compared to the
actual asyncify overhead.

Move some code to maybeStopUnwind, with no changes. That code
will be useful in the followup as well.

Also clean up some debugging code to make it more useful.
@sbc100 sbc100 merged commit 953aded into master Aug 31, 2020
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.

3 participants