ruby-trunk-changes r61536 - r61570

今日は get_insn_info() の実装を切り替えできるようにするリファクタリングや、いくつかの関数で前提が不十分で未初期化領域や解放済み領域へのアクセスの可能性があった不具合修正が多くありました。

mame: r61536 2018-01-01 21:51:21 +0900

r61534 の struct rb_iseq_constant_body の構造体のリファクタリングの続きで、今度は struct iseq_insn_info_entry の position というメンバーを、その上位の struct iseq_insn_info の positions という配列に釣り上げています。たぶん body->position だけ同時に舐めることが多いのでキャッシュに同時に乗りやすいようにまとめようとしているんだと思います。コミットログによると、さらに get_insn_info() で位置情報を検索する時に2分探索でなく succinct bitvector という構造を利用しようかという構想があるそうで、その時のための予備的な変更でもあるようです。これは TracePoint のための trace 命令をなくした時に trace 版の命令で位置情報を得るのに長い ISeq では時間がかかるようになったので、それを改善するために導入しようかというものですね。

mame: r61537 2018-01-01 22:18:55 +0900

get_insn_info() の実装を VM_INSN_INFO_TABLE_IMPL というマクロで切り替えできるようにしています。 0 だと線形探索、1 で2分探索を利用するようにしています。ここで切り替え可能なかたちでアルゴリズムを追加できるようにしておこうということですね。

nobu: r61538 2018-01-02 12:00:13 +0900

r61528 の parser_yyerror() でのエラー箇所の終端の計算を修正しています。

svn: r61539 2018-01-02 12:00:15 +0900

version.h の日付更新。

nobu: r61540 2018-01-02 13:24:32 +0900

parse.y の singleton のパースエラーメッセージの typo 修正と、エラー箇所の指定に使う位置情報のトークンを変更しています。

shyouhei: r61541 2018-01-02 15:41:39 +0900

cont_capture() において VAR_INITIALIZED() マクロの展開によって変数に volatile 修飾子が重複して指定されているため clang で警告になるらしく、これを黙らせるため __clang__ の時に #pragma でこの警告を無視させるようにしています。

shyouhei: r61542 2018-01-02 15:41:40 +0900

fprint(3) や rb_bug() などの printf 系のフォーマット文字列の指示子 %p に与える引数を void* にキャストする変更を入れています。

shyouhei: r61543 2018-01-02 15:41:41 +0900

compile.c の insn_data_to_s_detail() で dladdr(3) に渡すポインタを rb_insn_func_t の型にキャストしていましたが、どうせ dladdr() の引数の型は const void * なので最初から void * 型の変数にキャストしておくようにしています。

shyouhei: r61544 2018-01-02 15:41:42 +0900

lib/mkmf.rb で生成するコードでフォーマット指示子 %p の引数に不正な関数ポインタ型を渡すようになっていたところがあったので、関数を呼んだ結果を %d で表示するような式に変更しています。実際に実行されるわけではなくて、渡されたシンボルが最適化で消えないようにするためにするためのトリックの式だと思います。

shyouhei: r61545 2018-01-02 15:41:43 +0900

vm_dump.c で snprintf(3) に %zx というフォーマット指示子とサイズ指示子を直接使っていたところを PRIxSIZE マクロを使うようにしています。%z は C99 で定義されたものとのことで消しているようです。 #undef zx というのは消し忘れかな?

shyouhei: r61546 2018-01-02 15:41:43 +0900

C89 までの標準規格では、C の文字列リテラルのサイズは 509 文字までしか保証されていないそうで、それ以上は使えないコンパイラが存在する可能性がある(実際にあるかどうかはわかりませんが)ので、RUBY_ASSERT() マクロは使えないとのことで、assert(3) を再定義せずにそのまま使うようにしています。

shyouhei: r61547 2018-01-02 15:41:44 +0900

nlz_intptr() という inline 関数で SIZEOF_VOIDP == 8 の時に nlz_long_long() を利用するように実装されていましたが、厳密には 8 bytes == long long とは限らないので SIZEOF_UINTPTR_T と各種整数型の SIZEOF_XXX を比較して分岐するようにしています。

