Commit 3a078045 authored by Alessandro Rubini's avatar Alessandro Rubini

pp_printf: use new upstream master e17d874

This picks the following commits (most recent on top):

   e17d874 README: update for the new code base
   d391e62 added 64-bit example/test
   670b5e8 Makefile: support 64-bit option; add div64.c
   c2384cd vsprintf-full: offer 64-bit printing as an option
   220b19e vsprintf-full: simplify a little
   526daa6 vsprintf-full: remove unsed code for %p printing
   1a28825 vsprintf-full: prevent a warning when a pointer is 16 bits

We are using 64-bit prints currently, but wrpc-sw does.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <>
parent 3ef53ace
\ No newline at end of file
......@@ -20,12 +20,18 @@ obj-$(CONFIG_PRINTF_XINT) += vsprintf-xint.o
# set full as a default if nothing is selected
obj-y ?= vsprintf-full.o
# If you want to pick the local div64.c, define this as "y"
obj-$(CONFIG_PRINTF_LOCAL_DIV64) += div64.o
obj-y += printf.o
# There is a static variable in pp-printf.c to accumulate stuff
# Targets. You may want to make them different in your package
......@@ -37,6 +43,9 @@ pp-printf.o: $(obj-y)
example-printf: example-printf.c pp-printf.o
$(CC) $(CFLAGS) $^ -o $@
# build a special example/test for 64-bit prints (not built by default)
example-printf64: example-printf64.o pp-printf.o div64.o
$(CC) -c $(CFLAGS) $< -o $@
......@@ -16,6 +16,7 @@ The printf engine, vsprintf(), comes in four flavours:
In summary:
- the "full" version is a normal printf (GPL2, from older Linux kernel)
(as a build-time option, it supports int64_t items, as "ll" or "L")
- the "xint" version accepts all formats and prints hex and int only
......@@ -42,6 +43,10 @@ used in the Linux kernel. It is licensed according to the GNU GPL
version 2. It includes all formats and prefixes and so on. It is
clearly bugless because everybody is using it.
In July 2014 I made some changes, which includes adding 64-bit items,
but the feature is disabled by default. There is a whole section later
in this file about this.
It is selected at compile time by setting the make variable
"CONFIG_PRINTF_FULL" to "y". You can do that in the environment,
or use Kconfig in your application.
......@@ -72,6 +77,10 @@ Result (as you see, format modifiers are respected):
Footprint: 1400-3200 bytes, plus 100-400 bytes for the frontend.
(With the 2014 changes the footprint is a few hundred bytes less, but
I lacked the time to make comprehensive builds; if you enable 64-bit
support, you'll get a size impact of a few hunderd bytes: for ARM,
around 200 bytes in vsprintf-full plus 200 for div64.o).
The xint implementation in detail
......@@ -164,10 +173,83 @@ the constant strings in .rodata. I don't support this in the package,
though, and I discourage from doing it, for the usual
preprocessor-related reasons.
int64_t support
vsprintf-full.c now supports printing 64-bit items. It works in all
platforms, including the 16-bit AVR CPU. The feature is disabled by
default because it has run-time impact (all integer conversions are
performed using 64-bit division.
Moreover, 64-bit supports requires 64-bit division. As customary in
the kernel/bootloader world we avoid picking the libgcc
implementation, and rely on the smaller __div64_32() function,
instead, which only supports a 32-bit divisor and thus a 32-bit
remainder; but this is all we need. The function is available in
"lib64.c", by Bernardo Innocenti, which is used in the kernel and many
other projects.
So, to enable 64-bit support in pp-printf, please use
in your environment or command-line of make. If your project uses
Kconfig, you'll rely on it instead (I'm running pp-printf in 3
Kconfig-based projects and it works great).
If you already have __div64_32() in your project, you are done:
vsprintf-full.o will have an undefined symbol, that your final link
will resolve. If you miss the function, you can use the local
version. To do that, set
in your environment or the command line of make.
The test program for 64-bit formats is not built by default. To build
it, you should
make CONFIG_PRINTF_64BIT=y example-printf64
This automatically picks div64.o: you'll get a link error if you also
set CONFIG_PRINTF_LOCAL_DIV64=y. This is not a problem as the test
is meant to be run locally, only once or so.
The test prints one thousand, one million and so on using the various
formats, ending with the maximum positive and maximum negative::
positive: 1000
negative: -1000
neg-unsigned: 18446744073709550616
pos-hex: 0x00000000000003e8
pos-HEX: 0x00000000000003E8
neg-hex: 0xfffffffffffffc18
positive: 1000000000000000000
negative: -1000000000000000000
neg-unsigned: 17446744073709551616
pos-hex: 0x0de0b6b3a7640000
pos-HEX: 0x0DE0B6B3A7640000
neg-hex: 0xf21f494c589c0000
max positive: 9223372036854775807
max negative: -9223372036854775808
You may want to verify with your own target platform, before using the
code in production (you can avoid that if you trust me, but I wouldn't
blindly trust programmers, in general).
Footprint of the various implementations
NOTE: these figures refer to the 2012 master. The "full"
implementation is now a few hundred bytes smaller.
Also, I made no measures for the 64-bit version, which adds a few
hundred bytes (and run-time overhead of every integer print).
This table excludes the static buffer (256 in .bss by default) and
only lists the code size (command "size", column "text"), compiled
with -Os as for this Makefile.
/* This file in ppsi is a subset of lib/div64.c in Linux source code */
* Copyright (C) 2003 Bernardo Innocenti <>
* Based on former do_div() implementation from asm-parisc/div64.h:
* Copyright (C) 1999 Hewlett-Packard Co
* Copyright (C) 1999 David Mosberger-Tang <>
* Generic C version of 64bit/32bit division and modulo, with
* 64bit result and 32bit remainder.
* The fast case for (n>>32 == 0) is handled inline by do_div().
* Code generated for this function might be very inefficient
* for some CPUs. __div64_32() can be overridden by linking arch-specific
* assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S.
#include <stdint.h>
uint32_t __div64_32(uint64_t *n, uint32_t base)
uint64_t rem = *n;
uint64_t b = base;
uint64_t res, d = 1;
uint32_t high = rem >> 32;
/* Reduce the thing a bit first */
res = 0;
if (high >= base) {
high /= base;
res = (uint64_t) high << 32;
rem -= (uint64_t) (high*base) << 32;
while ((int64_t)b > 0 && b < rem) {
b = b+b;
d = d+d;
do {
if (rem >= b) {
rem -= b;
res += d;
b >>= 1;
d >>= 1;
} while (d);
*n = res;
return rem;
#include <stdint.h>
#include <pp-printf.h>
int main(int argc, char **argv)
long long ll; /* I'd use "int64_t" but gcc wars about formats */
for (ll = 1000; ll < (1LL << 61); ll *= 1000) {
pp_printf("positive: %20lli\n", ll);
pp_printf("negative: %20lli\n", -ll);
pp_printf("neg-unsigned: %20llu\n", -ll);
pp_printf("pos-hex: 0x%016llx\n", ll);
pp_printf("pos-HEX: 0x%016llX\n", ll);
pp_printf("neg-hex: 0x%016Lx\n", -ll);
ll = (1LL <<63) - 1;
pp_printf("max positive: %20lli\n", ll);
ll = (1LL << 63);
pp_printf("max negative: %20lli\n", ll);
return 0;
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment