c# - 为什么会产生AND指令?

标签 c# .net assembly

对于这样的代码:

int res = 0;
for (int i = 0; i < 32; i++)
{
    res += 1 << i;
}

此代码已生成( Release模式,未附加调试器,64 位):

 xor edx,edx 
 mov r8d,1 
_loop:
 lea ecx,[r8-1] 
 and ecx,1Fh     ; why?
 mov eax,1 
 shl eax,cl 
 add edx,eax 
 mov ecx,r8d 
 and ecx,1Fh     ; why?
 mov eax,1 
 shl eax,cl 
 add edx,eax 
 lea ecx,[r8+1] 
 and ecx,1Fh     ; why?
 mov eax,1 
 shl eax,cl 
 add edx,eax 
 lea ecx,[r8+2] 
 and ecx,1Fh     ; why?
 mov eax,1 
 shl eax,cl 
 add edx,eax 
 add r8d,4 
 cmp r8d,21h 
 jl  _loop

现在我可以看到那里大多数指令的要点,但是 AND 指令怎么了?无论如何,ecx 永远不会在此代码中超过 0x1F,但我原谅它没有注意到这一点(也没有注意到结果是一个常量),它不是提前编译器毕竟可以花很多时间进行分析。但更重要的是,具有 32 位操作数的 SHL 已经通过 0x1F 屏蔽了 cl。所以在我看来,这些 AND 完全没有用。它们为什么会产生?他们有什么我想念的目的吗?

最佳答案

and 已经存在于 C# 编译器生成的 CIL 代码中:

    IL_0009: ldc.i4.s 31
    IL_000b: and
    IL_000c: shl

CIL shl 指令的规范说:

The return value is unspecified if shiftAmount is greater than or equal to the size of value.

然而,C# 规范定义了 32 位移位以采用移位计数模 32:

When the type of x is int or uint, the shift count is given by the low-order five bits of count. In other words, the shift count is computed from count & 0x1F.

在这种情况下,C# 编译器除了发出显式 操作之外,真的做的再好不过了。最好的希望是 JITter 会注意到这一点并优化掉冗余的 ,但这需要时间,而且 JIT 的速度非常重要。因此,请考虑为基于 JIT 的系统支付的价格。

我想,真正的问题是为什么 CIL 以这种方式指定 shl 指令,而 C# 和 x86 都指定了截断行为。我不知道,但我推测对于 CIL 规范来说,避免指定可能对某些指令集上昂贵的东西进行 JIT 的行为很重要。同时,对于 C# 来说,尽可能少的未定义行为很重要,因为人们总是会使用这些未定义的行为,直到下一个版本的编译器/框架/操作系统/无论什么改变它们,破坏代码。

关于c# - 为什么会产生AND指令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9905668/

相关文章:

c# - jquery 数据表 Ajax-Error/http ://datatables.net/tn/7

.net - NServiceBus 与 Unity 2.0?

c# - 如果您没有任何异步代码,您如何处理返回 Task 的第 3 方接口(interface)?

assembly - 镜像 Byte/X86 嵌入式程序集(在 Visual Studio 2010 中)

assembly - 需要帮助理解 FF 间接调用指令 x86

assembly - 对汇编中的指针和值感到困惑

c# - ApiController,Viewmodel和DTO的设计

c# - Crystal Report ASP.NET MVC 中不显示本地镜像

javascript - 从 Javascript 调用 PUT 方法

.net - VS2010+甲骨文驱动: ORA-12154: TSN:could not resolve the connect identifier specified