Investigate why apply(this, arguments) is not inline in some cases

ASSIGNED
Assigned to

Status

()

Core
JavaScript Engine: JIT
P5
normal
ASSIGNED
3 years ago
a year ago

People

(Reporter: caiolima, Assigned: caiolima)

Tracking

Trunk
Points:
---

Firefox Tracking Flags

(Not tracked)

Details

Attachments

(3 attachments, 1 obsolete attachment)

(Assignee)

Description

3 years ago
Created attachment 8451146 [details]
apply_arguments_inline.js

Running the attattched script, Its is possible to notice that the ret_argumentsLength.apply(this, arguments); is not inlined, and it should be.
(Assignee)

Comment 1

3 years ago
Created attachment 8451164 [details]
func01-pass00-BuildSSA-mir.gv.png

This is the IonGraph of the script uploaded before.
(Assignee)

Comment 2

3 years ago
Created attachment 8451166 [details]
func02-pass00-BuildSSA-mir.gv.png

Fot the script [1], the ret_argumentsLength.apply(this, arguments) is being inlined as you can see on this IonGraph.

[1] - https://pastebin.mozilla.org/5518696
(Assignee)

Comment 3

3 years ago
I've started the investigation, and I've noticed that on script [1], the inlining is happening because of the numbers of iterations. I've tested it with the same number of iteration of [2] and there is no inline.
So, What I need to investigate now is about the rules for inlining and compilation polices. Is there any way(documentation, discussion thread or code pointing) to verify and investigate them? 

I've found the the inlining is being done here [3].
 
