The JIT sometimes suppresses explicit zeroing for locals because it expects them to be zeroed by prolog:
|
bool bbInALoop = impBlockIsInALoop(block); |
|
bool bbIsReturn = block->KindIs(BBJ_RETURN) && |
|
(!compIsForInlining() || (impInlineInfo->iciBlock->KindIs(BBJ_RETURN))); |
|
LclVarDsc* const lclDsc = lvaGetDesc(lclNum); |
|
if (fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn)) |
|
{ |
|
// Append a tree to zero-out the temp |
|
GenTree* newObjInit = |
|
gtNewZeroConNode(lclDsc->TypeIs(TYP_STRUCT) ? TYP_INT : lclDsc->TypeGet()); |
|
|
|
impStoreToTemp(lclNum, newObjInit, CHECK_SPILL_NONE); |
|
} |
|
else |
|
{ |
|
JITDUMP("\nSuppressing zero-init for V%02u -- expect to zero in prolog\n", lclNum); |
|
lclDsc->lvSuppressedZeroInit = 1; |
|
compSuppressedZeroInit = true; |
|
} |
This can interact poorly with async. If we suppress the zeroing and the object was created after a suspension point, then wecan end up hoisting a default-valued local to the heap if we see what looks like a use (e.g. LCL_ADDR passed to a constructor).
We may consider turning off the suppressions in some cases, or perhaps we should have a fully-fledged "default value" analysis that the async transformation can use to determine if the something is default-valued at a suspension point.
A small repro looks like:
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
await FooAsync();
}
private static async Task FooAsync()
{
await Task.Yield();
Inline();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Inline()
{
S s = new S();
Console.WriteLine(s.A);
}
private struct S
{
public long A, B, C, D, E;
public object F;
[MethodImpl(MethodImplOptions.NoInlining)]
public S()
{
A = B = C = D = E = 10;
F = null;
}
}
}
The JIT saves and restores the S that came from Inline at the suspension point in FooAsync.
The JIT sometimes suppresses explicit zeroing for locals because it expects them to be zeroed by prolog:
runtime/src/coreclr/jit/importer.cpp
Lines 8890 to 8907 in e399e13
This can interact poorly with async. If we suppress the zeroing and the object was created after a suspension point, then wecan end up hoisting a default-valued local to the heap if we see what looks like a use (e.g.
LCL_ADDRpassed to a constructor).We may consider turning off the suppressions in some cases, or perhaps we should have a fully-fledged "default value" analysis that the async transformation can use to determine if the something is default-valued at a suspension point.
A small repro looks like:
The JIT saves and restores the
Sthat came fromInlineat the suspension point inFooAsync.