Does execution stack and thread's stack and local variable list refer to different stacks?

StackOverflow https://stackoverflow.com/questions/16592299

  •  29-05-2022
  •  | 
  •  

Question

A value type may be stored in a thread's stack, and IL runs in the execution stack (abstract concept).

int y=0;
int a=0;
int b=0;

int x = y + (a - b);

IL_0001:  ldc.i4.0    
IL_0002:  stloc.0     // y
IL_0003:  ldc.i4.0    
IL_0004:  stloc.1     // a
IL_0005:  ldc.i4.0    
IL_0006:  stloc.2     // b
IL_0007:  ldloc.0     // y
IL_0008:  ldloc.1     // a
IL_0009:  ldloc.2     // b
IL_000A:  sub         
IL_000B:  add         
IL_000C:  stloc.3     // x

Now the stloc.0 pops the value from the execution stack and stored into a local variable list. So, the local variable list must be stored in a different space other than the execution stack. What is the local variable list? Is it a thread's stack?

Also, which stack does the .maxstack = 3 refer to for a method? Is it the size of the local variable list? or the max size of additional storage pushed into the execution stack?

It seems to me that the execution stack is the proper stack, as it only supports push and pop. The local variable list supports load from an index and store to an index. How would that be related to the stack?

Update:

ECMA 335 I.12.3 Machine state clearly answered my question.

Was it helpful?

Solution

Lasse's answer is quite good; I just want to emphasize a few of his points.

First, C# is translated into an equivalent IL program, and then the IL program is translated into an equivalent machine code program. The IL language has the concept of an evaluation stack; this is purely a concept in the IL language. All that is required is that the jitter translate that IL into machine code that has the same final result. There is no requirement whatsoever that the jitter actually use the "real" stack just because the IL program was written to use an "evaluation stack".

Basically, IL is a language which assumes an extremely simplified kind of processor; there are no "registers" in IL, only stack. In a real processor of course there are registers, and so of course the machine-code translation of the IL will not slavishly follow its usage of an evaluation stack; that would be silly.

The "max stack" refers to the "abstract" stack of the IL language; it has no particular connection to the actual thread's stack in the machine code.

I've written a number of articles about the meaning of "the stack" and why we use IL; you might want to check them out if this subject interests you:

OTHER TIPS

Local variables are typically stored on the stack. What happens is that at the start of the method, a number of bytes are reserved on the stack, and the local variables lives inside those bytes. Before the method returns, the stack space reserved is "unreserved", and thus the variables are removed from the stack.

The "local variable list" are those variables that live inside that reserved space.

The benefit of this is that a method can call itself without worrying about tripping over the variables in the previous method call, which presumably is still needed when the inner method call returns.

However, there is something else at play here as well. You're referring to IL code, which is "machine code" written for a virtual computer. Not the kind of virtual computer running under VMware or similar, but a virtual computer that does not really exist. There is no CPU available that will run the IL code, like the one you posted in the question, unmodified.

Instead, what happens is that before a method is executed the first time, the JITter steps in and translates the IL code into real binary machine code that will run on the actual CPU, typically into x86 compatible instructions.

The final binary code might not actually use a stack to hold local variables, it might use CPU registers. The virtual machine that the IL is targetting uses the stack as its registers, the x86 compatible cpus have multiple registers available.

As such, the code you posted might be translated into binary code that uses those registers instead of the stack, since those registers are faster than accessing memory.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top