[1] - https://pastebin.mozilla.org/5518696
[2] - https://bugzilla.mozilla.org/attachment.cgi?id=8451146
[3] - http://dxr.mozilla.org/mozilla-central/source/js/src/jit/IonBuilder.cpp#5025
(In reply to Caio Lima(:caiolima) from comment #3)
> I've started the investigation, and I've noticed that on script [1], the
> inlining is happening because of the numbers of iterations. I've tested it
> with the same number of iteration of [2] and there is no inline.

I am not sure to understand what you mean.

Also, when you comment in a bug, it is better to make an attachment or just copy the code in the bug comment (when small) than doing a pastebin, as the pastebin might expire one day.

> So, What I need to investigate now is about the rules for inlining and
> compilation polices. Is there any way(documentation, discussion thread or
> code pointing) to verify and investigate them? 

IONFLAGS=inline ?
Otherwise needinfo h4writer or djvj.
(Assignee)

Comment 5

3 years ago
Created attachment 8452305 [details]
func02-pass00-BuildSSA-mir.gv.png
Attachment #8451166 - Attachment is obsolete: true
(Assignee)

Comment 6

3 years ago
(In reply to Nicolas B. Pierron [:nbp] from comment #4)
> (In reply to Caio Lima(:caiolima) from comment #3)
> > I've started the investigation, and I've noticed that on script [1], the
> > inlining is happening because of the numbers of iterations. I've tested it
> > with the same number of iteration of [2] and there is no inline.
> 
> I am not sure to understand what you mean.
What I was trying to mean is that, for this script test:

js> function ret_argumentsLength() { return arguments.length; }
js> function rinline_arguments_length_3(i){return ret_argumentsLength.apply(this, arguments)}
js> for (var i = 0; i < 10000; i++) rinline_arguments_length_3(i,0,1,2)    

The number of iterations(in this case, 10000) is the reason for the rinline_arguments_length_3 and ret_argumentsLength being inlined. We can see it in the IonGraph[1]. To validate this assumption, I've run the script above with a small number of iteractions (100, to be more precise).
 
> > So, What I need to investigate now is about the rules for inlining and
> > compilation polices. Is there any way(documentation, discussion thread or
> > code pointing) to verify and investigate them? 
> 
> IONFLAGS=inline ?
> Otherwise needinfo h4writer or djvj.

The |IONFLAGS=inline| shows what is being inlined, but not why and when it should be inlined. djvj, could you help me understand about these heuristcs? 

[1] - https://bugzilla.mozilla.org/attachment.cgi?id=8452305
Flags: needinfo?(kvijayan)
(In reply to Caio Lima(:caiolima) from comment #6)
> (In reply to Nicolas B. Pierron [:nbp] from comment #4)
> > (In reply to Caio Lima(:caiolima) from comment #3)
> > > I've started the investigation, and I've noticed that on script [1], the
> > > inlining is happening because of the numbers of iterations. I've tested it
> > > with the same number of iteration of [2] and there is no inline.
> > 
> > I am not sure to understand what you mean.
> What I was trying to mean is that, for this script test:
> 
> js> function ret_argumentsLength() { return arguments.length; }
> js> function rinline_arguments_length_3(i){return
> ret_argumentsLength.apply(this, arguments)}
> js> for (var i = 0; i < 10000; i++) rinline_arguments_length_3(i,0,1,2)    
> 
> The number of iterations(in this case, 10000) is the reason for the
> rinline_arguments_length_3 and ret_argumentsLength being inlined. We can see
> it in the IonGraph[1]. To validate this assumption, I've run the script
> above with a small number of iteractions (100, to be more precise).

This is normal, inlining is only performed by IonMonkey.  Have you tried to use the setJitCompilerOptions with --ion-offthread-compile=off to IonMonkey.  You can also try with --ion-eager.
Flags: needinfo?(kvijayan)
(In reply to Caio Lima(:caiolima) from comment #6)
> (In reply to Nicolas B. Pierron [:nbp] from comment #4)
> > (In reply to Caio Lima(:caiolima) from comment #3)
> > > So, What I need to investigate now is about the rules for inlining and
> > > compilation polices. Is there any way(documentation, discussion thread or
> > > code pointing) to verify and investigate them? 
> > 
> > IONFLAGS=inline ?
> > Otherwise needinfo h4writer or djvj.
> 
> The |IONFLAGS=inline| shows what is being inlined, but not why and when it
> should be inlined. djvj, could you help me understand about these heuristcs? 

http://dxr.mozilla.org/mozilla-central/source/js/src/jit/IonBuilder.cpp?from=IonBuilder.cpp#341 ?
(In reply to Caio Lima(:caiolima) from comment #6)
> 
> The |IONFLAGS=inline| shows what is being inlined, but not why and when it
> should be inlined. djvj, could you help me understand about these heuristcs? 
> 
> [1] - https://bugzilla.mozilla.org/attachment.cgi?id=8452305

The link that nbp posted is probably the best place to look.  There is some spew on the specifics of inlining decision-making, but nothing to walk you through exactly why each inlining occurred.  For now, it's probably best to debug-step through your individual testcase and stop at the inlining phase.

For exmaple, in gdb, you can set a breakpoint at canInlineTarget, and set a conditional on that breakpoint so that it only triggers when the inline-candidate script's lineno matches the one you are checking for.  Then you can step through the IonBuilder inline logic and see where it's deciding not to inline.
Assignee: nobody → ticaiolima
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
(Assignee)

Comment 10

3 years ago
Hey guys, I've investigated the inlining heuristics. I've used this script for my test case:

js> setJitCompilerOption("baseline.usecount.trigger", 10);
js> setJitCompilerOption("ion.usecount.trigger", 20);
js> function func1(a) { return a + 1; }
js> function inline_apply_function(i){ return func1.apply(this, arguments);}
js> for (var i = 0; i < 30; i++) inline_apply_function(i);

Based on this script above, and debugging, I've found what is avoiding the function to be inlined. It's because this heuristic http://dxr.mozilla.org/mozilla-central/source/js/src/jit/IonBuilder.cpp?from=IonBuilder.cpp#5041

Based on the conditions, this call is analyzed to be inlined when inliningDepth_ != 0 or executionMode() == DefinitePropertiesAnalysis. What is the meaning of these conditions? May we change them?
Flags: needinfo?(kvijayan)
- inliningDepth_ != 0:

This is how deep we inlined functions. If we call "test" from the function "foo", we can decide to inline "test" instead of just calling it. http://en.wikipedia.org/wiki/Inline_function
This variable keeps track on how deep we are inlining. Inlining "apply" is disabled on the top script, since we can't retrieve the "arguments object". For inlined scripts this is easy. We know the arguments used to call the script. But for the top level script not.

- executionMode() == DefinitePropertiesAnalysis:

Before we compile IonMonkey code, we run some analysis passes. Like definite property analysis and argument object analysis. These don't create running code. They are just trying to find properties of the script. So it is not needed to inline here.
Flags: needinfo?(kvijayan)
(In reply to Caio Lima(:caiolima) from comment #10)
> Hey guys, I've investigated the inlining heuristics. I've used this script
> for my test case:
> 
> js> setJitCompilerOption("baseline.usecount.trigger", 10);
> js> setJitCompilerOption("ion.usecount.trigger", 20);
> js> function func1(a) { return a + 1; }
> js> function inline_apply_function(i){ return func1.apply(this, arguments);}
> js> for (var i = 0; i < 30; i++) inline_apply_function(i);

Note that the inlining heuristics depend on the script usecounts, so changing the compilation thresholds like this could affect that. I think it's better to test this with the default compilation thresholds.
(Assignee)

Comment 13

3 years ago
(In reply to Hannes Verschore [:h4writer] from comment #11)
> - inliningDepth_ != 0:
> 
> This is how deep we inlined functions. If we call "test" from the function
> "foo", we can decide to inline "test" instead of just calling it.
> http://en.wikipedia.org/wiki/Inline_function
> This variable keeps track on how deep we are inlining. Inlining "apply" is
> disabled on the top script, since we can't retrieve the "arguments object".
> For inlined scripts this is easy. We know the arguments used to call the
> script. But for the top level script not.

Thank you for this explanation!
What I would like to understand is why we can't retrieve the "arguments object" on the top script? The main reason of my doubt is because the compiled script is specialized, what means that the arguments are specialized either and we can access them. For instance, imagine that this function is being compiled by IonMonkey:

js> function func_test(i) { return arguments[0] + 1;}

The access of |arguments[0]| is compiled to getframeargument (with the bondscheck and etc). So, my questions are: 

1. Can't we think in a better way and inline the func.apply on the top script? 
2. This change will produce an interesting optimization result?

(In reply to Jan de Mooij [:jandem] from comment #12)
> (In reply to Caio Lima(:caiolima) from comment #10)
> > Hey guys, I've investigated the inlining heuristics. I've used this script
> > for my test case:
> > 
> > js> setJitCompilerOption("baseline.usecount.trigger", 10);
> > js> setJitCompilerOption("ion.usecount.trigger", 20);
> > js> function func1(a) { return a + 1; }
> > js> function inline_apply_function(i){ return func1.apply(this, arguments);}
> > js> for (var i = 0; i < 30; i++) inline_apply_function(i);
> 
> Note that the inlining heuristics depend on the script usecounts, so
> changing the compilation thresholds like this could affect that. I think
> it's better to test this with the default compilation thresholds.

I've noticed it. Thank you to remember!
But this testcase is being proposed this way because when we call |func1(i)| using the same useconts, the func1 is being inlined on |inline_apply_function| compilation. So, I investigated why the same behavior is not being applied to |func1.apply(this, arguments)|.
Still not being inlined.
Priority: -- → P5
You need to log in before you can comment on or make changes to this bug.