Skip to content

Commit 8f9125d

Browse files
authored
Update 2025-11-30-comptime-c-functions.md
1 parent 412e554 commit 8f9125d

1 file changed

Lines changed: 15 additions & 11 deletions

File tree

_posts/2025-11-30-comptime-c-functions.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Compile-time function execution is great, but what if:
99
2. You don't want to use evil C macros.
1010
3. You want generic data structures that work for all types.
1111

12-
The below data structure showcase programs get optimized away at compile time by GCC and Clang, such that only the `printf()` at the end of `main()` is left:
12+
The below data structure showcase programs get optimized away at compile time by Clang and GCC, such that only the `printf()` at the end of `main()` is left:
1313
```nasm
1414
main:
1515
push rax
@@ -29,13 +29,13 @@ Here is how it is achieved in C:
2929

3030
This optimization requires stack-allocated buffers with constant addresses. Heap allocation breaks the optimization, because the compiler can't trace memory operations through dynamic allocations.
3131

32-
[Link-time optimization](https://en.wikipedia.org/wiki/Interprocedural_optimization) with `-flto` should allow GCC and Clang to perform these optimizations even when the code is split across several object files.
32+
[Link-time optimization](https://en.wikipedia.org/wiki/Interprocedural_optimization) with `-flto` should allow Clang and GCC to perform these optimizations even when the code is split across several object files.
3333

3434
# Generic Stack
3535

36-
GCC requires `-O1`, while Clang requires `-O2`.
36+
Clang and GCC require `-O1`.
3737

38-
Copy of the code on [Compiler Explorer](https://godbolt.org/z/sGos8zvzE):
38+
Copy of the code on [Compiler Explorer](https://godbolt.org/z/r77c6hf8d):
3939

4040
```c
4141
#include <assert.h>
@@ -58,13 +58,15 @@ typedef struct stack {
5858
size_t element_size;
5959
} stack;
6060

61+
__attribute__((always_inline))
6162
static inline void stack_init(stack *s, void *buffer, size_t element_size, size_t capacity) {
6263
s->data = buffer;
6364
s->size = 0;
6465
s->capacity = capacity;
6566
s->element_size = element_size;
6667
}
6768

69+
__attribute__((always_inline))
6870
static inline ErrorCode stack_push(stack *s, const void *element) {
6971
if (s->size >= s->capacity) {
7072
return STACK_FULL;
@@ -75,6 +77,7 @@ static inline ErrorCode stack_push(stack *s, const void *element) {
7577
return SUCCESS;
7678
}
7779

80+
__attribute__((always_inline))
7881
static inline ErrorCode stack_pop(stack *s, void *out) {
7982
if (s->size == 0) {
8083
return STACK_EMPTY;
@@ -84,6 +87,7 @@ static inline ErrorCode stack_pop(stack *s, void *out) {
8487
return SUCCESS;
8588
}
8689

90+
__attribute__((always_inline))
8791
static inline bool stack_empty(const stack *s) {
8892
return s->size == 0;
8993
}
@@ -120,11 +124,9 @@ int main(void) {
120124
121125
# Generic Hash Map
122126
123-
GCC requires `__attribute__((always_inline))` above `hashmap_insert()` *or* `hashmap_get()`, while Clang does not require it.
127+
Clang requires `-O2`, while GCC requires `-O3`.
124128
125-
GCC requires `-O2`, while Clang requires `-O3`.
126-
127-
Copy of the code on [Compiler Explorer](https://godbolt.org/z/16xhne83s):
129+
Copy of the code on [Compiler Explorer](https://godbolt.org/z/xfo4Wa4vq):
128130
129131
```c
130132
#include <assert.h>
@@ -136,14 +138,15 @@ Copy of the code on [Compiler Explorer](https://godbolt.org/z/16xhne83s):
136138
137139
typedef struct {
138140
bool occupied;
139-
unsigned char key_value[]; // C99 flexible array member
141+
unsigned char key_value[];
140142
} entry;
141143
142144
typedef struct {
143145
void *entries;
144146
size_t capacity, key_size, value_size, entry_size;
145147
} hashmap;
146148
149+
__attribute__((always_inline))
147150
static inline void hashmap_init(hashmap *m, void *buf, size_t ks, size_t vs, size_t cap) {
148151
m->entries = buf;
149152
m->capacity = cap;
@@ -153,15 +156,15 @@ static inline void hashmap_init(hashmap *m, void *buf, size_t ks, size_t vs, siz
153156
memset(buf, 0, cap * m->entry_size);
154157
}
155158
156-
// Naive hashing
159+
__attribute__((always_inline))
157160
static inline size_t hash(const void *key, size_t size) {
158161
size_t h = 0;
159162
for (size_t i = 0; i < size; i++)
160163
h = h * 31 + ((unsigned char*)key)[i];
161164
return h;
162165
}
163166
164-
__attribute__((always_inline)) // Necessary for GCC, not Clang
167+
__attribute__((always_inline))
165168
static inline void hashmap_insert(hashmap *m, const void *key, const void *val) {
166169
size_t idx = hash(key, m->key_size) % m->capacity;
167170
for (size_t i = 0; i < m->capacity; i++) {
@@ -175,6 +178,7 @@ static inline void hashmap_insert(hashmap *m, const void *key, const void *val)
175178
}
176179
}
177180
181+
__attribute__((always_inline))
178182
static inline bool hashmap_get(hashmap *m, const void *key, void *out) {
179183
size_t idx = hash(key, m->key_size) % m->capacity;
180184
for (size_t i = 0; i < m->capacity; i++) {

0 commit comments

Comments
 (0)