Can anyone name a missing feature of ARM32 GCC 11 which arrives with GCC 12 and which is relevant to embedded development?
As you mention you only use C, you might be interested in the
Compiler support for C23 and C99 page at cppreference.com.
GCC 12 adds digit separators (so that
0xDEADBEEFCAKECAFE = 0xDEAD'BEEF'CAKE'CAFE) in literal constants,
#elifdef and
#elifndef preprocessor directives, so nothing really important.
However, GCC 13 adds
constexpr support which I believe is a major enhancement to C. Basically, it allows constructs like
static constexpr size_t pool_size = 16 * 23 + 42 * 25; static unsigned char pool_data[pool_size];where the compiler computes and verifies
pool_size at compile time. If you use floating-point values, then the values are computed at compile time (using the floating point implementation on the host doing the compilation). These are
immutable, and the compiler can hard-code their value in the machine code, even if it reserves space for the object (which it doesn't need to, unless it is externally visible or someone takes its address using
&).
Indeed, using
static constexpr size_t hashtable_entries = 31*33 + 1;
static struct hashtable_entry *hashtable_entry[hashtable_entries];
static inline struct hashtable_entry *hashtable_entry_for(size_t hash) {
return hashtable_entry[hash % hashtable_entries];
}
static inline struct hashtable_entry **hashtable_entry_ref(size_t hash) {
return hashtable_entry + (hash % hashtable_entries);
}
for a fixed-size hash table, whose size is computed at compile time, allows the compiler to optimize the
hash % hashtable_entries into
hash & 1023 (and GCC definitely does this for the above).
Thus far, we've had to use preprocessor macros for such sizes, and their values weren't checked for overflow and such;
constexpr are. So do think of them as more similar to preprocessor macros than to
const variables or objects, for practical use cases. If you have configuration preprocessor constants set at build time, this allows using them to calculate complex sizes and derivative values at compile time, and verify those fit their type.
Additionally, with GCC 9 and later you can use
_Static_assert() (and
static_assert() in GCC 13 and later, or when using
<assert.h>) to verify requirements at compile time. For example,
static_assert(hashtable_entries > 0, "hashtable_entries is too small"); static_assert(hashtable_entries < MAX_HASHTABLE_ENTRIES, "hashtable_entries is too large");will be evaluated at compile time, and will not generate any runtime machine code at all.
These two additions can really help a careful embedded C developer to calculate and verify compile-time constants, leading to better and more thoroughly verified and compiler-understood code. These alone would be enough for me personally to switch from C99 to C23 as the tools mature, but there are more goodies there. To me, the era after C99 up till C17 was pretty much dark ages for C, but C23 looks very, very promising.