lib/test_scanf: split up number parsing test routines
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 Sep 2021 18:04:03 +0000 (11:04 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 Sep 2021 18:04:03 +0000 (11:04 -0700)
It turns out that gcc has real trouble merging all the temporary
on-stack buffer allocation.  So despite the fact that their lifetimes do
not overlap, gcc will allocate stack for all of them when they have
different types.  Which they do in the number scanning test routines.

This is unfortunate in general, but with lots of test-cases in one
function, it becomes a real problem.  gcc will allocate a huge stack
frame for no actual good reason.

We have tried to counteract this tendency of gcc not merging stack slots
(see "-fconserve-stack"), but that has limited effect (and should be on
by default these days, iirc).

So with all the debug options enabled on an i386 allmodconfig build, we
end up with overly big stack frames, and the resulting stack frame size
warnings (now errors):

   lib/test_scanf.c: In function ‘numbers_list_field_width_val_width’:
   lib/test_scanf.c:530:1: error: the frame size of 2088 bytes is larger than 2048 bytes [-Werror=frame-larger-than=]
     530 | }
         | ^
   lib/test_scanf.c: In function ‘numbers_list_field_width_typemax’:
   lib/test_scanf.c:488:1: error: the frame size of 2568 bytes is larger than 2048 bytes [-Werror=frame-larger-than=]
     488 | }
         | ^
   lib/test_scanf.c: In function ‘numbers_list’:
   lib/test_scanf.c:437:1: error: the frame size of 2088 bytes is larger than 2048 bytes [-Werror=frame-larger-than=]
     437 | }
         | ^

In this particular case, the reasonably straightforward solution is to
just split out the test routines into multiple more targeted versions.
That way we don't have one huge stack, but several smaller ones, and
they aren't active all at the same time.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
lib/test_scanf.c

index abae88848972f8eeae5338b8a8c35f577d824f92..b620cf7de503542a98f38c45c76e9043b242746e 100644 (file)
@@ -398,7 +398,7 @@ do {                                                                                \
        test_array_8(fn, expect, test_buffer, fmt_buffer, result);              \
 } while (0)
 
-static void __init numbers_list(const char *delim)
+static void __init numbers_list_ll(const char *delim)
 {
        numbers_list_8(unsigned long long, "%llu",   delim, "llu", check_ull);
        numbers_list_8(long long,          "%lld",   delim, "lld", check_ll);
@@ -406,28 +406,40 @@ static void __init numbers_list(const char *delim)
        numbers_list_8(unsigned long long, "%llx",   delim, "llx", check_ull);
        numbers_list_8(unsigned long long, "0x%llx", delim, "llx", check_ull);
        numbers_list_8(long long,          "0x%llx", delim, "lli", check_ll);
+}
 
+static void __init numbers_list_l(const char *delim)
+{
        numbers_list_8(unsigned long,      "%lu",    delim, "lu", check_ulong);
        numbers_list_8(long,               "%ld",    delim, "ld", check_long);
        numbers_list_8(long,               "%ld",    delim, "li", check_long);
        numbers_list_8(unsigned long,      "%lx",    delim, "lx", check_ulong);
        numbers_list_8(unsigned long,      "0x%lx",  delim, "lx", check_ulong);
        numbers_list_8(long,               "0x%lx",  delim, "li", check_long);
+}
 
+static void __init numbers_list_d(const char *delim)
+{
        numbers_list_8(unsigned int,       "%u",     delim, "u", check_uint);
        numbers_list_8(int,                "%d",     delim, "d", check_int);
        numbers_list_8(int,                "%d",     delim, "i", check_int);
        numbers_list_8(unsigned int,       "%x",     delim, "x", check_uint);
        numbers_list_8(unsigned int,       "0x%x",   delim, "x", check_uint);
        numbers_list_8(int,                "0x%x",   delim, "i", check_int);
+}
 
+static void __init numbers_list_h(const char *delim)
+{
        numbers_list_8(unsigned short,     "%hu",    delim, "hu", check_ushort);
        numbers_list_8(short,              "%hd",    delim, "hd", check_short);
        numbers_list_8(short,              "%hd",    delim, "hi", check_short);
        numbers_list_8(unsigned short,     "%hx",    delim, "hx", check_ushort);
        numbers_list_8(unsigned short,     "0x%hx",  delim, "hx", check_ushort);
        numbers_list_8(short,              "0x%hx",  delim, "hi", check_short);
+}
 
