Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-93678: reduce boilerplate and code repetition in the compiler #93682

Merged
merged 18 commits into from
Jun 14, 2022
Merged
Changes from 3 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 73 additions & 120 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@
(IS_VIRTUAL_JUMP_OPCODE(opcode) || \
is_bit_set_in_table(_PyOpcode_Jump, opcode))

#define IS_BLOCK_PUSH_OPCODE(opcode) \
((opcode) == SETUP_FINALLY || \
(opcode) == SETUP_WITH || \
(opcode) == SETUP_CLEANUP)

/* opcodes which are not emitted in codegen stage, only by the assembler */
#define IS_ASSEMBLER_OPCODE(opcode) \
((opcode) == JUMP_FORWARD || \
Expand Down Expand Up @@ -191,7 +196,7 @@ static inline int
is_block_push(struct instr *instr)
{
int opcode = instr->i_opcode;
return opcode == SETUP_FINALLY || opcode == SETUP_WITH || opcode == SETUP_CLEANUP;
return IS_BLOCK_PUSH_OPCODE(opcode);
}

static inline int
Expand Down Expand Up @@ -432,10 +437,9 @@ static int basicblock_next_instr(basicblock *);
static int compiler_enter_scope(struct compiler *, identifier, int, void *, int);
static void compiler_free(struct compiler *);
static basicblock *compiler_new_block(struct compiler *);
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 *);
static int compiler_addop_j_noline(struct compiler *, int, basicblock *);
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);
Expand Down Expand Up @@ -967,6 +971,25 @@ basicblock_next_instr(basicblock *b)
(new).i_end_lineno = (old).i_end_lineno; \
(new).i_end_col_offset = (old).i_end_col_offset;


struct location {
int lineno;
int end_lineno;
int col_offset;
int end_col_offset;
};

#define NO_LOCATION ((struct location){-1, 0, 0, 0})

/* current compiler unit's location */
#define CU_LOCATION(CU) \
((struct location){ \
(CU)->u_lineno, \
(CU)->u_end_lineno, \
(CU)->u_col_offset, \
(CU)->u_end_col_offset, \
})

/* Return the stack effect of opcode with argument oparg.

Some opcodes have different stack effect when jump to the target and
Expand Down Expand Up @@ -1269,48 +1292,46 @@ compiler_use_new_implicit_block_if_needed(struct compiler *c)
*/

static int
basicblock_addop_line(basicblock *b, int opcode, int line,
int end_line, int col_offset, int end_col_offset)
basicblock_addop(basicblock *b, int opcode, int oparg,
basicblock *target, struct location loc)
{
assert(IS_WITHIN_OPCODE_RANGE(opcode));
assert(!IS_ASSEMBLER_OPCODE(opcode));
assert(!HAS_ARG(opcode) || IS_ARTIFICIAL(opcode));
assert(HAS_ARG(opcode) || oparg == 0);
assert(0 <= oparg && oparg <= 2147483647);
iritkatriel marked this conversation as resolved.
Show resolved Hide resolved
assert((target == NULL) ||
IS_JUMP_OPCODE(opcode) ||
IS_BLOCK_PUSH_OPCODE(opcode));
assert(oparg == 0 || target == NULL);

int off = basicblock_next_instr(b);
if (off < 0) {
return 0;
}
struct instr *i = &b->b_instr[off];
i->i_opcode = opcode;
i->i_oparg = 0;
i->i_lineno = line;
i->i_end_lineno = end_line;
i->i_col_offset = col_offset;
i->i_end_col_offset = end_col_offset;
i->i_oparg = oparg;
i->i_target = target;
i->i_lineno = loc.lineno;
i->i_end_lineno = loc.end_lineno;
i->i_col_offset = loc.col_offset;
i->i_end_col_offset = loc.end_col_offset;

return 1;
}

static int
compiler_addop(struct compiler *c, int opcode)
compiler_addop(struct compiler *c, int opcode, bool line)
{
assert(!HAS_ARG(opcode) || IS_ARTIFICIAL(opcode));
if (compiler_use_new_implicit_block_if_needed(c) < 0) {
return -1;
}
return basicblock_addop_line(c->u->u_curblock, opcode, c->u->u_lineno, c->u->u_end_lineno,
c->u->u_col_offset, c->u->u_end_col_offset);
}

