Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unnecessary stack spilling for struct instant property access #57055

Closed
hez2010 opened this issue Aug 9, 2021 · 5 comments · Fixed by #64171 or #79341
Closed

Unnecessary stack spilling for struct instant property access #57055

hez2010 opened this issue Aug 9, 2021 · 5 comments · Fixed by #64171 or #79341
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI help wanted [up-for-grabs] Good issue for external contributors tenet-performance Performance related issue
Milestone

Comments

@hez2010
Copy link
Contributor

hez2010 commented Aug 9, 2021

Description

Repro:

int Test()
{
    var a = new A().X;
    return 1 + a;
}

struct A
{
    public int X { get; }
}

Codegen:

push rax
xor eax, eax
mov [rsp], rax
lea rax, [rsp]
xor edx, edx
mov [rax], edx
mov eax, [rax]
inc eax
add rsp, 8
ret

But if I change the code to:

int Test()
{
    var a = new A();
    return 1 + a.X;
}

struct A
{
    public int X { get; }
}

The codegen will become:

mov eax, 1
ret

I think JIT should recognize patterns like: initobj - call - stloc - ldloc

Also applies to method calls.

Configuration

.NET 6 Preivew 6

@hez2010 hez2010 added the tenet-performance Performance related issue label Aug 9, 2021
@dotnet-issue-labeler dotnet-issue-labeler bot added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI untriaged New issue has not been triaged by the area owner labels Aug 9, 2021
@AndyAyersMS
Copy link
Member

Interesting -- I would have guessed const prop could handle this one either way.

@JulieLeeMSFT JulieLeeMSFT removed the untriaged New issue has not been triaged by the area owner label Aug 9, 2021
@JulieLeeMSFT JulieLeeMSFT added this to the 6.0.0 milestone Aug 9, 2021
@AndyAyersMS
Copy link
Member

The issue is that in the first example Roslyn dups an address and the jit doesn't clean this up very well.

;;; first example

  IL_0000:  ldloca.s   V_1
  IL_0002:  dup
  IL_0003:  initobj    A
  IL_0009:  call       instance int32 A::get_X()
  IL_000e:  stloc.0
  IL_000f:  ldc.i4.1
  IL_0010:  ldloc.0
  IL_0011:  add
  IL_0012:  ret

;;; second example

  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    A
  IL_0008:  ldc.i4.1
  IL_0009:  ldloca.s   V_0
  IL_000b:  call       instance int32 A::get_X()
  IL_0010:  add
  IL_0011:  ret

The fix is likely to consider ADDR(LCL(...)) as something cheap enough to dup, instead of spilling the address to a temp like we do now:

Importing BB01 (PC=000) of 'X:Main():int'
    [ 0]   0 (0x000) ldloca.s 1
    [ 1]   2 (0x002) dup
lvaGrabTemp returning 3 (V03 tmp1) called for dup spill.

STMT00000 (IL 0x000...  ???)
               [000003] -A----------              *  ASG       byref 
               [000002] D------N----              +--*  LCL_VAR   byref  V03 tmp1         
               [000001] ------------              \--*  ADDR      byref 
               [000000] -------N----                 \--*  LCL_VAR   struct<A, 4> V01 loc1         

spilling makes us very conservative:

LocalAddressVisitor visiting statement:
STMT00000 (IL 0x000...0x004)
               [000003] -A----------              *  ASG       byref 
               [000002] D------N----              +--*  LCL_VAR   byref  V03 tmp1         
               [000001] ------------              \--*  ADDR      byref 
               [000000] -------N----                 \--*  LCL_VAR   struct<A, 4>(P) V01 loc1         
                                                     \--*    int    V01.<X>k__BackingField (offs=0x00) -> V04 tmp2         

Local V04 should not be enregistered because: it is address exposed

Local V01 should not be enregistered because: it is address exposed

The logic to update is

// If the expression to dup is simple, just clone it.
// Otherwise spill it to a temp, and reload the temp
// twice.
StackEntry se = impPopStack();
GenTree* tree = se.val;
tiRetVal = se.seTypeInfo;
op1 = tree;
if (!opts.compDbgCode && !op1->IsIntegralConst(0) && !op1->IsFPZero() && !op1->IsLocal())
{
const unsigned tmpNum = lvaGrabTemp(true DEBUGARG("dup spill"));
impAssignTempGen(tmpNum, op1, tiRetVal.GetClassHandle(), (unsigned)CHECK_SPILL_ALL);
var_types type = genActualType(lvaTable[tmpNum].TypeGet());
op1 = gtNewLclvNode(tmpNum, type);
// Propagate type info to the temp from the stack and the original tree
if (type == TYP_REF)
{
assert(lvaTable[tmpNum].lvSingleDef == 0);
lvaTable[tmpNum].lvSingleDef = 1;
JITDUMP("Marked V%02u as a single def local\n", tmpNum);
lvaSetClass(tmpNum, tree, tiRetVal.GetClassHandle());
}
}
op1 = impCloneExpr(op1, &op2, tiRetVal.GetClassHandle(), (unsigned)CHECK_SPILL_ALL,
nullptr DEBUGARG("DUP instruction"));
assert(!(op1->gtFlags & GTF_GLOB_EFFECT) && !(op2->gtFlags & GTF_GLOB_EFFECT));

Since this is an optimization, marking as future.

@AndyAyersMS AndyAyersMS added the help wanted [up-for-grabs] Good issue for external contributors label Aug 9, 2021
@AndyAyersMS AndyAyersMS modified the milestones: 6.0.0, Future Aug 9, 2021
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jan 24, 2022
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jan 27, 2022
@EgorBo EgorBo reopened this Jan 27, 2022
@JulieLeeMSFT
Copy link
Member

@EgorBo, this issue was closed and reopened. Doesn't your PR #64171 close this issue? Do we need to fix something more?

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 6, 2022

Seems that #64171 doesn't fix this issue.

image

@EgorBo
Copy link
Member

EgorBo commented Feb 6, 2022

Yes it didn't, but it did improve the case from the tweet.
I had to make it conservative to avoid size regressions so I leave this issue open

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Dec 7, 2022
@jakobbotsch jakobbotsch modified the milestones: Future, 8.0.0 Dec 7, 2022
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Dec 8, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Jan 7, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI help wanted [up-for-grabs] Good issue for external contributors tenet-performance Performance related issue
Projects
Archived in project
5 participants