+static void __init numbers_list_hh(const char *delim)
+{
        numbers_list_8(unsigned char,      "%hhu",   delim, "hhu", check_uchar);
        numbers_list_8(signed char,        "%hhd",   delim, "hhd", check_char);
        numbers_list_8(signed char,        "%hhd",   delim, "hhi", check_char);
@@ -436,11 +448,16 @@ static void __init numbers_list(const char *delim)
        numbers_list_8(signed char,        "0x%hhx", delim, "hhi", check_char);
 }
 
-/*
- * List of numbers separated by delim. Each field width specifier is the
- * maximum possible digits for the given type and base.
- */
-static void __init numbers_list_field_width_typemax(const char *delim)
+static void __init numbers_list(const char *delim)
+{
+       numbers_list_ll(delim);
+       numbers_list_l(delim);
+       numbers_list_d(delim);
+       numbers_list_h(delim);
+       numbers_list_hh(delim);
+}
+
+static void __init numbers_list_field_width_ll(const char *delim)
 {
        numbers_list_fix_width(unsigned long long, "%llu",   delim, 20, "llu", check_ull);
        numbers_list_fix_width(long long,          "%lld",   delim, 20, "lld", check_ll);
@@ -448,7 +465,10 @@ static void __init numbers_list_field_width_typemax(const char *delim)
        numbers_list_fix_width(unsigned long long, "%llx",   delim, 16, "llx", check_ull);
        numbers_list_fix_width(unsigned long long, "0x%llx", delim, 18, "llx", check_ull);
        numbers_list_fix_width(long long,          "0x%llx", delim, 18, "lli", check_ll);
+}
 
+static void __init numbers_list_field_width_l(const char *delim)
+{
 #if BITS_PER_LONG == 64
        numbers_list_fix_width(unsigned long,   "%lu",       delim, 20, "lu", check_ulong);
        numbers_list_fix_width(long,            "%ld",       delim, 20, "ld", check_long);
@@ -464,21 +484,30 @@ static void __init numbers_list_field_width_typemax(const char *delim)
        numbers_list_fix_width(unsigned long,   "0x%lx",     delim, 10, "lx", check_ulong);
        numbers_list_fix_width(long,            "0x%lx",     delim, 10, "li", check_long);
 #endif
+}
 
+static void __init numbers_list_field_width_d(const char *delim)
+{
        numbers_list_fix_width(unsigned int,    "%u",        delim, 10, "u", check_uint);
        numbers_list_fix_width(int,             "%d",        delim, 11, "d", check_int);
        numbers_list_fix_width(int,             "%d",        delim, 11, "i", check_int);
        numbers_list_fix_width(unsigned int,    "%x",        delim, 8,  "x", check_uint);
        numbers_list_fix_width(unsigned int,    "0x%x",      delim, 10, "x", check_uint);
        numbers_list_fix_width(int,             "0x%x",      delim, 10, "i", check_int);
+}
 
+static void __init numbers_list_field_width_h(const char *delim)
+{
        numbers_list_fix_width(unsigned short,  "%hu",       delim, 5, "hu", check_ushort);
        numbers_list_fix_width(short,           "%hd",       delim, 6, "hd", check_short);
        numbers_list_fix_width(short,           "%hd",       delim, 6, "hi", check_short);
        numbers_list_fix_width(unsigned short,  "%hx",       delim, 4, "hx", check_ushort);
        numbers_list_fix_width(unsigned short,  "0x%hx",     delim, 6, "hx", check_ushort);
        numbers_list_fix_width(short,           "0x%hx",     delim, 6, "hi", check_short);
+}
 
