我试图通过跟踪不同的字节码/操作码来加深对CPython解释器的理解,因为它们通过ceval.c
中的解释器循环来完成一个简单的Python程序。我使用bytecode
和opcode
来表示同样的事情。
我的python程序是这样的:
#filename: test.py
x = 2
y = 3
if x < y:
z = x
elif True:
z = y
else:
z = 100
我正在使用Python 2.7.8,并使用如下调试标志构建它:
wget https://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz # download
tar xvf Python-2.7.8.tgz # extract
cd Python2.7.8
./configure --with-pydebug # build with debug flag
make -j # parallel make
我有兴趣跟踪解释器循环for(;;)
中不同switch
的{{1}}语句的opcode
循环,从第964行开始。
我在ceval.c
循环开始后立即添加了这些行,以检查解释器是否正在运行我的文件,如果是,则打印出for
。
opcode
我得到的输出是(手动添加注释以显示来自964 for (;;) {
965 if (strcmp(filename, "../test.py") == 0) {
966 printf("%d\n", opcode);
967 }
的{{1}} opcode
):
DEFINE
我期望12个不同的操作码而不是7个,因为当我得到相同文件的字节码反汇编时,有12个字节码命令。
opcode.h
我对CPython插件工作原理的心理模型和/或我记录不同操作码的方法不正确,或两者兼而有之。你能解释为什么我从 $ ./python.exe ../test.py | cat -n
1 0 // STOP_CODE
2 90 // HAVE_ARGUMENT
3 90 // HAVE_ARGUMENT
4 101 // LOAD_NAME
5 101 // LOAD_NAME
6 101 // LOAD_NAME
7 90 // STORE_NAME
文件的输出和使用$ ./python.exe -m dis ../pytests/test.py | sed "/^$/d" | cat -n
1 1 0 LOAD_CONST 0 (2)
2 3 STORE_NAME 0 (x)
3 2 6 LOAD_CONST 1 (3)
4 9 STORE_NAME 1 (y)
5 4 12 LOAD_NAME 0 (x)
6 15 LOAD_NAME 1 (y)
7 18 COMPARE_OP 0 (<)
8 21 POP_JUMP_IF_FALSE 33
9 5 24 LOAD_NAME 0 (x)
10 27 STORE_NAME 2 (z)
11 30 JUMP_FORWARD 21 (to 54)
12 6 >> 33 LOAD_NAME
包中看到不同的操作码吗?
答案 0 :(得分:0)
您的跟踪显示有问题,因为在正常情况下您不执行STOP_CODE
(值0)将停止执行。此外,HAVE_ARGUMENT
不是操作码。对于Python 2.7,操作码是STORE_NAME
。
对于值的差异,可以在任何非直线(基本块)代码的代码中预期。而你的不是直线代码。有COMPARE <
后跟POP_JUMP_IF_FALSE
跳转。