static int
compiler_addop_noline(struct compiler *c, int opcode)
{
if (compiler_use_new_implicit_block_if_needed(c) < 0) {
return -1;
}
return basicblock_addop_line(c->u->u_curblock, opcode, -1, 0, 0, 0);
struct location loc = line ? CU_LOCATION(c->u) : NO_LOCATION;
return basicblock_addop(c->u->u_curblock, opcode, 0, NULL, loc);
}


static Py_ssize_t
compiler_add_o(PyObject *dict, PyObject *o)
{
Expand Down Expand Up @@ -1467,7 +1488,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 compiler_addop_i(c, LOAD_CONST, arg);
return compiler_addop_i(c, LOAD_CONST, arg, true);
}

static int
Expand All @@ -1477,7 +1498,7 @@ compiler_addop_o(struct compiler *c, int opcode, PyObject *dict,
Py_ssize_t arg = compiler_add_o(dict, o);
if (arg < 0)
return 0;
return compiler_addop_i(c, opcode, arg);
return compiler_addop_i(c, opcode, arg, true);
}

static int
Expand All @@ -1493,18 +1514,18 @@ compiler_addop_name(struct compiler *c, int opcode, PyObject *dict,
Py_DECREF(mangled);
if (arg < 0)
return 0;
return compiler_addop_i(c, opcode, arg);
return compiler_addop_i(c, opcode, arg, true);
}

/* Add an opcode with an integer argument.
Returns 0 on failure, 1 on success.
*/

static int
basicblock_addop_i_line(basicblock *b, int opcode, Py_ssize_t oparg,
int lineno, int end_lineno,
int col_offset, int end_col_offset)
compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg, bool line)
{
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).

Expand All @@ -1513,104 +1534,36 @@ basicblock_addop_i_line(basicblock *b, int opcode, Py_ssize_t oparg,
The argument of a concrete bytecode instruction is limited to 8-bit.
EXTENDED_ARG is used for 16, 24, and 32-bit arguments. */

assert(IS_WITHIN_OPCODE_RANGE(opcode));
assert(!IS_ASSEMBLER_OPCODE(opcode));
assert(HAS_ARG(opcode));
assert(0 <= oparg && oparg <= 2147483647);
int oparg_ = Py_SAFE_DOWNCAST(oparg, Py_ssize_t, int);

int off = basicblock_next_instr(b);
if (off < 0) {
return 0;
}
struct instr *i = &b->b_instr[off];
i->i_opcode = opcode;
i->i_oparg = Py_SAFE_DOWNCAST(oparg, Py_ssize_t, int);
i->i_lineno = lineno;
i->i_end_lineno = end_lineno;
i->i_col_offset = col_offset;
i->i_end_col_offset = end_col_offset;

return 1;
struct location loc = line ? CU_LOCATION(c->u) : NO_LOCATION;
return basicblock_addop(c->u->u_curblock, opcode, oparg_, NULL, loc);
}

static int
compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg)
compiler_addop_j(struct compiler *c, int opcode, basicblock *target, bool line)
{
if (compiler_use_new_implicit_block_if_needed(c) < 0) {
return -1;
}
return basicblock_addop_i_line(c->u->u_curblock, opcode, oparg,
c->u->u_lineno, c->u->u_end_lineno,
c->u->u_col_offset, c->u->u_end_col_offset);
}

static int
compiler_addop_i_noline(struct compiler *c, int opcode, Py_ssize_t oparg)
{
if (compiler_use_new_implicit_block_if_needed(c) < 0) {
return -1;
}
return basicblock_addop_i_line(c->u->u_curblock, opcode, oparg, -1, 0, 0, 0);
}

static int
basicblock_add_jump(basicblock *b, int opcode,
int lineno, int end_lineno,
int col_offset, int end_col_offset,
basicblock *target)
{
assert(IS_WITHIN_OPCODE_RANGE(opcode));
assert(!IS_ASSEMBLER_OPCODE(opcode));
assert(HAS_ARG(opcode) || IS_VIRTUAL_OPCODE(opcode));
struct location loc = line ? CU_LOCATION(c->u) : NO_LOCATION;
assert(target != NULL);

int off = basicblock_next_instr(b);
struct instr *i = &b->b_instr[off];
if (off < 0) {
return 0;
}
i->i_opcode = opcode;
i->i_target = target;
i->i_lineno = lineno;
i->i_end_lineno = end_lineno;
i->i_col_offset = col_offset;
i->i_end_col_offset = end_col_offset;

return 1;
}

