Skip to content

JIT: Bad codegen on x86 for a function in collectible ALC #73951

Closed

Description

@jakobbotsch
// Generated by Fuzzlyn v1.5 on 2022-08-15 13:32:00
// Run on X86 Windows
// Seed: 9809551563097583633
// Reduced from 536.2 KiB to 1.8 KiB in 00:20:04
// Debug: Outputs 0
// Release: Outputs 61952
public struct S0
{
    public uint F1;
    public int F2;
    public byte F3;
}

public class C0
{
    public ushort F1;
    public C0(ushort f1)
    {
        F1 = f1;
    }
}

public class C1
{
    public C1(uint f0, uint f1)
    {
    }
}

public class C2
{
    public C2(C1 f0, uint f2, ulong f3, bool f5, int f6)
    {
    }
}

public struct S1
{
    public C2 F0;
    public S0 F1;
    public S1(C2 f0): this()
    {
        F0 = f0;
    }

    public S1 M105(S0 arg0)
    {
        Program.s_10.F0 = new C2(new C1(521025157U, 1254134592U), 2911867824U, 0, false, 0);
        return this;
    }
}

public class Program
{
    public static IRuntime s_rt;
    public static S1 s_10;
    public static S1 s_17;
    public static void Main()
    {
        CollectibleALC alc = new CollectibleALC();
        System.Reflection.Assembly asm = alc.LoadFromAssemblyPath(System.Reflection.Assembly.GetExecutingAssembly().Location);
        System.Reflection.MethodInfo mi = asm.GetType(typeof(Program).FullName).GetMethod(nameof(MainInner));
        System.Type runtimeTy = asm.GetType(typeof(Runtime).FullName);
        mi.Invoke(null, new object[]{System.Activator.CreateInstance(runtimeTy)});
    }

    public static void MainInner(IRuntime rt)
    {
        s_rt = rt;
        S0 vr21 = s_17.F1;
        new S1(new C2(new C1(0, 0), 0, 0, false, 0)).M105(vr21);
        var vr22 = new C0(vr21.F3);
        s_rt.WriteLine("c_1839", vr22.F1);
    }
}

public interface IRuntime
{
    void WriteLine<T>(string site, T value);
}

public class Runtime : IRuntime
{
    public void WriteLine<T>(string site, T value) => System.Console.WriteLine(value);
}

public class CollectibleALC : System.Runtime.Loader.AssemblyLoadContext
{
    public CollectibleALC(): base(true)
    {
    }
}