+static void __init numbers_list_field_width_hh(const char *delim)
+{
        numbers_list_fix_width(unsigned char,   "%hhu",      delim, 3, "hhu", check_uchar);
        numbers_list_fix_width(signed char,     "%hhd",      delim, 4, "hhd", check_char);
        numbers_list_fix_width(signed char,     "%hhd",      delim, 4, "hhi", check_char);
@@ -489,9 +518,18 @@ static void __init numbers_list_field_width_typemax(const char *delim)
 
 /*
  * List of numbers separated by delim. Each field width specifier is the
- * exact length of the corresponding value digits in the string being scanned.
+ * maximum possible digits for the given type and base.
  */
-static void __init numbers_list_field_width_val_width(const char *delim)
+static void __init numbers_list_field_width_typemax(const char *delim)
+{
+       numbers_list_field_width_ll(delim);
+       numbers_list_field_width_l(delim);
+       numbers_list_field_width_d(delim);
+       numbers_list_field_width_h(delim);
+       numbers_list_field_width_hh(delim);
+}
+
+static void __init numbers_list_field_width_val_ll(const char *delim)
 {
        numbers_list_val_width(unsigned long long, "%llu",   delim, "llu", check_ull);
        numbers_list_val_width(long long,          "%lld",   delim, "lld", check_ll);
@@ -499,28 +537,40 @@ static void __init numbers_list_field_width_val_width(const char *delim)
        numbers_list_val_width(unsigned long long, "%llx",   delim, "llx", check_ull);
        numbers_list_val_width(unsigned long long, "0x%llx", delim, "llx", check_ull);
        numbers_list_val_width(long long,          "0x%llx", delim, "lli", check_ll);
+}
 
+static void __init numbers_list_field_width_val_l(const char *delim)
+{
        numbers_list_val_width(unsigned long,   "%lu",       delim, "lu", check_ulong);
        numbers_list_val_width(long,            "%ld",       delim, "ld", check_long);
        numbers_list_val_width(long,            "%ld",       delim, "li", check_long);
        numbers_list_val_width(unsigned long,   "%lx",       delim, "lx", check_ulong);
        numbers_list_val_width(unsigned long,   "0x%lx",     delim, "lx", check_ulong);
        numbers_list_val_width(long,            "0x%lx",     delim, "li", check_long);
+}
 
+static void __init numbers_list_field_width_val_d(const char *delim)
+{
        numbers_list_val_width(unsigned int,    "%u",        delim, "u", check_uint);
        numbers_list_val_width(int,             "%d",        delim, "d", check_int);
        numbers_list_val_width(int,             "%d",        delim, "i", check_int);
        numbers_list_val_width(unsigned int,    "%x",        delim, "x", check_uint);
        numbers_list_val_width(unsigned int,    "0x%x",      delim, "x", check_uint);
        numbers_list_val_width(int,             "0x%x",      delim, "i", check_int);
+}
 
+static void __init numbers_list_field_width_val_h(const char *delim)
+{
        numbers_list_val_width(unsigned short,  "%hu",       delim, "hu", check_ushort);
        numbers_list_val_width(short,           "%hd",       delim, "hd", check_short);
        numbers_list_val_width(short,           "%hd",       delim, "hi", check_short);
        numbers_list_val_width(unsigned short,  "%hx",       delim, "hx", check_ushort);
        numbers_list_val_width(unsigned short,  "0x%hx",     delim, "hx", check_ushort);
        numbers_list_val_width(short,           "0x%hx",     delim, "hi", check_short);
+}
 
+static void __init numbers_list_field_width_val_hh(const char *delim)
+{
        numbers_list_val_width(unsigned char,   "%hhu",      delim, "hhu", check_uchar);
        numbers_list_val_width(signed char,     "%hhd",      delim, "hhd", check_char);
        numbers_list_val_width(signed char,     "%hhd",      delim, "hhi", check_char);
@@ -529,6 +579,19 @@ static void __init numbers_list_field_width_val_width(const char *delim)
        numbers_list_val_width(signed char,     "0x%hhx",    delim, "hhi", check_char);
 }
 
+/*
+ * List of numbers separated by delim. Each field width specifier is the
+ * exact length of the corresponding value digits in the string being scanned.
+ */
+static void __init numbers_list_field_width_val_width(const char *delim)
+{
+       numbers_list_field_width_val_ll(delim);
+       numbers_list_field_width_val_l(delim);
+       numbers_list_field_width_val_d(delim);
+       numbers_list_field_width_val_h(delim);
+       numbers_list_field_width_val_hh(delim);
+}
+
 /*
  * Slice a continuous string of digits without field delimiters, containing
  * numbers of varying length, using the field width to extract each group