[Bug c/108580] New: gcc treats shifts as signed operation, does wrong promotion

postmaster at raasu dot org gcc-bugzilla@gcc.gnu.org
Sat Jan 28 07:42:06 GMT 2023


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108580

            Bug ID: 108580
           Summary: gcc treats shifts as signed operation, does wrong
                    promotion
           Product: gcc
           Version: 12.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: postmaster at raasu dot org
  Target Milestone: ---

I have a simple program that fails to compile correctly on any common compiler:

int main()
{
   int bits = 8;
   char* a = (char*)malloc(1 << bits);
   char* b = (char*)malloc(1 << bits);
   memcpy(b, a, 1 << bits);
   return 0;
}

when assembled with "gcc -S", the result is

main:
.LFB6:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $32, %rsp
        movl    $8, -20(%rbp)
        movl    -20(%rbp), %eax
        movl    $1, %edx
        movl    %eax, %ecx
        sall    %cl, %edx
        movl    %edx, %eax
        cltq
        movq    %rax, %rdi
        call    malloc@PLT
        movq    %rax, -16(%rbp)
        movl    -20(%rbp), %eax
        movl    $1, %edx
        movl    %eax, %ecx
        sall    %cl, %edx
        movl    %edx, %eax
        cltq
        movq    %rax, %rdi
        call    malloc@PLT
        movq    %rax, -8(%rbp)
        movl    -20(%rbp), %eax
        movl    $1, %edx
        movl    %eax, %ecx
        sall    %cl, %edx
        movl    %edx, %eax
        movslq  %eax, %rdx
        movq    -16(%rbp), %rcx
        movq    -8(%rbp), %rax
        movq    %rcx, %rsi
        movq    %rax, %rdi
        call    memcpy@PLT
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc

The part that is incorrect is:

        sall    %cl, %edx
        movl    %edx, %eax
        cltq
        movq    %rax, %rdi

It should zero-extend before the shift, but instead it sign-extends after the
shift... Bit shifting is always unsigned operation. It correctly determines the
function requires 64-bit parameter, but fails to determine it's unsigned.
Integer promotion rules say that unsigned type in expression must be promoted
to larger unsigned type if it can hold the result. As bit shift is unsigned
operation, the temporary should also be unsigned.

Stock gcc headers don't have UINTPTR_C() macro which could be used to
explicitly cast the constant "1" to pointer size to give hint that the shift is
indeed unsigned operation.

gcc version is: gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0


More information about the Gcc-bugs mailing list