Skip to content

Commit bf68eb9

Browse files
committed
Add more tests
1 parent dd6426f commit bf68eb9

File tree

5 files changed

+207
-4
lines changed

5 files changed

+207
-4
lines changed

Include/internal/pycore_flowgraph.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ PyAPI_FUNC(PyObject*) _PyCompile_OptimizeCfg(
4141
PyObject *consts,
4242
int nlocals);
4343

44+
// Export for '_testinternalcapi' shared extension
45+
PyAPI_FUNC(PyObject*) _PyCompile_OptimizeLoadFast(PyObject *instructions);
46+
4447
#ifdef __cplusplus
4548
}
4649
#endif

Lib/test/test_peepholer.py

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
import sys
55
import textwrap
66
import unittest
7+
try:
8+
import _testinternalcapi
9+
except ImportError:
10+
_testinternalcapi = None
711

812
from test import support
9-
from test.support.bytecode_helper import BytecodeTestCase, CfgOptimizationTestCase
13+
from test.support.bytecode_helper import (
14+
BytecodeTestCase, CfgOptimizationTestCase, CompilationStepTestCase)
1015

1116

1217
def compile_pattern_with_fast_locals(pattern):
@@ -2353,5 +2358,97 @@ def test_list_to_tuple_get_iter_is_safe(self):
23532358
self.assertEqual(items, [])
23542359

23552360

2361+
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
2362+
class OptimizeLoadFastTestCase(CompilationStepTestCase):
2363+
def check(self, insts, expected_insts):
2364+
self.check_instructions(insts)
2365+
self.check_instructions(expected_insts)
2366+
seq = self.seq_from_insts(insts)
2367+
opt_insts = _testinternalcapi.optimize_load_fast(seq)
2368+
expected_insts = self.seq_from_insts(expected_insts).get_instructions()
2369+
self.assertInstructionsMatch(opt_insts, expected_insts)
2370+
2371+
def test_optimized(self):
2372+
insts = [
2373+
("LOAD_FAST", 0, 1),
2374+
("LOAD_FAST", 1, 2),
2375+
("BINARY_OP", 2, 3),
2376+
]
2377+
expected = [
2378+
("LOAD_FAST_BORROW", 0, 1),
2379+
("LOAD_FAST_BORROW", 1, 2),
2380+
("BINARY_OP", 2, 3),
2381+
]
2382+
self.check(insts, expected)
2383+
2384+
insts = [
2385+
("LOAD_FAST", 0, 1),
2386+
("LOAD_CONST", 1, 2),
2387+
("SWAP", 2, 3),
2388+
("POP_TOP", None, 4),
2389+
]
2390+
expected = [
2391+
("LOAD_FAST_BORROW", 0, 1),
2392+
("LOAD_CONST", 1, 2),
2393+
("SWAP", 2, 3),
2394+
("POP_TOP", None, 4),
2395+
]
2396+
self.check(insts, expected)
2397+
2398+
def test_unoptimized_if_unconsumed(self):
2399+
insts = [
2400+
("LOAD_FAST", 0, 1),
2401+
("LOAD_FAST", 1, 2),
2402+
("POP_TOP", None, 3),
2403+
]
2404+
expected = [
2405+
("LOAD_FAST", 0, 1),
2406+
("LOAD_FAST_BORROW", 1, 2),
2407+
("POP_TOP", None, 3),
2408+
]
2409+
self.check(insts, expected)
2410+
2411+
insts = [
2412+
("LOAD_FAST", 0, 1),
2413+
("COPY", 1, 2),
2414+
("POP_TOP", None, 3),
2415+
]
2416+
self.check(insts, insts)
2417+
2418+
def test_unoptimized_if_support_killed(self):
2419+
insts = [
2420+
("LOAD_FAST", 0, 1),
2421+
("LOAD_CONST", 0, 2),
2422+
("STORE_FAST", 0, 3),
2423+
("POP_TOP", None, 4),
2424+
]
2425+
self.check(insts, insts)
2426+
2427+
insts = [
2428+
("LOAD_FAST", 0, 1),
2429+
("LOAD_CONST", 0, 2),
2430+
("LOAD_CONST", 0, 3),
2431+
("STORE_FAST_STORE_FAST", 0 << 4 | 1, 4),
2432+
("POP_TOP", None, 5),
2433+
]
2434+
self.check(insts, insts)
2435+
2436+
def test_unoptimized_if_aliased(self):
2437+
insts = [
2438+
("LOAD_FAST", 0, 1),
2439+
("STORE_FAST", 1, 2),
2440+
]
2441+
self.check(insts, insts)
2442+
2443+
insts = [
2444+
("LOAD_FAST", 0, 1),
2445+
("LOAD_CONST", 0, 3),
2446+
("STORE_FAST_STORE_FAST", 0 << 4 | 1, 4),
2447+
]
2448+
self.check(insts, insts)
2449+
2450+
2451+
2452+
23562453
if __name__ == "__main__":
23572454
unittest.main()

