[メモ]Python仮想マシンif文の解釈

4480 ワード

demo.pyコードは次のとおりです.
a = 1
if a > 0:
    pass
elif a == 0:
    pass
else:
    pass

test.pyコードは次のとおりです.
import dis

source = open('./demo.py').read()
co = compile(source, './demo.py', 'exec')

dis.dis(co)

出力結果は次のとおりです.
  1           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (a)

  2           6 LOAD_NAME                0 (a)
              9 LOAD_CONST               1 (0)
             12 COMPARE_OP               4 (>)
             15 POP_JUMP_IF_FALSE       21

  3          18 JUMP_FORWARD            15 (to 36)

  4     >>   21 LOAD_NAME                0 (a)
             24 LOAD_CONST               1 (0)
             27 COMPARE_OP               2 (==)
             30 POP_JUMP_IF_FALSE       36

  5          33 JUMP_FORWARD             0 (to 36)

  7     >>   36 LOAD_CONST               2 (None)
             39 RETURN_VALUE

1:スタックを押して、スタックを出て、ローカルシンボルテーブルを入れます.
2:スタックを2回押して、aと0を比較して、opは大きい号>、COMPARE_OPコードは以下の通りです.
        case COMPARE_OP:
            w = POP();
            v = TOP();
            if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) {
                /* INLINE: cmp(int, int) */
                register long a, b;
                register int res;
                a = PyInt_AS_LONG(v);
                b = PyInt_AS_LONG(w);
                switch (oparg) {
                case PyCmp_LT: res = a <  b; break;
                case PyCmp_LE: res = a <= b; break;
                case PyCmp_EQ: res = a == b; break;
                case PyCmp_NE: res = a != b; break;
                case PyCmp_GT: res = a >  b; break;
                case PyCmp_GE: res = a >= b; break;
                case PyCmp_IS: res = v == w; break;
                case PyCmp_IS_NOT: res = v != w; break;
                default: goto slow_compare;
                }
                x = res ? Py_True : Py_False;
                Py_INCREF(x);
            }
            else {
              slow_compare:
                x = cmp_outcome(oparg, v, w);
            }
            Py_DECREF(v);
            Py_DECREF(w);
            SET_TOP(x);
            if (x == NULL) break;
            PREDICT(POP_JUMP_IF_FALSE);
            PREDICT(POP_JUMP_IF_TRUE);
            continue;

スタックを2回出て、比較します.ここではPyIntタイプの比較のためにファストパスを確立しました.他はslow_を歩きます.compareチャネル、そして比較結果をスタックの上に置いて、次の文が正しいかどうかを判断します.
POP_JUMP_IF_FALSE
関連マクロの定義は次のとおりです.
#ifdef DYNAMIC_EXECUTION_PROFILE
#define PREDICT(op)             if (0) goto PRED_##op
#else
#define PREDICT(op)             if (*next_instr == op) goto PRED_##op
#endif

#define PREDICTED(op)           PRED_##op: next_instr++
#define PREDICTED_WITH_ARG(op)  PRED_##op: oparg = PEEKARG(); next_instr += 3

次のコマンドがPOPであればJUMP_IF_FALSEは、パラメータを携帯するか否かに応じてPREDICTED(op)またはPREDICTED_を選択するWITH_ARG(op).
ここでは、PREDICTED_にジャンプします.WITH_ARG(POP_JUMP_IF_FALSE);,PEEKARG()マクロ(#define PEEKARG()((next_instr[2]<<8)+next_instr[1])取得JUMP_IF_FALSEのコマンドパラメータを移動し、コマンドポインタを移動して3バイト前に移動します.ここにはまだ疑問がある.
次のコードは次のとおりです.
        PREDICTED_WITH_ARG(POP_JUMP_IF_FALSE);
        case POP_JUMP_IF_FALSE:
            w = POP();
            if (w == Py_True) {
                Py_DECREF(w);
                goto fast_next_opcode;
            }
            if (w == Py_False) {
                Py_DECREF(w);
                JUMPTO(oparg);
                goto fast_next_opcode;
            }
            err = PyObject_IsTrue(w);
            Py_DECREF(w);
            if (err > 0)
                err = 0;
            else if (err == 0)
                JUMPTO(oparg);
            else
                break;
            continue;

opargは条件が偽の場合にジャンプする命令位置であり、条件が真の場合は次の命令を実行し、そうでなければジャンプする.
疑惑を持ってさっきの疑惑を分析してみましょう.
case COMPARE_でOP:文の最後にPREDICT(POP_JUMP_IF_FALSE)を実行する;パラメータが付いているので、対応するマクロは次のとおりです.
#define PREDICTED_WITH_ARG(op)  PRED_##op: oparg = PEEKARG(); next_instr += 3
まず、どのマクロに隣接する機能に似たパラメータを持たないマクロを見てみましょう.
#define PREDICTED(op)           PRED_##op: next_instr++
この2つの間には、パラメータ指令oparg=PEEKARG()が1つ欠けています.一方,バイトコードのパラメータはいずれも2バイトであるため,命令ポインタは2バイト前に移動した.
また、現在のコマンドを実行し、next_instrは次の命令の位置を指摘するので、もう1バイト先に移動しなければなりません.
すると全部で3バイト前に移動しました.
JasonLee     2011.08.22     23:47