From 6e081614eb7b7e93d3ddcdd1c34abe551e9c6550 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 8 Apr 2026 20:53:20 +0530 Subject: [PATCH] gh-148210: fix incorrect `_BINARY_OP_SUBSCR_DICT` JIT optimization (GH-148213) --- Lib/test/test_capi/test_opt.py | 15 +++++++++++++++ Python/optimizer_bytecodes.c | 10 +++++----- Python/optimizer_cases.c.h | 10 +++++----- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index b25c3c4c4e7..f89f0d8bc4e 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2340,6 +2340,21 @@ def testfunc(n): self.assertIn("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH", uops) self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops) + + def test_binary_op_subscr_constant_frozendict_known_hash(self): + def testfunc(n): + x = 0 + for _ in range(n): + x += FROZEN_DICT_CONST['x'] + return x + + res, ex = self._run_with_optimizer(testfunc, 2 * TIER2_THRESHOLD) + self.assertEqual(res, 2 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops) + def test_store_subscr_dict_known_hash(self): # str, int, bytes, float, complex, tuple and any python object which has generic hash def testfunc(n): diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index f0abdc098dd..3ba404b81ea 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -515,18 +515,18 @@ dummy_func(void) { } op(_BINARY_OP_SUBSCR_DICT, (dict_st, sub_st -- res, ds, ss)) { - PyObject *sub = sym_get_const(ctx, sub_st); - if (sub != NULL) { - optimize_dict_known_hash(ctx, dependencies, this_instr, - sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); - } res = sym_new_not_null(ctx); ds = dict_st; ss = sub_st; + PyObject *sub = sym_get_const(ctx, sub_st); if (sym_is_not_container(sub_st) && sym_matches_type(dict_st, &PyFrozenDict_Type)) { REPLACE_OPCODE_IF_EVALUATES_PURE(dict_st, sub_st, res); } + else if (sub != NULL) { + optimize_dict_known_hash(ctx, dependencies, this_instr, + sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); + } } op(_BINARY_OP_SUBSCR_LIST_SLICE, (list_st, sub_st -- res, ls, ss)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 6f9b0e6a4d0..2c8a0bff13c 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1463,14 +1463,10 @@ JitOptRef ss; sub_st = stack_pointer[-1]; dict_st = stack_pointer[-2]; - PyObject *sub = sym_get_const(ctx, sub_st); - if (sub != NULL) { - optimize_dict_known_hash(ctx, dependencies, this_instr, - sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); - } res = sym_new_not_null(ctx); ds = dict_st; ss = sub_st; + PyObject *sub = sym_get_const(ctx, sub_st); if (sym_is_not_container(sub_st) && sym_matches_type(dict_st, &PyFrozenDict_Type)) { if ( @@ -1520,6 +1516,10 @@ break; } } + else if (sub != NULL) { + optimize_dict_known_hash(ctx, dependencies, this_instr, + sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); + } CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; stack_pointer[-1] = ds;