A list of Crankshaft bailout reasons with examples, explanations and advices.
Unless otherwise specified, the following are Crankshaft bailouts.
Your contribution is most welcome!
In order to keep this section short and allow people to get to the primary content of this repo faster, here is what it's all about and why you (probably) should care: Chromium, Chrome, Node.js, V8, Crankshaft and bailout reasons.
- Assignment to parameter in arguments object
- Bad value context for arguments value
- ForInStatement with non-local each variable
- Inlining bailed out
- Object literal with complex property
- Optimized too many times
- Reference to a variable which requires dynamic lookup
- Rest parameters
- Smi addition overflow
- Smi subtraction overflow
- Too many parameters
- TryCatchStatement
- TryFinallyStatement
- Unsupported phi use of arguments
- Unsupported phi use of const or let variable
- Yield
Only happens if you reassign to a parameter while also mentioning arguments
in the function. More info.
- Simple reproduction(s)
// sloppy mode only
function test(a) {
if (arguments.length < 2) {
a = 0;
}
}
-
Why
- In sloppy mode V8 needs to preserve bindings between
arguments[0]
anda
so that when anya
was passed, and you reassign it to10
, and later try to readarguments[0]
, it has to return10
, too. This is non-trivial for the engine, so it chooses to bail out. Strict mode removes this requirement, andarguments
anda
behave as regular independent JavaScript variables, so deoptimization does not occur.
- In sloppy mode V8 needs to preserve bindings between
-
Advices
- In the above example, you could assign
a
to a new variable. - You should use strict mode anyway.
- It seems this will be optimized by TurboFan #1.
- In the above example, you could assign
-
External examples
- Simple reproduction(s)
// strict & sloppy modes
function test1() {
arguments[0] = 0;
}
// strict & sloppy modes
function test2() {
arguments.length = 0;
}
// strict & sloppy modes
function test3() {
return arguments;
}
// strict & sloppy modes
function test4() {
var args = [].slice.call(arguments);
}
// strict & sloppy modes
function test5() {
var a = arguments;
return function() {
return a;
};
}
-
Why
- It requires rematerialization of the
arguments
array.
- It requires rematerialization of the
-
Advices
- Read this: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments
- You could loop over
arguments
to build a new array, but it's not recommended. See Unsupported phi use of arguments - Usages of
arguments
as shown above are very rarely legitimate. - More about this bailout reason
- It seems this will be optimized by TurboFan #1.
-
External examples
- Simple reproduction(s)
// strict & sloppy modes
function test1() {
var obj = {};
for(key in obj);
}
// strict & sloppy modes
function key() {
return 'a';
}
function test2() {
var obj = {};
for(key in obj);
}
-
Why
-
Advices
- Only use pure (i.e. non-computed) local variable in a for...in.
- https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#5-for-in
-
External examples
- Simple reproduction(s)
// strict & sloppy modes
var obj = { prop1: ... };
function test(param) {
param.prop2 = ...; // Inlining bailed out
}
test(obj);
// strict & sloppy modes
var obj = { prop1: ... };
function someMethodThatAssignsSomeOtherProp(param) {
param.someOtherProp = ...; // Inlining bailed out
}
function test(param) {
someMethodThatAssignsSomeOtherProp(param);
}
f(obj);
-
Why
- Crankshaft predicts that
param
will have the same hidden class asobj
, allowing optimization by doing inline caching. At the annotated lines above, Crankshaft notices thatobj
's hidden class is not suitable forparam
and bails out (it cannot patch the inline cache code with the hidden class information). More about inline caching and hidden classes
- Crankshaft predicts that
-
Advices
- When creating an object initialize all the properties you are going to use, instead of adding new properties to an existing object.
-
External examples
- Simple reproduction(s)
// strict & sloppy modes
function test() {
return {
__proto__: 3
};
}
-
Why
-
Advices
-
External examples
- Simple reproduction(s)
// strict & sloppy modes
// No known canonical reproduction
-
Why
- Optimization failed so many times that Crankshaft gave up.
- "In reality this very often actually means a bug in V8 - there is some optimization which is too optimistic so the generated code deopts all the time." - @mraleph #5
- More about this bailout reason
-
Advices
- "Just use IRHydra and look at the deoptimization reasons - the picture should become clear immediately." - @mraleph #5
-
External examples
- Simple reproduction(s)
// sloppy mode only
function test() {
with ({x:1}) {
return x;
}
}
-
Why
- "Variable lookup fails at compile time, Crankshaft needs to resort to dynamic lookup at runtime." - Yang Guo #3
-
Advices
- "Refactor to remove the dependency on runtime-information to resolve the lookup." - Paul Irish #4
- No bailout with TurboFan.
-
External examples
- Simple reproduction(s)
// strict & sloppy modes
function test(...rest) {
return rest[0];
}
-
Why
- Probably because it requires materializing the
arguments
array.
- Probably because it requires materializing the
-
Advices
- Avoid rest parameters or use Babel's transform-es2015-parameters until TurboFan is able to optimize them #1, #2.
-
External examples
- Simple reproduction(s) Smi - A Smi is a 32-bit signed int on 64-bit architectures and a 31-bit signed int on 32-bit architectures.
Smi addition overflow
function add(a, b) {
return a + b;
}
add(1, 2);
add(1, 2);
%OptimizeFunctionOnNextCall(add);
add(2 ** 31 - 2, 20);
- Why
- Once addition is performed, the return value cannot be represented as a Smi and thus is casted to HeapNumber
- Advices
- You could have 2 separate functions - one for Smis and one for HeapNumbers.
- External examples
- Simple reproduction(s) Same case as with Smi addition overflow Smi addition overflow
function subtract(a, b) {
return a - b;
}
subtract(1, 2);
subtract(1, 2);
%OptimizeFunctionOnNextCall(subtract);
subtract(-3, 2 ** 31 - 1);
- Why
- Once subtraction is performed, the return value cannot be represented as a Smi and thus is casted to HeapNumber
- Advices
- You could have 2 separate functions - one for Smis and one for HeapNumbers.
- External examples
- Simple reproduction(s)
// strict & sloppy modes
function test(p1, p2, p3, ..., p512) {
}
-
Why
- Setting limits.
-
Advices
- If you write functions with 512 parameters or more, you probably don't worry about optimizing your code for V8 anyway.
-
External examples
- Obviously nobody ever did that. Hopefully nobody will ever do that. Zero google result on this bailout reason.
- V8 code source
- Simple reproduction(s)
// strict & sloppy modes
function test() {
return 3;
try {} catch(e) {}
}
-
Why
- Try/catch makes the control flow jump virtually anywhere. It's hardly optimizable because the caught exception is potentially only known at runtime.
-
Advices
- Don't put try/catch inside computationally intensive functions.
- You could
try { test() } catch
-
External examples
- Simple reproduction(s)
// strict & sloppy modes
function test() {
return 3;
try {} finally {}
}
-
Why
-
Advices
-
External example
- Simple reproduction(s)
// strict & sloppy modes
function test1() {
var _arguments = arguments;
if (0 === 0) { // anything evaluating to true, except a number or `true`
_arguments = [0]; // Unsupported phi use of arguments
}
}
// strict & sloppy modes
function test2() {
var _arguments = arguments;
for (var i = 0; i < 1; i++) {
_arguments = [0]; // Unsupported phi use of arguments
}
}
// strict & sloppy modes
function test3() {
var _arguments = arguments;
var again = true;
while (again) {
_arguments = [0]; // Unsupported phi use of arguments
again = false;
}
}
-
Why
- Crankshaft is unable to guess whether
_arguments
should be an object or an array. It cannot dematerialize_arguments
and gives up. - In-depth explaination
- Crankshaft is unable to guess whether
-
Advices
- There is no good workaround except splitting your function into smaller ones that don't manipulate a copy of
arguments
. - Don't try to fool V8 by looping over
arguments
to create a new array out of it: "Allocating array (and hope it will get handled by some optimization pass in the V8) is a bad idea." - @mraleph (source) - It seems this will be optimized by TurboFan #1.
- There is no good workaround except splitting your function into smaller ones that don't manipulate a copy of
-
External examples
- Simple reproduction(s)
function test() {
for (let i = 0; i < 0; i++) {
const x = __lookupGetter__; // `__lookupGetter__` and
}
const self = this; // `this` should both be present for this to happen
}
-
Why
- Crankshaft sees a hole (marker for Temporary Dead Zone of
let
/const
) and aborts compilation.
- Crankshaft sees a hole (marker for Temporary Dead Zone of
-
Advices
-
External examples
- Simple reproduction(s)
// strict & sloppy modes
function* test() {
yield 0;
}
-
Why
-
Advices
-
External examples
- All bailout reasons in Chromium codebase
- Bad value context for arguments value
- I-want-to-optimize-my-JS-application-on-V8 checklist
- JavaScript: Performance loss on incorrect arguments using
- Optimization killers
- OptimizationKillers
- Performance Tips for JavaScript in V8
- thlorenz/v8-perf
- A high-level tutorial about tracing deopts points
- 32 bit value in register is not zero-extended
- Alignment marker expected
- Allocation is not double aligned
- API call returned invalid object
- Arguments object value in a test context
- Array boilerplate creation failed
- Array index constant value too big
- Assignment to arguments
- Assignment to let variable before initialization
- Assignment to LOOKUP variable
Assignment to parameter in arguments object- Assignment to parameter, function uses arguments object
- Bad value context for arguments object value
Bad value context for arguments value- Bailed out due to dependency change
- Bailout was not prepared
- Both registers were smis in SelectNonSmi
- Call to a JavaScript runtime function
- Class literal
- Code generation failed
- Code object not properly patched
- Compound assignment to lookup slot
- Computed property name
- Context-allocated arguments
- Copy buffers overlap
- Could not generate +0.0
- Could not generate -0.0
- DebuggerStatement
- Declaration in catch context
- Declaration in with context
- Default NaN mode not set
- Delete with global variable
- Delete with non-global variable
- Destination of copy not aligned
- Do expression encountered
- DontDelete cells can't contain the hole
- DoPushArgument not implemented for double type
- Eliminated bounds check failed
- EmitLoadRegister: Unsupported double immediate
- eval
- Expected +0.0
- Expected alignment marker
- Expected allocation site
- Expected function object in register
- Expected HeapNumber
- Expected native context
- Expected new space object
- Expected non-identical objects
- Expected non-null context
- Expected undefined or cell in register
- Expecting alignment for CopyBytes
- Export declaration
- External string expected, but not found
- ForInStatement optimization is disabled
ForInStatement with non-local each variable- ForOfStatement
- Frame is expected to be aligned
- Function calls eval
- Function is being debugged
- Function with illegal redeclaration
- Generated code is too large
- Generator
- Generator failed to resume
- Global functions must have initial map
- HeapNumberMap register clobbered
- Import declaration
- Index is negative
- Index is too large
- Inlined runtime function: FastOneByteArrayJoin
Inlining bailed out- Input GPR is expected to have upper32 cleared
- Input string too long
- Integer32ToSmiField writing to non-smi location
- Invalid capture referenced
- Invalid ElementsKind for InternalArray or InternalPackedArray
- invalid full-codegen state
- Invalid HandleScope level
- Invalid left-hand side in assignment
- Invalid lhs in compound assignment
- Invalid lhs in count operation
- Invalid min_length
- JSGlobalObject::native_context should be a native context
- JSGlobalProxy::context() should not be null
- JSObject with fast elements map has slow elements
- Let binding re-initialization
- Live Bytes Count overflow chunk size
- LiveEdit
- Lookup variable in count operation
- Map became deprecated
- Map became unstable
- Native function literal
- Need a Smi literal here
- No cases left
- No empty arrays here in EmitFastOneByteArrayJoin
- Non-initializer assignment to const
- Non-object value
- Non-smi index
- Non-smi key in array literal
- Non-smi value
- Not enough spill slots for OSR
- Not enough virtual registers (regalloc)
- Not enough virtual registers for values
- Object found in smi-only array
Object literal with complex property- Offset out of range
- Operand is a smi
- Operand is a smi and not a bound function
- Operand is a smi and not a function
- Operand is a smi and not a name
- Operand is a smi and not a string
- Operand is not a bound function
- Operand is not a date
- Operand is not a function
- Operand is not a name
- Operand is not a number
- Operand is not a smi
- Operand is not a string
- Operand is not smi
- Operand not a number
- Optimization disabled by filter
- Optimization is disabled
Optimized too many times- Out of virtual registers while trying to allocate temp register
- Parse/scope error
- Possible direct call to eval
- Received invalid return address
Reference to a variable which requires dynamic lookup- Reference to global lexical variable
- Reference to uninitialized variable
- Register did not match expected root
- Register was clobbered
- Remembered set pointer is in new space
Rest parameters- Return address not found in frame
- Should not directly enter OSR-compiled function
- Sloppy function expects JSReceiver as receiver.
Smi addition overflowSmi subtraction overflow- Spread in array literal
- Stack access below stack pointer
- Stack frame types must match
- Super reference
- The current stack pointer is below csp
- The function_data field should be a BytecodeArray on interpreter entry
- The object is not tagged
- The object is tagged
- The source and destination are the same
- The stack pointer is not the expected value
- The stack was corrupted by MacroAssembler::Call()
Too many parameters- Too many parameters/locals
- Too many spill slots needed for OSR
- ToOperand IsDoubleRegister unimplemented
- ToOperand Unsupported double immediate
- ToOperand32 unsupported immediate.
TryCatchStatementTryFinallyStatement- Unaligned allocation in new space
- Unaligned cell in write barrier
- Unexpected allocation top
- Unexpected color bit pattern found
- Unexpected ElementsKind in array constructor
- Unexpected fall-through from string comparison
- Unexpected fallthrough from CharCodeAt slow case
- Unexpected fallthrough from CharFromCode slow case
- Unexpected fallthrough to CharCodeAt slow case
- Unexpected fallthrough to CharFromCode slow case
- Unexpected FPCR mode.
- Unexpected FPU stack depth after instruction
- Unexpected initial map for Array function
- Unexpected initial map for Array function (1)
- Unexpected initial map for Array function (2)
- Unexpected initial map for InternalArray function
- Unexpected level after return from api call
- Unexpected negative value
- Unexpected number of pre-allocated property fields
- Unexpected smi value
- Unexpected string type
- Unexpected type for RegExp data, FixedArray expected
- Unexpected value
- Unexpectedly returned from a throw
Unsupported const compound assignment- Unsupported count operation with const
- Unsupported double immediate
- Unsupported let compound assignment
- Unsupported lookup slot in declaration
- Unsupported non-primitive compare
Unsupported phi use of argumentsUnsupported phi use of const or let variable- Unsupported switch statement
- Unsupported tagged immediate
- Variable resolved to with context
- We should not have an empty lexical context
- WithStatement
- Wrong address or value passed to RecordWrite
- Wrong context passed to function
Yield