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: enable inlining methods with EH #112998

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

AndyAyersMS
Copy link
Member

Enable inlining of method with EH. Inlinee EH clauses are integrated into the root method EH table at the appropriate point (mid-table if the call site is in an EH region; at table end otherwise).

Contributes to #108900.

Enable inlining of method with EH. Inlinee EH clauses are integrated
into the root method EH table at the appropriate point (mid-table if
the call site is in an EH region; at table end otherwise).

Contributes to dotnet#108900.
@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 Feb 27, 2025
@AndyAyersMS
Copy link
Member Author

SPMI is not going to be helpful here. PMI may not be either, as callees with EH may not look like attractive candidates w/o PGO.

No special work done on profitability, we just use whatever rules the JIT has already.

Also expecting (though perhaps not in our tests) the usual slew of surprises as certain methods no longer show up in stack traces.

@EgorBo
Copy link
Member

EgorBo commented Feb 27, 2025

No special work done on profitability, we just use whatever rules the JIT has already.

But technically, inliner should resist a bit more for such methods? I think it's fine for this PR not to care about it

@AndyAyersMS
Copy link
Member Author

Seems like the x86 crossgen issues are that we have managed methods with both EH and unmanaged calling conventions and we try and inline these and get confused by the call argument reversal.

For now I'll just block inlining of managed methods with unmanaged conventions.

@AndyAyersMS
Copy link
Member Author

Found a couple places where we rely on info.compXcptnsCount > 0 which will be wrong if the root method has no EH but some inlinees do. Also it can be wrong if we manage to remove EH from the method. Will try using compHndBBtabCount instead.

@AndyAyersMS
Copy link
Member Author

Various folks have reminded me that the EH clause reporting for catches is token based, and this will need to be generalized somehow to handle cross-module inlines. The assumption today is that the class token in the clause is from the same module as the root method.

For runtime dependent types we resolve these tokens into code via fgCreateFiltersForGenericExceptions, though there we also assume the token is from the root method. Perhaps we could extend this to cover all callee-inserted EH clauses (or all clauses?).

For now we can perhaps restrict inlines with EH to be same module.

@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Feb 28, 2025

Also looks like we need to update ebdHandlerNestingLevel and ehMaxHndNestingCount for x86.

@AndyAyersMS
Copy link
Member Author

Hard to say what fraction of the failures are catch-type related, but likely quite a few. So am going to restrict to just try-finally for now.

Also x86 still not happy.

@AndyAyersMS
Copy link
Member Author

For x86, we need to remove the shadow SP slots var if we manage to remove all EH. Seems like what we ought to do is defer allocating this until we're done changing EH regions, then determine if we need this and the max nesting level.

Going to spin that off as a separate change as it leads to some diffs on its own.

@AndyAyersMS
Copy link
Member Author

Split off some of the enabling changes here: #113023.

Don't try an inline managed methods with unmanaged calling conventions.
This mainly copes with x86 where unmanaged calling conventions use reversed
arg order, but I've disabled it in general. No diffs as these methods seem
to always include EH.

Remove uses of `compXcptnsCount` as this goes stale whenever we clone or
remove EH, or (eventually) inline methods with EH. Instead, rely on
`compHndBBtabCount`.

Defer allocating x86's shadow SP var and area until later in jitting,
so this reflects any changes in EH table structure. In particular we
often are able to eliminate EH in part or all together and this saves
a low-offset allocation and so leads to some nice code size savings on
x86.

Also on x86 remove the runtime-dependent catch class case from the
computation for keeping this alive, as we now transform such into
runtime lookups in filters (that may well keep this alive).
@AndyAyersMS
Copy link
Member Author

Down to 300-ish failures

Libraries:

Assert failure(PID 3800 [0x00000ed8], Thread: 5684 [0x1634]): Assertion failed 'block->bbTryIndex == blockTryIndex[block->bbNum]' in 'Microsoft.Cci.MetadataVisitor:Visit(System.Collections.Generic.IEnumerable`1[Microsoft.Cci.IFieldDefinition]):this' during 'Create EH funclets' (IL size 44; hash 0x7157c8c3; Tier1)

