Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spurious -Wmaybe-uninitialized warning #276

Closed
chrisjbillington opened this issue Jan 22, 2025 · 7 comments
Closed

Spurious -Wmaybe-uninitialized warning #276

chrisjbillington opened this issue Jan 22, 2025 · 7 comments

Comments

@chrisjbillington
Copy link

chrisjbillington commented Jan 22, 2025

I'm using nanoprintf 0.5.3 and gcc 14:

$ gcc --version
gcc (GCC) 14.2.1 20240910

including nanoprintf in one of my .c files as:

#define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 1
#define NANOPRINTF_IMPLEMENTATION
#include "nanoprintf/nanoprintf.h"

and compiling with (includes and source files omitted):

arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mpure-code -Iextlibs -Wall -Wextra -ffunction-sections -fdata-sections -Wshadow -Wdouble-promotion -Wformat=2 -Wformat-truncation -Wundef -Wno-unused-parameter -fno-common  -Ofast -g -flto -Wl,--gc-sections -s -o <output>.elf <src files>

I get this -Wmaybe-uninitialized warning:

extlibs/nanoprintf/nanoprintf.h: In function 'npf_vpprintf.constprop':
extlibs/nanoprintf/nanoprintf.h:988:32: warning: 'fs.field_width' may be used uninitialized [-Wmaybe-uninitialized]
  988 |     field_pad = fs.field_width - cbuf_len - !!sign_c;
      |                                ^
extlibs/nanoprintf/nanoprintf.h:752:21: note: 'fs.field_width' was declared here
  752 |   npf_format_spec_t fs;

despite the fact that nanoprintf.h appears to contain directives to suppress this warning, which, it seems that due to aggressive optimisation, don't propagate to where they need to be to suppress the warning in all contexts.

Possible solutions:

  1. initialise the struct:
--- nanoprintf.h	2025-01-23 10:25:41.108681396 +1100
+++ nanoprintf.h.orig	2025-01-23 10:25:26.665782298 +1100
@@ -749,7 +749,7 @@
   case NPF_FMT_SPEC_LEN_MOD_##MOD: *(va_arg(args, TYPE *)) = (TYPE)pc_cnt.n; break

 int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) {
-  npf_format_spec_t fs;
+  npf_format_spec_t fs = {0};
   char const *cur = format;
   npf_cnt_putc_ctx_t pc_cnt;
   pc_cnt.pc = pc;
  1. Tell the compiler it can assume things are OK (something like this, but I don't know if this is correct for non-GCC):
--- nanoprintf.h.orig	2025-01-23 10:25:26.665782298 +1100
+++ nanoprintf.h	2025-01-23 10:34:31.392019376 +1100
@@ -188,6 +188,16 @@
   #pragma warning(disable:26812) // enum type is unscoped
 #endif

+#if defined(__GNUC__) && __GNUC__ >= 13
+  #define NPF_ASSUME(x) __attribute__((assume(x)))
+#elif defined(__clang__)
+  #define NPF_ASSUME(x) __builtin_assume(x)
+#elif defined(_MSC_VER)
+  #define NPF_ASSUME(x) __assume(x)
+#else
+  #define NPF_ASSUME(x)
+#endif
+
 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
   #define NPF_NOINLINE __attribute__((noinline))
 #elif defined(_MSC_VER)
@@ -756,6 +766,8 @@
   pc_cnt.ctx = pc_ctx;
   pc_cnt.n = 0;

+  NPF_ASSUME(fs.field_width == 0 || fs.field_width_opt != NPF_FMT_SPEC_OPT_NONE);
+
   while (*cur) {
     int const fs_len = (*cur != '%') ? 0 : npf_parse_format_spec(cur, &fs);
     if (!fs_len) { NPF_PUTC(*cur++); continue; }
@charlesnicholson
Copy link
Owner

Hi @chrisjbillington - thanks for the detailed report! I agree with your assessment; I've seen that when doing an LTO link, GCC seems to "forget" the warning suppressions that come in from the command line. Perhaps it's a different code path or something :(

I'd prefer option one (of the two you presented) in general for safety, tiptoeing around uninitialized memory doesn't make me feel great, despite it leading to small binaries. The last time I messed around with initialization, it caused a measurable size increase that I didn't really want to pay for, though. That was a few versions of GCC ago, though, and sometimes we can sneak things like that into the text stream without any impact, so it's worth a shot.

Option two is interesting, though- I didn't realize that "assume" could suppress warnings like that; I thought it was just for code-gen! If it shuts GCC up, though, then I'm happy to pull it in.

Do you have an appetite for exploring this stuff? I'd be happy to take a pull request. If not, I can probably get to playing with it in the next few days, perhaps this weekend sometime.

@chrisjbillington
Copy link
Author

chrisjbillington commented Jan 24, 2025

A little bit of exploration, I checked to see how program size for my project with various optimisation options is affected by whether the struct is zero-initialised or not. The answer at least for my project is "not much":

This is the file size of the .bin file as extracted with arm-none-eabi-objcopy -O binary <proj>.bin <proj>.elf:

  • -Ofast -flto:
    • non-initialised: 54324
    • initialised: 54328
  • -O2 -flto:
    • non-initialised: 49132
    • initialised: 48944
  • -O2:
    • non-initialised: 52884
    • initialised: 52868
  • -Os:
    • non-initialised: 41500
    • initialised: 41504
  • -O0:
    • non-initialised: 76600
    • initialised: 76616

And very similar results with an older gcc from Ubuntu 22.04:

$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0

This is just one project and I don't know how its use of nanoprintf differs from other projects - I'm only using it for debug printlines, which are all funnelled through a DEBUG function that wraps npf_vsnprintf.

So it seems like initialising is perhaps fine.

Pull request here: #277

@charlesnicholson
Copy link
Owner

Hi @chrisjbillington - could I ask you to try this PR and see if it fixes your issue? I'd rather make the smallest change possible; this one keeps the binary sizes the same because it just moves the specific initialization into a common path.

#278

If it works for you, I'll merge it and cut a release! If not, please let me know and we can brainstorm other solutions.

@chrisjbillington
Copy link
Author

Yep, that one works for me!

Sounds like a good solution.

@charlesnicholson
Copy link
Owner

Merged to main, cutting a new release now.

@charlesnicholson
Copy link
Owner

https://github.com/charlesnicholson/nanoprintf/releases/tag/v0.5.4

Thanks for raising the issue!

@chrisjbillington
Copy link
Author

Thanks for the fix and release, much appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants