This is a note in reply to Victor Felder’s question who asked me what “Unsupported phi use of arguments” Crankshaft bailout means.
What Crankshaft is trying to do is to completely avoid allocating
arguments object when you write the code like this:
If one compiles this code naively then an argument object will be allocated when function is entered and a normal property lookup will be used at property access sites
(2) to find number of arguments and their values respectively. However allocating
arguments object taxes garbage collector - which might be expensive if this function is very hot - and furthermore is completely unnecessary - as long as optimizing compiler knows that
arguments contains the
arguments object in
(2) it can just emit very short chunks of code to get the length and argument by index right from the stack - because this information is already there and thus there is no need to rewrap it into an
So essentially the code compiles into something like this:
Now we just need to make sure to apply this optimization only if we can easily establish that it is applicable. Easily is a keyword here. Crankshaft often preferred simple solution that cover most useful cases - to more generic but also more complicated solutions.
For example, compiler can obviously easily establish that variable
a in the code below always contains the current
arguments object and nothing else.
Which means optimization described above is still applicable.
There is a very important thing to understand here, so I will reiterate on it: once optimizing compiler chews on this code there will be no
arguments object, it will be dematerialized, it will not exist and neither will variable
a which contains it. However if a deoptimization happens (e.g.
foo was always called with numbers and got optimized for number addition, but later somebody called it with a string) then deoptimizer has to reconstruct unoptimized execution environment - and it has to “create” a slot for the variable
a then materialize
arguments object and put it into the variable
Now lets look at a slightly different chunk of code:
When compiler sees this it becomes confused: we can no longer statically know what
a contains - it can contain either
arguments object or an array. These two objects are completely different internally - because of that we can no longer easily dematerialize
a. So optimizing compiler just says “ok, it’s too difficult, I give up”. It gives up because it only supports optimizing functions when arguments object is dematerialized.
This case is exactly the case when “Unsupported phi use of arguments” bailout happens.
The confusing “phi” thing mentioned by the message is a phi-function from the SSA-form. Easiest way to explain them is to say that code like this
is internally represented like this:
This is SSA-form: each variable has exactly on definition (assigned only once) and if variable’s value is control dependent (like
y’s) - then this dependency is captured by a phi function which is placed at control flow merge point and magically knows where control came from.
Returning back to our bailout message: if arguments object flows into a phi it means that there is a variable which either contains arguments object or something else depending on the control flow.
Does it have to be this way?
As always with optimizing compiler the answer is a superposition of no and yes.
If I were implementing Crankshaft and
arguments object specific optimizations today I would probably opt for a completely different approach - off-load most of the
arguments object optimization into a more generic load-store forwarding and allocation sinking passes. However even that would not be entirely pretty due to abnormal nature of
Optimizer could turn it into:
Even when we look closely at the code like this:
We discover that it is not inherently unoptimizable. This code uses two objects which are different but are not that different. Optimizing compiler could transform this into something like this:
However this optimization is considerably more complicated (as in “it doesn’t fit into a simple allocation sinking pass”) because it involves stack allocation of an array literal. My prediction would be that a generic optimization of this sort is unlikely to come to JS engines soon. In other words: try to avoid writing or generating code like that.
[I can’t resist noting that
arguments object not being an
arguments object and parameters
Another common bailout is caused by aliasing between parameters and
Crankshaft does not like this aliasing because it influences how SSA-form has to be constructed and refuses to optimize this function. It will happily optimize a strict version of this though as there is no aliasing:
Optimizable uses of arguments in V8 (Crankshaft)
Here is a quick check list for optimizable uses of
- never mix
argumentsobject with anything in the same variable;
- property loads (not stores!)
a.lengthare optimizable if variable
foo.apply(x, a)are optimizable if variable
foois a function, this call site is monomorphic and