Skip to content

Commit 80163e1

Browse files
authored
gh-87092: move CFG related code from compile.c to flowgraph.c (#103021)
1 parent b0422e1 commit 80163e1

14 files changed

+2580
-2495
lines changed

Include/internal/pycore_compile.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ extern int _PyAST_Optimize(
3333
struct _arena *arena,
3434
_PyASTOptimizeState *state);
3535

36+
/* Utility for a number of growing arrays used in the compiler */
37+
int _PyCompile_EnsureArrayLargeEnough(
38+
int idx,
39+
void **array,
40+
int *alloc,
41+
int default_alloc,
42+
size_t item_size);
43+
44+
int _PyCompile_ConstCacheMergeOne(PyObject *const_cache, PyObject **obj);
45+
3646
/* Access compiler internals for unit testing */
3747

3848
PyAPI_FUNC(PyObject*) _PyCompile_CodeGen(

Include/internal/pycore_flowgraph.h

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#ifndef Py_INTERNAL_CFG_H
2+
#define Py_INTERNAL_CFG_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
#include "pycore_opcode_utils.h"
12+
13+
static const _PyCompilerSrcLocation NO_LOCATION = {-1, -1, -1, -1};
14+
15+
typedef struct {
16+
int i_opcode;
17+
int i_oparg;
18+
_PyCompilerSrcLocation i_loc;
19+
struct _PyCfgBasicblock_ *i_target; /* target block (if jump instruction) */
20+
struct _PyCfgBasicblock_ *i_except; /* target block when exception is raised */
21+
} _PyCfgInstruction;
22+
23+
typedef struct {
24+
int id;
25+
} _PyCfgJumpTargetLabel;
26+
27+
28+
typedef struct {
29+
struct _PyCfgBasicblock_ *handlers[CO_MAXBLOCKS+1];
30+
int depth;
31+
} _PyCfgExceptStack;
32+
33+
typedef struct _PyCfgBasicblock_ {
34+
/* Each basicblock in a compilation unit is linked via b_list in the
35+
reverse order that the block are allocated. b_list points to the next
36+
block, not to be confused with b_next, which is next by control flow. */
37+
struct _PyCfgBasicblock_ *b_list;
38+
/* The label of this block if it is a jump target, -1 otherwise */
39+
_PyCfgJumpTargetLabel b_label;
40+
/* Exception stack at start of block, used by assembler to create the exception handling table */
41+
_PyCfgExceptStack *b_exceptstack;
42+
/* pointer to an array of instructions, initially NULL */
43+
_PyCfgInstruction *b_instr;
44+
/* If b_next is non-NULL, it is a pointer to the next
45+
block reached by normal control flow. */
46+
struct _PyCfgBasicblock_ *b_next;
47+
/* number of instructions used */
48+
int b_iused;
49+
/* length of instruction array (b_instr) */
50+
int b_ialloc;
51+
/* Used by add_checks_for_loads_of_unknown_variables */
52+
uint64_t b_unsafe_locals_mask;
53+
/* Number of predecessors that a block has. */
54+
int b_predecessors;
55+
/* depth of stack upon entry of block, computed by stackdepth() */
56+
int b_startdepth;
57+
/* instruction offset for block, computed by assemble_jump_offsets() */
58+
int b_offset;
59+
/* Basic block is an exception handler that preserves lasti */
60+
unsigned b_preserve_lasti : 1;
61+
/* Used by compiler passes to mark whether they have visited a basic block. */
62+
unsigned b_visited : 1;
63+
/* b_except_handler is used by the cold-detection algorithm to mark exception targets */
64+
unsigned b_except_handler : 1;
65+
/* b_cold is true if this block is not perf critical (like an exception handler) */
66+
unsigned b_cold : 1;
67+
/* b_warm is used by the cold-detection algorithm to mark blocks which are definitely not cold */
68+
unsigned b_warm : 1;
69+
} _PyCfgBasicblock;
70+
71+
int _PyBasicblock_InsertInstruction(_PyCfgBasicblock *block, int pos, _PyCfgInstruction *instr);
72+
73+
typedef struct cfg_builder_ {
74+
/* The entryblock, at which control flow begins. All blocks of the
75+
CFG are reachable through the b_next links */
76+
_PyCfgBasicblock *g_entryblock;
77+
/* Pointer to the most recently allocated block. By following
78+
b_list links, you can reach all allocated blocks. */
79+
_PyCfgBasicblock *g_block_list;
80+
/* pointer to the block currently being constructed */
81+
_PyCfgBasicblock *g_curblock;
82+
/* label for the next instruction to be placed */
83+
_PyCfgJumpTargetLabel g_current_label;
84+
} _PyCfgBuilder;
85+
86+
int _PyCfgBuilder_UseLabel(_PyCfgBuilder *g, _PyCfgJumpTargetLabel lbl);
87+
int _PyCfgBuilder_Addop(_PyCfgBuilder *g, int opcode, int oparg, _PyCompilerSrcLocation loc);
88+
89+
int _PyCfgBuilder_Init(_PyCfgBuilder *g);
90+
void _PyCfgBuilder_Fini(_PyCfgBuilder *g);
91+
92+
_PyCfgInstruction* _PyCfg_BasicblockLastInstr(const _PyCfgBasicblock *b);
93+
int _PyCfg_OptimizeCodeUnit(_PyCfgBuilder *g, PyObject *consts, PyObject *const_cache,
94+
int code_flags, int nlocals, int nparams);
95+
int _PyCfg_Stackdepth(_PyCfgBasicblock *entryblock, int code_flags);
96+
void _PyCfg_ConvertExceptionHandlersToNops(_PyCfgBasicblock *entryblock);
97+
int _PyCfg_ResolveLineNumbers(_PyCfgBuilder *g, int firstlineno);
98+
int _PyCfg_ResolveJumps(_PyCfgBuilder *g);
99+
int _PyCfg_InstrSize(_PyCfgInstruction *instruction);
100+
101+
102+
static inline int
103+
basicblock_nofallthrough(const _PyCfgBasicblock *b) {
104+
_PyCfgInstruction *last = _PyCfg_BasicblockLastInstr(b);
105+
return (last &&
106+
(IS_SCOPE_EXIT_OPCODE(last->i_opcode) ||
107+
IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)));
108+
}
109+
110+
#define BB_NO_FALLTHROUGH(B) (basicblock_nofallthrough(B))
111+
#define BB_HAS_FALLTHROUGH(B) (!basicblock_nofallthrough(B))
112+
113+
114+
#ifdef __cplusplus
115+
}
116+
#endif
117+
#endif /* !Py_INTERNAL_CFG_H */

