Skip to content

Instantly share code, notes, and snippets.

@gfoidl
Last active March 18, 2018 18:19
Show Gist options
  • Save gfoidl/4bfa371af9ae21ea8b867e9206ace5cc to your computer and use it in GitHub Desktop.
Save gfoidl/4bfa371af9ae21ea8b867e9206ace5cc to your computer and use it in GitHub Desktop.
ThrowHelper
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace ConsoleApplication
{
public unsafe class Program
{
public static void Main(string[] args)
{
Sample sample = new Sample();
Console.WriteLine(A(sample));
Console.WriteLine(B(sample));
Console.WriteLine(C(sample));
Console.WriteLine(D(sample));
Console.WriteLine(E(sample));
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static int A(Sample sample)
{
if (sample == null) ThrowHelper.ThrowArgumentNull();
return sample.A;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static int B(Sample sample)
{
if (sample == null) ThrowHelper.ThrowArgumentNull1();
return sample.A;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static int C(Sample sample)
{
if (sample == null) ThrowArgumentNull();
return sample.A;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static int D(Sample sample)
{
ThrowHelper.ThrowIfArgumentNull(sample);
return sample.A;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static int E(Sample sample)
{
ThrowHelper.ThrowIfArgumentNull1(sample);
return sample.A;
}
private static void ThrowArgumentNull() => throw new ArgumentNullException("argument");
}
public class Sample
{
public int A { get; set; } = 42;
}
internal static class ThrowHelper
{
public static void ThrowArgumentNull() => throw CreateArgumentNull();
[MethodImpl(MethodImplOptions.NoInlining)]
private static Exception CreateArgumentNull() => new ArgumentNullException("argument");
public static void ThrowArgumentNull1() => throw new ArgumentNullException("argument");
public static void ThrowIfArgumentNull<T>(T arg) where T : class
{
if (arg == null)
throw new ArgumentNullException("argument");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ThrowIfArgumentNull1<T>(T arg) where T : class
{
if (arg == null)
throw new ArgumentNullException("argument");
}
}
}
42
42
42
42
; Assembly listing for method ConsoleApplication.Program:A(ref):int
; Emitting BLENDED_CODE for X64 CPU with AVX
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T00] ( 5, 5 ) ref -> rdi class-hnd
;# V01 OutArgs [V01 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00]
;
; Lcl frame size = 8
G_M64832_IG01:
50 push rax
G_M64832_IG02:
4885FF test rdi, rdi
7408 je SHORT G_M64832_IG05
G_M64832_IG03:
8B4708 mov eax, dword ptr [rdi+8]
G_M64832_IG04:
4883C408 add rsp, 8
C3 ret
G_M64832_IG05:
E8E5FBFFFF call ConsoleApplication.ThrowHelper:ThrowArgumentNull()
CC int3
; Total bytes of code 20, prolog size 1 for method ConsoleApplication.Program:A(ref):int
; ============================================================
; Assembly listing for method ConsoleApplication.Program:B(ref):int
; Emitting BLENDED_CODE for X64 CPU with AVX
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T00] ( 5, 5 ) ref -> rdi class-hnd
;# V01 OutArgs [V01 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00]
;
; Lcl frame size = 8
G_M64831_IG01:
50 push rax
G_M64831_IG02:
4885FF test rdi, rdi
7408 je SHORT G_M64831_IG05
G_M64831_IG03:
8B4708 mov eax, dword ptr [rdi+8]
G_M64831_IG04:
4883C408 add rsp, 8
C3 ret
G_M64831_IG05:
E885E7FFFF call ConsoleApplication.ThrowHelper:ThrowArgumentNull1()
CC int3
; Total bytes of code 20, prolog size 1 for method ConsoleApplication.Program:B(ref):int
; ============================================================
; Assembly listing for method ConsoleApplication.Program:C(ref):int
; Emitting BLENDED_CODE for X64 CPU with AVX
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T00] ( 5, 5 ) ref -> rdi class-hnd
;# V01 OutArgs [V01 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00]
;
; Lcl frame size = 8
G_M64830_IG01:
50 push rax
G_M64830_IG02:
4885FF test rdi, rdi
7408 je SHORT G_M64830_IG05
G_M64830_IG03:
8B4708 mov eax, dword ptr [rdi+8]
G_M64830_IG04:
4883C408 add rsp, 8
C3 ret
G_M64830_IG05:
E8D5E2FFFF call ConsoleApplication.Program:ThrowArgumentNull()
CC int3
; Total bytes of code 20, prolog size 1 for method ConsoleApplication.Program:C(ref):int
; ============================================================
; Assembly listing for method ConsoleApplication.Program:D(ref):int
; Emitting BLENDED_CODE for X64 CPU with AVX
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T00] ( 4, 4 ) ref -> rbx class-hnd
;# V01 OutArgs [V01 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00]
;
; Lcl frame size = 0
G_M64828_IG01:
53 push rbx
488BDF mov rbx, rdi
G_M64828_IG02:
488BF3 mov rsi, rbx
48BF6080ADC6377F0000 mov rdi, 0x7F37C6AD8060
E872F9FFFF call ConsoleApplication.ThrowHelper:ThrowIfArgumentNull(ref)
8B4308 mov eax, dword ptr [rbx+8]
G_M64828_IG03:
5B pop rbx
C3 ret
; Total bytes of code 27, prolog size 1 for method ConsoleApplication.Program:D(ref):int
; ============================================================
; Assembly listin42
g for method ConsoleApplication.Program:E(ref):int
; Emitting BLENDED_CODE for X64 CPU with AVX
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T00] ( 5, 5 ) ref -> rdi class-hnd
; V01 tmp0 [V01,T01] ( 3, 0 ) ref -> rbx class-hnd exact
; V02 tmp1 [V02,T02] ( 2, 0 ) ref -> rsi
;# V03 OutArgs [V03 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00]
;
; Lcl frame size = 0
G_M64828_IG01:
53 push rbx
G_M64828_IG02:
4885FF test rdi, rdi
7405 je SHORT G_M64828_IG05
G_M64828_IG03:
8B4708 mov eax, dword ptr [rdi+8]
G_M64828_IG04:
5B pop rbx
C3 ret
G_M64828_IG05:
48BF301658C6377F0000 mov rdi, 0x7F37C6581630
E8A62E9678 call CORINFO_HELP_NEWSFAST
488BD8 mov rbx, rax
BF01000000 mov edi, 1
48BE603D26C5377F0000 mov rsi, 0x7F37C5263D60
E8AF439678 call CORINFO_HELP_STRCNS
488BF0 mov rsi, rax
488BFB mov rdi, rbx
E8042760FF call System.ArgumentNullException:.ctor(ref):this
488BFB mov rdi, rbx
E89C249778 call CORINFO_HELP_THROW
CC int3
; Total bytes of code 69, prolog size 1 for method ConsoleApplication.Program:E(ref):int
; ============================================================
@gfoidl
Copy link
Author

gfoidl commented Jan 17, 2018

Discussion

As of dotnet/coreclr#6103 A, B, and C produce the same code.

@gfoidl
Copy link
Author

gfoidl commented Feb 7, 2018

There is even a further improvement when (string-) arguments must be passed to the ThrowHelper.
See Reference source -- ThrowHelper.

String argument in callee

G_M57615_IG01:
       55                   push     rbp
       488BEC               mov      rbp, rsp

G_M57615_IG02:
       85F6                 test     esi, esi
       7C07                 jl       SHORT G_M57615_IG05

G_M57615_IG03:
       8BC6                 mov      eax, esi
       C1E005               shl      eax, 5

G_M57615_IG04:
       5D                   pop      rbp
       C3                   ret      

G_M57615_IG05:
       E8D4FBFFFF           call     ConsoleApp3.Strings:get_Unknown():ref
       488BF8               mov      rdi, rax
       E8DCFBFFFF           call     ConsoleApp3.ThrowHelper:ThrowException(ref)
       CC                   int3     

; Total bytes of code 29, prolog size 4

Without string argument in callee

G_M57615_IG01:
       50                   push     rax

G_M57615_IG02:
       85F6                 test     esi, esi
       7C0A                 jl       SHORT G_M57615_IG05

G_M57615_IG03:
       8BC6                 mov      eax, esi
       C1E005               shl      eax, 5

G_M57615_IG04:
       4883C408             add      rsp, 8
       C3                   ret      

G_M57615_IG05:
       BF01000000           mov      edi, 1
       E8AFFBFFFF           call     ConsoleApp3.ThrowHelper:ThrowException(int)
       CC                   int3     

; Total bytes of code 26, prolog size 1

This is produced by:

public class Foo
{
    public int Do(int a)
    {
        if (a < 0) ThrowHelper.ThrowException(ThrowHelper.Reason.Unknown);

        return a * 32;
    }
}

internal static class ThrowHelper
{
    public static void ThrowException(Reason reason) => throw new Exception(GetMessage(reason));

    private static string GetMessage(Reason reason)
    {
        switch (reason)
        {
            case Reason.ArgumentOutOfRange:
                return Strings.ArgumentOutOfRange;
            case Reason.Unknown:
                return Strings.Unknown;
            default:
                return string.Empty;
        }
    }

    public enum Reason
    {
        ArgumentOutOfRange,
        Unknown
    }
}

@gfoidl
Copy link
Author

gfoidl commented Mar 18, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment