Truffle の最適化が一般的なコンパイラの最適化と大きく異なるところは, Truffle では個々のオブジェクト (AST のノード) ごとにメソッドの実行時プロファイルを集計しているところです。大半のコンパイラは、関数などのコード単位でプロファイルをとり、オブジェクト (receiver やその他の引数) ごとにプロファイルを分類はしません。
なぜこの性質が重要なのでしょうか。論文中の例にならい “+” 演算子を実装した AST ノード AddNode の例で考えてみましょう。この AddNode は引数が数値型か文字列か、あるいは独自の “+” を定義したクラスのオブジェクトかで挙動が変わるとします。もしコンパイラがコード単位で、たとえば AddNode#execute() という単位で実行時情報・・・ここでは引数の型・・・を集めるなら、あるプログラムの中に数値のプラスと文字列のプラスが現れた時点で AddNode#execute() は多態的なコードを生成する必要が生まれます。
Ruby, JS など動的なゲスト言語の AST は、ホスト言語である Java から見るとどうしても多態的になってしまうです。
Truffle では、文字列を扱う関数 F と数値を扱う関数 G の中で登場するプラス演算子の AST ノードがそれぞれ独立して実行時の情報を集めます。おかげで F の中では文字列を連結するコードを、G の中では数値を加算するコードを、生成することができます。
一般的なプログラムではあまり重視されていないオブジェクト単位の実行時プロファイルが、どうして Truffle では重要なのでしょうか。それは Truffle の AST がゲスト言語にとっての「コード」だからです。AST インタプリタとして実装されたゲスト言語がふつうの最適化コンパイラがやるようなコード単位の実行時プロファイルを集めたいと思ったら、AST の上にそのデータを置くのは自然なことです。
Truffle はそうしたデザインを前提として、最適化のためのヒントである実行情報をAST のオブジェクトとの間でやりとりする API を提供しました。それらの API をある種の instrincis として特別に解釈し、ふつうのコンパイラにはできないような最適化が可能になるのです。