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

JIT: Mark certain intrinsics as non-escaping #113093

Merged
merged 2 commits into from
Mar 4, 2025

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Mar 3, 2025

The goal is to support things like

BitConverter.GetBytes(val).AsSpan(0, 3).CopyTo(dst);

where we want to save n-bytes of a primitive into a span, today we can either use either ^ (allocates) or come up with something ugly/unsafe.

But looks like there more work to do in JIT to enable that. Currently, JIT gives up on saving the temp array into Span.

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 3, 2025
@EgorBo
Copy link
Member Author

EgorBo commented Mar 3, 2025

@AndyAyersMS so after GetBytes is inlined, we get:

static void Write3Bytes(int val, Span<byte> dst)
{
    byte[] bytes = new byte[4];
    Unsafe.As<byte, int>(ref bytes[0]) = val;
    bytes.AsSpan(0, 3).CopyTo(dst);
}

JIT IR before 'Allocate Objects' phase:

------------ BB01 [0000] [000..015) -> BB03(1) (always), preds={} succs={BB03}

***** BB01 [0000]
STMT00000 ( 0x000[E-] ... 0x006 )
               [000003] DACXG------                         *  STORE_LCL_VAR ref    V05 tmp1
               [000002] --CXG------                         \--*  CALL help ref    CORINFO_HELP_NEWARR_1_VC
               [000001] H---------- arg0                       +--*  CNS_INT(h) long   0x7ffa32c34ae0 class ubyte[]
               [000000] ----------- arg1                       \--*  CNS_INT   long   4

***** BB01 [0000]
STMT00001 ( ??? ... ??? )
               [000005] DA---------                         *  STORE_LCL_VAR ref    V02 loc0
               [000004] -----------                         \--*  LCL_VAR   ref    V05 tmp1

***** BB01 [0000]
STMT00002 ( 0x007[E-] ... 0x014 )
               [000010] nA-XG------                         *  STOREIND  int
               [000008] ---XG------                         +--*  INDEX_ADDR byref ubyte[]
               [000006] -----------                         |  +--*  LCL_VAR   ref    V02 loc0
               [000007] -----------                         |  \--*  CNS_INT   int    0
               [000009] -----------                         \--*  LCL_VAR   int    V00 arg0

***** BB01 [0000]
STMT00007 ( INL01 @ 0x000[E-] ... ??? ) <- INLRT @ 0x015[E-]
               [000025] DA---------                         *  STORE_LCL_VAR struct<System.Span`1, 16> V06 tmp2
               [000024] -----------                         \--*  CNS_INT   int    0

------------ BB03 [0002] [015..016) -> BB08(1),BB04(0) (cond), preds={BB01} succs={BB04,BB08}

***** BB03 [0002]
STMT00009 ( INL02 @ 0x000[E-] ... ??? ) <- INL01 @ ??? <- INLRT @ 0x015[E-]
               [000034] -----------                         *  JTRUE     void
               [000033] -----------                         \--*  NE        int
               [000011] -----------                            +--*  LCL_VAR   ref    V02 loc0
               [000032] -----------                            \--*  CNS_INT   ref    null

------------ BB04 [0003] [015..016) -> BB05(1) (always), preds={BB03} succs={BB05}

------------ BB05 [0004] [015..016) -> BB06(1) (always), preds={BB04} succs={BB06}

------------ BB06 [0005] [015..016) -> BB07(1) (always), preds={BB05} succs={BB07}

***** BB06 [0005]
STMT00015 ( INL02 @ 0x009[E-] ... ??? ) <- INL01 @ ??? <- INLRT @ 0x015[E-]
               [000073] --C-G------                         *  CALL      void   System.ThrowHelper:ThrowArgumentOutOfRangeException()

------------ BB07 [0006] [015..016) -> BB02(1) (always), preds={BB06} succs={BB02}

***** BB07 [0006]
STMT00016 ( INL02 @ 0x00E[E-] ... ??? ) <- INL01 @ ??? <- INLRT @ 0x015[E-]
               [000076] DA---------                         *  STORE_LCL_VAR struct<System.Span`1, 16> V06 tmp2
               [000075] -----------                         \--*  CNS_INT   int    0

------------ BB08 [0007] [015..016) -> BB11(1) (always), preds={BB03} succs={BB11}

------------ BB11 [0010] [015..016) -> BB13(1),BB12(0) (cond), preds={BB08} succs={BB12,BB13}

***** BB11 [0010]
STMT00010 ( INL02 @ 0x043[E-] ... ??? ) <- INL01 @ ??? <- INLRT @ 0x015[E-]
               [000050] ---X-------                         *  JTRUE     void
               [000049] N--X-----U-                         \--*  LE        int
               [000045] -----------                            +--*  CNS_INT   long   3
               [000048] ---X-----U-                            \--*  CAST      long <- ulong <- uint
               [000047] ---X-------                               \--*  ARR_LENGTH int
               [000046] -----------                                  \--*  LCL_VAR   ref    V02 loc0

