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 (1)
/(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 (1)
and (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 arguments
object.
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 a
.
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 arguments
object.
A combination of rather straightforward optimization passes could at least handle the code like (which is what I am told some to-JavaScript compilers produce and which is not optimized at all by todays V8):
Optimizer could turn it into:
While I believe that an optimization like this is within reach for most JS VMs I still think that if your to-JavaScript compiler emits the first version of the code and not the second then maybe it’s time to implement a real optimization pass in your compiler - instead of waiting for JS VMs to do it (especially given that JS VMs are likely to shift focus towards optimizing rest parameters and spread operator - given their more reasonable nature).
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 Array
is yet another JavaScript wart that causes nothing but special cases and complexity in optimization pipelines all over the world - makes JavaScript harder to optimize and thus makes harder to write optimizable JavaScript]
Aliasing between arguments
object and parameters
Another common bailout is caused by aliasing between parameters and arguments
object.
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 arguments
object:
- never mix
arguments
object with anything in the same variable; - property loads (not stores!)
a[i]
anda.length
are optimizable if variablea
always containsarguments
; foo.apply(x, a)
are optimizable if variablea
always containsarguments
,foo
is a function, this call site is monomorphic andapply
isFunction.prototype.apply
.