SpiderMonkey: Guarantee jitcode presence for ALL JSScripts, to simplify and speed up function calls from within jitcode

RESOLVED INACTIVE

Status

()

Core
JavaScript Engine
RESOLVED INACTIVE
5 years ago
2 days ago

People

(Reporter: djvj, Unassigned)

Tracking

Firefox Tracking Flags

(Not tracked)

Details

(Reporter)

Description

5 years ago
I was just speaking with Jeff Muizelaar about optimizing indirect function calls from asm.js, and he had a suggestion that I consider very interesting: to guarantee the existence of _some_ callable jitcode for all scripts.

Currently, we go to some amount of effort to check if a JSScript has jitcode that we can enter.  As of now, there are potentially three different jits that are going to probably be somewhat prominent in the future: ion, baseline, and asm.

As it stands, we'll probably end up writing some logic in the "CallGeneric" code for each of the jits to check for and enter other jits.  We already do this in baseline: our "Call Scripted" optimized code loads either ion or baseline jitcode for the callee and enters it.  This is possible because Baseline adopts Ion's calling convention, so the caller can set up the arguments appropriately and enter either jitcode without having to think about it.

If we assume that we can get all jits to follow a single calling convention, then we can have a single "preferredJitcode" field on a JSScript that points to the native code start address for the appropriate jitcode for that script - whether this be baseline, ion, or asm.

If a function can't be compiled by any of the jits and does not have any real jitcode (this rare, as we expect baseline to handle all scripts), the "preferredJitcode" points to some stubcode that enters the interpreter.

Given this setup, function calls from jitted code can become a blind "load the preferredJitcode & enter it".  This will probably have the most significance for Ion and Asm, since any gains in function-call-overheads will have the most effect there.
I've been meaning to do this for a while, but never had time for it. Asm.js is just the gift that keeps on giving, in the form of all these nice excuses to go work on things :)

Comment 2

5 years ago
Perhaps I'm misunderstanding, but I don't see this helping asm.js:
 - For asm->non-asm calls, I don't see this helping that much.  The current plan is to generate a (per-calling-signature, callee) trampoline (on demand, after the callee gets Ion-compiled) that boxes the caller's arguments onto the stack, pushes an IonFrame, and handles a few other transition details.  With this, there wouldn't really be any dynamic callee-querying overhead.
 - For the *->asm calls, since asm.js uses the system ABI, I'd expect we'd be able to mostly reuse the existing call-to-C++ path.

Now, it would be great if asm->Ion and asm->Baseline got to use the same trampoline logic, but it sounds like that is already the case?
(Reporter)

Comment 3

5 years ago
(In reply to Luke Wagner [:luke] from comment #2)
> Perhaps I'm misunderstanding, but I don't see this helping asm.js:
>  - For asm->non-asm calls, I don't see this helping that much.  The current
> plan is to generate a (per-calling-signature, callee) trampoline (on demand,
> after the callee gets Ion-compiled) that boxes the caller's arguments onto
> the stack, pushes an IonFrame, and handles a few other transition details. 
> With this, there wouldn't really be any dynamic callee-querying overhead.

What's the calling signature constructed from?  Something like (IsConstructing, ArgCount)?

>  - For the *->asm calls, since asm.js uses the system ABI, I'd expect we'd
> be able to mostly reuse the existing call-to-C++ path.

Do you foresee asm.js code calling into other jitcode a lot or is that expected to be a more rare situation?

> Now, it would be great if asm->Ion and asm->Baseline got to use the same
> trampoline logic, but it sounds like that is already the case?

Yeah, this is already the case.

Comment 4

5 years ago
(In reply to Kannan Vijayan [:djvj] from comment #3)
> >  - For asm->non-asm calls, I don't see this helping that much.  The current
> > plan is to generate a (per-calling-signature, callee) trampoline (on demand,
> > after the callee gets Ion-compiled) that boxes the caller's arguments onto
> > the stack, pushes an IonFrame, and handles a few other transition details. 
> > With this, there wouldn't really be any dynamic callee-querying overhead.
> 
> What's the calling signature constructed from?  Something like
> (IsConstructing, ArgCount)?

asm.js can't construct, in v.1 at least :)  The signature is (arguments types, return type), which are statically known at the call site.

> >  - For the *->asm calls, since asm.js uses the system ABI, I'd expect we'd
> > be able to mostly reuse the existing call-to-C++ path.
> 
> Do you foresee asm.js code calling into other jitcode a lot or is that
> expected to be a more rare situation?

It definitely can be hot.  In BananaBread in the browser, atm, I'm seeing ~10% of time in the generic asm->non-asm path (the trampoline boxes the arguments into Values then calls a C++ function that calls Invoke, very slow).  In particular, asm.js needs to call into IM to touch WebGL and some WebGL functions get called 10K or 100K times a frame.

Comment 5

5 years ago
On the subject, though, it would be pretty great if IM functions could be compiled to use the system ABI (args in registers, stack frame is the 1 word return address) when a certain set of preconditions held -- say, all callers pass the correct number of arguments and there is a single identity of the caller JSObject -- when all the necessary information could be looked up in a table keyed on the return pc stored in the stack frame.  This would be true for all the hot asm->non-asm calls Emscripten generates.
(In reply to Luke Wagner [:luke] from comment #5)
> On the subject, though, it would be pretty great if IM functions could be
> compiled to use the system ABI (args in registers, stack frame is the 1 word
> return address) when a certain set of preconditions held -- say, all callers
> pass the correct number of arguments and there is a single identity of the
> caller JSObject -- when all the necessary information could be looked up in
> a table keyed on the return pc stored in the stack frame.  This would be
> true for all the hot asm->non-asm calls Emscripten generates.

I've wondered about this myself, but that "all callers" bit is pretty problematic.  It seems like the IonScripts could be compiled with multiple entry points using different ABIs, with the generic one doing type checks on its boxed arguments and then unboxing into the representation used by the leaner ABI.  It seems silly though to have two separate mechanisms for this, one inside and one outside asm.

Comment 7

5 years ago
(In reply to Brian Hackett (:bhackett) from comment #6)
> It seems like the IonScripts could be compiled with multiple
> entry points using different ABIs, with the generic one doing type checks on
> its boxed arguments and then unboxing into the representation used by the
> leaner ABI.

I was thinking about this; one worrying thought is if an argument isn't used soon after the prologue, then we could run into the situation where we are unboxing the argument into the ABI register only to spill it afterwards.  This might not matter if it turned out that most calls can take the ABI path.

> It seems silly though to have two separate mechanisms for this,
> one inside and one outside asm.

I'm not sure what this second mechanism is, but 'yes', we shouldn't duplicate logic going forward.
(Assignee)

Updated

4 years ago
Assignee: general → nobody

Comment 8

2 days ago
Per policy at https://wiki.mozilla.org/Bug_Triage/Projects/Bug_Handling/Bug_Husbandry#Inactive_Bugs. If this bug is not an enhancement request or a bug not present in a supported release of Firefox, then it may be reopened.
Status: NEW → RESOLVED
Last Resolved: 2 days ago
Resolution: --- → INACTIVE
You need to log in before you can comment on or make changes to this bug.