gh-87092: expose the compiler's codegen to python for unit tests (GH-99111)

This commit is contained in:
Irit Katriel 2022-11-14 13:56:40 +00:00 committed by GitHub
parent 06d4e02c3b
commit a3ac9232f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 323 additions and 99 deletions

View file

@ -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)
{
memset(c, 0, sizeof(struct compiler));
static int
compiler_setup(struct compiler *c, mod_ty mod, PyObject *filename,
PyCompilerFlags flags, int optimize, PyArena *arena)
{
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;
c->c_filename = Py_NewRef(filename);
c->c_arena = arena;
if (!_PyFuture_FromAST(mod, filename, &c->c_future)) {
return 0;
}
if (!flags) {
flags = &local_flags;
}
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;
}
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;
}
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;
}
co = compiler_mod(&c, mod);
finally:
compiler_free(&c);
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, "<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 &&
@ -9859,6 +9875,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)