Python/バイトコード生成を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Pythonを読む]]
#contents
*compiler_mod (Python/compile.c) [#x6f2de9f]
さて、シンボルテーブル作成が予想以上に長かったですがとも...
#code(C){{
/*
* This file compiles an abstract syntax tree (AST) into ...
*
* The primary entry point is PyAST_Compile(), which retu...
* PyCodeObject. The compiler makes several passes to bu...
* object:
* 1. Checks for future statements. See future.c
* 2. Builds a symbol table. See symtable.c.
* 3. Generate code for basic blocks. See compiler_mod...
* 4. Assemble the basic blocks into final code. See a...
* 5. Optimize the byte code (peephole optimizations). ...
*/
}}
2.まで終わって、次は3.のcompiler_modです。(一部省略して...
#code(C){{
static PyCodeObject *
compiler_mod(struct compiler *c, mod_ty mod)
{
PyCodeObject *co;
int addNone = 1;
static PyObject *module;
if (!module) {
module = PyUnicode_InternFromString("<module>");
}
/* Use 0 for firstlineno initially, will fixup in ass...
if (!compiler_enter_scope(c, module, COMPILER_SCOPE_M...
return NULL;
switch (mod->kind) {
case Module_kind:
if (!compiler_body(c, mod->v.Module.body)) {
compiler_exit_scope(c);
return 0;
}
break;
case Interactive_kind:
c->c_interactive = 1;
VISIT_SEQ_IN_SCOPE(c, stmt,
mod->v.Interactive.body);
break;
case Expression_kind:
VISIT_IN_SCOPE(c, expr, mod->v.Expression.body);
addNone = 0;
break;
}
co = assemble(c, addNone);
compiler_exit_scope(c);
return co;
}
}}
compiler_enter_scopeは長いので代わりにcompiler構造体の上...
#code(C){{
/* This struct captures the global state of a compilation.
The u pointer points to the current compilation unit, whi...
for enclosing blocks are stored in c_stack. The u and c_s...
managed by compiler_enter_scope() and compiler_exit_scope...
*/
}}
名前の通り、スコープへのインとアウトを行っています。その...
さてというわけでcompiler_enter_scopeはさらっと流して次、V...
#code(C){{
#define VISIT_SEQ_IN_SCOPE(C, TYPE, SEQ) { \
int _i; \
asdl_seq *seq = (SEQ); /* avoid variable capture */ \
for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \
TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, ...
if (!compiler_visit_ ## TYPE((C), elt)) { \
compiler_exit_scope(c); \
return 0; \
} \
} \
}
}}
というわけでcompiler_visit_stmtに進みます。
*compiler_visit_stmt [#h88941dd]
compiler_visit_stmtはシンボルテーブル作成と同じでswitch文...
Expr
ListComp
Name(n) Load
Comprephension
Name(n) Store
Call
Name(Range) Load
Num(10)
Compare
BinOp
Mult
Name(n) Load
Name(n) Load
Gt
Num(10)
Exprです。対応部分を見ると、
#code(C){{
case Expr_kind:
if (c->c_interactive && c->c_nestlevel <= 1) {
VISIT(c, expr, s->v.Expr.value);
ADDOP(c, PRINT_EXPR);
}
else if (s->v.Expr.value->kind != Str_kind &&
s->v.Expr.value->kind != Num_kind) {
VISIT(c, expr, s->v.Expr.value);
ADDOP(c, POP_TOP);
}
break;
}}
interactiveでnestlevelも1なのでExprをたどった後にPRINT_EX...
ADDOPを先に見ておきます。
#code(C){{
#define ADDOP(C, OP) { \
if (!compiler_addop((C), (OP))) \
return 0; \
}
static int
compiler_addop(struct compiler *c, int opcode)
{
basicblock *b;
struct instr *i;
int off;
off = compiler_next_instr(c, c->u->u_curblock);
if (off < 0)
return 0;
b = c->u->u_curblock;
i = &b->b_instr[off];
i->i_opcode = opcode;
i->i_hasarg = 0;
if (opcode == RETURN_VALUE)
b->b_return = 1;
compiler_set_lineno(c, off);
return 1;
}
}}
basicblockはASTをたどった結果作られる命令列を格納する構造...
instrは紛らわしいですが、instructionの略で1命令を表す構造...
*compiler_comprehension [#hbd6d8c0]
さて、compiler_visit_exprに進みます。こちらもcompiler_vis...
#code(C){{
static int
compiler_comprehension(struct compiler *c, expr_ty e, int...
identifier name, asdl_seq *generat...
expr_ty val)
{
PyCodeObject *co = NULL;
expr_ty outermost_iter;
PyObject *qualname = NULL;
outermost_iter = ((comprehension_ty)
asdl_seq_GET(generators, 0))->iter;
if (!compiler_enter_scope(c, name, COMPILER_SCOPE_COM...
(void *)e, e->lineno))
goto error;
if (type != COMP_GENEXP) {
int op;
switch (type) {
case COMP_LISTCOMP:
op = BUILD_LIST;
break;
// 他省略
}
ADDOP_I(c, op, 0);
}
if (!compiler_comprehension_generator(c, generators, ...
goto error_in_scope;
if (type != COMP_GENEXP) {
ADDOP(c, RETURN_VALUE);
}
co = assemble(c, 1);
qualname = c->u->u_qualname;
Py_INCREF(qualname);
compiler_exit_scope(c);
if (!compiler_make_closure(c, co, 0, qualname))
goto error;
Py_DECREF(qualname);
Py_DECREF(co);
VISIT(c, expr, outermost_iter);
ADDOP(c, GET_ITER);
ADDOP_I(c, CALL_FUNCTION, 1);
return 1;
error_in_scope:
compiler_exit_scope(c);
error:
Py_XDECREF(qualname);
Py_XDECREF(co);
return 0;
}
}}
新しいスコープを作っているのは予想通りです。その後は少し...
+ADDOP(BUILD_LIST)
+compiler_comprehension_generator呼び出し
+ADDOP(RETURN_VALUE)
この後にcompiler_exit_scopeがあるのでスコープが戻ります。...
+compiler_make_closure呼び出し
+VISIT(outermost_iter)
+ADDOP(GET_ITER)
+ADDOP(CALL_FUNCTION)
を行っています。これらは元々のスコープに対して行われます。
**compiler_comprehension_generator [#yfe4fb89]
まずはcompiler_comprehension_generatorを見てみましょう。
#code(C){{
static int
compiler_comprehension_generator(struct compiler *c,
asdl_seq *generators, in...
expr_ty elt, expr_ty val...
{
/* generate code for the iterator, then each of the i...
and then write to the element */
comprehension_ty gen;
basicblock *start, *anchor, *skip, *if_cleanup;
Py_ssize_t i, n;
start = compiler_new_block(c);
skip = compiler_new_block(c);
if_cleanup = compiler_new_block(c);
anchor = compiler_new_block(c);
gen = (comprehension_ty)asdl_seq_GET(generators, gen_...
if (gen_index == 0) {
/* Receive outermost iter as an implicit argument...
c->u->u_argcount = 1;
ADDOP_I(c, LOAD_FAST, 0);
}
else {
/* Sub-iter - calculate on the fly */
VISIT(c, expr, gen->iter);
ADDOP(c, GET_ITER);
}
compiler_use_next_block(c, start);
ADDOP_JREL(c, FOR_ITER, anchor);
NEXT_BLOCK(c);
VISIT(c, expr, gen->target);
/* XXX this needs to be cleaned up...a lot! */
n = asdl_seq_LEN(gen->ifs);
for (i = 0; i < n; i++) {
expr_ty e = (expr_ty)asdl_seq_GET(gen->ifs, i);
VISIT(c, expr, e);
ADDOP_JABS(c, POP_JUMP_IF_FALSE, if_cleanup);
NEXT_BLOCK(c);
}
if (++gen_index < asdl_seq_LEN(generators))
if (!compiler_comprehension_generator(c,
generators,...
elt, val, t...
return 0;
/* only append after the last for generator */
if (gen_index >= asdl_seq_LEN(generators)) {
/* comprehension specific code */
switch (type) {
case COMP_LISTCOMP:
VISIT(c, expr, elt);
ADDOP_I(c, LIST_APPEND, gen_index + 1);
break;
// 他省略
}
compiler_use_next_block(c, skip);
}
compiler_use_next_block(c, if_cleanup);
ADDOP_JABS(c, JUMP_ABSOLUTE, start);
compiler_use_next_block(c, anchor);
return 1;
}
}}
急にいろいろ出てきました。ひとつずつ確認していきましょう。
まず先頭、start, skip, if_cleanup, anchorというblockが作...
#code(C){{
compiler_use_next_block(c, start);
ADDOP_JREL(c, FOR_ITER, anchor);
NEXT_BLOCK(c);
VISIT(c, expr, gen->target);
}}
という記述から、ジャンプの飛び先のラベルとして使われてい...
さて、target、今回の場合はName(n)、compiler_visit_exprを...
**compiler_visit_nameop [#z0adc0fc]
compiler_visit_nameopはスコープによってどのように参照する...
#code(C){{
ADDOP_O(c, op, mangled, varnames);
}}
ADDOP_Oを追っかけましょう。
#code(C){{
#define ADDOP_O(C, OP, O, TYPE) { \
if (!compiler_addop_o((C), (OP), (C)->u->u_ ## TYPE, ...
return 0; \
}
}}
OとTYPEがひっくり返るのが地味にめんどくさいです。
#code(C){{
static int
compiler_addop_o(struct compiler *c, int opcode, PyObject...
{
Py_ssize_t arg = compiler_add_o(c, dict, o);
if (arg < 0)
return 0;
return compiler_addop_i(c, opcode, arg);
}
static Py_ssize_t
compiler_add_o(struct compiler *c, PyObject *dict, PyObje...
{
PyObject *t, *v;
Py_ssize_t arg;
double d;
/* necessary to make sure types aren't coerced (e.g.,...
/* _and_ to distinguish 0.0 from -0.0 e.g. on IEEE pl...
if (PyFloat_Check(o)) {
// 省略
}
else if (PyComplex_Check(o)) {
// 省略
}
else {
t = PyTuple_Pack(2, o, o->ob_type);
}
v = PyDict_GetItem(dict, t);
if (!v) {
arg = PyDict_Size(dict);
v = PyLong_FromSsize_t(arg);
if (PyDict_SetItem(dict, t, v) < 0) {
Py_DECREF(t);
Py_DECREF(v);
return -1;
}
Py_DECREF(v);
}
else
arg = PyLong_AsLong(v);
Py_DECREF(t);
return arg;
}
}}
nという名前から0番目というインデックスに置き換えられ、opc...
**compiler_comprehension_generatorに戻る [#nbe985a6]
さて、compiler_comprehension_generatorに戻りましょう。
#code(C){{
/* XXX this needs to be cleaned up...a lot! */
n = asdl_seq_LEN(gen->ifs);
for (i = 0; i < n; i++) {
expr_ty e = (expr_ty)asdl_seq_GET(gen->ifs, i);
VISIT(c, expr, e);
ADDOP_JABS(c, POP_JUMP_IF_FALSE, if_cleanup);
NEXT_BLOCK(c);
}
if (++gen_index < asdl_seq_LEN(generators))
if (!compiler_comprehension_generator(c,
generators,...
elt, val, t...
return 0;
/* only append after the last for generator */
if (gen_index >= asdl_seq_LEN(generators)) {
/* comprehension specific code */
switch (type) {
case COMP_LISTCOMP:
VISIT(c, expr, elt);
ADDOP_I(c, LIST_APPEND, gen_index + 1);
break;
// 他省略
}
compiler_use_next_block(c, skip);
}
compiler_use_next_block(c, if_cleanup);
ADDOP_JABS(c, JUMP_ABSOLUTE, start);
compiler_use_next_block(c, anchor);
return 1;
}}
まず、ifsの処理をします。今回の場合、以下のASTです。
Compare
BinOp
Mult
Name(n) Load
Name(n) Load
Gt
Num(10)
対応するompile_compareは「0 < x < 10」のように書けるのに...
#code(C){{
static int
compiler_compare(struct compiler *c, expr_ty e)
{
Py_ssize_t i, n;
VISIT(c, expr, e->v.Compare.left);
n = asdl_seq_LEN(e->v.Compare.ops);
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.com...
ADDOP_I(c, COMPARE_OP, cmpop((cmpop_ty)(asdl_seq_GET(...
return 1;
}
}}
左辺を処理して、右辺を処理して、比較を行うということが想...
LOAD_FAST 0(n)
LOAD_FAST 0(n)
BINARY_MULTIPLY
LOAD_CONST 10
COMPARE_OP >
0(n)というのはバイトコードの引数としては0が設定される、そ...
ifsの処理が終わって、最終的にcompiler_comprehensionの初め...
BUILD_LIST 0
LOAD_FAST 0
:start
FOR_ITER REL:anchor
:
STORE_FAST 0(n)
LOAD_FAST 0(n)
LOAD_FAST 0(n)
BINARY_MULTIPLY
LOAD_CONST 10
COMPARE_OP >
POP_JUMP_IF_FALSE ABS:if_cleanup
:
LOAD_FAST 0(n)
LIST_APPEND 2
:skip
:if_cleanup
JUMP_ABSOLUTE ABS:start
:anchor
RETURN_VALUE
RELとかABSとか書いてあるのはジャンプです。また、:startな...
**compiler_comprehensionに戻る [#ge4f6aa3]
さてだいぶ長くなってきましたがまだ続きます。compiler_comp...
#code(C){{
if (!compiler_make_closure(c, co, 0, qualname))
goto error;
Py_DECREF(qualname);
Py_DECREF(co);
VISIT(c, expr, outermost_iter);
ADDOP(c, GET_ITER);
ADDOP_I(c, CALL_FUNCTION, 1);
}}
まずはcompiler_make_closure、ブロックの外の変数の取り込み...
#code(C){{
static int
compiler_make_closure(struct compiler *c, PyCodeObject *c...
{
Py_ssize_t i, free = PyCode_GetNumFree(co);
if (qualname == NULL)
qualname = co->co_name;
if (free == 0) {
ADDOP_O(c, LOAD_CONST, (PyObject*)co, consts);
ADDOP_O(c, LOAD_CONST, qualname, consts);
ADDOP_I(c, MAKE_FUNCTION, args);
return 1;
}
}
}}
次にoutermost_iterを処理、対応するASTは以下です。
Call
Name(Range) Load
Num(10)
プログラミングの華、関数呼び出しです。対応するのはcompile...
#code(C){{
compiler_call(struct compiler *c, expr_ty e)
{
VISIT(c, expr, e->v.Call.func);
return compiler_call_helper(c, 0, e->v.Call.args, e->...
}
}}
compiler_call_helperはキーワード変数など様々な呼び出しの...
最終的に、compiler_comprehensionが呼ばれて出来上がったバ...
LOAD_CONST 内包表記のコード
LOAD_CONST <listcomp>
MAKE_FUNCTION 0
LOAD_NAME 0(range)
LOAD_CONST 10
CALL_FUNCTION 1
GET_ITER
CALL_FUNCTION 1
*assemble [#xa027eaf]
最後に、飛ばしたassembleを見ます。assembleは今までに2回出...
-トップレベルに対するassemble
-内包表記のスコープに対するassemble
このことからスコープに対して最終的なバイトコードを作って...
assembleのコードは次の通り(一部省略)
#code(C){{
static PyCodeObject *
assemble(struct compiler *c, int addNone)
{
basicblock *b, *entryblock;
struct assembler a;
int i, j, nblocks;
PyCodeObject *co = NULL;
nblocks = 0;
entryblock = NULL;
for (b = c->u->u_blocks; b != NULL; b = b->b_list) {
nblocks++;
entryblock = b;
}
if (!assemble_init(&a, nblocks, c->u->u_firstlineno))
goto error;
dfs(c, entryblock, &a);
/* Can't modify the bytecode after computing jump off...
assemble_jump_offsets(&a, c);
/* Emit code in reverse postorder from dfs. */
for (i = a.a_nblocks - 1; i >= 0; i--) {
b = a.a_postorder[i];
for (j = 0; j < b->b_iused; j++)
if (!assemble_emit(&a, &b->b_instr[j]))
goto error;
}
if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0)
goto error;
if (_PyBytes_Resize(&a.a_bytecode, a.a_offset) < 0)
goto error;
co = makecode(c, &a);
error:
assemble_free(&a);
return co;
}
}}
初期化を除くと初めに呼ばれるのはdfs、depth-first searchで...
#code(C){{
static void
dfs(struct compiler *c, basicblock *b, struct assembler *a)
{
int i;
struct instr *instr = NULL;
if (b->b_seen)
return;
b->b_seen = 1;
if (b->b_next != NULL)
dfs(c, b->b_next, a);
for (i = 0; i < b->b_iused; i++) {
instr = &b->b_instr[i];
if (instr->i_jrel || instr->i_jabs)
dfs(c, instr->i_target, a);
}
a->a_postorder[a->a_nblocks++] = b;
}
}}
blockを次がある間再帰、次にinstrを見てジャンプが行われて...
assembleに戻って次に呼んでいるのはassemble_jump_offsets、...
続いて個々のinstrに対してassemble_emitを呼び出しています...
最後にmakecode。簡単な最適化を行ったうえで最終的なPyCode...
終了行:
[[Pythonを読む]]
#contents
*compiler_mod (Python/compile.c) [#x6f2de9f]
さて、シンボルテーブル作成が予想以上に長かったですがとも...
#code(C){{
/*
* This file compiles an abstract syntax tree (AST) into ...
*
* The primary entry point is PyAST_Compile(), which retu...
* PyCodeObject. The compiler makes several passes to bu...
* object:
* 1. Checks for future statements. See future.c
* 2. Builds a symbol table. See symtable.c.
* 3. Generate code for basic blocks. See compiler_mod...
* 4. Assemble the basic blocks into final code. See a...
* 5. Optimize the byte code (peephole optimizations). ...
*/
}}
2.まで終わって、次は3.のcompiler_modです。(一部省略して...
#code(C){{
static PyCodeObject *
compiler_mod(struct compiler *c, mod_ty mod)
{
PyCodeObject *co;
int addNone = 1;
static PyObject *module;
if (!module) {
module = PyUnicode_InternFromString("<module>");
}
/* Use 0 for firstlineno initially, will fixup in ass...
if (!compiler_enter_scope(c, module, COMPILER_SCOPE_M...
return NULL;
switch (mod->kind) {
case Module_kind:
if (!compiler_body(c, mod->v.Module.body)) {
compiler_exit_scope(c);
return 0;
}
break;
case Interactive_kind:
c->c_interactive = 1;
VISIT_SEQ_IN_SCOPE(c, stmt,
mod->v.Interactive.body);
break;
case Expression_kind:
VISIT_IN_SCOPE(c, expr, mod->v.Expression.body);
addNone = 0;
break;
}
co = assemble(c, addNone);
compiler_exit_scope(c);
return co;
}
}}
compiler_enter_scopeは長いので代わりにcompiler構造体の上...
#code(C){{
/* This struct captures the global state of a compilation.
The u pointer points to the current compilation unit, whi...
for enclosing blocks are stored in c_stack. The u and c_s...
managed by compiler_enter_scope() and compiler_exit_scope...
*/
}}
名前の通り、スコープへのインとアウトを行っています。その...
さてというわけでcompiler_enter_scopeはさらっと流して次、V...
#code(C){{
#define VISIT_SEQ_IN_SCOPE(C, TYPE, SEQ) { \
int _i; \
asdl_seq *seq = (SEQ); /* avoid variable capture */ \
for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \
TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, ...
if (!compiler_visit_ ## TYPE((C), elt)) { \
compiler_exit_scope(c); \
return 0; \
} \
} \
}
}}
というわけでcompiler_visit_stmtに進みます。
*compiler_visit_stmt [#h88941dd]
compiler_visit_stmtはシンボルテーブル作成と同じでswitch文...
Expr
ListComp
Name(n) Load
Comprephension
Name(n) Store
Call
Name(Range) Load
Num(10)
Compare
BinOp
Mult
Name(n) Load
Name(n) Load
Gt
Num(10)
Exprです。対応部分を見ると、
#code(C){{
case Expr_kind:
if (c->c_interactive && c->c_nestlevel <= 1) {
VISIT(c, expr, s->v.Expr.value);
ADDOP(c, PRINT_EXPR);
}
else if (s->v.Expr.value->kind != Str_kind &&
s->v.Expr.value->kind != Num_kind) {
VISIT(c, expr, s->v.Expr.value);
ADDOP(c, POP_TOP);
}
break;
}}
interactiveでnestlevelも1なのでExprをたどった後にPRINT_EX...
ADDOPを先に見ておきます。
#code(C){{
#define ADDOP(C, OP) { \
if (!compiler_addop((C), (OP))) \
return 0; \
}
static int
compiler_addop(struct compiler *c, int opcode)
{
basicblock *b;
struct instr *i;
int off;
off = compiler_next_instr(c, c->u->u_curblock);
if (off < 0)
return 0;
b = c->u->u_curblock;
i = &b->b_instr[off];
i->i_opcode = opcode;
i->i_hasarg = 0;
if (opcode == RETURN_VALUE)
b->b_return = 1;
compiler_set_lineno(c, off);
return 1;
}
}}
basicblockはASTをたどった結果作られる命令列を格納する構造...
instrは紛らわしいですが、instructionの略で1命令を表す構造...
*compiler_comprehension [#hbd6d8c0]
さて、compiler_visit_exprに進みます。こちらもcompiler_vis...
#code(C){{
static int
compiler_comprehension(struct compiler *c, expr_ty e, int...
identifier name, asdl_seq *generat...
expr_ty val)
{
PyCodeObject *co = NULL;
expr_ty outermost_iter;
PyObject *qualname = NULL;
outermost_iter = ((comprehension_ty)
asdl_seq_GET(generators, 0))->iter;
if (!compiler_enter_scope(c, name, COMPILER_SCOPE_COM...
(void *)e, e->lineno))
goto error;
if (type != COMP_GENEXP) {
int op;
switch (type) {
case COMP_LISTCOMP:
op = BUILD_LIST;
break;
// 他省略
}
ADDOP_I(c, op, 0);
}
if (!compiler_comprehension_generator(c, generators, ...
goto error_in_scope;
if (type != COMP_GENEXP) {
ADDOP(c, RETURN_VALUE);
}
co = assemble(c, 1);
qualname = c->u->u_qualname;
Py_INCREF(qualname);
compiler_exit_scope(c);
if (!compiler_make_closure(c, co, 0, qualname))
goto error;
Py_DECREF(qualname);
Py_DECREF(co);
VISIT(c, expr, outermost_iter);
ADDOP(c, GET_ITER);
ADDOP_I(c, CALL_FUNCTION, 1);
return 1;
error_in_scope:
compiler_exit_scope(c);
error:
Py_XDECREF(qualname);
Py_XDECREF(co);
return 0;
}
}}
新しいスコープを作っているのは予想通りです。その後は少し...
+ADDOP(BUILD_LIST)
+compiler_comprehension_generator呼び出し
+ADDOP(RETURN_VALUE)
この後にcompiler_exit_scopeがあるのでスコープが戻ります。...
+compiler_make_closure呼び出し
+VISIT(outermost_iter)
+ADDOP(GET_ITER)
+ADDOP(CALL_FUNCTION)
を行っています。これらは元々のスコープに対して行われます。
**compiler_comprehension_generator [#yfe4fb89]
まずはcompiler_comprehension_generatorを見てみましょう。
#code(C){{
static int
compiler_comprehension_generator(struct compiler *c,
asdl_seq *generators, in...
expr_ty elt, expr_ty val...
{
/* generate code for the iterator, then each of the i...
and then write to the element */
comprehension_ty gen;
basicblock *start, *anchor, *skip, *if_cleanup;
Py_ssize_t i, n;
start = compiler_new_block(c);
skip = compiler_new_block(c);
if_cleanup = compiler_new_block(c);
anchor = compiler_new_block(c);
gen = (comprehension_ty)asdl_seq_GET(generators, gen_...
if (gen_index == 0) {
/* Receive outermost iter as an implicit argument...
c->u->u_argcount = 1;
ADDOP_I(c, LOAD_FAST, 0);
}
else {
/* Sub-iter - calculate on the fly */
VISIT(c, expr, gen->iter);
ADDOP(c, GET_ITER);
}
compiler_use_next_block(c, start);
ADDOP_JREL(c, FOR_ITER, anchor);
NEXT_BLOCK(c);
VISIT(c, expr, gen->target);
/* XXX this needs to be cleaned up...a lot! */
n = asdl_seq_LEN(gen->ifs);
for (i = 0; i < n; i++) {
expr_ty e = (expr_ty)asdl_seq_GET(gen->ifs, i);
VISIT(c, expr, e);
ADDOP_JABS(c, POP_JUMP_IF_FALSE, if_cleanup);
NEXT_BLOCK(c);
}
if (++gen_index < asdl_seq_LEN(generators))
if (!compiler_comprehension_generator(c,
generators,...
elt, val, t...
return 0;
/* only append after the last for generator */
if (gen_index >= asdl_seq_LEN(generators)) {
/* comprehension specific code */
switch (type) {
case COMP_LISTCOMP:
VISIT(c, expr, elt);
ADDOP_I(c, LIST_APPEND, gen_index + 1);
break;
// 他省略
}
compiler_use_next_block(c, skip);
}
compiler_use_next_block(c, if_cleanup);
ADDOP_JABS(c, JUMP_ABSOLUTE, start);
compiler_use_next_block(c, anchor);
return 1;
}
}}
急にいろいろ出てきました。ひとつずつ確認していきましょう。
まず先頭、start, skip, if_cleanup, anchorというblockが作...
#code(C){{
compiler_use_next_block(c, start);
ADDOP_JREL(c, FOR_ITER, anchor);
NEXT_BLOCK(c);
VISIT(c, expr, gen->target);
}}
という記述から、ジャンプの飛び先のラベルとして使われてい...
さて、target、今回の場合はName(n)、compiler_visit_exprを...
**compiler_visit_nameop [#z0adc0fc]
compiler_visit_nameopはスコープによってどのように参照する...
#code(C){{
ADDOP_O(c, op, mangled, varnames);
}}
ADDOP_Oを追っかけましょう。
#code(C){{
#define ADDOP_O(C, OP, O, TYPE) { \
if (!compiler_addop_o((C), (OP), (C)->u->u_ ## TYPE, ...
return 0; \
}
}}
OとTYPEがひっくり返るのが地味にめんどくさいです。
#code(C){{
static int
compiler_addop_o(struct compiler *c, int opcode, PyObject...
{
Py_ssize_t arg = compiler_add_o(c, dict, o);
if (arg < 0)
return 0;
return compiler_addop_i(c, opcode, arg);
}
static Py_ssize_t
compiler_add_o(struct compiler *c, PyObject *dict, PyObje...
{
PyObject *t, *v;
Py_ssize_t arg;
double d;
/* necessary to make sure types aren't coerced (e.g.,...
/* _and_ to distinguish 0.0 from -0.0 e.g. on IEEE pl...
if (PyFloat_Check(o)) {
// 省略
}
else if (PyComplex_Check(o)) {
// 省略
}
else {
t = PyTuple_Pack(2, o, o->ob_type);
}
v = PyDict_GetItem(dict, t);
if (!v) {
arg = PyDict_Size(dict);
v = PyLong_FromSsize_t(arg);
if (PyDict_SetItem(dict, t, v) < 0) {
Py_DECREF(t);
Py_DECREF(v);
return -1;
}
Py_DECREF(v);
}
else
arg = PyLong_AsLong(v);
Py_DECREF(t);
return arg;
}
}}
nという名前から0番目というインデックスに置き換えられ、opc...
**compiler_comprehension_generatorに戻る [#nbe985a6]
さて、compiler_comprehension_generatorに戻りましょう。
#code(C){{
/* XXX this needs to be cleaned up...a lot! */
n = asdl_seq_LEN(gen->ifs);
for (i = 0; i < n; i++) {
expr_ty e = (expr_ty)asdl_seq_GET(gen->ifs, i);
VISIT(c, expr, e);
ADDOP_JABS(c, POP_JUMP_IF_FALSE, if_cleanup);
NEXT_BLOCK(c);
}
if (++gen_index < asdl_seq_LEN(generators))
if (!compiler_comprehension_generator(c,
generators,...
elt, val, t...
return 0;
/* only append after the last for generator */
if (gen_index >= asdl_seq_LEN(generators)) {
/* comprehension specific code */
switch (type) {
case COMP_LISTCOMP:
VISIT(c, expr, elt);
ADDOP_I(c, LIST_APPEND, gen_index + 1);
break;
// 他省略
}
compiler_use_next_block(c, skip);
}
compiler_use_next_block(c, if_cleanup);
ADDOP_JABS(c, JUMP_ABSOLUTE, start);
compiler_use_next_block(c, anchor);
return 1;
}}
まず、ifsの処理をします。今回の場合、以下のASTです。
Compare
BinOp
Mult
Name(n) Load
Name(n) Load
Gt
Num(10)
対応するompile_compareは「0 < x < 10」のように書けるのに...
#code(C){{
static int
compiler_compare(struct compiler *c, expr_ty e)
{
Py_ssize_t i, n;
VISIT(c, expr, e->v.Compare.left);
n = asdl_seq_LEN(e->v.Compare.ops);
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.com...
ADDOP_I(c, COMPARE_OP, cmpop((cmpop_ty)(asdl_seq_GET(...
return 1;
}
}}
左辺を処理して、右辺を処理して、比較を行うということが想...
LOAD_FAST 0(n)
LOAD_FAST 0(n)
BINARY_MULTIPLY
LOAD_CONST 10
COMPARE_OP >
0(n)というのはバイトコードの引数としては0が設定される、そ...
ifsの処理が終わって、最終的にcompiler_comprehensionの初め...
BUILD_LIST 0
LOAD_FAST 0
:start
FOR_ITER REL:anchor
:
STORE_FAST 0(n)
LOAD_FAST 0(n)
LOAD_FAST 0(n)
BINARY_MULTIPLY
LOAD_CONST 10
COMPARE_OP >
POP_JUMP_IF_FALSE ABS:if_cleanup
:
LOAD_FAST 0(n)
LIST_APPEND 2
:skip
:if_cleanup
JUMP_ABSOLUTE ABS:start
:anchor
RETURN_VALUE
RELとかABSとか書いてあるのはジャンプです。また、:startな...
**compiler_comprehensionに戻る [#ge4f6aa3]
さてだいぶ長くなってきましたがまだ続きます。compiler_comp...
#code(C){{
if (!compiler_make_closure(c, co, 0, qualname))
goto error;
Py_DECREF(qualname);
Py_DECREF(co);
VISIT(c, expr, outermost_iter);
ADDOP(c, GET_ITER);
ADDOP_I(c, CALL_FUNCTION, 1);
}}
まずはcompiler_make_closure、ブロックの外の変数の取り込み...
#code(C){{
static int
compiler_make_closure(struct compiler *c, PyCodeObject *c...
{
Py_ssize_t i, free = PyCode_GetNumFree(co);
if (qualname == NULL)
qualname = co->co_name;
if (free == 0) {
ADDOP_O(c, LOAD_CONST, (PyObject*)co, consts);
ADDOP_O(c, LOAD_CONST, qualname, consts);
ADDOP_I(c, MAKE_FUNCTION, args);
return 1;
}
}
}}
次にoutermost_iterを処理、対応するASTは以下です。
Call
Name(Range) Load
Num(10)
プログラミングの華、関数呼び出しです。対応するのはcompile...
#code(C){{
compiler_call(struct compiler *c, expr_ty e)
{
VISIT(c, expr, e->v.Call.func);
return compiler_call_helper(c, 0, e->v.Call.args, e->...
}
}}
compiler_call_helperはキーワード変数など様々な呼び出しの...
最終的に、compiler_comprehensionが呼ばれて出来上がったバ...
LOAD_CONST 内包表記のコード
LOAD_CONST <listcomp>
MAKE_FUNCTION 0
LOAD_NAME 0(range)
LOAD_CONST 10
CALL_FUNCTION 1
GET_ITER
CALL_FUNCTION 1
*assemble [#xa027eaf]
最後に、飛ばしたassembleを見ます。assembleは今までに2回出...
-トップレベルに対するassemble
-内包表記のスコープに対するassemble
このことからスコープに対して最終的なバイトコードを作って...
assembleのコードは次の通り(一部省略)
#code(C){{
static PyCodeObject *
assemble(struct compiler *c, int addNone)
{
basicblock *b, *entryblock;
struct assembler a;
int i, j, nblocks;
PyCodeObject *co = NULL;
nblocks = 0;
entryblock = NULL;
for (b = c->u->u_blocks; b != NULL; b = b->b_list) {
nblocks++;
entryblock = b;
}
if (!assemble_init(&a, nblocks, c->u->u_firstlineno))
goto error;
dfs(c, entryblock, &a);
/* Can't modify the bytecode after computing jump off...
assemble_jump_offsets(&a, c);
/* Emit code in reverse postorder from dfs. */
for (i = a.a_nblocks - 1; i >= 0; i--) {
b = a.a_postorder[i];
for (j = 0; j < b->b_iused; j++)
if (!assemble_emit(&a, &b->b_instr[j]))
goto error;
}
if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0)
goto error;
if (_PyBytes_Resize(&a.a_bytecode, a.a_offset) < 0)
goto error;
co = makecode(c, &a);
error:
assemble_free(&a);
return co;
}
}}
初期化を除くと初めに呼ばれるのはdfs、depth-first searchで...
#code(C){{
static void
dfs(struct compiler *c, basicblock *b, struct assembler *a)
{
int i;
struct instr *instr = NULL;
if (b->b_seen)
return;
b->b_seen = 1;
if (b->b_next != NULL)
dfs(c, b->b_next, a);
for (i = 0; i < b->b_iused; i++) {
instr = &b->b_instr[i];
if (instr->i_jrel || instr->i_jabs)
dfs(c, instr->i_target, a);
}
a->a_postorder[a->a_nblocks++] = b;
}
}}
blockを次がある間再帰、次にinstrを見てジャンプが行われて...
assembleに戻って次に呼んでいるのはassemble_jump_offsets、...
続いて個々のinstrに対してassemble_emitを呼び出しています...
最後にmakecode。簡単な最適化を行ったうえで最終的なPyCode...
ページ名: