News for hackers. Stuff that matters.
Re:How Python bytecode is executed by DonaldFisk on Monday November 09, 2020 @04:09PM (#25039929)
> The UNARY_NEGATIVE opcode pops value from the stack, negates it and pushes the result.

Why not have a top of stack register? Then all you need to put in your case statement (naively) is

    tos = -tos;
thereby avoiding a pop and a push. In a virtual machine for a dynamically typed language such as Python, you'll need to handle different types, and for a statically typed language you'll need separate instructions for int and float, but in any case you avoid the pop and push. If the instruction takes more than one item from the stack, you at least have one fewer pop.

Incidentally, Burroughs mainframes were hardware stack machines and had two top of stack registers, A and B.

Re:How Python bytecode is executed by teraflop on Monday November 09, 2020 @04:38PM (#25040271)
In fact, that's exactly how Python implements UNARY_NEGATIVE: https://github.com/python/cpython/blob/v3.9.0/Python/ceval.c...

If you look a bit further down in the original article, you'll see that the BINARY_ADD instruction does something similar. It pops (a pointer to) the first operand, and modifies (a pointer to) the second one in-place.

Semantically, it makes sense to define operations as popping the operand(s) and pushing a result, for simplicity. But there's no reason the interpreter has to actually be implemented that way, as long as the observable behavior is the same.

In any case, I wouldn't be surprised if an extra push/pop ended up having very little performance impact. The compiler might be able to optimize away the pointer increment/decrement instructions, and if not, the stack pointer is pretty much guaranteed to be in the L1 cache.

Re:How Python bytecode is executed by r4victor on Tuesday November 10, 2020 @01:41AM (#25043353)
As teraflop pointed out, CPython doesn't actually do the pop and the push in this case. Sorry for being misleading. I've added an update note to clarify this.