From a3ac9232f859a144b5e1db494dbb928e0cd169ab Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 14 Nov 2022 13:56:40 +0000 Subject: gh-87092: expose the compiler's codegen to python for unit tests (GH-99111) --- Include/internal/pycore_compile.h | 7 + .../pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + Include/internal/pycore_runtime_init_generated.h | 1 + Include/internal/pycore_unicodeobject_generated.h | 2 + Lib/test/support/bytecode_helper.py | 93 ++++++----- Lib/test/test_compiler_codegen.py | 50 ++++++ Lib/test/test_peepholer.py | 2 +- Modules/_testinternalcapi.c | 23 ++- Modules/clinic/_testinternalcapi.c.h | 65 +++++++- Python/compile.c | 173 ++++++++++++++------- 11 files changed, 321 insertions(+), 97 deletions(-) create mode 100644 Lib/test/test_compiler_codegen.py diff --git a/Include/internal/pycore_compile.h b/Include/internal/pycore_compile.h index cb490ab..967fe92 100644 --- a/Include/internal/pycore_compile.h +++ b/Include/internal/pycore_compile.h @@ -40,6 +40,13 @@ extern int _PyAST_Optimize( _PyASTOptimizeState *state); /* Access compiler internals for unit testing */ + +PyAPI_FUNC(PyObject*) _PyCompile_CodeGen( + PyObject *ast, + PyObject *filename, + PyCompilerFlags *flags, + int optimize); + PyAPI_FUNC(PyObject*) _PyCompile_OptimizeCfg( PyObject *instructions, PyObject *consts); diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 8883bbb..0b5833c 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -781,6 +781,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(arguments)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(argv)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(as_integer_ratio)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ast)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(attribute)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(authorizer_callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(autocommit)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 1227142..5bfd4be 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -267,6 +267,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(arguments) STRUCT_FOR_ID(argv) STRUCT_FOR_ID(as_integer_ratio) + STRUCT_FOR_ID(ast) STRUCT_FOR_ID(attribute) STRUCT_FOR_ID(authorizer_callback) STRUCT_FOR_ID(autocommit) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index e7fba8d..7228cb5 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -773,6 +773,7 @@ extern "C" { INIT_ID(arguments), \ INIT_ID(argv), \ INIT_ID(as_integer_ratio), \ + INIT_ID(ast), \ INIT_ID(attribute), \ INIT_ID(authorizer_callback), \ INIT_ID(autocommit), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index ada0485..208a1e1 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -440,6 +440,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(as_integer_ratio); PyUnicode_InternInPlace(&string); + string = &_Py_ID(ast); + PyUnicode_InternInPlace(&string); string = &_Py_ID(attribute); PyUnicode_InternInPlace(&string); string = &_Py_ID(authorizer_callback); diff --git a/Lib/test/support/bytecode_helper.py b/Lib/test/support/bytecode_helper.py index eb4ae1a..65ae7a2 100644 --- a/Lib/test/support/bytecode_helper.py +++ b/Lib/test/support/bytecode_helper.py @@ -3,7 +3,7 @@ import unittest import dis import io -from _testinternalcapi import optimize_cfg +from _testinternalcapi import compiler_codegen, optimize_cfg _UNSPECIFIED = object() @@ -44,8 +44,7 @@ class BytecodeTestCase(unittest.TestCase): msg = msg % (opname, argval, disassembly) self.fail(msg) - -class CfgOptimizationTestCase(unittest.TestCase): +class CompilationStepTestCase(unittest.TestCase): HAS_ARG = set(dis.hasarg) HAS_TARGET = set(dis.hasjrel + dis.hasjabs + dis.hasexc) @@ -58,24 +57,35 @@ class CfgOptimizationTestCase(unittest.TestCase): self.last_label += 1 return self.last_label - def complete_insts_info(self, insts): - # fill in omitted fields in location, and oparg 0 for ops with no arg. - instructions = [] - for item in insts: - if isinstance(item, int): - instructions.append(item) - else: - assert isinstance(item, tuple) - inst = list(reversed(item)) - opcode = dis.opmap[inst.pop()] - oparg = inst.pop() if opcode in self.HAS_ARG_OR_TARGET else 0 - loc = inst + [-1] * (4 - len(inst)) - instructions.append((opcode, oparg, *loc)) - return instructions + def assertInstructionsMatch(self, actual_, expected_): + # get two lists where each entry is a label or + # an instruction tuple. Compare them, while mapping + # each actual label to a corresponding expected label + # based on their locations. + + self.assertIsInstance(actual_, list) + self.assertIsInstance(expected_, list) + + actual = self.normalize_insts(actual_) + expected = self.normalize_insts(expected_) + self.assertEqual(len(actual), len(expected)) + + # compare instructions + for act, exp in zip(actual, expected): + if isinstance(act, int): + self.assertEqual(exp, act) + continue + self.assertIsInstance(exp, tuple) + self.assertIsInstance(act, tuple) + # crop comparison to the provided expected values + if len(act) > len(exp): + act = act[:len(exp)] + self.assertEqual(exp, act) def normalize_insts(self, insts): """ Map labels to instruction index. Remove labels which are not used as jump targets. + Map opcodes to opnames. """ labels_map = {} targets = set() @@ -107,31 +117,32 @@ class CfgOptimizationTestCase(unittest.TestCase): res.append((opcode, arg, *loc)) return res - def get_optimized(self, insts, consts): - insts = self.complete_insts_info(insts) - insts = optimize_cfg(insts, consts) - return insts, consts - def compareInstructions(self, actual_, expected_): - # get two lists where each entry is a label or - # an instruction tuple. Compare them, while mapping - # each actual label to a corresponding expected label - # based on their locations. +class CodegenTestCase(CompilationStepTestCase): - self.assertIsInstance(actual_, list) - self.assertIsInstance(expected_, list) + def generate_code(self, ast): + insts = compiler_codegen(ast, "my_file.py", 0) + return insts - actual = self.normalize_insts(actual_) - expected = self.normalize_insts(expected_) - self.assertEqual(len(actual), len(expected)) - # compare instructions - for act, exp in zip(actual, expected): - if isinstance(act, int): - self.assertEqual(exp, act) - continue - self.assertIsInstance(exp, tuple) - self.assertIsInstance(act, tuple) - # pad exp with -1's (if location info is incomplete) - exp += (-1,) * (len(act) - len(exp)) - self.assertEqual(exp, act) +class CfgOptimizationTestCase(CompilationStepTestCase): + + def complete_insts_info(self, insts): + # fill in omitted fields in location, and oparg 0 for ops with no arg. + instructions = [] + for item in insts: + if isinstance(item, int): + instructions.append(item) + else: + assert isinstance(item, tuple) + inst = list(reversed(item)) + opcode = dis.opmap[inst.pop()] + oparg = inst.pop() if opcode in self.HAS_ARG_OR_TARGET else 0 + loc = inst + [-1] * (4 - len(inst)) + instructions.append((opcode, oparg, *loc)) + return instructions + + def get_optimized(self, insts, consts): + insts = self.complete_insts_info(insts) + insts = optimize_cfg(insts, consts) + return insts, consts diff --git a/Lib/test/test_compiler_codegen.py b/Lib/test/test_compiler_codegen.py new file mode 100644 index 0000000..f2e14c1 --- /dev/null +++ b/Lib/test/test_compiler_codegen.py @@ -0,0 +1,50 @@ + +from test.support.bytecode_helper import CodegenTestCase + +# Tests for the code-generation stage of the compiler. +# Examine the un-optimized code generated from the AST. + +class IsolatedCodeGenTests(CodegenTestCase): + + def codegen_test(self, snippet, expected_insts): + import ast + a = ast.parse(snippet, "my_file.py", "exec"); + insts = self.generate_code(a) + self.assertInstructionsMatch(insts, expected_insts) + + def test_if_expression(self): + snippet = "42 if True else 24" + false_lbl = self.Label() + expected = [ + ('RESUME', 0, 0), + ('LOAD_CONST', 0, 1), + ('POP_JUMP_IF_FALSE', false_lbl := self.Label(), 1), + ('LOAD_CONST', 1, 1), + ('JUMP', exit_lbl := self.Label()), + false_lbl, + ('LOAD_CONST', 2, 1), + exit_lbl, + ('POP_TOP', None), + ] + self.codegen_test(snippet, expected) + + def test_for_loop(self): + snippet = "for x in l:\n\tprint(x)" + false_lbl = self.Label() + expected = [ + ('RESUME', 0, 0), + ('LOAD_NAME', 0, 1), + ('GET_ITER', None, 1), + loop_lbl := self.Label(), + ('FOR_ITER', exit_lbl := self.Label(), 1), + ('STORE_NAME', None, 1), + ('PUSH_NULL', None, 2), + ('LOAD_NAME', None, 2), + ('LOAD_NAME', None, 2), + ('CALL', None, 2), + ('POP_TOP', None), + ('JUMP', loop_lbl), + exit_lbl, + ('END_FOR', None), + ] + self.codegen_test(snippet, expected) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 0d398fc..239c9d0 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -984,7 +984,7 @@ class DirectiCfgOptimizerTests(CfgOptimizationTestCase): if expected_consts is None: expected_consts = consts opt_insts, opt_consts = self.get_optimized(insts, consts) - self.compareInstructions(opt_insts, expected_insts) + self.assertInstructionsMatch(opt_insts, expected_insts) self.assertEqual(opt_consts, expected_consts) def test_conditional_jump_forward_non_const_condition(self): diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 83ce756..cec114c 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -14,7 +14,7 @@ #include "Python.h" #include "pycore_atomic_funcs.h" // _Py_atomic_int_get() #include "pycore_bitutils.h" // _Py_bswap32() -#include "pycore_compile.h" // _PyCompile_OptimizeCfg() +#include "pycore_compile.h" // _PyCompile_CodeGen, _PyCompile_OptimizeCfg #include "pycore_fileutils.h" // _Py_normpath #include "pycore_frame.h" // _PyInterpreterFrame #include "pycore_gc.h" // PyGC_Head @@ -529,6 +529,26 @@ set_eval_frame_record(PyObject *self, PyObject *list) Py_RETURN_NONE; } +/*[clinic input] + +_testinternalcapi.compiler_codegen -> object + + ast: object + filename: object + optimize: int + +Apply compiler code generation to an AST. +[clinic start generated code]*/ + +static PyObject * +_testinternalcapi_compiler_codegen_impl(PyObject *module, PyObject *ast, + PyObject *filename, int optimize) +/*[clinic end generated code: output=fbbbbfb34700c804 input=e9fbe6562f7f75e4]*/ +{ + PyCompilerFlags *flags = NULL; + return _PyCompile_CodeGen(ast, filename, flags, optimize); +} + /*[clinic input] @@ -612,6 +632,7 @@ static PyMethodDef TestMethods[] = { {"DecodeLocaleEx", decode_locale_ex, METH_VARARGS}, {"set_eval_frame_default", set_eval_frame_default, METH_NOARGS, NULL}, {"set_eval_frame_record", set_eval_frame_record, METH_O, NULL}, + _TESTINTERNALCAPI_COMPILER_CODEGEN_METHODDEF _TESTINTERNALCAPI_OPTIMIZE_CFG_METHODDEF {"get_interp_settings", get_interp_settings, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ diff --git a/Modules/clinic/_testinternalcapi.c.h b/Modules/clinic/_testinternalcapi.c.h index 8113fff..e8d5681 100644 --- a/Modules/clinic/_testinternalcapi.c.h +++ b/Modules/clinic/_testinternalcapi.c.h @@ -8,6 +8,69 @@ preserve #endif +PyDoc_STRVAR(_testinternalcapi_compiler_codegen__doc__, +"compiler_codegen($module, /, ast, filename, optimize)\n" +"--\n" +"\n" +"Apply compiler code generation to an AST."); + +#define _TESTINTERNALCAPI_COMPILER_CODEGEN_METHODDEF \ + {"compiler_codegen", _PyCFunction_CAST(_testinternalcapi_compiler_codegen), METH_FASTCALL|METH_KEYWORDS, _testinternalcapi_compiler_codegen__doc__}, + +static PyObject * +_testinternalcapi_compiler_codegen_impl(PyObject *module, PyObject *ast, + PyObject *filename, int optimize); + +static PyObject * +_testinternalcapi_compiler_codegen(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(ast), &_Py_ID(filename), &_Py_ID(optimize), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"ast", "filename", "optimize", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compiler_codegen", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + PyObject *ast; + PyObject *filename; + int optimize; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + if (!args) { + goto exit; + } + ast = args[0]; + filename = args[1]; + optimize = _PyLong_AsInt(args[2]); + if (optimize == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _testinternalcapi_compiler_codegen_impl(module, ast, filename, optimize); + +exit: + return return_value; +} + PyDoc_STRVAR(_testinternalcapi_optimize_cfg__doc__, "optimize_cfg($module, /, instructions, consts)\n" "--\n" @@ -65,4 +128,4 @@ _testinternalcapi_optimize_cfg(PyObject *module, PyObject *const *args, Py_ssize exit: return return_value; } -/*[clinic end generated code: output=3b1fd713290f68a9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=efe95836482fd542 input=a9049054013a1b77]*/ diff --git a/Python/compile.c b/Python/compile.c index 37f3b23..bd41ebc 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -122,7 +122,7 @@ (opcode) == STORE_FAST__STORE_FAST) #define IS_TOP_LEVEL_AWAIT(c) ( \ - (c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \ + (c->c_flags.cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \ && (c->u->u_ste->ste_type == ModuleBlock)) typedef _PyCompilerSrcLocation location; @@ -418,7 +418,7 @@ struct compiler { PyObject *c_filename; struct symtable *c_st; PyFutureFeatures c_future; /* module's __future__ */ - PyCompilerFlags *c_flags; + PyCompilerFlags c_flags; int c_optimize; /* optimization level */ int c_interactive; /* true if in interactive mode */ @@ -583,11 +583,11 @@ _Py_Mangle(PyObject *privateobj, PyObject *ident) return result; } + static int -compiler_init(struct compiler *c) +compiler_setup(struct compiler *c, mod_ty mod, PyObject *filename, + PyCompilerFlags flags, int optimize, PyArena *arena) { - memset(c, 0, sizeof(struct compiler)); - c->c_const_cache = PyDict_New(); if (!c->c_const_cache) { return 0; @@ -595,57 +595,65 @@ compiler_init(struct compiler *c) c->c_stack = PyList_New(0); if (!c->c_stack) { - Py_CLEAR(c->c_const_cache); return 0; } - return 1; -} - -PyCodeObject * -_PyAST_Compile(mod_ty mod, PyObject *filename, PyCompilerFlags *flags, - int optimize, PyArena *arena) -{ - struct compiler c; - PyCodeObject *co = NULL; - PyCompilerFlags local_flags = _PyCompilerFlags_INIT; - int merged; - if (!compiler_init(&c)) - return NULL; - c.c_filename = Py_NewRef(filename); - c.c_arena = arena; - if (!_PyFuture_FromAST(mod, filename, &c.c_future)) { - goto finally; - } - if (!flags) { - flags = &local_flags; + c->c_filename = Py_NewRef(filename); + c->c_arena = arena; + if (!_PyFuture_FromAST(mod, filename, &c->c_future)) { + return 0; } - merged = c.c_future.ff_features | flags->cf_flags; - c.c_future.ff_features = merged; - flags->cf_flags = merged; - c.c_flags = flags; - c.c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level : optimize; - c.c_nestlevel = 0; + int merged = c->c_future.ff_features | flags.cf_flags; + c->c_future.ff_features = merged; + flags.cf_flags = merged; + c->c_flags = flags; + c->c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level : optimize; + c->c_nestlevel = 0; _PyASTOptimizeState state; - state.optimize = c.c_optimize; + state.optimize = c->c_optimize; state.ff_features = merged; if (!_PyAST_Optimize(mod, arena, &state)) { - goto finally; + return 0; } - - c.c_st = _PySymtable_Build(mod, filename, &c.c_future); - if (c.c_st == NULL) { - if (!PyErr_Occurred()) + c->c_st = _PySymtable_Build(mod, filename, &c->c_future); + if (c->c_st == NULL) { + if (!PyErr_Occurred()) { PyErr_SetString(PyExc_SystemError, "no symtable"); - goto finally; + } + return 0; } + return 1; +} - co = compiler_mod(&c, mod); +static struct compiler* +new_compiler(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags, + int optimize, PyArena *arena) +{ + PyCompilerFlags flags = pflags ? *pflags : _PyCompilerFlags_INIT; + struct compiler *c = PyMem_Calloc(1, sizeof(struct compiler)); + if (c == NULL) { + return NULL; + } + if (!compiler_setup(c, mod, filename, flags, optimize, arena)) { + compiler_free(c); + return NULL; + } + return c; +} - finally: - compiler_free(&c); +PyCodeObject * +_PyAST_Compile(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags, + int optimize, PyArena *arena) +{ + struct compiler *c = new_compiler(mod, filename, pflags, optimize, arena); + if (c == NULL) { + return NULL; + } + + PyCodeObject *co = compiler_mod(c, mod); + compiler_free(c); assert(co || PyErr_Occurred()); return co; } @@ -656,8 +664,9 @@ compiler_free(struct compiler *c) if (c->c_st) _PySymtable_Free(c->c_st); Py_XDECREF(c->c_filename); - Py_DECREF(c->c_const_cache); - Py_DECREF(c->c_stack); + Py_XDECREF(c->c_const_cache); + Py_XDECREF(c->c_stack); + PyMem_Free(c); } static PyObject * @@ -2136,15 +2145,13 @@ compiler_body(struct compiler *c, location loc, asdl_stmt_seq *stmts) return 1; } -static PyCodeObject * -compiler_mod(struct compiler *c, mod_ty mod) +static int +compiler_codegen(struct compiler *c, mod_ty mod) { - PyCodeObject *co; - int addNone = 1; _Py_DECLARE_STR(anon_module, ""); if (!compiler_enter_scope(c, &_Py_STR(anon_module), COMPILER_SCOPE_MODULE, mod, 1)) { - return NULL; + return 0; } location loc = LOCATION(1, 1, 0, 0); switch (mod->kind) { @@ -2163,7 +2170,6 @@ compiler_mod(struct compiler *c, mod_ty mod) break; case Expression_kind: VISIT_IN_SCOPE(c, expr, mod->v.Expression.body); - addNone = 0; break; default: PyErr_Format(PyExc_SystemError, @@ -2171,7 +2177,17 @@ compiler_mod(struct compiler *c, mod_ty mod) mod->kind); return 0; } - co = assemble(c, addNone); + return 1; +} + +static PyCodeObject * +compiler_mod(struct compiler *c, mod_ty mod) +{ + int addNone = mod->kind != Expression_kind; + if (!compiler_codegen(c, mod)) { + return NULL; + } + PyCodeObject *co = assemble(c, addNone); compiler_exit_scope(c); return co; } @@ -8229,7 +8245,7 @@ compute_code_flags(struct compiler *c) } /* (Only) inherit compilerflags in PyCF_MASK */ - flags |= (c->c_flags->cf_flags & PyCF_MASK); + flags |= (c->c_flags.cf_flags & PyCF_MASK); if ((IS_TOP_LEVEL_AWAIT(c)) && ste->ste_coroutine && @@ -9860,6 +9876,9 @@ duplicate_exits_without_lineno(cfg_builder *g) /* 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. * @@ -9954,7 +9973,9 @@ cfg_to_instructions(cfg_builder *g) for (int i = 0; i < b->b_iused; i++) { struct instr *instr = &b->b_instr[i]; location loc = instr->i_loc; - int arg = HAS_TARGET(instr->i_opcode) ? instr->i_target->b_label : instr->i_oparg; + int arg = HAS_TARGET(instr->i_opcode) ? + instr->i_target->b_label : instr->i_oparg; + PyObject *inst_tuple = Py_BuildValue( "(iiiiii)", instr->i_opcode, arg, loc.lineno, loc.end_lineno, @@ -9977,6 +9998,52 @@ error: return NULL; } +PyObject * +_PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags, + int optimize) +{ + PyObject *res = NULL; + + if (!PyAST_Check(ast)) { + PyErr_SetString(PyExc_TypeError, "expected an AST"); + return NULL; + } + + PyArena *arena = _PyArena_New(); + if (arena == NULL) { + return NULL; + } + + mod_ty mod = PyAST_obj2mod(ast, arena, 0 /* exec */); + if (mod == NULL || !_PyAST_Validate(mod)) { + _PyArena_Free(arena); + return NULL; + } + + struct compiler *c = new_compiler(mod, filename, pflags, optimize, arena); + if (c == NULL) { + _PyArena_Free(arena); + return NULL; + } + + if (!compiler_codegen(c, mod)) { + goto finally; + } + + cfg_builder *g = CFG_BUILDER(c); + + if (translate_jump_labels_to_targets(g->g_entryblock) < 0) { + goto finally; + } + + res = cfg_to_instructions(g); + +finally: + compiler_exit_scope(c); + compiler_free(c); + _PyArena_Free(arena); + return res; +} PyObject * _PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts) -- cgit v0.12