-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Cannot set breakpoint in Object Initializer #27184
Comments
It's not currently possible to set breakpoints in expressions. Relates to #22016 |
@Ciwan1859 the original issue's link is dead. Please include a complete description of the issue. |
See #42711 for the related switch expression work for reference when implementing this. |
I'm not sure I understand the request, and I'm not confident this would be useful as I imagine it is being requested. If the idea is being able to set breakpoints on the individual initializers of an object initializer, why would you want that? It is straight-line code. To the extent that it is not straight-line code (e.g. If the idea is to improve stack traces when an exception is thrown in the initializer, I'm not sure the code degradation required to support that is worth it (we have to spill the stack at every sequence point). I'm moving this to the back burner so we can investigate the options and possible benefits. |
@gafter that is fine, thank you. I've lost the original gif that I had. I'm really sorry about the dead-link :( |
For me it's about one thing: being able to step in to calls both on the RHS and LHS of the assignments. That's very hard to do if you can't step over to the right line before stepping in. |
@jnm2 Do you know that the debugger allows to "step into a specific method"? At a given breakpoint, you can invoke that command, it will display all the methods that could be invoked from there (different parts of the expression) and you can choose which one to step into. According to my custom bindings, the command is |
@jcouv Yes. The 'Step into' submenu doesn't always appear when I expect it to, but I do use it regularly. Next time I run into an object initializer I'll look for this. |
@gafter @jcouv Here's another scenario I've just remembered by running into it unexpectedly: If an exception is thrown, there is no way to see which line of the object creation expression was responsible. I have to search each line and try to see which ones could potentially have thrown the exception. |
I think this exception scenario gets to the root of most of what I've heard from folks that keeps them from using multiline expressions in general, including chaining of |
The quality of stack traces is not necessarily the same as where you can set breakpoints, which is not necessarily the same as where the debugger stops as you're single-stepping. Today we combine the three at statement boundaries, but that need not necessarily be the case in the future. The difficulty of stack trace quality occurs for any complex expression with multiple parts. It is not specific to object initializers. For example, the same issue occurs with chained calls written in a fluent style. Helping with the quality of stack traces needn't be done by making the debugger single-step through each call. Since there is no description of the original issue here, it is hard to tell what is being requested. |
+1 for what Neal says here:
@jnm2 In what scenarios is it a problem to not see from the call stack exactly what method call caused the exception? |
@tmat a simple example would just be: new T
{
A = a.B.C.D,
B = e.F.G.D,
C = h.H,
L = c.D.E
}; // NullReferenceException on this line isn't helpful... at all EDIT: And when you're working with large class structures generated by XSD.exe and you fall into the trap of taking VS suggestion to "simplify" that code (by going from statements to object initializer), then you're in debugging hell once stacktraces hit production. Just to clarify, this can easily exceed 20 lines. Being able to step through each assignment would be nice to have but i'd accept only getting better stacktraces as a compromise. |
We would not change how we generate sequence points in production (release) builds as that would regress performance. Besides the stack traces in release builds will be off anyways since most of your properties in your example above would be inlined. |
As @gafter pointed out those are two different things. Why would it be nice to step on each assignment (in debug builds)? They all happen unconditionally. So what new information would you get from one step to another? |
@tmat The following also happens unconditionally yet i can step over each of them. It would be nice to have symmetry but i also just step through the code until it blows up, e.g. until it hits the line that causes the NullReferenceException. A();
B();
C(); Bonus points if i could inspect the class that is being constructed by hovering the EDIT: Also i prefer the step over functionality over the step into specific, which, to be honest, i know exists, just never think of it when stepping through the code... Maybe i can get used to it with the keyboard shortcut (didn't know there was one, for me it was always in the right click context menu which just interrupted my debugging workflow so i usually went step into, step out, until i got where i want to be) |
I cannot confirm this using System;
using System.Runtime.CompilerServices;
namespace ConsoleApp80
{
class Program
{
static void Main(string[] args)
{
var a = new A { B = new B() };
try
{
Test(a);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
try
{
Test2(a);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static Test Test(A a)
{
return new Test()
{
A = a,
B = a.B,
C = a.B.C,
Value = a.B.C.Value,
};
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Test Test2(A a)
{
Test test = new Test();
test.A = a;
test.B = a.B;
test.C = a.B.C;
test.Value = a.B.C.Value;
return test;
}
}
class A
{
public B B { get; set; }
}
class B
{
public C C { get; set; }
}
class C
{
public string Value { get; set; }
}
class Test
{
public A A { get; set; }
public B B { get; set; }
public C C { get; set; }
public string Value { get; set; }
}
} Object Initializer; Assembly listing for method Program:Test(A):Test
; Emitting BLENDED_CODE for X64 CPU with AVX - Windows
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T01] ( 4, 4 ) ref -> rsi class-hnd
; V01 OutArgs [V01 ] ( 1, 1 ) lclBlk (32) [rsp+0x00] "OutgoingArgSpace"
; V02 tmp1 [V02,T00] ( 6, 12 ) ref -> rdi class-hnd exact "NewObj constructor temp"
; V03 tmp2 [V03,T03] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
; V04 tmp3 [V04,T04] ( 2, 4 ) ref -> rsi class-hnd "Inlining Arg"
; V05 tmp4 [V05,T05] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
;* V06 tmp5 [V06,T09] ( 0, 0 ) ref -> zero-ref class-hnd "Inlining Arg"
; V07 tmp6 [V07,T06] ( 2, 4 ) ref -> rsi class-hnd "Inlining Arg"
; V08 tmp7 [V08,T07] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
; V09 cse0 [V09,T02] ( 3, 3 ) ref -> rsi "CSE - aggressive"
; V10 cse1 [V10,T08] ( 3, 3 ) ref -> rsi "CSE - aggressive"
;
; Lcl frame size = 40
G_M206_IG01:
push rdi
push rsi
sub rsp, 40
mov rsi, rcx
;; bbWeight=1 PerfScore 2.50
G_M206_IG02:
mov rcx, 0xD1FFAB1E
call CORINFO_HELP_NEWSFAST
mov rdi, rax
lea rcx, bword ptr [rdi+8]
mov rdx, rsi
call CORINFO_HELP_ASSIGN_REF
mov rsi, gword ptr [rsi+8]
mov rdx, rsi
lea rcx, bword ptr [rdi+16]
call CORINFO_HELP_ASSIGN_REF
mov rsi, gword ptr [rsi+8]
mov rdx, rsi
lea rcx, bword ptr [rdi+24]
call CORINFO_HELP_ASSIGN_REF
mov rdx, gword ptr [rsi+8]
lea rcx, bword ptr [rdi+32]
call CORINFO_HELP_ASSIGN_REF
mov rax, rdi
;; bbWeight=1 PerfScore 14.50
G_M206_IG03:
add rsp, 40
pop rsi
pop rdi
ret
;; bbWeight=1 PerfScore 2.25
; Total bytes of code 94, prolog size 6, PerfScore 28.65, (MethodHash=d51eff31) for method Program:Test(A):Test
; ============================================================ Assignments; Assembly listing for method Program:Test2(A):Test
; Emitting BLENDED_CODE for X64 CPU with AVX - Windows
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T01] ( 4, 4 ) ref -> rsi class-hnd
; V01 OutArgs [V01 ] ( 1, 1 ) lclBlk (32) [rsp+0x00] "OutgoingArgSpace"
; V02 tmp1 [V02,T00] ( 6, 12 ) ref -> rdi class-hnd exact "NewObj constructor temp"
; V03 tmp2 [V03,T03] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
; V04 tmp3 [V04,T04] ( 2, 4 ) ref -> rsi class-hnd "Inlining Arg"
; V05 tmp4 [V05,T05] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
;* V06 tmp5 [V06,T09] ( 0, 0 ) ref -> zero-ref class-hnd "Inlining Arg"
; V07 tmp6 [V07,T06] ( 2, 4 ) ref -> rsi class-hnd "Inlining Arg"
; V08 tmp7 [V08,T07] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
; V09 cse0 [V09,T02] ( 3, 3 ) ref -> rsi "CSE - aggressive"
; V10 cse1 [V10,T08] ( 3, 3 ) ref -> rsi "CSE - aggressive"
;
; Lcl frame size = 40
G_M42780_IG01:
push rdi
push rsi
sub rsp, 40
mov rsi, rcx
;; bbWeight=1 PerfScore 2.50
G_M42780_IG02:
mov rcx, 0xD1FFAB1E
call CORINFO_HELP_NEWSFAST
mov rdi, rax
lea rcx, bword ptr [rdi+8]
mov rdx, rsi
call CORINFO_HELP_ASSIGN_REF
mov rsi, gword ptr [rsi+8]
mov rdx, rsi
lea rcx, bword ptr [rdi+16]
call CORINFO_HELP_ASSIGN_REF
mov rsi, gword ptr [rsi+8]
mov rdx, rsi
lea rcx, bword ptr [rdi+24]
call CORINFO_HELP_ASSIGN_REF
mov rdx, gword ptr [rsi+8]
lea rcx, bword ptr [rdi+32]
call CORINFO_HELP_ASSIGN_REF
mov rax, rdi
;; bbWeight=1 PerfScore 14.50
G_M42780_IG03:
add rsp, 40
pop rsi
pop rdi
ret
;; bbWeight=1 PerfScore 2.25
; Total bytes of code 94, prolog size 6, PerfScore 28.65, (MethodHash=cd2b58e3) for method Program:Test2(A):Test
; ============================================================ Unfortunately though, it seems that the improved JIT optimizations in .NET Core lead to the line numbers being scrambled for the assignments too. When you suppress JIT optimizations with Object Initializer; Assembly listing for method Program:Test(A):Test
; Emitting BLENDED_CODE for X64 CPU with AVX - Windows
; MinOpts code
; rbp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00 ] ( 1, 1 ) ref -> [rbp+0x10] class-hnd
; V01 OutArgs [V01 ] ( 1, 1 ) lclBlk (32) [rsp+0x00] "OutgoingArgSpace"
; V02 tmp1 [V02 ] ( 1, 1 ) ref -> [rbp-0x08] must-init class-hnd exact "NewObj constructor temp"
; V03 tmp2 [V03 ] ( 1, 1 ) ref -> [rbp-0x10] must-init "argument with side effect"
; V04 tmp3 [V04 ] ( 1, 1 ) ref -> [rbp-0x18] must-init "argument with side effect"
; V05 tmp4 [V05 ] ( 1, 1 ) ref -> [rbp-0x20] must-init "argument with side effect"
;
; Lcl frame size = 64
G_M206_IG01:
push rbp
sub rsp, 64
lea rbp, [rsp+40H]
vxorps xmm4, xmm4
vmovdqa xmmword ptr [rbp-20H], xmm4
vmovdqa xmmword ptr [rbp-10H], xmm4
mov gword ptr [rbp+10H], rcx
;; bbWeight=1 PerfScore 5.08
G_M206_IG02:
mov rcx, 0xD1FFAB1E
call CORINFO_HELP_NEWSFAST
mov gword ptr [rbp-08H], rax
mov rcx, gword ptr [rbp-08H]
call Test:.ctor():this
mov rcx, gword ptr [rbp-08H]
mov rdx, gword ptr [rbp+10H]
cmp dword ptr [rcx], ecx
call Test:set_A(A):this
mov rcx, gword ptr [rbp+10H]
cmp dword ptr [rcx], ecx
call A:get_B():B:this
mov gword ptr [rbp-10H], rax
mov rdx, gword ptr [rbp-10H]
mov rcx, gword ptr [rbp-08H]
cmp dword ptr [rcx], ecx
call Test:set_B(B):this
mov rcx, gword ptr [rbp+10H]
cmp dword ptr [rcx], ecx
call A:get_B():B:this
mov rcx, rax
cmp dword ptr [rcx], ecx
call B:get_C():C:this
mov gword ptr [rbp-18H], rax
mov rdx, gword ptr [rbp-18H]
mov rcx, gword ptr [rbp-08H]
cmp dword ptr [rcx], ecx
call Test:set_C(C):this
mov rcx, gword ptr [rbp+10H]
cmp dword ptr [rcx], ecx
call A:get_B():B:this
mov rcx, rax
cmp dword ptr [rcx], ecx
call B:get_C():C:this
mov rcx, rax
cmp dword ptr [rcx], ecx
call C:get_Value():String:this
mov gword ptr [rbp-20H], rax
mov rdx, gword ptr [rbp-20H]
mov rcx, gword ptr [rbp-08H]
cmp dword ptr [rcx], ecx
call Test:set_Value(String):this
mov rax, gword ptr [rbp-08H]
;; bbWeight=1 PerfScore 50.00
G_M206_IG03:
lea rsp, [rbp]
pop rbp
ret
;; bbWeight=1 PerfScore 2.00
; Total bytes of code 201, prolog size 24, PerfScore 77.48, (MethodHash=d51eff31) for method Program:Test(A):Test
; ============================================================ Assignments; Assembly listing for method Program:Test2(A):Test
; Emitting BLENDED_CODE for X64 CPU with AVX - Windows
; MinOpts code
; rbp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00 ] ( 1, 1 ) ref -> [rbp+0x10] class-hnd
; V01 OutArgs [V01 ] ( 1, 1 ) lclBlk (32) [rsp+0x00] "OutgoingArgSpace"
; V02 tmp1 [V02 ] ( 1, 1 ) ref -> [rbp-0x08] must-init class-hnd exact "NewObj constructor temp"
; V03 tmp2 [V03 ] ( 1, 1 ) ref -> [rbp-0x10] must-init "argument with side effect"
; V04 tmp3 [V04 ] ( 1, 1 ) ref -> [rbp-0x18] must-init "argument with side effect"
; V05 tmp4 [V05 ] ( 1, 1 ) ref -> [rbp-0x20] must-init "argument with side effect"
;
; Lcl frame size = 64
G_M42780_IG01:
push rbp
sub rsp, 64
lea rbp, [rsp+40H]
vxorps xmm4, xmm4
vmovdqa xmmword ptr [rbp-20H], xmm4
vmovdqa xmmword ptr [rbp-10H], xmm4
mov gword ptr [rbp+10H], rcx
;; bbWeight=1 PerfScore 5.08
G_M42780_IG02:
mov rcx, 0xD1FFAB1E
call CORINFO_HELP_NEWSFAST
mov gword ptr [rbp-08H], rax
mov rcx, gword ptr [rbp-08H]
call Test:.ctor():this
mov rcx, gword ptr [rbp-08H]
mov rdx, gword ptr [rbp+10H]
cmp dword ptr [rcx], ecx
call Test:set_A(A):this
mov rcx, gword ptr [rbp+10H]
cmp dword ptr [rcx], ecx
call A:get_B():B:this
mov gword ptr [rbp-10H], rax
mov rdx, gword ptr [rbp-10H]
mov rcx, gword ptr [rbp-08H]
cmp dword ptr [rcx], ecx
call Test:set_B(B):this
mov rcx, gword ptr [rbp+10H]
cmp dword ptr [rcx], ecx
call A:get_B():B:this
mov rcx, rax
cmp dword ptr [rcx], ecx
call B:get_C():C:this
mov gword ptr [rbp-18H], rax
mov rdx, gword ptr [rbp-18H]
mov rcx, gword ptr [rbp-08H]
cmp dword ptr [rcx], ecx
call Test:set_C(C):this
mov rcx, gword ptr [rbp+10H]
cmp dword ptr [rcx], ecx
call A:get_B():B:this
mov rcx, rax
cmp dword ptr [rcx], ecx
call B:get_C():C:this
mov rcx, rax
cmp dword ptr [rcx], ecx
call C:get_Value():String:this
mov gword ptr [rbp-20H], rax
mov rdx, gword ptr [rbp-20H]
mov rcx, gword ptr [rbp-08H]
cmp dword ptr [rcx], ecx
call Test:set_Value(String):this
mov rax, gword ptr [rbp-08H]
;; bbWeight=1 PerfScore 50.00
G_M42780_IG03:
lea rsp, [rbp]
pop rbp
ret
;; bbWeight=1 PerfScore 2.00
; Total bytes of code 201, prolog size 24, PerfScore 77.48, (MethodHash=cd2b58e3) for method Program:Test2(A):Test
; ============================================================ |
@Suchiman I can definitely confirm this. You are checking the simple case. There are many cases that would be affected. We would need to spill the stack. F(G(), new C { X = Z(), Y = Z() }, H()); |
These are statements and don't produce values consumed by other expressions. I don't think we want a "symmetry". I don't want to be stepping onto every method call in expressions like |
@tmat Also i kinda see the argument against symmetry but the distinction seems rather arbitrarily based on a technical limitation than a logical one. One could argue for everything that could also be a valid ExpressionStatement to be steppable and to reduce stepping noise, limit that to expressions that are wrapped, e.g. so you don't single step multiple times on the same line. Alternatively there's always run to cursor if there are too many steps inbetween. E.g. #21781 is another ticket that proposes more granular stepping which i'm a huge fan of (at least skim the gif's in that one 😉) using System;
using System.Runtime.CompilerServices;
namespace ConsoleApp80
{
class Program
{
static void Main(string[] args)
{
var a = new A { B = new B() };
try
{
Test(a);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
try
{
Test2(a);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static string Test(A a)
{
return F(G(), new Test { B = Z(a), C = X(a) }, H());
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static string Test2(A a)
{
string v = G();
Test test = new Test();
test.B = Z(a);
test.C = X(a);
return F(v, test, H());
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static string H()
{
return "World";
}
private static string G()
{
return "Hello";
}
private static C X(A a)
{
return a.B.C;
}
private static B Z(A a)
{
return a.B;
}
private static string F(string v, Test a, string p)
{
return $"{v} {a.B} {p}";
}
}
class A
{
public B B { get; set; }
}
class B
{
public C C { get; set; }
}
class C
{
public string Value { get; set; }
}
class Test
{
public A A { get; set; }
public B B { get; set; }
public C C { get; set; }
public string Value { get; set; }
}
} Object Initializer; Assembly listing for method Program:Test(A):String
; Emitting BLENDED_CODE for X64 CPU with AVX - Windows
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T02] ( 3, 3 ) ref -> rsi class-hnd
; V01 OutArgs [V01 ] ( 1, 1 ) lclBlk (32) [rsp+0x00] "OutgoingArgSpace"
; V02 tmp1 [V02,T01] ( 4, 8 ) ref -> rdi class-hnd exact "NewObj constructor temp"
; V03 tmp2 [V03,T03] ( 2, 4 ) ref -> rbx class-hnd exact "impAppendStmt"
; V04 tmp3 [V04,T04] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
; V05 tmp4 [V05,T05] ( 2, 4 ) ref -> rsi class-hnd "Inlining Arg"
; V06 tmp5 [V06,T06] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
; V07 tmp6 [V07,T07] ( 2, 4 ) ref -> rsi class-hnd "Inlining Arg"
; V08 tmp7 [V08,T08] ( 2, 4 ) ref -> rdi class-hnd "Inlining Arg"
;* V09 tmp8 [V09 ] ( 0, 0 ) struct (32) zero-ref "NewObj constructor temp"
; V10 tmp9 [V10,T11] ( 2, 2 ) ref -> rbx V09._arg0(offs=0x00) P-INDEP "field V09._arg0 (fldOffset=0x0)"
; V11 tmp10 [V11,T12] ( 2, 2 ) ref -> rdi V09._arg1(offs=0x08) P-INDEP "field V09._arg1 (fldOffset=0x8)"
; V12 tmp11 [V12,T13] ( 2, 2 ) ref -> rsi V09._arg2(offs=0x10) P-INDEP "field V09._arg2 (fldOffset=0x10)"
; V13 tmp12 [V13,T14] ( 2, 2 ) ref -> r8 V09._args(offs=0x18) P-INDEP "field V09._args (fldOffset=0x18)"
; V14 tmp13 [V14 ] ( 2, 4 ) struct (32) [rsp+0x20] do-not-enreg[XSB] must-init addr-exposed "by-value struct argument"
; V15 tmp14 [V15,T00] ( 5, 10 ) byref -> rcx stack-byref "BlockOp address local"
; V16 tmp15 [V16,T09] ( 2, 4 ) ref -> rdx "argument with side effect"
; V17 cse0 [V17,T10] ( 3, 3 ) ref -> rsi "CSE - aggressive"
;
; Lcl frame size = 64
G_M653_IG01:
push rdi
push rsi
push rbx
sub rsp, 64
vxorps xmm4, xmm4
vmovdqa xmmword ptr [rsp+20H], xmm4
vmovdqa xmmword ptr [rsp+30H], xmm4
mov rsi, rcx
;; bbWeight=1 PerfScore 5.83
G_M653_IG02:
mov rcx, 0xD1FFAB1E
call CORINFO_HELP_NEWSFAST
mov rdi, rax
mov rdx, 0xD1FFAB1E
mov rbx, gword ptr [rdx]
mov rsi, gword ptr [rsi+8]
mov rdx, rsi
lea rcx, bword ptr [rdi+16]
call CORINFO_HELP_ASSIGN_REF
mov rdx, gword ptr [rsi+8]
lea rcx, bword ptr [rdi+24]
call CORINFO_HELP_ASSIGN_REF
call Program:H():String
mov rsi, rax
mov rdi, gword ptr [rdi+16]
mov rcx, 0xD1FFAB1E
mov edx, 377
call CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE
mov r8, 0xD1FFAB1E
mov r8, gword ptr [r8]
mov rdx, 0xD1FFAB1E
mov rdx, gword ptr [rdx]
lea rcx, bword ptr [rsp+20H]
mov gword ptr [rcx], rbx
mov gword ptr [rcx+8], rdi
mov gword ptr [rcx+16], rsi
mov gword ptr [rcx+24], r8
lea r8, bword ptr [rsp+20H]
xor rcx, rcx
call String:FormatHelper(IFormatProvider,String,ParamsArray):String
nop
;; bbWeight=1 PerfScore 26.75
G_M653_IG03:
add rsp, 64
pop rbx
pop rsi
pop rdi
ret
;; bbWeight=1 PerfScore 2.75
; Total bytes of code 185, prolog size 23, PerfScore 54.13, (MethodHash=a712fd72) for method Program:Test(A):String
; ============================================================ Assignments; Assembly listing for method Program:Test2(A):String
; Emitting BLENDED_CODE for X64 CPU with AVX - Windows
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
; V00 arg0 [V00,T02] ( 3, 3 ) ref -> rsi class-hnd
;* V01 loc0 [V01 ] ( 0, 0 ) ref -> zero-ref class-hnd exact
; V02 OutArgs [V02 ] ( 1, 1 ) lclBlk (32) [rsp+0x00] "OutgoingArgSpace"
; V03 tmp1 [V03,T01] ( 4, 8 ) ref -> rdi class-hnd exact "NewObj constructor temp"
; V04 tmp2 [V04,T03] ( 2, 4 ) ref -> rbx class-hnd exact "impAppendStmt"
; V05 tmp3 [V05,T04] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
; V06 tmp4 [V06,T05] ( 2, 4 ) ref -> rsi class-hnd "Inlining Arg"
; V07 tmp5 [V07,T06] ( 2, 4 ) ref -> rdx class-hnd "Inlining Arg"
; V08 tmp6 [V08,T07] ( 2, 4 ) ref -> rax class-hnd "Inlining Arg"
; V09 tmp7 [V09,T08] ( 2, 4 ) ref -> r8 class-hnd "Inlining Arg"
;* V10 tmp8 [V10 ] ( 0, 0 ) struct (32) zero-ref "NewObj constructor temp"
; V11 tmp9 [V11,T11] ( 2, 2 ) ref -> rbx V10._arg0(offs=0x00) P-INDEP "field V10._arg0 (fldOffset=0x0)"
; V12 tmp10 [V12,T12] ( 2, 2 ) ref -> r8 V10._arg1(offs=0x08) P-INDEP "field V10._arg1 (fldOffset=0x8)"
; V13 tmp11 [V13,T13] ( 2, 2 ) ref -> rax V10._arg2(offs=0x10) P-INDEP "field V10._arg2 (fldOffset=0x10)"
; V14 tmp12 [V14,T14] ( 2, 2 ) ref -> rdx V10._args(offs=0x18) P-INDEP "field V10._args (fldOffset=0x18)"
; V15 tmp13 [V15 ] ( 2, 4 ) struct (32) [rsp+0x20] do-not-enreg[XSB] must-init addr-exposed "by-value struct argument"
; V16 tmp14 [V16,T00] ( 5, 10 ) byref -> r9 stack-byref "BlockOp address local"
; V17 tmp15 [V17,T09] ( 2, 4 ) ref -> rcx "argument with side effect"
; V18 cse0 [V18,T10] ( 3, 3 ) ref -> rsi "CSE - aggressive"
;
; Lcl frame size = 64
G_M13791_IG01:
push rdi
push rsi
push rbx
sub rsp, 64
vxorps xmm4, xmm4
vmovdqa xmmword ptr [rsp+20H], xmm4
vmovdqa xmmword ptr [rsp+30H], xmm4
mov rsi, rcx
;; bbWeight=1 PerfScore 5.83
G_M13791_IG02:
mov rcx, 0xD1FFAB1E
call CORINFO_HELP_NEWSFAST
mov rdi, rax
mov rdx, 0xD1FFAB1E
mov rbx, gword ptr [rdx]
mov rsi, gword ptr [rsi+8]
mov rdx, rsi
lea rcx, bword ptr [rdi+16]
call CORINFO_HELP_ASSIGN_REF
mov rdx, gword ptr [rsi+8]
lea rcx, bword ptr [rdi+24]
call CORINFO_HELP_ASSIGN_REF
call Program:H():String
mov r8, gword ptr [rdi+16]
mov rdx, 0xD1FFAB1E
mov rdx, gword ptr [rdx]
mov rcx, 0xD1FFAB1E
mov rcx, gword ptr [rcx]
lea r9, bword ptr [rsp+20H]
mov gword ptr [r9], rbx
mov gword ptr [r9+8], r8
mov gword ptr [r9+16], rax
mov gword ptr [r9+24], rdx
lea r8, bword ptr [rsp+20H]
mov rdx, rcx
xor rcx, rcx
call String:FormatHelper(IFormatProvider,String,ParamsArray):String
nop
;; bbWeight=1 PerfScore 25.25
G_M13791_IG03:
add rsp, 64
pop rbx
pop rsi
pop rdi
ret
;; bbWeight=1 PerfScore 2.75
; Total bytes of code 165, prolog size 23, PerfScore 50.63, (MethodHash=ccf4ca20) for method Program:Test2(A):String
; ============================================================ |
It's not arbitrary, but it is indeed based on technical limitations. It's a balance between multiple factors. We can and we do add sequence points into select expressions. We just did it for switch expression. But there are trade-offs. We need to consider all the contributing factors. Adding breakpoints on each method call would be very expensive, both from perf perspective and the effort to make features like Edit and Continue work. The benefit from such work is questionable. Maybe someone would appreciate stepping onto every single thing, but many would not. |
I opened a dupe because I couldn't find this issue. Here are the reasons why I recently found that I need this a lot:
Currently, the shortcomings are:
Regarding the concern that this would reduce performance significantly, I'm assuming this would only be in debug mode, right? Is it a valid concern, to worry about the performance of code built in debug mode? Also, can it be made an option, to "turn on" this detailed metadata, for those teams/projects where it's needed, not to negatively impact others? |
I'm not sure if it's related, but if you have an init block like this: var x = new Foo
{
Prop = await GetValue(),
OtherProp = 123,
}; And you try to modify that init block to change what's assigned to |
Version Used:
VS 15.7.2
Steps to Reproduce:
I've captured what is happening in a screen-cast.
https://www.screencast.com/t/aFvHcdnkYx
Expected Behavior:
I should be able to hit that single if condition, and not the whole block.
Actual Behavior:
The whole block gets covered by debug break-point.
The text was updated successfully, but these errors were encountered: