/* { dg-do run } */
/* { dg-require-effective-target ppc_float128_sw } */
/* { dg-options "-mdejagnu-cpu=power7 -O2 -mfloat128 -lm" } */

#ifdef DEBUG
#include <stdio.h>
#endif

#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <math.h>

#if defined(__BIG_ENDIAN__)
struct ieee128 {
  uint64_t upper;
  uint64_t lower;
};

#elif defined(__LITTLE_ENDIAN__)
struct ieee128 {
  uint64_t lower;
  uint64_t upper;
};

#else
#error "Unknown system"
#endif

union ieee_union {
  __float128 f128;
  struct ieee128 st128;
};

#ifdef DEBUG
static int num_errors = 0;

__attribute__((__noinline__))
static void
failure (int expected, int got, __float128 x)
{
  unsigned sign;
  unsigned exponent;
  uint64_t mantissa1;
  uint64_t mantissa2;
  uint64_t upper;
  uint64_t lower;

  union ieee_union u;

  u.f128 = x;
  upper  = u.st128.upper;
  lower  = u.st128.lower;

  sign      = (unsigned)((upper >> 63) & 1);
  exponent  = (unsigned)((upper >> 48) & ((((uint64_t)1) << 16) - 1));
  mantissa1 = (upper & ((((uint64_t)1) << 48) - 1));
  mantissa2 = lower;

  printf ("Expected %d, got %d, %c 0x%.4x 0x%.12" PRIx64 " 0x%.16" PRIx64,
	  expected, got,
	  sign ? '-' : '+',
	  exponent,
	  mantissa1,
	  mantissa2);

  num_errors++;
}

#else

#define failure(E, G, F) abort ()
#endif

__attribute__((__noinline__))
static void
test_signbit_arg (__float128 f128, int expected)
{
  int sign = __builtin_signbit (f128);

  if ((expected != 0 && sign == 0)
      || (expected == 0 && sign != 0))
    failure (f128, expected, sign);
}

__attribute__((__noinline__))
static void
test_signbit_mem (__float128 *ptr, int expected)
{
  int sign = __builtin_signbit (*ptr);

  if ((expected != 0 && sign == 0)
      || (expected == 0 && sign != 0))
    failure (*ptr, expected, sign);
}

__attribute__((__noinline__))
static void
test_signbit_gpr (__float128 *ptr, int expected)
{
  __float128 f128 = *ptr;
  int sign;

  __asm__ (" # %0" : "+r" (f128));

  sign = __builtin_signbit (f128);
  if ((expected != 0 && sign == 0)
      || (expected == 0 && sign != 0))
    failure (f128, expected, sign);
}

__attribute__((__noinline__))
static void
test_signbit (__float128 f128, int expected)
{
#ifdef DEBUG
  union ieee_union u;
  u.f128 = f128;
  printf ("Expecting %d, trying %-5g "
	  "(0x%.16" PRIx64 " 0x%.16" PRIx64 ")\n",
	  expected, (double)f128,
	  u.st128.upper, u.st128.lower);
#endif

  test_signbit_arg (f128,  expected);
  test_signbit_mem (&f128, expected);
  test_signbit_gpr (&f128, expected);
}

int
main (void)
{
  union ieee_union u;

  test_signbit (+0.0q, 0);
  test_signbit (+1.0q, 0);

  test_signbit (-0.0q, 1);
  test_signbit (-1.0q, 1);

  test_signbit (__builtin_copysign (__builtin_infq (), +1.0q), 0);
  test_signbit (__builtin_copysign (__builtin_infq (), -1.0q), 1);

  test_signbit (__builtin_copysign (__builtin_nanq (""), +1.0q), 0);
  test_signbit (__builtin_copysign (__builtin_nanq (""), -1.0q), 1);

  /* force the bottom double word to have specific bits in the 'sign' bit to
     make sure we are picking the right word.  */
  u.f128 = 1.0q;
  u.st128.lower = 0ULL;
  test_signbit (u.f128, 0);

  u.st128.lower = ~0ULL;
  test_signbit (u.f128, 0);

  u.f128 = -1.0q;
  u.st128.lower = 0ULL;
  test_signbit (u.f128, 1);

  u.st128.lower = ~0ULL;
  test_signbit (u.f128, 1);

#ifdef DEBUG
  printf ("%d error(s) were found\n", num_errors);
  if (num_errors)
    return num_errors;
#endif

  return 0;
}

