Donʼt abuse narrow integers
Sep. 18th, 2025 12:08 pmAt least third embedded-related project that I found being used constructions like:
Even there is known that the value is limited to be fully present in such a narrow type, it is harmful to use it this way. C/C++ compilers shall apply width extension to a value on each iteration, due to the usual «integral promotion», this is 1) an excessive action, 2) may mask the problem around the maximal value, in case like:
if nslots == 256, the index is formally valid, but the loop will be forever, because after 255 the next value of `i` will be 0, and the exit condition never succeeds.
In machine code, this entails either iteration on registers like AL (in x86), which causes false sharing with rest of EAX (RAX), or an explicit masking (width extension):
GCC, x86:
GCC, AArch64:
And even if there is no such an effect, there is no need to use such types for local variables, they have sense exclusively in external interfaces like structure members (and in fitting verification for such members).
uint8_t i;
for (i = 0; i < 100; ++i) {
do something
}
Even there is known that the value is limited to be fully present in such a narrow type, it is harmful to use it this way. C/C++ compilers shall apply width extension to a value on each iteration, due to the usual «integral promotion», this is 1) an excessive action, 2) may mask the problem around the maximal value, in case like:
uint8_t slot;
for (i = 0; i < nslots; ++i) {
do something
}
if nslots == 256, the index is formally valid, but the loop will be forever, because after 255 the next value of `i` will be 0, and the exit condition never succeeds.
In machine code, this entails either iteration on registers like AL (in x86), which causes false sharing with rest of EAX (RAX), or an explicit masking (width extension):
void foo(unsigned n) {
uint8_t i;
for (i = 0; i < n; ++i) {
moo(i);
}
}
GCC, x86:
foo:
testl %edi, %edi
je .L9
pushq %rbp
movl %edi, %ebp
xorl %edi, %edi
pushq %rbx
xorl %ebx, %ebx
subq $8, %rsp
.L3:
call moo@PLT
addl $1, %ebx
movzbl %bl, %edi ; <-- Oops!
cmpl %ebp, %edi
jb .L3
addq $8, %rsp
popq %rbx
popq %rbp
ret
GCC, AArch64:
foo:
cbz w0, .L9
stp x19, x20, [sp, -32]!
mov w20, w0
mov w19, 0
str x30, [sp, 16]
.L3:
mov w0, w19
bl moo
add w1, w19, 1
and w19, w1, 255
cmp w20, w1, uxtb ; <-- Oops!
bhi .L3
ldr x30, [sp, 16]
ldp x19, x20, [sp], 32
ret
.L9:
ret
And even if there is no such an effect, there is no need to use such types for local variables, they have sense exclusively in external interfaces like structure members (and in fitting verification for such members).