Search

The compiler seems to produce unconsistent results for shift operation by Birdy1

Closed
as Fixed Help for as Fixed

2
Sign in to vote
0
Sign in to vote
Sign in
to vote
Type: Bug
ID: 106414
Opened: 11/24/2005 8:48:45 AM
Access Restriction: Public
0
Workaround(s)
1
User(s) can reproduce this bug
If you compile the following codes,

unsigned __int64 x = 0x80000000;
unsigned __int64 y = x >> 32;

it may be expected that y is 0.
In terms of assembly, __aullshr is expected to be used instead of shr.
But actually the compiler doesn't produce one result, rather various ones
for optimization levels(/O1, /O2, /Og), and position of the code.
Other compilers like gcc, intel compiler do produce a correct one always.
I wonder if this is a bug or not.
Details (expand)
Product Language
English
Version
Visual Studio 2005
Category
Visual C++ Development
Operating System
Windows 2000 Professional
Operating System Language
Korean
Steps to Reproduce
#include <stdio.h>
#include <stdlib.h>

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long uint32;
#ifdef _WIN32
typedef unsigned __int64 uint64;
#else
typedef unsigned long long uint64;
#endif

#define RSHIFT32(x, c) (uint32)((uint32)(x) >> (c))
#define RSHIFT64(x, c) (uint32)((uint64)(x) >> (c))

uint32 rshift32(uint32 x, uint8 c){return RSHIFT32(x, c);}
uint32 rshift64(uint32 x, uint8 c){return RSHIFT64(x, c);}

uint32 afterR32;
uint32 afterr32;
uint32 afterR64;
uint32 afterr64;

void TestShift(uint32 before, uint8 count)
{
afterR32 = RSHIFT32(before, count);
afterr32 = rshift32(before, count);
afterR64 = RSHIFT64(before, count);
afterr64 = rshift64(before, count);
}

int main(int argc, char* argv[])
{
uint32 before;
uint8 count;

if(argc != 3)
{
before = 0x80000000;
count = 0;
}
else
{
before = (uint32)atoi(argv[1]);
count = (uint8)atoi(argv[2]);
}

TestShift(before, count);

printf("RSHIFT32(0x%08x,%d)=0x%08x\n", before, count, afterR32);
printf("rshift32(0x%08x,%d)=0x%08x\n", before, count, afterr32);
printf("RSHIFT64(0x%08x,%d)=0x%08x\n", before, count, afterR64);
printf("rshift64(0x%08x,%d)=0x%08x\n", before, count, afterr64);

return 0;
}
Actual Results
for Release mode,
RSHIFT64(0x80000000,32)=0x80000000
rshift64(0x80000000,32)=0x80000000
Expected Results
for Release mode,
RSHIFT64(0x80000000,32)=0x00000000
rshift64(0x80000000,32)=0x00000000
File Attachments
1 attachments
Sign in to post a comment.
Posted by Microsoft on 12/1/2005 at 10:54 AM
Thanks for reporting this issue. We will take a look at the code you sent us, and get back to you.

Thanks,

Kang Su Gatlin
Visual C++ Program Manager
Posted by Microsoft on 1/4/2006 at 3:34 PM
About shift operation, in MSDN, it says "The results are undefined if the right operand of a shift expression is negative or if the right operand is greater than or equal to the number of bits in the (promoted) left operand". Please see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang98/html/_pluslang_shift_operators.asp.
Posted by Birdy1 on 1/6/2006 at 8:21 AM
Thank you for the answer.
But I cannot accept it and wish that you could check it once more.

Although I already knew what MSDN explains regarding shift operation, there still be unclear issues.

1. In case of 32 bit left operand, MSDN seems to mean that right operand should be 0
to 31. It can be acceptable although I have another question in 2. In my case, the left operand is 64 bit and then the following code can be legal in terms of the explanation of MSDN because the right operand is less than 64.

unsigned __int64 y = x >> 32;

In this case, __aullshr can be used because it is a psuedo assembly instruction for 64 bit operand in 32 bit machine. The point I argue is that Visual Studio cannot produce consitent results for this case unlike other compilers.

2. MSDN means that the results are undefined for the following code,

unsigned long y = x >> 32;

nevertheless it is unclear if "undefined results" means
(A)the compiler cannot produce one assembly instruction for one C code, or
(B)an assembly instruction can make different results over various processors.
If it means (A), Visual Studio is a poor compiler because others can do it.
If it means (B), Visual Studio has a bug to produce inconsistent results on a processor because the manuals for Intel CPU(http://intel.com/design/pentium4/manuals/index_new.htm), for example, defines a unique result for shr or shl instruction by masking the shift counts to 5 bits.

I hope to know if I have a misunderstanding about these issues.
Posted by Microsoft on 1/6/2006 at 3:02 PM
With both the x64 & x86 compilers, all shifts of 64 bit values appear to be correct for /O1, /O2, and /Od. As far as what 'undefined behavior' means - dividing by zero results in 'undefined behavior' as another example: We are inconsistent (right shifting a 32-bit integer 32 bits), and that inconsistency is acceptable (we understand it's not pleasant, but maintaining consistent undefined behavior is not a requirement of the C language for this construct). We have a warning (4293) that explicitly catches this problem when it's done by a constant amount. We should consider adding a runtime check for this scenario under /Od /RTC1 to help people find problems like this.
Posted by Birdy1 on 2/3/2006 at 7:44 AM
Thanks for the answer.
To make this problem simple, let me focus on the following.
Thus, please check the project file attached.

You can find TestShift.asm in VS2005/Debug or VS2005/Release
and see the differences between the two assembly codes for
rshift64().

uint32 rshift64(uint32 x, uint8 c){return RSHIFT64(x, c);}

1. assembly codes for Release mode

_rshift64 PROC

shr eax, cl
ret 0
_rshift64 ENDP

In this case, shr is used.

2. assembly codes for Debug mode

_rshift64 PROC

push ebp
mov ebp, esp
sub esp, 192
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-192]
mov ecx, 48
mov eax, -858993460
rep stosd
mov eax, DWORD PTR _x$[ebp]
xor edx, edx
movzx ecx, BYTE PTR _c$[ebp]
call __aullshr
pop edi
pop esi
pop ebx
add esp, 192
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
_rshift64 ENDP

In this case, __aullshr is used, which is defined in ULLSHR.ASM.
And internally shrd is used, which is an assembly instruction
for the Intel processor.

The above result shows that two different assembly codes are
generated for a single function. And when you run the executable
with argument "-2147483648 32", you can see the following results:

rshift64(0x80000000,32)=0x00000000 for Debug
rshift64(0x80000000,32)=0x80000000 for Release

Please let me understand what this phenomenon means.
Posted by Microsoft on 2/14/2006 at 11:10 AM
The Microsoft Sub-status is now "Working on solution"
Posted by Microsoft on 2/14/2006 at 11:18 PM
Thank you very much for providing very detailed information of the problem. We have realized it is a bug in our compiler's optimization part that, when first convert uint32 to uint64 and then do a right shift by amount larger than 31, the optimized code generates wrong result. We have resolved this problem and the fix will be in the next version of our product. For now the work-around is to disable the optimization for that function.
Posted by Microsoft on 1/17/2007 at 5:42 PM
Thank you for submitting this issue. The bug has been resolved Fixed, and its resolution shipped as part of Visual Studio 2005 Service Pack 1 (SP1).

You can download SP1 at http://msdn.microsoft.com/vstudio/support/vs2005sp1/default.aspx. If installing SP1 does not resolve the issue for you, please reactivate.

Thanks again for your feedback!
-Scott Currie
-DDTFA Program Manager