runtime: Coalescing calls to non-returning throw helpers?

Repro:

using System;

class Program
{
    public static void Main()
    {
        var arr = new int[20];
        var s0 = new Span<int>(arr, 0, 1);
        var s1 = new Span<int>(arr, 1, 1);
        var s2 = new Span<int>(arr, 2, 1);
        var s3 = new Span<int>(arr, 3, 1);
        var s4 = new Span<int>(arr, 4, 1);
        var s5 = new Span<int>(arr, 5, 1);
    }
}

The generated asm includes this at the end:

G_M45847_IG05:
       E84B8ECC5D           call     System.ThrowHelper:ThrowArgumentOutOfRangeException()

G_M45847_IG06:
       E8468ECC5D           call     System.ThrowHelper:ThrowArgumentOutOfRangeException()

G_M45847_IG07:
       E8418ECC5D           call     System.ThrowHelper:ThrowArgumentOutOfRangeException()

G_M45847_IG08:
       E83C8ECC5D           call     System.ThrowHelper:ThrowArgumentOutOfRangeException()

G_M45847_IG09:
       E8378ECC5D           call     System.ThrowHelper:ThrowArgumentOutOfRangeException()

G_M45847_IG10:
       E8328ECC5D           call     System.ThrowHelper:ThrowArgumentOutOfRangeException()
       CC                   int3

Can/should these be coalesced into a common call point?

category:cq theme:basic-cq skill-level:expert cost:medium

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 20 (20 by maintainers)

Commits related to this issue

Most upvoted comments

Here’s a prototype SimpleThrowTailMerge that can merge many common throw helper blocks.

Jit-Diffs reports:

Total bytes of diff: -39926 (-0.10% of base)
    diff is an improvement.

Top file improvements by size (bytes):
       -7162 : System.Private.CoreLib.dasm (-0.16% of base)
       -4440 : Microsoft.CodeAnalysis.VisualBasic.dasm (-0.08% of base)
       -4345 : System.Memory.dasm (-1.87% of base)
       -2488 : CommandLine.dasm (-0.56% of base)
       -2015 : Microsoft.CodeAnalysis.dasm (-0.12% of base)

82 total files with size differences (82 improved, 0 regressed), 47 unchanged.

Top method regressions by size (bytes):
         111 ( 5.69% of base) : System.Memory.dasm - SequenceReader`1:TryReadToSlow(byref,struct,struct,int,bool):bool:this (2 methods)
          87 (14.19% of base) : System.Security.Cryptography.Algorithms.dasm - PasswordBasedEncryption:Pbes2Decrypt(struct,struct,struct,struct,struct):int
          87 (14.19% of base) : System.Security.Cryptography.Cng.dasm - PasswordBasedEncryption:Pbes2Decrypt(struct,struct,struct,struct,struct):int
          78 (15.82% of base) : System.Memory.dasm - SequenceReader`1:TryReadToSlow(byref,struct,bool):bool:this
          56 ( 5.15% of base) : System.Security.Cryptography.Algorithms.dasm - KeyFormatHelper:WriteEncryptedPkcs8(struct,struct,ref,ref):ref

Top method improvements by size (bytes):
        -438 (-4.46% of base) : System.Memory.dasm - ReadOnlySequenceDebugView`1:.ctor(struct):this (7 methods)
        -396 (-5.97% of base) : Microsoft.CodeAnalysis.dasm - ArrayBuilderExtensions:SelectAsArray(ref,ref):struct (7 methods)
        -390 (-6.95% of base) : System.Memory.dasm - SequenceReader`1:TryCopyMultisegment(struct):bool:this (6 methods)
        -294 (-1.13% of base) : Microsoft.CodeAnalysis.dasm - AsyncQueue`1:WithCancellation(ref,struct):ref (49 methods)
        -294 (-1.80% of base) : System.Linq.Parallel.dasm - ParallelQuery`1:OfType():ref:this (49 methods)

Top method regressions by size (percentage):
          78 (15.82% of base) : System.Memory.dasm - SequenceReader`1:TryReadToSlow(byref,struct,bool):bool:this
          87 (14.19% of base) : System.Security.Cryptography.Algorithms.dasm - PasswordBasedEncryption:Pbes2Decrypt(struct,struct,struct,struct,struct):int
          87 (14.19% of base) : System.Security.Cryptography.Cng.dasm - PasswordBasedEncryption:Pbes2Decrypt(struct,struct,struct,struct,struct):int
          12 ( 9.52% of base) : System.Text.RegularExpressions.dasm - <>c:<AddConcatenate>b__78_0(struct,struct):this
          27 ( 8.79% of base) : System.Net.Http.dasm - Http2ReadStream:ReadAsync(struct,struct):struct:this

Top method improvements by size (percentage):
         -56 (-17.50% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - LookupResult:CompactSymbols(int):this
        -278 (-16.27% of base) : System.Reflection.Metadata.dasm - MetadataBuilder:GetRowCounts():struct:this
         -45 (-16.25% of base) : System.Private.CoreLib.dasm - Decimal:ToDecimal(struct):struct
         -56 (-16.18% of base) : Microsoft.CodeAnalysis.dasm - ExceptionHandlerContainerScope:CloseScope(ref):this
         -14 (-14.89% of base) : System.Security.Cryptography.Encoding.dasm - ThrowHelper:ValidateTransformBlock(ref,int,int,int)

1781 total methods with size differences (1756 improved, 25 regressed), 201163 unchanged.

The size win is modest, though still perhaps worth pursuing.

  • We might consider handling more general cases like BBJ_THROW blocks – not clear how often users write the exact same throw constructs in a method or across an inline complex.
  • Probably should to generalize the map key to include EH Region.
  • I haven’t looked at diffs extensively yet. Might be missing more cases than I realize.
  • Don’t expect regressions so will have to drill into those.
  • Might want to move this transformation earlier in the phase order to remove IR sooner.