https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87248
Bug ID: 87248 Summary: Bad code for masked operations involving signed ints Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: david at westcontrol dot com Target Milestone: --- I am always wary of saying there might be a compiler bug - usually it is a bug in the user code. But this time I am very suspicious. The example here comes from a discussion in the comp.lang.c Usenet group. Here is the code I have been testing: unsigned char foo_u(unsigned int v) { return (v & 0x7f) | (v & ~0x7f ? 0x80 : 0); } unsigned char foo_s(int v) { return (v & 0x7f) | (v & ~0x7f ? 0x80 : 0); } unsigned char bar_s(int v) { int x = v & ~0x7f; return (v & 0x7f) | (x ? 0x80 : 0); } int test_u_1(void) { return foo_u(0x01); // Expect 0x01 = 1 } int test_u_2(void) { return foo_u(0x1001); // Expect 0x81 = 129 } int test_s_1(void) { return foo_s(0x01); // Expect 0x01 = 1 } int test_s_2(void) { return foo_s(0x1001); // Expect 0x81 = 129 } The assembly code generated for this is: foo_u(unsigned int): mov eax, edi mov edx, edi or edx, -128 and eax, 127 and edi, -128 cmovne eax, edx ret foo_s(int): mov eax, edi ret bar_s(int): mov eax, edi mov edx, edi or edx, -128 and eax, 127 and edi, 127 cmovne eax, edx ret test_u_1(): mov eax, 1 ret test_u_2(): mov eax, 129 ret test_s_1(): mov eax, 1 ret test_s_2(): mov eax, 1 ret When the code uses "int" rather than "unsigned int", in "foo_s", the compiler thinks the function can be optimised to a simple byte extraction. I cannot see how that interpretation is valid. And gcc cannot see it either, if there is an intermediate variable (in "bar_s"). (The type of the intermediate "x" in "bar_s" does not matter - "int", "unsigned int", "auto", "__auto_type", const or not). This effect is happening in the front-end. It is independent of optimisation levels (-O gives the same results), the target processor (I tried a half-dozen targets), the compiler version (from gcc 4.4 upwards - gcc 4.1 gives the same code for foo_s as foo_u. I haven't tried gcc 4.2 or 4.3). There are minor variations in the generated code (such as the use of conditional move instructions) between different versions - those don't affect the results. The "-fwrapv" flag has no effect either, nor does the choice of C or C++. The results from the compiler evaluations in the "test_" functions shows that it this happens in the compiler analysis - it is not a code generation issue. (<https://godbolt.org> is great for this kind of testing!) (This bug report was first sent to the mailing list <https://gcc.gnu.org/ml/gcc/2018-09/msg00028.html> - the report here has a few minor corrections.)