Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

28
3 Bytecode DSL & Indy & Brainf*ck G*Workshop Z 2013/09/20 上原潤二 (NTTソフトウェア株式会社) 13920日金曜日

description

implement Brainf*ck compiler using Indy with BytecodeDSL.

Transcript of Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

Page 1: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

Bytecode DSL& Indy& Brainf*ckG*Workshop Z 2013/09/20上原潤二(NTTソフトウェア株式会社)

13年9月20日金曜日

Page 2: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

JGGUG G*Workshop Z Copyright(C) NTT Software.

自己紹介上原潤二(@uehaj)NTTソフトウェア(株)Grails推進室JGGUG(日本Grails/Groovyユーザグループ)運営委員書籍:

プログラミングGROOVY(技術評論社)Grails徹底入門(翔泳社)

ブログ「Grな日々」GroovyServ, LispBuilder, GVM(JVM written in Groovy)開発者

213年9月20日金曜日

Page 3: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

JGGUG G*Workshop Z Copyright(C) NTT Software.

今日の内容Indyの基礎Bytecode DSLBrainf*ckを実装してみたG*Magazine Vol.7の宣伝

313年9月20日金曜日

Page 4: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

JGGUG G*Workshop Z Copyright(C) NTT Software.

テーマ:Indyで遊びたいIndy: Java VM上での動的言語の実行を効率化することを目的とした一連の機能拡張(JSR 292)気軽でないASM面倒くさい

ByteCode DSLを使おう!

413年9月20日金曜日

Page 5: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

35

Bytecode DSL

13年9月20日金曜日

Page 6: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

JGGUG G*Workshop Z Copyright(C) NTT Software.

ByteCode DSLGroovyの内部DSLで実現されたJavaバイトコードのアセンブラASMのラッパーIndy対応! (重要)

6

@groovyx.ast.bytecode.Bytecodeint  fib(int  i)  {        iload  1        iconst_2        if_icmpge  l1        iload  1        _goto  l2      l1        aload  0        iload  1        iconst_2

       isub        invokevirtual  '.fib',  '(I)I'        aload  0        iload  1        iconst_1        isub        invokevirtual  '.fib'  ,'(I)I'        iadd      l2        ireturn}

13年9月20日金曜日

Page 7: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

JGGUG G*Workshop Z Copyright(C) NTT Software.

BytecodeDSL以下で@Grab可

groovyによる実行

7

@GrabResolver(name="maven-­‐repox",  root  =  "https://raw.github.com/uehaj/maven-­‐repo/gh-­‐pages/snapshot")@Grab('groovyx.ast.bytecode:groovy-­‐bytecode-­‐ast:0.2.0-­‐separate-­‐asm')import  groovyx.ast.bytecode.Bytecode

$ groovy fib.groovy102334155

13年9月20日金曜日

Page 8: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

BytecodeDSLでHello Indy!

8

@GrabResolver(name="maven-­‐repo",  root="https://raw.github.com/uehaj/maven-­‐repo/gh-­‐pages/snapshot")@Grab("groovyx.ast.bytecode:groovy-­‐bytecode-­‐ast:0.2.0-­‐separate-­‐asm")import  groovyx.ast.bytecode.Bytecodeimport  java.lang.invoke.*import  java.lang.invoke.MethodHandles.Lookupimport  static  org.objectweb.asm.Opcodes.H_INVOKESTATIC

class  HelloIndy  {        public  static  CallSite  bootstrap(Lookup  lookup,  String  methodName,  MethodType  type)  {                assert  methodName  ==  'xx'                MethodHandle  mh  =  lookup.findVirtual(java.io.PrintStream,  "println",  MethodType.methodType(void,[String]))                return  new  ConstantCallSite(mh)        }        @Bytecode        static  main(args)  {                getstatic          'java/lang/System.out','Ljava/io/PrintStream;'                ldc                      'Hello  Indy'                invokedynamic  'xx',  '(Ljava/io/PrintStream;Ljava/lang/String;)V',  [H_INVOKESTATIC,  'HelloIndy','bootstrap',  [CallSite,  Lookup,  String,  MethodType]]                vreturn        }}

13年9月20日金曜日

Page 9: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

実行

9

$ groovy -Dgroovy.target.bytecode=1.7 HelloIndy.groovyHello Indy

13年9月20日金曜日

Page 10: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

310

Indy!!

13年9月20日金曜日

Page 11: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

JGGUG G*Workshop Z Copyright(C) NTT Software.

IndyIndyの動作は知ってますよね??

1113年9月20日金曜日

Page 12: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

従来のinvoke系命令のイメージ

バイトコード命令バイトコード命令Invokestatic/virtual/interface命令 メソッド

バイトコード命令return命令

バイトコード命令

(1)Java VMによって解決されて呼び出される

:

13年9月20日金曜日

Page 13: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

invokedynamic命令のイメージ

バイトコード命令バイトコード命令Invokedynamic命令 メソッド

バイトコード命令return命令

バイトコード命令

(1)Java以外の言語処理系ランタイムによって解決される

:

13年9月20日金曜日

Page 14: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

invokedynamicのイメージ(2)

バイトコード命令バイトコード命令Invokedynamic命令バイトコード命令

:

?

(2)クラスファイル上はブートストラップメソッドというメソッドに

紐付けられている

Bootstarpメソッド

バイトコード命令return命令

(1)クラスロード時には未定

13年9月20日金曜日

Page 15: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

invokedynamicのイメージ(3)

バイトコード命令バイトコード命令Invokedynamic命令バイトコード命令

:呼び出し先決定メソッド

バイトコード命令return命令

BootStrapメソッドを指すMH

(2)BootstrapメソッドはCallSiteオブジェクトを返す

CallSite呼び出し先メソッドを参照するMH

(1)BootStrapメソッドを示すMH(MethodHandle)で表現されている

13年9月20日金曜日

Page 16: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

invokedynamicのイメージ(5)

バイトコード命令バイトコード命令Invokedynamic命令バイトコード命令

:

CallSite

(1)invokedyamic命令にCallSiteが紐付けられる

この結び付きはJVMの実行を通じて以後不可逆、不変なので、以下のようにinvokedynamic

がCallSiteに置き変わると考えても良いかも知れない。 (※CallSiteオブジェクトは複数のinvokedynamic命令でシェアされ得る点でこのイメージは正確ではない。)

バイトコード命令バイトコード命令CallSite

バイトコード命令呼び出し先メソッドを参照するMH

呼び出し先メソッドを参照するMH

13年9月20日金曜日

Page 17: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

CallSite 呼び出し先メソッドを参照するMH

invokedynamicの最終形

バイトコード命令バイトコード命令

メソッドバイトコード命令return命令

バイトコード命令

MHによるメソッド参照

:

13年9月20日金曜日

Page 18: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

CallSite 呼び出し先メソッドを参照するMH

(Mutable|Volatile)CallSite

バイトコード命令バイトコード命令

バイトコード命令:

(1)

メソッド1

メソッド2

×

Groovyで言うところのmetaClassの変更のタイミングで実際のメソッドの差し替えを行なうことができるCallSiteもある。(これがキモ)

結局、「ポインタを解した呼び出し先アドレスの間接参照」をオブジェクト指向的に、型安全に行なっている。

13年9月20日金曜日

Page 19: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

Indyって結局何?メソッド呼び出し先を間接参照を使って書き換えるしくみ他言語メソッドの呼び出しが想定ユースケースだが技術的にはそれに限らない(Java 8 Lambdaとか)他言語だから、環境を持ち回したり引数をラップ・アンラップする処理が必要になる場合があるメソッド呼び出しに伴なう前後処理(MHに対する高階操作:後述)をJVM管理下で構成・実行するしくみがあることがメリット➡最適化(インライン展開)期待➡そういう前後処理が不要な場合、速度メリットがあるかは??※ 「MH呼び出しは速い」は都市伝説

1913年9月20日金曜日

Page 20: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

MHに対する高階操作

2013年9月20日金曜日

Page 21: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

321

Brainf*ck

13年9月20日金曜日

Page 22: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

Brainf*ckをindyで実装してみた :構成図

22

Brainf*ckソース>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.

compile.groovy

https://gist.github.com/uehaj/6614136https://gist.github.com/uehaj/6614447

Brainf*ckコンパイラ

生成コード(Bytecode DSL/indyを使用)

groovy

groovy“hello world”JVM

JVM(Java7)

a.groovy

13年9月20日金曜日

Page 23: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

Brainf*ckとindy静的言語だからIndyの意味ない(MutableCallSite出番なし)invokedynamicの引数文字列から一連のMHを連接させたものをBootStrapメソッドで生成してみる

23

invokedynamic 'dummy', '()V', [H_INVOKESTATIC, 'Brainfuck', 'bootstrap', [CallSite, Lookup, String, MethodType, String]], '>++++++++'

13年9月20日金曜日

Page 24: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

MHを連接MethodHandles#filterReturnValue(MH target, MH filter)「filter(target())」を表現するMHを返す。targetの戻り値の型がvoid、filterの引数が無しであれば、単にtarget, filterの順にMHを呼び出すfilterReternValueの結果をfilterReturnValueに与えることで任意個数のMHを逐次実行できる

2413年9月20日金曜日

Page 25: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

CallSite 呼び出し先メソッドを参照するMH

MHを連接

バイトコード命令バイトコード命令

メソッド(+)バイトコード命令return命令

バイトコード命令:

filterReturlValueの結果得られる、2つのMHを呼ぶMH

filterReturlValueの結果得られる、2つのMHを呼ぶMH

メソッド(+)バイトコード命令return命令

メソッド(>)バイトコード命令return命令

13年9月20日金曜日

Page 26: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

生成コード

26

 static  void  main(String[]  args)  throws  Exception  {                //  Brainfuckからコンバートされたコード                invokedynamic  'dummy',  '()V',  ...  '>+++++++++'                _GOTO                    tmp1        lab1:                invokedynamic  'dummy',  '()V',  ...  '<++++++++>-­‐'        tmp1:                getstatic          '.data','[B'                getstatic          '.dp','I'                baload                                ifne                    lab1                invokedynamic  'dummy',  '()V',  ...  '<.>+++++++'                _GOTO                    tmp2        lab2:                invokedynamic  'dummy',  '()V',  ....  '<++++>-­‐'        tmp2:                getstatic          '.data','[B'                getstatic          '.dp','I'                baload                                ifne                    lab2                invokedynamic  'dummy',  '()V',  ....  '<+.+++++++..+++.'                _GOTO                    tmp3        lab3: :

>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.

13年9月20日金曜日

Page 27: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

実行

27

% cat hello.bf >+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.

% groovy compile.groovy hello.bf > a.groovy% groovy -Dgroovy.target.bytecode=1.7 a.groovyHello World!

13年9月20日金曜日

Page 28: Indy(Invokedynamic) and Bytecode DSL and Brainf*ck

3

まとめIndyは面白いG*Magazine Vol7乞う期待!

2813年9月20日金曜日