X86 (widespread)

Assert failure(PID 36136 [0x00008d28], Thread: 33712 [0x83b0]): Assertion failed 'isFramePointerRequired()' in 'System.IO.FileStream:Dispose(ubyte):this' during 'Generate code' (IL size 18; hash 0xfa74c913; Tier1)

R2R

compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaMethod)resultMethod).OwningType)
at System.Diagnostics.DebugProvider.Fail(String, String) + 0x37
at System.Diagnostics.Debug.Fail(String, String) + 0x44
at Internal.JitInterface.CorInfoImpl.HandleToModuleToken(CORINFO_RESOLVED_TOKEN&) + 0x4e5
at Internal.JitInterface.CorInfoImpl.ComputeMethodWithToken(MethodDesc, CORINFO_RESOLVED_TOKEN&, TypeDesc, Boolean) + 0x58
at Internal.JitInterface.CorInfoImpl.getCallInfo(CORINFO_RESOLVED_TOKEN&, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO) + 0x7ac
at Internal.JitInterface.CorInfoImpl.getCallInfo(IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO) + 0x5d
at Internal.JitInterface.CorInfoImpl.JitCompileMethod(IntPtr&, IntPtr, IntPtr, IntPtr, CORINFO_METHOD_INFO&, UInt32, IntPtr&, UInt32&) + 0xc5

@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Mar 2, 2025

For the first assert. Before funclet splitting we have

BB176 [0022]  1  1    BB09                  0.27  4504 [???..???)-> BB177(1)                  (always) T1                  i IBC internal hascall gcsafe bwd
BB177 [0020]  2  1    BB175,BB176           3.87 64340 [017..01F)-> BB10(0.742),BB178(0.258)  ( cond ) T1                  i IBC internal bwd bwd-src
BB165 [0160]  1  1  0                       0        0 [010..011)-> BB169(0.48),BB166(0.52)   ( cond ) T1 H0   fault {     i IBC rare keep xentry bwd
BB166 [0161]  1  1  0 BB165                 0        0 [010..011)-> BB169(0.73),BB168(0.27)   ( cond ) T1 H0               i IBC rare xentry bwd
BB168 [0177]  1  1  0 BB166                 0        0 [010..011)-> BB169(1)                  (always) T1 H0               i IBC rare internal hascall xentry gcsafe bwd
BB169 [0175]  3  1  0 BB165,BB166,BB168     0        0 [010..011)                             (falret) T1 H0   } }         i IBC rare xentry bwd
BB178 [0009]  1       BB177                 1    16612 [021..024)-> BB181(0.22),BB180(0.78)   ( cond )                     i IBC
BB180 [0338]  1       BB178                 0.77 12728 [???..???)-> BB182(1)                  (always)                     i IBC internal hascall gcsafe
BB181 [0339]  1       BB178                 0.22  3590 [???..???)-> BB182(1)                  (always)                     i IBC internal hascall gcsafe
BB182 [0340]  2       BB180,BB181           0.98 16318 [02A..02C)                             (return)                     i IBC
BB183 [0005]  1     1                       0.00     0 [021..024)-> BB187(0.0177),BB184(0.982)  ( cond )    H1   fault {     i IBC keep xentry
BB184 [0006]  1     1 BB183                 0.00     0 [024..???)-> BB186(0.22),BB185(0.78)   ( cond )    H1               i IBC xentry
BB185 [0024]  1     1 BB184                 0.00     0 [???..???)-> BB187(1)                  (always)    H1               i IBC internal hascall xentry gcsafe
BB186 [0025]  1     1 BB184                 0.00     0 [???..???)-> BB187(1)                  (always)    H1               i IBC internal hascall xentry gcsafe
BB187 [0023]  3     1 BB183,BB185,BB186     0.00     0 [02A..02B)                             (falret)    H1   }           i IBC xentry

and afterwards we've broken up T1:

