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);
__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");
}
__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";
__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));
#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;
}
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)
Top comments (0)