Include/internal/pycore_opcode.h

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#ifndef Py_INTERNAL_OPCODE_UTILS_H
2+
#define Py_INTERNAL_OPCODE_UTILS_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
#include "pycore_opcode.h" // _PyOpcode_RelativeJump
12+
13+
14+
#define MAX_REAL_OPCODE 254
15+
16+
#define IS_WITHIN_OPCODE_RANGE(opcode) \
17+
(((opcode) >= 0 && (opcode) <= MAX_REAL_OPCODE) || \
18+
IS_PSEUDO_OPCODE(opcode))
19+
20+
#define IS_JUMP_OPCODE(opcode) \
21+
is_bit_set_in_table(_PyOpcode_Jump, opcode)
22+
23+
#define IS_BLOCK_PUSH_OPCODE(opcode) \
24+
((opcode) == SETUP_FINALLY || \
25+
(opcode) == SETUP_WITH || \
26+
(opcode) == SETUP_CLEANUP)
27+
28+
#define HAS_TARGET(opcode) \
29+
(IS_JUMP_OPCODE(opcode) || IS_BLOCK_PUSH_OPCODE(opcode))
30+
31+
/* opcodes that must be last in the basicblock */
32+
#define IS_TERMINATOR_OPCODE(opcode) \
33+
(IS_JUMP_OPCODE(opcode) || IS_SCOPE_EXIT_OPCODE(opcode))
34+
35+
/* opcodes which are not emitted in codegen stage, only by the assembler */
36+
#define IS_ASSEMBLER_OPCODE(opcode) \
37+
((opcode) == JUMP_FORWARD || \
38+
(opcode) == JUMP_BACKWARD || \
39+
(opcode) == JUMP_BACKWARD_NO_INTERRUPT)
40+
41+
#define IS_BACKWARDS_JUMP_OPCODE(opcode) \
42+
((opcode) == JUMP_BACKWARD || \
43+
(opcode) == JUMP_BACKWARD_NO_INTERRUPT)
44+
45+
#define IS_UNCONDITIONAL_JUMP_OPCODE(opcode) \
46+
((opcode) == JUMP || \
47+
(opcode) == JUMP_NO_INTERRUPT || \
48+
(opcode) == JUMP_FORWARD || \
49+
(opcode) == JUMP_BACKWARD || \
50+
(opcode) == JUMP_BACKWARD_NO_INTERRUPT)
51+
52+
#define IS_SCOPE_EXIT_OPCODE(opcode) \
53+
((opcode) == RETURN_VALUE || \
54+
(opcode) == RETURN_CONST || \
55+
(opcode) == RAISE_VARARGS || \
56+
(opcode) == RERAISE)
57+
58+
#define IS_SUPERINSTRUCTION_OPCODE(opcode) \
59+
((opcode) == LOAD_FAST__LOAD_FAST || \
60+
(opcode) == LOAD_FAST__LOAD_CONST || \
61+
(opcode) == LOAD_CONST__LOAD_FAST || \
62+
(opcode) == STORE_FAST__LOAD_FAST || \
63+
(opcode) == STORE_FAST__STORE_FAST)
64+
65+
66+
#define LOG_BITS_PER_INT 5
67+
#define MASK_LOW_LOG_BITS 31
68+
69+
static inline int
70+
is_bit_set_in_table(const uint32_t *table, int bitindex) {
71+
/* Is the relevant bit set in the relevant word? */
72+
/* 512 bits fit into 9 32-bits words.
73+
* Word is indexed by (bitindex>>ln(size of int in bits)).
74+
* Bit within word is the low bits of bitindex.
75+
*/
76+
if (bitindex >= 0 && bitindex < 512) {
77+
uint32_t word = table[bitindex >> LOG_BITS_PER_INT];
78+
return (word >> (bitindex & MASK_LOW_LOG_BITS)) & 1;
79+
}
80+
else {
81+
return 0;
82+
}
83+
}
84+
85+
#undef LOG_BITS_PER_INT
86+
#undef MASK_LOW_LOG_BITS
87+
88+
#define IS_RELATIVE_JUMP(opcode) (is_bit_set_in_table(_PyOpcode_RelativeJump, opcode))
89+
90+
91+
92+
#ifdef __cplusplus
93+
}
94+
#endif
95+
#endif /* !Py_INTERNAL_OPCODE_UTILS_H */

