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

Improve Disassembly exporters and add PrettyGithubMarkdownDiffDisassemblyExporter #927

Merged
merged 5 commits into from
Oct 30, 2018

Conversation

WojciechNagorski
Copy link
Contributor

Fixes for #544

I've changed all dissassembly exporters because the generated files were overwritten for each JIT.
I've used git diff --no-index --no-color --text to generate diff. I couldn't find any diff algorithm on MIT licence.

In the next comments I will add examples of the reports that were published after my changes.

@WojciechNagorski
Copy link
Contributor Author

file BenchmarkDotNet.Samples.IntroDisassemblyRyuJit-asm.pretty.diff.md:

BenchmarkDotNet.Samples.IntroDisassemblyRyuJit

Diff between SumLocal and SumField
on .NET Core 2.1.4 (CoreCLR 4.6.26814.03, CoreFX 4.6.26814.02), 64bit RyuJIT.

-; BenchmarkDotNet.Samples.IntroDisassemblyRyuJit.SumLocal()
-                   var local = field; // we use local variable that points to the field
-            ^^^^^^^^^^^^^^^^^^
-       mov     rax,qword ptr [rcx+8]
+; BenchmarkDotNet.Samples.IntroDisassemblyRyuJit.SumField()
                    int sum = 0;
             ^^^^^^^^^^^^
-       xor     edx,edx
-                   for (int i = 0; i < local.Length; i++)
+       xor     eax,eax
+                   for (int i = 0; i < field.Length; i++)
                  ^^^^^^^^^
-       xor     ecx,ecx
-                   for (int i = 0; i < local.Length; i++)
+       xor     edx,edx
+                   for (int i = 0; i < field.Length; i++)
                             ^^^^^^^^^^^^^^^^
-       mov     r8d,dword ptr [rax+8]
-       test    r8d,r8d
+       mov     rcx,qword ptr [rcx+8]
+       cmp     dword ptr [rcx+8],0
        jle     M00_L01
-                       sum += local[i];
+                       sum += field[i];
                 ^^^^^^^^^^^^^^^^
 M00_L00:
