Skip to content

Commit

Permalink
gh-102859: Remove JUMP_IF_FALSE_OR_POP and JUMP_IF_TRUE_OR_POP (#102870)
Browse files Browse the repository at this point in the history
  • Loading branch information
iritkatriel committed Mar 22, 2023
1 parent 04adf2d commit 3468c76
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 437 deletions.
24 changes: 0 additions & 24 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1152,30 +1152,6 @@ iterations of the loop.
.. versionchanged:: 3.12
This is no longer a pseudo-instruction.


.. opcode:: JUMP_IF_TRUE_OR_POP (delta)

If ``STACK[-1]`` is true, increments the bytecode counter by *delta* and leaves
``STACK[-1]`` on the stack. Otherwise (``STACK[-1]`` is false), ``STACK[-1]``
is popped.

.. versionadded:: 3.1

.. versionchanged:: 3.11
The oparg is now a relative delta rather than an absolute target.

.. opcode:: JUMP_IF_FALSE_OR_POP (delta)

If ``STACK[-1]`` is false, increments the bytecode counter by *delta* and leaves
``STACK[-1]`` on the stack. Otherwise (``STACK[-1]`` is true), ``STACK[-1]`` is
popped.

.. versionadded:: 3.1

.. versionchanged:: 3.11
The oparg is now a relative delta rather than an absolute target.


.. opcode:: FOR_ITER (delta)

``STACK[-1]`` is an :term:`iterator`. Call its :meth:`~iterator.__next__` method.
Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@ CPython bytecode changes
:opcode:`LOAD_METHOD` instruction if the low bit of its oparg is set.
(Contributed by Ken Jin in :gh:`93429`.)

* Removed the :opcode:`JUMP_IF_FALSE_OR_POP` and :opcode:`JUMP_IF_TRUE_OR_POP`
instructions. (Contributed by Irit Katriel in :gh:`102859`.)


Demos and Tools
===============
Expand Down
22 changes: 11 additions & 11 deletions Include/internal/pycore_opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 8 additions & 10 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12a6 3519 (Modify SEND instruction)
# Python 3.12a6 3520 (Remove PREP_RERAISE_STAR, add CALL_INTRINSIC_2)
# Python 3.12a7 3521 (Shrink the LOAD_GLOBAL caches)
# Python 3.12a7 3522 (Removed JUMP_IF_FALSE_OR_POP/JUMP_IF_TRUE_OR_POP)

# Python 3.13 will start with 3550

Expand All @@ -451,7 +452,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3521).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3522).to_bytes(2, 'little') + b'\r\n'

_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

Expand Down
2 changes: 0 additions & 2 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ def pseudo_op(name, op, real_ops):
name_op('IMPORT_NAME', 108) # Index in name list
name_op('IMPORT_FROM', 109) # Index in name list
jrel_op('JUMP_FORWARD', 110) # Number of words to skip
jrel_op('JUMP_IF_FALSE_OR_POP', 111) # Number of words to skip
jrel_op('JUMP_IF_TRUE_OR_POP', 112) # ""
jrel_op('POP_JUMP_IF_FALSE', 114)
jrel_op('POP_JUMP_IF_TRUE', 115)
name_op('LOAD_GLOBAL', 116) # Index in name list
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test__opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ def test_stack_effect(self):
self.assertRaises(ValueError, stack_effect, code, 0)

def test_stack_effect_jump(self):
JUMP_IF_TRUE_OR_POP = dis.opmap['JUMP_IF_TRUE_OR_POP']
self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0), 0)
self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0, jump=True), 0)
self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0, jump=False), -1)
FOR_ITER = dis.opmap['FOR_ITER']
self.assertEqual(stack_effect(FOR_ITER, 0), 1)
self.assertEqual(stack_effect(FOR_ITER, 0, jump=True), 1)
Expand Down
24 changes: 9 additions & 15 deletions Lib/test/test_peepholer.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ def check_jump_targets(self, code):
tgt.opname == 'RETURN_VALUE'):
self.fail(f'{instr.opname} at {instr.offset} '
f'jumps to {tgt.opname} at {tgt.offset}')
# JUMP_IF_*_OR_POP jump to conditional jump
if '_OR_POP' in instr.opname and 'JUMP_IF_' in tgt.opname:
self.fail(f'{instr.opname} at {instr.offset} '
f'jumps to {tgt.opname} at {tgt.offset}')

def check_lnotab(self, code):
"Check that the lnotab byte offsets are sensible."
Expand Down Expand Up @@ -384,38 +380,36 @@ def f():

