blob: 10a70f34ec8c7225a3036a8e7f6e30cee3efa917 [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"
Olli Etuaho7f9a55f2016-10-03 14:32:08 +010011#include "common/mathutil.h"
Jamie Madill47cb73a2016-09-09 11:41:44 -040012#include "compiler/translator/Diagnostics.h"
13
Jamie Madill5db69f52016-09-15 12:47:32 -040014namespace
15{
16
17template <typename T>
18T CheckedSum(base::CheckedNumeric<T> lhs,
19 base::CheckedNumeric<T> rhs,
20 TDiagnostics *diag,
21 const TSourceLoc &line)
22{
23 ASSERT(lhs.IsValid() && rhs.IsValid());
24 auto result = lhs + rhs;
25 if (!result.IsValid())
26 {
27 diag->error(line, "Addition out of range", "*", "");
28 return 0;
29 }
30 return result.ValueOrDefault(0);
31}
32
33template <typename T>
34T CheckedDiff(base::CheckedNumeric<T> lhs,
35 base::CheckedNumeric<T> rhs,
36 TDiagnostics *diag,
37 const TSourceLoc &line)
38{
39 ASSERT(lhs.IsValid() && rhs.IsValid());
40 auto result = lhs - rhs;
41 if (!result.IsValid())
42 {
43 diag->error(line, "Difference out of range", "*", "");
44 return 0;
45 }
46 return result.ValueOrDefault(0);
47}
48
49template <typename T>
50T CheckedMul(base::CheckedNumeric<T> lhs,
51 base::CheckedNumeric<T> rhs,
52 TDiagnostics *diag,
53 const TSourceLoc &line)
54{
55 ASSERT(lhs.IsValid() && rhs.IsValid());
56 auto result = lhs * rhs;
57 if (!result.IsValid())
58 {
59 diag->error(line, "Multiplication out of range", "*", "");
60 return 0;
61 }
62 return result.ValueOrDefault(0);
63}
64
65} // anonymous namespace
66
Jamie Madill47cb73a2016-09-09 11:41:44 -040067TConstantUnion::TConstantUnion()
68{
69 iConst = 0;
70 type = EbtVoid;
71}
72
73bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant)
74{
75 switch (newType)
76 {
77 case EbtFloat:
78 switch (constant.type)
79 {
80 case EbtInt:
81 setFConst(static_cast<float>(constant.getIConst()));
82 break;
83 case EbtUInt:
84 setFConst(static_cast<float>(constant.getUConst()));
85 break;
86 case EbtBool:
87 setFConst(static_cast<float>(constant.getBConst()));
88 break;
89 case EbtFloat:
90 setFConst(static_cast<float>(constant.getFConst()));
91 break;
92 default:
93 return false;
94 }
95 break;
96 case EbtInt:
97 switch (constant.type)
98 {
99 case EbtInt:
100 setIConst(static_cast<int>(constant.getIConst()));
101 break;
102 case EbtUInt:
103 setIConst(static_cast<int>(constant.getUConst()));
104 break;
105 case EbtBool:
106 setIConst(static_cast<int>(constant.getBConst()));
107 break;
108 case EbtFloat:
109 setIConst(static_cast<int>(constant.getFConst()));
110 break;
111 default:
112 return false;
113 }
114 break;
115 case EbtUInt:
116 switch (constant.type)
117 {
118 case EbtInt:
119 setUConst(static_cast<unsigned int>(constant.getIConst()));
120 break;
121 case EbtUInt:
122 setUConst(static_cast<unsigned int>(constant.getUConst()));
123 break;
124 case EbtBool:
125 setUConst(static_cast<unsigned int>(constant.getBConst()));
126 break;
127 case EbtFloat:
128 setUConst(static_cast<unsigned int>(constant.getFConst()));
129 break;
130 default:
131 return false;
132 }
133 break;
134 case EbtBool:
135 switch (constant.type)
136 {
137 case EbtInt:
138 setBConst(constant.getIConst() != 0);
139 break;
140 case EbtUInt:
141 setBConst(constant.getUConst() != 0);
142 break;
143 case EbtBool:
144 setBConst(constant.getBConst());
145 break;
146 case EbtFloat:
147 setBConst(constant.getFConst() != 0.0f);
148 break;
149 default:
150 return false;
151 }
152 break;
153 case EbtStruct: // Struct fields don't get cast
154 switch (constant.type)
155 {
156 case EbtInt:
157 setIConst(constant.getIConst());
158 break;
159 case EbtUInt:
160 setUConst(constant.getUConst());
161 break;
162 case EbtBool:
163 setBConst(constant.getBConst());
164 break;
165 case EbtFloat:
166 setFConst(constant.getFConst());
167 break;
168 default:
169 return false;
170 }
171 break;
172 default:
173 return false;
174 }
175
176 return true;
177}
178
179bool TConstantUnion::operator==(const int i) const
180{
181 return i == iConst;
182}
183
184bool TConstantUnion::operator==(const unsigned int u) const
185{
186 return u == uConst;
187}
188
189bool TConstantUnion::operator==(const float f) const
190{
191 return f == fConst;
192}
193
194bool TConstantUnion::operator==(const bool b) const
195{
196 return b == bConst;
197}
198
199bool TConstantUnion::operator==(const TConstantUnion &constant) const
200{
201 if (constant.type != type)
202 return false;
203
204 switch (type)
205 {
206 case EbtInt:
207 return constant.iConst == iConst;
208 case EbtUInt:
209 return constant.uConst == uConst;
210 case EbtFloat:
211 return constant.fConst == fConst;
212 case EbtBool:
213 return constant.bConst == bConst;
214 default:
215 return false;
216 }
217}
218
219bool TConstantUnion::operator!=(const int i) const
220{
221 return !operator==(i);
222}
223
224bool TConstantUnion::operator!=(const unsigned int u) const
225{
226 return !operator==(u);
227}
228
229bool TConstantUnion::operator!=(const float f) const
230{
231 return !operator==(f);
232}
233
234bool TConstantUnion::operator!=(const bool b) const
235{
236 return !operator==(b);
237}
238
239bool TConstantUnion::operator!=(const TConstantUnion &constant) const
240{
241 return !operator==(constant);
242}
243
244bool TConstantUnion::operator>(const TConstantUnion &constant) const
245{
246 ASSERT(type == constant.type);
247 switch (type)
248 {
249 case EbtInt:
250 return iConst > constant.iConst;
251 case EbtUInt:
252 return uConst > constant.uConst;
253 case EbtFloat:
254 return fConst > constant.fConst;
255 default:
256 return false; // Invalid operation, handled at semantic analysis
257 }
258}
259
260bool TConstantUnion::operator<(const TConstantUnion &constant) const
261{
262 ASSERT(type == constant.type);
263 switch (type)
264 {
265 case EbtInt:
266 return iConst < constant.iConst;
267 case EbtUInt:
268 return uConst < constant.uConst;
269 case EbtFloat:
270 return fConst < constant.fConst;
271 default:
272 return false; // Invalid operation, handled at semantic analysis
273 }
274}
275
276// static
277TConstantUnion TConstantUnion::add(const TConstantUnion &lhs,
278 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400279 TDiagnostics *diag,
280 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400281{
282 TConstantUnion returnValue;
283 ASSERT(lhs.type == rhs.type);
284 switch (lhs.type)
285 {
286 case EbtInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100287 returnValue.setIConst(gl::WrappingSum<int>(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400288 break;
289 case EbtUInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100290 returnValue.setUConst(gl::WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400291 break;
292 case EbtFloat:
Jamie Madill5db69f52016-09-15 12:47:32 -0400293 returnValue.setFConst(CheckedSum<float>(lhs.fConst, rhs.fConst, diag, line));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400294 break;
295 default:
296 UNREACHABLE();
297 }
298
299 return returnValue;
300}
301
302// static
303TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs,
304 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400305 TDiagnostics *diag,
306 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400307{
308 TConstantUnion returnValue;
309 ASSERT(lhs.type == rhs.type);
310 switch (lhs.type)
311 {
312 case EbtInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100313 returnValue.setIConst(gl::WrappingDiff<int>(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400314 break;
315 case EbtUInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100316 returnValue.setUConst(gl::WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400317 break;
318 case EbtFloat:
Jamie Madill5db69f52016-09-15 12:47:32 -0400319 returnValue.setFConst(CheckedDiff<float>(lhs.fConst, rhs.fConst, diag, line));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400320 break;
321 default:
322 UNREACHABLE();
323 }
324
325 return returnValue;
326}
327
328// static
329TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs,
330 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400331 TDiagnostics *diag,
332 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400333{
334 TConstantUnion returnValue;
335 ASSERT(lhs.type == rhs.type);
336 switch (lhs.type)
337 {
338 case EbtInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100339 returnValue.setIConst(gl::WrappingMul(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400340 break;
341 case EbtUInt:
Olli Etuaho1be4d492016-09-27 11:15:38 +0100342 // Unsigned integer math in C++ is defined to be done in modulo 2^n, so we rely on that
343 // to implement wrapping multiplication.
344 returnValue.setUConst(lhs.uConst * rhs.uConst);
Jamie Madill47cb73a2016-09-09 11:41:44 -0400345 break;
346 case EbtFloat:
Jamie Madill5db69f52016-09-15 12:47:32 -0400347 returnValue.setFConst(CheckedMul<float>(lhs.fConst, rhs.fConst, diag, line));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400348 break;
349 default:
350 UNREACHABLE();
351 }
352
353 return returnValue;
354}
355
356TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const
357{
358 TConstantUnion returnValue;
359 ASSERT(type == constant.type);
360 switch (type)
361 {
362 case EbtInt:
363 returnValue.setIConst(iConst % constant.iConst);
364 break;
365 case EbtUInt:
366 returnValue.setUConst(uConst % constant.uConst);
367 break;
368 default:
369 UNREACHABLE();
370 }
371
372 return returnValue;
373}
374
Jamie Madill596018c2016-09-21 12:57:03 -0400375// static
376TConstantUnion TConstantUnion::rshift(const TConstantUnion &lhs,
377 const TConstantUnion &rhs,
378 TDiagnostics *diag,
379 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400380{
381 TConstantUnion returnValue;
Olli Etuaho2cacb772016-09-26 08:50:40 +0100382 ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
383 ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
384 if ((lhs.type == EbtInt && lhs.iConst < 0) ||
385 (rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
386 (rhs.type == EbtUInt && rhs.uConst > 31))
387 {
388 diag->error(line, "Undefined shift (operand out of range)", ">>", "");
389 switch (lhs.type)
390 {
391 case EbtInt:
392 returnValue.setIConst(0);
393 break;
394 case EbtUInt:
395 returnValue.setUConst(0u);
396 break;
397 default:
398 UNREACHABLE();
399 }
400 return returnValue;
401 }
402
Jamie Madill596018c2016-09-21 12:57:03 -0400403 switch (lhs.type)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400404 {
405 case EbtInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100406 switch (rhs.type)
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400407 {
Olli Etuaho2cacb772016-09-26 08:50:40 +0100408 case EbtInt:
409 returnValue.setIConst(lhs.iConst >> rhs.iConst);
410 break;
411 case EbtUInt:
412 returnValue.setIConst(lhs.iConst >> rhs.uConst);
413 break;
414 default:
415 UNREACHABLE();
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400416 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400417 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400418
Jamie Madill47cb73a2016-09-09 11:41:44 -0400419 case EbtUInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100420 switch (rhs.type)
421 {
422 case EbtInt:
423 returnValue.setUConst(lhs.uConst >> rhs.iConst);
424 break;
425 case EbtUInt:
426 returnValue.setUConst(lhs.uConst >> rhs.uConst);
427 break;
428 default:
429 UNREACHABLE();
430 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400431 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400432
Jamie Madill47cb73a2016-09-09 11:41:44 -0400433 default:
434 UNREACHABLE();
435 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400436 return returnValue;
437}
438
Jamie Madill596018c2016-09-21 12:57:03 -0400439// static
440TConstantUnion TConstantUnion::lshift(const TConstantUnion &lhs,
441 const TConstantUnion &rhs,
442 TDiagnostics *diag,
443 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400444{
445 TConstantUnion returnValue;
Olli Etuaho2cacb772016-09-26 08:50:40 +0100446 ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
447 ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
448 if ((lhs.type == EbtInt && lhs.iConst < 0) ||
449 (rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
450 (rhs.type == EbtUInt && rhs.uConst > 31))
451 {
452 diag->error(line, "Undefined shift (operand out of range)", "<<", "");
453 switch (lhs.type)
454 {
455 case EbtInt:
456 returnValue.setIConst(0);
457 break;
458 case EbtUInt:
459 returnValue.setUConst(0u);
460 break;
461 default:
462 UNREACHABLE();
463 }
464 return returnValue;
465 }
466
Jamie Madill596018c2016-09-21 12:57:03 -0400467 switch (lhs.type)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400468 {
469 case EbtInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100470 switch (rhs.type)
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400471 {
Olli Etuaho2cacb772016-09-26 08:50:40 +0100472 case EbtInt:
473 returnValue.setIConst(lhs.iConst << rhs.iConst);
474 break;
475 case EbtUInt:
476 returnValue.setIConst(lhs.iConst << rhs.uConst);
477 break;
478 default:
479 UNREACHABLE();
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400480 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400481 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400482
Jamie Madill47cb73a2016-09-09 11:41:44 -0400483 case EbtUInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100484 switch (rhs.type)
485 {
486 case EbtInt:
487 returnValue.setUConst(lhs.uConst << rhs.iConst);
488 break;
489 case EbtUInt:
490 returnValue.setUConst(lhs.uConst << rhs.uConst);
491 break;
492 default:
493 UNREACHABLE();
494 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400495 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400496
Jamie Madill47cb73a2016-09-09 11:41:44 -0400497 default:
498 UNREACHABLE();
499 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400500 return returnValue;
501}
502
503TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const
504{
505 TConstantUnion returnValue;
506 ASSERT(constant.type == EbtInt || constant.type == EbtUInt);
507 switch (type)
508 {
509 case EbtInt:
510 returnValue.setIConst(iConst & constant.iConst);
511 break;
512 case EbtUInt:
513 returnValue.setUConst(uConst & constant.uConst);
514 break;
515 default:
516 UNREACHABLE();
517 }
518
519 return returnValue;
520}
521
522TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const
523{
524 TConstantUnion returnValue;
525 ASSERT(type == constant.type);
526 switch (type)
527 {
528 case EbtInt:
529 returnValue.setIConst(iConst | constant.iConst);
530 break;
531 case EbtUInt:
532 returnValue.setUConst(uConst | constant.uConst);
533 break;
534 default:
535 UNREACHABLE();
536 }
537
538 return returnValue;
539}
540
541TConstantUnion TConstantUnion::operator^(const TConstantUnion &constant) const
542{
543 TConstantUnion returnValue;
544 ASSERT(type == constant.type);
545 switch (type)
546 {
547 case EbtInt:
548 returnValue.setIConst(iConst ^ constant.iConst);
549 break;
550 case EbtUInt:
551 returnValue.setUConst(uConst ^ constant.uConst);
552 break;
553 default:
554 UNREACHABLE();
555 }
556
557 return returnValue;
558}
559
560TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const
561{
562 TConstantUnion returnValue;
563 ASSERT(type == constant.type);
564 switch (type)
565 {
566 case EbtBool:
567 returnValue.setBConst(bConst && constant.bConst);
568 break;
569 default:
570 UNREACHABLE();
571 }
572
573 return returnValue;
574}
575
576TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const
577{
578 TConstantUnion returnValue;
579 ASSERT(type == constant.type);
580 switch (type)
581 {
582 case EbtBool:
583 returnValue.setBConst(bConst || constant.bConst);
584 break;
585 default:
586 UNREACHABLE();
587 }
588
589 return returnValue;
590}