aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ir_fold.c
blob: fcfe88b34ed56d79b3445a99d59f849aeca9147a (plain) (blame)
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
#include "ir.h"
#include "u_endian.h"
#include <limits.h>

#ifdef __clang__
__attribute__((no_sanitize("float-cast-overflow"))) /* silence UBsan for float->int overflow */
#endif
static Ref
foldint(enum op op, enum irclass k, Ref lr, Ref rr)
{
   s64int x;
   union {
      s64int s;
      u64int u;
   } l = {.s = intconval(lr)}, r = {.s = intconval(rr)};
   bool w = cls2siz[k] == 8;
   if (in_range(op, Odiv, Ourem)) assert(r.u != 0);
   switch (op) {
   case Ocopy: x = l.s; break;
   case Oneg:  x = -l.s; break;
   case Onot:  x = ~l.s; break;
#define CVTF2I(TF, TI) do { \
   TF f = fltconval(lr);    \
   if (f != f) x = 0;       \
   else x = (TI)f;          \
} while (0)
   case Ocvtf32s: if (w) CVTF2I(float,  s64int); else CVTF2I(float,  int);  break;
   case Ocvtf32u: if (w) CVTF2I(float,  u64int); else CVTF2I(float,  uint); break;
   case Ocvtf64s: if (w) CVTF2I(double, s64int); else CVTF2I(double, int);  break;
   case Ocvtf64u: if (w) CVTF2I(double, u64int); else CVTF2I(double, uint); break;
#undef CVTF2I
   case Oexts8:  x = (schar)l.s; break;
   case Oextu8:  x = (uchar)l.s; break;
   case Oexts16: x = (short)l.s; break;
   case Oextu16: x = (ushort)l.s; break;
   case Oexts32: x = (int)l.s; break;
   case Oextu32: x = (uint)l.s; break;
   case Obswap16: x = bswap16(l.u); break;
   case Obswap32: x = bswap32(l.u); break;
   case Obswap64: x = bswap64(l.u); break;
   case Oadd:  x = l.u + r.u; break;
   case Osub:  x = l.u - r.u; break;
   case Omul:  x = l.u * r.u; break;
   case Odiv:  if (r.s == -1 && (w ? l.s == LLONG_MIN : (int)l.s == INT_MIN)) x = l.s;
               else x = w ? l.s / r.s : (int)l.s / (int)r.s;
               break;
   case Oudiv: x = w ? l.u / r.u : (uint)l.u / (uint)r.u; break;
   case Orem:  if (r.s == -1 && (w ? l.s == LLONG_MIN : (int)l.s == INT_MIN)) x = 0;
               else x = w ? l.s % r.s : (int)l.s % (int)r.s;
               break;
   case Ourem: x = w ? l.u % r.u : (uint)l.u % (uint)r.u; break;
   case Oand:  x = l.u & r.u; break;
   case Oior:  x = l.u | r.u; break;
   case Oxor:  x = l.u ^ r.u; break;
   case Oshl:  x = l.u << (r.u & (w ? 63 : 31)); break;
   case Oslr:  x = w ? l.u >> (r.u&63) : (int)l.s >> (r.u&31); break;
   case Osar:  x = w ? l.s >> (r.u&63) : (int)l.s >> (r.u&31); break;
   case Oequ:  x = l.s == r.s; break;
   case Oneq:  x = l.s != r.s; break;
   case Olth:  x = l.s < r.s; break;
   case Ogth:  x = l.s > r.s; break;
   case Olte:  x = l.s <= r.s; break;
   case Ogte:  x = l.s >= r.s; break;
   case Oulth: x = l.u < r.u; break;
   case Ougth: x = l.u > r.u; break;
   case Oulte: x = l.u <= r.u; break;
   case Ougte: x = l.u >= r.u; break;
   default: assert(0);
   }
   if (!w) x = (int)x;
   return mkintcon(k, x);
}

static Ref
foldflt(enum op op, enum irclass k, Ref lr, Ref rr)
{
   int xi;
   double x, l = fltconval(lr), r = fltconval(rr);
   bool w = k == KF64;
   switch (op) {
   case Ocopy: x = l; break;
   case Oneg:  x = -l; break;
   case Ocvtf32f64: x = (float)l; break;
   case Ocvtf64f32: x = (float)l; break;
   case Ocvts32f:   x = (int)intconval(lr); break;
   case Ocvtu32f:   x = (int)intconval(lr); break;
   case Ocvts64f:   x = (s64int)intconval(lr); break;
   case Ocvtu64f:   x = (u64int)intconval(lr); break;
   case Oadd: x = l + r; break;
   case Osub: x = l - r; break;
   case Omul: x = l * r; break;
   case Odiv: x = l / r; break;
   case Oequ: xi = l == r; goto Cmp;
   case Oneq: xi = l != r; goto Cmp;
   case Olth: xi = l < r; goto Cmp;
   case Ogth: xi = l > r; goto Cmp;
   case Olte: xi = l <= r; goto Cmp;
   case Ogte: xi = l >= r; Cmp: return mkref(RICON, xi);
   default: assert(0);
   }
   if (!w) x = (float)x;
   return mkfltcon(k, x);
}

bool
foldbinop(Ref *to, enum op op, enum irclass k, Ref l, Ref r)
{
   if (!oisarith(op))
      return 0;
   if (!isnumcon(l) || !isnumcon(r)) return 0;
   if (in_range(op, Odiv, Ourem) && kisint(k) && intconval(r) == 0)
      return 0;
   if (kisint(k))
      *to = foldint(op, k, l, r);
   else
      *to = foldflt(op, k, l, r);
   return 1;
}

bool
foldunop(Ref *to, enum op op, enum irclass k, Ref a)
{
   if (!isnumcon(a)) return 0;
   if (op != Ocopy && !oisarith(op))
      return 0;
   if (kisint(k))
      *to = foldint(op, k, a, ZEROREF);
   else
      *to = foldflt(op, k, a, ZEROREF);
   return 1;
}

/* vim:set ts=3 sw=3 expandtab: */