BB175 [0021]  1  1    BB09                  3.60 59836 [???..???)-> BB177(1)                  (always) T1                  i IBC internal hascall gcsafe bwd
BB176 [0022]  1  1    BB09                  0.27  4504 [???..???)-> BB177(1)                  (always) T1                  i IBC internal hascall gcsafe bwd
BB177 [0020]  2  1    BB175,BB176           3.87 64340 [017..01F)-> BB10(0.742),BB178(0.258)  ( cond ) T1      }           i IBC internal bwd bwd-src
BB178 [0009]  1       BB177                 1    16612 [021..024)-> BB181(0.22),BB180(0.78)   ( cond )                     i IBC
BB180 [0338]  1       BB178                 0.77 12728 [???..???)-> BB182(1)                  (always)                     i IBC internal hascall gcsafe
BB181 [0339]  1       BB178                 0.22  3590 [???..???)-> BB182(1)                  (always)                     i IBC internal hascall gcsafe
BB182 [0340]  2       BB180,BB181           0.98 16318 [02A..02C)                             (return)                     i IBC
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ funclets follow
BB165 [0160]  1  1  0                       0        0 [010..011)-> BB169(0.48),BB166(0.52)   ( cond ) T1 H0 F fault {     i IBC rare keep xentry flet bwd
BB166 [0161]  1  1  0 BB165                 0        0 [010..011)-> BB169(0.73),BB168(0.27)   ( cond ) T1 H0               i IBC rare xentry bwd
BB168 [0177]  1  1  0 BB166                 0        0 [010..011)-> BB169(1)                  (always) T1 H0               i IBC rare internal hascall xentry gcsafe bwd
BB169 [0175]  3  1  0 BB165,BB166,BB168     0        0 [010..011)                             (falret) T1 H0   }           i IBC rare xentry bwd
BB183 [0005]  1     1                       0.00     0 [021..024)-> BB187(0.0177),BB184(0.982)  ( cond )    H1 F fault {     i IBC keep xentry flet
BB184 [0006]  1     1 BB183                 0.00     0 [024..???)-> BB186(0.22),BB185(0.78)   ( cond )    H1               i IBC xentry
BB185 [0024]  1     1 BB184                 0.00     0 [???..???)-> BB187(1)                  (always)    H1               i IBC internal hascall xentry gcsafe
BB186 [0025]  1     1 BB184                 0.00     0 [???..???)-> BB187(1)                  (always)    H1               i IBC internal hascall xentry gcsafe
BB187 [0023]  3     1 BB183,BB185,BB186     0.00     0 [02A..02B)                             (falret)    H1   }           i IBC xentry

Likely fallout from using ehTrueEnclosingTryIndexIL which I believe we need to phase out (at least once we reach inlining), as there is no good way to interpret inlinee IL offsets vs inliner IL offsets.

Here we have

***************  Exception Handling table
index  eTry, eHnd
  0  ::   1        - Try at BB189..BB133 [007..021), Fault   at BB165..BB169 [021..02B)
  1  ::            - Try at BB188..BB177 [007..021), Fault   at BB183..BB187 [021..02B)
Relocated blocks [BB183..BB187] inserted after BB169 at the end of method
Create funclets: moved region

***************  Exception Handling table
index  eTry, eHnd
  0  ::   1        - Try at BB189..BB133 [007..021), Fault   at BB165..BB169 [021..02B)
  1  ::            - Try at BB188..BB177 [007..021), Fault   at BB183..BB187 [021..02B)

But EH0 comes from an inlinee, so these are not mutual-protect regions.

Once we can inline methods with EH, IL ranges are no longer a reliable indicator
of a mutual-protect try regions. Instead, after importation, we can rely on
mutual-protect trys having the same start and end blocks.

