[Python-Dev] PEP 203 Augmented Assignment

Thomas Wouters thomas@xs4all.net
Fri, 4 Aug 2000 15:01:35 +0200


[Don't be scared, I'm revisiting this thread for a purpose -- this isn't a
time jump ;-)]

On Thu, Jul 27, 2000 at 12:59:15AM -0500, Guido van Rossum wrote:

> I'm making up opcodes -- the different variants of LOAD and STORE
> don't matter.  On the right I'm displaying the stack contents after
> execution of the opcode (push appends to the end).  I'm writing
> 'result' to indicate the result of the += operator.

>   a[i] += b
> 
>       LOAD a			[a]
>       DUP			[a, a]
>       LOAD i			[a, a, i]
>       DUP			[a, a, i, i]
>       ROT3			[a, i, a, i]
>       GETITEM			[a, i, a[i]]
>       LOAD b			[a, i, a[i], b]
>       AUGADD			[a, i, result]
>       SETITEM			[]
> 
> I'm leaving the slice variant out; I'll get to that in a minute.

[ And later you gave an example of slicing using slice objects, rather than
the *SLICE+x opcodes ]

I have two tiny problems with making augmented assignment use the current
LOAD/STORE opcodes in the way Guido pointed out, above. One has to do with
the order of the arguments, and the other with ROT_FOUR. And they're closely
related, too :P

The question is in what order the expression

x += y

is evaluated. 

x = y

evaluates 'y' first, then 'x', but 

x + y

evaluates 'x' first, and then 'y'. 

x = x + y

Would thus evaluate 'x', then 'y', and then 'x' (for storing the result.)
(The problem isn't with single-variable expressions like these examples, of
course, but with expressions with sideeffects.)

I think it makes sense to make '+=' like '+', in that it evaluates the lhs
first. However, '+=' is as much '=' as it is '+', so it also makes sense to
evaluate the rhs first. There are plenty of arguments both ways, and both
sides of my brain have been beating eachother with spiked clubs for the
better part of a day now ;) On the other hand, how important is this issue ?
Does Python say anything about the order of argument evaluation ? Does it
need to ?

After making up your mind about the above issue, there's another problem,
and that's the generated bytecode.

If '+=' should be as '=' and evaluate the rhs first, here's what the
bytecode would have to look like for the most complicated case (two-argument
slicing.)

a[b:c] += i

LOAD i			[i]
LOAD a			[i, a]
DUP_TOP			[i, a, a]
LOAD b			[i, a, a, b]
DUP_TOP			[i, a, a, b, b]
ROT_THREE		[i, a, b, a, b]
LOAD c			[i, a, b, a, b, c]
DUP_TOP			[i, a, b, a, b, c, c]
ROT_FOUR		[i, a, b, c, a, b, c]
SLICE+3			[i, a, b, c, a[b:c]]
ROT_FIVE		[a[b:c], i, a, b, c]
ROT_FIVE		[c, a[b:c], i, a, b]
ROT_FIVE		[b, c, a[b:c], i, a]
ROT_FIVE		[a, b, c, a[b:c], i]
INPLACE_ADD		[a, b, c, result]
STORE_SLICE+3		[]

So, *two* new bytecodes, 'ROT_FOUR' and 'ROT_FIVE', just to get the right
operands in the right place.

On the other hand, if the *left* hand side is evaluated first, it would look
like this:

a[b:c] += i

LOAD a			[a]
DUP_TOP			[a, a]
LOAD b			[a, a, b]
DUP_TOP			[a, a, b, b]
ROT_THREE		[a, b, a, b]
LOAD c			[a, b, a, b, c]
DUP_TOP			[a, b, a, b, c, c]
ROT_FOUR		[a, b, c, a, b, c]
SLICE+3			[a, b, c, a[b:c]]
LOAD i			[a, b, c, a[b:c], i]
INPLACE_ADD		[a, b, c, result]
STORE_SLICE+3		[]

A lot shorter, and it only needs ROT_FOUR, not ROT_FIVE. An alternative
solution is to drop ROT_FOUR too, and instead use a DUP_TOPX argument-opcode
that duplicates the top 'x' items:

LOAD a			[a]
LOAD b			[a, b]
LOAD c			[a, b, c]
DUP_TOPX 3		[a, b, c, a, b, c]
SLICE+3			[a, b, c, a[b:c]]
LOAD i			[a, b, c, a[b:c], i]
INPLACE_ADD		[a, b, c, result]
STORE_SLICE+3		[]

I think 'DUP_TOPX' makes more sense than ROT_FOUR, as DUP_TOPX could be used
in the bytecode for 'a[b] += i' and 'a.b += i' as well. (Guido's example
would become something like this:

a[b] += i

LOAD a			[a]
LOAD b			[a, b]
DUP_TOPX 2		[a, b, a, b]
BINARY_SUBSCR		[a, b, a[b]]
LOAD i			[a, b, a[b], i]
INPLACE_ADD		[a, b, result]
STORE_SUBSCR		[]

So, *bytecode* wise, evaluating the lhs of '+=' first is easiest. It
requires a lot more hacking of compile.c, but I think I can manage that.
However, one part of me is still yelling that '+=' should evaluate its
arguments like '=', not '+'. Which part should I lobotomize ? :)

-- 
Thomas Wouters <thomas@xs4all.net>

Hi! I'm a .signature virus! copy me into your .signature file to help me spread!