blob: 90a0042c792a99a5756dfd39c25b5a0ada0c365f [file] [log] [blame]
Jamie Madill47cb73a2016-09-09 11:41:44 -04001//
2// Copyright 2016 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// ConstantUnion: Constant folding helper class.
7
8#include "compiler/translator/ConstantUnion.h"
9
Jamie Madill5db69f52016-09-15 12:47:32 -040010#include "base/numerics/safe_math.h"
Jamie Madill47cb73a2016-09-09 11:41:44 -040011#include "compiler/translator/Diagnostics.h"
12
Jamie Madill5db69f52016-09-15 12:47:32 -040013namespace
14{
15
16template <typename T>
17T CheckedSum(base::CheckedNumeric<T> lhs,
18 base::CheckedNumeric<T> rhs,
19 TDiagnostics *diag,
20 const TSourceLoc &line)
21{
22 ASSERT(lhs.IsValid() && rhs.IsValid());
23 auto result = lhs + rhs;
24 if (!result.IsValid())
25 {
26 diag->error(line, "Addition out of range", "*", "");
27 return 0;
28 }
29 return result.ValueOrDefault(0);
30}
31
32template <typename T>
33T CheckedDiff(base::CheckedNumeric<T> lhs,
34 base::CheckedNumeric<T> rhs,
35 TDiagnostics *diag,
36 const TSourceLoc &line)
37{
38 ASSERT(lhs.IsValid() && rhs.IsValid());
39 auto result = lhs - rhs;
40 if (!result.IsValid())
41 {
42 diag->error(line, "Difference out of range", "*", "");
43 return 0;
44 }
45 return result.ValueOrDefault(0);
46}
47
48template <typename T>
49T CheckedMul(base::CheckedNumeric<T> lhs,
50 base::CheckedNumeric<T> rhs,
51 TDiagnostics *diag,
52 const TSourceLoc &line)
53{
54 ASSERT(lhs.IsValid() && rhs.IsValid());
55 auto result = lhs * rhs;
56 if (!result.IsValid())
57 {
58 diag->error(line, "Multiplication out of range", "*", "");
59 return 0;
60 }
61 return result.ValueOrDefault(0);
62}
63
Olli Etuaho1be4d492016-09-27 11:15:38 +010064// Unsigned types are defined to do arithmetic modulo 2^n in C++. For signed types, overflow
65// behavior is undefined.
66
67template <typename T>
68T WrappingSum(T lhs, T rhs)
69{
70 uint32_t lhsUnsigned = static_cast<uint32_t>(lhs);
71 uint32_t rhsUnsigned = static_cast<uint32_t>(rhs);
72 return static_cast<T>(lhsUnsigned + rhsUnsigned);
73}
74
75template <typename T>
76T WrappingDiff(T lhs, T rhs)
77{
78 uint32_t lhsUnsigned = static_cast<uint32_t>(lhs);
79 uint32_t rhsUnsigned = static_cast<uint32_t>(rhs);
80 return static_cast<T>(lhsUnsigned - rhsUnsigned);
81}
82
83int32_t WrappingMul(int32_t lhs, int32_t rhs)
84{
85 int64_t lhsWide = static_cast<int64_t>(lhs);
86 int64_t rhsWide = static_cast<int64_t>(rhs);
87 // The multiplication is guaranteed not to overflow.
88 int64_t resultWide = lhsWide * rhsWide;
89 // Implement the desired wrapping behavior by masking out the high-order 32 bits.
90 resultWide = resultWide & 0xffffffffll;
91 // Casting to a narrower signed type is fine since the casted value is representable in the
92 // narrower type.
93 return static_cast<int32_t>(resultWide);
94}
95
Jamie Madill5db69f52016-09-15 12:47:32 -040096} // anonymous namespace
97
Jamie Madill47cb73a2016-09-09 11:41:44 -040098TConstantUnion::TConstantUnion()
99{
100 iConst = 0;
101 type = EbtVoid;
102}
103
104bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant)
105{
106 switch (newType)
107 {
108 case EbtFloat:
109 switch (constant.type)
110 {
111 case EbtInt:
112 setFConst(static_cast<float>(constant.getIConst()));
113 break;
114 case EbtUInt:
115 setFConst(static_cast<float>(constant.getUConst()));
116 break;
117 case EbtBool:
118 setFConst(static_cast<float>(constant.getBConst()));
119 break;
120 case EbtFloat:
121 setFConst(static_cast<float>(constant.getFConst()));
122 break;
123 default:
124 return false;
125 }
126 break;
127 case EbtInt:
128 switch (constant.type)
129 {
130 case EbtInt:
131 setIConst(static_cast<int>(constant.getIConst()));
132 break;
133 case EbtUInt:
134 setIConst(static_cast<int>(constant.getUConst()));
135 break;
136 case EbtBool:
137 setIConst(static_cast<int>(constant.getBConst()));
138 break;
139 case EbtFloat:
140 setIConst(static_cast<int>(constant.getFConst()));
141 break;
142 default:
143 return false;
144 }
145 break;
146 case EbtUInt:
147 switch (constant.type)
148 {
149 case EbtInt:
150 setUConst(static_cast<unsigned int>(constant.getIConst()));
151 break;
152 case EbtUInt:
153 setUConst(static_cast<unsigned int>(constant.getUConst()));
154 break;
155 case EbtBool:
156 setUConst(static_cast<unsigned int>(constant.getBConst()));
157 break;
158 case EbtFloat:
159 setUConst(static_cast<unsigned int>(constant.getFConst()));
160 break;
161 default:
162 return false;
163 }
164 break;
165 case EbtBool:
166 switch (constant.type)
167 {
168 case EbtInt:
169 setBConst(constant.getIConst() != 0);
170 break;
171 case EbtUInt:
172 setBConst(constant.getUConst() != 0);
173 break;
174 case EbtBool:
175 setBConst(constant.getBConst());
176 break;
177 case EbtFloat:
178 setBConst(constant.getFConst() != 0.0f);
179 break;
180 default:
181 return false;
182 }
183 break;
184 case EbtStruct: // Struct fields don't get cast
185 switch (constant.type)
186 {
187 case EbtInt:
188 setIConst(constant.getIConst());
189 break;
190 case EbtUInt:
191 setUConst(constant.getUConst());
192 break;
193 case EbtBool:
194 setBConst(constant.getBConst());
195 break;
196 case EbtFloat:
197 setFConst(constant.getFConst());
198 break;
199 default:
200 return false;
201 }
202 break;
203 default:
204 return false;
205 }
206
207 return true;
208}
209
210bool TConstantUnion::operator==(const int i) const
211{
212 return i == iConst;
213}
214
215bool TConstantUnion::operator==(const unsigned int u) const
216{
217 return u == uConst;
218}
219
220bool TConstantUnion::operator==(const float f) const
221{
222 return f == fConst;
223}
224
225bool TConstantUnion::operator==(const bool b) const
226{
227 return b == bConst;
228}
229
230bool TConstantUnion::operator==(const TConstantUnion &constant) const
231{
232 if (constant.type != type)
233 return false;
234
235 switch (type)
236 {
237 case EbtInt:
238 return constant.iConst == iConst;
239 case EbtUInt:
240 return constant.uConst == uConst;
241 case EbtFloat:
242 return constant.fConst == fConst;
243 case EbtBool:
244 return constant.bConst == bConst;
245 default:
246 return false;
247 }
248}
249
250bool TConstantUnion::operator!=(const int i) const
251{
252 return !operator==(i);
253}
254
255bool TConstantUnion::operator!=(const unsigned int u) const
256{
257 return !operator==(u);
258}
259
260bool TConstantUnion::operator!=(const float f) const
261{
262 return !operator==(f);
263}
264
265bool TConstantUnion::operator!=(const bool b) const
266{
267 return !operator==(b);
268}
269
270bool TConstantUnion::operator!=(const TConstantUnion &constant) const
271{
272 return !operator==(constant);
273}
274
275bool TConstantUnion::operator>(const TConstantUnion &constant) const
276{
277 ASSERT(type == constant.type);
278 switch (type)
279 {
280 case EbtInt:
281 return iConst > constant.iConst;
282 case EbtUInt:
283 return uConst > constant.uConst;
284 case EbtFloat:
285 return fConst > constant.fConst;
286 default:
287 return false; // Invalid operation, handled at semantic analysis
288 }
289}
290
291bool TConstantUnion::operator<(const TConstantUnion &constant) const
292{
293 ASSERT(type == constant.type);
294 switch (type)
295 {
296 case EbtInt:
297 return iConst < constant.iConst;
298 case EbtUInt:
299 return uConst < constant.uConst;
300 case EbtFloat:
301 return fConst < constant.fConst;
302 default:
303 return false; // Invalid operation, handled at semantic analysis
304 }
305}
306
307// static
308TConstantUnion TConstantUnion::add(const TConstantUnion &lhs,
309 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400310 TDiagnostics *diag,
311 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400312{
313 TConstantUnion returnValue;
314 ASSERT(lhs.type == rhs.type);
315 switch (lhs.type)
316 {
317 case EbtInt:
Olli Etuaho1be4d492016-09-27 11:15:38 +0100318 returnValue.setIConst(WrappingSum<int>(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400319 break;
320 case EbtUInt:
Olli Etuaho1be4d492016-09-27 11:15:38 +0100321 returnValue.setUConst(WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400322 break;
323 case EbtFloat:
Jamie Madill5db69f52016-09-15 12:47:32 -0400324 returnValue.setFConst(CheckedSum<float>(lhs.fConst, rhs.fConst, diag, line));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400325 break;
326 default:
327 UNREACHABLE();
328 }
329
330 return returnValue;
331}
332
333// static
334TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs,
335 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400336 TDiagnostics *diag,
337 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400338{
339 TConstantUnion returnValue;
340 ASSERT(lhs.type == rhs.type);
341 switch (lhs.type)
342 {
343 case EbtInt:
Olli Etuaho1be4d492016-09-27 11:15:38 +0100344 returnValue.setIConst(WrappingDiff<int>(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400345 break;
346 case EbtUInt:
Olli Etuaho1be4d492016-09-27 11:15:38 +0100347 returnValue.setUConst(WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400348 break;
349 case EbtFloat:
Jamie Madill5db69f52016-09-15 12:47:32 -0400350 returnValue.setFConst(CheckedDiff<float>(lhs.fConst, rhs.fConst, diag, line));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400351 break;
352 default:
353 UNREACHABLE();
354 }
355
356 return returnValue;
357}
358
359// static
360TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs,
361 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400362 TDiagnostics *diag,
363 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400364{
365 TConstantUnion returnValue;
366 ASSERT(lhs.type == rhs.type);
367 switch (lhs.type)
368 {
369 case EbtInt:
Olli Etuaho1be4d492016-09-27 11:15:38 +0100370 returnValue.setIConst(WrappingMul(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400371 break;
372 case EbtUInt:
Olli Etuaho1be4d492016-09-27 11:15:38 +0100373 // Unsigned integer math in C++ is defined to be done in modulo 2^n, so we rely on that
374 // to implement wrapping multiplication.
375 returnValue.setUConst(lhs.uConst * rhs.uConst);
Jamie Madill47cb73a2016-09-09 11:41:44 -0400376 break;
377 case EbtFloat:
Jamie Madill5db69f52016-09-15 12:47:32 -0400378 returnValue.setFConst(CheckedMul<float>(lhs.fConst, rhs.fConst, diag, line));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400379 break;
380 default:
381 UNREACHABLE();
382 }
383
384 return returnValue;
385}
386
387TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const
388{
389 TConstantUnion returnValue;
390 ASSERT(type == constant.type);
391 switch (type)
392 {
393 case EbtInt:
394 returnValue.setIConst(iConst % constant.iConst);
395 break;
396 case EbtUInt:
397 returnValue.setUConst(uConst % constant.uConst);
398 break;
399 default:
400 UNREACHABLE();
401 }
402
403 return returnValue;
404}
405
Jamie Madill596018c2016-09-21 12:57:03 -0400406// static
407TConstantUnion TConstantUnion::rshift(const TConstantUnion &lhs,
408 const TConstantUnion &rhs,
409 TDiagnostics *diag,
410 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400411{
412 TConstantUnion returnValue;
Olli Etuaho2cacb772016-09-26 08:50:40 +0100413 ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
414 ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
415 if ((lhs.type == EbtInt && lhs.iConst < 0) ||
416 (rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
417 (rhs.type == EbtUInt && rhs.uConst > 31))
418 {
419 diag->error(line, "Undefined shift (operand out of range)", ">>", "");
420 switch (lhs.type)
421 {
422 case EbtInt:
423 returnValue.setIConst(0);
424 break;
425 case EbtUInt:
426 returnValue.setUConst(0u);
427 break;
428 default:
429 UNREACHABLE();
430 }
431 return returnValue;
432 }
433
Jamie Madill596018c2016-09-21 12:57:03 -0400434 switch (lhs.type)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400435 {
436 case EbtInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100437 switch (rhs.type)
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400438 {
Olli Etuaho2cacb772016-09-26 08:50:40 +0100439 case EbtInt:
440 returnValue.setIConst(lhs.iConst >> rhs.iConst);
441 break;
442 case EbtUInt:
443 returnValue.setIConst(lhs.iConst >> rhs.uConst);
444 break;
445 default:
446 UNREACHABLE();
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400447 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400448 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400449
Jamie Madill47cb73a2016-09-09 11:41:44 -0400450 case EbtUInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100451 switch (rhs.type)
452 {
453 case EbtInt:
454 returnValue.setUConst(lhs.uConst >> rhs.iConst);
455 break;
456 case EbtUInt:
457 returnValue.setUConst(lhs.uConst >> rhs.uConst);
458 break;
459 default:
460 UNREACHABLE();
461 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400462 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400463
Jamie Madill47cb73a2016-09-09 11:41:44 -0400464 default:
465 UNREACHABLE();
466 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400467 return returnValue;
468}
469
Jamie Madill596018c2016-09-21 12:57:03 -0400470// static
471TConstantUnion TConstantUnion::lshift(const TConstantUnion &lhs,
472 const TConstantUnion &rhs,
473 TDiagnostics *diag,
474 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400475{
476 TConstantUnion returnValue;
Olli Etuaho2cacb772016-09-26 08:50:40 +0100477 ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
478 ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
479 if ((lhs.type == EbtInt && lhs.iConst < 0) ||
480 (rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
481 (rhs.type == EbtUInt && rhs.uConst > 31))
482 {
483 diag->error(line, "Undefined shift (operand out of range)", "<<", "");
484 switch (lhs.type)
485 {
486 case EbtInt:
487 returnValue.setIConst(0);
488 break;
489 case EbtUInt:
490 returnValue.setUConst(0u);
491 break;
492 default:
493 UNREACHABLE();
494 }
495 return returnValue;
496 }
497
Jamie Madill596018c2016-09-21 12:57:03 -0400498 switch (lhs.type)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400499 {
500 case EbtInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100501 switch (rhs.type)
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400502 {
Olli Etuaho2cacb772016-09-26 08:50:40 +0100503 case EbtInt:
504 returnValue.setIConst(lhs.iConst << rhs.iConst);
505 break;
506 case EbtUInt:
507 returnValue.setIConst(lhs.iConst << rhs.uConst);
508 break;
509 default:
510 UNREACHABLE();
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400511 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400512 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400513
Jamie Madill47cb73a2016-09-09 11:41:44 -0400514 case EbtUInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100515 switch (rhs.type)
516 {
517 case EbtInt:
518 returnValue.setUConst(lhs.uConst << rhs.iConst);
519 break;
520 case EbtUInt:
521 returnValue.setUConst(lhs.uConst << rhs.uConst);
522 break;
523 default:
524 UNREACHABLE();
525 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400526 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400527
Jamie Madill47cb73a2016-09-09 11:41:44 -0400528 default:
529 UNREACHABLE();
530 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400531 return returnValue;
532}
533
534TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const
535{
536 TConstantUnion returnValue;
537 ASSERT(constant.type == EbtInt || constant.type == EbtUInt);
538 switch (type)
539 {
540 case EbtInt:
541 returnValue.setIConst(iConst & constant.iConst);
542 break;
543 case EbtUInt:
544 returnValue.setUConst(uConst & constant.uConst);
545 break;
546 default:
547 UNREACHABLE();
548 }
549
550 return returnValue;
551}
552
553TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const
554{
555 TConstantUnion returnValue;
556 ASSERT(type == constant.type);
557 switch (type)
558 {
559 case EbtInt:
560 returnValue.setIConst(iConst | constant.iConst);
561 break;
562 case EbtUInt:
563 returnValue.setUConst(uConst | constant.uConst);
564 break;
565 default:
566 UNREACHABLE();
567 }
568
569 return returnValue;
570}
571
572TConstantUnion TConstantUnion::operator^(const TConstantUnion &constant) const
573{
574 TConstantUnion returnValue;
575 ASSERT(type == constant.type);
576 switch (type)
577 {
578 case EbtInt:
579 returnValue.setIConst(iConst ^ constant.iConst);
580 break;
581 case EbtUInt:
582 returnValue.setUConst(uConst ^ constant.uConst);
583 break;
584 default:
585 UNREACHABLE();
586 }
587
588 return returnValue;
589}
590
591TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const
592{
593 TConstantUnion returnValue;
594 ASSERT(type == constant.type);
595 switch (type)
596 {
597 case EbtBool:
598 returnValue.setBConst(bConst && constant.bConst);
599 break;
600 default:
601 UNREACHABLE();
602 }
603
604 return returnValue;
605}
606
607TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const
608{
609 TConstantUnion returnValue;
610 ASSERT(type == constant.type);
611 switch (type)
612 {
613 case EbtBool:
614 returnValue.setBConst(bConst || constant.bConst);
615 break;
616 default:
617 UNREACHABLE();
618 }
619
620 return returnValue;
621}