DEV Community

Ahmet Can Gulmez
Ahmet Can Gulmez

Posted on

Compiler-Specific Tools that Every Programmer Should Know

In this post, I will explain the some compiler-specific tools that make the code more powerful and elite. These tools:

  • __asm__,

  • __attribute__,

  • __auto_type,

  • __builtin_*,

  • #pragma,

  • __restrict__,

  • typeof().

Let's begin with __asm__. This tool lets us embed the assembly code into C syntax. This provides the following extra functionalities:

  • Direct CPU instruction control

  • Hardware-specific operation (e.g. reading registers)

  • Super-optimized code

  • OS kernel development

  • Embedded low-level control

It can be used like this:

int a = 10;
int b = 20;
int result;
unsigned long long tick;

__asm__(
   "addl %[val1], %[val2]"
   : [res] "=r" (result)
   : [val1] "r" (a), [val2] "r" (b)
);

__asm__ volatile("rdtsc" : "=A" (tick));

printf("Number of CPU cycles since boot: %lld\n", tick);
Enter fullscreen mode Exit fullscreen mode

__attribute__ is maybe most-known the GCC and Clang tool. This lets us attach special properties to functions, variables and types. It tells the compiler how to treat your code:

  • Put something in a special memory section

  • Warn if a function is used

  • Control optimization

  • Automatically run before main() or similar properties

Most common attributes are listed here:

  • __attribute__((noreturn))

  • __attribute__((packed))

  • __attribute__((aligned(n)))

  • __attribute__((section(".rodata")))

  • __attribute__((constructor))

  • __attribute__((destructor))

  • __attribute__((deprecated))

  • __attribute__((unused))

  • __attribute__((cold))

These attributes can be used as:

#define __aligned16__       __attribute__((aligned(16)))
#define __packed__          __attribute__((packed))
#define __section_bss__     __attribute__((section(".bss")))
#define __noreturn__        __attribute__((noreturn))
#define __depreceted__      __attribute__((deprecated))
#define __constructor__     __attribute__((constructor))
#define __destructor__      __attribute__((destructor))

int __aligned16__ buffer[4];

int __section_bss__ data;

typedef struct __packed__ {
   int a;
   char b;
} MyStruct;

void __noreturn__ noreturnfunc(void)
{
   printf("This is the noreturn function...\n");

   exit(EXIT_SUCCESS);
}

void __depreceted__ deprecatedfunc(void)
{
   printf("This is the deprecated function...\n");
}
Enter fullscreen mode Exit fullscreen mode

__auto_type is just like C++'s auto keyword. Using this tool, we don't have to write the data type explicitly. For example:

__auto_type x = 42;
__auto_type y = 3.14;
__auto_type z = "Auto Type";
Enter fullscreen mode Exit fullscreen mode

__builtin_* is a huge family of powerful compiler-provided functions that map to optimized assembly. These built-ins are faster than standard C functions and super useful in performance-critical code. Most ones:

  • __builtin_expect()

  • __builtin_popcount()

  • __builtin_clz()

  • __builtin_ctz()

  • __builtin_memcpy()

  • __builtin_return_address()

  • __builtin_unreachable()

  • __builtin_prefect()

We can use some of these like:

switch (x) {
   case 0:
      break;
   case 1:
      break;
   default:
      /* optimize away this path */
      __builtin_unreachable(); 
}

printf("Caller address: %p\n", __builtin_return_address(0));
Enter fullscreen mode Exit fullscreen mode

#pragma is a preprocessor directive that gives special instructions to the compiler. Unlike attributes which modifies variables/functions directly; #pragma is used more globally to control:

  • Warnings

  • Optimization

  • Structure packing

  • OpenMP/threading

  • GCC extensions

Common #pragma GCC options:

  • #pragma GCC poison (Forbid use of specific identifiers)

  • #pragma GCC dianostic (Control warnings)

  • #pragma GCC optimize("O3") (Change optimization locally)

  • #pragma GCC target("avx2") (Use specific CPU instruction)

  • #pragma pack(n) (Structure alignment)

  • #pragma opm ... (OpenMP parallelization)

__restrict__ is a compiler hint that tells GCC: "This pointer will not alias (overlap) with any other pointer." This allows the compiler to generate faster code because it doesn't have to worry about two pointers referencing the same memory.

Aliasing: Two pointers referring to the same memory region.

Below are the example usage of restriction:

/**
 * Compare the literal strings with each other.
 */
unsigned char S(const char* __restrict__ fliteral, 
                     const char* __restrict__ sliteral)
{
    int fnum = 0, snum = 0;

    /* Iterate over both literals until one reaches at end */
    while (*fliteral != 0 || *sliteral != 0) {
        if (*fliteral != *sliteral)
            return false;

        *fliteral ++, *sliteral ++; /* Next character */
        if (*fliteral != 0) fnum ++;
        if (*sliteral != 0) snum ++;
    }
    if (fnum != snum)              /* Maybe length mismatch? */
        return false;

    return true;
}
Enter fullscreen mode Exit fullscreen mode

typeof() gives us to learn the defined data types and if required, assign it to new type like:

#define swap(x, y) do {          \
   typeof(x) _tmp = x;           \
   x = y;                        \
   y = _tmp;                     \
} while (0)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)