Search code examples
cc11abiinteger-promotion

Using smallest necessary C type vs just using int


Given a C function (random example):

void* getMyStuff( size_t size, unsigned char alignment );

Does using unsigned char help the compiler by telling it the limit of the values being passed, or does it hinder the compiler by preventing it from using the native int (or unsigned int) size?

I know historically it's not ideal to use short since 16-bit operations were not native. I don't know if that applies here (or to local variables in general).

Basically, if you are passing values smaller than 32-bit, that could fit in a smaller data type, does the compiler care which you choose?

For reference, I'm using GCC and Clang, compiling C11 (not C++11) code. This question may be ABI-specific, but I don't know how to determine that.

Edit: I'm developing on x86_64, if it matters. I would hope the answer doesn't vary among CPU vendors.


Solution

  • Does using unsigned char help the compiler by telling it the limit of the values being passed, or does it hinder the compiler by preventing it from using the native int (or unsigned int) size?

    Depends on the CPU and ABI. Whatever is most efficient depends entirely on which calling conventions that are documented as fastest by the ABI.

    Generally, upper-end CPUs like to work with the type of their preferred data width and alignment. 32 bit for 32 bit CPUs and so on. They may however support operations on smaller types. Lower-end CPUs (8 or 16 bit) don't like larger types at all and generally prefer to work with 8 bit types if possible.

    I know historically it's not ideal to use short since 16-bit operations were not native.

    Not native where? 16 bit operations are perfectly "native" on all 16 bit CPUs. They may not be ideal on some 32 bitters with poor instruction support for 16 bit data.

    Basically, if you are passing values smaller than 32-bit, that could fit in a smaller data type, does the compiler care which you choose?

    The compiler is generally not allowed to swap out the type you chose for a larger one, since that could change the meaning and behavior of the program. With the exception of uint_fast8_t and similar modern types, where the compiler is allowed to swap for a larger type if it leads to faster code.

    For reference, I'm using GCC and Clang

    The compiler doesn't matter. Only the specific target port and its ABI matters. Though on some low end CPUs like specialized microcontrollers, there's generally no standardized ABI and each compiler therefore invents its own ABI/calling conventions for that target.