JUMP_ABSOLUTE in nested if statements
Hi, Could some one give a hand with explaining to me why we have a JUMP_ABSOLUTE followed by a JUMP_FORWARD op code when this function is disassembled.
def f1(): ... a, b = 10, 11 ... if a >= 10: ... if b >= 11: ... print("hello world") …
The disassembled function is shown below.
dis(f1) 2 0 LOAD_CONST 4 ((10, 11)) 3 UNPACK_SEQUENCE 2 6 STORE_FAST 0 (a) 9 STORE_FAST 1 (b)
3 12 LOAD_FAST 0 (a) 15 LOAD_CONST 1 (10) 18 COMPARE_OP 5 (>=) 21 POP_JUMP_IF_FALSE 47 4 24 LOAD_FAST 1 (b) 27 LOAD_CONST 2 (11) 30 COMPARE_OP 5 (>=) 33 POP_JUMP_IF_FALSE 47 5 36 LOAD_CONST 3 ('hello world') 39 PRINT_ITEM 40 PRINT_NEWLINE 41 JUMP_ABSOLUTE 47 44 JUMP_FORWARD 0 (to 47) >> 47 LOAD_CONST 0 (None) 50 RETURN_VALUE From my understanding, once JUMP_ABSOLUTE is executed, then JUMP_FORWARD is never gotten to so must be dead code so why is it being generated? Furthermore why is JUMP_ABSOLUTE rather than JUMP_FORWARD used in this particular case of nested if statements? I have tried other types of nested if statements and it has always been JUMP_FORWARD that is generated.
On Jun 18, 2016, at 2:04 PM, Obiesie ike-nwosu via Python-Dev <python-dev@python.org> wrote:
Hi,
Could some one give a hand with explaining to me why we have a JUMP_ABSOLUTE followed by a JUMP_FORWARD op code when this function is disassembled. < snipped> From my understanding, once JUMP_ABSOLUTE is executed, then JUMP_FORWARD is never gotten to so must be dead code so why is it being generated? Furthermore why is JUMP_ABSOLUTE rather than JUMP_FORWARD used in this particular case of nested if statements? I have tried other types of nested if statements and it has always been JUMP_FORWARD that is generated.
The AST compilation step generates code with two JUMP_FORWARDs (see below). Then, the peephole optimizer recognizes a jump-to-an-unconditional-jump and replaces the first one with a JUMP_ABSOLUTE to save an unnecessary step. The reason that it uses JUMP_ABSOLUTE instead of JUMP_FORWARD is that the former is more general (it can jump backwards). Using the more general form reduces the complexity of the optimizer. The reason that the remaining jump-to-jump isn't optimized is that the peepholer is intentionally kept simplistic, making only a single pass over the opcodes. That misses some optimizations but gets the most common cases. FWIW, the jump opcodes are very fast, so missing the final jump-to-jump isn't much of a loss. If you're curious, the relevant code is in Python/compile.c and Python/peephole.c. The compile.c code generated opcodes in the most straight-forward way possible and then the peephole optimizer gets some of the low-hanging fruit by making a few simple transformations. Raymond ------------ AST generated code before peephole optimization ----------------- 5 0 LOAD_CONST 1 (10) 3 LOAD_CONST 2 (11) 6 BUILD_TUPLE 2 9 UNPACK_SEQUENCE 2 12 STORE_FAST 0 (a) 15 STORE_FAST 1 (b) 6 18 LOAD_FAST 0 (a) 21 LOAD_CONST 1 (10) 24 COMPARE_OP 5 (>=) 27 POP_JUMP_IF_FALSE 53 7 30 LOAD_FAST 1 (b) 33 LOAD_CONST 2 (11) 36 COMPARE_OP 5 (>=) 39 POP_JUMP_IF_FALSE 50 8 42 LOAD_CONST 3 ('hello world') 45 PRINT_ITEM 46 PRINT_NEWLINE 47 JUMP_FORWARD 0 (to 50) >> 50 JUMP_FORWARD 0 (to 53) >> 53 LOAD_CONST 0 (None) 56 RETURN_VALUE
That is much clearer now. Thanks a lot Raymond for taking the time out to explain this to me. On a closing note, is this mailing list the right place to ask these kinds of n00b questions? Obi.
On 18 Jun 2016, at 23:10, Raymond Hettinger <raymond.hettinger@gmail.com> wrote:
On Jun 18, 2016, at 2:04 PM, Obiesie ike-nwosu via Python-Dev <python-dev@python.org> wrote:
Hi,
Could some one give a hand with explaining to me why we have a JUMP_ABSOLUTE followed by a JUMP_FORWARD op code when this function is disassembled. < snipped> From my understanding, once JUMP_ABSOLUTE is executed, then JUMP_FORWARD is never gotten to so must be dead code so why is it being generated? Furthermore why is JUMP_ABSOLUTE rather than JUMP_FORWARD used in this particular case of nested if statements? I have tried other types of nested if statements and it has always been JUMP_FORWARD that is generated.
The AST compilation step generates code with two JUMP_FORWARDs (see below). Then, the peephole optimizer recognizes a jump-to-an-unconditional-jump and replaces the first one with a JUMP_ABSOLUTE to save an unnecessary step.
The reason that it uses JUMP_ABSOLUTE instead of JUMP_FORWARD is that the former is more general (it can jump backwards). Using the more general form reduces the complexity of the optimizer.
The reason that the remaining jump-to-jump isn't optimized is that the peepholer is intentionally kept simplistic, making only a single pass over the opcodes. That misses some optimizations but gets the most common cases.
FWIW, the jump opcodes are very fast, so missing the final jump-to-jump isn't much of a loss.
If you're curious, the relevant code is in Python/compile.c and Python/peephole.c. The compile.c code generated opcodes in the most straight-forward way possible and then the peephole optimizer gets some of the low-hanging fruit by making a few simple transformations.
Raymond
------------ AST generated code before peephole optimization -----------------
5 0 LOAD_CONST 1 (10) 3 LOAD_CONST 2 (11) 6 BUILD_TUPLE 2 9 UNPACK_SEQUENCE 2 12 STORE_FAST 0 (a) 15 STORE_FAST 1 (b)
6 18 LOAD_FAST 0 (a) 21 LOAD_CONST 1 (10) 24 COMPARE_OP 5 (>=) 27 POP_JUMP_IF_FALSE 53
7 30 LOAD_FAST 1 (b) 33 LOAD_CONST 2 (11) 36 COMPARE_OP 5 (>=) 39 POP_JUMP_IF_FALSE 50
8 42 LOAD_CONST 3 ('hello world') 45 PRINT_ITEM 46 PRINT_NEWLINE 47 JUMP_FORWARD 0 (to 50)
50 JUMP_FORWARD 0 (to 53) 53 LOAD_CONST 0 (None) 56 RETURN_VALUE
On Sat, Jun 18, 2016 at 11:32:52PM +0100, Obiesie ike-nwosu via Python-Dev wrote:
That is much clearer now. Thanks a lot Raymond for taking the time out to explain this to me. On a closing note, is this mailing list the right place to ask these kinds of n00b questions?
That depends what sort of n00b question. If they are specifically related to the internals of the CPython interpreter, then this is certainly the right place. Code generation will count as an internal function of the interpreter. If they're general questions about Python the language, then the python-list mailing list is better. (Also available as comp.lang.python on Usenet.) Beware: it tends to be a high-volume, easily distracted forum where people often go off on long discussions which are only peripherally related to Python. -- Steve
Python has a peephole optimizer which does not remove dead code that it just created. Victor Le 18 juin 2016 23:14, "Obiesie ike-nwosu via Python-Dev" < python-dev@python.org> a écrit :
Hi,
Could some one give a hand with explaining to me why we have a JUMP_ABSOLUTE followed by a JUMP_FORWARD op code when this function is disassembled.
def f1(): ... a, b = 10, 11 ... if a >= 10: ... if b >= 11: ... print("hello world") …
The disassembled function is shown below.
dis(f1) 2 0 LOAD_CONST 4 ((10, 11)) 3 UNPACK_SEQUENCE 2 6 STORE_FAST 0 (a) 9 STORE_FAST 1 (b)
3 12 LOAD_FAST 0 (a) 15 LOAD_CONST 1 (10) 18 COMPARE_OP 5 (>=) 21 POP_JUMP_IF_FALSE 47
4 24 LOAD_FAST 1 (b) 27 LOAD_CONST 2 (11) 30 COMPARE_OP 5 (>=) 33 POP_JUMP_IF_FALSE 47
5 36 LOAD_CONST 3 ('hello world') 39 PRINT_ITEM 40 PRINT_NEWLINE 41 JUMP_ABSOLUTE 47 44 JUMP_FORWARD 0 (to 47) >> 47 LOAD_CONST 0 (None) 50 RETURN_VALUE
From my understanding, once JUMP_ABSOLUTE is executed, then JUMP_FORWARD is never gotten to so must be dead code so why is it being generated? Furthermore why is JUMP_ABSOLUTE rather than JUMP_FORWARD used in this particular case of nested if statements? I have tried other types of nested if statements and it has always been JUMP_FORWARD that is generated. _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/victor.stinner%40gmail.co...
participants (4)
-
Obiesie ike-nwosu -
Raymond Hettinger -
Steven D'Aprano -
Victor Stinner