avrdudes / avr-libc

The AVR-LibC package provides a subset of the standard C library for AVR 8-bit RISC microcontrollers.
https://avrdudes.github.io/avr-libc/
Other
250 stars 54 forks source link

[bug #50811] using -Wl,-u,vfprintf -lprintf_min causes program to grow a lot #654

Open avrs-admin opened 2 years ago

avrs-admin commented 2 years ago

Sun 16 Apr 2017 01:38:22 AM CEST

The documentation seems to be saying that using those flags is supposed to save flash, but without them, a main() that does a single printf("foo\n"); gives me a 330 byte flash, while with them I get a 1330 byte flash.

If I remove the printf() call, I get flash sizes of 1228 or 228 with or without the above mentioned printf-min flags, respectively.

It looks like somehow the printf-min flag ends up dragging in a bunch extra code somehow?

The rest of my linker invocation looks like this:

avr-gcc -mmcu=atmega328p  -lm -Wl,-gc-sections -Wl,-Map=program_to_upload.out.map -o program_to_upload.out main.o

I think probably the documentation ought to mention this possibility, assuming it isn't a bug.

This issue was migrated from https://savannah.nongnu.org/bugs/?50811

avrs-admin commented 2 years ago

Dave Hylands Sun 16 Apr 2017 11:11:13 PM CEST

Calling printf("foo\n"); will be optimized to puts("foo"); so your test isn't really valid.

Try omparing a program which uses printf("foo: %d\n", 42); instead.

avrs-admin commented 2 years ago

Mon 17 Apr 2017 08:18:07 AM CEST

Ok, with printf("foo: %d\n, 42), the program is indeed smaller with by 264 bytes when -Wl,-u,vfprintf -lprintf_min is used than when none of the printf linker flags are used.

I guess this is a wishlist item really: it would be nice if using printf_min didn't end up adding 1000 or so bytes to the program size when variadic vfprintf() invocations don't happen at all. I suppose given the realities of how the linker works that's unlikely.  At least the issue deserves a mention in the discussion of these options in the vfprintf() documentation (that they can end up making your program a lot bigger, rather than smaller).

avrs-admin commented 2 years ago

Dave Hylands Mon 17 Apr 2017 06:46:35 PM CEST

Using _Wl,-u,vfprintf is essentially forcing the symbol vfprintf to be undefined and added to the program. This is what's causing your code size to increase when not otherwise using vfprintf.

It isn't clear to me why that option should be required at all. You should only need to add the -lprintf_min option and that will pull in vprintf, if its used, and won't pull anything in if it isn't.

avrs-admin commented 2 years ago

Mon 17 Apr 2017 08:54:57 PM CEST

You're correct, there's no reason when printf isn't being used to use those linker options.  However:

"Note that if your program doesn't end up calling vfprintf(), using the printf_min linker flags will increase code size because it forces the vfprintf() function to be included even though it isn't used."

avrs-admin commented 2 years ago

Dave Hylands Mon 17 Apr 2017 09:19:55 PM CEST

I guess my point is that this statement (by itself) is false:

"Note that if your program doesn't end up calling vfprintf(), using the printf_min linker flags will increase code size because it forces the vfprintf() function to be included even though it isn't used."

It's only when both options are used together that you get that behaviour.

I think using -Wl,-u,vfprintf is incorrect and there is no need to use that option unless you want to keep unused code in your image.

Personally, I'd find the behaviour of keeping unused code in the image to be the confusing behaviour, not the fact that my debug builds are considerably larger than my release builds.

avrs-admin commented 2 years ago

Joerg Wunsch Mon 17 Apr 2017 10:07:54 PM CEST

Well, the fact that the -u vfprintf needs to be forced is an unfortunate side effect of libprintf_min.a only containing a different definition of vfprintf(), but not e.g. for printf() or other members of the family.

Thus, if you only add -lprintf_min, this library will be processed by a time when the linker has not seen any obvious need to drag the vfprintf symbol in from it, so in effect, it will be ignored.  Later on, when it comes to resolve the symbols needed by (e.g.) printf, it will suddenly also need vfprintf, but libprintf_min.a is already done by that time, so the standard version of vfprintf is used (from libc.a).

We could probably reorganize these libraries to also contain the other printf family members, but 1) that needs to be done by someone (who?), and 2) it won't fix the history, so the documentation has to remain the way it is for at least a while (a couple of years or more).

While the complaint this bug is focussing on is not completely invalid, I think it's practically a non-issue: except for the degraded testcase, nobody would want to drag in the full power (and bloat) of printf() & co. if all he wants to do is printing some constant strings without using any of the formatting functionality.  As soon as a real printf functionality is needed, as seen, there is indeed a minimal saving of space by libprintf_min.a.

avrs-admin commented 2 years ago

Mon 17 Apr 2017 11:04:22 PM CEST

If that's the case, why provide an optimized version of printf() for constant strings at all?  I guess maybe it happens inevitably due to compiler machinations.

Anyway, at least one person (me) hit this issue and it took me some time to find the culprit.  I set LDFLAGS for the printf_min long ago with the idea of generally keeping program size low, and this turns out to be a very poor general policy.  The current docs don't give any indication of this, while they do describe smaller savings related to vfprintf choice in the presence of printf().  That's inconsistent and somewhat misleading.

avrs-admin commented 2 years ago

Joerg Wunsch Tue 18 Apr 2017 08:02:58 AM CEST

I guess maybe it happens inevitably due to compiler machinations.

Exactly, it's a compiler thing we cannot influence.

avrs-admin commented 2 years ago

Georg-Johann Lay Tue 09 May 2017 10:55:01 PM CEST

Re. the "compiler thing" mentioned above:

avr-gcc might replace a call to printf by a call to puts provided the arguments of printf allows to do so.  You can avoid this optimization by means of -fno-builtin-printf which takes away any knowledge (except prototype information) about printf from the compiler.  This will result in a call to printf (which you are using anyway), saving the call to fputs.