-       movsxd  r9,ecx
-       add     edx,dword ptr [rax+r9*4+10h]
-                   for (int i = 0; i < local.Length; i++)
+       mov     r8,rcx
+       cmp     edx,dword ptr [r8+8]
+       jae     00007ff9`0c412c1f
+       movsxd  r9,edx
+       add     eax,dword ptr [r8+r9*4+10h]
+                   for (int i = 0; i < field.Length; i++)
                                               ^^^
-       inc     ecx
-       cmp     r8d,ecx
+       inc     edx
+       cmp     dword ptr [rcx+8],edx
        jg      M00_L00
                    return sum;
             ^^^^^^^^^^^
 M00_L01:
-       mov     eax,edx
-; Total bytes of code 34
+       add     rsp,28h
+; Total bytes of code 42
 

@WojciechNagorski
Copy link
Contributor Author

File BenchmarkDotNet.Samples.IntroDisassembly-asm.pretty.diff.md:

BenchmarkDotNet.Samples.IntroDisassembly

Diff for Sum method between:
.NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0
.NET Core 2.1.4 (CoreCLR 4.6.26814.03, CoreFX 4.6.26814.02), 64bit RyuJIT

 ; BenchmarkDotNet.Samples.IntroDisassembly.Sum()
-       fldz
+       vxorps  xmm0,xmm0,xmm0
        xor     eax,eax
 M00_L00:
-       mov     dword ptr [ebp-4],eax
-       fild    dword ptr [ebp-4]
-       faddp   st(1),st
+       vxorps  xmm1,xmm1,xmm1
+       vcvtsi2sd xmm1,xmm1,eax
+       vaddsd  xmm0,xmm0,xmm1
        inc     eax
        cmp     eax,40h
        jl      M00_L00
-       mov     esp,ebp
-; Total bytes of code 20
+       ret
+; Total bytes of code 30
 

@WojciechNagorski
Copy link
Contributor Author

File BenchmarkDotNet.Samples.IntroDisassemblyAllJits-asm.pretty.md:

Mono 5.16.0 (Visual Studio), 64bit

; CallVirtualMethod
       converting method int BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod ()
       created temp 0 (R16) of type System.Int32
       creating vars
       created temp 1 (R17) of type System.IntPtr
       	return :  arg R16 <-
       	this:  arg R17 <-
       creating locals
       locals done
       method to IR BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod ()
       converting (in B2: stack: 0) IL_0000: ldarg.0   
       converting (in B2: stack: 1) IL_0001: ldfld     0x04000005
       converting (in B2: stack: 1) IL_0006: ldc.i4.s  10
       converting (in B2: stack: 2) IL_0008: callvirt  0x060000fc
       INLINE START 000001DBB13B8AA0 BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod () -> BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)
       created temp 2 (R21) of type System.Int32
       method to IR BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)
       created temp 3 (R22) of type System.Object
       created temp 4 (R23) of type System.Int32
       converting (in B7: stack: 0) IL_0000: ldarg.0   
       converting (in B7: stack: 1) IL_0001: ldarg.0   
       converting (in B7: stack: 2) IL_0002: ldarg.1   
       converting (in B7: stack: 3) IL_0003: callvirt  0x060000fb
       inline failed: call
       INLINE ABORTED BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int) (cost -1) 
       converting (in B2: stack: 1) IL_000d: ret       
       REGION BB0 IL_0000 ID_FFFFFFFF
       REGION BB3 IL_0000 ID_FFFFFFFF
       REGION BB2 IL_000d ID_FFFFFFFF
       REGION BB1 IL_0000 ID_FFFFFFFF
       AFTER METHOD-TO-IR 0: [IN: , OUT:  BB3(0) ]
       AFTER METHOD-TO-IR 3: [IN:  BB0(0), OUT:  BB2(0) ]
       AFTER METHOD-TO-IR 2: [IN:  BB3(0), OUT:  BB1(0) ]
        il_seq_point il: 0x0
        move R18 <- R17
        load_membase R19 <- [R18 + 0x10]
        iconst R20 <- [10]
        move R29 <- R19
        move R30 <- R20
        checkthis [R19 + 0x0]
        not_null R19
        call R28 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
        move R16 <- R28
        br [B1]
       AFTER METHOD-TO-IR 1: [IN:  BB2(0), OUT:  ]
       CCOPY/2: R18 -> R17
       remove_block_if_useless, removed BB3
       br removal triggered 2 -> 1
       HANDLE-GLOBAL-VREGS BLOCK 0:
       HANDLE-GLOBAL-VREGS BLOCK 2:
        il_seq_point il: 0x0
        move R18 <- R17
        load_membase R19 <- [R17 + 0x10]
        iconst R20 <- [10]
        move R29 <- R19
        iconst R30 <- [10]
        checkthis [R19 + 0x0]
        not_null R19
        call R28 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
        move R16 <- R28
        nop
       HANDLE-GLOBAL-VREGS BLOCK 1:
       CONVERTED R21(2) TO VREG.
       CONVERTED R22(3) TO VREG.
       CONVERTED R23(4) TO VREG.
       	Reverse copyprop in BB2 on  move R16 <- R28
       BB0 IN: 
       BB2 IN: 0 
       BB1 IN: 2 
       DTREE BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod () 0
       BB0(dfn=0) (IDOM=BB-1):  BB0
       BB2(dfn=1) (IDOM=BB0):  BB0 BB2
       BB1(dfn=2) (IDOM=BB2):  BB0 BB2 BB1
       BEFORE LOWER-VTYPE-OPTS  0: [IN: , OUT:  BB2(1) ]
       AFTER LOWER-VTYPE-OPTS  0: [IN: , OUT:  BB2(1) ]
       BEFORE LOWER-VTYPE-OPTS  2: [IN:  BB0(0), OUT:  BB1(2) ]
        il_seq_point il: 0x0
        load_membase R19 <- [R17 + 0x10]
        move R29 <- R19
        iconst R30 <- [10]
        checkthis [R19 + 0x0]
        not_null R19
        call R16 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
       AFTER LOWER-VTYPE-OPTS  2: [IN:  BB0(0), OUT:  BB1(2) ]
        il_seq_point il: 0x0
        load_membase R19 <- [R17 + 0x10]
        move R29 <- R19
        iconst R30 <- [10]
        checkthis [R19 + 0x0]
        not_null R19
        call R16 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
       BEFORE LOWER-VTYPE-OPTS  1: [IN:  BB2(1), OUT:  ]
       AFTER LOWER-VTYPE-OPTS  1: [IN:  BB2(1), OUT:  ]
       LIVENESS:
       BLOCK BB0 (BB2, ):
       GEN  BB0: {}
       KILL BB0: {}
       BLOCK BB2 (BB1, ):
       	1  il_seq_point il: 0x0
       	1  load_membase R19 <- [R17 + 0x10]
       	GEN: R17(1)
       	1  move R29 <- R19
       	1  iconst R30 <- [10]
       	1  checkthis [R19 + 0x0]
       	1  not_null R19
       	1  call R16 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
       	KILL: R16(0)
       	1  il_seq_point il: 0xd, nonempty-stack
       	1  il_seq_point il: 0xd
       GEN  BB2: {1}
       KILL BB2: {0}
       BLOCK BB1 ():
       GEN  BB1: {}
       KILL BB1: {}
       ITERATION:
       P: BB1(2): IN: BB2 OUT:
       P: BB2(1): IN: BB0 OUT:BB1 
       	LIVE IN  BB2: {1}
       P: BB0(0): IN: OUT:BB2 
       	LIVE IN  BB0: {1}
       IT: 3 2.
       LIVE IN  BB1: {}
       LIVE OUT BB1: {}
       LIVE IN  BB2: {1}
       LIVE OUT BB2: {}
       LIVE IN  BB0: {1}
       LIVE OUT BB0: {1}
       V0: [0x0 - 0x4000f]
       V1: [0x0 - 0x40004]
       COSTLY: R1 C1 C1 %rsi
       NOT REGVAR: 1
       SPILL BLOCK 0:
       SPILL BLOCK 2:
        il_seq_point il: 0x0
       	     -1
       	1  il_seq_point il: 0x0
        load_membase R19 <- [R17 + 0x10]
       	 ii  19 17
       	1  load_membase R19 <- [R31 + 0x10]
        move R29 <- R19
       	 ii  29 19
       	1  move R29 <- R19
        iconst R30 <- [10]
       	 i   30
       	1  iconst R30 <- [10]
        checkthis [R19 + 0x0]
       	  i  -1 19
       	1  checkthis [R19 + 0x0]
        not_null R19
       	  i  -1 19
       	1  not_null R19
        call R16 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
       	 i   16
       	1  call %rax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
       	     -1
       	1  il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
       	     -1
       	1  il_seq_point il: 0xd
       SPILL BLOCK 1:
       DUMP BLOCK 0:
       DUMP BLOCK 2:
        il_seq_point il: 0x0
        load_membase R31 <- [%rbp + 0xfffffff8]
        load_membase R19 <- [R31 + 0x10]
        move R29 <- R19
        iconst R30 <- [10]
        checkthis [R19 + 0x0]
        not_null R19
        call %rax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
       DUMP BLOCK 1:
       LOCAL REGALLOC BLOCK 2:
       	1  il_seq_point il: 0x0
       	2  load_membase R31 <- [%rbp + 0xfffffff8]
       	3  load_membase R19 <- [R31 + 0x10]
       	4  move R29 <- R19
       	5  iconst R30 <- [10]
       	6  checkthis [R19 + 0x0]
       	7  not_null R19
       	8  call %rax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
       	9  il_seq_point il: 0xd, nonempty-stack
       	10 il_seq_point il: 0xd
       liveness: %rax [8 - 8]
       liveness: R19 [3 - 3]
       liveness: R29 [4 - 4]
       liveness: R30 [5 - 5]
       liveness: R31 [2 - 2]
       processing:	10 il_seq_point il: 0xd
       	10 il_seq_point il: 0xd
       processing:	9  il_seq_point il: 0xd, nonempty-stack
       	9  il_seq_point il: 0xd, nonempty-stack
       processing:	8  call %rax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
       	assigned arg reg %rcx to R29
       	assigned arg reg %rdx to R30
       	8  call %rax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] [%rcx <- R29] [%rdx <- R30] clobbers: c
       processing:	7  not_null R19
       	assigned sreg1 %rax to R19
       	7  not_null %rax
       processing:	6  checkthis [R19 + 0x0]
       	6  checkthis [%rax + 0x0]
       processing:	5  iconst R30 <- [10]
       	assigned dreg %rdx to dest R30
       	freeable %rdx (R30) (born in 5)
       	5  iconst %rdx <- [10]
       processing:	4  move R29 <- R19
       	assigned dreg %rcx to dest R29
       	freeable %rcx (R29) (born in 4)
       	4  move %rcx <- %rax
       processing:	3  load_membase R19 <- [R31 + 0x10]
       	assigned dreg %rax to dest R19
       	freeable %rax (R19) (born in 3)
       	assigned sreg1 %rax to R31
       	3  load_membase %rax <- [%rax + 0x10]
       processing:	2  load_membase R31 <- [%rbp + 0xfffffff8]
       	assigned dreg %rax to dest R31
       	freeable %rax (R31) (born in 2)
       	2  load_membase %rax <- [%rbp + 0xfffffff8]
       processing:	1  il_seq_point il: 0x0
       	1  il_seq_point il: 0x0
       CFA: [0] def_cfa: %rsp+0x8
       CFA: [0] offset: unknown at cfa-0x8
       CFA: [1] def_cfa_offset: 0x10
       CFA: [1] offset: %rbp at cfa-0x10
       CFA: [4] def_cfa_reg: %rbp
       Basic block 0 starting at offset 0xc
       Basic block 2 starting at offset 0xc
       Basic block 1 starting at offset 0x2b
       CFA: [30] def_cfa: %rsp+0x8
       'as' is not recognized as an internal or external command,
       operable program or batch file.
       Method int BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod () emitted at 000001DBB5131290 to 000001DBB51312C1 (code length 49) [BenchmarkDotNet.Samples.dll]
       'x86_64-w64-mingw32-objdump.exe' is not recognized as an internal or external command,
       operable program or batch file.
; Total bytes of code 0

Mono 5.16.0 (Visual Studio), 32bit

; CallVirtualMethod
       converting method int BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod ()
       created temp 0 (R8) of type System.Int32
       creating vars
       created temp 1 (R9) of type System.IntPtr
       	return :  arg R8 <-
       	this:  arg R9 <-
       creating locals
       locals done
       method to IR BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod ()
       converting (in B2: stack: 0) IL_0000: ldarg.0   
       converting (in B2: stack: 1) IL_0001: ldfld     0x04000005
       converting (in B2: stack: 1) IL_0006: ldc.i4.s  10
       converting (in B2: stack: 2) IL_0008: callvirt  0x060000fc
       INLINE START 02C85990 BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod () -> BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)
       created temp 2 (R13) of type System.Int32
       method to IR BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)
       created temp 3 (R14) of type System.Object
       created temp 4 (R15) of type System.Int32
       converting (in B7: stack: 0) IL_0000: ldarg.0   
       converting (in B7: stack: 1) IL_0001: ldarg.0   
       converting (in B7: stack: 2) IL_0002: ldarg.1   
       converting (in B7: stack: 3) IL_0003: callvirt  0x060000fb
       inline failed: call
       INLINE ABORTED BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int) (cost -1) 
       converting (in B2: stack: 1) IL_000d: ret       
       REGION BB0 IL_0000 ID_FFFFFFFF
       REGION BB3 IL_0000 ID_FFFFFFFF
       REGION BB2 IL_000d ID_FFFFFFFF
       REGION BB1 IL_0000 ID_FFFFFFFF
       AFTER METHOD-TO-IR 0: [IN: , OUT:  BB3(0) ]
       AFTER METHOD-TO-IR 3: [IN:  BB0(0), OUT:  BB2(0) ]
       AFTER METHOD-TO-IR 2: [IN:  BB3(0), OUT:  BB1(0) ]
        il_seq_point il: 0x0
        move R10 <- R9
        load_membase R11 <- [R10 + 0x8]
        iconst R12 <- [10]
        store_membase_reg [%esp + 0x4] <- R12
        store_membase_reg [%esp] <- R11
        checkthis [R11 + 0x0]
        not_null R11
        call R20 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
        move R8 <- R20
        br [B1]
       AFTER METHOD-TO-IR 1: [IN:  BB2(0), OUT:  ]
       CCOPY/2: R10 -> R9
       remove_block_if_useless, removed BB3
       br removal triggered 2 -> 1
       HANDLE-GLOBAL-VREGS BLOCK 0:
       HANDLE-GLOBAL-VREGS BLOCK 2:
        il_seq_point il: 0x0
        move R10 <- R9
        load_membase R11 <- [R9 + 0x8]
        iconst R12 <- [10]
        store_membase_imm [%esp + 0x4] <- [10]
        store_membase_reg [%esp] <- R11
        checkthis [R11 + 0x0]
        not_null R11
        call R20 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
        move R8 <- R20
        nop
       HANDLE-GLOBAL-VREGS BLOCK 1:
       CONVERTED R13(2) TO VREG.
       CONVERTED R14(3) TO VREG.
       CONVERTED R15(4) TO VREG.
       	Reverse copyprop in BB2 on  move R8 <- R20
       BB0 IN: 
       BB2 IN: 0 
       BB1 IN: 2 
       DTREE BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod () 0
       BB0(dfn=0) (IDOM=BB-1):  BB0
       BB2(dfn=1) (IDOM=BB0):  BB0 BB2
       BB1(dfn=2) (IDOM=BB2):  BB0 BB2 BB1
       BEFORE LOWER-VTYPE-OPTS  0: [IN: , OUT:  BB2(1) ]
       AFTER LOWER-VTYPE-OPTS  0: [IN: , OUT:  BB2(1) ]
       BEFORE LOWER-VTYPE-OPTS  2: [IN:  BB0(0), OUT:  BB1(2) ]
        il_seq_point il: 0x0
        load_membase R11 <- [R9 + 0x8]
        store_membase_imm [%esp + 0x4] <- [10]
        store_membase_reg [%esp] <- R11
        checkthis [R11 + 0x0]
        not_null R11
        call R8 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
       AFTER LOWER-VTYPE-OPTS  2: [IN:  BB0(0), OUT:  BB1(2) ]
        il_seq_point il: 0x0
        load_membase R11 <- [R9 + 0x8]
        store_membase_imm [%esp + 0x4] <- [10]
        store_membase_reg [%esp] <- R11
        checkthis [R11 + 0x0]
        not_null R11
        call R8 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
       BEFORE LOWER-VTYPE-OPTS  1: [IN:  BB2(1), OUT:  ]
       AFTER LOWER-VTYPE-OPTS  1: [IN:  BB2(1), OUT:  ]
       LIVENESS:
       BLOCK BB0 (BB2, ):
       GEN  BB0: {}
       KILL BB0: {}
       BLOCK BB2 (BB1, ):
       	1  il_seq_point il: 0x0
       	1  load_membase R11 <- [R9 + 0x8]
       	GEN: R9(1)
       	1  store_membase_imm [%esp + 0x4] <- [10]
       'as' is not recognized as an internal or external command,
       operable program or batch file.
       	1  store_membase_reg [%esp] <- R11
       	1  checkthis [R11 + 0x0]
       	1  not_null R11
       	1  call R8 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
       	KILL: R8(0)
       	1  il_seq_point il: 0xd, nonempty-stack
       	1  il_seq_point il: 0xd
       GEN  BB2: {1}
       KILL BB2: {0}
       BLOCK BB1 ():
       GEN  BB1: {}
       KILL BB1: {}
       ITERATION:
       P: BB1(2): IN: BB2 OUT:
       P: BB2(1): IN: BB0 OUT:BB1 
       	LIVE IN  BB2: {1}
       P: BB0(0): IN: OUT:BB2 
       	LIVE IN  BB0: {1}
       IT: 3 2.
       LIVE IN  BB1: {}
       LIVE OUT BB1: {}
       LIVE IN  BB2: {1}
       LIVE OUT BB2: {}
       LIVE IN  BB0: {1}
       LIVE OUT BB0: {1}
       V0: [0x0 - 0x4000f]
       V1: [0x0 - 0x40004]
       COSTLY: R1 C1 C3 %edi
       NOT REGVAR: 1
       SPILL BLOCK 0:
       SPILL BLOCK 2:
        il_seq_point il: 0x0
       	     -1
       	1  il_seq_point il: 0x0
        load_membase R11 <- [R9 + 0x8]
       	 ii  11 9
       	1  load_membase R11 <- [R21 + 0x8]
        store_membase_imm [%esp + 0x4] <- [10]
       	   i -1
       	1  store_membase_imm [%esp + 0x4] <- [10]
        store_membase_reg [%esp] <- R11
       	  ii -1 11
       	1  store_membase_reg [%esp] <- R11
        checkthis [R11 + 0x0]
       	  i  -1 11
       	1  checkthis [R11 + 0x0]
        not_null R11
       	  i  -1 11
       	1  not_null R11
        call R8 <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
       	 i   8
       	1  call %eax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
       	     -1
       	1  il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
       	     -1
       	1  il_seq_point il: 0xd
       SPILL BLOCK 1:
       DUMP BLOCK 0:
       DUMP BLOCK 2:
        il_seq_point il: 0x0
        load_membase R21 <- [%ebp + 0x8]
        load_membase R11 <- [R21 + 0x8]
        store_membase_imm [%esp + 0x4] <- [10]
        store_membase_reg [%esp] <- R11
        checkthis [R11 + 0x0]
        not_null R11
        call %eax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
        il_seq_point il: 0xd, nonempty-stack
        il_seq_point il: 0xd
       DUMP BLOCK 1:
       LOCAL REGALLOC BLOCK 2:
       	1  il_seq_point il: 0x0
       	2  load_membase R21 <- [%ebp + 0x8]
       	3  load_membase R11 <- [R21 + 0x8]
       	4  store_membase_imm [%esp + 0x4] <- [10]
       	5  store_membase_reg [%esp] <- R11
       	6  checkthis [R11 + 0x0]
       	7  not_null R11
       	8  call %eax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
       	9  il_seq_point il: 0xd, nonempty-stack
       	10 il_seq_point il: 0xd
       liveness: %eax [8 - 8]
       liveness: %esp [4 - 0]
       liveness: R11 [3 - 3]
       liveness: R21 [2 - 2]
       processing:	10 il_seq_point il: 0xd
       	10 il_seq_point il: 0xd
       processing:	9  il_seq_point il: 0xd, nonempty-stack
       	9  il_seq_point il: 0xd, nonempty-stack
       processing:	8  call %eax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
       	8  call %eax <- [BenchmarkDotNet.Samples.IntroDisassemblyAllJits/Operation:OperateTwice (int)] clobbers: c
       processing:	7  not_null R11
       	assigned sreg1 %eax to R11
       	7  not_null %eax
       processing:	6  checkthis [R11 + 0x0]
       	6  checkthis [%eax + 0x0]
       processing:	5  store_membase_reg [%esp] <- R11
       	5  store_membase_reg [%esp] <- %eax
       processing:	4  store_membase_imm [%esp + 0x4] <- [10]
       	4  store_membase_imm [%esp + 0x4] <- [10]
       processing:	3  load_membase R11 <- [R21 + 0x8]
       	assigned dreg %eax to dest R11
       	freeable %eax (R11) (born in 3)
       	assigned sreg1 %eax to R21
       	3  load_membase %eax <- [%eax + 0x8]
       processing:	2  load_membase R21 <- [%ebp + 0x8]
       	assigned dreg %eax to dest R21
       	freeable %eax (R21) (born in 2)
       	2  load_membase %eax <- [%ebp + 0x8]
       processing:	1  il_seq_point il: 0x0
       	1  il_seq_point il: 0x0
       CFA: [0] def_cfa: %esp+0x4
       CFA: [0] offset: unknown at cfa-0x4
       CFA: [1] def_cfa_offset: 0x8
       CFA: [1] offset: %ebp at cfa-0x8
       CFA: [3] def_cfa_reg: %ebp
       Basic block 0 starting at offset 0x6
       Basic block 2 starting at offset 0x6
       Basic block 1 starting at offset 0x20
       Method int BenchmarkDotNet.Samples.IntroDisassemblyAllJits:CallVirtualMethod () emitted at 05EB1198 to 05EB11BA (code length 34) [BenchmarkDotNet.Samples.dll]
       'objdump' is not recognized as an internal or external command,
       operable program or batch file.
; Total bytes of code 0

.NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0

; BenchmarkDotNet.Samples.IntroDisassemblyAllJits.CallVirtualMethod()
       mov     rax,qword ptr [rcx+8]
       mov     eax,dword ptr [rax+8]
       lea     edx,[rax+0Ah]
       add     eax,edx
       ret
       add     byte ptr [rax],al
       add     byte ptr [rcx],bl
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       sbb     byte ptr [rax-80h],al
       adc     cl,bh
       jg      M00_L00
M00_L00:
       add     bl,al
       add     byte ptr [rax],al
       add     byte ptr [rcx],bl
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
; Total bytes of code 44

Method got most probably inlined
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.OperateTwice(Int32)
Abstract method
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.Operate(Int32)

.NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0

; BenchmarkDotNet.Samples.IntroDisassemblyAllJits.CallVirtualMethod()
       push    ebp
       mov     ebp,esp
       push    esi
       mov     esi,dword ptr [ecx+4]
       mov     ecx,esi
       mov     edx,0Ah
       mov     eax,dword ptr [ecx]
       mov     eax,dword ptr [eax+28h]
       call    dword ptr [eax+10h]
       mov     edx,eax
       mov     ecx,esi
       mov     eax,dword ptr [ecx]
       mov     eax,dword ptr [eax+28h]
       call    dword ptr [eax+10h]
       pop     esi
       pop     ebp
       ret
       add     byte ptr [eax],al
       add     byte ptr [eax],al
       add     byte ptr [eax],al
       add     byte ptr [eax],al
       add     byte ptr [eax],al
       add     ah,dl
       dec     esi
       cmc
       push    es
       add     byte ptr [eax],al
       add     byte ptr [eax],al
       int     3
       dec     esi
       cmc
       push    es
       pop     esp
       jmp     518B:C28B06B4
; Total bytes of code 68

Method got most probably inlined
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.OperateTwice(Int32)
Abstract method
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.Operate(Int32)

.NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0

; BenchmarkDotNet.Samples.IntroDisassemblyAllJits.CallVirtualMethod()
       mov     rax,qword ptr [rcx+8]
       mov     eax,dword ptr [rax+8]
       lea     edx,[rax+0Ah]
       add     eax,edx
       ret
       add     byte ptr [rax],al
       add     byte ptr [rcx],bl
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       sbb     byte ptr [rax+7Eh],al
       adc     cl,bh
       jg      M00_L00
M00_L00:
       add     bl,al
       add     byte ptr [rax],al
       add     byte ptr [rcx],bl
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
; Total bytes of code 44

Method got most probably inlined
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.OperateTwice(Int32)
Abstract method
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.Operate(Int32)

.NET Core 2.0.9 (CoreCLR 4.6.26614.01, CoreFX 4.6.26614.01), 64bit RyuJIT

; BenchmarkDotNet.Samples.IntroDisassemblyAllJits.CallVirtualMethod()
       push    rsi
       sub     rsp,20h
       mov     rsi,qword ptr [rcx+8]
       mov     rcx,rsi
       mov     edx,0Ah
       mov     rax,qword ptr [rsi]
       mov     rax,qword ptr [rax+40h]
       call    qword ptr [rax+20h]
       mov     edx,eax
       mov     rcx,rsi
       mov     rax,qword ptr [rsi]
       mov     rax,qword ptr [rax+40h]
       mov     rax,qword ptr [rax+20h]
       add     rsp,20h
       pop     rsi
       jmp     rax
       add     byte ptr [rcx],bl
       add     eax,32050002h
       add     dword ptr [rax+3Eh],esp
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax-73AA3DDh],cl
; Total bytes of code 77

Method got most probably inlined
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.OperateTwice(Int32)
Abstract method
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.Operate(Int32)

.NET Core 2.1.4 (CoreCLR 4.6.26814.03, CoreFX 4.6.26814.02), 64bit RyuJIT

; BenchmarkDotNet.Samples.IntroDisassemblyAllJits.CallVirtualMethod()
       mov     rax,qword ptr [rcx+8]
       mov     eax,dword ptr [rax+8]
       lea     edx,[rax+0Ah]
       add     eax,edx
       ret
       add     byte ptr [rax],al
       add     byte ptr [rcx],bl
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       jo      00007ff9`0c41295f
       push    rbp
       or      al,0F9h
       jg      M00_L00
