]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-119744: move a few functions from compile.c to flowgraph.c (#119745)
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>
Thu, 30 May 2024 20:55:06 +0000 (21:55 +0100)
committerGitHub <noreply@github.com>
Thu, 30 May 2024 20:55:06 +0000 (21:55 +0100)
Include/internal/pycore_compile.h
Include/internal/pycore_flowgraph.h
Modules/_testinternalcapi.c
Python/compile.c
Python/flowgraph.c

index 3c21f83a18b52acb264ca6fa09ae602634654244..a1ac034e3e44aff74f2540aff8a2ed1f372492bb 100644 (file)
@@ -101,12 +101,6 @@ PyAPI_FUNC(PyObject*) _PyCompile_CodeGen(
         int optimize,
         int compile_mode);
 
-// Export for '_testinternalcapi' shared extension
-PyAPI_FUNC(PyObject*) _PyCompile_OptimizeCfg(
-        PyObject *instructions,
-        PyObject *consts,
-        int nlocals);
-
 // Export for '_testinternalcapi' shared extension
 PyAPI_FUNC(PyCodeObject*)
 _PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename,
index 819117b83114bcd5cf7d6d64fb04f5a9e07b6b40..5043260d2fd99f2a671a57915024c50df4dba0a7 100644 (file)
@@ -24,6 +24,7 @@ int _PyCfgBuilder_CheckSize(struct _PyCfgBuilder* g);
 int _PyCfg_OptimizeCodeUnit(struct _PyCfgBuilder *g, PyObject *consts, PyObject *const_cache,
                             int nlocals, int nparams, int firstlineno);
 
+struct _PyCfgBuilder* _PyCfg_FromInstructionSequence(_PyInstructionSequence *seq);
 int _PyCfg_ToInstructionSequence(struct _PyCfgBuilder *g, _PyInstructionSequence *seq);
 int _PyCfg_OptimizedCfgToInstructionSequence(struct _PyCfgBuilder *g, _PyCompile_CodeUnitMetadata *umd,
                                              int code_flags, int *stackdepth, int *nlocalsplus,
@@ -34,6 +35,12 @@ _PyAssemble_MakeCodeObject(_PyCompile_CodeUnitMetadata *u, PyObject *const_cache
                            PyObject *consts, int maxdepth, _PyInstructionSequence *instrs,
                            int nlocalsplus, int code_flags, PyObject *filename);
 
+// Export for '_testinternalcapi' shared extension
+PyAPI_FUNC(PyObject*) _PyCompile_OptimizeCfg(
+        PyObject *instructions,
+        PyObject *consts,
+        int nlocals);
+
 #ifdef __cplusplus
 }
 #endif
index 129c136906739dd5fbd722b3e5a6c4ea5639d2af..d9b9c999603d5a7c512d9ac3cdbcc67becc2c9d5 100644 (file)
@@ -18,6 +18,7 @@
 #include "pycore_context.h"       // _PyContext_NewHamtForTests()
 #include "pycore_dict.h"          // _PyManagedDictPointer_GetValues()
 #include "pycore_fileutils.h"     // _Py_normpath()
+#include "pycore_flowgraph.h"     // _PyCompile_OptimizeCfg()
 #include "pycore_frame.h"         // _PyInterpreterFrame
 #include "pycore_gc.h"            // PyGC_Head
 #include "pycore_hashtable.h"     // _Py_hashtable_new()
index 3a80577e0f2b2daaa7c2a41c8952919b7ff439aa..7d74096fcdf94e0ca5f0061e7158124b1242153a 100644 (file)
@@ -197,47 +197,6 @@ _PyCompile_EnsureArrayLargeEnough(int idx, void **array, int *alloc,
     return SUCCESS;
 }
 
-static cfg_builder*
-instr_sequence_to_cfg(instr_sequence *seq) {
-    if (_PyInstructionSequence_ApplyLabelMap(seq) < 0) {
-        return NULL;
-    }
-    cfg_builder *g = _PyCfgBuilder_New();
-    if (g == NULL) {
-        return NULL;
-    }
-    for (int i = 0; i < seq->s_used; i++) {
-        seq->s_instrs[i].i_target = 0;
-    }
-    for (int i = 0; i < seq->s_used; i++) {
-        instruction *instr = &seq->s_instrs[i];
-        if (HAS_TARGET(instr->i_opcode)) {
-            assert(instr->i_oparg >= 0 && instr->i_oparg < seq->s_used);
-            seq->s_instrs[instr->i_oparg].i_target = 1;
-        }
-    }
-    for (int i = 0; i < seq->s_used; i++) {
-        instruction *instr = &seq->s_instrs[i];
-        if (instr->i_target) {
-            jump_target_label lbl_ = {i};
-            if (_PyCfgBuilder_UseLabel(g, lbl_) < 0) {
-                goto error;
-            }
-        }
-        int opcode = instr->i_opcode;
-        int oparg = instr->i_oparg;
-        if (_PyCfgBuilder_Addop(g, opcode, oparg, instr->i_loc) < 0) {
-            goto error;
-        }
-    }
-    if (_PyCfgBuilder_CheckSize(g) < 0) {
-        goto error;
-    }
-    return g;
-error:
-    _PyCfgBuilder_Free(g);
-    return NULL;
-}
 
 /* The following items change on entry and exit of code blocks.
    They must be saved and restored when returning to a block.
@@ -691,48 +650,6 @@ compiler_set_qualname(struct compiler *c)
     return SUCCESS;
 }
 
-/* Return the stack effect of opcode with argument oparg.
-
-   Some opcodes have different stack effect when jump to the target and
-   when not jump. The 'jump' parameter specifies the case:
-
-   * 0 -- when not jump
-   * 1 -- when jump
-   * -1 -- maximal
- */
-static int
-stack_effect(int opcode, int oparg, int jump)
-{
-    if (opcode < 0) {
-        return PY_INVALID_STACK_EFFECT;
-    }
-    if ((opcode <= MAX_REAL_OPCODE) && (_PyOpcode_Deopt[opcode] != opcode)) {
-        // Specialized instructions are not supported.
-        return PY_INVALID_STACK_EFFECT;
-    }
-    int popped = _PyOpcode_num_popped(opcode, oparg);
-    int pushed = _PyOpcode_num_pushed(opcode, oparg);
-    if (popped < 0 || pushed < 0) {
-        return PY_INVALID_STACK_EFFECT;
-    }
-    if (IS_BLOCK_PUSH_OPCODE(opcode) && !jump) {
-        return 0;
-    }
-    return pushed - popped;
-}
-
-int
-PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump)
-{
-    return stack_effect(opcode, oparg, jump);
-}
-
-int
-PyCompile_OpcodeStackEffect(int opcode, int oparg)
-{
-    return stack_effect(opcode, oparg, -1);
-}
-
 int
 _PyCompile_OpcodeIsValid(int opcode)
 {
@@ -7592,7 +7509,7 @@ optimize_and_assemble_code_unit(struct compiler_unit *u, PyObject *const_cache,
     if (consts == NULL) {
         goto error;
     }
-    g = instr_sequence_to_cfg(u->u_instr_sequence);
+    g = _PyCfg_FromInstructionSequence(u->u_instr_sequence);
     if (g == NULL) {
         goto error;
     }
@@ -7645,39 +7562,6 @@ optimize_and_assemble(struct compiler *c, int addNone)
     return optimize_and_assemble_code_unit(u, const_cache, code_flags, filename);
 }
 
-/* Access to compiler optimizations for unit tests.
- *
- * _PyCompile_CodeGen takes and AST, applies code-gen and
- * returns the unoptimized CFG as an instruction list.
- *
- * _PyCompile_OptimizeCfg takes an instruction list, constructs
- * a CFG, optimizes it and converts back to an instruction list.
- *
- * An instruction list is a PyList where each item is either
- * a tuple describing a single instruction:
- * (opcode, oparg, lineno, end_lineno, col, end_col), or
- * a jump target label marking the beginning of a basic block.
- */
-
-
-static PyObject *
-cfg_to_instruction_sequence(cfg_builder *g)
-{
-    instr_sequence *seq = (instr_sequence *)_PyInstructionSequence_New();
-    if (seq != NULL) {
-        if (_PyCfg_ToInstructionSequence(g, seq) < 0) {
-            goto error;
-        }
-        if (_PyInstructionSequence_ApplyLabelMap(seq) < 0) {
-            goto error;
-        }
-    }
-    return (PyObject*)seq;
-error:
-    PyInstructionSequence_Fini(seq);
-    return NULL;
-}
-
 // C implementation of inspect.cleandoc()
 //
 // Difference from inspect.cleandoc():
@@ -7768,6 +7652,12 @@ _PyCompile_CleanDoc(PyObject *doc)
     return res;
 }
 