shyouhei: r61548 2018-01-02 15:41:45 +0900

関数マクロで可変長引数を扱う __VA_ARGS__ は C99 からの機能なので HAVE_VA_ARGS_MACRO というマクロをチェックして使えるときだけ利用するようにしています。

shyouhei: r61549 2018-01-02 15:41:46 +0900

vm_insnhelper.h で enum の最後の要素の後にカンマを使っているのも C99 から許容されるようになった記法なので削っています。これもういいのかなーと思ってたけどやっぱりダメなのか。

shyouhei: r61550 2018-01-02 15:41:47 +0900

なんと long long という型がそもそも C99 で定義されたものとのことで HAVE_LONG_LONG でチェックして利用するようにしたり、ULL2NUM を SIZET2NUM で代替するようにしたりしています。

shyouhei: r61551 2018-01-02 15:41:48 +0900

拡張ライブラリ bigdecimal で構造体の最後に配列を置いて可変長のサイズの配列として扱う方法で、配列サイズを省略した記法が C99 以降でのみ利用可能なので __STDC_VERSION__ をチェックしてこれを利用し、それ以前のコンパイラではサイズに 0 または 1 を明示的に指定するようにしています。

shyouhei: r61552 2018-01-02 15:41:49 +0900

r61546 と同様に C89 では文字列リテラルの文字数上限が 509 文字なので、r59637 でtemplate/insns_info.inc.tmpl の insn_name_info を1つの長い文字列リテラルで埋め込んでいたところを、__STDC_VERSION__ をチェックして古いコンパイラでは普通の文字列の配列として定義するように戻しています。

shyouhei: r61553 2018-01-02 15:41:49 +0900

構造体の初期化子の中でかっこを使う {("str")} のような記法も C99 以降で使えるものらしく(かっこで括ると定数とはみなされないようで、また古い規格では定数でないと初期化子に使えないため)、regenc.h の POSIX_BRACKET_ENTRY_INIT() というマクロでかっこでくくっていたところをくくらないようにしています。

shyouhei: r61554 2018-01-02 15:41:50 +0900

int (signed, unsigned は含む)以外の型の bit field は C90 では許容されていないそうなので、一部別の型つきで bit field を定義していたところは、専用の型をマクロ定義して、__STDC_VERSION__ をチェックして古いコンパイラでは unsigned int などで代替するようにしています。

shyouhei: r61555 2018-01-02 15:41:51 +0900

STATIC_ASSERT() の実装として _Static_assert() というのを利用するのを GCC のバージョンチェックで分岐していましたが、これは C11 で定義されている標準規格だそうなので、__STDC_VERSION__ のチェックで利用するようにしています。

shyouhei: r61556 2018-01-02 15:41:52 +0900

enum の値を明示的に定義する時に、その範囲が int で表現できる範囲を超えるのは GCC拡張機能とのことで VM_FRAME_MAGIC_RESCUE、VM_FRAME_MAGIC_DUMMY、VM_FRAME_MAGIC_MASK などの定数の値を最上位ビットを落としておくようにしています。

shyouhei: r61557 2018-01-02 15:41:53 +0900

Data_Make_Struct() や TypedData_Make_Struct() などのマクロ定義でも利用されていましたが、複文ブロックをかっこで括って式として扱うのは GCC拡張機能だったそうで、この差異を吸収するため RB_GNUC_EXTENSION_BLOCK というマクロが定義済みなのでこれを使うようにしています。

shyouhei: r61558 2018-01-02 15:41:54 +0900

vm_exec.h および regexec.c で goto 文のジャンプ先のラベルを変数などを使うのも GCC 拡張? ちょっと違うかも。これも RB_GNUC_EXTENSION_BLOCK() マクロなどを使うようにしていますが、gcc 以外の時に展開されてもそのまま残るので、ジャンプすること自体は問題ないのかも。

shyouhei: r61559 2018-01-02 15:41:55 +0900

