Let's see what the "dumb" compiler will do if the following code is specified:
#include <stdio.h> #include <stdint.h> int main(int argc, char **argv) { uint16_t test1 = 0x1122; uint32_t test2 = 0x11223344; if (test1 & 0x0100) printf("yay1.\n"); if (test2 & 0x00010000) printf("yay2.\n"); }
This is similar to the likely use case for values ββof type mode_t
, checking if the flag is set. Now we compile it with gcc -O0
and check the generated assembly:
0000000000000000 <main>: ... f: 66 c7 45 fe 22 11 movw $0x1122,-0x2(%rbp) 15: c7 45 f8 44 33 22 11 movl $0x11223344,-0x8(%rbp) 1c: 0f b7 45 fe movzwl -0x2(%rbp),%eax ; load test1 into %eax 20: 25 00 01 00 00 and $0x100,%eax 25: 85 c0 test %eax,%eax ... 33: 8b 45 f8 mov -0x8(%rbp),%eax ; load test2 into %eax 36: 25 00 00 01 00 and $0x10000,%eax 3b: 85 c0 test %eax,%eax ...
See how the special movzwl
command is movzwl
to load a 16-bit value? This is due to the fact that to register in the register, you must add a character in two additional bytes. Obviously, this instruction is more complicated than a simple mov
. This can have a slight impact on performance, and it can increase the size of the executable by several bytes, which in itself will not be so bad.
However, given that there is no advantage in using a 16-bit value, because in any case it will occupy 32 bits of memory due to alignment, it clearly explains why designers prefer to use the size of the CPU native word here.
source share