+/* Access to compiler optimizations for unit tests.
+ *
+ * _PyCompile_CodeGen takes an AST, applies code-gen and
+ * returns the unoptimized CFG as an instruction list.
+ *
+ */
 
 PyObject *
 _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags,
@@ -7859,35 +7749,6 @@ finally:
     return res;
 }
 
-PyObject *
-_PyCompile_OptimizeCfg(PyObject *seq, PyObject *consts, int nlocals)
-{
-    if (!_PyInstructionSequence_Check(seq)) {
-        PyErr_SetString(PyExc_ValueError, "expected an instruction sequence");
-        return NULL;
-    }
-    PyObject *const_cache = PyDict_New();
-    if (const_cache == NULL) {
-        return NULL;
-    }
-
-    PyObject *res = NULL;
-    cfg_builder *g = instr_sequence_to_cfg((instr_sequence*)seq);
-    if (g == NULL) {
-        goto error;
-    }
-    int nparams = 0, firstlineno = 1;
-    if (_PyCfg_OptimizeCodeUnit(g, consts, const_cache, nlocals,
-                                nparams, firstlineno) < 0) {
-        goto error;
-    }
-    res = cfg_to_instruction_sequence(g);
-error:
-    Py_DECREF(const_cache);
-    _PyCfgBuilder_Free(g);
-    return res;
-}
-
 int _PyCfg_JumpLabelsToTargets(cfg_builder *g);
 
 PyCodeObject *