static int
compiler_addop_j(struct compiler *c, int opcode, basicblock *b)
{
if (compiler_use_new_implicit_block_if_needed(c) < 0) {
return -1;
}
return basicblock_add_jump(c->u->u_curblock, opcode, c->u->u_lineno,
c->u->u_end_lineno, c->u->u_col_offset,
c->u->u_end_col_offset, b);
}

static int
compiler_addop_j_noline(struct compiler *c, int opcode, basicblock *b)
{
if (compiler_use_new_implicit_block_if_needed(c) < 0) {
return -1;
}
return basicblock_add_jump(c->u->u_curblock, opcode, -1, 0, 0, 0, b);
assert(IS_JUMP_OPCODE(opcode) || IS_BLOCK_PUSH_OPCODE(opcode));
return basicblock_addop(c->u->u_curblock, opcode, 0, target, loc);
}

#define ADDOP(C, OP) { \
if (!compiler_addop((C), (OP))) \
if (!compiler_addop((C), (OP), true)) \
return 0; \
}

#define ADDOP_NOLINE(C, OP) { \
if (!compiler_addop_noline((C), (OP))) \
if (!compiler_addop((C), (OP), false)) \
return 0; \
}

#define ADDOP_IN_SCOPE(C, OP) { \
if (!compiler_addop((C), (OP))) { \
if (!compiler_addop((C), (OP), true)) { \
compiler_exit_scope(c); \
return 0; \
} \
Expand Down Expand Up @@ -1649,25 +1602,25 @@ compiler_addop_j_noline(struct compiler *c, int opcode, basicblock *b)
}

#define ADDOP_I(C, OP, O) { \
if (!compiler_addop_i((C), (OP), (O))) \
if (!compiler_addop_i((C), (OP), (O), true)) \
return 0; \
}

#define ADDOP_I_NOLINE(C, OP, O) { \
if (!compiler_addop_i_noline((C), (OP), (O))) \
if (!compiler_addop_i((C), (OP), (O), false)) \
return 0; \
}

#define ADDOP_JUMP(C, OP, O) { \
if (!compiler_addop_j((C), (OP), (O))) \
if (!compiler_addop_j((C), (OP), (O), true)) \
return 0; \
}

/* Add a jump with no line number.
* Used for artificial jumps that have no corresponding
* token in the source code. */
#define ADDOP_JUMP_NOLINE(C, OP, O) { \
if (!compiler_addop_j_noline((C), (OP), (O))) \
if (!compiler_addop_j((C), (OP), (O), false)) \
return 0; \
}

Expand Down Expand Up @@ -4320,7 +4273,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);
return compiler_addop_i(c, op, arg, true);
}

static int
Expand Down Expand Up @@ -6299,7 +6252,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)) {
if (!compiler_addop(c, POP_TOP, true)) {
pc->fail_pop_size = 0;
PyObject_Free(pc->fail_pop);
pc->fail_pop = NULL;
Expand Down Expand Up @@ -6733,7 +6686,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 (!compiler_addop_i(c, COPY, 1) || !compiler_pattern(c, alt, pc)) {
if (!compiler_addop_i(c, COPY, 1, true) || !compiler_pattern(c, alt, pc)) {
goto error;
}
// Success!
Expand Down Expand Up @@ -6796,7 +6749,7 @@ compiler_pattern_or(struct compiler *c, pattern_ty p, pattern_context *pc)
}
}
assert(control);
if (!compiler_addop_j(c, JUMP, end) ||
if (!compiler_addop_j(c, JUMP, end, true) ||
!emit_and_reset_fail_pop(c, pc))
{
goto error;
Expand All @@ -6808,7 +6761,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) || !jump_to_fail_pop(c, pc, JUMP)) {
if (!compiler_addop(c, POP_TOP, true) || !jump_to_fail_pop(c, pc, JUMP)) {
goto error;
}
compiler_use_next_block(c, end);
Expand Down Expand Up @@ -7471,7 +7424,7 @@ push_cold_blocks_to_end(struct compiler *c, basicblock *entry, int code_flags) {
if (explicit_jump == NULL) {
return -1;
}
basicblock_add_jump(explicit_jump, JUMP, -1, 0, 0, 0, b->b_next);
basicblock_addop(explicit_jump, JUMP, 0, b->b_next, NO_LOCATION);

explicit_jump->b_cold = 1;
explicit_jump->b_next = b->b_next;
Expand Down