[前][次][番号順一覧][スレッド一覧][生データ]

yarv-dev:705

From: SASADA Koichi <ko1 atdot.net>
Date: Mon, 26 Dec 2005 07:50:43 +0900
Subject: [yarv-dev:705] fixed> Re: open> return from ensure

 ささだです。

Minero Aoki wrote:
> ensure 節から return したら SEGV しました。
> 
> ~/c/yarv % ./miniruby -e 'def m() 2 ensure return 3; end;  p m()' 
> -e:1: DBG> : "-e:1:in `<main>'"
> -- stack frame ------------
> 0x2a95ee2010 (0000): 00000001
> 0x2a95ee2018 (0001): 00000004
> 0x2a95ee2020 (0002): 00000001
> 0x2a95ee2028 (0003): 2a95ede1d8
> 0x2a95ee2030 (0004): 2a95ede1d8
> 0x2a95ee2038 (0005): 00000004
> 0x2a95ee2040 (0006): 00000001 <- lfp <- dfp
> 0x2a95ee2048 (0007): 00000005 <- bp
> -- control frame ----------
> c:0004 p:0005 s:0008 b:0007 l:0006 d:0006 METHOD i:m        s:         -  
> c:0003 p:0011 s:0004 b:0003 l:0002 d:0002 TOP    i:<main>   s:         -  
> c:0002 p:-001 s:0001 b:0001 l:0000 d:0000 FINISH i:-        s:         -  
> c:0001 p:-001 s:0000 b:-001 l:0000 d:0000 ------ i:-        s:         -  
> ---------------------------
> [BUG] Stack consistency error (sp: 0x2a95ee2050, bp: 0x2a95ee2048)
> ruby 1.9.0 (2005-11-18) [x86_64-linux]
> 
> zsh: 936 abort (core dumped)  ./miniruby -e 'def m()return 2;ensure return 3; end; p m()'
> 
 SEGV じゃなくて、rb_bug() ですね。さいごに、

> [BUG] Stack consistency error (sp: 0x2a95ee2050, bp: 0x2a95ee2048)

とあるのがわかると思います。

 で、これはいったい何か、というとそのスタックフレームから戻る前に、ス
タックの深さのチェックをしています。end 命令は 1 つ詰まれたスタックから
戻る、という命令なのですが、その際にはスタックにはただ 1 つしか詰まれて
いないといけません(深さ 1)。そうじゃないと、バグです。実際、このチェッ
クを入れて、多めに積んでいた場合、stack underflow していた場合のバグを発
見できました。多くの場合、この問題は compiler のバグです。

 で、その問題というのは対応しました。

 どういう話だったかというと、先の命令列をコンパイルすると以下のようにな
ります。

> == disasm: <ISeq:<main>@../yarv/test.rb>================================
> local scope table (size: 1, argc: 0)
> 
> 0000 methoddef        :m, <ISeq:m@../yarv/test.rb>                    (   1)
> 0003 putself
> 0004 putself
> 0005 send             :m, 0, nil, 4, <ic>
> 0011 send             :p, 1, nil, 4, <ic>
> 0017 end
> == disasm: <ISeq:m@../yarv/test.rb>=====================================
> == catch table
> | catch type: ensure st: 0000 ed: 0002 sp: 0002 cont: 0005
> == disasm: <ISeq:ensure in m@../yarv/test.rb>===========================
> local scope table (size: 1, argc: 0)
> [ 1] __$!
> 0000 putobject        3                                               (   1)
> 0002 throw            1
> 0004 getdynamic       1, 0
> 0007 throw            0
> |------------------------------------------------------------------------
> local scope table (size: 1, argc: 0)
> 
> 0000 putobject        2                                               (   1)
> 0002 putobject        3
> 0004 end
> 0005 end

 メソッド m() の定義は

> 0000 putobject        2
> 0002 putobject        3
> 0004 end
> 0005 end

 にあるとおりですが、putobject を2回呼び出してスタックの深さが 2 になっ
ていたところで end 命令を呼んでいます。スタックの深さが 2 なので、stack
consistency error になります。

> [BUG] Stack consistency error (sp: 0x2a95ee2050, bp: 0x2a95ee2048)

 という表記を見てみると、sp が bp (base pointer) よりも 16 (... そう
か、64 bit machine  か)と、2 word 多いことがわかり、深さが 1 じゃない、
とそういっているわけです。

 で、根本的な原因は、

* YARV では method 中の return 文は end 命令へジャンプするだけ

 ということでした。ensure 節をコンパイル中に、その直前でいくつスタック
が詰まれているかわからない、というのが問題です。

 で、解決策としては、ensure 節(メソッド中)で return があった場合、
emptstack という命令を用意して、そいつでスタックを空にして、end 命令を実
行することにしました。

> 0000 putobject        2
> 0002 emptstack
> 0003 putobject        3
> 0005 end
> 0006 end

 解決策の他の選択肢としては、

a) TAG_hoge を新しく用意する
b) stack consistency を気にしない end 命令を用意する (*)

などがあったのですが、もしかしたら今後役に立つかもしれない、と思って
emptstack 命令を用意しました。

(*) ちなみに、最適化オプションを有効 (vm_opts.h) にするとこのチェックは
なくなります。あくまでデバッグ用の機能だから。

 で、よくよく考えてみると、

      def m1 *args

      end
      def m2
        m1(:a, :b, (return 1; :c))
      end
      m2

というのも同じようにエラーになるため、ensure 関係ありませんでした。

 で、毎回 emptstack を挿入しようとしたら、

def m
  return 1
  p 2
end

 こういうのでも同様に emptstack をはさむことになってしまったので、無駄
な命令(return の前には stack は空です)なので、コンパイル時に nop に変
更するようにしたんですが、結局 nop が残ることになってしまいました(ス
タックの深さがコンパイル時に確定する段階では、もう命令の削除ができない。
命令の番地が決まってしまうため)。やっぱり、return 用の命令を考えたほう
がよかったような気がします。まぁ、nop くらいいいか、という気もするんですが。

 ちなみに、私はファイルに書いてデバッグするので、miniruby -e の形ではな
く、複数行で書いてもらえるとうれしいです。

-- 
// SASADA Koichi at atdot dot net


-- 
ML: yarv-dev quickml.atdot.net
使い方: http://www.atdot.net/~ko1/quickml

[前][次][番号順一覧][スレッド一覧][生データ]

       702 2005-12-26 02:29 [aamine loveruby.net ] open> return from ensure                
->     705 2005-12-26 07:50 ┗[ko1 atdot.net       ] fixed> Re: open> return from ensure   
       715 2005-12-27 15:48  ┗[aamine loveruby.net ] close> Re: return from ensure