2

The ECMA CLI spec has the following statement in the description for the initobj CLI instruction:

"If typeTok is a value type, then after this instruction is executed, the instance is ready for a constructor method to be called."

However, the following C# code (where S is a struct):

S s = default;
S s2 = new S();
S s3 = new S(5);

compiles to IL that looks something like this:

IL_0001: ldloca.s     s
IL_0003: initobj      S

IL_0009: ldloca.s     s2
IL_000b: initobj      S

IL_0011: ldloca.s     s3
IL_0013: ldc.i4.5
IL_0014: call         instance void S::.ctor(int32)

My question is, when would a compiler ever use initobj followed by calling the value type's constructor?

Wizard Brony
  • 111
  • 5
  • Does the same thing happen when using other locations besides local variables? E.g. static fields, fields in instances of objects, etc... I ask because local variables are already all-zeroed when the method begins, at least in the code C# produces. – Joe Sewell Aug 12 '20 at 13:34
  • @JoeSewell Yes, it appears the compiler handles fields similarly: `ld[s]flda`, `initobj` – Wizard Brony Aug 12 '20 at 14:35
  • Chapter II.13.2 is important, note that how it states that zero-initialization for value types is not required. So the C# compiler must emit initobj to get the guarantee. A constructor is not required to initialize all fields, C++/CLI is an example. C# requires it, so it can omit initobj. – Hans Passant Aug 16 '20 at 02:31
  • @HansPassant That makes sense. However, C# uses the `localsinit` flag, so in the context of my example, isn't the use of `initobj` redundant? – Wizard Brony Aug 16 '20 at 22:24
  • IL generation by the C# compiler is not micro-optimized at all. It is the job of the jitter to turn it into efficient machine code. It is especially good at making value types optimal, the only reason that type system wart got added to .NET, They're a good match for the way a processor works. – Hans Passant Aug 16 '20 at 22:31
  • @HansPassant You mentioned "A constructor is not required to initialize all fields, C++/CLI is an example. C# requires it, so it can omit initobj." That's fair, but what if S was defined in a different assembly and written in a language that didn't require it? Would the C# compiler then all of a sudden include `initobj`? If so, it would seem weird to me that the compiler would change the way it initializes an object based on the location of the assembly in which the type is defined. – Wizard Brony Aug 18 '20 at 01:03
  • @HansPassant After deeper thought I realized my above comment is kind of silly: If another language chooses to not require field initialization, that's its decision. If C# forced an `initobj` before a constructor call, it would basically be saying that no other language that it calls into would have the option to leave fields uninitialized in their constructor. But a language could choose to force initialization if it wanted that guarantee. C# doesn't, and therefore leaves the decision up to the callee. – Wizard Brony Aug 18 '20 at 16:49

1 Answers1

1

If typeTok is a value type, then after this instruction is executed, the instance is ready for a constructor method to be called.

It means that a constructor can call a constructor after this instruction.

But, as you mentioned in the comments above there is no need to initialize the locals again before the constructor because of the localsinit flag on a method sig.

My question is, when would a compiler ever use initobj followed by calling the value type's constructor?

I can only find one case where I will require the compiler to use initobj, tell it not to use the localsinit flag. As of this moment, SkipLocalsInitAttribute should help you reproduce the case, but the implementation of this compiler feature has not started.

Svirin
  • 564
  • 1
  • 7
  • 20