def test_elim_jump_to_uncond_jump3(self):
# Intentionally use two-line expressions to test issue37213.
# JUMP_IF_FALSE_OR_POP to JUMP_IF_FALSE_OR_POP --> JUMP_IF_FALSE_OR_POP to non-jump
# POP_JUMP_IF_FALSE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_FALSE to non-jump
def f(a, b, c):
return ((a and b)
and c)
self.check_jump_targets(f)
self.check_lnotab(f)
self.assertEqual(count_instr_recursively(f, 'JUMP_IF_FALSE_OR_POP'), 2)
# JUMP_IF_TRUE_OR_POP to JUMP_IF_TRUE_OR_POP --> JUMP_IF_TRUE_OR_POP to non-jump
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 2)
# POP_JUMP_IF_TRUE to POP_JUMP_IF_TRUE --> POP_JUMP_IF_TRUE to non-jump
def f(a, b, c):
return ((a or b)
or c)
self.check_jump_targets(f)
self.check_lnotab(f)
self.assertEqual(count_instr_recursively(f, 'JUMP_IF_TRUE_OR_POP'), 2)
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 2)
# JUMP_IF_FALSE_OR_POP to JUMP_IF_TRUE_OR_POP --> POP_JUMP_IF_FALSE to non-jump
def f(a, b, c):
return ((a and b)
or c)
self.check_jump_targets(f)
self.check_lnotab(f)
self.assertNotInBytecode(f, 'JUMP_IF_FALSE_OR_POP')
self.assertInBytecode(f, 'JUMP_IF_TRUE_OR_POP')
self.assertInBytecode(f, 'POP_JUMP_IF_FALSE')
# JUMP_IF_TRUE_OR_POP to JUMP_IF_FALSE_OR_POP --> POP_JUMP_IF_TRUE to non-jump
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1)
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1)
# POP_JUMP_IF_TRUE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE to non-jump
def f(a, b, c):
return ((a or b)
and c)
self.check_jump_targets(f)
self.check_lnotab(f)
self.assertNotInBytecode(f, 'JUMP_IF_TRUE_OR_POP')
self.assertInBytecode(f, 'JUMP_IF_FALSE_OR_POP')
self.assertInBytecode(f, 'POP_JUMP_IF_TRUE')
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1)
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1)

def test_elim_jump_to_uncond_jump4(self):
def f():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Removed :opcode:`JUMP_IF_FALSE_OR_POP` and :opcode:`JUMP_IF_TRUE_OR_POP`
instructions.
14 changes: 2 additions & 12 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,6 @@ mark_stacks(PyCodeObject *code_obj, int len)
}
opcode = code[i].op.code;
switch (opcode) {
case JUMP_IF_FALSE_OR_POP:
case JUMP_IF_TRUE_OR_POP:
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
{
Expand All @@ -318,16 +316,8 @@ mark_stacks(PyCodeObject *code_obj, int len)
if (stacks[j] == UNINITIALIZED && j < i) {
todo = 1;
}
if (opcode == JUMP_IF_FALSE_OR_POP ||
opcode == JUMP_IF_TRUE_OR_POP)
{
target_stack = next_stack;
next_stack = pop_value(next_stack);
}
else {
next_stack = pop_value(next_stack);
target_stack = next_stack;
}
next_stack = pop_value(next_stack);
target_stack = next_stack;
assert(stacks[j] == UNINITIALIZED || stacks[j] == target_stack);
stacks[j] = target_stack;
stacks[i+1] = next_stack;
Expand Down
50 changes: 0 additions & 50 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1918,56 +1918,6 @@ dummy_func(
}
}

inst(JUMP_IF_FALSE_OR_POP, (cond -- cond if (jump))) {
bool jump = false;
int err;
if (Py_IsTrue(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
}
else if (Py_IsFalse(cond)) {
JUMPBY(oparg);
jump = true;
}
else {
err = PyObject_IsTrue(cond);
if (err > 0) {
Py_DECREF(cond);
}
else if (err == 0) {
JUMPBY(oparg);
jump = true;
}
else {
goto error;
}
}
}

inst(JUMP_IF_TRUE_OR_POP, (cond -- cond if (jump))) {
bool jump = false;
int err;
if (Py_IsFalse(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
}
else if (Py_IsTrue(cond)) {
JUMPBY(oparg);
jump = true;
}
else {
err = PyObject_IsTrue(cond);
if (err > 0) {
JUMPBY(oparg);
jump = true;
}
else if (err == 0) {
Py_DECREF(cond);
}
else {
goto error;
}
}
}

inst(JUMP_BACKWARD_NO_INTERRUPT, (--)) {
/* This bytecode is used in the `yield from` or `await` loop.
* If there is an interrupt, we want it handled in the innermost
Expand Down

0 comments on commit 3468c76

Please sign in to comment.