阪田 浩一
Koichi Sakata
GraalVMで使われている、
他言語をJVM上に実装する
仕組みを学ぼう
About Me
• 阪田 浩一 @jyukutyo
• 関西Javaエンジニアの会 会長
• Javaチャンピオン
• ポノス株式会社(京都)
• JVMになりたい人
http://www.graalvm.org/
http://www.graalvm.org/
WIP for
Windows
GraalVM = HotSpotVM + α
Custom Part in GraalVM
• Graal
– JIT Compiler
• Truffle
– Language Implementation Framework
• SubstrateVM
– Runtime Library andTools
for Java AOT Compiled Code
HotSpotVM
Compiler Interface
C2C1
HotSpotVM
C++
GraalVM
Compiler
Interface
GraalC1
HotSpotVM
JVMCI
Java
GraalVM ≠ Graal
Top 10ThingsTo DoWith GraalVM
1. High-performance modern Java
2. Low-footprint, fast-startup Java
3. Combine JavaScript, Java, Ruby,
and R
4. Run native languages on the
JVM
5. Tools that work across all
languages
6. Extend a JVM-based application
7. Extend a native application
8. Java code as a native library
9. Polyglot in the database
10. Create your own language
http://www.graalvm.org/
Top 10ThingsTo DoWith GraalVM
1. High-performance modern Java
2. Low-footprint, fast-startup Java
3. Combine JavaScript, Java, Ruby,
and R
4. Run native languages on the
JVM
5. Tools that work across all
languages
6. Extend a JVM-based application
7. Extend a native application
8. Java code as a native library
9. Polyglot in the database
10. Create your own language
Polyglot
HotSpotVM
JVMCI
Graal
JVM lang Truffle
LLVMJS R Ruby
C C++
Fortran
Polyglot
HotSpotVM
JVMCI
Graal
JVM lang Truffle
LLVMJS R Ruby
C C++
Fortran
Interpreter
Truffleでの言語実装
• TruffleRuby (Ruby)
– https://github.com/graalvm/truffleruby
• FastR (GNU R)
– https://github.com/graalvm/fastr
• Graal.js (JavaScript)
– ECMAScript 2018/Node.js 10.15.0
– https://github.com/graalvm/graaljs
• SimpleLanguage (学習用)
– https://github.com/graalvm/simplelanguage
ということは…!
オレオレ言語もJVM上で!
HotSpotVM
JVMCI
Graal
JVM lang Truffle
オレオレ言語
なんでそんなことするの?
Because it's fun!
今日のゴール
私もTruffleを使って、
言語を実装してみようかな、
と思ってもらうこと
Truffle
• Language Implementation Framework
– A library for building
programming language implementations as
interpreters for AST
AST
Abstract SyntaxTree
抽象構文木
サンプルとして
四則演算を考える
AST
*
3
1 2
+(1 + 2) * 3
プログラムを
ASTに変換する
とは
プログラムの解析
(1 + 2) * 3
( 1 + 2 ) * 3
レキサー
パーサー
サンプル
(1 + 2) * 3
( 1 + 2 ) * 3
テキスト
トークン
構文木
ASTへ
*
3
1 2
+
ASTを
インタープリトして
コードを実行する
Truffleのメリット
ASTにさえできれば、
GraalとJVMに
実行を任せられる
Truffleでのメリット
ランタイム構築を
自分でやらなくてよい
たとえば
JITコンパイル
(Partial Evaluation/部分評価)
モジュール 入力 出力
レキサー
(字句解析)
テキスト
(コード)
トークン
パーサー
(構文解析)
トークン 構文木
AST
インタプリタ
構文木から作成した
AST
実行結果
プログラムを実行するまで
モジュール 入力 出力
レキサー
(字句解析)
テキスト
(コード)
トークン
パーサー
(構文解析)
トークン 構文木
AST
インタプリタ
構文木から作成した
AST
実行結果
プログラムを実行するまで今日の範囲外
レキサー/パーサー
• ANTLR http://www.antlr.org/
モジュール ライブラリ
レキサー ANTLR
パーサー ANTLR
ASTインタプリタ Truffle
ライブラリの役割
今日の内容
オレオレ言語のコードを
ASTに変換した前提で、
そのASTをTruffleを使って
JVM上で実行させる
今日のソースコード
• Truffle 1.0.0 RC12対応
– https://github.com/jyukutyo/JVM-Math-Language
Truffle
• トラフル(トリュフ)
• 言語実装用フレームワーク
– ASTインタプリタ構築の基盤を提供する
• Graalプロジェクトの一部
– Oracle Labs主導
– https://github.com/graalvm/graal/tree/master/truffle
Truffleでの言語実装
1. AST用ノードクラスを作成
2. Truffleの言語実装に必須のクラスを実装
3. (入出力などの実装)
Truffleでの言語実装
1. AST用ノードクラスを作成
2.
3.
再度
四則演算
四則演算でのノード
• 数値ノード(子ノード:なし)
– BigDecimal
– Long(BigDecimalだけでもよいが、あえて)
• 演算子ノード(子ノード:オペランド2つ)
– Add(+)
– Subtract(-)
– Multiply(*)
– Divide(/)
• 括弧ノード
"-1"といった
書き方はサポートしない
今回のノード
RootNode
MulNode
ParenNode LongNode
LongNode LongNode
AddNode
ノードのクラス図
全ノードの
スーパークラス
ノードのクラス図
かっこに対応する
ノード
演算子ノードのクラス図
Truffleのノードクラス
• Truffle DSL APIを使う
1. 提供されるアノテーションを
実装コードに付与する
2. Truffleのアノテーションプロセッサが
コードを生成する
アノテーションの種類、意味は
Javadocを読むしかない
(ドキュメントは整備されていない)
(なお、アノテーションで、
JITコンパイルにおける
部分評価の範囲なども
設定できる)
数値ノード
@NodeInfo(shortName = "const")
public class LongNode extends JvmMathLangNode {
private final long value;
public LongNode(long l) {
this.value = l;
}
public long executeLong(VirtualFrame frame) {
return this.value; }
@Override
public Object executeGeneric(VirtualFrame frame) {
return this.value;
}
}
加算ノード
@NodeInfo(shortName = "+")
public abstract class AddNode extends BinaryNode {
@Specialization(rewriteOn = ArithmeticException.class)
@TruffleBoundary
protected long add(long left, long right) {
return Math.addExact(left, right);
}
@Specialization
@TruffleBoundary
protected BigDecimal add(BigDecimal left, BigDecimal right) {
return left.add(right);
}
BinaryNode
@NodeChildren({ @NodeChild("leftNode"), @NodeChild("righrNode") })
public abstract class BinaryNode extends JvmMathLangNode {
}
子ノードを持つノード
(演算子ノードなど)
の実際の処理は、
アノテーションプロセッサが
自動生成する
ノードのクラス図
Truffleのアノテーションプロセッサが生成する
自動生成コードを
見ましょう
Truffleでの言語実装
1.
2. Truffleの言語実装に必須のクラスを実装
3.
言語を定める
クラスを作る
com.oracle.truffle.api.
TruffleLanguageクラス
を継承して実装する
CallTarget parse(ParsingRequest r)
実装する必要があるのは
この1メソッドのみ
CallTarget parse(ParsingRequest r)
Truffleのエンジンが
このメソッドを
呼び出す
クラス 説明
ParsingRequest
getSource()で
実行するプログラムを取得する
CallTarget
Truffle.getRuntime()
.createCallTarget(RootNode)で生
成できる
CallTarget parse(ParsingRequest r)
言語規定クラス
@TruffleLanguage.Registration(id = "jvmmathlang",
name = "JVM Math Language", version = "0.0.2")
public class JvmMathLang
extends TruffleLanguage<JvmMathLangContext> {
@Override
protected CallTarget parse(ParsingRequest r) throws Exception {
JvmMathLangRootNode main = parseSource(r.getSource());
return Truffle.getRuntime().createCallTarget(main);
}
parseSource()内で、
字句解析、構文解析、
AST構築をする
parseSource()を
見ましょう
Truffleでの言語実装
1.
2.
3. (入出力などの実装)
入出力
実行したいプログラムを
Truffleのエンジンに
どのように渡し、
結果をどのように
受け取るか?
メインクラス
public class JvmMathLangMain {
public static void main(String[] args) throws IOException {
Context context = Context.create("jvmmathlang");
Object answer = runCode(context, [PROGRAM_FOR_RUN]);
}
private static Object runCode(Context context, String program)
throws IOException {
Source source = Source.newBuilder("jvmmathlang", program,
"MATH").build();
Value value = context.eval(source);
return value;
}
}
Context#eval(Source)すると
CallTarget parse(ParsingRequest)
を経由してプログラムが実行され、
結果がValueインスタンスで返ってくる
実行
• TruffleのJARファイルを追加して、HotSpot
VM上でも実行可能
– なお、Java 10から、Graalはデフォルトで
入っている(Experimental)
• GraalVM上でも実行可能
– Truffle言語をGraalVMで動かす - きしだのHatena
• http://nowokay.hatenablog.com/entry/20190104/1546620114
よりTruffleを学ぶには
• コードを読むしかありません
まとめ
Truffleで、
あなたもオレオレ言語を
JVM上で実行できる!
Truffle言語には
Interoperability
がある
単体の言語を
高パフォーマンスに
実行できるだけではない
例) JavaScriptコードから
Rubyコードを
“低コスト”で呼び出せる
JavaScriptからRuby
js>vararray= Polyglot.eval("ruby","[1,2,42,4]")
js> array[2]
42
[GRAALVM_HOME]/bin/polyglot --shell --jvm
https://www.sakatakoichi.com/entry/graalvmpolyglot
Chromeでのデバッグ
Graal &Truffle
• Graalは言語実装そのものは知らない
– Truffleを間に挟んでいる(JVM言語以外)
• JITコンパイルでは(結果として)
複数言語にまたがったコンパイルができる
call call
call many times
call call
今日のソースコード
• Truffle 1.0.0 RC12対応
– https://github.com/jyukutyo/JVM-Math-Language
ThankYou
for Coming!
ANTLRの書籍
加算ノード
@NodeInfo(shortName = "+")
public abstract class AddNode extends BinaryNode {
@Specialization
@TruffleBoundary
protected BigDecimalTruffleObject add(Object left, Object right) {
BigDecimal l = left instanceof BigDecimalTruffleObject ?
((BigDecimalTruffleObject) left).getValue() : BigDecimal.valueOf((long)
left);
BigDecimal r = right instanceof BigDecimalTruffleObject ?
((BigDecimalTruffleObject) right).getValue() :
BigDecimal.valueOf((long) right);
return new BigDecimalTruffleObject(l.add(r));
}
}

GraalVMで使われている、他言語をJVM上に実装する仕組みを学ぼう