M00_L00:
       add     bl,al
       add     byte ptr [rax],al
       add     byte ptr [rcx],bl
       add     byte ptr [rax],al
       add     byte ptr [rax],al
       add     byte ptr [rax],al
; Total bytes of code 44

No ILOffsetMap found
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.OperateTwice(Int32)
BenchmarkDotNet.Samples.IntroDisassemblyAllJits+Operation.Operate(Int32)

@WojciechNagorski
Copy link
Contributor Author

@adamsitnik When will you be able to look at this?

Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

@wojtpl2 another great PR! I am truly impressed!

I had few comments, mostly modulo ones.

Please change the behavior so we produce the diff only for the same benchmark executed for multiple runtimes. As soon as we have that and the CI is green, I merge it.

And big thanks for solving the file name conflict issue!

public string Name => nameof(PrettyHtmlDisassemblyExporter);

public void ExportToLog(Summary summary, ILogger logger) { }
private static int referenceIndex;
Copy link
Member

Choose a reason for hiding this comment

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

I don't like the idea of introducing a static state. Could you please create a local variable with the same name in ExportToLog and pass it via reference to the Export method?

{
var benchmarksCases = summary.BenchmarksCases
Copy link
Member

Choose a reason for hiding this comment

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

nit: this local variable is a good candidate for inlining.

so it would be just:

- foreach (var benchmarkCase in benchmarksCases)
+ foreach (var benchmarkCase in summary.BenchmarksCases).Where(results.ContainsKey))


