Skip to content

JIT: Investigate interaction between suppressions of explicit inits and async hoisting #124512

@jakobbotsch

Description

@jakobbotsch

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.

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions