Strange disassembly
Terry Reedy
tjreedy at udel.edu
Fri Jun 18 14:03:00 EDT 2021
On 6/18/2021 6:04 AM, Chris Angelico wrote:
>>>> sys.version
> '3.10.0b2+ (heads/3.10:33a7a24288, Jun 9 2021, 20:47:39) [GCC 8.3.0]'
>>>> def chk(x):
> ... if not(0 < x < 10): raise Exception
0 < x < 10 == 0 < x and x < 10, except that 'x' is evaluated once.
not(_) == (not 0 < x) or (not x < 10)
[== x <= 0 or 10 <= x]
>>>> dis.dis(chk)
> 2 0 LOAD_CONST 1 (0)
> 2 LOAD_FAST 0 (x)
stack = 0 x. Since compare will remove both, must duplicate x and move
duplicate out of the way.
> 4 DUP_TOP
> 6 ROT_THREE
stack = x 0 x
> 8 COMPARE_OP 0 (<)
test 0 < x, remove both, leaving stack = x
> 10 POP_JUMP_IF_FALSE 11 (to 22)
if false, not 0<x, so 'or' is true, so raise.
But must first remove unneeded duplicate!
> 12 LOAD_CONST 2 (10)
> 14 COMPARE_OP 0 (<)
Raise exception if false, making not x < 10 true
So if true, jump to normal exit at end. Stack is empty.
> 16 POP_JUMP_IF_TRUE 14 (to 28)
> 18 LOAD_GLOBAL 0 (Exception)
> 20 RAISE_VARARGS > >> 22 POP_TOP
Must first remove unneeded duplicate of x!
> 24 LOAD_GLOBAL 0 (Exception)
> 26 RAISE_VARARGS 1
> >> 28 LOAD_CONST 0 (None)
> 30 RETURN_VALUE
> Why are there two separate bytecode blocks for the "raise Exception"?
Because one block must POP_TOP and other must not.
> I'd have thought that the double condition would still be evaluated as
> one thing, or at least that the jump destinations for both the
> early-abort and the main evaluation should be the same.
To reuse the exception block with POP_TOP, could jump over it after the
2nd compare.
> 14 COMPARE_OP 0 (<)
> 16 POP_JUMP_IF_TRUE 14 (to 28)
18 JUMP (to 24)
20 NOP (#to avoid renumbering)
> >> 22 POP_TOP
> 24 LOAD_GLOBAL 0 (Exception)
> 26 RAISE_VARARGS 1
For the simplest and fasted bytecode, write normal logic and let x be
reloaded instead of duplicated and rotated.
>>> import dis
>>> def f(x):
... if x <= 0 or 10 <= x: raise Exception
...
...
>>> dis.dis(f)
2 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (0)
4 COMPARE_OP 1 (<=)
6 POP_JUMP_IF_TRUE 8 (to 16)
8 LOAD_CONST 2 (10)
10 LOAD_FAST 0 (x)
12 COMPARE_OP 1 (<=)
14 POP_JUMP_IF_FALSE 10 (to 20)
>> 16 LOAD_GLOBAL 0 (Exception)
18 RAISE_VARARGS 1
>> 20 LOAD_CONST 0 (None)
22 RETURN_VALUE
>>>
--
Terry Jan Reedy
More information about the Python-list
mailing list