Also update other case where we were using `info.compXcptnsCount` in morph
to decide if we needed a frame pointer. This lets us simplify the logic around
frame pointers and EH (though I still think we're making up our minds too early).

Contributes to dotnet#108900.
@AndyAyersMS
Copy link
Member Author

Latest batch of issues

x86:

Assert failure(PID 5348 [0x000014e4], Thread: 5532 [0x159c]): Assertion failed 'bbNumTryLast <= bbNumOuterHndLast' in 'System.Threading.Tasks.Task:InternalWaitCore(int,System.Threading.CancellationToken):ubyte:this' during 'Optimize control flow' (IL size 239; hash 0xbcf79fbe; Tier1)

R2R: as above

Arm64 Linux: widespread crypto failures

@AndyAyersMS
Copy link
Member Author

@jkotas @davidwrighton The jit is triggering an assert in crossgen2 inside getCallInfo when assessing inlining for a native library wrapper method. Previously we wouldn't try inlining these because they contain EH, now we think we can.

I suspect there's a missing check somewhere but not sure where it should be or what form it should take.

call_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaMethod)resultMethod).OwningType)
   at System.Diagnostics.DebugProvider.Fail(String, String) + 0x25
   at System.Diagnostics.Debug.Fail(String, String) + 0x2e
   at Internal.JitInterface.CorInfoImpl.HandleToModuleToken(CORINFO_RESOLVED_TOKEN&) + 0x3c6
   at Internal.JitInterface.CorInfoImpl.HandleToModuleToken(CORINFO_RESOLVED_TOKEN&, MethodDesc, Object&, TypeDesc&) + 0xdb
   at Internal.JitInterface.CorInfoImpl.ComputeMethodWithToken(MethodDesc, CORINFO_RESOLVED_TOKEN&, TypeDesc, Boolean) + 0x2f
   at Internal.JitInterface.CorInfoImpl.getCallInfo(CORINFO_RESOLVED_TOKEN&, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*) + 0x626
   at Internal.JitInterface.CorInfoImpl._getCallInfo(IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*) + 0x39
   at Internal.JitInterface.CorInfoImpl.JitCompileMethod(IntPtr&, IntPtr, IntPtr, IntPtr, CORINFO_METHOD_INFO&, UInt32, IntPtr&, UInt32&) + 0x6c
   at Internal.JitInterface.CorInfoImpl.CompileMethodInternal(IMethodNode, MethodIL) + 0x9c
   at Internal.JitInterface.CorInfoImpl.CompileMethod(MethodWithGCInfo, Logger) + 0x2f4
   at ILCompiler.ReadyToRunCodegenCompilation.<>c__DisplayClass50_0.<ComputeDependencyNodeDependencies>g__CompileOneMethod|5(DependencyNodeCore`1, Int32) + 0x306
   at ILCompiler.ReadyToRunCodegenCompilation.<>c__DisplayClass50_0.<ComputeDependencyNodeDependencies>g__CompileOnThread|4(Int32) + 0x27
   at ILCompiler.ReadyToRunCodegenCompilation.<>c__DisplayClass50_0.<ComputeDependencyNodeDependencies>g__CompilationThread|3(Object) + 0x35
   at System.Threading.Thread.StartHelper.RunWorker() + 0x47
   at System.Threading.Thread.StartThread(IntPtr) + 0x6f
   at System.Threading.Thread.ThreadEntryPoint(IntPtr) + 0x15 0A000020

This comes up in the R2R version resilience tests.

@AndyAyersMS
Copy link
Member Author

Seems like maybe these stubs are not safely inlinable. Perhaps they have some residual state that does not get reset?

The jit doesn't seem to have a way to detect that an inlinee is one of these stubs, it's only told if the root method is one (via jit flags). Perhaps it makes sense to mark these as noinline for now.

@AndyAyersMS
Copy link
Member Author

@mihu-bot

@AndyAyersMS
Copy link
Member Author

There is still some issue on x86 I can't repro... seemingly requires timing like we see on the underpowered machines in CI.

@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Mar 8, 2025

Arm64 alpine musl failure happens if we allow EH inlining in this method:

System.Security.Cryptography.PasswordBasedEncryption:Decrypt(System.Security.Cryptography.SymmetricAlgorithm,System.ReadOnlySpan`1[ubyte],System.ReadOnlySpan`1[ubyte],System.ReadOnlySpan`1[ubyte],System.Span`1[ubyte]):int [Tier1 with Synthesized PGO, IL size=417, code size=5292, hash=0x04a50ab

Root method had 3 EH clauses. Root table was

index  eTry, eHnd
  0  ::   1        - Try at BB20..BB24 [112..152), Finally at BB25..BB25 [152..156)
  1  ::   2        - Try at BB19..BB26 [0CD..161), Finally at BB27..BB29 [161..16D)
  2  ::            - Try at BB18..BB29 [0A9..16D), Finally at BB30..BB30 [16D..19E)

3 inlinees have EH (one with 2, others with 1) that nest inside root region 1, so we end up with 7 clauses and 6 deep nesting. After inlining, the table looks like:

index  eTry, eHnd
  0  ::   5        - Try at BB20..BB462 [112..152), Finally at BB25..BB25 [152..156)  [root 0]
  1  ::   2        - Try at BB328..BB333 [00A..026), Finally at BB329..BB341 [026..02E)
  2  ::   3        - Try at BB253..BB255 [0A5..106), Finally at BB256..BB256 [106..10B)
  3  ::   4        - Try at BB249..BB364 [01F..117), Finally at BB258..BB260 [117..122)
  4  ::   5        - Try at BB220..BB403 [036..065), Finally at BB221..BB410 [065..06D)
  5  ::   6        - Try at BB19..BB26 [0CD..161), Finally at BB27..BB29 [161..16D)  [root 1]
  6  ::            - Try at BB18..BB29 [0A9..16D), Finally at BB30..BB537 [16D..19E) [root 2]

Inline tree here is massive (177 inlines).

We finally clone in 3 of these, and end up with

index  eTry, eHnd
  0  ::   5        - Try at BB219..BB283 [112..152), Fault   at BB233..BB233 [152..156)
  1  ::   2        - Try at BB142..BB146 [00A..026), Fault   at BB150..BB152 [026..02E)
  2  ::   3        - Try at BB135..BB162 [0A5..106), Fault   at BB164..BB164 [106..10B)
  3  ::   4        - Try at BB112..BB171 [01F..117), Finally at BB174..BB185 [117..122)
  4  ::   5        - Try at BB103..BB195 [036..065), Finally at BB198..BB207 [065..06D)
  5  ::   6        - Try at BB36..BB232 [0CD..161), Finally at BB236..BB254 [161..16D)
  6  ::            - Try at BB17..BB235 [0A9..16D), Finally at BB257..BB280 [16D..19E)

Looks like other methods also have issues. If I flip the enable range around and disable just this method things still fail.

@jkotas
Copy link
Member

jkotas commented Mar 9, 2025

The jit is triggering an assert in crossgen2 inside getCallInfo when assessing inlining for a native library wrapper method. Previously we wouldn't try inlining these because they contain EH, now we think we can.

Do you still need help with this one?

@AndyAyersMS
Copy link
Member Author

The jit is triggering an assert in crossgen2 inside getCallInfo when assessing inlining for a native library wrapper method. Previously we wouldn't try inlining these because they contain EH, now we think we can.

Do you still need help with this one?

I've blocked inlining of pinvoke methods with EH, so don't need help right now. And I still have some other issues to sort out...

@AndyAyersMS
Copy link
Member Author

I think there is an arm64 codegen bug where we convert mul/neg/jcc into mneg/bne, but mneg does not set flags and so this doesn't work right.

Good codegen (one less inline)

IN0101: 00045C      ldr     x2, [x20, #0x08]
IN0102: 000460      ldr     w2, [x2, #0x14]
IN0103: 000464      cbz     w2, G_M62798_IG43
IN0104: 000468      cmn     w2, #1
IN0105: 00046C      bne     G_M62798_IG21
IN0106: 000470      cmp     w1, #1
IN0107: 000474      bvs     G_M62798_IG44

G_M62798_IG21:        ; offs=0x000478, size=0x0014, bbWeight=0.04, PerfScore 0.64, gcrefRegs=2900000 {x20 x23 x25}, byrefRegs=1000000 {x24}, BB109 [0281], BB111 [0284], byref, isz

IN0108: 000478      sdiv    w3, w1, w2
IN0109: 00047C      msub    w1, w3, w2, w1
IN010a: 000480      cbnz    w1, G_M62798_IG42

bad codegen

;; because of inlining we realized the operand to be divided is zero and the divisor is nonzero
;; (we could have optimized this better, the divide/multiply are unnecessary
;; but the bug is we lost flags

IN00ff: 000458      ldr     x1, [x20, #0x08]
IN0100: 00045C      ldr     w1, [x1, #0x14]
IN0101: 000460      mov     w2, wzr
IN0102: 000464      cbz     w1, G_M62798_IG42
IN0103: 000468      sdiv    w2, w2, w1
IN0104: 00046C      mneg    w1, w2, w1
IN0105: 000470      bne     G_M62798_IG41

Trying a hacky fix but doubt it is sufficient as we should be avoiding this... we'll see. I can get an SPMI of this compilation and work it out there tomorrow.

@jakobbotsch
Copy link
Member

@AndyAyersMS indeed there is a bug like that, #113320. The fix should be simple – SupportsSettingZeroFlag needs treatment of neg with contained mul similar to what's already there for add/sub

@AndyAyersMS
Copy link
Member Author

@jakobbotsch thanks

@amanasifkhalid
Copy link
Member

@AndyAyersMS the x86 assert failure during control flow opts is because fgRelocateEHRegions moves a cold try region inside a handler region to the end of the method, breaking up the handler:

*************** In fgRelocateEHRegions()
Relocating try range BB12..BB13 (EH#0) to end of BBlist

---------------------------------------------------------------------------------------------------------------------------------------------------------------------
BBnum BBid ref try hnd preds           weight   IBC [IL range]   [jump]                            [EH region]        [flags]
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
BB01 [0000]  1                             1        [000..007)-> BB18(0.34),BB02(0.66)   ( cond )                     i
BB02 [0002]  1       BB01                  0.66  66 [000..001)-> BB03(1)                 (always)                     i IBC internal nullcheck
BB03 [0004]  1  1    BB02                  0.66  66 [000..001)-> BB07(0),BB04(1)         ( cond ) T1      try {       i IBC keep
BB04 [0005]  1  1    BB03                  0.66  66 [000..001)-> BB06(1),BB05(0)         ( cond ) T1                  i IBC
BB05 [0014]  1  1    BB04                  0      0 [000..001)                           (throw ) T1                  i IBC rare hascall gcsafe
BB06 [0015]  1  1    BB04                  0.66  66 [000..001)-> BB07(1)                 (always) T1                  i IBC hascall gcsafe
BB07 [0006]  2  1    BB03,BB06             0.66  66 [000..001)-> BB10(1)                 (callf ) T1                  i IBC
BB08 [0010]  1  1    BB16                  0.66  66 [000..001)-> BB09(1)                 (callfr) T1      }           i IBC internal
BB09 [0011]  1       BB08                  0.66  66 [000..001)-> BB17(1)                 (ALWAYS)                     i IBC internal KEEP
BB10 [0007]  2     1 BB07                  0.66  66 [000..001)-> BB16(1),BB11(0)         ( cond )    H1   finally {   i IBC keep
BB11 [0020]  1     1 BB10                  0      0 [000..001)-> BB16(0.2),BB12(0.8)     ( cond )    H1               i IBC rare
BB12 [0021]  1  0  1 BB11                  0      0 [000..001)-> BB15(1)                 (callf ) T0 H1   try {       i IBC rare keep hascall gcsafe
BB13 [0026]  1  0  1 BB15                  0      0 [000..001)-> BB14(1)                 (callfr) T0 H1   }           i IBC rare internal
BB14 [0027]  1     1 BB13                  0      0 [000..001)-> BB16(1)                 (ALWAYS)    H1               i IBC rare internal KEEP
BB15 [0024]  2     0 BB12                  0      0 [000..001)-> BB13(1)                 (finret)    H0   finally { } i IBC rare keep
BB16 [0025]  3     1 BB10,BB11,BB14        0.66  66 [000..001)-> BB08(1)                 (finret)    H1   }           i IBC
BB17 [0008]  2       BB18,BB09             1    100 [000..008)-> BB20(0.2),BB19(0.8)     ( cond )                     i IBC nullcheck
BB18 [0003]  1       BB01                  0.34     [???..???)-> BB17(1)                 (always)                     i internal hascall gcsafe
BB19 [0032]  1       BB17                  0.80  80 [007..008)                           (throw )                     i IBC hascall gcsafe
BB20 [0033]  1       BB17                  0.20  20 [007..00E)                           (return)                     i IBC hascall
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

***************  Exception Handling table
index  nest, eTry, eHnd
  0  ::               1  - Try at BB12..BB13 [010..020), Finally at BB15..BB15 [020..036)
  1  ::                  - Try at BB03..BB08 [000..01D), Finally at BB10..BB16 [01D..025)
Relocated blocks [BB12..BB13] inserted after BB20 at the end of method

After relocating an EH try region
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
BBnum BBid ref try hnd preds           weight   IBC [IL range]   [jump]                            [EH region]        [flags]
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
BB01 [0000]  1                             1        [000..007)-> BB18(0.34),BB02(0.66)   ( cond )                     i
BB02 [0002]  1       BB01                  0.66  66 [000..001)-> BB03(1)                 (always)                     i IBC internal nullcheck
BB03 [0004]  1  1    BB02                  0.66  66 [000..001)-> BB07(0),BB04(1)         ( cond ) T1      try {       i IBC keep
BB04 [0005]  1  1    BB03                  0.66  66 [000..001)-> BB06(1),BB05(0)         ( cond ) T1                  i IBC
BB05 [0014]  1  1    BB04                  0      0 [000..001)                           (throw ) T1                  i IBC rare hascall gcsafe
BB06 [0015]  1  1    BB04                  0.66  66 [000..001)-> BB07(1)                 (always) T1                  i IBC hascall gcsafe
BB07 [0006]  2  1    BB03,BB06             0.66  66 [000..001)-> BB10(1)                 (callf ) T1                  i IBC
BB08 [0010]  1  1    BB16                  0.66  66 [000..001)-> BB09(1)                 (callfr) T1      }           i IBC internal
BB09 [0011]  1       BB08                  0.66  66 [000..001)-> BB17(1)                 (ALWAYS)                     i IBC internal KEEP
BB10 [0007]  2     1 BB07                  0.66  66 [000..001)-> BB16(1),BB11(0)         ( cond )    H1   finally {   i IBC keep
BB11 [0020]  1     1 BB10                  0      0 [000..001)-> BB16(0.2),BB12(0.8)     ( cond )    H1               i IBC rare
BB14 [0027]  1     1 BB13                  0      0 [000..001)-> BB16(1)                 (ALWAYS)    H1               i IBC rare internal KEEP
BB15 [0024]  2     0 BB12                  0      0 [000..001)-> BB13(1)                 (finret)    H0   finally { } i IBC rare keep
BB16 [0025]  3     1 BB10,BB11,BB14        0.66  66 [000..001)-> BB08(1)                 (finret)    H1   }           i IBC
BB17 [0008]  2       BB18,BB09             1    100 [000..008)-> BB20(0.2),BB19(0.8)     ( cond )                     i IBC nullcheck
BB18 [0003]  1       BB01                  0.34     [???..???)-> BB17(1)                 (always)                     i internal hascall gcsafe
BB19 [0032]  1       BB17                  0.80  80 [007..008)                           (throw )                     i IBC hascall gcsafe
BB20 [0033]  1       BB17                  0.20  20 [007..00E)                           (return)                     i IBC hascall
BB12 [0021]  1  0  1 BB11                  0      0 [000..001)-> BB15(1)                 (callf ) T0 H1   try {       i IBC rare keep hascall gcsafe
BB13 [0026]  1  0  1 BB15                  0      0 [000..001)-> BB14(1)                 (callfr) T0 H1   }           i IBC rare internal
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

I suspect the JIT isn't setting ebdHandlerNestingLevel for inlined try regions. I can share the whole dump with you offline if it would be useful.

@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Mar 11, 2025

I have been trying to repro this with no luck, how did you do it?

I am recomputing the handler nesting level, but perhaps not early enough or correctly? See the newly added FinalizeEH (added in #113023).

@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Mar 11, 2025

Ah maybe it is missing the +1 bias...no, that's just for the max nesting.

@amanasifkhalid
Copy link
Member

I have been trying to repro this with no luck, how did you do it?

I was able to using the build from this run. I didn't have to do anything special locally to get a consistent repro on my machine.

I am recomputing the handler nesting level, but perhaps not early enough or correctly?

Let me dig into my repro and take a look...

@amanasifkhalid
Copy link
Member

See the newly added FinalizeEH

I see that we only call this in the backend at the moment, presumably because codegen relies on the handler nesting level to be accurate. From a quick search, fgRelocateEHRegions is the only frontend pass after inlining that uses ebdHandlerNestingLevel, and it doesn't even really need to check it -- I think checking if a try region's entry block has a handler index would be sufficient. Or we can just get rid of fgRelocateEHRegions altogether (#113330).

I don't think you ought to refactor FinalizeEH so it can be called mid-frontend just to placate fgRelocateEHRegions.

@AndyAyersMS
Copy link
Member Author

Yeah let's take #113330...

@AndyAyersMS
Copy link
Member Author

Finally past those failures. The x86 failure was also popping up in jitstress, too bad I didn't look there...

Am going to run some stress legs but currently jit-stress, jit-experimental and pgostress have a handful of known failures.

@AndyAyersMS
Copy link
Member Author

/azp run runtime-coreclr libraries-pgo

@AndyAyersMS AndyAyersMS marked this pull request as ready for review March 11, 2025 22:59
@Copilot Copilot bot review requested due to automatic review settings March 11, 2025 22:59

Choose a reason for hiding this comment

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

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

@AndyAyersMS
Copy link
Member Author

@dotnet/jit-contrib think this is ready for review.

I will run some stress modes but am holding off a bit to see if we can get some of them cleaned up first.

Copy link
Member

@BruceForstall BruceForstall left a comment

Choose a reason for hiding this comment

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

LGTM. Definitely needs some stress runs.

if (enclosingRegion == 0)
{
// The call site is not in an EH region, so we can put the inlinee EH clauses
// at the end of root method's the EH table.
Copy link
Member

Choose a reason for hiding this comment

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

nit

Suggested change
// at the end of root method's the EH table.
// at the end of root method's EH table.

// inlinee eh1 -> eh2
//
// root eh0 -> eh0
//
Copy link
Member

Choose a reason for hiding this comment

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

Is this true, and if so, desirable? What if the root method has:

try { } ... // #1
call()
try { } ... // #2

and call has EH and is inlined. Don't we want to put the call EH info in between the EH info for #1 and #2 in the resulting EH table? So, the lexical order is respected?

Or maybe the lexical order doesn't matter for EH regions without nesting relationships, since we already presumably can move them around ("make them out of order") today.

//
EHblkDsc* const outermostEbd =
fgTryAddEHTableEntries(insertBeforeIndex, inlineeRegionCount, /* deferAdding */ false);
assert(outermostEbd != nullptr);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
assert(outermostEbd != nullptr);
noway_assert(outermostEbd != nullptr);

?

{
return true;
}

// We do not support setting zero flag for madd/msub.
if (OperIs(GT_NEG) && (!gtGetOp1()->OperIs(GT_MUL) || !gtGetOp1()->isContained()))
Copy link
Member

Choose a reason for hiding this comment

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

Looks like this is from your other PR

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.

None yet

6 participants