From 582aaf19e8b94a70c1f96792197770d604ba0fdf Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 4 Aug 2020 17:30:11 +0100 Subject: [PATCH] bpo-41463: Generate information about jumps from 'opcode.py' rather than duplicating it in 'compile.c' (GH-21714) Generate information about jumps from 'opcode.py' rather than duplicate it in 'compile.c' --- Include/opcode.h | 22 +++++ Python/compile.c | 132 ++++++++++++++++------------- Tools/scripts/generate_opcode_h.py | 19 +++++ 3 files changed, 114 insertions(+), 59 deletions(-) diff --git a/Include/opcode.h b/Include/opcode.h index 19944fac0b9..420c87aa0f2 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -127,6 +127,28 @@ extern "C" { #define SET_UPDATE 163 #define DICT_MERGE 164 #define DICT_UPDATE 165 +#ifdef NEED_OPCODE_JUMP_TABLES +static uint32_t _PyOpcode_RelativeJump[8] = { + 0U, + 0U, + 536870912U, + 67125248U, + 67141632U, + 0U, + 0U, + 0U, +}; +static uint32_t _PyOpcode_Jump[8] = { + 0U, + 0U, + 536870912U, + 101695488U, + 67141632U, + 0U, + 0U, + 0U, +}; +#endif /* OPCODE_TABLES */ /* EXCEPT_HANDLER is a special, implicit block type which is created when entering an except handler. It is not an opcode but we define it here diff --git a/Python/compile.c b/Python/compile.c index 42b09fd96df..5dbd9f221fd 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -27,6 +27,7 @@ #include "ast.h" #include "code.h" #include "symtable.h" +#define NEED_OPCODE_JUMP_TABLES #include "opcode.h" #include "wordcode_helpers.h" @@ -45,14 +46,38 @@ && (c->u->u_ste->ste_type == ModuleBlock)) struct instr { - unsigned i_jabs : 1; - unsigned i_jrel : 1; unsigned char i_opcode; int i_oparg; struct basicblock_ *i_target; /* target block (if jump instruction) */ int i_lineno; }; +#define LOG_BITS_PER_INT 5 +#define MASK_LOW_LOG_BITS 31 + +static inline int +is_bit_set_in_table(uint32_t *table, int bitindex) { + /* Is the relevant bit set in the relevant word? */ + /* 256 bits fit into 8 32-bits words. + * Word is indexed by (bitindex>>ln(size of int in bits)). + * Bit within word is the low bits of bitindex. + */ + uint32_t word = table[bitindex >> LOG_BITS_PER_INT]; + return (word >> (bitindex & MASK_LOW_LOG_BITS)) & 1; +} + +static inline int +is_relative_jump(struct instr *i) +{ + return is_bit_set_in_table(_PyOpcode_RelativeJump, i->i_opcode); +} + +static inline int +is_jump(struct instr *i) +{ + return is_bit_set_in_table(_PyOpcode_Jump, i->i_opcode); +} + typedef struct basicblock_ { /* Each basicblock in a compilation unit is linked via b_list in the reverse order that the block are allocated. b_list points to the next @@ -182,7 +207,7 @@ static basicblock *compiler_new_block(struct compiler *); static int compiler_next_instr(basicblock *); static int compiler_addop(struct compiler *, int); static int compiler_addop_i(struct compiler *, int, Py_ssize_t); -static int compiler_addop_j(struct compiler *, int, basicblock *, int); +static int compiler_addop_j(struct compiler *, int, basicblock *); static int compiler_error(struct compiler *, const char *); static int compiler_warn(struct compiler *, const char *, ...); static int compiler_nameop(struct compiler *, identifier, expr_context_ty); @@ -1381,7 +1406,7 @@ compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg) } static int -compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) +compiler_addop_j(struct compiler *c, int opcode, basicblock *b) { struct instr *i; int off; @@ -1398,10 +1423,6 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) i = &c->u->u_curblock->b_instr[off]; i->i_opcode = opcode; i->i_target = b; - if (absolute) - i->i_jabs = 1; - else - i->i_jrel = 1; i->i_lineno = c->u->u_lineno; return 1; } @@ -1471,17 +1492,11 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) return 0; \ } -#define ADDOP_JABS(C, OP, O) { \ - if (!compiler_addop_j((C), (OP), (O), 1)) \ +#define ADDOP_JUMP(C, OP, O) { \ + if (!compiler_addop_j((C), (OP), (O))) \ return 0; \ } -#define ADDOP_JREL(C, OP, O) { \ - if (!compiler_addop_j((C), (OP), (O), 0)) \ - return 0; \ -} - - #define ADDOP_COMPARE(C, CMP) { \ if (!compiler_addcompare((C), (cmpop_ty)(CMP))) \ return 0; \ @@ -2545,7 +2560,7 @@ compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond) return 0; if (!compiler_jump_if(c, e->v.IfExp.body, next, cond)) return 0; - ADDOP_JREL(c, JUMP_FORWARD, end); + ADDOP_JUMP(c, JUMP_FORWARD, end); compiler_use_next_block(c, next2); if (!compiler_jump_if(c, e->v.IfExp.orelse, next, cond)) return 0; @@ -2568,20 +2583,20 @@ compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond) ADDOP(c, DUP_TOP); ADDOP(c, ROT_THREE); ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, i)); - ADDOP_JABS(c, POP_JUMP_IF_FALSE, cleanup); + ADDOP_JUMP(c, POP_JUMP_IF_FALSE, cleanup); NEXT_BLOCK(c); } VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n)); ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, n)); - ADDOP_JABS(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next); + ADDOP_JUMP(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next); basicblock *end = compiler_new_block(c); if (end == NULL) return 0; - ADDOP_JREL(c, JUMP_FORWARD, end); + ADDOP_JUMP(c, JUMP_FORWARD, end); compiler_use_next_block(c, cleanup); ADDOP(c, POP_TOP); if (!cond) { - ADDOP_JREL(c, JUMP_FORWARD, next); + ADDOP_JUMP(c, JUMP_FORWARD, next); } compiler_use_next_block(c, end); return 1; @@ -2596,7 +2611,7 @@ compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond) /* general implementation */ VISIT(c, expr, e); - ADDOP_JABS(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next); + ADDOP_JUMP(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next); return 1; } @@ -2615,7 +2630,7 @@ compiler_ifexp(struct compiler *c, expr_ty e) if (!compiler_jump_if(c, e->v.IfExp.test, next, 0)) return 0; VISIT(c, expr, e->v.IfExp.body); - ADDOP_JREL(c, JUMP_FORWARD, end); + ADDOP_JUMP(c, JUMP_FORWARD, end); compiler_use_next_block(c, next); VISIT(c, expr, e->v.IfExp.orelse); compiler_use_next_block(c, end); @@ -2721,7 +2736,7 @@ compiler_if(struct compiler *c, stmt_ty s) } VISIT_SEQ(c, stmt, s->v.If.body); if (asdl_seq_LEN(s->v.If.orelse)) { - ADDOP_JREL(c, JUMP_FORWARD, end); + ADDOP_JUMP(c, JUMP_FORWARD, end); compiler_use_next_block(c, next); VISIT_SEQ(c, stmt, s->v.If.orelse); } @@ -2747,10 +2762,10 @@ compiler_for(struct compiler *c, stmt_ty s) VISIT(c, expr, s->v.For.iter); ADDOP(c, GET_ITER); compiler_use_next_block(c, start); - ADDOP_JREL(c, FOR_ITER, cleanup); + ADDOP_JUMP(c, FOR_ITER, cleanup); VISIT(c, expr, s->v.For.target); VISIT_SEQ(c, stmt, s->v.For.body); - ADDOP_JABS(c, JUMP_ABSOLUTE, start); + ADDOP_JUMP(c, JUMP_ABSOLUTE, start); compiler_use_next_block(c, cleanup); compiler_pop_fblock(c, FOR_LOOP, start); @@ -2786,7 +2801,7 @@ compiler_async_for(struct compiler *c, stmt_ty s) return 0; } /* SETUP_FINALLY to guard the __anext__ call */ - ADDOP_JREL(c, SETUP_FINALLY, except); + ADDOP_JUMP(c, SETUP_FINALLY, except); ADDOP(c, GET_ANEXT); ADDOP_LOAD_CONST(c, Py_None); ADDOP(c, YIELD_FROM); @@ -2795,7 +2810,7 @@ compiler_async_for(struct compiler *c, stmt_ty s) /* Success block for __anext__ */ VISIT(c, expr, s->v.AsyncFor.target); VISIT_SEQ(c, stmt, s->v.AsyncFor.body); - ADDOP_JABS(c, JUMP_ABSOLUTE, start); + ADDOP_JUMP(c, JUMP_ABSOLUTE, start); compiler_pop_fblock(c, FOR_LOOP, start); @@ -2859,7 +2874,7 @@ compiler_while(struct compiler *c, stmt_ty s) return 0; } VISIT_SEQ(c, stmt, s->v.While.body); - ADDOP_JABS(c, JUMP_ABSOLUTE, loop); + ADDOP_JUMP(c, JUMP_ABSOLUTE, loop); /* XXX should the two POP instructions be in a separate block if there is no else clause ? @@ -2918,7 +2933,7 @@ compiler_break(struct compiler *c) if (!compiler_unwind_fblock(c, loop, 0)) { return 0; } - ADDOP_JABS(c, JUMP_ABSOLUTE, loop->fb_exit); + ADDOP_JUMP(c, JUMP_ABSOLUTE, loop->fb_exit); return 1; } @@ -2932,7 +2947,7 @@ compiler_continue(struct compiler *c) if (loop == NULL) { return compiler_error(c, "'continue' not properly in loop"); } - ADDOP_JABS(c, JUMP_ABSOLUTE, loop->fb_block); + ADDOP_JUMP(c, JUMP_ABSOLUTE, loop->fb_block); return 1; } @@ -2978,7 +2993,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s) return 0; /* `try` block */ - ADDOP_JREL(c, SETUP_FINALLY, end); + ADDOP_JUMP(c, SETUP_FINALLY, end); compiler_use_next_block(c, body); if (!compiler_push_fblock(c, FINALLY_TRY, body, end, s->v.Try.finalbody)) return 0; @@ -2992,7 +3007,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s) ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, FINALLY_TRY, body); VISIT_SEQ(c, stmt, s->v.Try.finalbody); - ADDOP_JREL(c, JUMP_FORWARD, exit); + ADDOP_JUMP(c, JUMP_FORWARD, exit); /* `finally` block */ compiler_use_next_block(c, end); if (!compiler_push_fblock(c, FINALLY_END, end, NULL, NULL)) @@ -3046,14 +3061,14 @@ compiler_try_except(struct compiler *c, stmt_ty s) end = compiler_new_block(c); if (body == NULL || except == NULL || orelse == NULL || end == NULL) return 0; - ADDOP_JREL(c, SETUP_FINALLY, except); + ADDOP_JUMP(c, SETUP_FINALLY, except); compiler_use_next_block(c, body); if (!compiler_push_fblock(c, EXCEPT, body, NULL, NULL)) return 0; VISIT_SEQ(c, stmt, s->v.Try.body); ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, EXCEPT, body); - ADDOP_JREL(c, JUMP_FORWARD, orelse); + ADDOP_JUMP(c, JUMP_FORWARD, orelse); n = asdl_seq_LEN(s->v.Try.handlers); compiler_use_next_block(c, except); for (i = 0; i < n; i++) { @@ -3068,7 +3083,7 @@ compiler_try_except(struct compiler *c, stmt_ty s) if (handler->v.ExceptHandler.type) { ADDOP(c, DUP_TOP); VISIT(c, expr, handler->v.ExceptHandler.type); - ADDOP_JABS(c, JUMP_IF_NOT_EXC_MATCH, except); + ADDOP_JUMP(c, JUMP_IF_NOT_EXC_MATCH, except); } ADDOP(c, POP_TOP); if (handler->v.ExceptHandler.name) { @@ -3095,7 +3110,7 @@ compiler_try_except(struct compiler *c, stmt_ty s) */ /* second try: */ - ADDOP_JREL(c, SETUP_FINALLY, cleanup_end); + ADDOP_JUMP(c, SETUP_FINALLY, cleanup_end); compiler_use_next_block(c, cleanup_body); if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, handler->v.ExceptHandler.name)) return 0; @@ -3109,7 +3124,7 @@ compiler_try_except(struct compiler *c, stmt_ty s) ADDOP_LOAD_CONST(c, Py_None); compiler_nameop(c, handler->v.ExceptHandler.name, Store); compiler_nameop(c, handler->v.ExceptHandler.name, Del); - ADDOP_JREL(c, JUMP_FORWARD, end); + ADDOP_JUMP(c, JUMP_FORWARD, end); /* except: */ compiler_use_next_block(c, cleanup_end); @@ -3136,7 +3151,7 @@ compiler_try_except(struct compiler *c, stmt_ty s) VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body); compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body); ADDOP(c, POP_EXCEPT); - ADDOP_JREL(c, JUMP_FORWARD, end); + ADDOP_JUMP(c, JUMP_FORWARD, end); } compiler_use_next_block(c, except); } @@ -3645,7 +3660,7 @@ compiler_boolop(struct compiler *c, expr_ty e) assert(n >= 0); for (i = 0; i < n; ++i) { VISIT(c, expr, (expr_ty)asdl_seq_GET(s, i)); - ADDOP_JABS(c, jumpi, end); + ADDOP_JUMP(c, jumpi, end); basicblock *next = compiler_new_block(c); if (next == NULL) { return 0; @@ -3933,7 +3948,7 @@ compiler_compare(struct compiler *c, expr_ty e) ADDOP(c, DUP_TOP); ADDOP(c, ROT_THREE); ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, i)); - ADDOP_JABS(c, JUMP_IF_FALSE_OR_POP, cleanup); + ADDOP_JUMP(c, JUMP_IF_FALSE_OR_POP, cleanup); NEXT_BLOCK(c); } VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n)); @@ -3941,7 +3956,7 @@ compiler_compare(struct compiler *c, expr_ty e) basicblock *end = compiler_new_block(c); if (end == NULL) return 0; - ADDOP_JREL(c, JUMP_FORWARD, end); + ADDOP_JUMP(c, JUMP_FORWARD, end); compiler_use_next_block(c, cleanup); ADDOP(c, ROT_TWO); ADDOP(c, POP_TOP); @@ -4435,7 +4450,7 @@ compiler_sync_comprehension_generator(struct compiler *c, if (start) { depth++; compiler_use_next_block(c, start); - ADDOP_JREL(c, FOR_ITER, anchor); + ADDOP_JUMP(c, FOR_ITER, anchor); NEXT_BLOCK(c); } VISIT(c, expr, gen->target); @@ -4487,7 +4502,7 @@ compiler_sync_comprehension_generator(struct compiler *c, } compiler_use_next_block(c, if_cleanup); if (start) { - ADDOP_JABS(c, JUMP_ABSOLUTE, start); + ADDOP_JUMP(c, JUMP_ABSOLUTE, start); compiler_use_next_block(c, anchor); } @@ -4526,7 +4541,7 @@ compiler_async_comprehension_generator(struct compiler *c, compiler_use_next_block(c, start); - ADDOP_JREL(c, SETUP_FINALLY, except); + ADDOP_JUMP(c, SETUP_FINALLY, except); ADDOP(c, GET_ANEXT); ADDOP_LOAD_CONST(c, Py_None); ADDOP(c, YIELD_FROM); @@ -4577,7 +4592,7 @@ compiler_async_comprehension_generator(struct compiler *c, } } compiler_use_next_block(c, if_cleanup); - ADDOP_JABS(c, JUMP_ABSOLUTE, start); + ADDOP_JUMP(c, JUMP_ABSOLUTE, start); compiler_use_next_block(c, except); ADDOP(c, END_ASYNC_FOR); @@ -4773,7 +4788,7 @@ compiler_with_except_finish(struct compiler *c) { exit = compiler_new_block(c); if (exit == NULL) return 0; - ADDOP_JABS(c, POP_JUMP_IF_TRUE, exit); + ADDOP_JUMP(c, POP_JUMP_IF_TRUE, exit); ADDOP(c, RERAISE); compiler_use_next_block(c, exit); ADDOP(c, POP_TOP); @@ -4835,7 +4850,7 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos) ADDOP_LOAD_CONST(c, Py_None); ADDOP(c, YIELD_FROM); - ADDOP_JREL(c, SETUP_ASYNC_WITH, final); + ADDOP_JUMP(c, SETUP_ASYNC_WITH, final); /* SETUP_ASYNC_WITH pushes a finally block. */ compiler_use_next_block(c, block); @@ -4873,7 +4888,7 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos) ADDOP(c, POP_TOP); - ADDOP_JABS(c, JUMP_ABSOLUTE, exit); + ADDOP_JUMP(c, JUMP_ABSOLUTE, exit); /* For exceptional outcome: */ compiler_use_next_block(c, final); @@ -4927,7 +4942,7 @@ compiler_with(struct compiler *c, stmt_ty s, int pos) /* Evaluate EXPR */ VISIT(c, expr, item->context_expr); /* Will push bound __exit__ */ - ADDOP_JREL(c, SETUP_WITH, final); + ADDOP_JUMP(c, SETUP_WITH, final); /* SETUP_WITH pushes a finally block. */ compiler_use_next_block(c, block); @@ -4961,7 +4976,7 @@ compiler_with(struct compiler *c, stmt_ty s, int pos) if (!compiler_call_exit_with_nones(c)) return 0; ADDOP(c, POP_TOP); - ADDOP_JREL(c, JUMP_FORWARD, exit); + ADDOP_JUMP(c, JUMP_FORWARD, exit); /* For exceptional outcome: */ compiler_use_next_block(c, final); @@ -5492,7 +5507,7 @@ stackdepth(struct compiler *c) maxdepth = new_depth; } assert(depth >= 0); /* invalid code or bug in stackdepth() */ - if (instr->i_jrel || instr->i_jabs) { + if (is_jump(instr)) { effect = stack_effect(instr->i_opcode, instr->i_oparg, 1); assert(effect != PY_INVALID_STACK_EFFECT); int target_depth = depth + effect; @@ -5730,9 +5745,9 @@ assemble_jump_offsets(struct assembler *a, struct compiler *c) the jump instruction. */ bsize += isize; - if (instr->i_jabs || instr->i_jrel) { + if (is_jump(instr)) { instr->i_oparg = instr->i_target->b_offset; - if (instr->i_jrel) { + if (is_relative_jump(instr)) { instr->i_oparg -= bsize; } instr->i_oparg *= sizeof(_Py_CODEUNIT); @@ -5946,8 +5961,8 @@ makecode(struct compiler *c, struct assembler *a, PyObject *consts) static void dump_instr(const struct instr *i) { - const char *jrel = i->i_jrel ? "jrel " : ""; - const char *jabs = i->i_jabs ? "jabs " : ""; + const char *jrel = (is_relative_jump(instr)) ? "jrel " : ""; + const char *jabs = (is_jump(instr) && !is_relative_jump(instr))? "jabs " : ""; char arg[128]; *arg = '\0'; @@ -6122,7 +6137,7 @@ optimize_basic_block(basicblock *bb, PyObject *consts) struct instr *inst = &bb->b_instr[i]; int oparg = inst->i_oparg; int nextop = i+1 < bb->b_iused ? bb->b_instr[i+1].i_opcode : 0; - if (inst->i_jabs || inst->i_jrel) { + if (is_jump(inst)) { /* Skip over empty basic blocks. */ while (inst->i_target->b_iused == 0) { inst->i_target = inst->i_target->b_next; @@ -6148,7 +6163,6 @@ optimize_basic_block(basicblock *bb, PyObject *consts) if (is_true == 1) { inst->i_opcode = NOP; bb->b_instr[i+1].i_opcode = NOP; - bb->b_instr[i+1].i_jabs = 0; } break; @@ -6318,7 +6332,7 @@ mark_reachable(struct assembler *a) { } for (int i = 0; i < b->b_iused; i++) { basicblock *target; - if (b->b_instr[i].i_jrel || b->b_instr[i].i_jabs) { + if (is_jump(&b->b_instr[i])) { target = b->b_instr[i].i_target; if (target->b_reachable == 0) { target->b_reachable = 1; diff --git a/Tools/scripts/generate_opcode_h.py b/Tools/scripts/generate_opcode_h.py index 873f82156e2..cba13b24213 100644 --- a/Tools/scripts/generate_opcode_h.py +++ b/Tools/scripts/generate_opcode_h.py @@ -30,6 +30,18 @@ footer = """ #endif /* !Py_OPCODE_H */ """ +UINT32_MASK = (1<<32)-1 + +def write_int_array_from_ops(name, ops, out): + bits = 0 + for op in ops: + bits |= 1<>= 32 + assert bits == 0 + out.write(f"}};\n") def main(opcode_py, outfile='Include/opcode.h'): opcode = {} @@ -41,6 +53,8 @@ def main(opcode_py, outfile='Include/opcode.h'): code = fp.read() exec(code, opcode) opmap = opcode['opmap'] + hasjrel = opcode['hasjrel'] + hasjabs = opcode['hasjabs'] with open(outfile, 'w') as fobj: fobj.write(header) for name in opcode['opname']: @@ -49,8 +63,13 @@ def main(opcode_py, outfile='Include/opcode.h'): if name == 'POP_EXCEPT': # Special entry for HAVE_ARGUMENT fobj.write("#define %-23s %3d\n" % ('HAVE_ARGUMENT', opcode['HAVE_ARGUMENT'])) + fobj.write("#ifdef NEED_OPCODE_JUMP_TABLES\n") + write_int_array_from_ops("_PyOpcode_RelativeJump", opcode['hasjrel'], fobj) + write_int_array_from_ops("_PyOpcode_Jump", opcode['hasjrel'] + opcode['hasjabs'], fobj) + fobj.write("#endif /* OPCODE_TABLES */\n") fobj.write(footer) + print("%s regenerated from %s" % (outfile, opcode_py))