When designing an ISA , is it a good design decision to permit a `pop` instruction that does not require an operand to receive the popped value?

softwareengineering.stackexchange https://softwareengineering.stackexchange.com/questions/349092

  •  12-01-2021
  •  | 
  •  

Question

A lot of ISAs including x86, x64, ARM, Itanium, have a pop instruction that requires an operand which is usually a register (or, in ARM's case, a register list). In fact, I can't think of an ISA off the top of my head where the pop instruction doesn't require an operand (other than stack-machines for bytecode like the JVM and CIL).

I've noticed that when doing assembly programming, there are some times I will want to get rid of the value at the top of the stack without needing to preserve it anywhere. Normally this can be done by directly modifying the stack or frame pointer, but, from the perspective of actually writing assembly code, it seems more economical to simply have a pop opcode that accepts no register and simply gets rid of whatever was at the top of the stack.

When designing an ISA for a register machine, is it a good design decision to permit a pop instruction that does not require an operand to receive the popped value? I can't really see any downfall to this.

Was it helpful?

Solution

This will depend greatly on the rest of your ISA, but in general adding stuff isn't free.

Adding an operand-less pop instruction reduces the available opcode space for other instructions.

Sure, you can create some baroque, x86-inspired scheme to support infinite instructions, but then you have to actually implement that, and pay the additional associated costs.

Also, is it limited to only word-sized pops, or can it do byte-pops, half-word-pops, and double-word-pops as well? With a register operand, the size of the register could indicate how many bits to pop. Without any operands, you'd have to use multiple opcodes to support different sized pops. Or support just a default size and force direct stack pointer manipulation for anything else.

It's another instruction that needs to be implemented.

That means more time and money spent designing and testing the implementation. In hardware implementations, it probably means more circuitry, resulting in a larger, more expensive, hotter, slower product. Software implementations require more code, resulting in more space for bugs, and possibly requiring a more powerful (i.e. more expensive) target platform.

There's really not much benefit.

As you've already pointed out, it's redundant. sub $SP, 4 or similar basically already does the same thing. Yeah, maybe it's slightly more convenient than manual stack pointer manipulation in the rare occasion that it's applicable, but we've known for decades that assembly language is not what you use when you want to code with high productivity. If you're worried about productivity, port a C compiler to your ISA, or better yet, something like D or Rust. Maybe even get some high level non-systems languages running on it. Leave the assembly to the rare times it's necessary, or for educational purposes.

If it comes for free, then maybe leave it there, rather than spending extra work to remove it.

MIPS gets something similar for free. Or would, if it had a pop instruction. MIPS's $0 register is hardcoded to 0 and discards writes. A hypothetical pop instruction for MIPS would still take an operand, but, if you specify register $0, then you'd get your stack pointer adjustment without saving the popped value. This way you wouldn't need an extra pop opcode for a particular edge case, and it could just reuse the same dataflow infrastructure used by every other instruction that writes to a register.

Dedicating a register to zero does have it's own costs, notably that it reduces the number of other, more versatile registers that can be made available, and that it's a special case register. The former is only significant if your ISA is already limited on available registers. The latter is offset by the fact that it's an exceedingly simple special case. Depending on the implementation, it may in fact be less expensive in some ways than a general purpose register (no need for memory logic, no need for any input connections...).

OTHER TIPS

I personally wouldn't put such an instruction in my ISA.

Here's why: ISAs should be economical; that is, they should provide their functionality using as few instructions and as few permutations of those instructions as possible. There are already many factors that work against this economy (backwards compatibility, for one), so you shouldn't throw away such economy frivolously.

If you create a Pop instruction that makes the operand optional, you've now created a bifurcation: two instructions instead of one, both for the assembler and for the chip that will run the instructions. And you've done this arguably for an edge case, in such a way that Pop doesn't really mean "pop" anymore.

Once you go down this road, you'll be asking yourself the question "Should other instructions also have optional operands for consistency? What will omitting the operand mean for those instructions?" Why would you do it for the Pop instruction, but not for other instructions?

Save your edge cases for ordinary instructions; require the user to specify a register and simply disregard the return value. Make the Pop instruction work the same way every time it is used.

Licensed under: CC-BY-SA with attribution
scroll top