public void ExportToLog(Summary summary, ILogger logger) { }
public RawDisassemblyExporter(IReadOnlyDictionary<BenchmarkCase, DisassemblyResult> results)
{
Copy link
Member

Choose a reason for hiding this comment

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

nit: please keep =>


using (var stream = StreamWriter.FromPath(filePath))
foreach (var benchmarkCase in benchmarksCases)
Copy link
Member

Choose a reason for hiding this comment

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

nit: please inline benchmarksCases

@@ -141,5 +135,6 @@ internal static string FormatMethodAddress(ulong nativeCode)

return buffer.ToString();
}
private static string GetImportantInfo(BenchmarkReport benchmarkReport) => benchmarkReport.GetRuntimeInfo();
Copy link
Member

Choose a reason for hiding this comment

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

nit: if this method is called only from one place, then it should also be inlined


namespace BenchmarkDotNet.Exporters
{
public abstract class PrettyGithubMarkdownDisassemblyExporterBase : ExporterBase
Copy link
Member

Choose a reason for hiding this comment

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

I am guilty of creating too many classes and too deep inheritance hierarchy in BenchmarkDotNet. We should not be doing this anymore (Composition over inheritance).

Could you put this method back to PrettyGithubMarkdownDisassemblyExporter and do the following;

  1. make it static internal
  2. make PrettyGithubMarkdownDisassemblyExporter and PrettyGithubMarkdownDiffDisassemblyExporter inherit directly from ExporterBase
  3. reuse PrettyGithubMarkdownDisassemblyExporter.Export in PrettyGithubMarkdownDiffDisassemblyExporter


private static void RunGitDiff(string firstFile, string secondFile, StringBuilder result)
{
var startInfo = new ProcessStartInfo
Copy link
Member

Choose a reason for hiding this comment

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

You should be able to reuse ProcessHelper.RunAndReadOutput here and simplify the code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can reuse ProcessHelper.RunAndReadOutputLineByLine because ProcessHelper.RunAndReadOutput doesn't throw exception in case git is not installed on the system. There is a problem:

try
{
process.Start();
}
catch (Exception)
{
return null;
}

I can refactor ProcessHelper but I think it should be a different task.

public override void ExportToLog(Summary summary, ILogger logger)
{
var benchmarksCases = summary.BenchmarksCases
.Where(results.ContainsKey).ToList();
Copy link
Member

Choose a reason for hiding this comment

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

nit: if you don't intend to add any elements to a materialized collection, you should prefer array over list. The best choice would be ImmutableArray - materialized, fast and immutable

protected override string FileCaption => "asm.pretty.diff";

public PrettyGithubMarkdownDiffDisassemblyExporter(IReadOnlyDictionary<BenchmarkCase, DisassemblyResult> results)
{
Copy link
Member

Choose a reason for hiding this comment

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

nit: =>


RunGitDiff(firstFileName, secondFileName, builder);

if (firstBenchmarkCase.Descriptor.WorkloadMethod == secondBenchmarkCase.Descriptor.WorkloadMethod) // diff between the same method for different JITs
Copy link
Member

Choose a reason for hiding this comment

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

I think that this is the only case when we should be producing the diff if we want to have it enabled by default.

Example: a ListBenchmarks type with 3 benchmarks: Add, AddRange and Insert. When we use the disassembly diagnoser we want to get the disassembly for all 3 of them. But we are rather not interested in diff.
When we run it for few different Runtime settings, then we want to get the diff.

@adamsitnik adamsitnik merged commit 1903a1b into dotnet:master Oct 30, 2018
@adamsitnik
Copy link
Member

@wojtpl2 excellent! thank you!!

@adamsitnik adamsitnik added this to the v0.11.2 milestone Oct 30, 2018
@WojciechNagorski WojciechNagorski deleted the issue_544 branch October 30, 2018 10:45
@WojciechNagorski
Copy link
Contributor Author

WojciechNagorski commented Oct 30, 2018

@adamsitnik I can take another task. Do you have any preferences? Maybe this #678 ?

In this task I found a weak point.
When you run: BenchmarkDotNet.Samples --filter *IntroDisassemblyRyuJit*
Then BDN generate file BenchmarkDotNet.Samples.IntroDisassemblyRyuJit-asm.pretty.diff.md without diff.
BenchmarkDotNet.Samples.IntroDisassemblyRyuJit-asm.pretty.diff.txt

This is because ExporterBasefirst creates a file and then calls 'ExportToLog'

using (var stream = StreamWriter.FromPath(filePath))
{
ExportToLog(summary, new StreamLogger(stream));
}

@adamsitnik
Copy link
Member

@wojtpl2 I have an idea: maybe producing the diff should be on demand? You could extend the DisassemblyDiagnoserConfig with new flag and expose it via command line args in similar way to disasmDepth that way we could produce the diff only when the user asks for it.

@WojciechNagorski
Copy link
Contributor Author

Great idea. Ok, I'll do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants