JIT: Handle primitive-sized remainders overlapping padding/promotions in physical...
authorJakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Tue, 4 Jul 2023 16:02:38 +0000 (18:02 +0200)
committerGitHub <noreply@github.com>
Tue, 4 Jul 2023 16:02:38 +0000 (18:02 +0200)
commit210a7a5d094d4fcf9ede8344810e2f14da727154
tree804591f1e90f19fbf2f536f1a3ba02bc0ecec49c
parente5be81a39b1337c0b066e97b702b5ac5518c4831
JIT: Handle primitive-sized remainders overlapping padding/promotions in physical promotion (#88109)

The remainder may be separated by a bit of padding or other promoted fields but
still fit into a primitive; in this case it is still beneficial to copy it all
as a primitive, instead of falling back to a full block copy.

Example:
```csharp
private S _s;

void Foo()
{
    S s = new();
    s.A = 10;
    s.D = 20;
    s.F = 30; // A, D, F gets promoted
    _s = s;
}

private struct S
{
    public byte A;
    public byte B;
    public byte C;
    public byte D;
    public byte E;
    public byte F;
}
```

```diff
 Processing block operation [000018] that involves replacements
   dst+003 <- V04 (V01.[003..004)) (last use)
   dst+005 <- V05 (V01.[005..006)) (last use)
   Block op remainder: [001..003) [004..005)
-  => Remainder strategy: retain a full block op
+  => Remainder strategy: int at +001
```

```diff
 ;  V00 this         [V00,T01] (  3,  3   )     ref  ->  rcx         this class-hnd single-def
 ;* V01 loc0         [V01    ] (  0,  0   )  struct ( 8) zero-ref    do-not-enreg[SF] ld-addr-op
 ;# V02 OutArgs      [V02    ] (  1,  1   )  struct ( 0) [rsp+00H]   do-not-enreg[XS] addr-exposed "OutgoingArgSpace"
 ;* V03 tmp1         [V03    ] (  0,  0   )   ubyte  ->  zero-ref    "V01.[000..001)"
 ;* V04 tmp2         [V04    ] (  0,  0   )   ubyte  ->  zero-ref    "V01.[003..004)"
 ;* V05 tmp3         [V05    ] (  0,  0   )   ubyte  ->  zero-ref    "V01.[005..006)"
 ;  V06 tmp4         [V06,T00] (  5, 10   )   byref  ->  rcx         single-def "Spilling address for field-by-field copy"
 ;
 ; Lcl frame size = 0

 G_M52879_IG01:  ;; offset=0000H
  ;; size=0 bbWeight=1 PerfScore 0.00
 G_M52879_IG02:  ;; offset=0000H
        add      rcx, 8
        xor      eax, eax
-       mov      dword ptr [rcx], eax
-       mov      dword ptr [rcx+02H], eax
+       mov      dword ptr [rcx+01H], eax
        mov      byte  ptr [rcx], 10
        mov      byte  ptr [rcx+03H], 20
        mov      byte  ptr [rcx+05H], 30
- ;; size=22 bbWeight=1 PerfScore 5.50
-G_M52879_IG03:  ;; offset=0016H
+ ;; size=20 bbWeight=1 PerfScore 4.50
+G_M52879_IG03:  ;; offset=0014H
        ret
  ;; size=1 bbWeight=1 PerfScore 1.00

-; Total bytes of code 23, prolog size 0, PerfScore 8.80, instruction count 8, allocated bytes for code 23 (MethodHash=1da13170) for method Program:Foo():this (FullOpts)
+; Total bytes of code 21, prolog size 0, PerfScore 7.60, instruction count 7, allocated bytes for code 21 (MethodHash=1da13170) for method Program:Foo():this (FullOpts)
 ; ============================================================

```

We have to be careful, however, since the covering segment can now contain
promoted fields. If this happens we need to make sure we write the promoted
field _after_ the remainder.
src/coreclr/jit/promotion.cpp
src/coreclr/jit/promotion.h
src/coreclr/jit/promotiondecomposition.cpp