blob: 1ee3f648708e29d6d848d1e68f574587a5adae3c [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 Madill45bcc782016-11-07 13:58:48 -050014namespace sh
15{
16
Jamie Madill5db69f52016-09-15 12:47:32 -040017namespace
18{
19
20template <typename T>
21T CheckedSum(base::CheckedNumeric<T> lhs,
22 base::CheckedNumeric<T> rhs,
23 TDiagnostics *diag,
24 const TSourceLoc &line)
25{
26 ASSERT(lhs.IsValid() && rhs.IsValid());
27 auto result = lhs + rhs;
28 if (!result.IsValid())
29 {
30 diag->error(line, "Addition out of range", "*", "");
31 return 0;
32 }
33 return result.ValueOrDefault(0);
34}
35
36template <typename T>
37T CheckedDiff(base::CheckedNumeric<T> lhs,
38 base::CheckedNumeric<T> rhs,
39 TDiagnostics *diag,
40 const TSourceLoc &line)
41{
42 ASSERT(lhs.IsValid() && rhs.IsValid());
43 auto result = lhs - rhs;
44 if (!result.IsValid())
45 {
46 diag->error(line, "Difference out of range", "*", "");
47 return 0;
48 }
49 return result.ValueOrDefault(0);
50}
51
52template <typename T>
53T CheckedMul(base::CheckedNumeric<T> lhs,
54 base::CheckedNumeric<T> rhs,
55 TDiagnostics *diag,
56 const TSourceLoc &line)
57{
58 ASSERT(lhs.IsValid() && rhs.IsValid());
59 auto result = lhs * rhs;
60 if (!result.IsValid())
61 {
62 diag->error(line, "Multiplication out of range", "*", "");
63 return 0;
64 }
65 return result.ValueOrDefault(0);
66}
67
68} // anonymous namespace
69
Jamie Madill47cb73a2016-09-09 11:41:44 -040070TConstantUnion::TConstantUnion()
71{
72 iConst = 0;
73 type = EbtVoid;
74}
75
76bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant)
77{
78 switch (newType)
79 {
80 case EbtFloat:
81 switch (constant.type)
82 {
83 case EbtInt:
84 setFConst(static_cast<float>(constant.getIConst()));
85 break;
86 case EbtUInt:
87 setFConst(static_cast<float>(constant.getUConst()));
88 break;
89 case EbtBool:
90 setFConst(static_cast<float>(constant.getBConst()));
91 break;
92 case EbtFloat:
93 setFConst(static_cast<float>(constant.getFConst()));
94 break;
95 default:
96 return false;
97 }
98 break;
99 case EbtInt:
100 switch (constant.type)
101 {
102 case EbtInt:
103 setIConst(static_cast<int>(constant.getIConst()));
104 break;
105 case EbtUInt:
106 setIConst(static_cast<int>(constant.getUConst()));
107 break;
108 case EbtBool:
109 setIConst(static_cast<int>(constant.getBConst()));
110 break;
111 case EbtFloat:
112 setIConst(static_cast<int>(constant.getFConst()));
113 break;
114 default:
115 return false;
116 }
117 break;
118 case EbtUInt:
119 switch (constant.type)
120 {
121 case EbtInt:
122 setUConst(static_cast<unsigned int>(constant.getIConst()));
123 break;
124 case EbtUInt:
125 setUConst(static_cast<unsigned int>(constant.getUConst()));
126 break;
127 case EbtBool:
128 setUConst(static_cast<unsigned int>(constant.getBConst()));
129 break;
130 case EbtFloat:
131 setUConst(static_cast<unsigned int>(constant.getFConst()));
132 break;
133 default:
134 return false;
135 }
136 break;
137 case EbtBool:
138 switch (constant.type)
139 {
140 case EbtInt:
141 setBConst(constant.getIConst() != 0);
142 break;
143 case EbtUInt:
144 setBConst(constant.getUConst() != 0);
145 break;
146 case EbtBool:
147 setBConst(constant.getBConst());
148 break;
149 case EbtFloat:
150 setBConst(constant.getFConst() != 0.0f);
151 break;
152 default:
153 return false;
154 }
155 break;
156 case EbtStruct: // Struct fields don't get cast
157 switch (constant.type)
158 {
159 case EbtInt:
160 setIConst(constant.getIConst());
161 break;
162 case EbtUInt:
163 setUConst(constant.getUConst());
164 break;
165 case EbtBool:
166 setBConst(constant.getBConst());
167 break;
168 case EbtFloat:
169 setFConst(constant.getFConst());
170 break;
171 default:
172 return false;
173 }
174 break;
175 default:
176 return false;
177 }
178
179 return true;
180}
181
182bool TConstantUnion::operator==(const int i) const
183{
184 return i == iConst;
185}
186
187bool TConstantUnion::operator==(const unsigned int u) const
188{
189 return u == uConst;
190}
191
192bool TConstantUnion::operator==(const float f) const
193{
194 return f == fConst;
195}
196
197bool TConstantUnion::operator==(const bool b) const
198{
199 return b == bConst;
200}
201
202bool TConstantUnion::operator==(const TConstantUnion &constant) const
203{
204 if (constant.type != type)
205 return false;
206
207 switch (type)
208 {
209 case EbtInt:
210 return constant.iConst == iConst;
211 case EbtUInt:
212 return constant.uConst == uConst;
213 case EbtFloat:
214 return constant.fConst == fConst;
215 case EbtBool:
216 return constant.bConst == bConst;
217 default:
218 return false;
219 }
220}
221
222bool TConstantUnion::operator!=(const int i) const
223{
224 return !operator==(i);
225}
226
227bool TConstantUnion::operator!=(const unsigned int u) const
228{
229 return !operator==(u);
230}
231
232bool TConstantUnion::operator!=(const float f) const
233{
234 return !operator==(f);
235}
236
237bool TConstantUnion::operator!=(const bool b) const
238{
239 return !operator==(b);
240}
241
242bool TConstantUnion::operator!=(const TConstantUnion &constant) const
243{
244 return !operator==(constant);
245}
246
247bool TConstantUnion::operator>(const TConstantUnion &constant) const
248{
249 ASSERT(type == constant.type);
250 switch (type)
251 {
252 case EbtInt:
253 return iConst > constant.iConst;
254 case EbtUInt:
255 return uConst > constant.uConst;
256 case EbtFloat:
257 return fConst > constant.fConst;
258 default:
259 return false; // Invalid operation, handled at semantic analysis
260 }
261}
262
263bool TConstantUnion::operator<(const TConstantUnion &constant) const
264{
265 ASSERT(type == constant.type);
266 switch (type)
267 {
268 case EbtInt:
269 return iConst < constant.iConst;
270 case EbtUInt:
271 return uConst < constant.uConst;
272 case EbtFloat:
273 return fConst < constant.fConst;
274 default:
275 return false; // Invalid operation, handled at semantic analysis
276 }
277}
278
279// static
280TConstantUnion TConstantUnion::add(const TConstantUnion &lhs,
281 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400282 TDiagnostics *diag,
283 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400284{
285 TConstantUnion returnValue;
286 ASSERT(lhs.type == rhs.type);
287 switch (lhs.type)
288 {
289 case EbtInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100290 returnValue.setIConst(gl::WrappingSum<int>(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400291 break;
292 case EbtUInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100293 returnValue.setUConst(gl::WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400294 break;
295 case EbtFloat:
Jamie Madill5db69f52016-09-15 12:47:32 -0400296 returnValue.setFConst(CheckedSum<float>(lhs.fConst, rhs.fConst, diag, line));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400297 break;
298 default:
299 UNREACHABLE();
300 }
301
302 return returnValue;
303}
304
305// static
306TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs,
307 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400308 TDiagnostics *diag,
309 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400310{
311 TConstantUnion returnValue;
312 ASSERT(lhs.type == rhs.type);
313 switch (lhs.type)
314 {
315 case EbtInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100316 returnValue.setIConst(gl::WrappingDiff<int>(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400317 break;
318 case EbtUInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100319 returnValue.setUConst(gl::WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400320 break;
321 case EbtFloat:
Jamie Madill5db69f52016-09-15 12:47:32 -0400322 returnValue.setFConst(CheckedDiff<float>(lhs.fConst, rhs.fConst, diag, line));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400323 break;
324 default:
325 UNREACHABLE();
326 }
327
328 return returnValue;
329}
330
331// static
332TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs,
333 const TConstantUnion &rhs,
Jamie Madill5db69f52016-09-15 12:47:32 -0400334 TDiagnostics *diag,
335 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400336{
337 TConstantUnion returnValue;
338 ASSERT(lhs.type == rhs.type);
339 switch (lhs.type)
340 {
341 case EbtInt:
Olli Etuaho7f9a55f2016-10-03 14:32:08 +0100342 returnValue.setIConst(gl::WrappingMul(lhs.iConst, rhs.iConst));
Jamie Madill47cb73a2016-09-09 11:41:44 -0400343 break;
344 case EbtUInt:
Olli Etuaho1be4d492016-09-27 11:15:38 +0100345 // Unsigned integer math in C++ is defined to be done in modulo 2^n, so we rely on that
346 // to implement wrapping multiplication.
347 returnValue.setUConst(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(CheckedMul<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
359TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const
360{
361 TConstantUnion returnValue;
362 ASSERT(type == constant.type);
363 switch (type)
364 {
365 case EbtInt:
366 returnValue.setIConst(iConst % constant.iConst);
367 break;
368 case EbtUInt:
369 returnValue.setUConst(uConst % constant.uConst);
370 break;
371 default:
372 UNREACHABLE();
373 }
374
375 return returnValue;
376}
377
Jamie Madill596018c2016-09-21 12:57:03 -0400378// static
379TConstantUnion TConstantUnion::rshift(const TConstantUnion &lhs,
380 const TConstantUnion &rhs,
381 TDiagnostics *diag,
382 const TSourceLoc &line)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400383{
384 TConstantUnion returnValue;
Olli Etuaho2cacb772016-09-26 08:50:40 +0100385 ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
386 ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
Olli Etuaho43103542016-10-10 12:28:13 +0100387 if ((rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
388 (rhs.type == EbtUInt && rhs.uConst > 31u))
Olli Etuaho2cacb772016-09-26 08:50:40 +0100389 {
390 diag->error(line, "Undefined shift (operand out of range)", ">>", "");
391 switch (lhs.type)
392 {
393 case EbtInt:
394 returnValue.setIConst(0);
395 break;
396 case EbtUInt:
397 returnValue.setUConst(0u);
398 break;
399 default:
400 UNREACHABLE();
401 }
402 return returnValue;
403 }
404
Jamie Madill596018c2016-09-21 12:57:03 -0400405 switch (lhs.type)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400406 {
407 case EbtInt:
Olli Etuaho43103542016-10-10 12:28:13 +0100408 {
409 unsigned int shiftOffset = 0;
Olli Etuaho2cacb772016-09-26 08:50:40 +0100410 switch (rhs.type)
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400411 {
Olli Etuaho2cacb772016-09-26 08:50:40 +0100412 case EbtInt:
Olli Etuaho43103542016-10-10 12:28:13 +0100413 shiftOffset = static_cast<unsigned int>(rhs.iConst);
Olli Etuaho2cacb772016-09-26 08:50:40 +0100414 break;
415 case EbtUInt:
Olli Etuaho43103542016-10-10 12:28:13 +0100416 shiftOffset = rhs.uConst;
Olli Etuaho2cacb772016-09-26 08:50:40 +0100417 break;
418 default:
419 UNREACHABLE();
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400420 }
Olli Etuaho43103542016-10-10 12:28:13 +0100421 if (shiftOffset > 0)
422 {
423 // ESSL 3.00.6 section 5.9: "If E1 is a signed integer, the right-shift will extend
424 // the sign bit." In C++ shifting negative integers is undefined, so we implement
425 // extending the sign bit manually.
426 bool extendSignBit = false;
427 int lhsSafe = lhs.iConst;
428 if (lhsSafe < 0)
429 {
430 extendSignBit = true;
431 // Clear the sign bit so that bitshift right is defined in C++.
432 lhsSafe &= 0x7fffffff;
433 ASSERT(lhsSafe > 0);
434 }
435 returnValue.setIConst(lhsSafe >> shiftOffset);
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400436
Olli Etuaho43103542016-10-10 12:28:13 +0100437 // Manually fill in the extended sign bit if necessary.
438 if (extendSignBit)
439 {
440 int extendedSignBit = static_cast<int>(0xffffffffu << (31 - shiftOffset));
441 returnValue.setIConst(returnValue.getIConst() | extendedSignBit);
442 }
443 }
444 else
445 {
446 returnValue.setIConst(rhs.iConst);
447 }
448 break;
449 }
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);
Olli Etuaho43103542016-10-10 12:28:13 +0100479 if ((rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
480 (rhs.type == EbtUInt && rhs.uConst > 31u))
Olli Etuaho2cacb772016-09-26 08:50:40 +0100481 {
482 diag->error(line, "Undefined shift (operand out of range)", "<<", "");
483 switch (lhs.type)
484 {
485 case EbtInt:
486 returnValue.setIConst(0);
487 break;
488 case EbtUInt:
489 returnValue.setUConst(0u);
490 break;
491 default:
492 UNREACHABLE();
493 }
494 return returnValue;
495 }
496
Jamie Madill596018c2016-09-21 12:57:03 -0400497 switch (lhs.type)
Jamie Madill47cb73a2016-09-09 11:41:44 -0400498 {
499 case EbtInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100500 switch (rhs.type)
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400501 {
Olli Etuaho43103542016-10-10 12:28:13 +0100502 // Cast to unsigned integer before shifting, since ESSL 3.00.6 section 5.9 says that
503 // lhs is "interpreted as a bit pattern". This also avoids the possibility of signed
504 // integer overflow or undefined shift of a negative integer.
Olli Etuaho2cacb772016-09-26 08:50:40 +0100505 case EbtInt:
Olli Etuaho43103542016-10-10 12:28:13 +0100506 returnValue.setIConst(
507 static_cast<int>(static_cast<uint32_t>(lhs.iConst) << rhs.iConst));
Olli Etuaho2cacb772016-09-26 08:50:40 +0100508 break;
509 case EbtUInt:
Olli Etuaho43103542016-10-10 12:28:13 +0100510 returnValue.setIConst(
511 static_cast<int>(static_cast<uint32_t>(lhs.iConst) << rhs.uConst));
Olli Etuaho2cacb772016-09-26 08:50:40 +0100512 break;
513 default:
514 UNREACHABLE();
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400515 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400516 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400517
Jamie Madill47cb73a2016-09-09 11:41:44 -0400518 case EbtUInt:
Olli Etuaho2cacb772016-09-26 08:50:40 +0100519 switch (rhs.type)
520 {
521 case EbtInt:
522 returnValue.setUConst(lhs.uConst << rhs.iConst);
523 break;
524 case EbtUInt:
525 returnValue.setUConst(lhs.uConst << rhs.uConst);
526 break;
527 default:
528 UNREACHABLE();
529 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400530 break;
Jamie Madill44ebf6b2016-09-22 13:00:02 -0400531
Jamie Madill47cb73a2016-09-09 11:41:44 -0400532 default:
533 UNREACHABLE();
534 }
Jamie Madill47cb73a2016-09-09 11:41:44 -0400535 return returnValue;
536}
537
538TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const
539{
540 TConstantUnion returnValue;
541 ASSERT(constant.type == EbtInt || constant.type == EbtUInt);
542 switch (type)
543 {
544 case EbtInt:
545 returnValue.setIConst(iConst & constant.iConst);
546 break;
547 case EbtUInt:
548 returnValue.setUConst(uConst & constant.uConst);
549 break;
550 default:
551 UNREACHABLE();
552 }
553
554 return returnValue;
555}
556
557TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const
558{
559 TConstantUnion returnValue;
560 ASSERT(type == constant.type);
561 switch (type)
562 {
563 case EbtInt:
564 returnValue.setIConst(iConst | constant.iConst);
565 break;
566 case EbtUInt:
567 returnValue.setUConst(uConst | constant.uConst);
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 EbtInt:
583 returnValue.setIConst(iConst ^ constant.iConst);
584 break;
585 case EbtUInt:
586 returnValue.setUConst(uConst ^ constant.uConst);
587 break;
588 default:
589 UNREACHABLE();
590 }
591
592 return returnValue;
593}
594
595TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const
596{
597 TConstantUnion returnValue;
598 ASSERT(type == constant.type);
599 switch (type)
600 {
601 case EbtBool:
602 returnValue.setBConst(bConst && constant.bConst);
603 break;
604 default:
605 UNREACHABLE();
606 }
607
608 return returnValue;
609}
610
611TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const
612{
613 TConstantUnion returnValue;
614 ASSERT(type == constant.type);
615 switch (type)
616 {
617 case EbtBool:
618 returnValue.setBConst(bConst || constant.bConst);
619 break;
620 default:
621 UNREACHABLE();
622 }
623
624 return returnValue;
625}
Jamie Madill45bcc782016-11-07 13:58:48 -0500626
627} // namespace sh