Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where does local variables actually allocated within CLR?

I'm just going inside the CLR and IL and I'm confused by this thing.

I have the following C# code:

int x = 1;
object obj = x;
int y = (int)obj;

And IL disassemble for this

      // Code size       18 (0x12)
  .maxstack  1
  .locals init ([0] int32 x,
           [1] object obj,
           [2] int32 y)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  box        [mscorlib]System.Int32
  IL_0009:  stloc.1
  IL_000a:  ldloc.1
  IL_000b:  unbox.any  [mscorlib]System.Int32
  IL_0010:  stloc.2
  IL_0011:  ret

So, the ldloc.0 instruction "Loads the local variable at index 0 onto stack.". But where does the locals really stored and where does they loaded from. Because I thought that there are two places where the memory can be allocated: thread stack and heap. And variables should be stored in the stack.

Now, I suppose, that the stack is just an "evaluation stack", while the memory allocation for variables is an implementation detail and depend on platform and JIT compiler. And we actually can split the memory used by our programm on evaluation stack, managed heap, and locals allocated memory.

Is this true? Or there are some other mechanism here?

like image 672
Kirill Shpak Avatar asked Dec 10 '22 08:12

Kirill Shpak


1 Answers

You are severely conflating many things which are logically different:

  • Just because a variable is a local variable in C# does not mean that it is on the short term storage pool in IL. Local variables in C# can correspond to short term storage, long term storage, or evaluation stack in the corresponding IL.
  • Short term storage and evaluation stack in IL can correspond to stack or register storage in the jitted machine code.

When compiling C# to IL, the C# compiler makes locals members of a closure class -- they go on the long term storage pool -- when the lifetime of the local is possibly longer than the activation of the method. (Or when the activation of the method is broken up into little pieces, like it is in an async method.)

If the locals have short lifetimes then the compiler's optimizer chooses whether they go on the short term pool or the evaluation stack; the compiler calls the latter "ephemeral" locals. The algorithm for deciding when to make a local into an ephemeral is interesting; see the compiler source code for details.

The jitter then must decide whether to make short term pool variables and evaluation stack variables into stack locations or registers; it does so again using a complex optimization algorithm that varies depending on register availability and so on.

Finally, of course the C# compiler and the jitter are both free to reify an unread local as nothing at all; storage that is never read from need not actually be allocated.

like image 107
Eric Lippert Avatar answered Dec 30 '22 09:12

Eric Lippert