It’s finally here. Every V8 release, every six weeks when we branch as part of our release process, the question comes up about what will happen when V8 hits version 8. Will we have a party? Will we ship a new compiler? Will we skip versions 8 and 9 and just stay at an eternal V8 version X? Finally, after over 10 years of work, on our 100th blog post, we’re pleased to announce our newest branch, V8 version 8.0 V8, and we can finally answer that question:

It’s bug fixes and performance improvements.

This post provides a preview of some of the highlights in anticipation of the release in coordination with Chrome 80 Stable in several weeks.

Pointer compression #

We changed all our void * to pv , reducing source file size by up to 66%.

The V8 heap contains a whole slew of items, for example floating point values, string characters, compiled code, and tagged values (which represent pointers into the V8 heap or small integers). Upon inspection of the heap, we discovered that these tagged values occupy the majority of the heap!

Tagged values are as big as the system pointer: they are 32 bits wide for 32-bit architectures, and 64 bits in 64-bit architectures. Then, when comparing the 32-bit version with the 64-bit one, we are using twice as much heap memory for every tagged value.

Luckily for us, we have a trick up our sleeve. The top bits can be synthesized from the lower bits. Then, we only need to store the unique lower bits into the heap saving precious memory resources... to save an average of 40% of the heap memory! Pointer compression saves an average of 40% of memory.

When improving memory, usually it comes at the cost of performance. Usually. We are proud to announce that we saw improvements in performance on real websites in the time spent in V8, and in its garbage collector!

Desktop Mobile Facebook V8-Total -8% -6% GC -10% -17% CNN V8-Total -3% -8% GC -14% -20% Google Maps V8-Total -4% -6% GC -7% -12%

If pointer compression piqued your interest, be on the lookout for a full blog post with more details.

Optimizing higher-order builtins #

We recently removed a limitation within TurboFan’s optimization pipeline that prevented aggressive optimizations of higher-order builtins.

const charCodeAt = Function . prototype . call . bind ( String . prototype . charCodeAt ) ;



charCodeAt ( string , 8 ) ;

So far, the call to charCodeAt was completely opaque to TurboFan, which led to the generation of a generic call to a user-defined function. With this change, we are now able to recognize that we are actually calling the built-in String.prototype.charCodeAt function and are thus able to trigger all the further optimizations that TurboFan has in stock to improve calls to builtins, which leads to the same performance as:

string . charCodeAt ( 8 ) ;

This change affects a bunch of other builtins like Function.prototype.apply , Reflect.apply , and many higher-order array builtins (e.g. Array.prototype.map ).

Optional chaining #

When writing chains of property accesses, programmers often need to check if intermediate values are nullish (that is, null or undefined ). A chain without error checking may throw, and a chain with explicit error checking is verbose and has the unwanted consequence of checking for all truthy values instead of only non-nullish values.



const nameLength = db . user . name . length ;





let nameLength ;

if ( db && db . user && db . user . name )

nameLength = db . user . name . length ;

Optional chaining ( ?. ) lets programmers write terser, robust chains of property accesses that check if intermediate values are nullish. If an intermediate value is nullish, the entire expression evaluates to undefined .



const nameLength = db ?. user ?. name ?. length ;

In addition to static property accesses, dynamic property accesses and calls are also supported. Please see our feature explainer for details and more examples.

Nullish coalescing #

The nullish coalescing operator ?? is a new short-circuiting binary operator for handling default values. Currently, default values are sometimes handled with the logical || operator, such as in the following example.

function Component ( props ) {

const enable = props . enabled || true ;



}

Use of || is undesirable for computing default values because a || b evaluates to b when a is falsy. If props.enabled were explicitly set to false , enable would still be true.

With the nullish coalescing operator, a ?? b evaluates to b when a is nullish ( null or undefined ), and otherwise evaluates to a . This is the desired default value behavior, and rewriting the example using ?? fixes the bug above.

function Component ( props ) {

const enable = props . enabled ?? true ;



}

The nullish coalescing operator and optional chaining are companion features and work well together. The example may be further amended to handle the case when no props argument is passed in.

function Component ( props ) {

const enable = props ?. enabled ?? true ;



}

Please see our feature explainer for details and more examples.

V8 API #

Please use git log branch-heads/7.9..branch-heads/8.0 include/v8.h to get a list of the API changes.