From 98b4902187617a7e6244da2c9d5066a5774cabfc Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 21 Jul 2022 16:26:52 +0100 Subject: [PATCH 01/10] gh-93678: extract 'struct cfg' from the compiler so that the CFG can be manipulated directly --- Python/compile.c | 186 +++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 88 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 52b7d9242eb2b0..fa5e68db5f343a 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -325,6 +325,13 @@ enum { COMPILER_SCOPE_COMPREHENSION, }; +typedef struct cfg_ { + /* Pointer to the most recently allocated block. By following b_list + members, you can reach all early allocated blocks. */ + basicblock *block_list; + basicblock *curblock; /* pointer to current block */ +} cfg; + /* The following items change on entry and exit of code blocks. They must be saved and restored when returning to a block. */ @@ -352,8 +359,8 @@ struct compiler_unit { Py_ssize_t u_kwonlyargcount; /* number of keyword only arguments for block */ /* Pointer to the most recently allocated block. By following b_list members, you can reach all early allocated blocks. */ - basicblock *u_blocks; - basicblock *u_curblock; /* pointer to current block */ + + cfg u_cfg; /* The control flow graph */ int u_nfblocks; struct fblockinfo u_fblock[CO_MAXBLOCKS]; @@ -390,6 +397,9 @@ struct compiler { PyArena *c_arena; /* pointer to memory allocation arena */ }; +#define CFG(c) (&((c)->u->u_cfg)) +#define COMPILER_LOC(c) (&((c)->u->u_loc)) + typedef struct { // A list of strings corresponding to name captures. It is used to track: // - Repeated name assignments in the same pattern. @@ -417,12 +427,9 @@ typedef struct { static int basicblock_next_instr(basicblock *); -static int compiler_enter_scope(struct compiler *, identifier, int, void *, int); +static int cfg_addop_i(cfg *g, int opcode, Py_ssize_t oparg, const struct location *loc); + static void compiler_free(struct compiler *); -static basicblock *compiler_new_block(struct compiler *); -static int compiler_addop(struct compiler *, int, bool); -static int compiler_addop_i(struct compiler *, int, Py_ssize_t, bool); -static int compiler_addop_j(struct compiler *, int, basicblock *, bool); 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); @@ -442,7 +449,6 @@ static int are_all_items_const(asdl_expr_seq *, Py_ssize_t, Py_ssize_t); static int compiler_with(struct compiler *, stmt_ty, int); static int compiler_async_with(struct compiler *, stmt_ty, int); static int compiler_async_for(struct compiler *, stmt_ty); -static int validate_keywords(struct compiler *c, asdl_keyword_seq *keywords); static int compiler_call_simple_kw_helper(struct compiler *c, asdl_keyword_seq *keywords, Py_ssize_t nkwelts); @@ -714,10 +720,9 @@ dictbytype(PyObject *src, int scope_type, int flag, Py_ssize_t offset) } static void -compiler_unit_check(struct compiler_unit *u) +cfg_check(cfg *g) { - basicblock *block; - for (block = u->u_blocks; block != NULL; block = block->b_list) { + for (basicblock *block = g->block_list; block != NULL; block = block->b_list) { assert(!_PyMem_IsPtrFreed(block)); if (block->b_instr != NULL) { assert(block->b_ialloc > 0); @@ -732,19 +737,24 @@ compiler_unit_check(struct compiler_unit *u) } static void -compiler_unit_free(struct compiler_unit *u) +cfg_free(cfg* g) { - basicblock *b, *next; - - compiler_unit_check(u); - b = u->u_blocks; + cfg_check(g); + basicblock *b = g->block_list; while (b != NULL) { - if (b->b_instr) + if (b->b_instr) { PyObject_Free((void *)b->b_instr); - next = b->b_list; + } + basicblock *next = b->b_list; PyObject_Free((void *)b); b = next; } +} + +static void +compiler_unit_free(struct compiler_unit *u) +{ + cfg_free(&u->u_cfg); Py_CLEAR(u->u_ste); Py_CLEAR(u->u_name); Py_CLEAR(u->u_qualname); @@ -842,29 +852,40 @@ new_basicblock() return b; } -static basicblock * -compiler_new_block(struct compiler *c) +basicblock * +cfg_new_block(cfg *g) { basicblock *b = new_basicblock(); if (b == NULL) { return NULL; } /* Extend the singly linked list of blocks with new block. */ - struct compiler_unit *u = c->u; - b->b_list = u->u_blocks; - u->u_blocks = b; + b->b_list = g->block_list; + g->block_list = b; return b; } -static basicblock * -compiler_use_next_block(struct compiler *c, basicblock *block) +basicblock * +cfg_use_next_block(cfg *g, basicblock *block) { assert(block != NULL); - c->u->u_curblock->b_next = block; - c->u->u_curblock = block; + g->curblock->b_next = block; + g->curblock = block; return block; } +static basicblock * +compiler_new_block(struct compiler *c) +{ + return cfg_new_block(CFG(c)); +} + +static basicblock * +compiler_use_next_block(struct compiler *c, basicblock *block) +{ + return cfg_use_next_block(CFG(c), block); +} + static basicblock * basicblock_new_b_list_successor(basicblock *prev) { @@ -1247,21 +1268,6 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg) return stack_effect(opcode, oparg, -1); } -static int -compiler_use_new_implicit_block_if_needed(struct compiler *c) -{ - basicblock *b = c->u->u_curblock; - struct instr *last = basicblock_last_instr(b); - if (last && IS_TERMINATOR_OPCODE(last->i_opcode)) { - basicblock *b = compiler_new_block(c); - if (b == NULL) { - return -1; - } - compiler_use_next_block(c, b); - } - return 0; -} - /* Add an opcode with no argument. Returns 0 on failure, 1 on success. */ @@ -1293,19 +1299,29 @@ basicblock_addop(basicblock *b, int opcode, int oparg, } static int -compiler_addop(struct compiler *c, int opcode, bool line) +cfg_addop(cfg *g, int opcode, int oparg, basicblock *target, + const struct location *loc) { - assert(!HAS_ARG(opcode)); - if (compiler_use_new_implicit_block_if_needed(c) < 0) { - return -1; + struct instr *last = basicblock_last_instr(g->curblock); + if (last && IS_TERMINATOR_OPCODE(last->i_opcode)) { + basicblock *b = cfg_new_block(g); + if (b == NULL) { + return -1; + } + cfg_use_next_block(g, b); } + return basicblock_addop(g->curblock, opcode, oparg, target, loc); +} - const struct location *loc = line ? &c->u->u_loc : NULL; - return basicblock_addop(c->u->u_curblock, opcode, 0, NULL, loc); +static int +cfg_addop_noarg(cfg *g, int opcode, const struct location *loc) +{ + assert(!HAS_ARG(opcode)); + return cfg_addop(g, opcode, 0, NULL, loc); } static Py_ssize_t -compiler_add_o(PyObject *dict, PyObject *o) +dict_add_o(PyObject *dict, PyObject *o) { PyObject *v; Py_ssize_t arg; @@ -1450,7 +1466,7 @@ compiler_add_const(struct compiler *c, PyObject *o) return -1; } - Py_ssize_t arg = compiler_add_o(c->u->u_consts, key); + Py_ssize_t arg = dict_add_o(c->u->u_consts, key); Py_DECREF(key); return arg; } @@ -1461,17 +1477,17 @@ compiler_addop_load_const(struct compiler *c, PyObject *o) Py_ssize_t arg = compiler_add_const(c, o); if (arg < 0) return 0; - return compiler_addop_i(c, LOAD_CONST, arg, true); + return cfg_addop_i(CFG(c), LOAD_CONST, arg, COMPILER_LOC(c)); } static int compiler_addop_o(struct compiler *c, int opcode, PyObject *dict, PyObject *o) { - Py_ssize_t arg = compiler_add_o(dict, o); + Py_ssize_t arg = dict_add_o(dict, o); if (arg < 0) return 0; - return compiler_addop_i(c, opcode, arg, true); + return cfg_addop_i(CFG(c), opcode, arg, COMPILER_LOC(c)); } static int @@ -1483,7 +1499,7 @@ compiler_addop_name(struct compiler *c, int opcode, PyObject *dict, PyObject *mangled = _Py_Mangle(c->u->u_private, o); if (!mangled) return 0; - arg = compiler_add_o(dict, mangled); + arg = dict_add_o(dict, mangled); Py_DECREF(mangled); if (arg < 0) return 0; @@ -1495,18 +1511,15 @@ compiler_addop_name(struct compiler *c, int opcode, PyObject *dict, arg <<= 1; arg |= 1; } - return compiler_addop_i(c, opcode, arg, true); + return cfg_addop_i(CFG(c), opcode, arg, COMPILER_LOC(c)); } /* Add an opcode with an integer argument. Returns 0 on failure, 1 on success. */ static int -compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg, bool line) +cfg_addop_i(cfg *g, int opcode, Py_ssize_t oparg, const struct location *loc) { - if (compiler_use_new_implicit_block_if_needed(c) < 0) { - return -1; - } /* oparg value is unsigned, but a signed C int is usually used to store it in the C code (like Python/ceval.c). @@ -1516,35 +1529,30 @@ compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg, bool line) EXTENDED_ARG is used for 16, 24, and 32-bit arguments. */ int oparg_ = Py_SAFE_DOWNCAST(oparg, Py_ssize_t, int); - - const struct location *loc = line ? &c->u->u_loc : NULL; - return basicblock_addop(c->u->u_curblock, opcode, oparg_, NULL, loc); + return cfg_addop(g, opcode, oparg_, NULL, loc); } static int -compiler_addop_j(struct compiler *c, int opcode, basicblock *target, bool line) +cfg_addop_j(cfg *g, int opcode, basicblock *target, const struct location *loc) { - if (compiler_use_new_implicit_block_if_needed(c) < 0) { - return -1; - } - const struct location *loc = line ? &c->u->u_loc : NULL; assert(target != NULL); assert(IS_JUMP_OPCODE(opcode) || IS_BLOCK_PUSH_OPCODE(opcode)); - return basicblock_addop(c->u->u_curblock, opcode, 0, target, loc); + return cfg_addop(g, opcode, 0, target, loc); } + #define ADDOP(C, OP) { \ - if (!compiler_addop((C), (OP), true)) \ + if (!cfg_addop_noarg(CFG(C), (OP), COMPILER_LOC(C))) \ return 0; \ } #define ADDOP_NOLINE(C, OP) { \ - if (!compiler_addop((C), (OP), false)) \ + if (!cfg_addop_noarg(CFG(C), (OP), NULL)) \ return 0; \ } #define ADDOP_IN_SCOPE(C, OP) { \ - if (!compiler_addop((C), (OP), true)) { \ + if (!cfg_addop_noarg(CFG(C), (OP), COMPILER_LOC(C))) { \ compiler_exit_scope(c); \ return 0; \ } \ @@ -1583,17 +1591,17 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *target, bool line) } #define ADDOP_I(C, OP, O) { \ - if (!compiler_addop_i((C), (OP), (O), true)) \ + if (!cfg_addop_i(CFG(C), (OP), (O), COMPILER_LOC(C))) \ return 0; \ } #define ADDOP_I_NOLINE(C, OP, O) { \ - if (!compiler_addop_i((C), (OP), (O), false)) \ + if (!cfg_addop_i(CFG(C), (OP), (O), NULL)) \ return 0; \ } #define ADDOP_JUMP(C, OP, O) { \ - if (!compiler_addop_j((C), (OP), (O), true)) \ + if (!cfg_addop_j(CFG(C), (OP), (O), COMPILER_LOC(C))) \ return 0; \ } @@ -1601,7 +1609,7 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *target, bool line) * Used for artificial jumps that have no corresponding * token in the source code. */ #define ADDOP_JUMP_NOLINE(C, OP, O) { \ - if (!compiler_addop_j((C), (OP), (O), false)) \ + if (!cfg_addop_j(CFG(C), (OP), (O), NULL)) \ return 0; \ } @@ -1718,7 +1726,7 @@ compiler_enter_scope(struct compiler *c, identifier name, return 0; } - u->u_blocks = NULL; + u->u_cfg.block_list = NULL; u->u_nfblocks = 0; u->u_firstlineno = lineno; u->u_loc = LOCATION(lineno, lineno, 0, 0); @@ -1751,10 +1759,11 @@ compiler_enter_scope(struct compiler *c, identifier name, c->c_nestlevel++; - block = compiler_new_block(c); + cfg *g = CFG(c); + block = cfg_new_block(g); if (block == NULL) return 0; - c->u->u_curblock = block; + g->curblock = block; if (u->u_scope_type == COMPILER_SCOPE_MODULE) { c->u->u_loc.lineno = 0; @@ -1791,7 +1800,7 @@ compiler_exit_scope(struct compiler *c) _PyErr_WriteUnraisableMsg("on removing the last compiler " "stack item", NULL); } - compiler_unit_check(c->u); + cfg_check(CFG(c)); } else { c->u = NULL; @@ -4240,7 +4249,7 @@ compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx) } assert(op); - arg = compiler_add_o(dict, mangled); + arg = dict_add_o(dict, mangled); Py_DECREF(mangled); if (arg < 0) { return 0; @@ -4248,7 +4257,7 @@ compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx) if (op == LOAD_GLOBAL) { arg <<= 1; } - return compiler_addop_i(c, op, arg, true); + return cfg_addop_i(CFG(c), op, arg, COMPILER_LOC(c)); } static int @@ -6288,7 +6297,7 @@ emit_and_reset_fail_pop(struct compiler *c, pattern_context *pc) } while (--pc->fail_pop_size) { compiler_use_next_block(c, pc->fail_pop[pc->fail_pop_size]); - if (!compiler_addop(c, POP_TOP, true)) { + if (!cfg_addop_noarg(CFG(c), POP_TOP, COMPILER_LOC(c))) { pc->fail_pop_size = 0; PyObject_Free(pc->fail_pop); pc->fail_pop = NULL; @@ -6722,7 +6731,8 @@ compiler_pattern_or(struct compiler *c, pattern_ty p, pattern_context *pc) pc->fail_pop = NULL; pc->fail_pop_size = 0; pc->on_top = 0; - if (!compiler_addop_i(c, COPY, 1, true) || !compiler_pattern(c, alt, pc)) { + if (!cfg_addop_i(CFG(c), COPY, 1, COMPILER_LOC(c)) || + !compiler_pattern(c, alt, pc)) { goto error; } // Success! @@ -6785,7 +6795,7 @@ compiler_pattern_or(struct compiler *c, pattern_ty p, pattern_context *pc) } } assert(control); - if (!compiler_addop_j(c, JUMP, end, true) || + if (!cfg_addop_j(CFG(c), JUMP, end, COMPILER_LOC(c)) || !emit_and_reset_fail_pop(c, pc)) { goto error; @@ -6797,7 +6807,7 @@ compiler_pattern_or(struct compiler *c, pattern_ty p, pattern_context *pc) // Need to NULL this for the PyObject_Free call in the error block. old_pc.fail_pop = NULL; // No match. Pop the remaining copy of the subject and fail: - if (!compiler_addop(c, POP_TOP, true) || !jump_to_fail_pop(c, pc, JUMP)) { + if (!cfg_addop_noarg(CFG(c), POP_TOP, COMPILER_LOC(c)) || !jump_to_fail_pop(c, pc, JUMP)) { goto error; } compiler_use_next_block(c, end); @@ -8021,7 +8031,7 @@ consts_dict_keys_inorder(PyObject *dict) while (PyDict_Next(dict, &pos, &k, &v)) { i = PyLong_AS_LONG(v); /* The keys of the dictionary can be tuples wrapping a constant. - * (see compiler_add_o and _PyCode_ConstantKey). In that case + * (see dict_add_o and _PyCode_ConstantKey). In that case * the object we want is always second. */ if (PyTuple_CheckExact(k)) { k = PyTuple_GET_ITEM(k, 1); @@ -8544,7 +8554,7 @@ assemble(struct compiler *c, int addNone) } /* Make sure every block that falls off the end returns None. */ - if (!basicblock_returns(c->u->u_curblock)) { + if (!basicblock_returns(CFG(c)->curblock)) { UNSET_LOC(c); if (addNone) ADDOP_LOAD_CONST(c, Py_None); @@ -8567,7 +8577,7 @@ assemble(struct compiler *c, int addNone) int nblocks = 0; basicblock *entryblock = NULL; - for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) { + for (basicblock *b = ((c)->u->u_cfg.block_list); b != NULL; b = b->b_list) { nblocks++; entryblock = b; } From 4bbebf8f01031e08a4566a5837047a633cbab6a5 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 21 Jul 2022 21:35:57 +0100 Subject: [PATCH 02/10] add two missing static qualifiers --- Python/compile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index fa5e68db5f343a..90f52545a0ac75 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -852,7 +852,7 @@ new_basicblock() return b; } -basicblock * +static basicblock * cfg_new_block(cfg *g) { basicblock *b = new_basicblock(); @@ -865,7 +865,7 @@ cfg_new_block(cfg *g) return b; } -basicblock * +static basicblock * cfg_use_next_block(cfg *g, basicblock *block) { assert(block != NULL); From 7984b093bf43124822a65e00e5fe3c6b2376afc7 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sun, 24 Jul 2022 21:04:18 +0100 Subject: [PATCH 03/10] add entryblock field to cfg struct --- Python/compile.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 90f52545a0ac75..8a3de545a0f437 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -326,6 +326,7 @@ enum { }; typedef struct cfg_ { + basicblock *entryblock; /* Where control flow begins */ /* Pointer to the most recently allocated block. By following b_list members, you can reach all early allocated blocks. */ basicblock *block_list; @@ -1726,7 +1727,6 @@ compiler_enter_scope(struct compiler *c, identifier name, return 0; } - u->u_cfg.block_list = NULL; u->u_nfblocks = 0; u->u_firstlineno = lineno; u->u_loc = LOCATION(lineno, lineno, 0, 0); @@ -1760,10 +1760,11 @@ compiler_enter_scope(struct compiler *c, identifier name, c->c_nestlevel++; cfg *g = CFG(c); + g->block_list = NULL; block = cfg_new_block(g); if (block == NULL) return 0; - g->curblock = block; + g->curblock = g->entryblock = block; if (u->u_scope_type == COMPILER_SCOPE_MODULE) { c->u->u_loc.lineno = 0; @@ -8576,17 +8577,17 @@ assemble(struct compiler *c, int addNone) } int nblocks = 0; - basicblock *entryblock = NULL; - for (basicblock *b = ((c)->u->u_cfg.block_list); b != NULL; b = b->b_list) { + for (basicblock *b = CFG(c)->block_list; b != NULL; b = b->b_list) { nblocks++; - entryblock = b; } - assert(entryblock != NULL); if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) { PyErr_NoMemory(); goto error; } + basicblock *entryblock = CFG(c)->entryblock; + assert(entryblock != NULL); + /* Set firstlineno if it wasn't explicitly set. */ if (!c->u->u_firstlineno) { if (entryblock->b_instr && entryblock->b_instr->i_loc.lineno) { From 43fa2926d5e25eefba559566f4ecaf5494947fa4 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sun, 24 Jul 2022 21:46:25 +0100 Subject: [PATCH 04/10] remove basicblock_new_b_list_successor and use cfg_new_block instead --- Python/compile.c | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 8a3de545a0f437..54cad57d056c0f 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -327,8 +327,8 @@ enum { typedef struct cfg_ { basicblock *entryblock; /* Where control flow begins */ - /* Pointer to the most recently allocated block. By following b_list - members, you can reach all early allocated blocks. */ + /* Pointer to the most recently allocated block. By following + b_list links, you can reach all allocated blocks. */ basicblock *block_list; basicblock *curblock; /* pointer to current block */ } cfg; @@ -888,25 +888,13 @@ compiler_use_next_block(struct compiler *c, basicblock *block) } static basicblock * -basicblock_new_b_list_successor(basicblock *prev) -{ - basicblock *result = new_basicblock(); - if (result == NULL) { - return NULL; - } - result->b_list = prev->b_list; - prev->b_list = result; - return result; -} - -static basicblock * -copy_basicblock(basicblock *block) +copy_basicblock(cfg *g, basicblock *block) { /* Cannot copy a block if it has a fallthrough, since * a block can only have one fallthrough predecessor. */ assert(BB_NO_FALLTHROUGH(block)); - basicblock *result = basicblock_new_b_list_successor(block); + basicblock *result = cfg_new_block(g); if (result == NULL) { return NULL; } @@ -7438,7 +7426,8 @@ mark_cold(basicblock *entryblock) { } static int -push_cold_blocks_to_end(basicblock *entryblock, int code_flags) { +push_cold_blocks_to_end(cfg *g, int code_flags) { + basicblock *entryblock = g->entryblock; if (entryblock->b_next == NULL) { /* single basicblock, no need to reorder */ return 0; @@ -7451,7 +7440,7 @@ push_cold_blocks_to_end(basicblock *entryblock, int code_flags) { /* an explicit jump instead of fallthrough */ for (basicblock *b = entryblock; b != NULL; b = b->b_next) { if (b->b_cold && BB_HAS_FALLTHROUGH(b) && b->b_next && b->b_next->b_warm) { - basicblock *explicit_jump = basicblock_new_b_list_successor(b); + basicblock *explicit_jump = cfg_new_block(g); if (explicit_jump == NULL) { return -1; } @@ -8304,7 +8293,7 @@ trim_unused_consts(basicblock *entryblock, PyObject *consts); /* Duplicates exit BBs, so that line numbers can be propagated to them */ static int -duplicate_exits_without_lineno(basicblock *entryblock); +duplicate_exits_without_lineno(cfg *g); static int extend_block(basicblock *bb); @@ -8585,7 +8574,8 @@ assemble(struct compiler *c, int addNone) goto error; } - basicblock *entryblock = CFG(c)->entryblock; + cfg *g = CFG(c); + basicblock *entryblock = g->entryblock; assert(entryblock != NULL); /* Set firstlineno if it wasn't explicitly set. */ @@ -8622,7 +8612,7 @@ assemble(struct compiler *c, int addNone) if (trim_unused_consts(entryblock, consts)) { goto error; } - if (duplicate_exits_without_lineno(entryblock)) { + if (duplicate_exits_without_lineno(g)) { return NULL; } propagate_line_numbers(entryblock); @@ -8639,7 +8629,7 @@ assemble(struct compiler *c, int addNone) } convert_exception_handlers_to_nops(entryblock); - if (push_cold_blocks_to_end(entryblock, code_flags) < 0) { + if (push_cold_blocks_to_end(g, code_flags) < 0) { goto error; } @@ -9558,15 +9548,16 @@ is_exit_without_lineno(basicblock *b) { * copy the line number from the sole predecessor block. */ static int -duplicate_exits_without_lineno(basicblock *entryblock) +duplicate_exits_without_lineno(cfg *g) { /* Copy all exit blocks without line number that are targets of a jump. */ + basicblock *entryblock = g->entryblock; for (basicblock *b = entryblock; b != NULL; b = b->b_next) { if (b->b_iused > 0 && is_jump(&b->b_instr[b->b_iused-1])) { basicblock *target = b->b_instr[b->b_iused-1].i_target; if (is_exit_without_lineno(target) && target->b_predecessors > 1) { - basicblock *new_target = copy_basicblock(target); + basicblock *new_target = copy_basicblock(g, target); if (new_target == NULL) { return -1; } From 1e7d1df5ef9204869b3a1da236c4d9c623e12356 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Mon, 25 Jul 2022 10:33:46 +0100 Subject: [PATCH 05/10] inline new_basicblock - it's called in onyl one place now and it's not safe to call from elsewhere --- Python/compile.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 54cad57d056c0f..0dacedfaabfe1a 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -843,23 +843,13 @@ compiler_set_qualname(struct compiler *c) Returns NULL on error. */ static basicblock * -new_basicblock() +cfg_new_block(cfg *g) { basicblock *b = (basicblock *)PyObject_Calloc(1, sizeof(basicblock)); if (b == NULL) { PyErr_NoMemory(); return NULL; } - return b; -} - -static basicblock * -cfg_new_block(cfg *g) -{ - basicblock *b = new_basicblock(); - if (b == NULL) { - return NULL; - } /* Extend the singly linked list of blocks with new block. */ b->b_list = g->block_list; g->block_list = b; From e943cc2f4ec47944772873ccc380cad0f030f6cf Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Mon, 25 Jul 2022 19:23:14 +0100 Subject: [PATCH 06/10] rename cfg --> cfg_builder, entryblock field --> cfg --- Python/compile.c | 113 ++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 0dacedfaabfe1a..ec581782a3a7b0 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -325,13 +325,16 @@ enum { COMPILER_SCOPE_COMPREHENSION, }; -typedef struct cfg_ { - basicblock *entryblock; /* Where control flow begins */ +typedef struct cfg_builder_ { + /* The entryblock, at which control flow begins. All blocks of the + CFG are reachable through the b_next links */ + basicblock *cfg; /* Pointer to the most recently allocated block. By following b_list links, you can reach all allocated blocks. */ basicblock *block_list; - basicblock *curblock; /* pointer to current block */ -} cfg; + /* pointer to the block currently being constructed */ + basicblock *curblock; +} cfg_builder; /* The following items change on entry and exit of code blocks. They must be saved and restored when returning to a block. @@ -361,7 +364,7 @@ struct compiler_unit { /* Pointer to the most recently allocated block. By following b_list members, you can reach all early allocated blocks. */ - cfg u_cfg; /* The control flow graph */ + cfg_builder u_cfg_builder; /* The control flow graph */ int u_nfblocks; struct fblockinfo u_fblock[CO_MAXBLOCKS]; @@ -398,7 +401,7 @@ struct compiler { PyArena *c_arena; /* pointer to memory allocation arena */ }; -#define CFG(c) (&((c)->u->u_cfg)) +#define CFG_BUILDER(c) (&((c)->u->u_cfg_builder)) #define COMPILER_LOC(c) (&((c)->u->u_loc)) typedef struct { @@ -428,7 +431,7 @@ typedef struct { static int basicblock_next_instr(basicblock *); -static int cfg_addop_i(cfg *g, int opcode, Py_ssize_t oparg, const struct location *loc); +static int cfg_builder_addop_i(cfg_builder *g, int opcode, Py_ssize_t oparg, const struct location *loc); static void compiler_free(struct compiler *); static int compiler_error(struct compiler *, const char *, ...); @@ -721,7 +724,7 @@ dictbytype(PyObject *src, int scope_type, int flag, Py_ssize_t offset) } static void -cfg_check(cfg *g) +cfg_builder_check(cfg_builder *g) { for (basicblock *block = g->block_list; block != NULL; block = block->b_list) { assert(!_PyMem_IsPtrFreed(block)); @@ -738,9 +741,9 @@ cfg_check(cfg *g) } static void -cfg_free(cfg* g) +cfg_builder_free(cfg_builder* g) { - cfg_check(g); + cfg_builder_check(g); basicblock *b = g->block_list; while (b != NULL) { if (b->b_instr) { @@ -755,7 +758,7 @@ cfg_free(cfg* g) static void compiler_unit_free(struct compiler_unit *u) { - cfg_free(&u->u_cfg); + cfg_builder_free(&u->u_cfg_builder); Py_CLEAR(u->u_ste); Py_CLEAR(u->u_name); Py_CLEAR(u->u_qualname); @@ -843,7 +846,7 @@ compiler_set_qualname(struct compiler *c) Returns NULL on error. */ static basicblock * -cfg_new_block(cfg *g) +cfg_builder_new_block(cfg_builder *g) { basicblock *b = (basicblock *)PyObject_Calloc(1, sizeof(basicblock)); if (b == NULL) { @@ -857,7 +860,7 @@ cfg_new_block(cfg *g) } static basicblock * -cfg_use_next_block(cfg *g, basicblock *block) +cfg_builder_use_next_block(cfg_builder *g, basicblock *block) { assert(block != NULL); g->curblock->b_next = block; @@ -868,23 +871,23 @@ cfg_use_next_block(cfg *g, basicblock *block) static basicblock * compiler_new_block(struct compiler *c) { - return cfg_new_block(CFG(c)); + return cfg_builder_new_block(CFG_BUILDER(c)); } static basicblock * compiler_use_next_block(struct compiler *c, basicblock *block) { - return cfg_use_next_block(CFG(c), block); + return cfg_builder_use_next_block(CFG_BUILDER(c), block); } static basicblock * -copy_basicblock(cfg *g, basicblock *block) +copy_basicblock(cfg_builder *g, basicblock *block) { /* Cannot copy a block if it has a fallthrough, since * a block can only have one fallthrough predecessor. */ assert(BB_NO_FALLTHROUGH(block)); - basicblock *result = cfg_new_block(g); + basicblock *result = cfg_builder_new_block(g); if (result == NULL) { return NULL; } @@ -1278,25 +1281,25 @@ basicblock_addop(basicblock *b, int opcode, int oparg, } static int -cfg_addop(cfg *g, int opcode, int oparg, basicblock *target, +cfg_builder_addop(cfg_builder *g, int opcode, int oparg, basicblock *target, const struct location *loc) { struct instr *last = basicblock_last_instr(g->curblock); if (last && IS_TERMINATOR_OPCODE(last->i_opcode)) { - basicblock *b = cfg_new_block(g); + basicblock *b = cfg_builder_new_block(g); if (b == NULL) { return -1; } - cfg_use_next_block(g, b); + cfg_builder_use_next_block(g, b); } return basicblock_addop(g->curblock, opcode, oparg, target, loc); } static int -cfg_addop_noarg(cfg *g, int opcode, const struct location *loc) +cfg_builder_addop_noarg(cfg_builder *g, int opcode, const struct location *loc) { assert(!HAS_ARG(opcode)); - return cfg_addop(g, opcode, 0, NULL, loc); + return cfg_builder_addop(g, opcode, 0, NULL, loc); } static Py_ssize_t @@ -1456,7 +1459,7 @@ compiler_addop_load_const(struct compiler *c, PyObject *o) Py_ssize_t arg = compiler_add_const(c, o); if (arg < 0) return 0; - return cfg_addop_i(CFG(c), LOAD_CONST, arg, COMPILER_LOC(c)); + return cfg_builder_addop_i(CFG_BUILDER(c), LOAD_CONST, arg, COMPILER_LOC(c)); } static int @@ -1466,7 +1469,7 @@ compiler_addop_o(struct compiler *c, int opcode, PyObject *dict, Py_ssize_t arg = dict_add_o(dict, o); if (arg < 0) return 0; - return cfg_addop_i(CFG(c), opcode, arg, COMPILER_LOC(c)); + return cfg_builder_addop_i(CFG_BUILDER(c), opcode, arg, COMPILER_LOC(c)); } static int @@ -1490,14 +1493,14 @@ compiler_addop_name(struct compiler *c, int opcode, PyObject *dict, arg <<= 1; arg |= 1; } - return cfg_addop_i(CFG(c), opcode, arg, COMPILER_LOC(c)); + return cfg_builder_addop_i(CFG_BUILDER(c), opcode, arg, COMPILER_LOC(c)); } /* Add an opcode with an integer argument. Returns 0 on failure, 1 on success. */ static int -cfg_addop_i(cfg *g, int opcode, Py_ssize_t oparg, const struct location *loc) +cfg_builder_addop_i(cfg_builder *g, int opcode, Py_ssize_t oparg, const struct location *loc) { /* oparg value is unsigned, but a signed C int is usually used to store it in the C code (like Python/ceval.c). @@ -1508,30 +1511,30 @@ cfg_addop_i(cfg *g, int opcode, Py_ssize_t oparg, const struct location *loc) EXTENDED_ARG is used for 16, 24, and 32-bit arguments. */ int oparg_ = Py_SAFE_DOWNCAST(oparg, Py_ssize_t, int); - return cfg_addop(g, opcode, oparg_, NULL, loc); + return cfg_builder_addop(g, opcode, oparg_, NULL, loc); } static int -cfg_addop_j(cfg *g, int opcode, basicblock *target, const struct location *loc) +cfg_builder_addop_j(cfg_builder *g, int opcode, basicblock *target, const struct location *loc) { assert(target != NULL); assert(IS_JUMP_OPCODE(opcode) || IS_BLOCK_PUSH_OPCODE(opcode)); - return cfg_addop(g, opcode, 0, target, loc); + return cfg_builder_addop(g, opcode, 0, target, loc); } #define ADDOP(C, OP) { \ - if (!cfg_addop_noarg(CFG(C), (OP), COMPILER_LOC(C))) \ + if (!cfg_builder_addop_noarg(CFG_BUILDER(C), (OP), COMPILER_LOC(C))) \ return 0; \ } #define ADDOP_NOLINE(C, OP) { \ - if (!cfg_addop_noarg(CFG(C), (OP), NULL)) \ + if (!cfg_builder_addop_noarg(CFG_BUILDER(C), (OP), NULL)) \ return 0; \ } #define ADDOP_IN_SCOPE(C, OP) { \ - if (!cfg_addop_noarg(CFG(C), (OP), COMPILER_LOC(C))) { \ + if (!cfg_builder_addop_noarg(CFG_BUILDER(C), (OP), COMPILER_LOC(C))) { \ compiler_exit_scope(c); \ return 0; \ } \ @@ -1570,17 +1573,17 @@ cfg_addop_j(cfg *g, int opcode, basicblock *target, const struct location *loc) } #define ADDOP_I(C, OP, O) { \ - if (!cfg_addop_i(CFG(C), (OP), (O), COMPILER_LOC(C))) \ + if (!cfg_builder_addop_i(CFG_BUILDER(C), (OP), (O), COMPILER_LOC(C))) \ return 0; \ } #define ADDOP_I_NOLINE(C, OP, O) { \ - if (!cfg_addop_i(CFG(C), (OP), (O), NULL)) \ + if (!cfg_builder_addop_i(CFG_BUILDER(C), (OP), (O), NULL)) \ return 0; \ } #define ADDOP_JUMP(C, OP, O) { \ - if (!cfg_addop_j(CFG(C), (OP), (O), COMPILER_LOC(C))) \ + if (!cfg_builder_addop_j(CFG_BUILDER(C), (OP), (O), COMPILER_LOC(C))) \ return 0; \ } @@ -1588,7 +1591,7 @@ cfg_addop_j(cfg *g, int opcode, basicblock *target, const struct location *loc) * Used for artificial jumps that have no corresponding * token in the source code. */ #define ADDOP_JUMP_NOLINE(C, OP, O) { \ - if (!cfg_addop_j(CFG(C), (OP), (O), NULL)) \ + if (!cfg_builder_addop_j(CFG_BUILDER(C), (OP), (O), NULL)) \ return 0; \ } @@ -1737,12 +1740,12 @@ compiler_enter_scope(struct compiler *c, identifier name, c->c_nestlevel++; - cfg *g = CFG(c); + cfg_builder *g = CFG_BUILDER(c); g->block_list = NULL; - block = cfg_new_block(g); + block = cfg_builder_new_block(g); if (block == NULL) return 0; - g->curblock = g->entryblock = block; + g->curblock = g->cfg = block; if (u->u_scope_type == COMPILER_SCOPE_MODULE) { c->u->u_loc.lineno = 0; @@ -1779,7 +1782,7 @@ compiler_exit_scope(struct compiler *c) _PyErr_WriteUnraisableMsg("on removing the last compiler " "stack item", NULL); } - cfg_check(CFG(c)); + cfg_builder_check(CFG_BUILDER(c)); } else { c->u = NULL; @@ -4236,7 +4239,7 @@ compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx) if (op == LOAD_GLOBAL) { arg <<= 1; } - return cfg_addop_i(CFG(c), op, arg, COMPILER_LOC(c)); + return cfg_builder_addop_i(CFG_BUILDER(c), op, arg, COMPILER_LOC(c)); } static int @@ -6276,7 +6279,7 @@ emit_and_reset_fail_pop(struct compiler *c, pattern_context *pc) } while (--pc->fail_pop_size) { compiler_use_next_block(c, pc->fail_pop[pc->fail_pop_size]); - if (!cfg_addop_noarg(CFG(c), POP_TOP, COMPILER_LOC(c))) { + if (!cfg_builder_addop_noarg(CFG_BUILDER(c), POP_TOP, COMPILER_LOC(c))) { pc->fail_pop_size = 0; PyObject_Free(pc->fail_pop); pc->fail_pop = NULL; @@ -6710,7 +6713,7 @@ compiler_pattern_or(struct compiler *c, pattern_ty p, pattern_context *pc) pc->fail_pop = NULL; pc->fail_pop_size = 0; pc->on_top = 0; - if (!cfg_addop_i(CFG(c), COPY, 1, COMPILER_LOC(c)) || + if (!cfg_builder_addop_i(CFG_BUILDER(c), COPY, 1, COMPILER_LOC(c)) || !compiler_pattern(c, alt, pc)) { goto error; } @@ -6774,7 +6777,7 @@ compiler_pattern_or(struct compiler *c, pattern_ty p, pattern_context *pc) } } assert(control); - if (!cfg_addop_j(CFG(c), JUMP, end, COMPILER_LOC(c)) || + if (!cfg_builder_addop_j(CFG_BUILDER(c), JUMP, end, COMPILER_LOC(c)) || !emit_and_reset_fail_pop(c, pc)) { goto error; @@ -6786,7 +6789,7 @@ compiler_pattern_or(struct compiler *c, pattern_ty p, pattern_context *pc) // Need to NULL this for the PyObject_Free call in the error block. old_pc.fail_pop = NULL; // No match. Pop the remaining copy of the subject and fail: - if (!cfg_addop_noarg(CFG(c), POP_TOP, COMPILER_LOC(c)) || !jump_to_fail_pop(c, pc, JUMP)) { + if (!cfg_builder_addop_noarg(CFG_BUILDER(c), POP_TOP, COMPILER_LOC(c)) || !jump_to_fail_pop(c, pc, JUMP)) { goto error; } compiler_use_next_block(c, end); @@ -7416,8 +7419,8 @@ mark_cold(basicblock *entryblock) { } static int -push_cold_blocks_to_end(cfg *g, int code_flags) { - basicblock *entryblock = g->entryblock; +push_cold_blocks_to_end(cfg_builder *g, int code_flags) { + basicblock *entryblock = g->cfg; if (entryblock->b_next == NULL) { /* single basicblock, no need to reorder */ return 0; @@ -7430,7 +7433,7 @@ push_cold_blocks_to_end(cfg *g, int code_flags) { /* an explicit jump instead of fallthrough */ for (basicblock *b = entryblock; b != NULL; b = b->b_next) { if (b->b_cold && BB_HAS_FALLTHROUGH(b) && b->b_next && b->b_next->b_warm) { - basicblock *explicit_jump = cfg_new_block(g); + basicblock *explicit_jump = cfg_builder_new_block(g); if (explicit_jump == NULL) { return -1; } @@ -8283,7 +8286,7 @@ trim_unused_consts(basicblock *entryblock, PyObject *consts); /* Duplicates exit BBs, so that line numbers can be propagated to them */ static int -duplicate_exits_without_lineno(cfg *g); +duplicate_exits_without_lineno(cfg_builder *g); static int extend_block(basicblock *bb); @@ -8534,7 +8537,7 @@ assemble(struct compiler *c, int addNone) } /* Make sure every block that falls off the end returns None. */ - if (!basicblock_returns(CFG(c)->curblock)) { + if (!basicblock_returns(CFG_BUILDER(c)->curblock)) { UNSET_LOC(c); if (addNone) ADDOP_LOAD_CONST(c, Py_None); @@ -8556,7 +8559,7 @@ assemble(struct compiler *c, int addNone) } int nblocks = 0; - for (basicblock *b = CFG(c)->block_list; b != NULL; b = b->b_list) { + for (basicblock *b = CFG_BUILDER(c)->block_list; b != NULL; b = b->b_list) { nblocks++; } if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) { @@ -8564,8 +8567,8 @@ assemble(struct compiler *c, int addNone) goto error; } - cfg *g = CFG(c); - basicblock *entryblock = g->entryblock; + cfg_builder *g = CFG_BUILDER(c); + basicblock *entryblock = g->cfg; assert(entryblock != NULL); /* Set firstlineno if it wasn't explicitly set. */ @@ -9538,11 +9541,11 @@ is_exit_without_lineno(basicblock *b) { * copy the line number from the sole predecessor block. */ static int -duplicate_exits_without_lineno(cfg *g) +duplicate_exits_without_lineno(cfg_builder *g) { /* Copy all exit blocks without line number that are targets of a jump. */ - basicblock *entryblock = g->entryblock; + basicblock *entryblock = g->cfg; for (basicblock *b = entryblock; b != NULL; b = b->b_next) { if (b->b_iused > 0 && is_jump(&b->b_instr[b->b_iused-1])) { basicblock *target = b->b_instr[b->b_iused-1].i_target; From f7ba9e81f57d36b5ec0ad1ff547c51b0b0587223 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 26 Jul 2022 09:31:15 +0000 Subject: [PATCH 07/10] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2022-07-26-09-31-12.gh-issue-93678.W8vvgT.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-07-26-09-31-12.gh-issue-93678.W8vvgT.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-07-26-09-31-12.gh-issue-93678.W8vvgT.rst b/Misc/NEWS.d/next/Core and Builtins/2022-07-26-09-31-12.gh-issue-93678.W8vvgT.rst new file mode 100644 index 00000000000000..6ff816a172cc27 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-07-26-09-31-12.gh-issue-93678.W8vvgT.rst @@ -0,0 +1 @@ +Add cfg_builder struct and refactor the relevant code so that a cfg can be constructed without an instance of the compiler struct. From 5a6dc5d1fb01ee2d9ebd4e188d6ba580b6ce4183 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 26 Jul 2022 10:55:05 +0100 Subject: [PATCH 08/10] cfg --> cfg_entryblock --- Python/compile.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index f2c138c8e780b6..1c42bb50857e19 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -328,7 +328,7 @@ enum { typedef struct cfg_builder_ { /* The entryblock, at which control flow begins. All blocks of the CFG are reachable through the b_next links */ - basicblock *cfg; + basicblock *cfg_entryblock; /* Pointer to the most recently allocated block. By following b_list links, you can reach all allocated blocks. */ basicblock *block_list; @@ -1745,7 +1745,7 @@ compiler_enter_scope(struct compiler *c, identifier name, block = cfg_builder_new_block(g); if (block == NULL) return 0; - g->curblock = g->cfg = block; + g->curblock = g->cfg_entryblock = block; if (u->u_scope_type == COMPILER_SCOPE_MODULE) { c->u->u_loc.lineno = 0; @@ -7423,7 +7423,7 @@ mark_cold(basicblock *entryblock) { static int push_cold_blocks_to_end(cfg_builder *g, int code_flags) { - basicblock *entryblock = g->cfg; + basicblock *entryblock = g->cfg_entryblock; if (entryblock->b_next == NULL) { /* single basicblock, no need to reorder */ return 0; @@ -8571,7 +8571,7 @@ assemble(struct compiler *c, int addNone) } cfg_builder *g = CFG_BUILDER(c); - basicblock *entryblock = g->cfg; + basicblock *entryblock = g->cfg_entryblock; assert(entryblock != NULL); /* Set firstlineno if it wasn't explicitly set. */ @@ -9548,7 +9548,7 @@ duplicate_exits_without_lineno(cfg_builder *g) { /* Copy all exit blocks without line number that are targets of a jump. */ - basicblock *entryblock = g->cfg; + basicblock *entryblock = g->cfg_entryblock; for (basicblock *b = entryblock; b != NULL; b = b->b_next) { if (b->b_iused > 0 && is_jump(&b->b_instr[b->b_iused-1])) { basicblock *target = b->b_instr[b->b_iused-1].i_target; From 3a8436ed6f7575b60ab30a4067bf702dd5e7d4c0 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 26 Jul 2022 11:04:25 +0100 Subject: [PATCH 09/10] remove obsolete comment --- Python/compile.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 1c42bb50857e19..7654a7430cca90 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -361,8 +361,6 @@ struct compiler_unit { Py_ssize_t u_argcount; /* number of arguments for block */ Py_ssize_t u_posonlyargcount; /* number of positional only arguments for block */ Py_ssize_t u_kwonlyargcount; /* number of keyword only arguments for block */ - /* Pointer to the most recently allocated block. By following b_list - members, you can reach all early allocated blocks. */ cfg_builder u_cfg_builder; /* The control flow graph */ From 873d34f7921d72f2cde2b2a7c5d54decea7064de Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 26 Jul 2022 13:17:58 +0100 Subject: [PATCH 10/10] pass location by value --- Python/compile.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 7654a7430cca90..975efa7e23ecdf 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -400,7 +400,7 @@ struct compiler { }; #define CFG_BUILDER(c) (&((c)->u->u_cfg_builder)) -#define COMPILER_LOC(c) (&((c)->u->u_loc)) +#define COMPILER_LOC(c) ((c)->u->u_loc) typedef struct { // A list of strings corresponding to name captures. It is used to track: @@ -429,7 +429,7 @@ typedef struct { static int basicblock_next_instr(basicblock *); -static int cfg_builder_addop_i(cfg_builder *g, int opcode, Py_ssize_t oparg, const struct location *loc); +static int cfg_builder_addop_i(cfg_builder *g, int opcode, Py_ssize_t oparg, struct location loc); static void compiler_free(struct compiler *); static int compiler_error(struct compiler *, const char *, ...); @@ -1254,7 +1254,7 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg) static int basicblock_addop(basicblock *b, int opcode, int oparg, - basicblock *target, const struct location *loc) + basicblock *target, struct location loc) { assert(IS_WITHIN_OPCODE_RANGE(opcode)); assert(!IS_ASSEMBLER_OPCODE(opcode)); @@ -1273,14 +1273,14 @@ basicblock_addop(basicblock *b, int opcode, int oparg, i->i_opcode = opcode; i->i_oparg = oparg; i->i_target = target; - i->i_loc = loc ? *loc : NO_LOCATION; + i->i_loc = loc; return 1; } static int cfg_builder_addop(cfg_builder *g, int opcode, int oparg, basicblock *target, - const struct location *loc) + struct location loc) { struct instr *last = basicblock_last_instr(g->curblock); if (last && IS_TERMINATOR_OPCODE(last->i_opcode)) { @@ -1294,7 +1294,7 @@ cfg_builder_addop(cfg_builder *g, int opcode, int oparg, basicblock *target, } static int -cfg_builder_addop_noarg(cfg_builder *g, int opcode, const struct location *loc) +cfg_builder_addop_noarg(cfg_builder *g, int opcode, struct location loc) { assert(!HAS_ARG(opcode)); return cfg_builder_addop(g, opcode, 0, NULL, loc); @@ -1498,7 +1498,7 @@ compiler_addop_name(struct compiler *c, int opcode, PyObject *dict, Returns 0 on failure, 1 on success. */ static int -cfg_builder_addop_i(cfg_builder *g, int opcode, Py_ssize_t oparg, const struct location *loc) +cfg_builder_addop_i(cfg_builder *g, int opcode, Py_ssize_t oparg, struct location loc) { /* oparg value is unsigned, but a signed C int is usually used to store it in the C code (like Python/ceval.c). @@ -1513,7 +1513,7 @@ cfg_builder_addop_i(cfg_builder *g, int opcode, Py_ssize_t oparg, const struct l } static int -cfg_builder_addop_j(cfg_builder *g, int opcode, basicblock *target, const struct location *loc) +cfg_builder_addop_j(cfg_builder *g, int opcode, basicblock *target, struct location loc) { assert(target != NULL); assert(IS_JUMP_OPCODE(opcode) || IS_BLOCK_PUSH_OPCODE(opcode)); @@ -1527,7 +1527,7 @@ cfg_builder_addop_j(cfg_builder *g, int opcode, basicblock *target, const struct } #define ADDOP_NOLINE(C, OP) { \ - if (!cfg_builder_addop_noarg(CFG_BUILDER(C), (OP), NULL)) \ + if (!cfg_builder_addop_noarg(CFG_BUILDER(C), (OP), NO_LOCATION)) \ return 0; \ } @@ -1576,7 +1576,7 @@ cfg_builder_addop_j(cfg_builder *g, int opcode, basicblock *target, const struct } #define ADDOP_I_NOLINE(C, OP, O) { \ - if (!cfg_builder_addop_i(CFG_BUILDER(C), (OP), (O), NULL)) \ + if (!cfg_builder_addop_i(CFG_BUILDER(C), (OP), (O), NO_LOCATION)) \ return 0; \ } @@ -1589,7 +1589,7 @@ cfg_builder_addop_j(cfg_builder *g, int opcode, basicblock *target, const struct * Used for artificial jumps that have no corresponding * token in the source code. */ #define ADDOP_JUMP_NOLINE(C, OP, O) { \ - if (!cfg_builder_addop_j(CFG_BUILDER(C), (OP), (O), NULL)) \ + if (!cfg_builder_addop_j(CFG_BUILDER(C), (OP), (O), NO_LOCATION)) \ return 0; \ } @@ -7438,7 +7438,7 @@ push_cold_blocks_to_end(cfg_builder *g, int code_flags) { if (explicit_jump == NULL) { return -1; } - basicblock_addop(explicit_jump, JUMP, 0, b->b_next, &NO_LOCATION); + basicblock_addop(explicit_jump, JUMP, 0, b->b_next, NO_LOCATION); explicit_jump->b_cold = 1; explicit_jump->b_next = b->b_next;