Web App Development

Compiling V8 and viewing the assembly code it generates from JavaScript

I was recently looking into some code optimization behavior by V8, the JavaScript engine used by Chrome and Node. (The JavaScript engine in Firefox is called SpiderMonkey, the one in Internet Explorer Chakra).

Modern JavaScript engines don’t interpret the JavaScript code directly, but instead translate it to machine code that can run much faster.

This post explains how to compile V8 and and use the JavaScript shell it provides (called D8).

Compiling V8

Before compiling V8 you need to install the Chromium depot_tools - when I tried it without V8 complained that it couldn’t find a program called gclient:

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=`pwd`/depot_tools:"$PATH"

Then check out the V8 repo and build the JavaScript engine:

svn co http://v8.googlecode.com/svn/trunk v8-trunk
cd v8-trunk
make dependencies
make ia32.release objectprint=on disassembler=on
export PATH=${PATH}:`pwd`/out/ia32.release

I’m not sure what exactly objectprint does and if it’s necessary, but you need the disassembler options to be able to view the assembly code generated by V8.

Now the D8 program should be available in your console.

Running a JavaScript program with D8

The D8 command line tool is very similar to the node command. You can ither use the interactive shell or use it to execute a specific file.

For example if we have this code in add.js:

function add(a, b){
  return a + b;
}

print(add(15, 19));

Then you can run it by calling

d8 add.js

And the output will be

34

Not how we use print rather than console.log. The console API isn’t available natively in the V8 engine.

Using D8 to view the Hydrogen code representation

There are four main stages that code inside V8 passes through:

  1. JavaScript Code - this is what you wrote
  2. Hydrogen - intermediate code representation where optimizations are applied
  3. Lithium - machine specific code used to generate native code
  4. Machine Code - this is what the computer understands

The --trace-hydrogen option allows you to see the Hydrogen code that’s generated when running V8. To ensure the Hydrogen code is created for the addfunction, add this to the code:

for (var i=0;i<1000;i++){
  add(i, 1);
}

Now d8 --trace-hydrogen add.js will create a hydrogen.cfg file in the current directory.

The file is 80KB large and contains code like this:

begin_compilation
  name "add"
  method "add:0"
  date 1440029178000
end_compilation
begin_cfg
  name "H_Assign dominators"
  begin_block
    name "B0"
    from_bci -1
    to_bci -1
    predecessors
    successors "B1"
    xhandlers
    flags
    loop_depth 0
    begin_states
      begin_locals
        size 0
        method "None"
      end_locals
    end_states
    begin_HIR
      0 0 v0 BlockEntry  type:Tagged <|@
      0 0 t12 Constant 0x333080a1 <the hole> [map 0x29e08211]  <|@

You can have a look at the entire file on Github.

Using D8 to view the generated assembly code

Finally, you can use the --print-code option to see the native code generated by V8:

d8 --print-code add.js

Which will output something like this:

--- Raw source ---
function add(a, b){
  return a + b;
}
...

--- Code ---
source_position = 0
kind = FUNCTION
Instructions (size = 456)
0x36953100     0  8b4c2404       mov ecx,[esp+0x4]
0x36953104     4  81f991806049   cmp ecx,0x49608091          ;; object: 0x49608091 <undefined>
0x3695310a    10  750a           jnz 22  (0x36953116)
0x3695310c    12  8b4e13         mov ecx,[esi+0x13]
0x3695310f    15  8b4917         mov ecx,[ecx+0x17]
0x36953112    18  894c2404       mov [esp+0x4],ecx
0x36953116    22  55             push ebp

You can have a look at the full assembly code for add.js on Github.


Follow me on Twitter