dlsym(3) で得た関数ポインタを void * のポインタと比較するのも GCC 拡張とのことで、古い ruby バージョン向けの拡張ライブラリがロードされたのを ruby_xmalloc のシンボルの重複で検出するロジックのところを xmalloc_mismatch_p() という関数に切り出しています。しかし代替の方法もないので __clang__ や __GNUC__ の時は #pragma を使って警告を抑制するのみに留めています。

shyouhei: r61560 2018-01-02 15:41:56 +0900

offsetof() マクロで構造体のネストしたメンバーのオフセットを得るのは gcc 拡張の機能らしく clang では警告が出るそうなので autoload 用のロック管理用の struct autoload_state という構造体で union を使って共用していたメンバー node と head をフラットに展開するようにしています。

shyouhei: r61561 2018-01-02 15:41:57 +0900

Array#sample の実装でサンプリング数が memoize 法を使う範囲だった時に生成される内部的オブジェクトの Data_Wrap_Struct() に渡す free 関数のポインタの引数の順番が間違ってて解放漏れが起きていたのを修正しています。コンパイラの警告が出ていたようです。

shyouhei: r61562 2018-01-02 15:41:58 +0900

variable.c の rb_class_path_cached() で引数の klass の T_CLASS 型オブジェクトの rb_classext_t のポインタが NULL の場合に SEGV するようになっていたので、明示的にチェックするようにしています。 [ruby-dev:50406] [Bug #14269]

shyouhei: r61563 2018-01-02 15:41:59 +0900

次は gc.c の rb_raw_obj_info() で me->def が NULL の時にも参照してしまうようになっていたので、InitVM_Object() から呼ばれた時に SEGV してしまう可能性があったので、チェックしてその時は method_type_name() での文字列化をあきらめるようにしています。 [ruby-dev:50407] [Bug #14270]

shyouhei: r61564 2018-01-02 15:42:00 +0900

次は gc.c の rb_raw_iseq_info() で同様に iseq->body が NULL の時に SEGV するようになっていたのでチェックしてアクセスを抑制するようにしています。 [ruby-dev:50407] [Bug #14270]

shyouhei: r61565 2018-01-02 15:42:01 +0900

次は gc.c の rb_raw_iseq_info() で行番号を FIX2INT に渡す前に 0 の可能性があるので(Fixnum なら最下位ビットが立っているはず)その時は 0 にするようにしているのと、vm_core.h の vm_block_iseq() で引数の block が NULL の場合に SEGV の恐れがあったのでそれも修正しています。コメントでは後者しか触れてないのでなんとなく分けるつもりで1つのコミットに混ざったっぽいですね。 [ruby-dev:50407] [Bug #14270]

shyouhei: r61566 2018-01-02 15:42:02 +0900

gc.c の rb_raw_obj_info() の T_IMEMO 型の時の imemo_name という変数が未初期化のままアクセスされる可能性があったので空文字列で初期化しておくようにしています。 [ruby-dev:50406] [Bug #14269]

shyouhei: r61567 2018-01-02 15:42:03 +0900

eval_error.c の rb_ec_error_print() において errat という変数が未初期化のままアクセスされる恐れがあったという警告があったので初期化を追加しています。

shyouhei: r61568 2018-01-02 15:42:04 +0900

gc.c の gc_page_sweep() で既に回収済みの RVALUE スロットについて obj_info() を呼ぶ可能性があり、解放済みのメモリ領域へのアクセスの可能性があったので、obj_info() の呼び出しはあきらめて %p でポインタの表示のみに留めるようにしています。 [ruby-dev:50406] [Bug #14269]

shyouhei: r61569 2018-01-02 15:42:05 +0900

gc_writebarrier_incremental() でも r61568 と同様に解放済みの RVALUE スロットに対して obj_info() を呼ぶ恐れがあったので %p でポインタの表示に留めるようにしています。 [ruby-dev:50406] [Bug #14269]

shyouhei: r61570 2018-01-02 15:46:40 +0900

dln.c に #include "internal.h" を追加しています。