[メモ]Python仮想マシンif文の解釈
4480 ワード
demo.pyコードは次のとおりです.
test.pyコードは次のとおりです.
出力結果は次のとおりです.
1:スタックを押して、スタックを出て、ローカルシンボルテーブルを入れます.
2:スタックを2回押して、aと0を比較して、opは大きい号>、COMPARE_OPコードは以下の通りです.
スタックを2回出て、比較します.ここではPyIntタイプの比較のためにファストパスを確立しました.他はslow_を歩きます.compareチャネル、そして比較結果をスタックの上に置いて、次の文が正しいかどうかを判断します.
POP_JUMP_IF_FALSE
関連マクロの定義は次のとおりです.
次のコマンドが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バイト前に移動します.ここにはまだ疑問がある.
次のコードは次のとおりです.
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
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