Makefile.pre.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ PYTHON_OBJS= \
374374
Python/ast_unparse.o \
375375
Python/bltinmodule.o \
376376
Python/ceval.o \
377+
Python/flowgraph.o \
377378
Python/codecs.o \
378379
Python/compile.o \
379380
Python/context.o \
@@ -1702,6 +1703,8 @@ PYTHON_HEADERS= \
17021703
$(srcdir)/Include/internal/pycore_object_state.h \
17031704
$(srcdir)/Include/internal/pycore_obmalloc.h \
17041705
$(srcdir)/Include/internal/pycore_obmalloc_init.h \
1706+
$(srcdir)/Include/internal/pycore_opcode.h \
1707+
$(srcdir)/Include/internal/pycore_opcode_utils.h \
17051708
$(srcdir)/Include/internal/pycore_pathconfig.h \
17061709
$(srcdir)/Include/internal/pycore_pyarena.h \
17071710
$(srcdir)/Include/internal/pycore_pyerrors.h \

PCbuild/_freeze_module.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@
183183
<ClCompile Include="..\Python\bltinmodule.c" />
184184
<ClCompile Include="..\Python\bootstrap_hash.c" />
185185
<ClCompile Include="..\Python\ceval.c" />
186+
<ClCompile Include="..\Python\flowgraph.c" />
186187
<ClCompile Include="..\Python\codecs.c" />
187188
<ClCompile Include="..\Python\compile.c" />
188189
<ClCompile Include="..\Python\context.c" />

PCbuild/_freeze_module.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@
7676
<ClCompile Include="..\Python\ceval.c">
7777
<Filter>Source Files</Filter>
7878
</ClCompile>
79+
<ClCompile Include="..\Python\flowgraph.c">
80+
<Filter>Source Files</Filter>
81+
</ClCompile>
7982
<ClCompile Include="..\Objects\classobject.c">
8083
<Filter>Source Files</Filter>
8184
</ClCompile>

PCbuild/pythoncore.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@
205205
<ClInclude Include="..\Include\internal\pycore_call.h" />
206206
<ClInclude Include="..\Include\internal\pycore_ceval.h" />
207207
<ClInclude Include="..\Include\internal\pycore_ceval_state.h" />
208+
<ClInclude Include="..\Include\internal\pycore_cfg.h" />
208209
<ClInclude Include="..\Include\internal\pycore_code.h" />
209210
<ClInclude Include="..\Include\internal\pycore_compile.h" />
210211
<ClInclude Include="..\Include\internal\pycore_condvar.h" />
@@ -504,6 +505,7 @@
504505
<ClCompile Include="..\Python\bltinmodule.c" />
505506
<ClCompile Include="..\Python\bootstrap_hash.c" />
506507
<ClCompile Include="..\Python\ceval.c" />
508+
<ClCompile Include="..\Python\flowgraph.c" />
507509
<ClCompile Include="..\Python\codecs.c" />
508510
<ClCompile Include="..\Python\compile.c" />
509511
<ClCompile Include="..\Python\context.c" />

PCbuild/pythoncore.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,9 @@
11061106
<ClCompile Include="..\Python\ceval.c">
11071107
<Filter>Python</Filter>
11081108
</ClCompile>
1109+
<ClCompile Include="..\Python\flowgraph.c">
1110+
<Filter>Python</Filter>
1111+
</ClCompile>
11091112
<ClCompile Include="..\Python\codecs.c">
11101113
<Filter>Python</Filter>
11111114
</ClCompile>

0 commit comments

Comments
 (0)