[Python-Dev] [PEP] += on return of function call result

Luke Kenneth Casson Leighton lkcl@samba-tng.org
Mon, 19 May 2003 09:08:11 +0000


hiya jeff,

on radio 4 today there was a discussion about art - what makes
people go "wow" instead of being shocked.  seeing the byte code
in front of my eyes isn't so much of a shock, more of a "wow"
because i have at some point in my past actually _looked_ at the
python sources stack machine, for investigating parallelising it
(!!!!!)


okay.

how do i run the examples you list?  dis.dis(f) gives an
"unrecognised variablename dis".

okay.  let's give this a shot.


Script started on Mon May 19 08:44:19 2003
lkcl@highfield:~$ python O
Python 2.2.2 (#1, Jan 18 2003, 10:18:59) 
[GCC 3.2.2 20030109 (Debian prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> def g(): return f(x)
... 
>>> dis.dis(g)
          0 LOAD_GLOBAL              0 (f)
          3 LOAD_GLOBAL              1 (x)
          6 CALL_FUNCTION            1
          9 RETURN_VALUE        
         10 LOAD_CONST               0 (None)
         13 RETURN_VALUE        
>>> def g(): f(x)
... 
>>> dis.dis(g)
          0 LOAD_GLOBAL              0 (f)
          3 LOAD_GLOBAL              1 (x)
          6 CALL_FUNCTION            1
          9 POP_TOP             
         10 LOAD_CONST               0 (None)
         13 RETURN_VALUE        
>>> 
lkcl@highfield:~$ exit

Script done on Mon May 19 08:44:56 2003


right.

the difference between these two is the POP_TOP.

so, the return result is placed on the stack, from the call to
f(x).

so... if there's instead an f(x) += 1 instead of f(x), then
the result is going to be pushed onto the top of the stack,
followed by the += 1, followed at the end by a POP_TOP.

if the result is used (e.g. assigned to a variable),

x = f(x) += 1

then you don't do the POP_TOP.


... am i missing something?


what am i missing?

that it's not known what type of variable is returned, therefore
you're not certain as to what type of STORE to use?


Script started on Mon May 19 08:51:22 2003
lkcl@highfield:~$ python -O
Python 2.2.2 (#1, Jan 18 2003, 10:18:59) 
[GCC 3.2.2 20030109 (Debian prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(): return 5
... 
>>>     def g(): 
...     x = f() + 1
...     return x
... 
>>> import dis
>>> dis.dis(g)
          0 LOAD_GLOBAL              0 (f)
          3 CALL_FUNCTION            0
          6 LOAD_CONST               1 (1)
          9 BINARY_ADD          
         10 STORE_FAST               0 (x)
         13 LOAD_FAST                0 (x)
         16 RETURN_VALUE        
         17 LOAD_CONST               0 (None)
         20 RETURN_VALUE        
>>> 
lkcl@highfield:~$ 
Script done on Mon May 19 08:52:40 2003


okay...  soo.... you get an assignment into a variable...
...

okay, i think i see what the problem is.

because the return result _may_ not be used, you don't know
what type of STORE to use?


or, because there are optimisations added, it's not always
possible to "pass down" the right kind of STORE_xxx to
the previous stack level?



i believe you may be thinking that this is more complex than
it is.  that's very patronising of me.  scratch that.

i believe this should not be complex :)


"+=" itself is a function call with two arguments and a return
result, where the return result is the first argument.

it just _happens_ that that function call has been drastically
optimised - with its RETURN_VALUE removed; STORE_xxx removed.


more thought needed.  i'll go look at some code.

l.

p.s. 10 and 13 in the 8:52:40am typescript above look like they
could be optimised / removed.

p.p.s. yes i _have_ written a stack-machine optimiser before.




On Sat, May 17, 2003 at 10:21:39AM -0500, Jeff Epler wrote:
> I think that looking at the generated bytecode is useful.
> 
> # Running with 'python -O'
> >>> def f(x): x += 1
> >>> dis.dis(f)
>           0 LOAD_FAST                0 (x)
>           3 LOAD_CONST               1 (1)
>           6 INPLACE_ADD         
>           7 STORE_FAST               0 (x)    ***
>          10 LOAD_CONST               0 (None)
>          13 RETURN_VALUE        
> >>> def g(x): x[0] += 1
> >>> dis.dis(g)
>           0 LOAD_GLOBAL              0 (x)
>           3 LOAD_CONST               1 (0)
>           6 DUP_TOPX                 2
>           9 BINARY_SUBSCR       
>          10 LOAD_CONST               2 (1)
>          13 INPLACE_ADD         
>          14 ROT_THREE           
>          15 STORE_SUBSCR                      ***
>          16 LOAD_CONST               0 (None)
>          19 RETURN_VALUE        
> >>> def h(x): x.a += 1
> >>> dis.dis(h)
>           0 LOAD_GLOBAL              0 (x)
>           3 DUP_TOP             
>           4 LOAD_ATTR                1 (a)
>           7 LOAD_CONST               1 (1)
>          10 INPLACE_ADD         
>          11 ROT_TWO             
>          12 STORE_ATTR               1 (a)    ***
>          15 LOAD_CONST               0 (None)
>          18 RETURN_VALUE        
> 
> In each case, there's a STORE step to the inplace statement.  In the case of the proposed
> 	def j(x): x() += 1
> what STORE instruction would you use?
> 
> >>> [opname for opname in dis.opname if opname.startswith("STORE")]
> ['STORE_SLICE+0', 'STORE_SLICE+1', 'STORE_SLICE+2', 'STORE_SLICE+3',
>  'STORE_SUBSCR', 'STORE_NAME', 'STORE_ATTR', 'STORE_GLOBAL', 'STORE_FAST',
>  'STORE_DEREF']
> 
> If you don't want one from the list, then you're looking at substantial
> changes to Python.. (and STORE_DEREF probably doesn't do anything that's
> relevant to this situation, though the name sure sounds promising,
> doesn't it)
> 
> Jeff

-- 
-- 
expecting email to be received and understood is a bit like
picking up the telephone and immediately dialing without
checking for a dial-tone; speaking immediately without listening
for either an answer or ring-tone; hanging up immediately and
then expecting someone to call you (and to be able to call you).
--
every day, people send out email expecting it to be received
without being tampered with, read by other people, delayed or
simply - without prejudice but lots of incompetence - destroyed.
--
please therefore treat email more like you would a CB radio
to communicate across the world (via relaying stations):
ask and expect people to confirm receipt; send nothing that
you don't mind everyone in the world knowing about...