@@ -7908,7 +7769,7 @@ _PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename,
         return NULL;
     }
 
-    g = instr_sequence_to_cfg((instr_sequence*)seq);
+    g = _PyCfg_FromInstructionSequence((instr_sequence*)seq);
     if (g == NULL) {
         goto error;
     }
index 83768023a4d870e331ad1b98360f067e34c4c74e..b0c8004130fb07e3ea05d322f1f001b5c172dcea 100644 (file)
@@ -751,6 +751,36 @@ make_cfg_traversal_stack(basicblock *entryblock) {
     return stack;
 }
 
+/* Return the stack effect of opcode with argument oparg.
+
+   Some opcodes have different stack effect when jump to the target and
+   when not jump. The 'jump' parameter specifies the case:
+
+   * 0 -- when not jump
+   * 1 -- when jump
+   * -1 -- maximal
+ */
+Py_LOCAL(int)
+stack_effect(int opcode, int oparg, int jump)
+{
+    if (opcode < 0) {
+        return PY_INVALID_STACK_EFFECT;
+    }
+    if ((opcode <= MAX_REAL_OPCODE) && (_PyOpcode_Deopt[opcode] != opcode)) {
+        // Specialized instructions are not supported.
+        return PY_INVALID_STACK_EFFECT;
+    }
+    int popped = _PyOpcode_num_popped(opcode, oparg);
+    int pushed = _PyOpcode_num_pushed(opcode, oparg);
+    if (popped < 0 || pushed < 0) {
+        return PY_INVALID_STACK_EFFECT;
+    }
+    if (IS_BLOCK_PUSH_OPCODE(opcode) && !jump) {
+        return 0;
+    }
+    return pushed - popped;
+}
+
 Py_LOCAL_INLINE(int)
 stackdepth_push(basicblock ***sp, basicblock *b, int depth)
 {
@@ -795,8 +825,7 @@ calculate_stackdepth(cfg_builder *g)
         basicblock *next = b->b_next;
         for (int i = 0; i < b->b_iused; i++) {
             cfg_instr *instr = &b->b_instr[i];
-            int effect = PyCompile_OpcodeStackEffectWithJump(
-                             instr->i_opcode, instr->i_oparg, 0);
+            int effect = stack_effect(instr->i_opcode, instr->i_oparg, 0);
             if (effect == PY_INVALID_STACK_EFFECT) {
                 PyErr_Format(PyExc_SystemError,
                              "Invalid stack effect for opcode=%d, arg=%i",
@@ -813,8 +842,7 @@ calculate_stackdepth(cfg_builder *g)
                 maxdepth = new_depth;
             }
             if (HAS_TARGET(instr->i_opcode)) {
-                effect = PyCompile_OpcodeStackEffectWithJump(
-                             instr->i_opcode, instr->i_oparg, 1);
+                effect = stack_effect(instr->i_opcode, instr->i_oparg, 1);
                 if (effect == PY_INVALID_STACK_EFFECT) {
                     PyErr_Format(PyExc_SystemError,
                                  "Invalid stack effect for opcode=%d, arg=%i",
@@ -2711,6 +2739,49 @@ prepare_localsplus(_PyCompile_CodeUnitMetadata *umd, cfg_builder *g, int code_fl
     return nlocalsplus;
 }
 
+cfg_builder *
+_PyCfg_FromInstructionSequence(_PyInstructionSequence *seq)
+{
+    if (_PyInstructionSequence_ApplyLabelMap(seq) < 0) {
+        return NULL;
+    }
+    cfg_builder *g = _PyCfgBuilder_New();
+    if (g == NULL) {
+        return NULL;
+    }
+    for (int i = 0; i < seq->s_used; i++) {
+        seq->s_instrs[i].i_target = 0;
+    }
+    for (int i = 0; i < seq->s_used; i++) {
+        _PyInstruction *instr = &seq->s_instrs[i];
+        if (HAS_TARGET(instr->i_opcode)) {
+            assert(instr->i_oparg >= 0 && instr->i_oparg < seq->s_used);
+            seq->s_instrs[instr->i_oparg].i_target = 1;
+        }
+    }
+    for (int i = 0; i < seq->s_used; i++) {
+        _PyInstruction *instr = &seq->s_instrs[i];
+        if (instr->i_target) {
+            jump_target_label lbl_ = {i};
+            if (_PyCfgBuilder_UseLabel(g, lbl_) < 0) {
+                goto error;
+            }
+        }
+        int opcode = instr->i_opcode;
+        int oparg = instr->i_oparg;
+        if (_PyCfgBuilder_Addop(g, opcode, oparg, instr->i_loc) < 0) {
+            goto error;
+        }
+    }
+    if (_PyCfgBuilder_CheckSize(g) < 0) {
+        goto error;
+    }
+    return g;
+error:
+    _PyCfgBuilder_Free(g);
+    return NULL;
+}
+
 int
 _PyCfg_ToInstructionSequence(cfg_builder *g, _PyInstructionSequence *seq)
 {
@@ -2742,6 +2813,9 @@ _PyCfg_ToInstructionSequence(cfg_builder *g, _PyInstructionSequence *seq)
             }
         }
     }
+    if (_PyInstructionSequence_ApplyLabelMap(seq) < 0) {
+        return ERROR;
+    }
     return SUCCESS;
 }
 
@@ -2796,3 +2870,66 @@ _PyCfg_JumpLabelsToTargets(cfg_builder *g)
     RETURN_IF_ERROR(label_exception_targets(g->g_entryblock));
     return SUCCESS;
 }
+
+/* Exported API functions */
+
+int
+PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump)
+{
+    return stack_effect(opcode, oparg, jump);
+}
+
+int
+PyCompile_OpcodeStackEffect(int opcode, int oparg)
+{
+    return stack_effect(opcode, oparg, -1);
+}
+
+/* Access to compiler optimizations for unit tests.
+
+ * _PyCompile_OptimizeCfg takes an instruction list, constructs
+ * a CFG, optimizes it and converts back to an instruction list.
+ */
+
+static PyObject *
+cfg_to_instruction_sequence(cfg_builder *g)
+{
+    _PyInstructionSequence *seq = (_PyInstructionSequence *)_PyInstructionSequence_New();
+    if (seq == NULL) {
+        return NULL;
+    }
+    if (_PyCfg_ToInstructionSequence(g, seq) < 0) {
+        PyInstructionSequence_Fini(seq);
+        return NULL;
+    }
+    return (PyObject*)seq;
+}
+
+PyObject *
+_PyCompile_OptimizeCfg(PyObject *seq, PyObject *consts, int nlocals)
+{
+    if (!_PyInstructionSequence_Check(seq)) {
+        PyErr_SetString(PyExc_ValueError, "expected an instruction sequence");
+        return NULL;
+    }
+    PyObject *const_cache = PyDict_New();
+    if (const_cache == NULL) {
+        return NULL;
+    }
+
+    PyObject *res = NULL;
+    cfg_builder *g = _PyCfg_FromInstructionSequence((_PyInstructionSequence*)seq);
+    if (g == NULL) {
+        goto error;
+    }
+    int nparams = 0, firstlineno = 1;
+    if (_PyCfg_OptimizeCodeUnit(g, consts, const_cache, nlocals,
+                                nparams, firstlineno) < 0) {
+        goto error;
+    }
+    res = cfg_to_instruction_sequence(g);
+error:
+    Py_DECREF(const_cache);
+    _PyCfgBuilder_Free(g);
+    return res;
+}