mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-106149: move CFG and basicblock definitions into flowgraph.c, use them as opaque types in compile.c (#107639)
This commit is contained in:
parent
494e3d4436
commit
bafedfbebd
4 changed files with 483 additions and 479 deletions
|
@ -54,6 +54,11 @@ typedef struct {
|
||||||
int s_next_free_label; /* next free label id */
|
int s_next_free_label; /* next free label id */
|
||||||
} _PyCompile_InstructionSequence;
|
} _PyCompile_InstructionSequence;
|
||||||
|
|
||||||
|
int _PyCompile_InstructionSequence_UseLabel(_PyCompile_InstructionSequence *seq, int lbl);
|
||||||
|
int _PyCompile_InstructionSequence_Addop(_PyCompile_InstructionSequence *seq,
|
||||||
|
int opcode, int oparg,
|
||||||
|
_PyCompilerSrcLocation loc);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject *u_name;
|
PyObject *u_name;
|
||||||
PyObject *u_qualname; /* dot-separated qualified name (lazy) */
|
PyObject *u_qualname; /* dot-separated qualified name (lazy) */
|
||||||
|
|
|
@ -11,88 +11,26 @@ extern "C" {
|
||||||
#include "pycore_opcode_utils.h"
|
#include "pycore_opcode_utils.h"
|
||||||
#include "pycore_compile.h"
|
#include "pycore_compile.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int i_opcode;
|
|
||||||
int i_oparg;
|
|
||||||
_PyCompilerSrcLocation i_loc;
|
|
||||||
struct _PyCfgBasicblock_ *i_target; /* target block (if jump instruction) */
|
|
||||||
struct _PyCfgBasicblock_ *i_except; /* target block when exception is raised */
|
|
||||||
} _PyCfgInstruction;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int id;
|
int id;
|
||||||
} _PyCfgJumpTargetLabel;
|
} _PyCfgJumpTargetLabel;
|
||||||
|
|
||||||
|
struct _PyCfgBuilder;
|
||||||
|
|
||||||
typedef struct {
|
int _PyCfgBuilder_UseLabel(struct _PyCfgBuilder *g, _PyCfgJumpTargetLabel lbl);
|
||||||
struct _PyCfgBasicblock_ *handlers[CO_MAXBLOCKS+1];
|
int _PyCfgBuilder_Addop(struct _PyCfgBuilder *g, int opcode, int oparg, _PyCompilerSrcLocation loc);
|
||||||
int depth;
|
|
||||||
} _PyCfgExceptStack;
|
|
||||||
|
|
||||||
typedef struct _PyCfgBasicblock_ {
|
struct _PyCfgBuilder* _PyCfgBuilder_New(void);
|
||||||
/* Each basicblock in a compilation unit is linked via b_list in the
|
void _PyCfgBuilder_Free(struct _PyCfgBuilder *g);
|
||||||
reverse order that the block are allocated. b_list points to the next
|
int _PyCfgBuilder_CheckSize(struct _PyCfgBuilder* g);
|
||||||
block in this list, not to be confused with b_next, which is next by
|
|
||||||
control flow. */
|
|
||||||
struct _PyCfgBasicblock_ *b_list;
|
|
||||||
/* The label of this block if it is a jump target, -1 otherwise */
|
|
||||||
_PyCfgJumpTargetLabel b_label;
|
|
||||||
/* Exception stack at start of block, used by assembler to create the exception handling table */
|
|
||||||
_PyCfgExceptStack *b_exceptstack;
|
|
||||||
/* pointer to an array of instructions, initially NULL */
|
|
||||||
_PyCfgInstruction *b_instr;
|
|
||||||
/* If b_next is non-NULL, it is a pointer to the next
|
|
||||||
block reached by normal control flow. */
|
|
||||||
struct _PyCfgBasicblock_ *b_next;
|
|
||||||
/* number of instructions used */
|
|
||||||
int b_iused;
|
|
||||||
/* length of instruction array (b_instr) */
|
|
||||||
int b_ialloc;
|
|
||||||
/* Used by add_checks_for_loads_of_unknown_variables */
|
|
||||||
uint64_t b_unsafe_locals_mask;
|
|
||||||
/* Number of predecessors that a block has. */
|
|
||||||
int b_predecessors;
|
|
||||||
/* depth of stack upon entry of block, computed by stackdepth() */
|
|
||||||
int b_startdepth;
|
|
||||||
/* Basic block is an exception handler that preserves lasti */
|
|
||||||
unsigned b_preserve_lasti : 1;
|
|
||||||
/* Used by compiler passes to mark whether they have visited a basic block. */
|
|
||||||
unsigned b_visited : 1;
|
|
||||||
/* b_except_handler is used by the cold-detection algorithm to mark exception targets */
|
|
||||||
unsigned b_except_handler : 1;
|
|
||||||
/* b_cold is true if this block is not perf critical (like an exception handler) */
|
|
||||||
unsigned b_cold : 1;
|
|
||||||
/* b_warm is used by the cold-detection algorithm to mark blocks which are definitely not cold */
|
|
||||||
unsigned b_warm : 1;
|
|
||||||
} _PyCfgBasicblock;
|
|
||||||
|
|
||||||
int _PyBasicblock_InsertInstruction(_PyCfgBasicblock *block, int pos, _PyCfgInstruction *instr);
|
int _PyCfg_OptimizeCodeUnit(struct _PyCfgBuilder *g, PyObject *consts, PyObject *const_cache,
|
||||||
|
|
||||||
typedef struct cfg_builder_ {
|
|
||||||
/* The entryblock, at which control flow begins. All blocks of the
|
|
||||||
CFG are reachable through the b_next links */
|
|
||||||
_PyCfgBasicblock *g_entryblock;
|
|
||||||
/* Pointer to the most recently allocated block. By following
|
|
||||||
b_list links, you can reach all allocated blocks. */
|
|
||||||
_PyCfgBasicblock *g_block_list;
|
|
||||||
/* pointer to the block currently being constructed */
|
|
||||||
_PyCfgBasicblock *g_curblock;
|
|
||||||
/* label for the next instruction to be placed */
|
|
||||||
_PyCfgJumpTargetLabel g_current_label;
|
|
||||||
} _PyCfgBuilder;
|
|
||||||
|
|
||||||
int _PyCfgBuilder_UseLabel(_PyCfgBuilder *g, _PyCfgJumpTargetLabel lbl);
|
|
||||||
int _PyCfgBuilder_Addop(_PyCfgBuilder *g, int opcode, int oparg, _PyCompilerSrcLocation loc);
|
|
||||||
|
|
||||||
int _PyCfgBuilder_Init(_PyCfgBuilder *g);
|
|
||||||
void _PyCfgBuilder_Fini(_PyCfgBuilder *g);
|
|
||||||
|
|
||||||
int _PyCfg_OptimizeCodeUnit(_PyCfgBuilder *g, PyObject *consts, PyObject *const_cache,
|
|
||||||
int nlocals, int nparams, int firstlineno);
|
int nlocals, int nparams, int firstlineno);
|
||||||
int _PyCfg_Stackdepth(_PyCfgBuilder *g);
|
|
||||||
void _PyCfg_ConvertPseudoOps(_PyCfgBasicblock *entryblock);
|
int _PyCfg_ToInstructionSequence(struct _PyCfgBuilder *g, _PyCompile_InstructionSequence *seq);
|
||||||
int _PyCfg_ResolveJumps(_PyCfgBuilder *g);
|
int _PyCfg_OptimizedCfgToInstructionSequence(struct _PyCfgBuilder *g, _PyCompile_CodeUnitMetadata *umd,
|
||||||
|
int code_flags, int *stackdepth, int *nlocalsplus,
|
||||||
|
_PyCompile_InstructionSequence *seq);
|
||||||
|
|
||||||
PyCodeObject *
|
PyCodeObject *
|
||||||
_PyAssemble_MakeCodeObject(_PyCompile_CodeUnitMetadata *u, PyObject *const_cache,
|
_PyAssemble_MakeCodeObject(_PyCompile_CodeUnitMetadata *u, PyObject *const_cache,
|
||||||
|
|
432
Python/compile.c
432
Python/compile.c
|
@ -70,9 +70,7 @@
|
||||||
&& ((C)->u->u_ste->ste_type == ModuleBlock))
|
&& ((C)->u->u_ste->ste_type == ModuleBlock))
|
||||||
|
|
||||||
typedef _PyCompilerSrcLocation location;
|
typedef _PyCompilerSrcLocation location;
|
||||||
typedef _PyCfgInstruction cfg_instr;
|
typedef struct _PyCfgBuilder cfg_builder;
|
||||||
typedef _PyCfgBasicblock basicblock;
|
|
||||||
typedef _PyCfgBuilder cfg_builder;
|
|
||||||
|
|
||||||
#define LOCATION(LNO, END_LNO, COL, END_COL) \
|
#define LOCATION(LNO, END_LNO, COL, END_COL) \
|
||||||
((const _PyCompilerSrcLocation){(LNO), (END_LNO), (COL), (END_COL)})
|
((const _PyCompilerSrcLocation){(LNO), (END_LNO), (COL), (END_COL)})
|
||||||
|
@ -101,7 +99,7 @@ static jump_target_label NO_LABEL = {-1};
|
||||||
}
|
}
|
||||||
|
|
||||||
#define USE_LABEL(C, LBL) \
|
#define USE_LABEL(C, LBL) \
|
||||||
RETURN_IF_ERROR(instr_sequence_use_label(INSTR_SEQUENCE(C), (LBL).id))
|
RETURN_IF_ERROR(_PyCompile_InstructionSequence_UseLabel(INSTR_SEQUENCE(C), (LBL).id))
|
||||||
|
|
||||||
|
|
||||||
/* fblockinfo tracks the current frame block.
|
/* fblockinfo tracks the current frame block.
|
||||||
|
@ -218,8 +216,9 @@ instr_sequence_new_label(instr_sequence *seq)
|
||||||
return lbl;
|
return lbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
instr_sequence_use_label(instr_sequence *seq, int lbl) {
|
_PyCompile_InstructionSequence_UseLabel(instr_sequence *seq, int lbl)
|
||||||
|
{
|
||||||
int old_size = seq->s_labelmap_size;
|
int old_size = seq->s_labelmap_size;
|
||||||
RETURN_IF_ERROR(
|
RETURN_IF_ERROR(
|
||||||
_PyCompile_EnsureArrayLargeEnough(lbl,
|
_PyCompile_EnsureArrayLargeEnough(lbl,
|
||||||
|
@ -238,8 +237,9 @@ instr_sequence_use_label(instr_sequence *seq, int lbl) {
|
||||||
|
|
||||||
#define MAX_OPCODE 511
|
#define MAX_OPCODE 511
|
||||||
|
|
||||||
static int
|
int
|
||||||
instr_sequence_addop(instr_sequence *seq, int opcode, int oparg, location loc)
|
_PyCompile_InstructionSequence_Addop(instr_sequence *seq, int opcode, int oparg,
|
||||||
|
location loc)
|
||||||
{
|
{
|
||||||
assert(0 <= opcode && opcode <= MAX_OPCODE);
|
assert(0 <= opcode && opcode <= MAX_OPCODE);
|
||||||
assert(IS_WITHIN_OPCODE_RANGE(opcode));
|
assert(IS_WITHIN_OPCODE_RANGE(opcode));
|
||||||
|
@ -288,10 +288,12 @@ instr_sequence_fini(instr_sequence *seq) {
|
||||||
seq->s_instrs = NULL;
|
seq->s_instrs = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static cfg_builder*
|
||||||
instr_sequence_to_cfg(instr_sequence *seq, cfg_builder *g) {
|
instr_sequence_to_cfg(instr_sequence *seq) {
|
||||||
memset(g, 0, sizeof(cfg_builder));
|
cfg_builder *g = _PyCfgBuilder_New();
|
||||||
RETURN_IF_ERROR(_PyCfgBuilder_Init(g));
|
if (g == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* There can be more than one label for the same offset. The
|
/* There can be more than one label for the same offset. The
|
||||||
* offset2lbl maping selects one of them which we use consistently.
|
* offset2lbl maping selects one of them which we use consistently.
|
||||||
|
@ -300,7 +302,7 @@ instr_sequence_to_cfg(instr_sequence *seq, cfg_builder *g) {
|
||||||
int *offset2lbl = PyMem_Malloc(seq->s_used * sizeof(int));
|
int *offset2lbl = PyMem_Malloc(seq->s_used * sizeof(int));
|
||||||
if (offset2lbl == NULL) {
|
if (offset2lbl == NULL) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
return ERROR;
|
goto error;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < seq->s_used; i++) {
|
for (int i = 0; i < seq->s_used; i++) {
|
||||||
offset2lbl[i] = -1;
|
offset2lbl[i] = -1;
|
||||||
|
@ -336,23 +338,17 @@ instr_sequence_to_cfg(instr_sequence *seq, cfg_builder *g) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_PyCfgBuilder_CheckSize(g) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
PyMem_Free(offset2lbl);
|
PyMem_Free(offset2lbl);
|
||||||
|
return g;
|
||||||
int nblocks = 0;
|
|
||||||
for (basicblock *b = g->g_block_list; b != NULL; b = b->b_list) {
|
|
||||||
nblocks++;
|
|
||||||
}
|
|
||||||
if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) {
|
|
||||||
PyErr_NoMemory();
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
return SUCCESS;
|
|
||||||
error:
|
error:
|
||||||
|
_PyCfgBuilder_Free(g);
|
||||||
PyMem_Free(offset2lbl);
|
PyMem_Free(offset2lbl);
|
||||||
return ERROR;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* The following items change on entry and exit of code blocks.
|
/* The following items change on entry and exit of code blocks.
|
||||||
They must be saved and restored when returning to a block.
|
They must be saved and restored when returning to a block.
|
||||||
*/
|
*/
|
||||||
|
@ -920,7 +916,7 @@ codegen_addop_noarg(instr_sequence *seq, int opcode, location loc)
|
||||||
{
|
{
|
||||||
assert(!OPCODE_HAS_ARG(opcode));
|
assert(!OPCODE_HAS_ARG(opcode));
|
||||||
assert(!IS_ASSEMBLER_OPCODE(opcode));
|
assert(!IS_ASSEMBLER_OPCODE(opcode));
|
||||||
return instr_sequence_addop(seq, opcode, 0, loc);
|
return _PyCompile_InstructionSequence_Addop(seq, opcode, 0, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Py_ssize_t
|
static Py_ssize_t
|
||||||
|
@ -1153,7 +1149,7 @@ codegen_addop_i(instr_sequence *seq, int opcode, Py_ssize_t oparg, location loc)
|
||||||
|
|
||||||
int oparg_ = Py_SAFE_DOWNCAST(oparg, Py_ssize_t, int);
|
int oparg_ = Py_SAFE_DOWNCAST(oparg, Py_ssize_t, int);
|
||||||
assert(!IS_ASSEMBLER_OPCODE(opcode));
|
assert(!IS_ASSEMBLER_OPCODE(opcode));
|
||||||
return instr_sequence_addop(seq, opcode, oparg_, loc);
|
return _PyCompile_InstructionSequence_Addop(seq, opcode, oparg_, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1163,7 +1159,7 @@ codegen_addop_j(instr_sequence *seq, location loc,
|
||||||
assert(IS_LABEL(target));
|
assert(IS_LABEL(target));
|
||||||
assert(OPCODE_HAS_JUMP(opcode) || IS_BLOCK_PUSH_OPCODE(opcode));
|
assert(OPCODE_HAS_JUMP(opcode) || IS_BLOCK_PUSH_OPCODE(opcode));
|
||||||
assert(!IS_ASSEMBLER_OPCODE(opcode));
|
assert(!IS_ASSEMBLER_OPCODE(opcode));
|
||||||
return instr_sequence_addop(seq, opcode, target.id, loc);
|
return _PyCompile_InstructionSequence_Addop(seq, opcode, target.id, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RETURN_IF_ERROR_IN_SCOPE(C, CALL) { \
|
#define RETURN_IF_ERROR_IN_SCOPE(C, CALL) { \
|
||||||
|
@ -7492,201 +7488,6 @@ _PyCompile_ConstCacheMergeOne(PyObject *const_cache, PyObject **obj)
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int *
|
|
||||||
build_cellfixedoffsets(_PyCompile_CodeUnitMetadata *umd)
|
|
||||||
{
|
|
||||||
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
|
|
||||||
int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars);
|
|
||||||
int nfreevars = (int)PyDict_GET_SIZE(umd->u_freevars);
|
|
||||||
|
|
||||||
int noffsets = ncellvars + nfreevars;
|
|
||||||
int *fixed = PyMem_New(int, noffsets);
|
|
||||||
if (fixed == NULL) {
|
|
||||||
PyErr_NoMemory();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < noffsets; i++) {
|
|
||||||
fixed[i] = nlocals + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *varname, *cellindex;
|
|
||||||
Py_ssize_t pos = 0;
|
|
||||||
while (PyDict_Next(umd->u_cellvars, &pos, &varname, &cellindex)) {
|
|
||||||
PyObject *varindex = PyDict_GetItem(umd->u_varnames, varname);
|
|
||||||
if (varindex != NULL) {
|
|
||||||
assert(PyLong_AS_LONG(cellindex) < INT_MAX);
|
|
||||||
assert(PyLong_AS_LONG(varindex) < INT_MAX);
|
|
||||||
int oldindex = (int)PyLong_AS_LONG(cellindex);
|
|
||||||
int argoffset = (int)PyLong_AS_LONG(varindex);
|
|
||||||
fixed[oldindex] = argoffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define IS_GENERATOR(CF) \
|
|
||||||
((CF) & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR))
|
|
||||||
|
|
||||||
static int
|
|
||||||
insert_prefix_instructions(_PyCompile_CodeUnitMetadata *umd, basicblock *entryblock,
|
|
||||||
int *fixed, int nfreevars, int code_flags)
|
|
||||||
{
|
|
||||||
assert(umd->u_firstlineno > 0);
|
|
||||||
|
|
||||||
/* Add the generator prefix instructions. */
|
|
||||||
if (IS_GENERATOR(code_flags)) {
|
|
||||||
/* Note that RETURN_GENERATOR + POP_TOP have a net stack effect
|
|
||||||
* of 0. This is because RETURN_GENERATOR pushes an element
|
|
||||||
* with _PyFrame_StackPush before switching stacks.
|
|
||||||
*/
|
|
||||||
cfg_instr make_gen = {
|
|
||||||
.i_opcode = RETURN_GENERATOR,
|
|
||||||
.i_oparg = 0,
|
|
||||||
.i_loc = LOCATION(umd->u_firstlineno, umd->u_firstlineno, -1, -1),
|
|
||||||
.i_target = NULL,
|
|
||||||
};
|
|
||||||
RETURN_IF_ERROR(_PyBasicblock_InsertInstruction(entryblock, 0, &make_gen));
|
|
||||||
cfg_instr pop_top = {
|
|
||||||
.i_opcode = POP_TOP,
|
|
||||||
.i_oparg = 0,
|
|
||||||
.i_loc = NO_LOCATION,
|
|
||||||
.i_target = NULL,
|
|
||||||
};
|
|
||||||
RETURN_IF_ERROR(_PyBasicblock_InsertInstruction(entryblock, 1, &pop_top));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set up cells for any variable that escapes, to be put in a closure. */
|
|
||||||
const int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars);
|
|
||||||
if (ncellvars) {
|
|
||||||
// umd->u_cellvars has the cells out of order so we sort them
|
|
||||||
// before adding the MAKE_CELL instructions. Note that we
|
|
||||||
// adjust for arg cells, which come first.
|
|
||||||
const int nvars = ncellvars + (int)PyDict_GET_SIZE(umd->u_varnames);
|
|
||||||
int *sorted = PyMem_RawCalloc(nvars, sizeof(int));
|
|
||||||
if (sorted == NULL) {
|
|
||||||
PyErr_NoMemory();
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < ncellvars; i++) {
|
|
||||||
sorted[fixed[i]] = i + 1;
|
|
||||||
}
|
|
||||||
for (int i = 0, ncellsused = 0; ncellsused < ncellvars; i++) {
|
|
||||||
int oldindex = sorted[i] - 1;
|
|
||||||
if (oldindex == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cfg_instr make_cell = {
|
|
||||||
.i_opcode = MAKE_CELL,
|
|
||||||
// This will get fixed in offset_derefs().
|
|
||||||
.i_oparg = oldindex,
|
|
||||||
.i_loc = NO_LOCATION,
|
|
||||||
.i_target = NULL,
|
|
||||||
};
|
|
||||||
if (_PyBasicblock_InsertInstruction(entryblock, ncellsused, &make_cell) < 0) {
|
|
||||||
PyMem_RawFree(sorted);
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
ncellsused += 1;
|
|
||||||
}
|
|
||||||
PyMem_RawFree(sorted);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nfreevars) {
|
|
||||||
cfg_instr copy_frees = {
|
|
||||||
.i_opcode = COPY_FREE_VARS,
|
|
||||||
.i_oparg = nfreevars,
|
|
||||||
.i_loc = NO_LOCATION,
|
|
||||||
.i_target = NULL,
|
|
||||||
};
|
|
||||||
RETURN_IF_ERROR(_PyBasicblock_InsertInstruction(entryblock, 0, ©_frees));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
fix_cell_offsets(_PyCompile_CodeUnitMetadata *umd, basicblock *entryblock, int *fixedmap)
|
|
||||||
{
|
|
||||||
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
|
|
||||||
int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars);
|
|
||||||
int nfreevars = (int)PyDict_GET_SIZE(umd->u_freevars);
|
|
||||||
int noffsets = ncellvars + nfreevars;
|
|
||||||
|
|
||||||
// First deal with duplicates (arg cells).
|
|
||||||
int numdropped = 0;
|
|
||||||
for (int i = 0; i < noffsets ; i++) {
|
|
||||||
if (fixedmap[i] == i + nlocals) {
|
|
||||||
fixedmap[i] -= numdropped;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// It was a duplicate (cell/arg).
|
|
||||||
numdropped += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then update offsets, either relative to locals or by cell2arg.
|
|
||||||
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
|
||||||
for (int i = 0; i < b->b_iused; i++) {
|
|
||||||
cfg_instr *inst = &b->b_instr[i];
|
|
||||||
// This is called before extended args are generated.
|
|
||||||
assert(inst->i_opcode != EXTENDED_ARG);
|
|
||||||
int oldoffset = inst->i_oparg;
|
|
||||||
switch(inst->i_opcode) {
|
|
||||||
case MAKE_CELL:
|
|
||||||
case LOAD_CLOSURE:
|
|
||||||
case LOAD_DEREF:
|
|
||||||
case STORE_DEREF:
|
|
||||||
case DELETE_DEREF:
|
|
||||||
case LOAD_FROM_DICT_OR_DEREF:
|
|
||||||
assert(oldoffset >= 0);
|
|
||||||
assert(oldoffset < noffsets);
|
|
||||||
assert(fixedmap[oldoffset] >= 0);
|
|
||||||
inst->i_oparg = fixedmap[oldoffset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return numdropped;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
prepare_localsplus(_PyCompile_CodeUnitMetadata *umd, cfg_builder *g, int code_flags)
|
|
||||||
{
|
|
||||||
assert(PyDict_GET_SIZE(umd->u_varnames) < INT_MAX);
|
|
||||||
assert(PyDict_GET_SIZE(umd->u_cellvars) < INT_MAX);
|
|
||||||
assert(PyDict_GET_SIZE(umd->u_freevars) < INT_MAX);
|
|
||||||
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
|
|
||||||
int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars);
|
|
||||||
int nfreevars = (int)PyDict_GET_SIZE(umd->u_freevars);
|
|
||||||
assert(INT_MAX - nlocals - ncellvars > 0);
|
|
||||||
assert(INT_MAX - nlocals - ncellvars - nfreevars > 0);
|
|
||||||
int nlocalsplus = nlocals + ncellvars + nfreevars;
|
|
||||||
int* cellfixedoffsets = build_cellfixedoffsets(umd);
|
|
||||||
if (cellfixedoffsets == NULL) {
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This must be called before fix_cell_offsets().
|
|
||||||
if (insert_prefix_instructions(umd, g->g_entryblock, cellfixedoffsets, nfreevars, code_flags)) {
|
|
||||||
PyMem_Free(cellfixedoffsets);
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numdropped = fix_cell_offsets(umd, g->g_entryblock, cellfixedoffsets);
|
|
||||||
PyMem_Free(cellfixedoffsets); // At this point we're done with it.
|
|
||||||
cellfixedoffsets = NULL;
|
|
||||||
if (numdropped < 0) {
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
nlocalsplus -= numdropped;
|
|
||||||
return nlocalsplus;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
add_return_at_end(struct compiler *c, int addNone)
|
add_return_at_end(struct compiler *c, int addNone)
|
||||||
{
|
{
|
||||||
|
@ -7700,12 +7501,11 @@ add_return_at_end(struct compiler *c, int addNone)
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cfg_to_instr_sequence(cfg_builder *g, instr_sequence *seq);
|
|
||||||
|
|
||||||
static PyCodeObject *
|
static PyCodeObject *
|
||||||
optimize_and_assemble_code_unit(struct compiler_unit *u, PyObject *const_cache,
|
optimize_and_assemble_code_unit(struct compiler_unit *u, PyObject *const_cache,
|
||||||
int code_flags, PyObject *filename)
|
int code_flags, PyObject *filename)
|
||||||
{
|
{
|
||||||
|
cfg_builder *g = NULL;
|
||||||
instr_sequence optimized_instrs;
|
instr_sequence optimized_instrs;
|
||||||
memset(&optimized_instrs, 0, sizeof(instr_sequence));
|
memset(&optimized_instrs, 0, sizeof(instr_sequence));
|
||||||
|
|
||||||
|
@ -7714,51 +7514,28 @@ optimize_and_assemble_code_unit(struct compiler_unit *u, PyObject *const_cache,
|
||||||
if (consts == NULL) {
|
if (consts == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
cfg_builder g;
|
g = instr_sequence_to_cfg(&u->u_instr_sequence);
|
||||||
if (instr_sequence_to_cfg(&u->u_instr_sequence, &g) < 0) {
|
if (g == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
int nparams = (int)PyList_GET_SIZE(u->u_ste->ste_varnames);
|
|
||||||
int nlocals = (int)PyDict_GET_SIZE(u->u_metadata.u_varnames);
|
int nlocals = (int)PyDict_GET_SIZE(u->u_metadata.u_varnames);
|
||||||
|
int nparams = (int)PyList_GET_SIZE(u->u_ste->ste_varnames);
|
||||||
assert(u->u_metadata.u_firstlineno);
|
assert(u->u_metadata.u_firstlineno);
|
||||||
if (_PyCfg_OptimizeCodeUnit(&g, consts, const_cache, nlocals,
|
|
||||||
|
if (_PyCfg_OptimizeCodeUnit(g, consts, const_cache, nlocals,
|
||||||
nparams, u->u_metadata.u_firstlineno) < 0) {
|
nparams, u->u_metadata.u_firstlineno) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stackdepth = _PyCfg_Stackdepth(&g);
|
int stackdepth;
|
||||||
if (stackdepth < 0) {
|
int nlocalsplus;
|
||||||
|
if (_PyCfg_OptimizedCfgToInstructionSequence(g, &u->u_metadata, code_flags,
|
||||||
|
&stackdepth, &nlocalsplus,
|
||||||
|
&optimized_instrs) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* prepare_localsplus adds instructions for generators that push
|
|
||||||
* and pop an item on the stack. This assertion makes sure there
|
|
||||||
* is space on the stack for that.
|
|
||||||
* It should always be true, because a generator must have at
|
|
||||||
* least one expression or call to INTRINSIC_STOPITERATION_ERROR,
|
|
||||||
* which requires stackspace.
|
|
||||||
*/
|
|
||||||
assert(!(IS_GENERATOR(code_flags) && stackdepth == 0));
|
|
||||||
|
|
||||||
/** Assembly **/
|
/** Assembly **/
|
||||||
int nlocalsplus = prepare_localsplus(&u->u_metadata, &g, code_flags);
|
|
||||||
if (nlocalsplus < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
_PyCfg_ConvertPseudoOps(g.g_entryblock);
|
|
||||||
|
|
||||||
/* Order of basic blocks must have been determined by now */
|
|
||||||
|
|
||||||
if (_PyCfg_ResolveJumps(&g) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Can't modify the bytecode after computing jump offsets. */
|
|
||||||
|
|
||||||
if (cfg_to_instr_sequence(&g, &optimized_instrs) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
co = _PyAssemble_MakeCodeObject(&u->u_metadata, const_cache, consts,
|
co = _PyAssemble_MakeCodeObject(&u->u_metadata, const_cache, consts,
|
||||||
stackdepth, &optimized_instrs, nlocalsplus,
|
stackdepth, &optimized_instrs, nlocalsplus,
|
||||||
|
@ -7767,7 +7544,7 @@ optimize_and_assemble_code_unit(struct compiler_unit *u, PyObject *const_cache,
|
||||||
error:
|
error:
|
||||||
Py_XDECREF(consts);
|
Py_XDECREF(consts);
|
||||||
instr_sequence_fini(&optimized_instrs);
|
instr_sequence_fini(&optimized_instrs);
|
||||||
_PyCfgBuilder_Fini(&g);
|
_PyCfgBuilder_Free(g);
|
||||||
return co;
|
return co;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7790,39 +7567,6 @@ optimize_and_assemble(struct compiler *c, int addNone)
|
||||||
return optimize_and_assemble_code_unit(u, const_cache, code_flags, filename);
|
return optimize_and_assemble_code_unit(u, const_cache, code_flags, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
cfg_to_instr_sequence(cfg_builder *g, instr_sequence *seq)
|
|
||||||
{
|
|
||||||
int lbl = 0;
|
|
||||||
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
|
||||||
b->b_label = (jump_target_label){lbl};
|
|
||||||
lbl += b->b_iused;
|
|
||||||
}
|
|
||||||
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
|
||||||
RETURN_IF_ERROR(instr_sequence_use_label(seq, b->b_label.id));
|
|
||||||
for (int i = 0; i < b->b_iused; i++) {
|
|
||||||
cfg_instr *instr = &b->b_instr[i];
|
|
||||||
if (OPCODE_HAS_JUMP(instr->i_opcode)) {
|
|
||||||
instr->i_oparg = instr->i_target->b_label.id;
|
|
||||||
}
|
|
||||||
RETURN_IF_ERROR(
|
|
||||||
instr_sequence_addop(seq, instr->i_opcode, instr->i_oparg, instr->i_loc));
|
|
||||||
|
|
||||||
_PyCompile_ExceptHandlerInfo *hi = &seq->s_instrs[seq->s_used-1].i_except_handler_info;
|
|
||||||
if (instr->i_except != NULL) {
|
|
||||||
hi->h_label = instr->i_except->b_label.id;
|
|
||||||
hi->h_startdepth = instr->i_except->b_startdepth;
|
|
||||||
hi->h_preserve_lasti = instr->i_except->b_preserve_lasti;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hi->h_label = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Access to compiler optimizations for unit tests.
|
/* Access to compiler optimizations for unit tests.
|
||||||
*
|
*
|
||||||
* _PyCompile_CodeGen takes and AST, applies code-gen and
|
* _PyCompile_CodeGen takes and AST, applies code-gen and
|
||||||
|
@ -7872,7 +7616,7 @@ instructions_to_instr_sequence(PyObject *instructions, instr_sequence *seq)
|
||||||
|
|
||||||
for (int i = 0; i < num_insts; i++) {
|
for (int i = 0; i < num_insts; i++) {
|
||||||
if (is_target[i]) {
|
if (is_target[i]) {
|
||||||
if (instr_sequence_use_label(seq, i) < 0) {
|
if (_PyCompile_InstructionSequence_UseLabel(seq, i) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7912,7 +7656,7 @@ instructions_to_instr_sequence(PyObject *instructions, instr_sequence *seq)
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (instr_sequence_addop(seq, opcode, oparg, loc) < 0) {
|
if (_PyCompile_InstructionSequence_Addop(seq, opcode, oparg, loc) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7923,23 +7667,26 @@ error:
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static cfg_builder*
|
||||||
instructions_to_cfg(PyObject *instructions, cfg_builder *g)
|
instructions_to_cfg(PyObject *instructions)
|
||||||
{
|
{
|
||||||
|
cfg_builder *g = NULL;
|
||||||
instr_sequence seq;
|
instr_sequence seq;
|
||||||
memset(&seq, 0, sizeof(instr_sequence));
|
memset(&seq, 0, sizeof(instr_sequence));
|
||||||
|
|
||||||
if (instructions_to_instr_sequence(instructions, &seq) < 0) {
|
if (instructions_to_instr_sequence(instructions, &seq) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (instr_sequence_to_cfg(&seq, g) < 0) {
|
g = instr_sequence_to_cfg(&seq);
|
||||||
|
if (g == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
instr_sequence_fini(&seq);
|
instr_sequence_fini(&seq);
|
||||||
return SUCCESS;
|
return g;
|
||||||
error:
|
error:
|
||||||
|
_PyCfgBuilder_Free(g);
|
||||||
instr_sequence_fini(&seq);
|
instr_sequence_fini(&seq);
|
||||||
return ERROR;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -7978,42 +7725,14 @@ error:
|
||||||
static PyObject *
|
static PyObject *
|
||||||
cfg_to_instructions(cfg_builder *g)
|
cfg_to_instructions(cfg_builder *g)
|
||||||
{
|
{
|
||||||
PyObject *instructions = PyList_New(0);
|
instr_sequence seq;
|
||||||
if (instructions == NULL) {
|
memset(&seq, 0, sizeof(seq));
|
||||||
|
if (_PyCfg_ToInstructionSequence(g, &seq) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
int lbl = 0;
|
PyObject *res = instr_sequence_to_instructions(&seq);
|
||||||
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
instr_sequence_fini(&seq);
|
||||||
b->b_label = (jump_target_label){lbl};
|
return res;
|
||||||
lbl += b->b_iused;
|
|
||||||
}
|
|
||||||
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
|
||||||
for (int i = 0; i < b->b_iused; i++) {
|
|
||||||
cfg_instr *instr = &b->b_instr[i];
|
|
||||||
location loc = instr->i_loc;
|
|
||||||
int arg = HAS_TARGET(instr->i_opcode) ?
|
|
||||||
instr->i_target->b_label.id : instr->i_oparg;
|
|
||||||
|
|
||||||
PyObject *inst_tuple = Py_BuildValue(
|
|
||||||
"(iiiiii)", instr->i_opcode, arg,
|
|
||||||
loc.lineno, loc.end_lineno,
|
|
||||||
loc.col_offset, loc.end_col_offset);
|
|
||||||
if (inst_tuple == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PyList_Append(instructions, inst_tuple) != 0) {
|
|
||||||
Py_DECREF(inst_tuple);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
Py_DECREF(inst_tuple);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return instructions;
|
|
||||||
error:
|
|
||||||
Py_DECREF(instructions);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// C implementation of inspect.cleandoc()
|
// C implementation of inspect.cleandoc()
|
||||||
|
@ -8195,34 +7914,36 @@ finally:
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts, int nlocals)
|
_PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts, int nlocals)
|
||||||
{
|
{
|
||||||
|
cfg_builder *g = NULL;
|
||||||
PyObject *res = NULL;
|
PyObject *res = NULL;
|
||||||
PyObject *const_cache = PyDict_New();
|
PyObject *const_cache = PyDict_New();
|
||||||
if (const_cache == NULL) {
|
if (const_cache == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_builder g;
|
g = instructions_to_cfg(instructions);
|
||||||
if (instructions_to_cfg(instructions, &g) < 0) {
|
if (g == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
int nparams = 0, firstlineno = 1;
|
int nparams = 0, firstlineno = 1;
|
||||||
if (_PyCfg_OptimizeCodeUnit(&g, consts, const_cache, nlocals,
|
if (_PyCfg_OptimizeCodeUnit(g, consts, const_cache, nlocals,
|
||||||
nparams, firstlineno) < 0) {
|
nparams, firstlineno) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
res = cfg_to_instructions(&g);
|
res = cfg_to_instructions(g);
|
||||||
error:
|
error:
|
||||||
Py_DECREF(const_cache);
|
Py_DECREF(const_cache);
|
||||||
_PyCfgBuilder_Fini(&g);
|
_PyCfgBuilder_Free(g);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _PyCfg_JumpLabelsToTargets(basicblock *entryblock);
|
int _PyCfg_JumpLabelsToTargets(cfg_builder *g);
|
||||||
|
|
||||||
PyCodeObject *
|
PyCodeObject *
|
||||||
_PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename,
|
_PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename,
|
||||||
PyObject *instructions)
|
PyObject *instructions)
|
||||||
{
|
{
|
||||||
|
cfg_builder *g = NULL;
|
||||||
PyCodeObject *co = NULL;
|
PyCodeObject *co = NULL;
|
||||||
instr_sequence optimized_instrs;
|
instr_sequence optimized_instrs;
|
||||||
memset(&optimized_instrs, 0, sizeof(instr_sequence));
|
memset(&optimized_instrs, 0, sizeof(instr_sequence));
|
||||||
|
@ -8232,37 +7953,20 @@ _PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_builder g;
|
g = instructions_to_cfg(instructions);
|
||||||
if (instructions_to_cfg(instructions, &g) < 0) {
|
if (g == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_PyCfg_JumpLabelsToTargets(g.g_entryblock) < 0) {
|
if (_PyCfg_JumpLabelsToTargets(g) < 0) {
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stackdepth = _PyCfg_Stackdepth(&g);
|
|
||||||
if (stackdepth < 0) {
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int code_flags = 0;
|
int code_flags = 0;
|
||||||
int nlocalsplus = prepare_localsplus(umd, &g, code_flags);
|
int stackdepth, nlocalsplus;
|
||||||
if (nlocalsplus < 0) {
|
if (_PyCfg_OptimizedCfgToInstructionSequence(g, umd, code_flags,
|
||||||
goto error;
|
&stackdepth, &nlocalsplus,
|
||||||
}
|
&optimized_instrs) < 0) {
|
||||||
|
|
||||||
_PyCfg_ConvertPseudoOps(g.g_entryblock);
|
|
||||||
|
|
||||||
/* Order of basic blocks must have been determined by now */
|
|
||||||
|
|
||||||
if (_PyCfg_ResolveJumps(&g) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Can't modify the bytecode after computing jump offsets. */
|
|
||||||
|
|
||||||
if (cfg_to_instr_sequence(&g, &optimized_instrs) < 0) {
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8277,7 +7981,7 @@ _PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename,
|
||||||
|
|
||||||
error:
|
error:
|
||||||
Py_DECREF(const_cache);
|
Py_DECREF(const_cache);
|
||||||
_PyCfgBuilder_Fini(&g);
|
_PyCfgBuilder_Free(g);
|
||||||
instr_sequence_fini(&optimized_instrs);
|
instr_sequence_fini(&optimized_instrs);
|
||||||
return co;
|
return co;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,15 +24,75 @@
|
||||||
|
|
||||||
typedef _PyCompilerSrcLocation location;
|
typedef _PyCompilerSrcLocation location;
|
||||||
typedef _PyCfgJumpTargetLabel jump_target_label;
|
typedef _PyCfgJumpTargetLabel jump_target_label;
|
||||||
typedef _PyCfgBasicblock basicblock;
|
|
||||||
typedef _PyCfgBuilder cfg_builder;
|
typedef struct _PyCfgInstruction {
|
||||||
typedef _PyCfgInstruction cfg_instr;
|
int i_opcode;
|
||||||
|
int i_oparg;
|
||||||
|
_PyCompilerSrcLocation i_loc;
|
||||||
|
struct _PyCfgBasicblock *i_target; /* target block (if jump instruction) */
|
||||||
|
struct _PyCfgBasicblock *i_except; /* target block when exception is raised */
|
||||||
|
} cfg_instr;
|
||||||
|
|
||||||
|
typedef struct _PyCfgBasicblock {
|
||||||
|
/* 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
|
||||||
|
block in this list, not to be confused with b_next, which is next by
|
||||||
|
control flow. */
|
||||||
|
struct _PyCfgBasicblock *b_list;
|
||||||
|
/* The label of this block if it is a jump target, -1 otherwise */
|
||||||
|
_PyCfgJumpTargetLabel b_label;
|
||||||
|
/* Exception stack at start of block, used by assembler to create the exception handling table */
|
||||||
|
struct _PyCfgExceptStack *b_exceptstack;
|
||||||
|
/* pointer to an array of instructions, initially NULL */
|
||||||
|
cfg_instr *b_instr;
|
||||||
|
/* If b_next is non-NULL, it is a pointer to the next
|
||||||
|
block reached by normal control flow. */
|
||||||
|
struct _PyCfgBasicblock *b_next;
|
||||||
|
/* number of instructions used */
|
||||||
|
int b_iused;
|
||||||
|
/* length of instruction array (b_instr) */
|
||||||
|
int b_ialloc;
|
||||||
|
/* Used by add_checks_for_loads_of_unknown_variables */
|
||||||
|
uint64_t b_unsafe_locals_mask;
|
||||||
|
/* Number of predecessors that a block has. */
|
||||||
|
int b_predecessors;
|
||||||
|
/* depth of stack upon entry of block, computed by stackdepth() */
|
||||||
|
int b_startdepth;
|
||||||
|
/* Basic block is an exception handler that preserves lasti */
|
||||||
|
unsigned b_preserve_lasti : 1;
|
||||||
|
/* Used by compiler passes to mark whether they have visited a basic block. */
|
||||||
|
unsigned b_visited : 1;
|
||||||
|
/* b_except_handler is used by the cold-detection algorithm to mark exception targets */
|
||||||
|
unsigned b_except_handler : 1;
|
||||||
|
/* b_cold is true if this block is not perf critical (like an exception handler) */
|
||||||
|
unsigned b_cold : 1;
|
||||||
|
/* b_warm is used by the cold-detection algorithm to mark blocks which are definitely not cold */
|
||||||
|
unsigned b_warm : 1;
|
||||||
|
} basicblock;
|
||||||
|
|
||||||
|
|
||||||
|
struct _PyCfgBuilder {
|
||||||
|
/* The entryblock, at which control flow begins. All blocks of the
|
||||||
|
CFG are reachable through the b_next links */
|
||||||
|
struct _PyCfgBasicblock *g_entryblock;
|
||||||
|
/* Pointer to the most recently allocated block. By following
|
||||||
|
b_list links, you can reach all allocated blocks. */
|
||||||
|
struct _PyCfgBasicblock *g_block_list;
|
||||||
|
/* pointer to the block currently being constructed */
|
||||||
|
struct _PyCfgBasicblock *g_curblock;
|
||||||
|
/* label for the next instruction to be placed */
|
||||||
|
_PyCfgJumpTargetLabel g_current_label;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _PyCfgBuilder cfg_builder;
|
||||||
|
|
||||||
static const jump_target_label NO_LABEL = {-1};
|
static const jump_target_label NO_LABEL = {-1};
|
||||||
|
|
||||||
#define SAME_LABEL(L1, L2) ((L1).id == (L2).id)
|
#define SAME_LABEL(L1, L2) ((L1).id == (L2).id)
|
||||||
#define IS_LABEL(L) (!SAME_LABEL((L), (NO_LABEL)))
|
#define IS_LABEL(L) (!SAME_LABEL((L), (NO_LABEL)))
|
||||||
|
|
||||||
|
#define LOCATION(LNO, END_LNO, COL, END_COL) \
|
||||||
|
((const _PyCompilerSrcLocation){(LNO), (END_LNO), (COL), (END_COL)})
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
is_block_push(cfg_instr *i)
|
is_block_push(cfg_instr *i)
|
||||||
|
@ -50,7 +110,7 @@ is_jump(cfg_instr *i)
|
||||||
#define INSTR_SET_OP1(I, OP, ARG) \
|
#define INSTR_SET_OP1(I, OP, ARG) \
|
||||||
do { \
|
do { \
|
||||||
assert(OPCODE_HAS_ARG(OP)); \
|
assert(OPCODE_HAS_ARG(OP)); \
|
||||||
_PyCfgInstruction *_instr__ptr_ = (I); \
|
cfg_instr *_instr__ptr_ = (I); \
|
||||||
_instr__ptr_->i_opcode = (OP); \
|
_instr__ptr_->i_opcode = (OP); \
|
||||||
_instr__ptr_->i_oparg = (ARG); \
|
_instr__ptr_->i_oparg = (ARG); \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
@ -59,7 +119,7 @@ is_jump(cfg_instr *i)
|
||||||
#define INSTR_SET_OP0(I, OP) \
|
#define INSTR_SET_OP0(I, OP) \
|
||||||
do { \
|
do { \
|
||||||
assert(!OPCODE_HAS_ARG(OP)); \
|
assert(!OPCODE_HAS_ARG(OP)); \
|
||||||
_PyCfgInstruction *_instr__ptr_ = (I); \
|
cfg_instr *_instr__ptr_ = (I); \
|
||||||
_instr__ptr_->i_opcode = (OP); \
|
_instr__ptr_->i_opcode = (OP); \
|
||||||
_instr__ptr_->i_oparg = 0; \
|
_instr__ptr_->i_oparg = 0; \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
@ -148,8 +208,8 @@ basicblock_last_instr(const basicblock *b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
basicblock_nofallthrough(const _PyCfgBasicblock *b) {
|
basicblock_nofallthrough(const basicblock *b) {
|
||||||
_PyCfgInstruction *last = basicblock_last_instr(b);
|
cfg_instr *last = basicblock_last_instr(b);
|
||||||
return (last &&
|
return (last &&
|
||||||
(IS_SCOPE_EXIT_OPCODE(last->i_opcode) ||
|
(IS_SCOPE_EXIT_OPCODE(last->i_opcode) ||
|
||||||
IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)));
|
IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)));
|
||||||
|
@ -175,8 +235,8 @@ copy_basicblock(cfg_builder *g, basicblock *block)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_PyBasicblock_InsertInstruction(basicblock *block, int pos, cfg_instr *instr) {
|
basicblock_insert_instruction(basicblock *block, int pos, cfg_instr *instr) {
|
||||||
RETURN_IF_ERROR(basicblock_next_instr(block));
|
RETURN_IF_ERROR(basicblock_next_instr(block));
|
||||||
for (int i = block->b_iused - 1; i > pos; i--) {
|
for (int i = block->b_iused - 1; i > pos; i--) {
|
||||||
block->b_instr[i] = block->b_instr[i-1];
|
block->b_instr[i] = block->b_instr[i-1];
|
||||||
|
@ -311,8 +371,8 @@ cfg_builder_check(cfg_builder *g)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int
|
static int
|
||||||
_PyCfgBuilder_Init(cfg_builder *g)
|
init_cfg_builder(cfg_builder *g)
|
||||||
{
|
{
|
||||||
g->g_block_list = NULL;
|
g->g_block_list = NULL;
|
||||||
basicblock *block = cfg_builder_new_block(g);
|
basicblock *block = cfg_builder_new_block(g);
|
||||||
|
@ -324,9 +384,28 @@ _PyCfgBuilder_Init(cfg_builder *g)
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
cfg_builder *
|
||||||
_PyCfgBuilder_Fini(cfg_builder* g)
|
_PyCfgBuilder_New(void)
|
||||||
{
|
{
|
||||||
|
cfg_builder *g = PyMem_Malloc(sizeof(cfg_builder));
|
||||||
|
if (g == NULL) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(g, 0, sizeof(cfg_builder));
|
||||||
|
if (init_cfg_builder(g) < 0) {
|
||||||
|
PyMem_Free(g);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyCfgBuilder_Free(cfg_builder *g)
|
||||||
|
{
|
||||||
|
if (g == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
assert(cfg_builder_check(g));
|
assert(cfg_builder_check(g));
|
||||||
basicblock *b = g->g_block_list;
|
basicblock *b = g->g_block_list;
|
||||||
while (b != NULL) {
|
while (b != NULL) {
|
||||||
|
@ -337,6 +416,21 @@ _PyCfgBuilder_Fini(cfg_builder* g)
|
||||||
PyObject_Free((void *)b);
|
PyObject_Free((void *)b);
|
||||||
b = next;
|
b = next;
|
||||||
}
|
}
|
||||||
|
PyMem_Free(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyCfgBuilder_CheckSize(cfg_builder *g)
|
||||||
|
{
|
||||||
|
int nblocks = 0;
|
||||||
|
for (basicblock *b = g->g_block_list; b != NULL; b = b->b_list) {
|
||||||
|
nblocks++;
|
||||||
|
}
|
||||||
|
if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -450,7 +544,7 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
normalize_jumps(_PyCfgBuilder *g)
|
normalize_jumps(cfg_builder *g)
|
||||||
{
|
{
|
||||||
basicblock *entryblock = g->g_entryblock;
|
basicblock *entryblock = g->g_entryblock;
|
||||||
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
||||||
|
@ -463,14 +557,6 @@ normalize_jumps(_PyCfgBuilder *g)
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
_PyCfg_ResolveJumps(_PyCfgBuilder *g)
|
|
||||||
{
|
|
||||||
RETURN_IF_ERROR(normalize_jumps(g));
|
|
||||||
assert(no_redundant_jumps(g));
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
check_cfg(cfg_builder *g) {
|
check_cfg(cfg_builder *g) {
|
||||||
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
||||||
|
@ -529,9 +615,9 @@ translate_jump_labels_to_targets(basicblock *entryblock)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyCfg_JumpLabelsToTargets(basicblock *entryblock)
|
_PyCfg_JumpLabelsToTargets(cfg_builder *g)
|
||||||
{
|
{
|
||||||
return translate_jump_labels_to_targets(entryblock);
|
return translate_jump_labels_to_targets(g->g_entryblock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -553,10 +639,14 @@ mark_except_handlers(basicblock *entryblock) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef _PyCfgExceptStack ExceptStack;
|
struct _PyCfgExceptStack {
|
||||||
|
basicblock *handlers[CO_MAXBLOCKS+1];
|
||||||
|
int depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static basicblock *
|
static basicblock *
|
||||||
push_except_block(ExceptStack *stack, cfg_instr *setup) {
|
push_except_block(struct _PyCfgExceptStack *stack, cfg_instr *setup) {
|
||||||
assert(is_block_push(setup));
|
assert(is_block_push(setup));
|
||||||
int opcode = setup->i_opcode;
|
int opcode = setup->i_opcode;
|
||||||
basicblock * target = setup->i_target;
|
basicblock * target = setup->i_target;
|
||||||
|
@ -568,19 +658,19 @@ push_except_block(ExceptStack *stack, cfg_instr *setup) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static basicblock *
|
static basicblock *
|
||||||
pop_except_block(ExceptStack *stack) {
|
pop_except_block(struct _PyCfgExceptStack *stack) {
|
||||||
assert(stack->depth > 0);
|
assert(stack->depth > 0);
|
||||||
return stack->handlers[--stack->depth];
|
return stack->handlers[--stack->depth];
|
||||||
}
|
}
|
||||||
|
|
||||||
static basicblock *
|
static basicblock *
|
||||||
except_stack_top(ExceptStack *stack) {
|
except_stack_top(struct _PyCfgExceptStack *stack) {
|
||||||
return stack->handlers[stack->depth];
|
return stack->handlers[stack->depth];
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExceptStack *
|
static struct _PyCfgExceptStack *
|
||||||
make_except_stack(void) {
|
make_except_stack(void) {
|
||||||
ExceptStack *new = PyMem_Malloc(sizeof(ExceptStack));
|
struct _PyCfgExceptStack *new = PyMem_Malloc(sizeof(struct _PyCfgExceptStack));
|
||||||
if (new == NULL) {
|
if (new == NULL) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -590,14 +680,14 @@ make_except_stack(void) {
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExceptStack *
|
static struct _PyCfgExceptStack *
|
||||||
copy_except_stack(ExceptStack *stack) {
|
copy_except_stack(struct _PyCfgExceptStack *stack) {
|
||||||
ExceptStack *copy = PyMem_Malloc(sizeof(ExceptStack));
|
struct _PyCfgExceptStack *copy = PyMem_Malloc(sizeof(struct _PyCfgExceptStack));
|
||||||
if (copy == NULL) {
|
if (copy == NULL) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memcpy(copy, stack, sizeof(ExceptStack));
|
memcpy(copy, stack, sizeof(struct _PyCfgExceptStack));
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,8 +723,8 @@ stackdepth_push(basicblock ***sp, basicblock *b, int depth)
|
||||||
/* Find the flow path that needs the largest stack. We assume that
|
/* Find the flow path that needs the largest stack. We assume that
|
||||||
* cycles in the flow graph have no net effect on the stack depth.
|
* cycles in the flow graph have no net effect on the stack depth.
|
||||||
*/
|
*/
|
||||||
int
|
static int
|
||||||
_PyCfg_Stackdepth(cfg_builder *g)
|
calculate_stackdepth(cfg_builder *g)
|
||||||
{
|
{
|
||||||
basicblock *entryblock = g->g_entryblock;
|
basicblock *entryblock = g->g_entryblock;
|
||||||
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
||||||
|
@ -723,7 +813,7 @@ label_exception_targets(basicblock *entryblock) {
|
||||||
if (todo_stack == NULL) {
|
if (todo_stack == NULL) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
ExceptStack *except_stack = make_except_stack();
|
struct _PyCfgExceptStack *except_stack = make_except_stack();
|
||||||
if (except_stack == NULL) {
|
if (except_stack == NULL) {
|
||||||
PyMem_Free(todo_stack);
|
PyMem_Free(todo_stack);
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
@ -747,7 +837,7 @@ label_exception_targets(basicblock *entryblock) {
|
||||||
cfg_instr *instr = &b->b_instr[i];
|
cfg_instr *instr = &b->b_instr[i];
|
||||||
if (is_block_push(instr)) {
|
if (is_block_push(instr)) {
|
||||||
if (!instr->i_target->b_visited) {
|
if (!instr->i_target->b_visited) {
|
||||||
ExceptStack *copy = copy_except_stack(except_stack);
|
struct _PyCfgExceptStack *copy = copy_except_stack(except_stack);
|
||||||
if (copy == NULL) {
|
if (copy == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -766,7 +856,7 @@ label_exception_targets(basicblock *entryblock) {
|
||||||
assert(i == b->b_iused -1);
|
assert(i == b->b_iused -1);
|
||||||
if (!instr->i_target->b_visited) {
|
if (!instr->i_target->b_visited) {
|
||||||
if (BB_HAS_FALLTHROUGH(b)) {
|
if (BB_HAS_FALLTHROUGH(b)) {
|
||||||
ExceptStack *copy = copy_except_stack(except_stack);
|
struct _PyCfgExceptStack *copy = copy_except_stack(except_stack);
|
||||||
if (copy == NULL) {
|
if (copy == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -2103,8 +2193,8 @@ push_cold_blocks_to_end(cfg_builder *g) {
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_PyCfg_ConvertPseudoOps(basicblock *entryblock)
|
convert_pseudo_ops(basicblock *entryblock)
|
||||||
{
|
{
|
||||||
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
||||||
for (int i = 0; i < b->b_iused; i++) {
|
for (int i = 0; i < b->b_iused; i++) {
|
||||||
|
@ -2293,3 +2383,270 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache,
|
||||||
RETURN_IF_ERROR(resolve_line_numbers(g, firstlineno));
|
RETURN_IF_ERROR(resolve_line_numbers(g, firstlineno));
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int *
|
||||||
|
build_cellfixedoffsets(_PyCompile_CodeUnitMetadata *umd)
|
||||||
|
{
|
||||||
|
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
|
||||||
|
int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars);
|
||||||
|
int nfreevars = (int)PyDict_GET_SIZE(umd->u_freevars);
|
||||||
|
|
||||||
|
int noffsets = ncellvars + nfreevars;
|
||||||
|
int *fixed = PyMem_New(int, noffsets);
|
||||||
|
if (fixed == NULL) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < noffsets; i++) {
|
||||||
|
fixed[i] = nlocals + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *varname, *cellindex;
|
||||||
|
Py_ssize_t pos = 0;
|
||||||
|
while (PyDict_Next(umd->u_cellvars, &pos, &varname, &cellindex)) {
|
||||||
|
PyObject *varindex = PyDict_GetItem(umd->u_varnames, varname);
|
||||||
|
if (varindex != NULL) {
|
||||||
|
assert(PyLong_AS_LONG(cellindex) < INT_MAX);
|
||||||
|
assert(PyLong_AS_LONG(varindex) < INT_MAX);
|
||||||
|
int oldindex = (int)PyLong_AS_LONG(cellindex);
|
||||||
|
int argoffset = (int)PyLong_AS_LONG(varindex);
|
||||||
|
fixed[oldindex] = argoffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IS_GENERATOR(CF) \
|
||||||
|
((CF) & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR))
|
||||||
|
|
||||||
|
static int
|
||||||
|
insert_prefix_instructions(_PyCompile_CodeUnitMetadata *umd, basicblock *entryblock,
|
||||||
|
int *fixed, int nfreevars, int code_flags)
|
||||||
|
{
|
||||||
|
assert(umd->u_firstlineno > 0);
|
||||||
|
|
||||||
|
/* Add the generator prefix instructions. */
|
||||||
|
if (IS_GENERATOR(code_flags)) {
|
||||||
|
/* Note that RETURN_GENERATOR + POP_TOP have a net stack effect
|
||||||
|
* of 0. This is because RETURN_GENERATOR pushes an element
|
||||||
|
* with _PyFrame_StackPush before switching stacks.
|
||||||
|
*/
|
||||||
|
cfg_instr make_gen = {
|
||||||
|
.i_opcode = RETURN_GENERATOR,
|
||||||
|
.i_oparg = 0,
|
||||||
|
.i_loc = LOCATION(umd->u_firstlineno, umd->u_firstlineno, -1, -1),
|
||||||
|
.i_target = NULL,
|
||||||
|
};
|
||||||
|
RETURN_IF_ERROR(basicblock_insert_instruction(entryblock, 0, &make_gen));
|
||||||
|
cfg_instr pop_top = {
|
||||||
|
.i_opcode = POP_TOP,
|
||||||
|
.i_oparg = 0,
|
||||||
|
.i_loc = NO_LOCATION,
|
||||||
|
.i_target = NULL,
|
||||||
|
};
|
||||||
|
RETURN_IF_ERROR(basicblock_insert_instruction(entryblock, 1, &pop_top));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up cells for any variable that escapes, to be put in a closure. */
|
||||||
|
const int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars);
|
||||||
|
if (ncellvars) {
|
||||||
|
// umd->u_cellvars has the cells out of order so we sort them
|
||||||
|
// before adding the MAKE_CELL instructions. Note that we
|
||||||
|
// adjust for arg cells, which come first.
|
||||||
|
const int nvars = ncellvars + (int)PyDict_GET_SIZE(umd->u_varnames);
|
||||||
|
int *sorted = PyMem_RawCalloc(nvars, sizeof(int));
|
||||||
|
if (sorted == NULL) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ncellvars; i++) {
|
||||||
|
sorted[fixed[i]] = i + 1;
|
||||||
|
}
|
||||||
|
for (int i = 0, ncellsused = 0; ncellsused < ncellvars; i++) {
|
||||||
|
int oldindex = sorted[i] - 1;
|
||||||
|
if (oldindex == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cfg_instr make_cell = {
|
||||||
|
.i_opcode = MAKE_CELL,
|
||||||
|
// This will get fixed in offset_derefs().
|
||||||
|
.i_oparg = oldindex,
|
||||||
|
.i_loc = NO_LOCATION,
|
||||||
|
.i_target = NULL,
|
||||||
|
};
|
||||||
|
if (basicblock_insert_instruction(entryblock, ncellsused, &make_cell) < 0) {
|
||||||
|
PyMem_RawFree(sorted);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
ncellsused += 1;
|
||||||
|
}
|
||||||
|
PyMem_RawFree(sorted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nfreevars) {
|
||||||
|
cfg_instr copy_frees = {
|
||||||
|
.i_opcode = COPY_FREE_VARS,
|
||||||
|
.i_oparg = nfreevars,
|
||||||
|
.i_loc = NO_LOCATION,
|
||||||
|
.i_target = NULL,
|
||||||
|
};
|
||||||
|
RETURN_IF_ERROR(basicblock_insert_instruction(entryblock, 0, ©_frees));
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
fix_cell_offsets(_PyCompile_CodeUnitMetadata *umd, basicblock *entryblock, int *fixedmap)
|
||||||
|
{
|
||||||
|
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
|
||||||
|
int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars);
|
||||||
|
int nfreevars = (int)PyDict_GET_SIZE(umd->u_freevars);
|
||||||
|
int noffsets = ncellvars + nfreevars;
|
||||||
|
|
||||||
|
// First deal with duplicates (arg cells).
|
||||||
|
int numdropped = 0;
|
||||||
|
for (int i = 0; i < noffsets ; i++) {
|
||||||
|
if (fixedmap[i] == i + nlocals) {
|
||||||
|
fixedmap[i] -= numdropped;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// It was a duplicate (cell/arg).
|
||||||
|
numdropped += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then update offsets, either relative to locals or by cell2arg.
|
||||||
|
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
||||||
|
for (int i = 0; i < b->b_iused; i++) {
|
||||||
|
cfg_instr *inst = &b->b_instr[i];
|
||||||
|
// This is called before extended args are generated.
|
||||||
|
assert(inst->i_opcode != EXTENDED_ARG);
|
||||||
|
int oldoffset = inst->i_oparg;
|
||||||
|
switch(inst->i_opcode) {
|
||||||
|
case MAKE_CELL:
|
||||||
|
case LOAD_CLOSURE:
|
||||||
|
case LOAD_DEREF:
|
||||||
|
case STORE_DEREF:
|
||||||
|
case DELETE_DEREF:
|
||||||
|
case LOAD_FROM_DICT_OR_DEREF:
|
||||||
|
assert(oldoffset >= 0);
|
||||||
|
assert(oldoffset < noffsets);
|
||||||
|
assert(fixedmap[oldoffset] >= 0);
|
||||||
|
inst->i_oparg = fixedmap[oldoffset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return numdropped;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
prepare_localsplus(_PyCompile_CodeUnitMetadata *umd, cfg_builder *g, int code_flags)
|
||||||
|
{
|
||||||
|
assert(PyDict_GET_SIZE(umd->u_varnames) < INT_MAX);
|
||||||
|
assert(PyDict_GET_SIZE(umd->u_cellvars) < INT_MAX);
|
||||||
|
assert(PyDict_GET_SIZE(umd->u_freevars) < INT_MAX);
|
||||||
|
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
|
||||||
|
int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars);
|
||||||
|
int nfreevars = (int)PyDict_GET_SIZE(umd->u_freevars);
|
||||||
|
assert(INT_MAX - nlocals - ncellvars > 0);
|
||||||
|
assert(INT_MAX - nlocals - ncellvars - nfreevars > 0);
|
||||||
|
int nlocalsplus = nlocals + ncellvars + nfreevars;
|
||||||
|
int* cellfixedoffsets = build_cellfixedoffsets(umd);
|
||||||
|
if (cellfixedoffsets == NULL) {
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This must be called before fix_cell_offsets().
|
||||||
|
if (insert_prefix_instructions(umd, g->g_entryblock, cellfixedoffsets, nfreevars, code_flags)) {
|
||||||
|
PyMem_Free(cellfixedoffsets);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numdropped = fix_cell_offsets(umd, g->g_entryblock, cellfixedoffsets);
|
||||||
|
PyMem_Free(cellfixedoffsets); // At this point we're done with it.
|
||||||
|
cellfixedoffsets = NULL;
|
||||||
|
if (numdropped < 0) {
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlocalsplus -= numdropped;
|
||||||
|
return nlocalsplus;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyCfg_ToInstructionSequence(cfg_builder *g, _PyCompile_InstructionSequence *seq)
|
||||||
|
{
|
||||||
|
int lbl = 0;
|
||||||
|
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
||||||
|
b->b_label = (jump_target_label){lbl};
|
||||||
|
lbl += b->b_iused;
|
||||||
|
}
|
||||||
|
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
||||||
|
RETURN_IF_ERROR(_PyCompile_InstructionSequence_UseLabel(seq, b->b_label.id));
|
||||||
|
for (int i = 0; i < b->b_iused; i++) {
|
||||||
|
cfg_instr *instr = &b->b_instr[i];
|
||||||
|
if (OPCODE_HAS_JUMP(instr->i_opcode)) {
|
||||||
|
instr->i_oparg = instr->i_target->b_label.id;
|
||||||
|
}
|
||||||
|
RETURN_IF_ERROR(
|
||||||
|
_PyCompile_InstructionSequence_Addop(
|
||||||
|
seq, instr->i_opcode, instr->i_oparg, instr->i_loc));
|
||||||
|
|
||||||
|
_PyCompile_ExceptHandlerInfo *hi = &seq->s_instrs[seq->s_used-1].i_except_handler_info;
|
||||||
|
if (instr->i_except != NULL) {
|
||||||
|
hi->h_label = instr->i_except->b_label.id;
|
||||||
|
hi->h_startdepth = instr->i_except->b_startdepth;
|
||||||
|
hi->h_preserve_lasti = instr->i_except->b_preserve_lasti;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hi->h_label = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyCfg_OptimizedCfgToInstructionSequence(cfg_builder *g,
|
||||||
|
_PyCompile_CodeUnitMetadata *umd, int code_flags,
|
||||||
|
int *stackdepth, int *nlocalsplus,
|
||||||
|
_PyCompile_InstructionSequence *seq)
|
||||||
|
{
|
||||||
|
*stackdepth = calculate_stackdepth(g);
|
||||||
|
if (*stackdepth < 0) {
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prepare_localsplus adds instructions for generators that push
|
||||||
|
* and pop an item on the stack. This assertion makes sure there
|
||||||
|
* is space on the stack for that.
|
||||||
|
* It should always be true, because a generator must have at
|
||||||
|
* least one expression or call to INTRINSIC_STOPITERATION_ERROR,
|
||||||
|
* which requires stackspace.
|
||||||
|
*/
|
||||||
|
assert(!(IS_GENERATOR(code_flags) && *stackdepth == 0));
|
||||||
|
|
||||||
|
*nlocalsplus = prepare_localsplus(umd, g, code_flags);
|
||||||
|
if (*nlocalsplus < 0) {
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
convert_pseudo_ops(g->g_entryblock);
|
||||||
|
|
||||||
|
/* Order of basic blocks must have been determined by now */
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(normalize_jumps(g));
|
||||||
|
assert(no_redundant_jumps(g));
|
||||||
|
|
||||||
|
/* Can't modify the bytecode after computing jump offsets. */
|
||||||
|
if (_PyCfg_ToInstructionSequence(g, seq) < 0) {
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue