/* { dg-require-effective-target int32plus } */

#ifdef __UINT32_TYPE__
typedef __UINT32_TYPE__ uint32_t;
#else
typedef __UINT32_TYPE__ unsigned;
#endif

struct bitfield {
  unsigned char f0:7;
  unsigned char   :1;
  unsigned char f1:7;
  unsigned char   :1;
  unsigned char f2:7;
  unsigned char   :1;
  unsigned char f3:7;
};

struct ok {
  unsigned char f0;
  unsigned char f1;
  unsigned char f2;
  unsigned char f3;
};

union bf_or_uint32 {
  struct ok inval;
  struct bitfield bfval;
};

__attribute__ ((noinline, noclone)) uint32_t
partial_read_le32 (union bf_or_uint32 in)
{
  return in.bfval.f0 | (in.bfval.f1 << 8)
	 | (in.bfval.f2 << 16) | (in.bfval.f3 << 24);
}

__attribute__ ((noinline, noclone)) uint32_t
partial_read_be32 (union bf_or_uint32 in)
{
  return in.bfval.f3 | (in.bfval.f2 << 8)
	 | (in.bfval.f1 << 16) | (in.bfval.f0 << 24);
}

__attribute__ ((noinline, noclone)) uint32_t
fake_read_le32 (char *x, char *y)
{
  unsigned char c0, c1, c2, c3;

  c0 = x[0];
  c1 = x[1];
  *y = 1;
  c2 = x[2];
  c3 = x[3];
  return c0 | c1 << 8 | c2 << 16 | c3 << 24;
}

__attribute__ ((noinline, noclone)) uint32_t
fake_read_be32 (char *x, char *y)
{
  unsigned char c0, c1, c2, c3;

  c0 = x[0];
  c1 = x[1];
  *y = 1;
  c2 = x[2];
  c3 = x[3];
  return c3 | c2 << 8 | c1 << 16 | c0 << 24;
}

__attribute__ ((noinline, noclone)) uint32_t
incorrect_read_le32 (char *x, char *y)
{
  unsigned char c0, c1, c2, c3;

  c0 = x[0];
  c1 = x[1];
  c2 = x[2];
  c3 = x[3];
  *y = 1;
  return c0 | c1 << 8 | c2 << 16 | c3 << 24;
}

__attribute__ ((noinline, noclone)) uint32_t
incorrect_read_be32 (char *x, char *y)
{
  unsigned char c0, c1, c2, c3;

  c0 = x[0];
  c1 = x[1];
  c2 = x[2];
  c3 = x[3];
  *y = 1;
  return c3 | c2 << 8 | c1 << 16 | c0 << 24;
}

int
main ()
{
  union bf_or_uint32 bfin;
  uint32_t out;
  char cin[] = { 0x83, 0x85, 0x87, 0x89 };

  if (sizeof (uint32_t) * __CHAR_BIT__ != 32)
    return 0;
  bfin.inval = (struct ok) { 0x83, 0x85, 0x87, 0x89 };
  out = partial_read_le32 (bfin);
  /* Test what bswap would do if its check are not strict enough instead of
     what is the expected result as there is too many possible results with
     bitfields.  */
  if (out == 0x89878583)
    __builtin_abort ();
  bfin.inval = (struct ok) { 0x83, 0x85, 0x87, 0x89 };
  out = partial_read_be32 (bfin);
  /* Test what bswap would do if its check are not strict enough instead of
     what is the expected result as there is too many possible results with
     bitfields.  */
  if (out == 0x83858789)
    __builtin_abort ();
  out = fake_read_le32 (cin, &cin[2]);
  if (out != 0x89018583)
    __builtin_abort ();
  cin[2] = 0x87;
  out = fake_read_be32 (cin, &cin[2]);
  if (out != 0x83850189)
    __builtin_abort ();
  cin[2] = 0x87;
  out = incorrect_read_le32 (cin, &cin[2]);
  if (out != 0x89878583)
    __builtin_abort ();
  cin[2] = 0x87;
  out = incorrect_read_be32 (cin, &cin[2]);
  if (out != 0x83858789)
    __builtin_abort ();
  return 0;
}