Activity

  1. ghost added
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on Aug 15, 2022
  2. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Aug 15, 2022
  3. jakobbotsch commented on Aug 15, 2022

    @jakobbotsch
    MemberAuthor

    Looks like we fail to normalize vr21.F3 when we construct vr22:

    ;  V12 tmp8         [V12,T09] (  2,  2   )     int  ->  [ebp-40H]   single-def V01.F1(offs=0x00) P-INDEP "field V01.F1 (fldOffset=0x0)"
    ;  V13 tmp9         [V13,T10] (  2,  2   )     int  ->  [ebp-44H]   single-def V01.F2(offs=0x04) P-INDEP "field V01.F2 (fldOffset=0x4)"
    ;  V14 tmp10        [V14,T08] (  3,  3   )   ubyte  ->  [ebp-48H]   single-def V01.F3(offs=0x08) P-INDEP "field V01.F3 (fldOffset=0x8)"
    ...
           B914DDC208   mov      ecx, 0x8C2DD14      ; C2
           E87B2B3BFA   call     CORINFO_HELP_NEWSFAST
           C5F857C0     vxorps   xmm0, xmm0
           C5FA7F45D4   vmovdqu  xmmword ptr [ebp-2CH], xmm0
           8945D4       mov      gword ptr [ebp-2CH], eax
           8D7DE4       lea      edi, bword ptr [ebp-1CH]
           8D75D4       lea      esi, bword ptr [ebp-2CH]
           B904000000   mov      ecx, 4
           F3A5         rep movsd
           8B75B8       mov      esi, dword ptr [ebp-48H]          ; missing normalize-on-load
           56           push     esi
           FF75BC       push     dword ptr [ebp-44H]
           FF75C0       push     dword ptr [ebp-40H]
           8D55C4       lea      edx, bword ptr [ebp-3CH]
           8D4DE4       lea      ecx, [ebp-1CH]
           FF1564CFC208 call     [S1:M105(S0):S1:this]

    Full disassembly:

    ; Assembly listing for method Program:MainInner(IRuntime)
    ; Emitting BLENDED_CODE for Pentium 4 - Windows
    ; optimized code
    ; ebp based frame
    ; partially interruptible
    ; No PGO data
    ; 0 inlinees with PGO data; 7 single block inlinees; 0 inlinees without PGO data
    ; Final local variable assignments
    ;
    ;  V00 arg0         [V00,T04] (  3,  3   )     ref  ->  esi         class-hnd single-def
    ;* V01 loc0         [V01    ] (  0,  0   )  struct (12) zero-ref
    ;* V02 loc1         [V02    ] (  0,  0   )     ref  ->  zero-ref    class-hnd exact single-def
    ;  V03 loc2         [V03    ] (  2,  2   )  struct (16) [ebp-1CH]   do-not-enreg[XS] must-init addr-exposed ld-addr-op
    ;* V04 tmp0         [V04    ] (  0,  0   )     ref  ->  zero-ref    class-hnd exact single-def "NewObj constructor temp"
    ;  V05 tmp1         [V05,T06] (  2,  4   )     ref  ->  eax         class-hnd exact single-def "NewObj constructor temp"
    ;  V06 tmp2         [V06,T03] (  3,  6   )  struct (16) [ebp-2CH]   do-not-enreg[SF] must-init "NewObj constructor temp"
    ;  V07 tmp3         [V07,T11] (  1,  2   )  struct (16) [ebp-3CH]   do-not-enreg[HS] must-init hidden-struct-arg "struct address for call/obj"
    ;  V08 tmp4         [V08,T01] (  3,  6   )     ref  ->  edi         class-hnd exact single-def "NewObj constructor temp"
    ;  V09 tmp5         [V09,T02] (  3,  6   )     ref  ->  esi         single-def "LDVIRTFTN this pointer"
    ;  V10 tmp6         [V10,T07] (  2,  4   )     int  ->  eax         "VirtualCall through function pointer"
    ;* V11 tmp7         [V11    ] (  0,  0   )  ushort  ->  zero-ref    "Inlining Arg"
    ;  V12 tmp8         [V12,T09] (  2,  2   )     int  ->  [ebp-40H]   single-def V01.F1(offs=0x00) P-INDEP "field V01.F1 (fldOffset=0x0)"
    ;  V13 tmp9         [V13,T10] (  2,  2   )     int  ->  [ebp-44H]   single-def V01.F2(offs=0x04) P-INDEP "field V01.F2 (fldOffset=0x4)"
    ;  V14 tmp10        [V14,T08] (  3,  3   )   ubyte  ->  [ebp-48H]   single-def V01.F3(offs=0x08) P-INDEP "field V01.F3 (fldOffset=0x8)"     ; normalize-on-load struct field
    ;  V15 tmp11        [V15,T00] (  4,  8   )   byref  ->  ecx         single-def "BlockOp address local"
    ;  V16 cse0         [V16,T05] (  4,  4   )   byref  ->  ebx         "CSE - moderate"
    ;
    ; Lcl frame size = 60
    
    G_M13525_IG01:              ;; offset=0000H
           55           push     ebp
           8BEC         mov      ebp, esp
           57           push     edi
           56           push     esi
           53           push     ebx
           83EC3C       sub      esp, 60
           C5F877       vzeroupper
           C5D857E4     vxorps   xmm4, xmm4
           C5FA7F65C4   vmovdqu  xmmword ptr [ebp-3CH], xmm4
           C5FA7F65D4   vmovdqu  xmmword ptr [ebp-2CH], xmm4
           C5FA7F65E4   vmovdqu  xmmword ptr [ebp-1CH], xmm4
           8BF1         mov      esi, ecx
                                                    ;; size=33 bbWeight=1    PerfScore 12.08
    G_M13525_IG02:              ;; offset=0021H
           B930CAC208   mov      ecx, 0x8C2CA30
           33D2         xor      edx, edx
           E803CCFC71   call     CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS
           8BD8         mov      ebx, eax
           8D5308       lea      edx, bword ptr [ebx+8]
           E825FC38FA   call     CORINFO_HELP_ASSIGN_REF_ESI
           8B4B04       mov      ecx, gword ptr [ebx+4]
           83C108       add      ecx, 8
           8B01         mov      eax, dword ptr [ecx]
           8945C0       mov      dword ptr [ebp-40H], eax
           8B4104       mov      eax, dword ptr [ecx+4]
           8945BC       mov      dword ptr [ebp-44H], eax
           0FB64108     movzx    eax, byte  ptr [ecx+8]
           8845B8       mov      byte  ptr [ebp-48H], al
           B914DDC208   mov      ecx, 0x8C2DD14      ; C2
           E87B2B3BFA   call     CORINFO_HELP_NEWSFAST
           C5F857C0     vxorps   xmm0, xmm0
           C5FA7F45D4   vmovdqu  xmmword ptr [ebp-2CH], xmm0
           8945D4       mov      gword ptr [ebp-2CH], eax
           8D7DE4       lea      edi, bword ptr [ebp-1CH]
           8D75D4       lea      esi, bword ptr [ebp-2CH]
           B904000000   mov      ecx, 4
           F3A5         rep movsd
           8B75B8       mov      esi, dword ptr [ebp-48H]
           56           push     esi
           FF75BC       push     dword ptr [ebp-44H]
           FF75C0       push     dword ptr [ebp-40H]
           8D55C4       lea      edx, bword ptr [ebp-3CH]
           8D4DE4       lea      ecx, [ebp-1CH]
           FF1564CFC208 call     [S1:M105(S0):S1:this]
           B9C4D9C208   mov      ecx, 0x8C2D9C4      ; C0
           E8422B3BFA   call     CORINFO_HELP_NEWSFAST
           8BF8         mov      edi, eax
           66897704     mov      word  ptr [edi+4], si
           8B7308       mov      esi, gword ptr [ebx+8]
           68E8DDC208   push     0x8C2DDE8
           8BCE         mov      ecx, esi
           BAC8D5C208   mov      edx, 0x8C2D5C8      ; IRuntime
           E8A464FC71   call     CORINFO_HELP_VIRTUAL_FUNC_PTR
           0FB75704     movzx    edx, word  ptr [edi+4]
           52           push     edx
           8B1514333F07 mov      edx, gword ptr [073F3314H]      'c_1839'      ; "c_1839"
           8BCE         mov      ecx, esi
           FFD0         call     eax
                                                    ;; size=154 bbWeight=1    PerfScore 67.58
    G_M13525_IG03:              ;; offset=00BBH
           8D65F4       lea      esp, [ebp-0CH]
           5B           pop      ebx
           5E           pop      esi
           5F           pop      edi
           5D           pop      ebp
           C3           ret
                                                    ;; size=8 bbWeight=1    PerfScore 3.50
    
    ; Total bytes of code 195, prolog size 31, PerfScore 103.27, instruction count 62, allocated bytes for code 201 (MethodHash=34b1cb2a) for method Program:MainInner(IRuntime)
    ; ============================================================
  4. JulieLeeMSFT commented on Aug 15, 2022

    @JulieLeeMSFT
    Member

    @jakobbotsch, putting this to .NET 8. Please change to .NET 7 if it needs to be fixed in this release.

  5. added this to the 8.0.0 milestone on Aug 15, 2022
  6. removed
    untriagedNew issue has not been triaged by the area owner
    on Aug 15, 2022
  7. jakobbotsch commented on Aug 15, 2022

    @jakobbotsch
    MemberAuthor

    This seems like an an interaction between spilling/unspilling and this transformation:

    // If we are loading from an in-memory local, we would like to use "push", but this
    // is only legal if we can safely load all 4 bytes. Retype the local node here to
    // TYP_INT for such legal cases to make downstream (LSRA & codegen) logic simpler.
    // Retyping is ok because we model this node as STORE<field type>(LOAD<node type>).
    // If the field came from promotion, we allow the padding to remain undefined, if
    // from decomposition, the field type will be INT (naturally blocking the retyping).
    if (varTypeIsSmall(fieldNode) && (genTypeSize(fieldType) <= genTypeSize(fieldNode)) &&
    fieldNode->OperIsLocalRead())
    {
    fieldNode->ChangeType(TYP_INT);
    }

    This was added in #65803 from what I can see, cc @SingleAccretion

  8. SingleAccretion commented on Aug 15, 2022

    @SingleAccretion
    Contributor

    Looking at this already.

    The assumption being made is that we're only altering the padding contents and those should not matter. Clearly, there is a logic error there somewhere.

    Edit: note we had this logic in codegen even before #65803.

  9. SingleAccretion commented on Aug 15, 2022

    @SingleAccretion
    Contributor

    I see what is going on. I suppose it should have been obvious from the start...

    IN0018: 000072 mov      esi, dword ptr [V13 ebp-48H] // Denormalized value loaded (recall V13 is "ubyte")
    IN0019: 000075 push     esi                          // This use doesn't care
    ...
    IN0022: 000094 mov      word  ptr [edi+4], si        // This one does

    The implication is that this kind of retyping is not legal for candidate locals.

    Will be preparing fixes tomorrow; they may take some time due to this code relying in the retyping a good deal (remember that the whole thing is intended to make pushing thing easier).

    Edit: this is also an interesting case from a perspective of normalize-on-load semantics. We remove all of the normalizing casts because the def of the small field local is itself a small value. One could view the bug as the "normalize-on-load locals can only appear without the corresponding casts if typed small" rule being violated.

  10. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Aug 16, 2022
  11. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Sep 3, 2022
  12. ghost locked as resolved and limited conversation to collaborators on Oct 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

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