Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are AND instructions generated?

Tags:

c#

.net

assembly

For code such as this:

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

This code is generated (release mode, no debugger attached, 64bit):

 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 

Now I can see the point of most instructions there, but what's up with the AND instructions? ecx will never be more than 0x1F in this code anyway, but I excuse it for not noticing that (and also for not noticing that the result is a constant), it's not an ahead-of-time compiler that can afford to spend much time on analysis after all. But more importantly, SHL with a 32bit operand masks cl by 0x1F already. So it seems to me that these ANDs are entirely useless. Why are they generated? Do they have some purpose I'm missing?

like image 949
harold Avatar asked Mar 28 '12 10:03

harold


People also ask

What are instructions in programming?

A computer instruction is an order given to a computer processor by a computer program. At the lowest level, each instruction is a sequence of 0s and 1s that describes a physical operation the computer is to perform.

Can you write machine code directly?

While it is possible to write programs directly in machine code, managing individual bits and calculating numerical addresses and constants manually is tedious and error-prone.


1 Answers

The and is already present in the CIL code emitted by the C# compiler:

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

The spec for the CIL shl instruction says:

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

The C# spec, however, defines the 32-bit shift to take the shift count mod 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.

In this situation, the C# compiler can’t really do much better than emit an explicit and operation. Best you can hope for is that the JITter will notice this and optimize away the redundant and, but that takes time, and the speed of JIT is pretty important. So consider this the price paid for a JIT-based system.

The real question, I guess, is why the CIL specifies the shl instruction that way, when C# and x86 both specify the truncating behaviour. That I do not know, but I speculate that it’s important for the CIL spec to avoid specifying a behaviour that may JIT to something expensive on some instruction sets. At the same time, it’s important for C# to have as few undefined behaviours as possible, because people invariably end up using such undefined behaviours until the next version of the compiler/framework/OS/whatever changes them, breaking the code.

like image 147
Roman Starkov Avatar answered Sep 22 '22 10:09

Roman Starkov