Acme-Perl-VM

 view release on metacpan or  search on metacpan

lib/Acme/Perl/VM/JA.pod  view on Meta::CPAN

    };


=head1 DESCRIPTION

C<Amce::Perl::VM>(APVM)はPure Perlで実装されたPerl5の仮想マシンです。

Perlディストリビューションにはコンパイルされた構文木にアクセスするためのモジュールが用意されており,B<B - The Perl Compiler>と呼ばれています。
APVMはこのBモジュールを利用して構文木を解釈・実行するモジュールです。

この文書では,Perl5の仮想マシンについて概説しつつ,APVMとPerl5実装との対応について解説します。

=head2 The Perl5 Virtual Machine

Perl5の仮想マシンはスタックマシンであり,組み込み演算子やサブルーチンなどの手続きの引数と戻り値を,スタックを通じてやり取りします。

この仮想マシンのマシンコードはopcodeと呼ばれ,これがコンパイルされたPerlプログラムの最小単位となります。opcodeはさらにデータと手続きからなるオブジェクトとして表現され,その...

opcodeオブジェクトは他のopcodeオブジェクトへのリンクを持つ木構造を成しており,このopcodeの木を構文木と呼びます。したがって,Perlプログラムを実行するというのは,opcodeを実行しつ...

ここでは以下のPerlコードを例にとり,プログラムの実行を追っていきます:

    print(10 + 20);

まず,このコードをコンパイルすると,以下のような構文木が生成されます:

    nextstate     # ステートメントの始まり
    print         # 引数リストを印字
      pushmark    # 可変長引数のためのマーク
      add         # 加算演算
        const(10) # 定数[10]
        const(20) # 定数[20]

構文木は子ノードから実行されるので,この構文木を解釈すると以下の順になります。

    nextstate
    pushmark
    const(10)
    const(20)
    add
    print

それぞれのopcodeは必要に応じてスタックから引数をポップし,戻り値をプッシュします。opcodeの実行とスタックの中身を同時に表すと以下のようになります。

    nextstate ()
    pushmark  ()       # mark = -1 (MARKについては後のセクションで解説)
    const(10) (10)     # スタックに値をPUSH
    const(20) (10, 20) # スタックに値をPUSH
    add       (30)     # 値を2つPOPし,演算結果をPUSH
    print     (1)      # mark+1からTOPまでを印字し,結果(真)をPUSH

これが構文木を解釈する基本的な流れです。プログラムの分岐やサブルーチンの呼び出しなどがあると更に複雑になりますが,一連の流れは同じです。

以下のセクションでは,仮想マシンの実装の中で特に重要なコンポーネントについて説明します。

=head2 The Perl Stack (PL_stack_base)

Perlプログラムの手続きと戻り値のために使われるスタックです。
このスタックは組み込み関数とサブルーチンの両方で使われます。

現在のperl5の実装では,このスタックはCの配列で表現され,必要に応じてC<realloc()>で拡張されます。スタックの先頭はスタックポインタ(C<PL_stack_sp>)として参照できるのですが,ppcode内で...

APVMではこれはPerlの配列で表現され,スタックポインタは配列の最後の添え字です。
ローカルコピーは作りません。

AVPMとの対応:
    perl            APVM

    PL_stack_base   @PL_stack
    PL_stack_sp     $#PL_stack
    dSP             (nothing)
    SP              $#PL_stack
    TOPs            TOP
    PUSHs(sv)       PUSH($sv)
    POPs            POP
    SPAGAIN         (nothing)
    PUTBACK         (nothing)
    EXTEND(SP, n)   (nothing)

See also F<pp.h>.

=head2 The Perl Stack Marker (PL_markstack)

可変長引数を扱うためのスタックのマーカーです。

二項演算子などは引数の数が固定ですが,C<print>のように引数の数が可変長である組み込み関数もあります。可変長引数を扱うためには,引数スタック中で引数が始まる位置を保存するå¿...

可変長引数の開始を宣言するためには,C<PUSHMARK(SP)>マクロを使います。
また,C<dMARK>マクロによりスタックから値をポップし,C<MARK>マクロを使えるようにします。

