Skip to content

Commit 90a3196

Browse files
committed
move constant subscript folding to CFG
1 parent 71ae933 commit 90a3196

File tree

4 files changed

+87
-30
lines changed

4 files changed

+87
-30
lines changed

Lib/test/test_ast/test_ast.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3279,16 +3279,6 @@ def test_folding_iter(self):
32793279

32803280
self.assert_ast(code % (left, right), non_optimized_target, optimized_target)
32813281

3282-
def test_folding_subscript(self):
3283-
code = "(1,)[0]"
3284-
3285-
non_optimized_target = self.wrap_expr(
3286-
ast.Subscript(value=ast.Tuple(elts=[ast.Constant(value=1)]), slice=ast.Constant(value=0))
3287-
)
3288-
optimized_target = self.wrap_expr(ast.Constant(value=1))
3289-
3290-
self.assert_ast(code, non_optimized_target, optimized_target)
3291-
32923282
def test_folding_type_param_in_function_def(self):
32933283
code = "def foo[%s = 1 + 1](): pass"
32943284

Lib/test/test_peepholer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,8 @@ def test_constant_folding(self):
463463
'(1, 2, -3)',
464464
'(1, 2, -3) * 6',
465465
'lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}',
466+
'(1, )[0]',
467+
'(1, (1, 2))[1][1]'
466468
]
467469
for e in exprs:
468470
with self.subTest(e=e):

Python/ast_opt.c

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -567,25 +567,6 @@ fold_tuple(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
567567
return make_const(node, newval, arena);
568568
}
569569

570-
static int
571-
fold_subscr(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
572-
{
573-
PyObject *newval;
574-
expr_ty arg, idx;
575-
576-
arg = node->v.Subscript.value;
577-
idx = node->v.Subscript.slice;
578-
if (node->v.Subscript.ctx != Load ||
579-
arg->kind != Constant_kind ||
580-
idx->kind != Constant_kind)
581-
{
582-
return 1;
583-
}
584-
585-
newval = PyObject_GetItem(arg->v.Constant.value, idx->v.Constant.value);
586-
return make_const(node, newval, arena);
587-
}
588-
589570
/* Change literal list or set of constants into constant
590571
tuple or frozenset respectively. Change literal list of
591572
non-constants into tuple.
@@ -822,7 +803,7 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
822803
case Subscript_kind:
823804
CALL(astfold_expr, expr_ty, node_->v.Subscript.value);
824805
CALL(astfold_expr, expr_ty, node_->v.Subscript.slice);
825-
CALL(fold_subscr, expr_ty, node_);
806+
/* Subscript folding is now done in flowgraph.c (optimize_constant_subscr) */
826807
break;
827808
case Starred_kind:
828809
CALL(astfold_expr, expr_ty, node_->v.Starred.value);

Python/flowgraph.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121
return ERROR; \
2222
}
2323

24+
#define RETURN_IF_FOLD_FAIL(X) \
25+
if ((X) == NULL) { \
26+
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) { \
27+
return ERROR; \
28+
} \
29+
PyErr_Clear(); \
30+
return SUCCESS; \
31+
}
32+
2433
#define DEFAULT_BLOCK_SIZE 16
2534

2635
typedef _Py_SourceLocation location;
@@ -1443,6 +1452,78 @@ optimize_if_const_list_or_set(PyObject *const_cache, cfg_instr* inst, int n, PyO
14431452
return SUCCESS;
14441453
}
14451454

1455+
/*
1456+
Walk basic block upwards starting from "start" to collect instruction pair
1457+
that loads consts skipping NOP's in between.
1458+
*/
1459+
static bool
1460+
find_load_const_pair(basicblock *bb, int start, cfg_instr **first, cfg_instr **second)
1461+
{
1462+
cfg_instr *second_load_const = NULL;
1463+
while (start >= 0) {
1464+
cfg_instr *inst = &bb->b_instr[start--];
1465+
if (inst->i_opcode == NOP) {
1466+
continue;
1467+
}
1468+
if (!loads_const(inst->i_opcode)) {
1469+
return false;
1470+
}
1471+
if (second_load_const == NULL) {
1472+
second_load_const = inst;
1473+
continue;
1474+
}
1475+
*first = inst;
1476+
*second = second_load_const;
1477+
return true;
1478+
}
1479+
return false;
1480+
}
1481+
1482+
/* Determine opcode & oparg for freshly folded constant. */
1483+
static int
1484+
newop_from_folded(PyObject *newconst, PyObject *consts,
1485+
PyObject *const_cache, int *newopcode, int *newoparg)
1486+
{
1487+
if (PyLong_CheckExact(newconst)) {
1488+
int overflow;
1489+
long val = PyLong_AsLongAndOverflow(newconst, &overflow);
1490+
if (!overflow && val >= 0 && val < 256) {
1491+
*newopcode = LOAD_SMALL_INT;
1492+
*newoparg = val;
1493+
return SUCCESS;
1494+
}
1495+
}
1496+
*newopcode = LOAD_CONST;
1497+
*newoparg = add_const(newconst, consts, const_cache);
1498+
RETURN_IF_ERROR(*newoparg);
1499+
return SUCCESS;
1500+
}
1501+
1502+
static int
1503+
optimize_if_const_subscr(basicblock *bb, int n, PyObject *consts, PyObject *const_cache)
1504+
{
1505+
cfg_instr *subscr = &bb->b_instr[n];
1506+
assert(subscr->i_opcode == BINARY_SUBSCR);
1507+
cfg_instr *arg, *idx;
1508+
if (!find_load_const_pair(bb, n-1, &arg, &idx)) {
1509+
return SUCCESS;
1510+
}
1511+
PyObject *o, *key;
1512+
if ((o = get_const_value(arg->i_opcode, arg->i_oparg, consts)) == NULL
1513+
|| (key = get_const_value(idx->i_opcode, idx->i_oparg, consts)) == NULL)
1514+
{
1515+
return ERROR;
1516+
}
1517+
PyObject *newconst = PyObject_GetItem(o, key);
1518+
RETURN_IF_FOLD_FAIL(newconst);
1519+
int newopcode, newoparg;
1520+
RETURN_IF_ERROR(newop_from_folded(newconst, consts, const_cache, &newopcode, &newoparg));
1521+
INSTR_SET_OP1(subscr, newopcode, newoparg);
1522+
INSTR_SET_OP0(arg, NOP);
1523+
INSTR_SET_OP0(idx, NOP);
1524+
return SUCCESS;
1525+
}
1526+
14461527
#define VISITED (-1)
14471528

14481529
// Replace an arbitrary run of SWAPs and NOPs with an optimal one that has the
@@ -1948,6 +2029,9 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
19482029
INSTR_SET_OP0(inst, NOP);
19492030
}
19502031
break;
2032+
case BINARY_SUBSCR:
2033+
RETURN_IF_ERROR(optimize_if_const_subscr(bb, i, consts, const_cache));
2034+
break;
19512035
}
19522036
}
19532037

0 commit comments

Comments
 (0)