Modules/_testinternalcapi.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,23 @@ _testinternalcapi_optimize_cfg_impl(PyObject *module, PyObject *instructions,
748748
return _PyCompile_OptimizeCfg(instructions, consts, nlocals);
749749
}
750750

751+
/*[clinic input]
752+
753+
_testinternalcapi.optimize_load_fast -> object
754+
755+
instructions: object
756+
757+
Optimize LOAD_FAST{_LOAD_FAST} instructions.
758+
[clinic start generated code]*/
759+
760+
static PyObject *
761+
_testinternalcapi_optimize_load_fast_impl(PyObject *module,
762+
PyObject *instructions)
763+
/*[clinic end generated code: output=6f975349c976d017 input=c59f3eac68308c01]*/
764+
{
765+
return _PyCompile_OptimizeLoadFast(instructions);
766+
}
767+
751768
static int
752769
get_nonnegative_int_from_dict(PyObject *dict, const char *key) {
753770
PyObject *obj = PyDict_GetItemString(dict, key);
@@ -2023,6 +2040,7 @@ static PyMethodDef module_functions[] = {
20232040
_TESTINTERNALCAPI_NEW_INSTRUCTION_SEQUENCE_METHODDEF
20242041
_TESTINTERNALCAPI_COMPILER_CODEGEN_METHODDEF
20252042
_TESTINTERNALCAPI_OPTIMIZE_CFG_METHODDEF
2043+
_TESTINTERNALCAPI_OPTIMIZE_LOAD_FAST_METHODDEF
20262044
_TESTINTERNALCAPI_ASSEMBLE_CODE_OBJECT_METHODDEF
20272045
{"get_interp_settings", get_interp_settings, METH_VARARGS, NULL},
20282046
{"clear_extension", clear_extension, METH_VARARGS, NULL},

Modules/clinic/_testinternalcapi.c.h

Lines changed: 58 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/flowgraph.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2572,12 +2572,15 @@ load_fast_push_block(basicblock ***sp, basicblock *target, int start_depth)
25722572
* non-violating LOAD_FAST{_LOAD_FAST} can be optimized.
25732573
*/
25742574
static int
2575-
optimize_load_fast(cfg_builder *g)
2575+
optimize_load_fast(cfg_builder *g, bool compute_stackdepth)
25762576
{
25772577
int status;
25782578
ref_stack refs = {0};
25792579
int max_instrs = 0;
25802580
basicblock *entryblock = g->g_entryblock;
2581+
if (compute_stackdepth) {
2582+
calculate_stackdepth(g);
2583+
}
25812584
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
25822585
max_instrs = Py_MAX(max_instrs, b->b_iused);
25832586
}
@@ -2620,6 +2623,7 @@ optimize_load_fast(cfg_builder *g)
26202623
assert(opcode != EXTENDED_ARG);
26212624
switch (opcode) {
26222625
case COPY: {
2626+
assert(oparg > 0);
26232627
Py_ssize_t idx = refs.size - oparg;
26242628
ref r = ref_stack_at(&refs, idx);
26252629
if (ref_stack_push(&refs, r) < 0) {
@@ -2687,6 +2691,7 @@ optimize_load_fast(cfg_builder *g)
26872691
}
26882692

26892693
case SWAP: {
2694+
assert(oparg >= 2);
26902695
ref_stack_swap_top(&refs, oparg);
26912696
break;
26922697
}
@@ -3711,7 +3716,7 @@ _PyCfg_OptimizedCfgToInstructionSequence(cfg_builder *g,
37113716
/* Can't modify the bytecode after inserting instructions that produce
37123717
* borrowed references.
37133718
*/
3714-
RETURN_IF_ERROR(optimize_load_fast(g));
3719+
RETURN_IF_ERROR(optimize_load_fast(g, /* compute_stackdepth */ false));
37153720

37163721
/* Can't modify the bytecode after computing jump offsets. */
37173722
if (_PyCfg_ToInstructionSequence(g, seq) < 0) {
@@ -3802,3 +3807,26 @@ _PyCompile_OptimizeCfg(PyObject *seq, PyObject *consts, int nlocals)
38023807
_PyCfgBuilder_Free(g);
38033808
return res;
38043809
}
3810+
3811+
PyObject *
3812+
_PyCompile_OptimizeLoadFast(PyObject *seq)
3813+
{
3814+
if (!_PyInstructionSequence_Check(seq)) {
3815+
PyErr_SetString(PyExc_ValueError, "expected an instruction sequence");
3816+
return NULL;
3817+
}
3818+
3819+
cfg_builder *g = _PyCfg_FromInstructionSequence((_PyInstructionSequence*)seq);
3820+
if (g == NULL) {
3821+
return NULL;
3822+
}
3823+
3824+
if (optimize_load_fast(g, /* compute_stackdepth */ true) != SUCCESS) {
3825+
_PyCfgBuilder_Free(g);
3826+
return NULL;
3827+
}
3828+
3829+
PyObject *res = cfg_to_instruction_sequence(g);
3830+
_PyCfgBuilder_Free(g);
3831+
return res;
3832+
}

0 commit comments

Comments
 (0)