Interpreter以前、東京Node学園25時限目で発表した内容を修正して書いていこうと思う。このインタープリタは単純そうに見えて非常にわかりづらいので解説していく。バイトコードインタープリタとはインタープリタとは、ソースコードを逐次実行する形式のエンジン。今までのV8はソースコードを即アセンブラにコンパイルし実行していたが、インタープリタはそれとは違い、一度ソースコードを高レベルなバイト命令に変換し、そのバイト命令を逐次実行していく。高級アセンブラみたいな感じ。Ignition概要Ignitionはレジスタベースのバイトコードインタープリタである。Javaのスタックベースとは違って、CPUのレジスタに実際に値を割り付けて実行する。IgnitionはBytecodeHandlerと呼ばれるバイトコード処理関数を予め生成しておき、バイトコードから配列のインデックスを取得、そのインデックスに生成された処理関数を割り当て、Bytecodeの配列を次々巡回して、対応するインデックスの関数を呼び出しコードを実行する。JSで非常に単純化されたコードを書くと以下の様になる。dispatch(Bytecodes[index++])},}このモデルを念頭にV8のコードベースを確認していく。ASTからバイトコードを生成する。このバイトコード生成のステップを確認していく。ASTを巡回しながら対応しているバイトコードを生成していく。BytecodeGeneratorはsrc/interpreter/bytecode-generator.hにあり、バイトコード生成メソッドはBytecodeGenerator::GenerateBytecodeである。BytecodeGenerator::GenerateBytecodeはどこから呼ばれるかというと、InterpreterCompilationJob::ExecuteJobImpl(src/interpreter/interpreter.cc)内で呼び出される。Interpreter::NewCompilationJobで実行される。Interpreter::NewCompilationJobの階層は以下のようになっている。Interpreter::NewCompilationJobはコンパイラパイプラインのジョブを生成するメソッドなので、compiler.cc(src/compiler.cc)を見ていこう。compiler.cc(src/compiler.cc)は非常に複雑でわかりづらい呼び出し階層をもっており、さらにオプションの設定パーサーの設定も相まって非常に読みづらい。Interpreter::NewCompilationJobを呼び出すまでのコールスタックは以下の様になっている。Compilerのエントリーポイントとなっており、そこから順次関数を呼び出し、最終的にInterpreterのJobを生成する。最終的なBytecodeGenerator::GenerateBytecodeまでの呼び出しコールスタックは以下のようになる。FullCodeGenerator::NewCompilationJob|Interpreter::NewCompilationJob|InterpreterCompilationJob::ExecuteJobImpl|BytecodeGenerator::GenerateBytecodeバイトコード生成さて、呼び出し階層を把握したところで、バイトコードの生成方法を見ていく。バイトコード生成は先程も書いたとおりAstVisitorを継承しているので、各種Visit***メソッドを実装する必要がある。各種Visit***の実装を見ていけば何をしているか理解できるはず。闇雲にコードを見てもバイトコード自体は理解できないので、一旦trace_bytecodeでd8を実行してみる。a>そうするとこのような結果が得られる。バイトコードを出力したのはいいが、見方がわからないと意味が無いので、見方も解説。ここは関数のbytecodeの場合に関数名が入る。今回はグローバルなので空。]これはstackのパラメータの数。今回のバイトコードはグローバルなので無視。ポインタのサイズポインタのサイズは大体の環境で、32bitでは4byte、64bitでは8byteになる。