summaryrefslogtreecommitdiff
path: root/gcc/testsuite/gcc.c-torture/execute/20040703-1.c
blob: eba358d35ae31d7489e7fbf749b9690c050491b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/* PR 16341 */

#define PART_PRECISION (sizeof (cpp_num_part) * 8)

typedef unsigned int cpp_num_part;
typedef struct cpp_num cpp_num;
struct cpp_num
{
   cpp_num_part high;
   cpp_num_part low;
   int unsignedp;  /* True if value should be treated as unsigned.  */
   int overflow;   /* True if the most recent calculation overflowed.  */
};

static  int
num_positive (cpp_num num, unsigned int precision)
{
   if (precision > PART_PRECISION)
     {
       precision -= PART_PRECISION;
       return (num.high & (cpp_num_part) 1 << (precision - 1)) == 0;
     }

   return (num.low & (cpp_num_part) 1 << (precision - 1)) == 0;
}

static cpp_num
num_trim (cpp_num num, unsigned int precision)
{
   if (precision > PART_PRECISION)
     {
       precision -= PART_PRECISION;
       if (precision < PART_PRECISION)
         num.high &= ((cpp_num_part) 1 << precision) - 1;
     }
   else
     {
       if (precision < PART_PRECISION)
         num.low &= ((cpp_num_part) 1 << precision) - 1;
       num.high = 0;
     }

   return num;
}

/* Shift NUM, of width PRECISION, right by N bits.  */
static cpp_num
num_rshift (cpp_num num, unsigned int precision, unsigned int n)
{
   cpp_num_part sign_mask;
   int x = num_positive (num, precision);

   if (num.unsignedp || x)
     sign_mask = 0;
   else
     sign_mask = ~(cpp_num_part) 0;

   if (n >= precision)
     num.high = num.low = sign_mask;
   else
     {
       /* Sign-extend.  */
       if (precision < PART_PRECISION)
         num.high = sign_mask, num.low |= sign_mask << precision;
       else if (precision < 2 * PART_PRECISION)
         num.high |= sign_mask << (precision - PART_PRECISION);

       if (n >= PART_PRECISION)
         {
           n -= PART_PRECISION;
           num.low = num.high;
           num.high = sign_mask;
         }

       if (n)
         {
           num.low = (num.low >> n) | (num.high << (PART_PRECISION - n));
           num.high = (num.high >> n) | (sign_mask << (PART_PRECISION - n));
         }
     }

   num = num_trim (num, precision);
   num.overflow = 0;
   return num;
}
                              #define num_zerop(num) ((num.low | num.high) == 0)
#define num_eq(num1, num2) (num1.low == num2.low && num1.high == num2.high)

cpp_num
num_lshift (cpp_num num, unsigned int precision, unsigned int n)
{
   if (n >= precision)
     {
       num.overflow = !num.unsignedp && !num_zerop (num);
       num.high = num.low = 0;
     }
   else
     {
       cpp_num orig;
       unsigned int m = n;

       orig = num;
       if (m >= PART_PRECISION)
         {
           m -= PART_PRECISION;
           num.high = num.low;
           num.low = 0;
         }
       if (m)
         {
           num.high = (num.high << m) | (num.low >> (PART_PRECISION - m));
           num.low <<= m;
         }
       num = num_trim (num, precision);

       if (num.unsignedp)
         num.overflow = 0;
       else
         {
           cpp_num maybe_orig = num_rshift (num, precision, n);
           num.overflow = !num_eq (orig, maybe_orig);
         }
     }

   return num;
}

unsigned int precision = 64;
unsigned int n = 16;

cpp_num num = { 0, 3, 0, 0 };

int main()
{
   cpp_num res = num_lshift (num, 64, n);

   if (res.low != 0x30000)
     abort ();

   if (res.high != 0)
     abort ();

   if (res.overflow != 0)
     abort ();

   exit (0);
}