0

This question is sort of the opposite of this existing one: What is the role of operand stack in JVM?

That question asks why the JVM does not operate directly on the local variables. I'm wondering, since we have the stack anyway, why not operate only on the stack and forego the local variables entirely?

I guess it would need a few more instructions to manipulate the stack (much like advanced RPN calculators do), but how does one know whether that tradeoff would be worth it? Is this design rationale recorded somewhere?

The specification is light on rationale (https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-2.html#jvms-2.6.1), but I suspect that's because the specification is the wrong document for it, and maybe there's an annotated specification somewhere like with Ada?

kqr
  • 14,791
  • 3
  • 41
  • 72

2 Answers2

2

To evaluate a = b * c, for example, it's push b; push c; multiply; pop a.

After the multiply, b and c are not 'in' the stack. The values were only temporarily there.

I think the appropriate way to look at the operand stack is to consider it as a set of fast registers. Like general-register machines, arithmetic is done in the fast registers; the stack has the benefit that the 'register addresses' are implicit, saving some instruction bits.

If you want b and c to be 'always' in the stack, which means they're at arbitrary locations relative to the stack top, then you've just turned your stack into a non-stack machine since you either need 3-address instructions (like 'multiply a,b,c') or you need an accumulator (so 'load a; multiply b; store c'), where in each case a,b,c are the addresses of some locations in the stack - possibly encoded as offsets from the stack top.

What makes that a 'non-stack' architecture is that arithmetic is no longer being done on the topmost cells of the stack, implicitly identified, but instead is between arbitrary locations explicitly identified.

Arfur Narf
  • 392
  • 1
  • 5
2

Is this design rationale recorded somewhere?

I doubt that you will find a satisfying answer to this question.

When people began working on what is now known as Java around 30 years ago this wasn't a big commitee and some elaborate process . It was a small group of people working tightly together and explicitly formed to work autonomously without the bureaucracy that was stalling other Sun projects". The process was therefore probably more: someone proposed something to the group, there was some discussion and then it was decided without much writing down of rationales.

Source for the "small group": https://en.wikipedia.org/wiki/Java_(software_platform)#History


Some thoughts on the other points you've made:

I guess it would need a few more instructions to manipulate the stack (much like advanced RPN calculators do)

Without local variables you would have to replace iload, aload and some more variants with ipick, apick and similar variants that pick a value from some offset from the top of the operand stack and push it on top of the operand stack.

Similarly you would need to replace istore, astore and some more variants with ireplace, areplace and similar variants that pop a value from the operand stack and replace the value at some offset of the top of the operand stack with the popped value.

The main difference between the existing instructions and the proposed instructions would be that the existing instructions always use the same offset for a specific local variable while the proposed instructions would have to use different offsets for a specific local variable depending on how much values have been pushed to the operand stack.

but how does one know whether that tradeoff would be worth it?

IMHO not using local variables would make Java byte code harder to work with (for example when doing Verification of class Files) without reaping any benefits.

Note that at runtime there are no separate memory spaces for local variables and the operand stack. Conceptually the JVM uses one Frame per method invocation and this frame contains the local variables and the operand stack.

Thomas Kläger
  • 17,754
  • 3
  • 23
  • 34
  • This answer says some of the same things as the existing answer but, in my opinion, more clearly. It also expands on their content. My only remaining question would be "is it not possible to compile code down to pure stack operations without referencing other values deeper in the stack?" (similar to how correct sequencing of nested arithmetic expressions can be evaluated with very few levels of stack in an RPN calculator) but I suppose that's less of a JVM implementation question and more of a general CS question. – kqr Apr 27 '23 at 05:30
  • 1
    One advantage of fixed variable indices is that you can have optimized instructions for the most commonly used indices, e.g. `aload_0` to read the first variable, typically holding `this`. Besides that, I think such stack frames were common practice at least since the seventies… – Holger Apr 27 '23 at 06:46