APVMとの対応:
    perl            APVM

    PUSHMARK(SP)    PUSHMARK($#PL_stack)
    TOPMARK         TOPMARK
    POPMARK         POPMARK
    dMARK           my $mark = POPMARK
    MARK++          $mark++
    *MARK           $PL_stack[$mark]

    dORIGMARK       my $origmark = $mark
    SP = ORIGMARK   $#PL_stack = $origmark

See also F<pp.h>.

=head2 The Opcode Family

Perlプログラムの最小単位である,手続きとデータを持ったオブジェクトです。
opcodeクラス群はCの構造体の先頭メンバをいくつか共有する構造体群として表現されます。

opcodeオブジェクトの持つ手続きは対応するppcodeであり,C<op_ppaddr>メンバで参照します。データはPerlの値やCの値,または他のopcodeへのリンクです。

各opcodeオブジェクトは名前と外部出力用の説明を持ち,それぞれC<OP_NAME(op)>,C<OP_DESC(op)>マクロで得ることができます。

See also F<op.h>, F<cop.h>, F<opcode.h> and F<opcode.pl>.

=head2 The PPcodes

opcodeが持つ手続きで,実際に行う処理を実装した関数です。

たとえば,OP_CONSTに対応するppcodeは以下のようになっています:

  /* in pp_hot.c (5.8.8) */
  PP(pp_const)
  {
    dSP;
    XPUSHs(cSVOP_sv);
    RETURN;
  }

マクロをいくつか展開すると以下のようになります。

  PP(pp_const)
  {
      dSP;
      EXTEND(SP, 1);
      PUSHs(cSVOPx_sv(PL_op));
      PUTBACK;
      return PL_op->next;
  }

一つひとつを順に追うと以下のようになります。

=over 4

=item C<dSP>

ローカルスタックポインタ変数(SP)を宣言し,グローバルスタックポインタの値で初期化します。

=item C<EXTEND(SP, 1)>

スタックポインタに値を1つプッシュすることを宣言します。このとき,必要に応じてスタックは拡張されます。

=item C<cSVOPx_sv(PL_op)>

現在実行中のopcodeのsvフィールドを参照します。

=item C<PUSH(sv)>

スタックポインタを通じて引数スタックに値を1つプッシュします。

=item C<PUTBACK>

ローカルスタックポインタをグローバルスタックポインタ変数に戻します。

=item C<< return PL_op->op_next >>

次に実行するopcodeを返します。

OP_CONSTであれば可能性のあるプログラムの経路は常に一つですが,OP_COND_EXPRのような制御を担うopcodeであればC<< PL_op->next >>以外のopcodeを返すことがあります。

=back

ppcodeはopcodeオブジェクトのC<op_ppaddr>メンバを通じて取得・変更することができます。このC<op_ppaddr>ã‚’C<PL_check>というコンパイル時フックテーブルを通じて変更し,プログラムの挙動を変ã...

See also F<pp.c>, F<pp_hot.c>, F<pp_ctl.c>, F<pp_sys.c>, F<pp_sort.c> and F<pp_pack.c>.

=head2 The interpreter loop (PL_runops)

構文木を解釈・実行するループの実装です。

デフォルトでは,F<run.c>にあるC<Perl_runops_standard()>が用いられます。
これは,perl 5.8.8では以下のようになっています:

    int
    Perl_runops_standard(pTHX)
    {
      while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) {
        PERL_ASYNC_CHECK();
      }

      TAINT_NOT;
      return 0;
    }

C<PL_op>は現在実行中のopcodeオブジェクトが入っているグローバル変数(またはTLS変数)です。C<op_ppaddr>はopcodeに対応したppcodeが入っており,ppcodeは次に実行するopcodeを返すことになっていま...

C<PERL_ASYNC_CHECK()>は単にセーフシグナルの処理なので実行には関係ありません。したがって,インタプリタループの実体は一行しかありません。

ところで,このようなopcodeの多態性を利用した実行ループと対極にあるのが,switch文やifの連鎖による分岐を利用した実行ループです。Perl4の実行ループはそのような実装でしたし,Perl5ã...

インタプリタループについては,APVMのC<runops_standard()>でも実装はほぼ同じです。

See also F<run.c>.

=head2 Other components

この他にもいくつか重要なコンポーネントがあります。

=over 4

=item *

The Scratchpads (PL_comppad and PL_curpad)

=item *

The Save Stack (PL_savestack)

=item *

The Temporary Value Stack (PL_tmps)



( run in 3.455 seconds using v1.01-cache-2.11-cpan-97f6503c9c8 )