C# 如果块未优化,则为空?

C# 如果块未优化,则为空?,c#,optimization,jit,cil,C#,Optimization,Jit,Cil,下面的if块是否会被优化掉 公共C类 { private bool foo=false; 公屋 { 如果(foo){} } } SharpLab(master 5 Dec 2020,发行版)指出编译器没有优化if块: .method public hidebysig instance void M () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld bool C::foo IL_0006:

下面的if块是否会被优化掉

公共C类
{
private bool foo=false;
公屋
{
如果(foo){}
}
}
SharpLab(master 5 Dec 2020,发行版)指出编译器没有优化if块:

.method public hidebysig instance void M () cil managed 
{
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldfld bool C::foo
    IL_0006: pop
    IL_0007: ret
}
C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: ret
C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: ret
编译器是否不能看到
ldfld
后跟
pop
无效,并且不需要发出三条指令(
ldarg.0

我想不出在不发出
ldfld
时可能出现的副作用(在if块内调用方法时可能会出现这种副作用)


此外,SharpLab生成的JIT ASM似乎也没有优化空if块:

.method public hidebysig instance void M () cil managed 
{
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldfld bool C::foo
    IL_0006: pop
    IL_0007: ret
}
C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: ret
C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: ret

我认为JIT仍然会优化空if块,这对吗

  • 如果是这样的话,你能解释一下为什么编译器不能这样做吗
  • 若否,原因为何
提前谢谢你

此外,SharpLab生成的JIT ASM似乎也没有优化空if块:

.method public hidebysig instance void M () cil managed 
{
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldfld bool C::foo
    IL_0006: pop
    IL_0007: ret
}
C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: ret
C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: ret
实际的
如果
块已优化掉,如果它存在,它将看起来与此相当:

C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: test eax, eax
    L0006: je short L0008
    L0008: ret
但这并没有发生,JIT编译器显然发现这是没有用的

只有布尔字段的加载仍然存在

此外,SharpLab生成的JIT ASM似乎也没有优化空if块:

.method public hidebysig instance void M () cil managed 
{
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldfld bool C::foo
    IL_0006: pop
    IL_0007: ret
}
C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: ret
C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: ret
实际的
如果
块已优化掉,如果它存在,它将看起来与此相当:

C.M()
    L0000: movzx eax, byte ptr [ecx+4]
    L0004: test eax, eax
    L0006: je short L0008
    L0008: ret
但这并没有发生,JIT编译器显然发现这是没有用的


只有布尔字段的加载仍然存在。

您是将此目标构建为发行版还是调试版?我认为在调试中某些优化不会发生。您是将此目标构建为发行版还是调试版?我认为在调试中我的某些优化不会发生。谢谢!我对汇编不太了解,而且过于关注JIT ASM,不仅是
ret
,所以我得出了一个错误的结论。。。但是,您知道为什么布尔字段的负载仍然存在吗?你的(现在编辑的)加载点如何,这样就不能将
null
传递给该方法?@ThomasFlinkow如果需要,会将一个虚拟加载插入调用者(例如,
mov eax,[rdx]
除了触发NRE之外,什么都不做),这样就不会是它了。虽然很无聊,但我不得不把它归因于JIT编译器有点笨。优化加载可能会导致行为上的明显变化。如果对方法使用
call
而不是
callvirt
,并将
null
作为
this
传递,则原始代码仍将从加载中抛出NullReferenceException,而没有它,方法将正常返回。查看
静态
字段的行为是否相同可能很有趣。@IllidanS4supportsMonica最初是一个假设,但在调用程序中插入了一个虚拟负载,因此,如果
this
null
,则该方法甚至从未被调用,而且它本身也不需要崩溃。我猜编译器是如此愚蠢。谢谢!我对汇编不太了解,而且过于关注JIT ASM,不仅是
ret
,所以我得出了一个错误的结论。。。但是,您知道为什么布尔字段的负载仍然存在吗?你的(现在编辑的)加载点如何,这样就不能将
null
传递给该方法?@ThomasFlinkow如果需要,会将一个虚拟加载插入调用者(例如,
mov eax,[rdx]
除了触发NRE之外,什么都不做),这样就不会是它了。虽然很无聊,但我不得不把它归因于JIT编译器有点笨。优化加载可能会导致行为上的明显变化。如果对方法使用
call
而不是
callvirt
,并将
null
作为
this
传递,则原始代码仍将从加载中抛出NullReferenceException,而没有它,方法将正常返回。查看
静态
字段是否有相同的行为可能会很有趣。@IllidanS4supportsMonica这本来是一个假设,但是在调用者中插入了一个虚拟负载,因此如果
null
,则该方法甚至从未被调用过,也不需要崩溃它自己。我想编译器当时是如此愚蠢。