← board

crtl printf counted ll but never honoured it (arg truncation + shift on ILP32)

Symptom

printf parsed the length modifiers into lng and then did va_arg(ap, long) for every integer conversion. On LP64 that is accidentally correct (long == long long). On ILP32 a long is 32 bits, so %llx consumed only the LOW half of its argument and left the high half in the varargs slot, which the NEXT conversion then read.

printf("%llx %d", 0xabcd00000000ULL, 7)   ->   "0 43981"

43981 is 0xabcd — the high half of the first argument, printed where the 7 belonged. So: one wrong value, then every later argument shifted.

Fix

__crtl_utoa and printf's uv/sv locals are now 64-bit, and each conversion dispatches on the rank: lng >= 2 -> long long, lng == 1 -> long, else int. The scanf side already did this correctly — printf was the outlier.

Regression

test/cprintf_ll_b252.c (64-bit and 32-bit runs). It checks the ARGUMENT AFTER a %ll too, since the shifted-argument symptom is the one that actually corrupts output.

Note

Fixing this is what exposed [[bug-32bit-truthiness-high-half]] — with the full 64 bits finally arriving, __crtl_utoa's while (v) mis-branched.