Skip to content

Commit 9cdd2fa

Browse files
authored
GH-98831: Add DECREF_INPUTS(), expanding to DECREF() each stack input (#100205)
The presence of this macro indicates that a particular instruction may be considered for conversion to a register-based format (see faster-cpython/ideas#485). An invariant (currently unchecked) is that `DEOPT_IF()` may only occur *before* `DECREF_INPUTS()`, and `ERROR_IF()` may only occur *after* it. One reason not to check this is that there are a few places where we insert *two* `DECREF_INPUTS()` calls, in different branches of the code. The invariant checking would have to be able to do some flow control analysis to understand this. Note that many instructions, especially specialized ones, can't be converted to use this macro straightforwardly. This is because the generator currently only generates plain `Py_DECREF(variable)` statements, and cannot generate things like `_Py_DECREF_SPECIALIZED()` let alone deal with `_PyList_AppendTakeRef()`.
1 parent 5a991da commit 9cdd2fa

File tree

2 files changed

+18
-19
lines changed

2 files changed

+18
-19
lines changed

Python/bytecodes.c

+13-18
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ dummy_func(
161161
super(LOAD_CONST__LOAD_FAST) = LOAD_CONST + LOAD_FAST;
162162

163163
inst(POP_TOP, (value --)) {
164-
Py_DECREF(value);
164+
DECREF_INPUTS();
165165
}
166166

167167
inst(PUSH_NULL, (-- res)) {
@@ -172,19 +172,19 @@ dummy_func(
172172

173173
inst(UNARY_POSITIVE, (value -- res)) {
174174
res = PyNumber_Positive(value);
175-
Py_DECREF(value);
175+
DECREF_INPUTS();
176176
ERROR_IF(res == NULL, error);
177177
}
178178

179179
inst(UNARY_NEGATIVE, (value -- res)) {
180180
res = PyNumber_Negative(value);
181-
Py_DECREF(value);
181+
DECREF_INPUTS();
182182
ERROR_IF(res == NULL, error);
183183
}
184184

185185
inst(UNARY_NOT, (value -- res)) {
186186
int err = PyObject_IsTrue(value);
187-
Py_DECREF(value);
187+
DECREF_INPUTS();
188188
ERROR_IF(err < 0, error);
189189
if (err == 0) {
190190
res = Py_True;
@@ -197,7 +197,7 @@ dummy_func(
197197

198198
inst(UNARY_INVERT, (value -- res)) {
199199
res = PyNumber_Invert(value);
200-
Py_DECREF(value);
200+
DECREF_INPUTS();
201201
ERROR_IF(res == NULL, error);
202202
}
203203

@@ -351,8 +351,7 @@ dummy_func(
351351
STAT_INC(BINARY_SUBSCR, deferred);
352352
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
353353
res = PyObject_GetItem(container, sub);
354-
Py_DECREF(container);
355-
Py_DECREF(sub);
354+
DECREF_INPUTS();
356355
ERROR_IF(res == NULL, error);
357356
}
358357

@@ -438,8 +437,7 @@ dummy_func(
438437
ERROR_IF(true, error);
439438
}
440439
Py_INCREF(res); // Do this before DECREF'ing dict, sub
441-
Py_DECREF(dict);
442-
Py_DECREF(sub);
440+
DECREF_INPUTS();
443441
}
444442

445443
inst(BINARY_SUBSCR_GETITEM, (unused/1, type_version/2, func_version/1, container, sub -- unused)) {
@@ -500,9 +498,7 @@ dummy_func(
500498
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
501499
/* container[sub] = v */
502500
int err = PyObject_SetItem(container, sub, v);
503-
Py_DECREF(v);
504-
Py_DECREF(container);
505-
Py_DECREF(sub);
501+
DECREF_INPUTS();
506502
ERROR_IF(err, error);
507503
}
508504

@@ -538,8 +534,7 @@ dummy_func(
538534
inst(DELETE_SUBSCR, (container, sub --)) {
539535
/* del container[sub] */
540536
int err = PyObject_DelItem(container, sub);
541-
Py_DECREF(container);
542-
Py_DECREF(sub);
537+
DECREF_INPUTS();
543538
ERROR_IF(err, error);
544539
}
545540

@@ -550,11 +545,11 @@ dummy_func(
550545
if (hook == NULL) {
551546
_PyErr_SetString(tstate, PyExc_RuntimeError,
552547
"lost sys.displayhook");
553-
Py_DECREF(value);
548+
DECREF_INPUTS();
554549
ERROR_IF(true, error);
555550
}
556551
res = PyObject_CallOneArg(hook, value);
557-
Py_DECREF(value);
552+
DECREF_INPUTS();
558553
ERROR_IF(res == NULL, error);
559554
Py_DECREF(res);
560555
}
@@ -625,12 +620,12 @@ dummy_func(
625620
"'async for' requires an object with "
626621
"__aiter__ method, got %.100s",
627622
type->tp_name);
628-
Py_DECREF(obj);
623+
DECREF_INPUTS();
629624
ERROR_IF(true, error);
630625
}
631626

632627
iter = (*getter)(obj);
633-
Py_DECREF(obj);
628+
DECREF_INPUTS();
634629
ERROR_IF(iter == NULL, error);
635630

636631
if (Py_TYPE(iter)->tp_as_async == NULL ||

Tools/cases_generator/generate_cases.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
209209
cache_offset += ceffect.size
210210
assert cache_offset == self.cache_offset + cache_adjust
211211

212-
# Write the body, substituting a goto for ERROR_IF()
212+
# Write the body, substituting a goto for ERROR_IF() and other stuff
213213
assert dedent <= 0
214214
extra = " " * -dedent
215215
for line in self.block_text:
@@ -232,6 +232,10 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
232232
)
233233
else:
234234
out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n")
235+
elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*$", line):
236+
space = m.group(1)
237+
for ieff in self.input_effects:
238+
out.write_raw(f"{extra}{space}Py_DECREF({ieff.name});\n")
235239
else:
236240
out.write_raw(extra + line)
237241

0 commit comments

Comments
 (0)