------------ BB12 [0011] [015..016) -> BB13(1) (always), preds={BB11} succs={BB13}

***** BB12 [0011]
STMT00014 ( INL02 @ 0x04E[E-] ... ??? ) <- INL01 @ ??? <- INLRT @ 0x015[E-]
               [000066] --C-G------                         *  CALL      void   System.ThrowHelper:ThrowArgumentOutOfRangeException()

------------ BB13 [0012] [015..016) -> BB02(1) (always), preds={BB11,BB12} succs={BB02}

***** BB13 [0012]
STMT00011 ( INL02 @ 0x053[E-] ... ??? ) <- INL01 @ ??? <- INLRT @ 0x015[E-]
               [000054] ---X-------                         *  NULLCHECK byte
               [000052] -----------                         \--*  LCL_VAR   ref    V02 loc0

***** BB13 [0012]
STMT00012 ( INL02 @ ??? ... ??? ) <- INL01 @ ??? <- INLRT @ 0x015[E-]
               [000061] sA-X-------                         *  STOREIND  byref
               [000060] -----------                         +--*  FIELD_ADDR byref  System.Span`1[ubyte]:_reference
               [000051] -----------                         |  \--*  LCL_ADDR  byref  V06 tmp2         [+0]
               [000059] ---XG------                         \--*  ADD       byref
               [000056] ---XG------                            +--*  INDEX_ADDR byref ubyte[]
               [000053] -----------                            |  +--*  LCL_VAR   ref    V02 loc0
               [000055] -----------                            |  \--*  CNS_INT   long   0
               [000058] -----------                            \--*  CNS_INT   long   0

***** BB13 [0012]
STMT00013 ( INL02 @ 0x066[E-] ... ??? ) <- INL01 @ ??? <- INLRT @ 0x015[E-]
               [000065] sA---------                         *  STOREIND  int
               [000064] -----------                         +--*  FIELD_ADDR byref  System.Span`1[ubyte]:_length
               [000062] -----------                         |  \--*  LCL_ADDR  byref  V06 tmp2         [+0]
               [000063] -----------                         \--*  CNS_INT   int    3

------------ BB02 [0013] [015..026) -> BB15(1) (always), preds={BB07,BB13} succs={BB15}

***** BB02 [0013]
STMT00004 ( 0x015[E-] ... ??? )
               [000030] DA---------                         *  STORE_LCL_VAR struct<System.Span`1, 16> V03 loc1
               [000028] -----------                         \--*  LCL_VAR   struct<System.Span`1, 16> V06 tmp2

***** BB02 [0013]
STMT00020 ( 0x01E[E-] ... ??? )
               [000098] DA---------                         *  STORE_LCL_VAR struct<System.Span`1, 16> V07 tmp3
               [000019] -----------                         \--*  LCL_VAR   struct<System.Span`1, 16> V01 arg1

------------ BB15 [0014] [01E..01F) -> BB17(0),BB16(1) (cond), preds={BB02} succs={BB16,BB17}

***** BB15 [0014]
STMT00017 ( INL03 @ 0x000[E-] ... ??? ) <- INLRT @ 0x01E[E-]
               [000085] ----G------                         *  JTRUE     void
               [000084] N---G----U-                         \--*  GT        int
               [000080] n----------                            +--*  IND       int
               [000079] -----------                            |  \--*  FIELD_ADDR byref  System.Span`1[ubyte]:_length
               [000078] -----------                            |     \--*  LCL_ADDR  byref  V03 loc1         [+0]
               [000083] n---G------                            \--*  IND       int
               [000082] -----------                               \--*  FIELD_ADDR byref  System.Span`1[ubyte]:_length
               [000081] -----------                                  \--*  LCL_ADDR  byref  V07 tmp3         [+0]

------------ BB16 [0015] [01E..???) -> BB19(1) (always), preds={BB15} succs={BB19}

***** BB16 [0015]
STMT00022 ( INL03 @ 0x00F[E-] ... ??? ) <- INLRT @ 0x01E[E-]
               [000109] DA---------                         *  STORE_LCL_VAR byref  V08 tmp4
               [000089] n----------                         \--*  IND       byref
               [000088] -----------                            \--*  FIELD_ADDR byref  System.Span`1[ubyte]:_reference
               [000087] -----------                               \--*  LCL_ADDR  byref  V07 tmp3         [+0]

***** BB16 [0015]
STMT00023 ( INL03 @ 0x00F[E-] ... ??? ) <- INLRT @ 0x01E[E-]
               [000110] DA---------                         *  STORE_LCL_VAR byref  V09 tmp5
               [000092] n----------                         \--*  IND       byref
               [000091] -----------                            \--*  FIELD_ADDR byref  System.Span`1[ubyte]:_reference
               [000090] -----------                               \--*  LCL_ADDR  byref  V03 loc1         [+0]

