Question

My C program is very simple, and runs in 64-bit Linux:

#include <stdio.h>
int main(void)
{
    unsigned char a = 0xff;
    unsigned  short b = (a << 6) ;
    return 0;
}

I am curious about where the temporary result of (a << 6) is stored, so I check the assembly code:

0x0000000000400474 <+0>:     push   %rbp
0x0000000000400475 <+1>:     mov    %rsp,%rbp
0x0000000000400478 <+4>:     movb   $0xff,-0x3(%rbp)
0x000000000040047c <+8>:     movzbl -0x3(%rbp),%eax
0x0000000000400480 <+12>:    shl    $0x6,%eax
0x0000000000400483 <+15>:    mov    %ax,-0x2(%rbp)
0x0000000000400487 <+19>:    mov    $0x0,%eax
0x000000000040048c <+24>:    leaveq
0x000000000040048d <+25>:    retq

From the assembly code, I can see the eax is used to store the temporary result of shift operation.

So I want to know is it a C standard to use signed/unsigned 32-bit integer to store the temporary result of shift operation?

I have checked section 6.5.7 Bitwise shift operators of n1570 specification, but nothing can be found.

Was it helpful?

Solution 2

The C compiler can store the result anywhere it likes: you specified a as char (8 bits typically) and b as short int (16 bits typically), both of them easily fit within the 32-bit %eax.

If you define a and b as long long int (64 bits), you'll note that the compiler has to use a 64 bit register (e.g. %rax).

OTHER TIPS

The standard does not specify those low-level details but you missed one important part in the draft C11 standrd section 6.5.7 paragraph 2 says (emphasis mine):

The integer promotions are performed on each of the operands.[...]

This is covered in section 6.3.1 Arithmetic operands subsection 6.3.1.1 Boolean, characters, and integers paragraph 2 which says:

The following may be used in an expression wherever an int or unsigned int may be used:

and includes the following bullet:

An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.

which includes short and char and goes on to say (emphasis mine):

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.58) All other types are unchanged by the integer promotions.

So both a and 6 has to be promoted to int and that will provide a lower bound on which register can be used to store the intermediate results.

Note, as Keith points out in your code sample since the assignment and shift have no observable behavior the compiler would be free to optimize those instructions out.

The standard only specifies observable behavior of a C program. Since your program has none, the compiler is totally entitled to replace all of it by a nop, or by just about anything that has no observable side effects.

But in general operands of integral expressions that are smaller than int are promoted to int or to the other operand type, whichever is larger.

Actually, for shift operations, the C standard requires the operand(s) to be sign extended to int, do the calculation and truncate the result (if necessary). It can only omit that if it gets the same result otherwise.

That's why even picky compilers do not issue a warning if you shift a char wider than 7 bits (try it - you'll get the warning only if you shift wider than an int allows).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top