/* PR middle-end/78476 - snprintf(0, 0, ...) with known arguments not
   optimized away
   { dg-do compile }
   { dg-options "-O2 -fdump-tree-optimized" }
   { dg-require-effective-target int32plus } */

#define CAT(s, n)   s ## n
#define FAIL(line)  CAT (failure_on_line_, line)
#define PASS(line)  CAT (success_on_line_, line)

/* Emit a call to a function named failure_on_line_NNN when VALUE
   is not equal to the constant EXPECTED, otherwise emit a call to
   function success_on_line_NNN.  */
#define ASSERT(value, expected)			\
  do {						\
    extern void FAIL (__LINE__)(int);		\
    extern void PASS (__LINE__)(int);		\
    if (value == expected)			\
      PASS (__LINE__)(value);			\
    else					\
      FAIL (__LINE__)(value);			\
  } while (0)

/* Verify that EXPECT == snprintf(0, 0, ...).  */
#define EQL(expect, ...)				\
  do {							\
    int n = __builtin_snprintf (0, 0, __VA_ARGS__);	\
    ASSERT (n, expect);					\
  } while (0)

int ival (int i) { return i; }

/* Generate a signed int value in the specified range.  */

static int
int_range (int min, int max)
{
  extern int int_value (void);
  int val = int_value ();
  if (val < min || max < val)
    val = min;
  return val;
}

#define R(min, max) int_range (min, max)

void test_arg_int (int i, int n)
{
  EQL (1, "%i", ival (0));
  EQL (1, "%i", ival (1));
  EQL (2, "%i%i", ival (0), ival (1));
  EQL (3, "%i%i%i", ival (0), ival (1), ival (9));
  EQL (5, "%i %i %i", ival (0), ival (1), ival (9));

  EQL (5, "%i %i %i", ival (0), ival (1), ival (9));

  EQL (13, "%hhu.%hhu.%hhu.%hhu", ival (23), ival (78), ival (216), ival (135));

  for (i = 0; i != 9; ++i)
    EQL (1, "%i", i);

  for (i = -n; i != n; ++i)
    EQL (8, "%08x", i);

  /*  As a special case, a precision of zero with an argument of zero
      results in zero bytes (unless modified by width).  */
  EQL (0, "%.0d", ival (0));
  EQL (0, "%.0i", ival (0));
  EQL (0, "%.0o", ival (0));
  EQL (0, "%.0u", ival (0));
  EQL (0, "%.0x", ival (0));

  EQL (0, "%.*d", 0, ival (0));
  EQL (0, "%.*i", 0, ival (0));
  EQL (0, "%.*o", 0, ival (0));
  EQL (0, "%.*u", 0, ival (0));
  EQL (0, "%.*x", 0, ival (0));

  EQL (1, "%1.0d", ival (0));
  EQL (1, "%1.0i", ival (0));
  EQL (1, "%1.0o", ival (0));
  EQL (1, "%1.0u", ival (0));
  EQL (1, "%1.0x", ival (0));

  EQL (4, "%hhi", R (-128, -127));
  EQL (3, "%hhi", R ( -99,  -10));
  EQL (2, "%hhi", R (  -9,   -1));
  EQL (1, "%hhi", R (   0,    9));
  EQL (1, "%hhi", R (   0,    9));

  EQL (1, "%1.0hhi", R (   0,    1));
  EQL (1, "%1.1hhi", R (   0,    9));
  EQL (2, "%1.2hhi", R (   0,    9));
  EQL (3, "%1.3hhi", R (   0,    9));

  EQL (1, "%hhi", R (1024, 1033));
  EQL (2, "%hhi", R (1034, 1123));
  EQL (1, "%hhu", R (1024, 1033));
  EQL (2, "%hhu", R (1034, 1123));
}

void test_arg_string (const char *s)
{
  EQL ( 0, "%-s", "");
  EQL ( 1, "%%");
  EQL ( 1, "%-s", "1");
  EQL ( 2, "%-s", "12");
  EQL ( 3, "%-s", "123");
  EQL ( 5, "s=%s", "123");
  EQL (10, "%%s=\"%s\"", "12345");

  EQL ( 1, "%.*s", 1, "123");
  EQL ( 2, "%.*s", 2, "123");
  EQL ( 3, "%.*s", 3, "123");
  EQL ( 3, "%.*s", 4, "123");

  EQL ( 1, "%1.*s", 1, "123");
  EQL ( 2, "%1.*s", 2, "123");
  EQL ( 3, "%1.*s", 3, "123");
  EQL ( 3, "%1.*s", 4, "123");
  EQL ( 4, "%4.*s", 1, "123");
  EQL ( 4, "%4.*s", 2, "123");
  EQL ( 4, "%4.*s", 3, "123");
  EQL ( 4, "%4.*s", 4, "123");
  EQL ( 4, "%4.*s", 5, "123");

  const char *a = "123";
  const char *b = "456";

  EQL ( 3, "%-s", s ? a : b);
  EQL ( 0, "%.0s", s);
  EQL ( 1, "%1.1s", s);
  EQL ( 2, "%2.2s", s);
  EQL ( 2, "%2.1s", s);
}

void test_arg_multiarg (int i, double d)
{
  EQL (16, "%12i %s", i, "abc");
  EQL (16, "%*i %s", 12, i, "abc");
}

/* Verify that EXPECT == vsnprintf(0, 0, ...).  */
#define EQLv(expect, fmt, va)				\
  do {							\
    int n = __builtin_vsnprintf (0, 0, fmt, va);	\
    ASSERT (n, expect);					\
  } while (0)

void test_va_int (__builtin_va_list va)
{
  EQLv ( 2, "%02hhx", va);
  EQLv ( 4, "%04hx", va);
}

void test_va_multiarg (__builtin_va_list va)
{
  EQLv ( 8, "%8x", va);
  EQLv ( 8, "% 8x", va);
  EQLv ( 9, "%9x", va);
  EQLv (11, "%11o", va);
  EQLv (12, "%12o", va);

  EQLv (16, "%12i %3.2s", va);
}


/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized"} }
   { dg-final { scan-tree-dump-not "snprintf" "optimized"} } */