***** BB16 [0015]
STMT00024 ( INL03 @ 0x00F[E-] ... ??? ) <- INLRT @ 0x01E[E-]
               [000111] DA---------                         *  STORE_LCL_VAR long   V10 tmp6
               [000096] ---------U-                         \--*  CAST      long <- ulong <- uint
               [000095] n----------                            \--*  IND       int
               [000094] -----------                               \--*  FIELD_ADDR byref  System.Span`1[ubyte]:_length
               [000093] -----------                                  \--*  LCL_ADDR  byref  V03 loc1         [+0]

------------ BB19 [0018] [01E..01F) -> BB20(1) (always), preds={BB16} succs={BB20}

------------ BB20 [0019] [01E..01F) -> BB18(1) (always), preds={BB19} succs={BB18}

***** BB20 [0019]
STMT00021 ( INL04 @ 0x007[E-] ... ??? ) <- INL03 @ 0x00F[E-] <- INLRT @ 0x01E[E-]
               [000108] --C-G------                         *  CALL      void   System.SpanHelpers:Memmove(byref,byref,ulong)
               [000103] ----------- arg0                    +--*  LCL_VAR   byref  V08 tmp4
               [000104] ----------- arg1                    +--*  LCL_VAR   byref  V09 tmp5
               [000105] ----------- arg2                    \--*  LCL_VAR   long   V10 tmp6

------------ BB18 [0021] [01F..01F) -> BB14(1) (always), preds={BB20} succs={BB14}

------------ BB17 [0016] [01E..01F) -> BB14(1) (always), preds={BB15} succs={BB14}

***** BB17 [0016]
STMT00018 ( INL03 @ 0x029[E-] ... ??? ) <- INLRT @ 0x01E[E-]
               [000086] --C-G------                         *  CALL      void   System.ThrowHelper:ThrowArgumentException_DestinationTooShort()

------------ BB14 [0017] [026..027) (return), preds={BB17,BB18} succs={}

***** BB14 [0017]
STMT00006 ( 0x026[E-] ... ??? )
               [000021] -----------                         *  RETURN    void

-------------------------------------------------------------------------------------------------------------------



*************** Starting PHASE Allocate Objects
enabled, analyzing...
... V05 ... checking [000005]
... V02 ... checking [000008]
... V02 ... checking [000010]
V00 first escapes via [000009]
... V02 ... checking [000033]
... V02 ... checking [000047]
... V02 ... checking [000054]
V06 first escapes via [000051]
... V02 ... checking [000056]
... V02 ... checking [000059]
... V02 ... checking [000061]
V02 first escapes via [000053]
V01 first escapes via [000019]
V03 first escapes via [000078]
V07 first escapes via [000081]
... V08 ... checking [000108]
Named Intrinsic System.SpanHelpers.Memmove: Recognized
... V09 ... checking [000108]
Named Intrinsic System.SpanHelpers.Memmove: Recognized
... V10 ... checking [000108]
Named Intrinsic System.SpanHelpers.Memmove: Recognized

Computing escape closure

V02 causes V05 to escape
Allocating V05 on the heap: [escapes]

*************** Finishing PHASE Allocate Objects

Looks like saving array object into Span._reference is preventing us from optimizing it

@EgorBo EgorBo marked this pull request as ready for review March 3, 2025 21:04
@Copilot Copilot bot review requested due to automatic review settings March 3, 2025 21:04

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 1 out of 1 changed files in this pull request and generated no comments.

Copy link
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@EgorBo
Copy link
Member Author

EgorBo commented Mar 3, 2025

@MihuBot

@AndyAyersMS
Copy link
Member

As I mentioned in chat, I can split off the part of #112543 that deals with local span captures from the part that tries to reason about what happens when spans get passed to calls.

@EgorBo
Copy link
Member Author

EgorBo commented Mar 3, 2025

As I mentioned in chat, I can split off the part of #112543 that deals with local span captures from the part that tries to reason about what happens when spans get passed to calls.

@AndyAyersMS Nice! Should we land this PR then? It seems to find some diffs alone too, but it definitely need your work to properly handle more cases

@AndyAyersMS
Copy link
Member

As I mentioned in chat, I can split off the part of #112543 that deals with local span captures from the part that tries to reason about what happens when spans get passed to calls.

@AndyAyersMS Nice! Should we land this PR then? It seems to find some diffs alone too, but it definitely need your work to properly handle more cases

Yeah go ahead, I will try and get the other thing going but it may be a day or two.

@@ -1139,6 +1139,25 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parent
canLclVarEscapeViaParentStack =
!Compiler::s_helperCallProperties.IsNoEscape(comp->eeGetHelperNum(asCall->gtCallMethHnd));
}
else if (asCall->IsSpecialIntrinsic())
{
// Some known special intrinsics don't escape. At this moment, only the ones accepting byrefs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

byrefs for non-GC types....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add that to the comment as part of a different PR to avoid re-running CI 🙂

@EgorBo EgorBo merged commit 75e7800 into dotnet:main Mar 4, 2025
112 checks passed
@EgorBo EgorBo deleted the inline-bitconverter branch March 4, 2025 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants