1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "Assert.hpp"
16 #include "Coroutine.hpp"
17 #include "Print.hpp"
18 #include "Reactor.hpp"
19
20 #include "gtest/gtest.h"
21
22 #include <array>
23 #include <cmath>
24 #include <filesystem>
25 #include <fstream>
26 #include <thread>
27 #include <tuple>
28
29 using namespace rr;
30
31 using float4 = float[4];
32 using int4 = int[4];
33
testName()34 static std::string testName()
35 {
36 auto info = ::testing::UnitTest::GetInstance()->current_test_info();
37 return std::string{ info->test_suite_name() } + "_" + info->name();
38 }
39
reference(int * p,int y)40 int reference(int *p, int y)
41 {
42 int x = p[-1];
43 int z = 4;
44
45 for(int i = 0; i < 10; i++)
46 {
47 z += (2 << i) - (i / 3);
48 }
49
50 int sum = x + y + z;
51
52 return sum;
53 }
54
TEST(ReactorUnitTests,Sample)55 TEST(ReactorUnitTests, Sample)
56 {
57 FunctionT<int(int *, int)> function;
58 {
59 Pointer<Int> p = function.Arg<0>();
60 Int x = p[-1];
61 Int y = function.Arg<1>();
62 Int z = 4;
63
64 For(Int i = 0, i < 10, i++)
65 {
66 z += (2 << i) - (i / 3);
67 }
68
69 Float4 v;
70 v.z = As<Float>(z);
71 z = As<Int>(Float(Float4(v.xzxx).y));
72
73 Int sum = x + y + z;
74
75 Return(sum);
76 }
77
78 auto routine = function(testName().c_str());
79
80 int one[2] = { 1, 0 };
81 int result = routine(&one[1], 2);
82 EXPECT_EQ(result, reference(&one[1], 2));
83 }
84
85 // This test demonstrates the use of a 'trampoline', where a routine calls
86 // a static function which then generates another routine during the execution
87 // of the first routine. Also note the code generated for the second routine
88 // depends on a parameter passed to the first routine.
TEST(ReactorUnitTests,Trampoline)89 TEST(ReactorUnitTests, Trampoline)
90 {
91 using SecondaryFunc = int(int, int);
92
93 static auto generateSecondary = [](int upDown) {
94 FunctionT<SecondaryFunc> secondary;
95 {
96 Int x = secondary.Arg<0>();
97 Int y = secondary.Arg<1>();
98 Int r;
99
100 if(upDown > 0)
101 {
102 r = x + y;
103 }
104 else if(upDown < 0)
105 {
106 r = x - y;
107 }
108 else
109 {
110 r = 0;
111 }
112
113 Return(r);
114 }
115
116 static auto routine = secondary((testName() + "_secondary").c_str());
117 return routine.getEntry();
118 };
119
120 using SecondaryGeneratorFunc = SecondaryFunc *(*)(int);
121 SecondaryGeneratorFunc secondaryGenerator = (SecondaryGeneratorFunc)generateSecondary;
122
123 using PrimaryFunc = int(int, int, int);
124
125 FunctionT<PrimaryFunc> primary;
126 {
127 Int x = primary.Arg<0>();
128 Int y = primary.Arg<1>();
129 Int z = primary.Arg<2>();
130
131 Pointer<Byte> secondary = Call(secondaryGenerator, z);
132 Int r = Call<SecondaryFunc>(secondary, x, y);
133
134 Return(r);
135 }
136
137 auto routine = primary((testName() + "_primary").c_str());
138
139 int result = routine(100, 20, -3);
140 EXPECT_EQ(result, 80);
141 }
142
TEST(ReactorUnitTests,Uninitialized)143 TEST(ReactorUnitTests, Uninitialized)
144 {
145 #if __has_feature(memory_sanitizer)
146 // Building the static C++ code with MemorySanitizer enabled does not
147 // automatically enable MemorySanitizer instrumentation for Reactor
148 // routines. False positives can also be prevented by unpoisoning all
149 // memory writes. This Pragma ensures proper instrumentation is enabled.
150 Pragma(MemorySanitizerInstrumentation, true);
151 #endif
152
153 FunctionT<int()> function;
154 {
155 Int a;
156 Int z = 4;
157 Int q;
158 Int c;
159 Int p;
160 Bool b;
161
162 q += q;
163
164 If(b)
165 {
166 c = p;
167 }
168
169 Return(a + z + q + c);
170 }
171
172 auto routine = function(testName().c_str());
173
174 if(!__has_feature(memory_sanitizer))
175 {
176 int result = routine();
177 EXPECT_EQ(result, result); // Anything is fine, just don't crash
178 }
179 else
180 {
181 // Optimizations may turn the conditional If() in the Reactor code
182 // into a conditional move or arithmetic operations, which would not
183 // trigger a MemorySanitizer error. However, in that case the equals
184 // operator below should trigger it before the abort is reached.
185 EXPECT_DEATH(
186 {
187 int result = routine();
188 if(result == 0) abort();
189 },
190 "MemorySanitizer: use-of-uninitialized-value");
191 }
192
193 Pragma(MemorySanitizerInstrumentation, false);
194 }
195
TEST(ReactorUnitTests,Unreachable)196 TEST(ReactorUnitTests, Unreachable)
197 {
198 FunctionT<int(int)> function;
199 {
200 Int a = function.Arg<0>();
201 Int z = 4;
202
203 Return(a + z);
204
205 // Code beyond this point is unreachable but should not cause any
206 // compilation issues.
207
208 z += a;
209 }
210
211 auto routine = function(testName().c_str());
212
213 int result = routine(16);
214 EXPECT_EQ(result, 20);
215 }
216
217 // Stopping in the middle of a `Function<>` is supported and should not affect
218 // subsequent complete ones.
TEST(ReactorUnitTests,UnfinishedFunction)219 TEST(ReactorUnitTests, UnfinishedFunction)
220 {
221 do
222 {
223 FunctionT<int(int)> function;
224 {
225 Int a = function.Arg<0>();
226 Int z = 4;
227
228 if((true)) break; // Terminate do-while early.
229
230 Return(a + z);
231 }
232 } while(true);
233
234 FunctionT<int(int)> function;
235 {
236 Int a = function.Arg<0>();
237 Int z = 4;
238
239 Return(a - z);
240 }
241
242 auto routine = function(testName().c_str());
243
244 int result = routine(16);
245 EXPECT_EQ(result, 12);
246 }
247
248 // Deriving from `Function<>` and using Reactor variables as members can be a
249 // convenient way to 'name' function arguments and compose complex functions
250 // with helper methods. This test checks the interactions between the lifetime
251 // of the `Function<>` and the variables belonging to the derived class.
252 struct FunctionMembers : FunctionT<int(int)>
253 {
FunctionMembersFunctionMembers254 FunctionMembers()
255 : level(Arg<0>())
256 {
257 For(Int i = 0, i < 3, i++)
258 {
259 pourSomeMore();
260 }
261
262 Return(level);
263 }
264
pourSomeMoreFunctionMembers265 void pourSomeMore()
266 {
267 level += 2;
268 }
269
270 Int level;
271 };
272
TEST(ReactorUnitTests,FunctionMembers)273 TEST(ReactorUnitTests, FunctionMembers)
274 {
275 FunctionMembers function;
276
277 auto routine = function(testName().c_str());
278
279 int result = routine(3);
280 EXPECT_EQ(result, 9);
281 }
282
283 // This test excercises modifying the value of a local variable through a
284 // pointer to it.
TEST(ReactorUnitTests,VariableAddress)285 TEST(ReactorUnitTests, VariableAddress)
286 {
287 FunctionT<int(int)> function;
288 {
289 Int a = function.Arg<0>();
290 Int z = 0;
291 Pointer<Int> p = &z;
292 *p = 4;
293
294 Return(a + z);
295 }
296
297 auto routine = function(testName().c_str());
298
299 int result = routine(16);
300 EXPECT_EQ(result, 20);
301 }
302
303 // This test exercises taking the address of a local varible at the end of a
304 // loop and modifying its value through the pointer in the second iteration.
TEST(ReactorUnitTests,LateVariableAddress)305 TEST(ReactorUnitTests, LateVariableAddress)
306 {
307 FunctionT<int(void)> function;
308 {
309 Pointer<Int> p = nullptr;
310 Int a = 0;
311
312 While(a == 0)
313 {
314 If(p != Pointer<Int>(nullptr))
315 {
316 *p = 1;
317 }
318
319 p = &a;
320 }
321
322 Return(a);
323 }
324
325 auto routine = function(testName().c_str());
326
327 int result = routine();
328 EXPECT_EQ(result, 1);
329 }
330
331 // This test checks that the value of a local variable which has been modified
332 // though a pointer is correct at the point before its address is (statically)
333 // obtained.
TEST(ReactorUnitTests,LoadAfterIndirectStore)334 TEST(ReactorUnitTests, LoadAfterIndirectStore)
335 {
336 FunctionT<int(void)> function;
337 {
338 Pointer<Int> p = nullptr;
339 Int a = 0;
340 Int b = 0;
341
342 While(a == 0)
343 {
344 If(p != Pointer<Int>(nullptr))
345 {
346 *p = 1;
347 }
348
349 // `a` must be loaded from memory here, despite not statically knowing
350 // yet that its address will be taken below.
351 b = a + 5;
352
353 p = &a;
354 }
355
356 Return(b);
357 }
358
359 auto routine = function(testName().c_str());
360
361 int result = routine();
362 EXPECT_EQ(result, 6);
363 }
364
365 // This test checks that variables statically accessed after a Return statement
366 // are still loaded, modified, and stored correctly.
TEST(ReactorUnitTests,LoopAfterReturn)367 TEST(ReactorUnitTests, LoopAfterReturn)
368 {
369 FunctionT<int(void)> function;
370 {
371 Int min = 100;
372 Int max = 200;
373
374 If(min > max)
375 {
376 Return(5);
377 }
378
379 While(min < max)
380 {
381 min++;
382 }
383
384 Return(7);
385 }
386
387 auto routine = function(testName().c_str());
388
389 int result = routine();
390 EXPECT_EQ(result, 7);
391 }
392
TEST(ReactorUnitTests,ConstantPointer)393 TEST(ReactorUnitTests, ConstantPointer)
394 {
395 int c = 44;
396
397 FunctionT<int()> function;
398 {
399 Int x = *Pointer<Int>(ConstantPointer(&c));
400
401 Return(x);
402 }
403
404 auto routine = function(testName().c_str());
405
406 int result = routine();
407 EXPECT_EQ(result, 44);
408 }
409
410 // This test excercises the Optimizer::eliminateLoadsFollowingSingleStore() optimization pass.
411 // The three load operations for `y` should get eliminated.
TEST(ReactorUnitTests,EliminateLoadsFollowingSingleStore)412 TEST(ReactorUnitTests, EliminateLoadsFollowingSingleStore)
413 {
414 FunctionT<int(int)> function;
415 {
416 Int x = function.Arg<0>();
417
418 Int y;
419 Int z;
420
421 // This branch materializes the variables.
422 If(x != 0) // TODO(b/179922668): Support If(x)
423 {
424 y = x;
425 z = y + y + y;
426 }
427
428 Return(z);
429 }
430
431 Nucleus::setOptimizerCallback([](const Nucleus::OptimizerReport *report) {
432 EXPECT_EQ(report->allocas, 2);
433 EXPECT_EQ(report->loads, 2);
434 EXPECT_EQ(report->stores, 2);
435 });
436
437 auto routine = function(testName().c_str());
438
439 int result = routine(11);
440 EXPECT_EQ(result, 33);
441 }
442
443 // This test excercises the Optimizer::propagateAlloca() optimization pass.
444 // The pointer variable should not get stored to / loaded from memory.
TEST(ReactorUnitTests,PropagateAlloca)445 TEST(ReactorUnitTests, PropagateAlloca)
446 {
447 FunctionT<int(int)> function;
448 {
449 Int b = function.Arg<0>();
450
451 Int a = 22;
452 Pointer<Int> p;
453
454 // This branch materializes both `a` and `p`, and ensures single basic block
455 // optimizations don't also eliminate the pointer store and load.
456 If(b != 0) // TODO(b/179922668): Support If(b)
457 {
458 p = &a;
459 }
460
461 Return(Int(*p)); // TODO(b/179694472): Support Return(*p)
462 }
463
464 Nucleus::setOptimizerCallback([](const Nucleus::OptimizerReport *report) {
465 EXPECT_EQ(report->allocas, 1);
466 EXPECT_EQ(report->loads, 1);
467 EXPECT_EQ(report->stores, 1);
468 });
469
470 auto routine = function(testName().c_str());
471
472 int result = routine(true);
473 EXPECT_EQ(result, 22);
474 }
475
476 // Corner case for Optimizer::propagateAlloca(). It should not replace loading of `p`
477 // with the addres of `a`, since it also got the address of `b` assigned.
TEST(ReactorUnitTests,PointerToPointer)478 TEST(ReactorUnitTests, PointerToPointer)
479 {
480 FunctionT<int()> function;
481 {
482 Int a = 444;
483 Int b = 555;
484
485 Pointer<Int> p = &a;
486 Pointer<Pointer<Int>> pp = &p;
487 p = &b;
488
489 Return(Int(*Pointer<Int>(*pp))); // TODO(b/179694472): Support **pp
490 }
491
492 auto routine = function(testName().c_str());
493
494 int result = routine();
495 EXPECT_EQ(result, 555);
496 }
497
498 // Corner case for Optimizer::propagateAlloca(). It should not replace loading of `p[i]`
499 // with any of the addresses of the `a`, `b`, or `c`.
TEST(ReactorUnitTests,ArrayOfPointersToLocals)500 TEST(ReactorUnitTests, ArrayOfPointersToLocals)
501 {
502 FunctionT<int(int)> function;
503 {
504 Int i = function.Arg<0>();
505
506 Int a = 111;
507 Int b = 222;
508 Int c = 333;
509
510 Array<Pointer<Int>, 3> p;
511 p[0] = &a;
512 p[1] = &b;
513 p[2] = &c;
514
515 Return(Int(*Pointer<Int>(p[i]))); // TODO(b/179694472): Support *p[i]
516 }
517
518 auto routine = function(testName().c_str());
519
520 int result = routine(1);
521 EXPECT_EQ(result, 222);
522 }
523
TEST(ReactorUnitTests,ModifyLocalThroughPointer)524 TEST(ReactorUnitTests, ModifyLocalThroughPointer)
525 {
526 FunctionT<int(void)> function;
527 {
528 Int a = 1;
529
530 Pointer<Int> p = &a;
531 Pointer<Pointer<Int>> pp = &p;
532
533 Pointer<Int> q = *pp;
534 *q = 3;
535
536 Return(a);
537 }
538
539 auto routine = function(testName().c_str());
540
541 int result = routine();
542 EXPECT_EQ(result, 3);
543 }
544
TEST(ReactorUnitTests,ScalarReplacementOfArray)545 TEST(ReactorUnitTests, ScalarReplacementOfArray)
546 {
547 FunctionT<int(void)> function;
548 {
549 Array<Int, 2> a;
550 a[0] = 1;
551 a[1] = 2;
552
553 Return(a[0] + a[1]);
554 }
555
556 auto routine = function(testName().c_str());
557
558 int result = routine();
559 EXPECT_EQ(result, 3);
560 }
561
TEST(ReactorUnitTests,CArray)562 TEST(ReactorUnitTests, CArray)
563 {
564 FunctionT<int(void)> function;
565 {
566 Int a[2];
567 a[0] = 1;
568 a[1] = 2;
569
570 auto x = a[0];
571 a[0] = a[1];
572 a[1] = x;
573
574 Return(a[0] + a[1]);
575 }
576
577 auto routine = function(testName().c_str());
578
579 int result = routine();
580 EXPECT_EQ(result, 3);
581 }
582
583 // SRoA should replace the array elements with scalars, which in turn enables
584 // eliminating all loads and stores.
TEST(ReactorUnitTests,ReactorArray)585 TEST(ReactorUnitTests, ReactorArray)
586 {
587 FunctionT<int(void)> function;
588 {
589 Array<Int, 2> a;
590 a[0] = 1;
591 a[1] = 2;
592
593 Int x = a[0];
594 a[0] = a[1];
595 a[1] = x;
596
597 Return(a[0] + a[1]);
598 }
599
600 Nucleus::setOptimizerCallback([](const Nucleus::OptimizerReport *report) {
601 EXPECT_EQ(report->allocas, 0);
602 EXPECT_EQ(report->loads, 0);
603 EXPECT_EQ(report->stores, 0);
604 });
605
606 auto routine = function(testName().c_str());
607
608 int result = routine();
609 EXPECT_EQ(result, 3);
610 }
611
612 // Excercises the optimizeSingleBasicBlockLoadsStores optimization pass.
TEST(ReactorUnitTests,StoresInMultipleBlocks)613 TEST(ReactorUnitTests, StoresInMultipleBlocks)
614 {
615 FunctionT<int(int)> function;
616 {
617 Int b = function.Arg<0>();
618
619 Int a = 13;
620
621 If(b != 0) // TODO(b/179922668): Support If(b)
622 {
623 a = 4;
624 a = a + 3;
625 }
626 Else
627 {
628 a = 6;
629 a = a + 5;
630 }
631
632 Return(a);
633 }
634
635 Nucleus::setOptimizerCallback([](const Nucleus::OptimizerReport *report) {
636 EXPECT_EQ(report->allocas, 1);
637 EXPECT_EQ(report->loads, 1);
638 EXPECT_EQ(report->stores, 3);
639 });
640
641 auto routine = function(testName().c_str());
642
643 int result = routine(true);
644 EXPECT_EQ(result, 7);
645 }
646
647 // This is similar to the LoadAfterIndirectStore test except that the indirect
648 // store is preceded by a direct store. The subsequent load should not be replaced
649 // by the value written by the direct store.
TEST(ReactorUnitTests,StoreBeforeIndirectStore)650 TEST(ReactorUnitTests, StoreBeforeIndirectStore)
651 {
652 FunctionT<int(int)> function;
653 {
654 // Int b = function.Arg<0>();
655
656 Int b;
657 Pointer<Int> p = &b;
658 Int a = 13;
659
660 For(Int i = 0, i < 2, i++)
661 {
662 a = 10;
663
664 *p = 4;
665
666 // This load of `a` should not be replaced by the 10 written above, since
667 // in the second iteration `p` points to `a` and writes 4.
668 b = a;
669
670 p = &a;
671 }
672
673 Return(b);
674 }
675
676 auto routine = function(testName().c_str());
677
678 int result = routine(true);
679 EXPECT_EQ(result, 4);
680 }
681
TEST(ReactorUnitTests,AssertTrue)682 TEST(ReactorUnitTests, AssertTrue)
683 {
684 FunctionT<int()> function;
685 {
686 Int a = 3;
687 Int b = 5;
688
689 Assert(a < b);
690
691 Return(a + b);
692 }
693
694 auto routine = function(testName().c_str());
695
696 int result = routine();
697 EXPECT_EQ(result, 8);
698 }
699
TEST(ReactorUnitTests,AssertFalse)700 TEST(ReactorUnitTests, AssertFalse)
701 {
702 FunctionT<int()> function;
703 {
704 Int a = 3;
705 Int b = 5;
706
707 Assert(a == b);
708
709 Return(a + b);
710 }
711
712 auto routine = function(testName().c_str());
713
714 #ifndef NDEBUG
715 # if !defined(__APPLE__)
716 const char *stderrRegex = "AssertFalse"; // stderr should contain the assert's expression, file:line, and function
717 # else
718 const char *stderrRegex = ""; // TODO(b/156389924): On macOS an stderr redirect can cause googletest to fail the capture
719 # endif
720
721 EXPECT_DEATH(
722 {
723 int result = routine();
724 EXPECT_NE(result, result); // We should never reach this
725 },
726 stderrRegex);
727 #else
728 int result = routine();
729 EXPECT_EQ(result, 8);
730 #endif
731 }
732
TEST(ReactorUnitTests,SubVectorLoadStore)733 TEST(ReactorUnitTests, SubVectorLoadStore)
734 {
735 FunctionT<int(void *, void *)> function;
736 {
737 Pointer<Byte> in = function.Arg<0>();
738 Pointer<Byte> out = function.Arg<1>();
739
740 *Pointer<Int4>(out + 16 * 0) = *Pointer<Int4>(in + 16 * 0);
741 *Pointer<Short4>(out + 16 * 1) = *Pointer<Short4>(in + 16 * 1);
742 *Pointer<Byte8>(out + 16 * 2) = *Pointer<Byte8>(in + 16 * 2);
743 *Pointer<Byte4>(out + 16 * 3) = *Pointer<Byte4>(in + 16 * 3);
744 *Pointer<Short2>(out + 16 * 4) = *Pointer<Short2>(in + 16 * 4);
745
746 Return(0);
747 }
748
749 auto routine = function(testName().c_str());
750
751 int8_t in[16 * 5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
752 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, 0, 0, 0, 0,
753 25, 26, 27, 28, 29, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0, 0,
754 33, 34, 35, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
755 37, 38, 39, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
756
757 int8_t out[16 * 5] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
758 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
759 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
760 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
761 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
762
763 routine(in, out);
764
765 for(int row = 0; row < 5; row++)
766 {
767 for(int col = 0; col < 16; col++)
768 {
769 int i = row * 16 + col;
770
771 if(in[i] == 0)
772 {
773 EXPECT_EQ(out[i], -1) << "Row " << row << " column " << col << " not left untouched.";
774 }
775 else
776 {
777 EXPECT_EQ(out[i], in[i]) << "Row " << row << " column " << col << " not equal to input.";
778 }
779 }
780 }
781 }
782
TEST(ReactorUnitTests,VectorConstant)783 TEST(ReactorUnitTests, VectorConstant)
784 {
785 FunctionT<int(void *)> function;
786 {
787 Pointer<Byte> out = function.Arg<0>();
788
789 *Pointer<Int4>(out + 16 * 0) = Int4(0x04030201, 0x08070605, 0x0C0B0A09, 0x100F0E0D);
790 *Pointer<Short4>(out + 16 * 1) = Short4(0x1211, 0x1413, 0x1615, 0x1817);
791 *Pointer<Byte8>(out + 16 * 2) = Byte8(0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20);
792 *Pointer<Int2>(out + 16 * 3) = Int2(0x24232221, 0x28272625);
793
794 Return(0);
795 }
796
797 auto routine = function(testName().c_str());
798
799 int8_t out[16 * 4] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
800 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
801 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
802 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
803
804 int8_t exp[16 * 4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
805 17, 18, 19, 20, 21, 22, 23, 24, -1, -1, -1, -1, -1, -1, -1, -1,
806 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, -1,
807 33, 34, 35, 36, 37, 38, 39, 40, -1, -1, -1, -1, -1, -1, -1, -1 };
808
809 routine(out);
810
811 for(int row = 0; row < 4; row++)
812 {
813 for(int col = 0; col < 16; col++)
814 {
815 int i = row * 16 + col;
816
817 EXPECT_EQ(out[i], exp[i]);
818 }
819 }
820 }
821
TEST(ReactorUnitTests,Concatenate)822 TEST(ReactorUnitTests, Concatenate)
823 {
824 FunctionT<int(void *)> function;
825 {
826 Pointer<Byte> out = function.Arg<0>();
827
828 *Pointer<Int4>(out + 16 * 0) = Int4(Int2(0x04030201, 0x08070605), Int2(0x0C0B0A09, 0x100F0E0D));
829 *Pointer<Short8>(out + 16 * 1) = Short8(Short4(0x0201, 0x0403, 0x0605, 0x0807), Short4(0x0A09, 0x0C0B, 0x0E0D, 0x100F));
830
831 Return(0);
832 }
833
834 auto routine = function(testName().c_str());
835
836 int8_t ref[16 * 5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
837 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
838
839 int8_t out[16 * 5] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
840 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
841
842 routine(out);
843
844 for(int row = 0; row < 2; row++)
845 {
846 for(int col = 0; col < 16; col++)
847 {
848 int i = row * 16 + col;
849
850 EXPECT_EQ(out[i], ref[i]) << "Row " << row << " column " << col << " not equal to reference.";
851 }
852 }
853 }
854
TEST(ReactorUnitTests,Cast)855 TEST(ReactorUnitTests, Cast)
856 {
857 FunctionT<void(void *)> function;
858 {
859 Pointer<Byte> out = function.Arg<0>();
860
861 Int4 c = Int4(0x01020304, 0x05060708, 0x09101112, 0x13141516);
862 *Pointer<Short4>(out + 16 * 0) = Short4(c);
863 *Pointer<Byte4>(out + 16 * 1 + 0) = Byte4(c);
864 *Pointer<Byte4>(out + 16 * 1 + 4) = Byte4(As<Byte8>(c));
865 *Pointer<Byte4>(out + 16 * 1 + 8) = Byte4(As<Short4>(c));
866 }
867
868 auto routine = function(testName().c_str());
869
870 int out[2][4];
871
872 memset(&out, 0, sizeof(out));
873
874 routine(&out);
875
876 EXPECT_EQ(out[0][0], 0x07080304);
877 EXPECT_EQ(out[0][1], 0x15161112);
878
879 EXPECT_EQ(out[1][0], 0x16120804);
880 EXPECT_EQ(out[1][1], 0x01020304);
881 EXPECT_EQ(out[1][2], 0x06080204);
882 }
883
swizzleCode4(int i)884 static uint16_t swizzleCode4(int i)
885 {
886 auto x = (i >> 0) & 0x03;
887 auto y = (i >> 2) & 0x03;
888 auto z = (i >> 4) & 0x03;
889 auto w = (i >> 6) & 0x03;
890 return static_cast<uint16_t>((x << 12) | (y << 8) | (z << 4) | (w << 0));
891 }
892
TEST(ReactorUnitTests,Swizzle4)893 TEST(ReactorUnitTests, Swizzle4)
894 {
895 FunctionT<void(void *)> function;
896 {
897 Pointer<Byte> out = function.Arg<0>();
898
899 for(int i = 0; i < 256; i++)
900 {
901 *Pointer<Float4>(out + 16 * i) = Swizzle(Float4(1.0f, 2.0f, 3.0f, 4.0f), swizzleCode4(i));
902 }
903
904 for(int i = 0; i < 256; i++)
905 {
906 *Pointer<Float4>(out + 16 * (256 + i)) = ShuffleLowHigh(Float4(1.0f, 2.0f, 3.0f, 4.0f), Float4(5.0f, 6.0f, 7.0f, 8.0f), swizzleCode4(i));
907 }
908
909 *Pointer<Float4>(out + 16 * (512 + 0)) = UnpackLow(Float4(1.0f, 2.0f, 3.0f, 4.0f), Float4(5.0f, 6.0f, 7.0f, 8.0f));
910 *Pointer<Float4>(out + 16 * (512 + 1)) = UnpackHigh(Float4(1.0f, 2.0f, 3.0f, 4.0f), Float4(5.0f, 6.0f, 7.0f, 8.0f));
911 *Pointer<Int2>(out + 16 * (512 + 2)) = UnpackLow(Short4(1, 2, 3, 4), Short4(5, 6, 7, 8));
912 *Pointer<Int2>(out + 16 * (512 + 3)) = UnpackHigh(Short4(1, 2, 3, 4), Short4(5, 6, 7, 8));
913 *Pointer<Short4>(out + 16 * (512 + 4)) = UnpackLow(Byte8(1, 2, 3, 4, 5, 6, 7, 8), Byte8(9, 10, 11, 12, 13, 14, 15, 16));
914 *Pointer<Short4>(out + 16 * (512 + 5)) = UnpackHigh(Byte8(1, 2, 3, 4, 5, 6, 7, 8), Byte8(9, 10, 11, 12, 13, 14, 15, 16));
915
916 for(int i = 0; i < 256; i++)
917 {
918 *Pointer<Short4>(out + 16 * (512 + 6) + (8 * i)) =
919 Swizzle(Short4(1, 2, 3, 4), swizzleCode4(i));
920 }
921
922 for(int i = 0; i < 256; i++)
923 {
924 *Pointer<Int4>(out + 16 * (512 + 6 + i) + (8 * 256)) =
925 Swizzle(Int4(1, 2, 3, 4), swizzleCode4(i));
926 }
927 }
928
929 auto routine = function(testName().c_str());
930
931 struct
932 {
933 float f[256 + 256 + 2][4];
934 int i[388][4];
935 } out;
936
937 memset(&out, 0, sizeof(out));
938
939 routine(&out);
940
941 for(int i = 0; i < 256; i++)
942 {
943 EXPECT_EQ(out.f[i][0], float((i >> 0) & 0x03) + 1.0f);
944 EXPECT_EQ(out.f[i][1], float((i >> 2) & 0x03) + 1.0f);
945 EXPECT_EQ(out.f[i][2], float((i >> 4) & 0x03) + 1.0f);
946 EXPECT_EQ(out.f[i][3], float((i >> 6) & 0x03) + 1.0f);
947 }
948
949 for(int i = 0; i < 256; i++)
950 {
951 EXPECT_EQ(out.f[256 + i][0], float((i >> 0) & 0x03) + 1.0f);
952 EXPECT_EQ(out.f[256 + i][1], float((i >> 2) & 0x03) + 1.0f);
953 EXPECT_EQ(out.f[256 + i][2], float((i >> 4) & 0x03) + 5.0f);
954 EXPECT_EQ(out.f[256 + i][3], float((i >> 6) & 0x03) + 5.0f);
955 }
956
957 EXPECT_EQ(out.f[512 + 0][0], 1.0f);
958 EXPECT_EQ(out.f[512 + 0][1], 5.0f);
959 EXPECT_EQ(out.f[512 + 0][2], 2.0f);
960 EXPECT_EQ(out.f[512 + 0][3], 6.0f);
961
962 EXPECT_EQ(out.f[512 + 1][0], 3.0f);
963 EXPECT_EQ(out.f[512 + 1][1], 7.0f);
964 EXPECT_EQ(out.f[512 + 1][2], 4.0f);
965 EXPECT_EQ(out.f[512 + 1][3], 8.0f);
966
967 EXPECT_EQ(out.i[0][0], 0x00050001);
968 EXPECT_EQ(out.i[0][1], 0x00060002);
969 EXPECT_EQ(out.i[0][2], 0x00000000);
970 EXPECT_EQ(out.i[0][3], 0x00000000);
971
972 EXPECT_EQ(out.i[1][0], 0x00070003);
973 EXPECT_EQ(out.i[1][1], 0x00080004);
974 EXPECT_EQ(out.i[1][2], 0x00000000);
975 EXPECT_EQ(out.i[1][3], 0x00000000);
976
977 EXPECT_EQ(out.i[2][0], 0x0A020901);
978 EXPECT_EQ(out.i[2][1], 0x0C040B03);
979 EXPECT_EQ(out.i[2][2], 0x00000000);
980 EXPECT_EQ(out.i[2][3], 0x00000000);
981
982 EXPECT_EQ(out.i[3][0], 0x0E060D05);
983 EXPECT_EQ(out.i[3][1], 0x10080F07);
984 EXPECT_EQ(out.i[3][2], 0x00000000);
985 EXPECT_EQ(out.i[3][3], 0x00000000);
986
987 for(int i = 0; i < 256; i++)
988 {
989 EXPECT_EQ(out.i[4 + i / 2][0 + (i % 2) * 2] & 0xFFFF,
990 ((i >> 0) & 0x03) + 1);
991 EXPECT_EQ(out.i[4 + i / 2][0 + (i % 2) * 2] >> 16,
992 ((i >> 2) & 0x03) + 1);
993 EXPECT_EQ(out.i[4 + i / 2][1 + (i % 2) * 2] & 0xFFFF,
994 ((i >> 4) & 0x03) + 1);
995 EXPECT_EQ(out.i[4 + i / 2][1 + (i % 2) * 2] >> 16,
996 ((i >> 6) & 0x03) + 1);
997 }
998
999 for(int i = 0; i < 256; i++)
1000 {
1001 EXPECT_EQ(out.i[132 + i][0], ((i >> 0) & 0x03) + 1);
1002 EXPECT_EQ(out.i[132 + i][1], ((i >> 2) & 0x03) + 1);
1003 EXPECT_EQ(out.i[132 + i][2], ((i >> 4) & 0x03) + 1);
1004 EXPECT_EQ(out.i[132 + i][3], ((i >> 6) & 0x03) + 1);
1005 }
1006 }
1007
TEST(ReactorUnitTests,Swizzle)1008 TEST(ReactorUnitTests, Swizzle)
1009 {
1010 FunctionT<void(void *)> function;
1011 {
1012 Pointer<Byte> out = function.Arg<0>();
1013
1014 Int4 c = Int4(0x01020304, 0x05060708, 0x09101112, 0x13141516);
1015 *Pointer<Byte16>(out + 16 * 0) = Swizzle(As<Byte16>(c), 0xFEDCBA9876543210ull);
1016 *Pointer<Byte8>(out + 16 * 1) = Swizzle(As<Byte8>(c), 0x76543210u);
1017 *Pointer<UShort8>(out + 16 * 2) = Swizzle(As<UShort8>(c), 0x76543210u);
1018 }
1019
1020 auto routine = function(testName().c_str());
1021
1022 int out[3][4];
1023
1024 memset(&out, 0, sizeof(out));
1025
1026 routine(&out);
1027
1028 EXPECT_EQ(out[0][0], 0x16151413);
1029 EXPECT_EQ(out[0][1], 0x12111009);
1030 EXPECT_EQ(out[0][2], 0x08070605);
1031 EXPECT_EQ(out[0][3], 0x04030201);
1032
1033 EXPECT_EQ(out[1][0], 0x08070605);
1034 EXPECT_EQ(out[1][1], 0x04030201);
1035
1036 EXPECT_EQ(out[2][0], 0x15161314);
1037 EXPECT_EQ(out[2][1], 0x11120910);
1038 EXPECT_EQ(out[2][2], 0x07080506);
1039 EXPECT_EQ(out[2][3], 0x03040102);
1040 }
1041
TEST(ReactorUnitTests,Shuffle)1042 TEST(ReactorUnitTests, Shuffle)
1043 {
1044 // |select| is [0aaa:0bbb:0ccc:0ddd] where |aaa|, |bbb|, |ccc|
1045 // and |ddd| are 7-bit selection indices. For a total (1 << 12)
1046 // possibilities.
1047 const int kSelectRange = 1 << 12;
1048
1049 // Unfortunately, testing the whole kSelectRange results in a test
1050 // that is far too slow to run, because LLVM spends exponentially more
1051 // time optimizing the function below as the number of test cases
1052 // increases.
1053 //
1054 // To work-around the problem, only test a subset of the range by
1055 // skipping every kRangeIncrement value.
1056 //
1057 // Set this value to 1 if you want to test the whole implementation,
1058 // which will take a little less than 2 minutes on a fast workstation.
1059 //
1060 // The default value here takes about 1390ms, which is a little more than
1061 // what the Swizzle test takes (993 ms) on my machine. A non-power-of-2
1062 // value ensures a better spread over possible values.
1063 const int kRangeIncrement = 11;
1064
1065 auto rangeIndexToSelect = [](int i) {
1066 return static_cast<unsigned short>(
1067 (((i >> 9) & 7) << 0) |
1068 (((i >> 6) & 7) << 4) |
1069 (((i >> 3) & 7) << 8) |
1070 (((i >> 0) & 7) << 12));
1071 };
1072
1073 FunctionT<int(void *)> function;
1074 {
1075 Pointer<Byte> out = function.Arg<0>();
1076
1077 for(int i = 0; i < kSelectRange; i += kRangeIncrement)
1078 {
1079 unsigned short select = rangeIndexToSelect(i);
1080
1081 *Pointer<Float4>(out + 16 * i) = Shuffle(Float4(1.0f, 2.0f, 3.0f, 4.0f),
1082 Float4(5.0f, 6.0f, 7.0f, 8.0f),
1083 select);
1084
1085 *Pointer<Int4>(out + (kSelectRange + i) * 16) = Shuffle(Int4(10, 11, 12, 13),
1086 Int4(14, 15, 16, 17),
1087 select);
1088
1089 *Pointer<UInt4>(out + (2 * kSelectRange + i) * 16) = Shuffle(UInt4(100, 101, 102, 103),
1090 UInt4(104, 105, 106, 107),
1091 select);
1092 }
1093
1094 Return(0);
1095 }
1096
1097 auto routine = function(testName().c_str());
1098
1099 struct
1100 {
1101 float f[kSelectRange][4];
1102 int i[kSelectRange][4];
1103 unsigned u[kSelectRange][4];
1104 } out;
1105
1106 memset(&out, 0, sizeof(out));
1107
1108 routine(&out);
1109
1110 for(int i = 0; i < kSelectRange; i += kRangeIncrement)
1111 {
1112 EXPECT_EQ(out.f[i][0], float(1.0f + (i & 7)));
1113 EXPECT_EQ(out.f[i][1], float(1.0f + ((i >> 3) & 7)));
1114 EXPECT_EQ(out.f[i][2], float(1.0f + ((i >> 6) & 7)));
1115 EXPECT_EQ(out.f[i][3], float(1.0f + ((i >> 9) & 7)));
1116 }
1117
1118 for(int i = 0; i < kSelectRange; i += kRangeIncrement)
1119 {
1120 EXPECT_EQ(out.i[i][0], int(10 + (i & 7)));
1121 EXPECT_EQ(out.i[i][1], int(10 + ((i >> 3) & 7)));
1122 EXPECT_EQ(out.i[i][2], int(10 + ((i >> 6) & 7)));
1123 EXPECT_EQ(out.i[i][3], int(10 + ((i >> 9) & 7)));
1124 }
1125
1126 for(int i = 0; i < kSelectRange; i += kRangeIncrement)
1127 {
1128 EXPECT_EQ(out.u[i][0], unsigned(100 + (i & 7)));
1129 EXPECT_EQ(out.u[i][1], unsigned(100 + ((i >> 3) & 7)));
1130 EXPECT_EQ(out.u[i][2], unsigned(100 + ((i >> 6) & 7)));
1131 EXPECT_EQ(out.u[i][3], unsigned(100 + ((i >> 9) & 7)));
1132 }
1133 }
1134
TEST(ReactorUnitTests,Broadcast)1135 TEST(ReactorUnitTests, Broadcast)
1136 {
1137 FunctionT<int()> function;
1138 {
1139 Int4 i = 2;
1140 Int j = 3 + i.x;
1141 Int4 k = i * 7;
1142
1143 Return(k.z - j);
1144 }
1145
1146 auto routine = function(testName().c_str());
1147
1148 int result = routine();
1149
1150 EXPECT_EQ(result, 9);
1151 }
1152
TEST(ReactorUnitTests,Branching)1153 TEST(ReactorUnitTests, Branching)
1154 {
1155 FunctionT<int()> function;
1156 {
1157 Int x = 0;
1158
1159 For(Int i = 0, i < 8, i++)
1160 {
1161 If(i < 2)
1162 {
1163 x += 1;
1164 }
1165 Else If(i < 4)
1166 {
1167 x += 10;
1168 }
1169 Else If(i < 6)
1170 {
1171 x += 100;
1172 }
1173 Else
1174 {
1175 x += 1000;
1176 }
1177
1178 For(Int i = 0, i < 5, i++)
1179 x += 10000;
1180 }
1181
1182 For(Int i = 0, i < 10, i++) for(int i = 0; i < 10; i++)
1183 For(Int i = 0, i < 10, i++)
1184 {
1185 x += 1000000;
1186 }
1187
1188 For(Int i = 0, i < 2, i++)
1189 If(x == 1000402222)
1190 {
1191 If(x != 1000402222)
1192 x += 1000000000;
1193 }
1194 Else
1195 x = -5;
1196
1197 Return(x);
1198 }
1199
1200 auto routine = function(testName().c_str());
1201
1202 int result = routine();
1203
1204 EXPECT_EQ(result, 1000402222);
1205 }
1206
TEST(ReactorUnitTests,FMulAdd)1207 TEST(ReactorUnitTests, FMulAdd)
1208 {
1209 Function<Void(Pointer<Float4>, Pointer<Float4>, Pointer<Float4>, Pointer<Float4>)> function;
1210 {
1211 Pointer<Float4> r = function.Arg<0>();
1212 Pointer<Float4> x = function.Arg<1>();
1213 Pointer<Float4> y = function.Arg<2>();
1214 Pointer<Float4> z = function.Arg<3>();
1215
1216 *r = MulAdd(*x, *y, *z);
1217 }
1218
1219 auto routine = function(testName().c_str());
1220 auto callable = (void (*)(float4 *, float4 *, float4 *, float4 *))routine->getEntry();
1221
1222 float x[] = { 0.0f, 2.0f, 4.0f, 1.00000011920929f };
1223 float y[] = { 0.0f, 3.0f, 0.0f, 53400708.0f };
1224 float z[] = { 0.0f, 0.0f, 7.0f, -53400708.0f };
1225
1226 for(size_t i = 0; i < std::size(x); i++)
1227 {
1228 float4 x_in = { x[i], x[i], x[i], x[i] };
1229 float4 y_in = { y[i], y[i], y[i], y[i] };
1230 float4 z_in = { z[i], z[i], z[i], z[i] };
1231 float4 r_out;
1232
1233 callable(&r_out, &x_in, &y_in, &z_in);
1234
1235 // Possible results
1236 float fma = fmaf(x[i], y[i], z[i]);
1237 float mul_add = x[i] * y[i] + z[i];
1238
1239 // If the backend and the CPU support FMA instructions, we assume MulAdd to use
1240 // them. Otherwise it may behave as a multiplication followed by an addition.
1241 if(rr::Caps::fmaIsFast())
1242 {
1243 EXPECT_FLOAT_EQ(r_out[0], fma);
1244 }
1245 else if(r_out[0] != fma)
1246 {
1247 EXPECT_FLOAT_EQ(r_out[0], mul_add);
1248 }
1249 }
1250 }
1251
TEST(ReactorUnitTests,FMA)1252 TEST(ReactorUnitTests, FMA)
1253 {
1254 Function<Void(Pointer<Float4>, Pointer<Float4>, Pointer<Float4>, Pointer<Float4>)> function;
1255 {
1256 Pointer<Float4> r = function.Arg<0>();
1257 Pointer<Float4> x = function.Arg<1>();
1258 Pointer<Float4> y = function.Arg<2>();
1259 Pointer<Float4> z = function.Arg<3>();
1260
1261 *r = FMA(*x, *y, *z);
1262 }
1263
1264 auto routine = function(testName().c_str());
1265 auto callable = (void (*)(float4 *, float4 *, float4 *, float4 *))routine->getEntry();
1266
1267 float x[] = { 0.0f, 2.0f, 4.0f, 1.00000011920929f };
1268 float y[] = { 0.0f, 3.0f, 0.0f, 53400708.0f };
1269 float z[] = { 0.0f, 0.0f, 7.0f, -53400708.0f };
1270
1271 for(size_t i = 0; i < std::size(x); i++)
1272 {
1273 float4 x_in = { x[i], x[i], x[i], x[i] };
1274 float4 y_in = { y[i], y[i], y[i], y[i] };
1275 float4 z_in = { z[i], z[i], z[i], z[i] };
1276 float4 r_out;
1277
1278 callable(&r_out, &x_in, &y_in, &z_in);
1279
1280 float expected = fmaf(x[i], y[i], z[i]);
1281 EXPECT_FLOAT_EQ(r_out[0], expected);
1282 }
1283 }
1284
TEST(ReactorUnitTests,FAbs)1285 TEST(ReactorUnitTests, FAbs)
1286 {
1287 Function<Void(Pointer<Float4>, Pointer<Float4>)> function;
1288 {
1289 Pointer<Float4> x = function.Arg<0>();
1290 Pointer<Float4> y = function.Arg<1>();
1291
1292 *y = Abs(*x);
1293 }
1294
1295 auto routine = function(testName().c_str());
1296 auto callable = (void (*)(float4 *, float4 *))routine->getEntry();
1297
1298 float input[] = { 1.0f, -1.0f, -0.0f, 0.0f };
1299
1300 for(float x : input)
1301 {
1302 float4 v_in = { x, x, x, x };
1303 float4 v_out;
1304
1305 callable(&v_in, &v_out);
1306
1307 float expected = fabs(x);
1308 EXPECT_FLOAT_EQ(v_out[0], expected);
1309 }
1310 }
1311
TEST(ReactorUnitTests,Abs)1312 TEST(ReactorUnitTests, Abs)
1313 {
1314 Function<Void(Pointer<Int4>, Pointer<Int4>)> function;
1315 {
1316 Pointer<Int4> x = function.Arg<0>();
1317 Pointer<Int4> y = function.Arg<1>();
1318
1319 *y = Abs(*x);
1320 }
1321
1322 auto routine = function(testName().c_str());
1323 auto callable = (void (*)(int4 *, int4 *))routine->getEntry();
1324
1325 int input[] = { 1, -1, 0, (int)0x80000000 };
1326
1327 for(int x : input)
1328 {
1329 int4 v_in = { x, x, x, x };
1330 int4 v_out;
1331
1332 callable(&v_in, &v_out);
1333
1334 float expected = abs(x);
1335 EXPECT_EQ(v_out[0], expected);
1336 }
1337 }
1338
TEST(ReactorUnitTests,MinMax)1339 TEST(ReactorUnitTests, MinMax)
1340 {
1341 FunctionT<int(void *)> function;
1342 {
1343 Pointer<Byte> out = function.Arg<0>();
1344
1345 *Pointer<Float4>(out + 16 * 0) = Min(Float4(1.0f, 0.0f, -0.0f, +0.0f), Float4(0.0f, 1.0f, +0.0f, -0.0f));
1346 *Pointer<Float4>(out + 16 * 1) = Max(Float4(1.0f, 0.0f, -0.0f, +0.0f), Float4(0.0f, 1.0f, +0.0f, -0.0f));
1347
1348 *Pointer<Int4>(out + 16 * 2) = Min(Int4(1, 0, -1, -0), Int4(0, 1, 0, +0));
1349 *Pointer<Int4>(out + 16 * 3) = Max(Int4(1, 0, -1, -0), Int4(0, 1, 0, +0));
1350 *Pointer<UInt4>(out + 16 * 4) = Min(UInt4(1, 0, -1, -0), UInt4(0, 1, 0, +0));
1351 *Pointer<UInt4>(out + 16 * 5) = Max(UInt4(1, 0, -1, -0), UInt4(0, 1, 0, +0));
1352
1353 *Pointer<Short4>(out + 16 * 6) = Min(Short4(1, 0, -1, -0), Short4(0, 1, 0, +0));
1354 *Pointer<Short4>(out + 16 * 7) = Max(Short4(1, 0, -1, -0), Short4(0, 1, 0, +0));
1355 *Pointer<UShort4>(out + 16 * 8) = Min(UShort4(1, 0, -1, -0), UShort4(0, 1, 0, +0));
1356 *Pointer<UShort4>(out + 16 * 9) = Max(UShort4(1, 0, -1, -0), UShort4(0, 1, 0, +0));
1357
1358 Return(0);
1359 }
1360
1361 auto routine = function(testName().c_str());
1362
1363 unsigned int out[10][4];
1364
1365 memset(&out, 0, sizeof(out));
1366
1367 routine(&out);
1368
1369 EXPECT_EQ(out[0][0], 0x00000000u);
1370 EXPECT_EQ(out[0][1], 0x00000000u);
1371 EXPECT_EQ(out[0][2], 0x00000000u);
1372 EXPECT_EQ(out[0][3], 0x80000000u);
1373
1374 EXPECT_EQ(out[1][0], 0x3F800000u);
1375 EXPECT_EQ(out[1][1], 0x3F800000u);
1376 EXPECT_EQ(out[1][2], 0x00000000u);
1377 EXPECT_EQ(out[1][3], 0x80000000u);
1378
1379 EXPECT_EQ(out[2][0], 0x00000000u);
1380 EXPECT_EQ(out[2][1], 0x00000000u);
1381 EXPECT_EQ(out[2][2], 0xFFFFFFFFu);
1382 EXPECT_EQ(out[2][3], 0x00000000u);
1383
1384 EXPECT_EQ(out[3][0], 0x00000001u);
1385 EXPECT_EQ(out[3][1], 0x00000001u);
1386 EXPECT_EQ(out[3][2], 0x00000000u);
1387 EXPECT_EQ(out[3][3], 0x00000000u);
1388
1389 EXPECT_EQ(out[4][0], 0x00000000u);
1390 EXPECT_EQ(out[4][1], 0x00000000u);
1391 EXPECT_EQ(out[4][2], 0x00000000u);
1392 EXPECT_EQ(out[4][3], 0x00000000u);
1393
1394 EXPECT_EQ(out[5][0], 0x00000001u);
1395 EXPECT_EQ(out[5][1], 0x00000001u);
1396 EXPECT_EQ(out[5][2], 0xFFFFFFFFu);
1397 EXPECT_EQ(out[5][3], 0x00000000u);
1398
1399 EXPECT_EQ(out[6][0], 0x00000000u);
1400 EXPECT_EQ(out[6][1], 0x0000FFFFu);
1401 EXPECT_EQ(out[6][2], 0x00000000u);
1402 EXPECT_EQ(out[6][3], 0x00000000u);
1403
1404 EXPECT_EQ(out[7][0], 0x00010001u);
1405 EXPECT_EQ(out[7][1], 0x00000000u);
1406 EXPECT_EQ(out[7][2], 0x00000000u);
1407 EXPECT_EQ(out[7][3], 0x00000000u);
1408
1409 EXPECT_EQ(out[8][0], 0x00000000u);
1410 EXPECT_EQ(out[8][1], 0x00000000u);
1411 EXPECT_EQ(out[8][2], 0x00000000u);
1412 EXPECT_EQ(out[8][3], 0x00000000u);
1413
1414 EXPECT_EQ(out[9][0], 0x00010001u);
1415 EXPECT_EQ(out[9][1], 0x0000FFFFu);
1416 EXPECT_EQ(out[9][2], 0x00000000u);
1417 EXPECT_EQ(out[9][3], 0x00000000u);
1418 }
1419
TEST(ReactorUnitTests,NotNeg)1420 TEST(ReactorUnitTests, NotNeg)
1421 {
1422 FunctionT<int(void *)> function;
1423 {
1424 Pointer<Byte> out = function.Arg<0>();
1425
1426 *Pointer<Int>(out + 16 * 0) = ~Int(0x55555555);
1427 *Pointer<Short>(out + 16 * 1) = ~Short(0x5555);
1428 *Pointer<Int4>(out + 16 * 2) = ~Int4(0x55555555, 0xAAAAAAAA, 0x00000000, 0xFFFFFFFF);
1429 *Pointer<Short4>(out + 16 * 3) = ~Short4(0x5555, 0xAAAA, 0x0000, 0xFFFF);
1430
1431 *Pointer<Int>(out + 16 * 4) = -Int(0x55555555);
1432 *Pointer<Short>(out + 16 * 5) = -Short(0x5555);
1433 *Pointer<Int4>(out + 16 * 6) = -Int4(0x55555555, 0xAAAAAAAA, 0x00000000, 0xFFFFFFFF);
1434 *Pointer<Short4>(out + 16 * 7) = -Short4(0x5555, 0xAAAA, 0x0000, 0xFFFF);
1435
1436 *Pointer<Float4>(out + 16 * 8) = -Float4(1.0f, -1.0f, 0.0f, -0.0f);
1437
1438 Return(0);
1439 }
1440
1441 auto routine = function(testName().c_str());
1442
1443 unsigned int out[10][4];
1444
1445 memset(&out, 0, sizeof(out));
1446
1447 routine(&out);
1448
1449 EXPECT_EQ(out[0][0], 0xAAAAAAAAu);
1450 EXPECT_EQ(out[0][1], 0x00000000u);
1451 EXPECT_EQ(out[0][2], 0x00000000u);
1452 EXPECT_EQ(out[0][3], 0x00000000u);
1453
1454 EXPECT_EQ(out[1][0], 0x0000AAAAu);
1455 EXPECT_EQ(out[1][1], 0x00000000u);
1456 EXPECT_EQ(out[1][2], 0x00000000u);
1457 EXPECT_EQ(out[1][3], 0x00000000u);
1458
1459 EXPECT_EQ(out[2][0], 0xAAAAAAAAu);
1460 EXPECT_EQ(out[2][1], 0x55555555u);
1461 EXPECT_EQ(out[2][2], 0xFFFFFFFFu);
1462 EXPECT_EQ(out[2][3], 0x00000000u);
1463
1464 EXPECT_EQ(out[3][0], 0x5555AAAAu);
1465 EXPECT_EQ(out[3][1], 0x0000FFFFu);
1466 EXPECT_EQ(out[3][2], 0x00000000u);
1467 EXPECT_EQ(out[3][3], 0x00000000u);
1468
1469 EXPECT_EQ(out[4][0], 0xAAAAAAABu);
1470 EXPECT_EQ(out[4][1], 0x00000000u);
1471 EXPECT_EQ(out[4][2], 0x00000000u);
1472 EXPECT_EQ(out[4][3], 0x00000000u);
1473
1474 EXPECT_EQ(out[5][0], 0x0000AAABu);
1475 EXPECT_EQ(out[5][1], 0x00000000u);
1476 EXPECT_EQ(out[5][2], 0x00000000u);
1477 EXPECT_EQ(out[5][3], 0x00000000u);
1478
1479 EXPECT_EQ(out[6][0], 0xAAAAAAABu);
1480 EXPECT_EQ(out[6][1], 0x55555556u);
1481 EXPECT_EQ(out[6][2], 0x00000000u);
1482 EXPECT_EQ(out[6][3], 0x00000001u);
1483
1484 EXPECT_EQ(out[7][0], 0x5556AAABu);
1485 EXPECT_EQ(out[7][1], 0x00010000u);
1486 EXPECT_EQ(out[7][2], 0x00000000u);
1487 EXPECT_EQ(out[7][3], 0x00000000u);
1488
1489 EXPECT_EQ(out[8][0], 0xBF800000u);
1490 EXPECT_EQ(out[8][1], 0x3F800000u);
1491 EXPECT_EQ(out[8][2], 0x80000000u);
1492 EXPECT_EQ(out[8][3], 0x00000000u);
1493 }
1494
TEST(ReactorUnitTests,RoundInt)1495 TEST(ReactorUnitTests, RoundInt)
1496 {
1497 FunctionT<int(void *)> function;
1498 {
1499 Pointer<Byte> out = function.Arg<0>();
1500
1501 *Pointer<Int4>(out + 0) = RoundInt(Float4(3.1f, 3.6f, -3.1f, -3.6f));
1502 *Pointer<Int4>(out + 16) = RoundIntClamped(Float4(2147483648.0f, -2147483648.0f, 2147483520, -2147483520));
1503
1504 Return(0);
1505 }
1506
1507 auto routine = function(testName().c_str());
1508
1509 int out[2][4];
1510
1511 memset(&out, 0, sizeof(out));
1512
1513 routine(&out);
1514
1515 EXPECT_EQ(out[0][0], 3);
1516 EXPECT_EQ(out[0][1], 4);
1517 EXPECT_EQ(out[0][2], -3);
1518 EXPECT_EQ(out[0][3], -4);
1519
1520 // x86 returns 0x80000000 for values which cannot be represented in a 32-bit
1521 // integer, but RoundIntClamped() clamps to ensure a positive value for
1522 // positive input. ARM saturates to the largest representable integers.
1523 EXPECT_GE(out[1][0], 2147483520);
1524 EXPECT_LT(out[1][1], -2147483647);
1525 EXPECT_EQ(out[1][2], 2147483520);
1526 EXPECT_EQ(out[1][3], -2147483520);
1527 }
1528
TEST(ReactorUnitTests,FPtoUI)1529 TEST(ReactorUnitTests, FPtoUI)
1530 {
1531 FunctionT<int(void *)> function;
1532 {
1533 Pointer<Byte> out = function.Arg<0>();
1534
1535 *Pointer<UInt>(out + 0) = UInt(Float(0xF0000000u));
1536 *Pointer<UInt>(out + 4) = UInt(Float(0xC0000000u));
1537 *Pointer<UInt>(out + 8) = UInt(Float(0x00000001u));
1538 *Pointer<UInt>(out + 12) = UInt(Float(0xF000F000u));
1539
1540 *Pointer<UInt4>(out + 16) = UInt4(Float4(0xF0000000u, 0x80000000u, 0x00000000u, 0xCCCC0000u));
1541
1542 Return(0);
1543 }
1544
1545 auto routine = function(testName().c_str());
1546
1547 unsigned int out[2][4];
1548
1549 memset(&out, 0, sizeof(out));
1550
1551 routine(&out);
1552
1553 EXPECT_EQ(out[0][0], 0xF0000000u);
1554 EXPECT_EQ(out[0][1], 0xC0000000u);
1555 EXPECT_EQ(out[0][2], 0x00000001u);
1556 EXPECT_EQ(out[0][3], 0xF000F000u);
1557
1558 EXPECT_EQ(out[1][0], 0xF0000000u);
1559 EXPECT_EQ(out[1][1], 0x80000000u);
1560 EXPECT_EQ(out[1][2], 0x00000000u);
1561 EXPECT_EQ(out[1][3], 0xCCCC0000u);
1562 }
1563
TEST(ReactorUnitTests,VectorCompare)1564 TEST(ReactorUnitTests, VectorCompare)
1565 {
1566 FunctionT<int(void *)> function;
1567 {
1568 Pointer<Byte> out = function.Arg<0>();
1569
1570 *Pointer<Int4>(out + 16 * 0) = CmpEQ(Float4(1.0f, 1.0f, -0.0f, +0.0f), Float4(0.0f, 1.0f, +0.0f, -0.0f));
1571 *Pointer<Int4>(out + 16 * 1) = CmpEQ(Int4(1, 0, -1, -0), Int4(0, 1, 0, +0));
1572 *Pointer<Byte8>(out + 16 * 2) = CmpEQ(SByte8(1, 2, 3, 4, 5, 6, 7, 8), SByte8(7, 6, 5, 4, 3, 2, 1, 0));
1573
1574 *Pointer<Int4>(out + 16 * 3) = CmpNLT(Float4(1.0f, 1.0f, -0.0f, +0.0f), Float4(0.0f, 1.0f, +0.0f, -0.0f));
1575 *Pointer<Int4>(out + 16 * 4) = CmpNLT(Int4(1, 0, -1, -0), Int4(0, 1, 0, +0));
1576 *Pointer<Byte8>(out + 16 * 5) = CmpGT(SByte8(1, 2, 3, 4, 5, 6, 7, 8), SByte8(7, 6, 5, 4, 3, 2, 1, 0));
1577
1578 Return(0);
1579 }
1580
1581 auto routine = function(testName().c_str());
1582
1583 unsigned int out[6][4];
1584
1585 memset(&out, 0, sizeof(out));
1586
1587 routine(&out);
1588
1589 EXPECT_EQ(out[0][0], 0x00000000u);
1590 EXPECT_EQ(out[0][1], 0xFFFFFFFFu);
1591 EXPECT_EQ(out[0][2], 0xFFFFFFFFu);
1592 EXPECT_EQ(out[0][3], 0xFFFFFFFFu);
1593
1594 EXPECT_EQ(out[1][0], 0x00000000u);
1595 EXPECT_EQ(out[1][1], 0x00000000u);
1596 EXPECT_EQ(out[1][2], 0x00000000u);
1597 EXPECT_EQ(out[1][3], 0xFFFFFFFFu);
1598
1599 EXPECT_EQ(out[2][0], 0xFF000000u);
1600 EXPECT_EQ(out[2][1], 0x00000000u);
1601
1602 EXPECT_EQ(out[3][0], 0xFFFFFFFFu);
1603 EXPECT_EQ(out[3][1], 0xFFFFFFFFu);
1604 EXPECT_EQ(out[3][2], 0xFFFFFFFFu);
1605 EXPECT_EQ(out[3][3], 0xFFFFFFFFu);
1606
1607 EXPECT_EQ(out[4][0], 0xFFFFFFFFu);
1608 EXPECT_EQ(out[4][1], 0x00000000u);
1609 EXPECT_EQ(out[4][2], 0x00000000u);
1610 EXPECT_EQ(out[4][3], 0xFFFFFFFFu);
1611
1612 EXPECT_EQ(out[5][0], 0x00000000u);
1613 EXPECT_EQ(out[5][1], 0xFFFFFFFFu);
1614 }
1615
TEST(ReactorUnitTests,SaturatedAddAndSubtract)1616 TEST(ReactorUnitTests, SaturatedAddAndSubtract)
1617 {
1618 FunctionT<int(void *)> function;
1619 {
1620 Pointer<Byte> out = function.Arg<0>();
1621
1622 *Pointer<Byte8>(out + 8 * 0) =
1623 AddSat(Byte8(1, 2, 3, 4, 5, 6, 7, 8),
1624 Byte8(7, 6, 5, 4, 3, 2, 1, 0));
1625 *Pointer<Byte8>(out + 8 * 1) =
1626 AddSat(Byte8(0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE),
1627 Byte8(7, 6, 5, 4, 3, 2, 1, 0));
1628 *Pointer<Byte8>(out + 8 * 2) =
1629 SubSat(Byte8(1, 2, 3, 4, 5, 6, 7, 8),
1630 Byte8(7, 6, 5, 4, 3, 2, 1, 0));
1631
1632 *Pointer<SByte8>(out + 8 * 3) =
1633 AddSat(SByte8(1, 2, 3, 4, 5, 6, 7, 8),
1634 SByte8(7, 6, 5, 4, 3, 2, 1, 0));
1635 *Pointer<SByte8>(out + 8 * 4) =
1636 AddSat(SByte8(0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E),
1637 SByte8(7, 6, 5, 4, 3, 2, 1, 0));
1638 *Pointer<SByte8>(out + 8 * 5) =
1639 AddSat(SByte8(0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88),
1640 SByte8(-7, -6, -5, -4, -3, -2, -1, -0));
1641 *Pointer<SByte8>(out + 8 * 6) =
1642 SubSat(SByte8(0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88),
1643 SByte8(7, 6, 5, 4, 3, 2, 1, 0));
1644
1645 *Pointer<Short4>(out + 8 * 7) =
1646 AddSat(Short4(1, 2, 3, 4), Short4(3, 2, 1, 0));
1647 *Pointer<Short4>(out + 8 * 8) =
1648 AddSat(Short4(0x7FFE, 0x7FFE, 0x7FFE, 0x7FFE),
1649 Short4(3, 2, 1, 0));
1650 *Pointer<Short4>(out + 8 * 9) =
1651 AddSat(Short4(0x8001, 0x8002, 0x8003, 0x8004),
1652 Short4(-3, -2, -1, -0));
1653 *Pointer<Short4>(out + 8 * 10) =
1654 SubSat(Short4(0x8001, 0x8002, 0x8003, 0x8004),
1655 Short4(3, 2, 1, 0));
1656
1657 *Pointer<UShort4>(out + 8 * 11) =
1658 AddSat(UShort4(1, 2, 3, 4), UShort4(3, 2, 1, 0));
1659 *Pointer<UShort4>(out + 8 * 12) =
1660 AddSat(UShort4(0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE),
1661 UShort4(3, 2, 1, 0));
1662 *Pointer<UShort4>(out + 8 * 13) =
1663 SubSat(UShort4(1, 2, 3, 4), UShort4(3, 2, 1, 0));
1664
1665 Return(0);
1666 }
1667
1668 auto routine = function(testName().c_str());
1669
1670 unsigned int out[14][2];
1671
1672 memset(&out, 0, sizeof(out));
1673
1674 routine(&out);
1675
1676 EXPECT_EQ(out[0][0], 0x08080808u);
1677 EXPECT_EQ(out[0][1], 0x08080808u);
1678
1679 EXPECT_EQ(out[1][0], 0xFFFFFFFFu);
1680 EXPECT_EQ(out[1][1], 0xFEFFFFFFu);
1681
1682 EXPECT_EQ(out[2][0], 0x00000000u);
1683 EXPECT_EQ(out[2][1], 0x08060402u);
1684
1685 EXPECT_EQ(out[3][0], 0x08080808u);
1686 EXPECT_EQ(out[3][1], 0x08080808u);
1687
1688 EXPECT_EQ(out[4][0], 0x7F7F7F7Fu);
1689 EXPECT_EQ(out[4][1], 0x7E7F7F7Fu);
1690
1691 EXPECT_EQ(out[5][0], 0x80808080u);
1692 EXPECT_EQ(out[5][1], 0x88868482u);
1693
1694 EXPECT_EQ(out[6][0], 0x80808080u);
1695 EXPECT_EQ(out[6][1], 0x88868482u);
1696
1697 EXPECT_EQ(out[7][0], 0x00040004u);
1698 EXPECT_EQ(out[7][1], 0x00040004u);
1699
1700 EXPECT_EQ(out[8][0], 0x7FFF7FFFu);
1701 EXPECT_EQ(out[8][1], 0x7FFE7FFFu);
1702
1703 EXPECT_EQ(out[9][0], 0x80008000u);
1704 EXPECT_EQ(out[9][1], 0x80048002u);
1705
1706 EXPECT_EQ(out[10][0], 0x80008000u);
1707 EXPECT_EQ(out[10][1], 0x80048002u);
1708
1709 EXPECT_EQ(out[11][0], 0x00040004u);
1710 EXPECT_EQ(out[11][1], 0x00040004u);
1711
1712 EXPECT_EQ(out[12][0], 0xFFFFFFFFu);
1713 EXPECT_EQ(out[12][1], 0xFFFEFFFFu);
1714
1715 EXPECT_EQ(out[13][0], 0x00000000u);
1716 EXPECT_EQ(out[13][1], 0x00040002u);
1717 }
1718
TEST(ReactorUnitTests,Unpack)1719 TEST(ReactorUnitTests, Unpack)
1720 {
1721 FunctionT<int(void *, void *)> function;
1722 {
1723 Pointer<Byte> in = function.Arg<0>();
1724 Pointer<Byte> out = function.Arg<1>();
1725
1726 Byte4 test_byte_a = *Pointer<Byte4>(in + 4 * 0);
1727 Byte4 test_byte_b = *Pointer<Byte4>(in + 4 * 1);
1728
1729 *Pointer<Short4>(out + 8 * 0) =
1730 Unpack(test_byte_a, test_byte_b);
1731
1732 *Pointer<Short4>(out + 8 * 1) = Unpack(test_byte_a);
1733
1734 Return(0);
1735 }
1736
1737 auto routine = function(testName().c_str());
1738
1739 unsigned int in[1][2];
1740 unsigned int out[2][2];
1741
1742 memset(&out, 0, sizeof(out));
1743
1744 in[0][0] = 0xABCDEF12u;
1745 in[0][1] = 0x34567890u;
1746
1747 routine(&in, &out);
1748
1749 EXPECT_EQ(out[0][0], 0x78EF9012u);
1750 EXPECT_EQ(out[0][1], 0x34AB56CDu);
1751
1752 EXPECT_EQ(out[1][0], 0xEFEF1212u);
1753 EXPECT_EQ(out[1][1], 0xABABCDCDu);
1754 }
1755
TEST(ReactorUnitTests,Pack)1756 TEST(ReactorUnitTests, Pack)
1757 {
1758 FunctionT<int(void *)> function;
1759 {
1760 Pointer<Byte> out = function.Arg<0>();
1761
1762 *Pointer<SByte8>(out + 8 * 0) =
1763 PackSigned(Short4(-1, -2, 1, 2),
1764 Short4(3, 4, -3, -4));
1765
1766 *Pointer<Byte8>(out + 8 * 1) =
1767 PackUnsigned(Short4(-1, -2, 1, 2),
1768 Short4(3, 4, -3, -4));
1769
1770 *Pointer<Short8>(out + 8 * 2) =
1771 PackSigned(Int4(-1, -2, 1, 2),
1772 Int4(3, 4, -3, -4));
1773
1774 *Pointer<UShort8>(out + 8 * 4) =
1775 PackUnsigned(Int4(-1, -2, 1, 2),
1776 Int4(3, 4, -3, -4));
1777
1778 Return(0);
1779 }
1780
1781 auto routine = function(testName().c_str());
1782
1783 unsigned int out[6][2];
1784
1785 memset(&out, 0, sizeof(out));
1786
1787 routine(&out);
1788
1789 EXPECT_EQ(out[0][0], 0x0201FEFFu);
1790 EXPECT_EQ(out[0][1], 0xFCFD0403u);
1791
1792 EXPECT_EQ(out[1][0], 0x02010000u);
1793 EXPECT_EQ(out[1][1], 0x00000403u);
1794
1795 EXPECT_EQ(out[2][0], 0xFFFEFFFFu);
1796 EXPECT_EQ(out[2][1], 0x00020001u);
1797
1798 EXPECT_EQ(out[3][0], 0x00040003u);
1799 EXPECT_EQ(out[3][1], 0xFFFCFFFDu);
1800
1801 EXPECT_EQ(out[4][0], 0x00000000u);
1802 EXPECT_EQ(out[4][1], 0x00020001u);
1803
1804 EXPECT_EQ(out[5][0], 0x00040003u);
1805 EXPECT_EQ(out[5][1], 0x00000000u);
1806 }
1807
TEST(ReactorUnitTests,MulHigh)1808 TEST(ReactorUnitTests, MulHigh)
1809 {
1810 FunctionT<int(void *)> function;
1811 {
1812 Pointer<Byte> out = function.Arg<0>();
1813
1814 *Pointer<Short4>(out + 16 * 0) =
1815 MulHigh(Short4(0x01AA, 0x02DD, 0x03EE, 0xF422),
1816 Short4(0x01BB, 0x02CC, 0x03FF, 0xF411));
1817 *Pointer<UShort4>(out + 16 * 1) =
1818 MulHigh(UShort4(0x01AA, 0x02DD, 0x03EE, 0xF422),
1819 UShort4(0x01BB, 0x02CC, 0x03FF, 0xF411));
1820
1821 *Pointer<Int4>(out + 16 * 2) =
1822 MulHigh(Int4(0x000001AA, 0x000002DD, 0xC8000000, 0xF8000000),
1823 Int4(0x000001BB, 0x84000000, 0x000003EE, 0xD7000000));
1824 *Pointer<UInt4>(out + 16 * 3) =
1825 MulHigh(UInt4(0x000001AAu, 0x000002DDu, 0xC8000000u, 0xD8000000u),
1826 UInt4(0x000001BBu, 0x84000000u, 0x000003EEu, 0xD7000000u));
1827
1828 *Pointer<Int4>(out + 16 * 4) =
1829 MulHigh(Int4(0x7FFFFFFF, 0x7FFFFFFF, 0x80008000, 0xFFFFFFFF),
1830 Int4(0x7FFFFFFF, 0x80000000, 0x80008000, 0xFFFFFFFF));
1831 *Pointer<UInt4>(out + 16 * 5) =
1832 MulHigh(UInt4(0x7FFFFFFFu, 0x7FFFFFFFu, 0x80008000u, 0xFFFFFFFFu),
1833 UInt4(0x7FFFFFFFu, 0x80000000u, 0x80008000u, 0xFFFFFFFFu));
1834
1835 // (U)Short8 variants currently unimplemented.
1836
1837 Return(0);
1838 }
1839
1840 auto routine = function(testName().c_str());
1841
1842 unsigned int out[6][4];
1843
1844 memset(&out, 0, sizeof(out));
1845
1846 routine(&out);
1847
1848 EXPECT_EQ(out[0][0], 0x00080002u);
1849 EXPECT_EQ(out[0][1], 0x008D000Fu);
1850
1851 EXPECT_EQ(out[1][0], 0x00080002u);
1852 EXPECT_EQ(out[1][1], 0xE8C0000Fu);
1853
1854 EXPECT_EQ(out[2][0], 0x00000000u);
1855 EXPECT_EQ(out[2][1], 0xFFFFFE9Cu);
1856 EXPECT_EQ(out[2][2], 0xFFFFFF23u);
1857 EXPECT_EQ(out[2][3], 0x01480000u);
1858
1859 EXPECT_EQ(out[3][0], 0x00000000u);
1860 EXPECT_EQ(out[3][1], 0x00000179u);
1861 EXPECT_EQ(out[3][2], 0x00000311u);
1862 EXPECT_EQ(out[3][3], 0xB5680000u);
1863
1864 EXPECT_EQ(out[4][0], 0x3FFFFFFFu);
1865 EXPECT_EQ(out[4][1], 0xC0000000u);
1866 EXPECT_EQ(out[4][2], 0x3FFF8000u);
1867 EXPECT_EQ(out[4][3], 0x00000000u);
1868
1869 EXPECT_EQ(out[5][0], 0x3FFFFFFFu);
1870 EXPECT_EQ(out[5][1], 0x3FFFFFFFu);
1871 EXPECT_EQ(out[5][2], 0x40008000u);
1872 EXPECT_EQ(out[5][3], 0xFFFFFFFEu);
1873 }
1874
TEST(ReactorUnitTests,MulAdd)1875 TEST(ReactorUnitTests, MulAdd)
1876 {
1877 FunctionT<int(void *)> function;
1878 {
1879 Pointer<Byte> out = function.Arg<0>();
1880
1881 *Pointer<Int2>(out + 8 * 0) =
1882 MulAdd(Short4(0x1aa, 0x2dd, 0x3ee, 0xF422),
1883 Short4(0x1bb, 0x2cc, 0x3ff, 0xF411));
1884
1885 // (U)Short8 variant is mentioned but unimplemented
1886 Return(0);
1887 }
1888
1889 auto routine = function(testName().c_str());
1890
1891 unsigned int out[1][2];
1892
1893 memset(&out, 0, sizeof(out));
1894
1895 routine(&out);
1896
1897 EXPECT_EQ(out[0][0], 0x000AE34Au);
1898 EXPECT_EQ(out[0][1], 0x009D5254u);
1899 }
1900
TEST(ReactorUnitTests,PointersEqual)1901 TEST(ReactorUnitTests, PointersEqual)
1902 {
1903 FunctionT<int(void *, void *)> function;
1904 {
1905 Pointer<Byte> ptrA = function.Arg<0>();
1906 Pointer<Byte> ptrB = function.Arg<1>();
1907 If(ptrA == ptrB)
1908 {
1909 Return(1);
1910 }
1911 Else
1912 {
1913 Return(0);
1914 }
1915 }
1916
1917 auto routine = function(testName().c_str());
1918 int *a = reinterpret_cast<int *>(uintptr_t(0x0000000000000000));
1919 int *b = reinterpret_cast<int *>(uintptr_t(0x00000000F0000000));
1920 int *c = reinterpret_cast<int *>(uintptr_t(0xF000000000000000));
1921 EXPECT_EQ(routine(&a, &a), 1);
1922 EXPECT_EQ(routine(&b, &b), 1);
1923 EXPECT_EQ(routine(&c, &c), 1);
1924
1925 EXPECT_EQ(routine(&a, &b), 0);
1926 EXPECT_EQ(routine(&b, &a), 0);
1927 EXPECT_EQ(routine(&b, &c), 0);
1928 EXPECT_EQ(routine(&c, &b), 0);
1929 EXPECT_EQ(routine(&c, &a), 0);
1930 EXPECT_EQ(routine(&a, &c), 0);
1931 }
1932
TEST(ReactorUnitTests,Args_2Mixed)1933 TEST(ReactorUnitTests, Args_2Mixed)
1934 {
1935 // 2 mixed type args
1936 FunctionT<float(int, float)> function;
1937 {
1938 Int a = function.Arg<0>();
1939 Float b = function.Arg<1>();
1940 Return(Float(a) + b);
1941 }
1942
1943 if(auto routine = function(testName().c_str()))
1944 {
1945 float result = routine(1, 2.f);
1946 EXPECT_EQ(result, 3.f);
1947 }
1948 }
1949
TEST(ReactorUnitTests,Args_4Mixed)1950 TEST(ReactorUnitTests, Args_4Mixed)
1951 {
1952 // 4 mixed type args (max register allocation on Windows)
1953 FunctionT<float(int, float, int, float)> function;
1954 {
1955 Int a = function.Arg<0>();
1956 Float b = function.Arg<1>();
1957 Int c = function.Arg<2>();
1958 Float d = function.Arg<3>();
1959 Return(Float(a) + b + Float(c) + d);
1960 }
1961
1962 if(auto routine = function(testName().c_str()))
1963 {
1964 float result = routine(1, 2.f, 3, 4.f);
1965 EXPECT_EQ(result, 10.f);
1966 }
1967 }
1968
TEST(ReactorUnitTests,Args_5Mixed)1969 TEST(ReactorUnitTests, Args_5Mixed)
1970 {
1971 // 5 mixed type args (5th spills over to stack on Windows)
1972 FunctionT<float(int, float, int, float, int)> function;
1973 {
1974 Int a = function.Arg<0>();
1975 Float b = function.Arg<1>();
1976 Int c = function.Arg<2>();
1977 Float d = function.Arg<3>();
1978 Int e = function.Arg<4>();
1979 Return(Float(a) + b + Float(c) + d + Float(e));
1980 }
1981
1982 if(auto routine = function(testName().c_str()))
1983 {
1984 float result = routine(1, 2.f, 3, 4.f, 5);
1985 EXPECT_EQ(result, 15.f);
1986 }
1987 }
1988
TEST(ReactorUnitTests,Args_GreaterThan5Mixed)1989 TEST(ReactorUnitTests, Args_GreaterThan5Mixed)
1990 {
1991 // >5 mixed type args
1992 FunctionT<float(int, float, int, float, int, float, int, float, int, float)> function;
1993 {
1994 Int a = function.Arg<0>();
1995 Float b = function.Arg<1>();
1996 Int c = function.Arg<2>();
1997 Float d = function.Arg<3>();
1998 Int e = function.Arg<4>();
1999 Float f = function.Arg<5>();
2000 Int g = function.Arg<6>();
2001 Float h = function.Arg<7>();
2002 Int i = function.Arg<8>();
2003 Float j = function.Arg<9>();
2004 Return(Float(a) + b + Float(c) + d + Float(e) + f + Float(g) + h + Float(i) + j);
2005 }
2006
2007 if(auto routine = function(testName().c_str()))
2008 {
2009 float result = routine(1, 2.f, 3, 4.f, 5, 6.f, 7, 8.f, 9, 10.f);
2010 EXPECT_EQ(result, 55.f);
2011 }
2012 }
2013
2014 // This test was written because on Windows with Subzero, we would get a crash when executing a function
2015 // with a large number of local variables. The problem was that on Windows, 4K pages are allocated as
2016 // needed for the stack whenever an access is made in a "guard page", at which point the page is committed,
2017 // and the next 4K page becomes the guard page. If a stack access is made that's beyond the guard page,
2018 // a regular page fault occurs. To fix this, Subzero (and any compiler) now emits a call to __chkstk with
2019 // the stack size in EAX, so that it can probe the stack in 4K increments up to that size, committing the
2020 // required pages. See https://docs.microsoft.com/en-us/windows/win32/devnotes/-win32-chkstk.
TEST(ReactorUnitTests,LargeStack)2021 TEST(ReactorUnitTests, LargeStack)
2022 {
2023 // An empirically large enough value to access outside the guard pages
2024 constexpr int ArrayByteSize = 24 * 1024;
2025 constexpr int ArraySize = ArrayByteSize / sizeof(int32_t);
2026
2027 FunctionT<void(int32_t * v)> function;
2028 {
2029 // Allocate a stack array large enough that writing to the first element will reach beyond
2030 // the guard page.
2031 Array<Int, ArraySize> largeStackArray;
2032 for(int i = 0; i < ArraySize; ++i)
2033 {
2034 largeStackArray[i] = i;
2035 }
2036
2037 Pointer<Int> in = function.Arg<0>();
2038 for(int i = 0; i < ArraySize; ++i)
2039 {
2040 in[i] = largeStackArray[i];
2041 }
2042 }
2043
2044 // LLVM takes very long to generate this routine when O2 optimizations are enabled. Disable for now.
2045 // TODO(b/174031014): Remove this once we fix LLVM taking so long.
2046 ScopedPragma O0(OptimizationLevel, 0);
2047
2048 auto routine = function(testName().c_str());
2049
2050 std::array<int32_t, ArraySize> v;
2051
2052 // Run this in a thread, so that we get the default reserved stack size (8K on Win64).
2053 std::thread t([&] {
2054 routine(v.data());
2055 });
2056 t.join();
2057
2058 for(int i = 0; i < ArraySize; ++i)
2059 {
2060 EXPECT_EQ(v[i], i);
2061 }
2062 }
2063
TEST(ReactorUnitTests,ShlSmallRHSScalar)2064 TEST(ReactorUnitTests, ShlSmallRHSScalar)
2065 {
2066 // TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
2067 if(Caps::backendName().find("LLVM") == std::string::npos) return;
2068
2069 FunctionT<unsigned()> function;
2070 {
2071 auto lhs = UInt(4);
2072 auto rhs = UInt(8);
2073 auto res = lhs << rhs;
2074 Return(res);
2075 }
2076
2077 auto routine = function(testName().c_str());
2078
2079 unsigned res = routine();
2080 EXPECT_EQ(res, 1u << 10u);
2081 }
2082
TEST(ReactorUnitTests,ShlLargeRHSScalar)2083 TEST(ReactorUnitTests, ShlLargeRHSScalar)
2084 {
2085 // TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
2086 if(Caps::backendName().find("LLVM") == std::string::npos) return;
2087
2088 FunctionT<unsigned()> function;
2089 {
2090 auto lhs = UInt(1);
2091 auto rhs = UInt(99);
2092 auto res = lhs << rhs;
2093 Return(res);
2094 }
2095
2096 auto routine = function(testName().c_str());
2097
2098 unsigned res = routine();
2099 EXPECT_EQ(res, 1u << 31u);
2100 }
2101
TEST(ReactorUnitTests,ShrSmallRHSScalar)2102 TEST(ReactorUnitTests, ShrSmallRHSScalar)
2103 {
2104 // TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
2105 if(Caps::backendName().find("LLVM") == std::string::npos) return;
2106
2107 FunctionT<unsigned()> function;
2108 {
2109 auto lhs = UInt(64);
2110 auto rhs = UInt(4);
2111 auto res = lhs >> rhs;
2112 Return(res);
2113 }
2114
2115 auto routine = function(testName().c_str());
2116
2117 unsigned res = routine();
2118 EXPECT_EQ(res, 4u);
2119 }
2120
TEST(ReactorUnitTests,ShrLargeRHSScalar)2121 TEST(ReactorUnitTests, ShrLargeRHSScalar)
2122 {
2123 // TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
2124 if(Caps::backendName().find("LLVM") == std::string::npos) return;
2125
2126 FunctionT<unsigned()> function;
2127 {
2128 auto lhs = UInt(4);
2129 auto rhs = UInt(99);
2130 auto res = lhs >> rhs;
2131 Return(res);
2132 }
2133
2134 auto routine = function(testName().c_str());
2135
2136 unsigned res = routine();
2137 EXPECT_EQ(res, 0u);
2138 }
2139
TEST(ReactorUnitTests,ShlRHSVector)2140 TEST(ReactorUnitTests, ShlRHSVector)
2141 {
2142 // TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
2143 if(Caps::backendName().find("LLVM") == std::string::npos) return;
2144
2145 FunctionT<void(unsigned *a, unsigned *b, unsigned *c, unsigned *d)> function;
2146 {
2147 Pointer<UInt> a = function.Arg<0>();
2148 Pointer<UInt> b = function.Arg<1>();
2149 Pointer<UInt> c = function.Arg<2>();
2150 Pointer<UInt> d = function.Arg<3>();
2151
2152 auto lhs = UInt4(4, 3, 6, 5);
2153 auto rhs = UInt4(8, 99, 2, 50);
2154 UInt4 res = lhs << rhs;
2155 *a = res.x;
2156 *b = res.y;
2157 *c = res.z;
2158 *d = res.w;
2159 }
2160
2161 auto routine = function(testName().c_str());
2162
2163 unsigned a = 0;
2164 unsigned b = 0;
2165 unsigned c = 0;
2166 unsigned d = 0;
2167 routine(&a, &b, &c, &d);
2168 EXPECT_EQ(a, 1024u);
2169 EXPECT_EQ(b, 0x80000000u);
2170 EXPECT_EQ(c, 24u);
2171 EXPECT_EQ(d, 0x80000000u);
2172 }
2173
TEST(ReactorUnitTests,ShrRHSVector)2174 TEST(ReactorUnitTests, ShrRHSVector)
2175 {
2176 // TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
2177 if(Caps::backendName().find("LLVM") == std::string::npos) return;
2178
2179 FunctionT<void(unsigned *a, unsigned *b, unsigned *c, unsigned *d)> function;
2180 {
2181 Pointer<UInt> a = function.Arg<0>();
2182 Pointer<UInt> b = function.Arg<1>();
2183 Pointer<UInt> c = function.Arg<2>();
2184 Pointer<UInt> d = function.Arg<3>();
2185
2186 auto lhs = UInt4(745, 23, 234, 54);
2187 auto rhs = UInt4(8, 99, 2, 50);
2188 UInt4 res = lhs >> rhs;
2189 *a = res.x;
2190 *b = res.y;
2191 *c = res.z;
2192 *d = res.w;
2193 }
2194
2195 auto routine = function(testName().c_str());
2196
2197 unsigned a = 0;
2198 unsigned b = 0;
2199 unsigned c = 0;
2200 unsigned d = 0;
2201 routine(&a, &b, &c, &d);
2202 EXPECT_EQ(a, 2u);
2203 EXPECT_EQ(b, 0u);
2204 EXPECT_EQ(c, 58u);
2205 EXPECT_EQ(d, 0u);
2206 }
2207
TEST(ReactorUnitTests,ShrLargeRHSVector)2208 TEST(ReactorUnitTests, ShrLargeRHSVector)
2209 {
2210 // TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
2211 if(Caps::backendName().find("LLVM") == std::string::npos) return;
2212
2213 FunctionT<unsigned()> function;
2214 {
2215 auto lhs = UInt(4);
2216 auto rhs = UInt(99);
2217 auto res = lhs >> rhs;
2218 Return(res);
2219 }
2220
2221 auto routine = function(testName().c_str());
2222
2223 unsigned res = routine();
2224 EXPECT_EQ(res, 0u);
2225 }
2226
TEST(ReactorUnitTests,Call)2227 TEST(ReactorUnitTests, Call)
2228 {
2229 struct Class
2230 {
2231 static int Callback(Class *p, int i, float f)
2232 {
2233 p->i = i;
2234 p->f = f;
2235 return i + int(f);
2236 }
2237
2238 int i = 0;
2239 float f = 0.0f;
2240 };
2241
2242 FunctionT<int(void *)> function;
2243 {
2244 Pointer<Byte> c = function.Arg<0>();
2245 auto res = Call(Class::Callback, c, 10, 20.0f);
2246 Return(res);
2247 }
2248
2249 auto routine = function(testName().c_str());
2250
2251 Class c;
2252 int res = routine(&c);
2253 EXPECT_EQ(res, 30);
2254 EXPECT_EQ(c.i, 10);
2255 EXPECT_EQ(c.f, 20.0f);
2256 }
2257
TEST(ReactorUnitTests,CallMemberFunction)2258 TEST(ReactorUnitTests, CallMemberFunction)
2259 {
2260 struct Class
2261 {
2262 int Callback(int argI, float argF)
2263 {
2264 i = argI;
2265 f = argF;
2266 return i + int(f);
2267 }
2268
2269 int i = 0;
2270 float f = 0.0f;
2271 };
2272
2273 Class c;
2274
2275 FunctionT<int()> function;
2276 {
2277 auto res = Call(&Class::Callback, &c, 10, 20.0f);
2278 Return(res);
2279 }
2280
2281 auto routine = function(testName().c_str());
2282
2283 int res = routine();
2284 EXPECT_EQ(res, 30);
2285 EXPECT_EQ(c.i, 10);
2286 EXPECT_EQ(c.f, 20.0f);
2287 }
2288
TEST(ReactorUnitTests,CallMemberFunctionIndirect)2289 TEST(ReactorUnitTests, CallMemberFunctionIndirect)
2290 {
2291 struct Class
2292 {
2293 int Callback(int argI, float argF)
2294 {
2295 i = argI;
2296 f = argF;
2297 return i + int(f);
2298 }
2299
2300 int i = 0;
2301 float f = 0.0f;
2302 };
2303
2304 FunctionT<int(void *)> function;
2305 {
2306 Pointer<Byte> c = function.Arg<0>();
2307 auto res = Call(&Class::Callback, c, 10, 20.0f);
2308 Return(res);
2309 }
2310
2311 auto routine = function(testName().c_str());
2312
2313 Class c;
2314 int res = routine(&c);
2315 EXPECT_EQ(res, 30);
2316 EXPECT_EQ(c.i, 10);
2317 EXPECT_EQ(c.f, 20.0f);
2318 }
2319
TEST(ReactorUnitTests,CallImplicitCast)2320 TEST(ReactorUnitTests, CallImplicitCast)
2321 {
2322 struct Class
2323 {
2324 static void Callback(Class *c, const char *s)
2325 {
2326 c->str = s;
2327 }
2328 std::string str;
2329 };
2330
2331 FunctionT<void(Class * c, const char *s)> function;
2332 {
2333 Pointer<Byte> c = function.Arg<0>();
2334 Pointer<Byte> s = function.Arg<1>();
2335 Call(Class::Callback, c, s);
2336 }
2337
2338 auto routine = function(testName().c_str());
2339
2340 Class c;
2341 routine(&c, "hello world");
2342 EXPECT_EQ(c.str, "hello world");
2343 }
2344
TEST(ReactorUnitTests,CallBoolReturnFunction)2345 TEST(ReactorUnitTests, CallBoolReturnFunction)
2346 {
2347 struct Class
2348 {
2349 static bool IsEven(int a)
2350 {
2351 return a % 2 == 0;
2352 }
2353 };
2354
2355 FunctionT<int(int)> function;
2356 {
2357 Int a = function.Arg<0>();
2358 Bool res = Call(Class::IsEven, a);
2359 If(res)
2360 {
2361 Return(1);
2362 }
2363 Return(0);
2364 }
2365
2366 auto routine = function(testName().c_str());
2367
2368 for(int i = 0; i < 10; ++i)
2369 {
2370 EXPECT_EQ(routine(i), i % 2 == 0);
2371 }
2372 }
2373
TEST(ReactorUnitTests,Call_Args4)2374 TEST(ReactorUnitTests, Call_Args4)
2375 {
2376 struct Class
2377 {
2378 static int Func(int a, int b, int c, int d)
2379 {
2380 return a + b + c + d;
2381 }
2382 };
2383
2384 {
2385 FunctionT<int()> function;
2386 {
2387 auto res = Call(Class::Func, 1, 2, 3, 4);
2388 Return(res);
2389 }
2390
2391 auto routine = function(testName().c_str());
2392
2393 int res = routine();
2394 EXPECT_EQ(res, 1 + 2 + 3 + 4);
2395 }
2396 }
2397
TEST(ReactorUnitTests,Call_Args5)2398 TEST(ReactorUnitTests, Call_Args5)
2399 {
2400 struct Class
2401 {
2402 static int Func(int a, int b, int c, int d, int e)
2403 {
2404 return a + b + c + d + e;
2405 }
2406 };
2407
2408 {
2409 FunctionT<int()> function;
2410 {
2411 auto res = Call(Class::Func, 1, 2, 3, 4, 5);
2412 Return(res);
2413 }
2414
2415 auto routine = function(testName().c_str());
2416
2417 int res = routine();
2418 EXPECT_EQ(res, 1 + 2 + 3 + 4 + 5);
2419 }
2420 }
2421
TEST(ReactorUnitTests,Call_ArgsMany)2422 TEST(ReactorUnitTests, Call_ArgsMany)
2423 {
2424 struct Class
2425 {
2426 static int Func(int a, int b, int c, int d, int e, int f, int g, int h)
2427 {
2428 return a + b + c + d + e + f + g + h;
2429 }
2430 };
2431
2432 {
2433 FunctionT<int()> function;
2434 {
2435 auto res = Call(Class::Func, 1, 2, 3, 4, 5, 6, 7, 8);
2436 Return(res);
2437 }
2438
2439 auto routine = function(testName().c_str());
2440
2441 int res = routine();
2442 EXPECT_EQ(res, 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8);
2443 }
2444 }
2445
TEST(ReactorUnitTests,Call_ArgsMixed)2446 TEST(ReactorUnitTests, Call_ArgsMixed)
2447 {
2448 struct Class
2449 {
2450 static int Func(int a, float b, int *c, float *d, int e, float f, int *g, float *h)
2451 {
2452 return a + b + *c + *d + e + f + *g + *h;
2453 }
2454 };
2455
2456 {
2457 FunctionT<int()> function;
2458 {
2459 Int c(3);
2460 Float d(4);
2461 Int g(7);
2462 Float h(8);
2463 auto res = Call(Class::Func, 1, 2.f, &c, &d, 5, 6.f, &g, &h);
2464 Return(res);
2465 }
2466
2467 auto routine = function(testName().c_str());
2468
2469 int res = routine();
2470 EXPECT_EQ(res, 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8);
2471 }
2472 }
2473
TEST(ReactorUnitTests,Call_ArgsPointer)2474 TEST(ReactorUnitTests, Call_ArgsPointer)
2475 {
2476 struct Class
2477 {
2478 static int Func(int *a)
2479 {
2480 return *a;
2481 }
2482 };
2483
2484 {
2485 FunctionT<int()> function;
2486 {
2487 Int a(12345);
2488 auto res = Call(Class::Func, &a);
2489 Return(res);
2490 }
2491
2492 auto routine = function(testName().c_str());
2493
2494 int res = routine();
2495 EXPECT_EQ(res, 12345);
2496 }
2497 }
2498
TEST(ReactorUnitTests,CallExternalCallRoutine)2499 TEST(ReactorUnitTests, CallExternalCallRoutine)
2500 {
2501 // routine1 calls Class::Func, passing it a pointer to routine2, and Class::Func calls routine2
2502
2503 auto routine2 = [] {
2504 FunctionT<float(float, int)> function;
2505 {
2506 Float a = function.Arg<0>();
2507 Int b = function.Arg<1>();
2508 Return(a + Float(b));
2509 }
2510 return function("%s2", testName().c_str());
2511 }();
2512
2513 struct Class
2514 {
2515 static float Func(void *p, float a, int b)
2516 {
2517 auto funcToCall = reinterpret_cast<float (*)(float, int)>(p);
2518 return funcToCall(a, b);
2519 }
2520 };
2521
2522 auto routine1 = [] {
2523 FunctionT<float(void *, float, int)> function;
2524 {
2525 Pointer<Byte> funcToCall = function.Arg<0>();
2526 Float a = function.Arg<1>();
2527 Int b = function.Arg<2>();
2528 Float result = Call(Class::Func, funcToCall, a, b);
2529 Return(result);
2530 }
2531 return function(testName().c_str());
2532 }();
2533
2534 float result = routine1((void *)routine2.getEntry(), 12.f, 13);
2535 EXPECT_EQ(result, 25.f);
2536 }
2537
2538 // Check that a complex generated function which utilizes all 8 or 16 XMM
2539 // registers computes the correct result.
2540 // (Note that due to MSC's lack of support for inline assembly in x64,
2541 // this test does not actually check that the register contents are
2542 // preserved, just that the generated function computes the correct value.
2543 // It's necessary to inspect the registers in a debugger to actually verify.)
TEST(ReactorUnitTests,PreserveXMMRegisters)2544 TEST(ReactorUnitTests, PreserveXMMRegisters)
2545 {
2546 FunctionT<void(void *, void *)> function;
2547 {
2548 Pointer<Byte> in = function.Arg<0>();
2549 Pointer<Byte> out = function.Arg<1>();
2550
2551 Float4 a = *Pointer<Float4>(in + 16 * 0);
2552 Float4 b = *Pointer<Float4>(in + 16 * 1);
2553 Float4 c = *Pointer<Float4>(in + 16 * 2);
2554 Float4 d = *Pointer<Float4>(in + 16 * 3);
2555 Float4 e = *Pointer<Float4>(in + 16 * 4);
2556 Float4 f = *Pointer<Float4>(in + 16 * 5);
2557 Float4 g = *Pointer<Float4>(in + 16 * 6);
2558 Float4 h = *Pointer<Float4>(in + 16 * 7);
2559 Float4 i = *Pointer<Float4>(in + 16 * 8);
2560 Float4 j = *Pointer<Float4>(in + 16 * 9);
2561 Float4 k = *Pointer<Float4>(in + 16 * 10);
2562 Float4 l = *Pointer<Float4>(in + 16 * 11);
2563 Float4 m = *Pointer<Float4>(in + 16 * 12);
2564 Float4 n = *Pointer<Float4>(in + 16 * 13);
2565 Float4 o = *Pointer<Float4>(in + 16 * 14);
2566 Float4 p = *Pointer<Float4>(in + 16 * 15);
2567
2568 Float4 ab = a + b;
2569 Float4 cd = c + d;
2570 Float4 ef = e + f;
2571 Float4 gh = g + h;
2572 Float4 ij = i + j;
2573 Float4 kl = k + l;
2574 Float4 mn = m + n;
2575 Float4 op = o + p;
2576
2577 Float4 abcd = ab + cd;
2578 Float4 efgh = ef + gh;
2579 Float4 ijkl = ij + kl;
2580 Float4 mnop = mn + op;
2581
2582 Float4 abcdefgh = abcd + efgh;
2583 Float4 ijklmnop = ijkl + mnop;
2584 Float4 sum = abcdefgh + ijklmnop;
2585 *Pointer<Float4>(out) = sum;
2586 Return();
2587 }
2588
2589 auto routine = function(testName().c_str());
2590 assert(routine);
2591
2592 float input[64] = { 1.0f, 0.0f, 0.0f, 0.0f,
2593 -1.0f, 1.0f, -1.0f, 0.0f,
2594 1.0f, 2.0f, -2.0f, 0.0f,
2595 -1.0f, 3.0f, -3.0f, 0.0f,
2596 1.0f, 4.0f, -4.0f, 0.0f,
2597 -1.0f, 5.0f, -5.0f, 0.0f,
2598 1.0f, 6.0f, -6.0f, 0.0f,
2599 -1.0f, 7.0f, -7.0f, 0.0f,
2600 1.0f, 8.0f, -8.0f, 0.0f,
2601 -1.0f, 9.0f, -9.0f, 0.0f,
2602 1.0f, 10.0f, -10.0f, 0.0f,
2603 -1.0f, 11.0f, -11.0f, 0.0f,
2604 1.0f, 12.0f, -12.0f, 0.0f,
2605 -1.0f, 13.0f, -13.0f, 0.0f,
2606 1.0f, 14.0f, -14.0f, 0.0f,
2607 -1.0f, 15.0f, -15.0f, 0.0f };
2608
2609 float result[4];
2610
2611 routine(input, result);
2612
2613 EXPECT_EQ(result[0], 0.0f);
2614 EXPECT_EQ(result[1], 120.0f);
2615 EXPECT_EQ(result[2], -120.0f);
2616 EXPECT_EQ(result[3], 0.0f);
2617 }
2618
2619 template<typename T>
2620 class CToReactorTCastTest : public ::testing::Test
2621 {
2622 public:
2623 using CType = typename std::tuple_element<0, T>::type;
2624 using ReactorType = typename std::tuple_element<1, T>::type;
2625 };
2626
2627 using CToReactorTCastTestTypes = ::testing::Types< // Subset of types that can be used as arguments.
2628 // std::pair<bool, Bool>, FIXME(capn): Not supported as argument type by Subzero.
2629 // std::pair<uint8_t, Byte>, FIXME(capn): Not supported as argument type by Subzero.
2630 // std::pair<int8_t, SByte>, FIXME(capn): Not supported as argument type by Subzero.
2631 // std::pair<int16_t, Short>, FIXME(capn): Not supported as argument type by Subzero.
2632 // std::pair<uint16_t, UShort>, FIXME(capn): Not supported as argument type by Subzero.
2633 std::pair<int, Int>,
2634 std::pair<unsigned int, UInt>,
2635 std::pair<float, Float>>;
2636
2637 TYPED_TEST_SUITE(CToReactorTCastTest, CToReactorTCastTestTypes);
2638
TYPED_TEST(CToReactorTCastTest,Casts)2639 TYPED_TEST(CToReactorTCastTest, Casts)
2640 {
2641 using CType = typename TestFixture::CType;
2642 using ReactorType = typename TestFixture::ReactorType;
2643
2644 std::shared_ptr<Routine> routine;
2645
2646 {
2647 Function<Int(ReactorType)> function;
2648 {
2649 ReactorType a = function.template Arg<0>();
2650 ReactorType b = CType{};
2651 RValue<ReactorType> c = RValue<ReactorType>(CType{});
2652 Bool same = (a == b) && (a == c);
2653 Return(IfThenElse(same, Int(1), Int(0))); // TODO: Ability to use Bools as return values.
2654 }
2655
2656 routine = function(testName().c_str());
2657
2658 auto callable = (int (*)(CType))routine->getEntry();
2659 CType in = {};
2660 EXPECT_EQ(callable(in), 1);
2661 }
2662 }
2663
2664 template<typename T>
2665 class GEPTest : public ::testing::Test
2666 {
2667 public:
2668 using CType = typename std::tuple_element<0, T>::type;
2669 using ReactorType = typename std::tuple_element<1, T>::type;
2670 };
2671
2672 using GEPTestTypes = ::testing::Types<
2673 std::pair<bool, Bool>,
2674 std::pair<int8_t, Byte>,
2675 std::pair<int8_t, SByte>,
2676 std::pair<int8_t[4], Byte4>,
2677 std::pair<int8_t[4], SByte4>,
2678 std::pair<int8_t[8], Byte8>,
2679 std::pair<int8_t[8], SByte8>,
2680 std::pair<int8_t[16], Byte16>,
2681 std::pair<int8_t[16], SByte16>,
2682 std::pair<int16_t, Short>,
2683 std::pair<int16_t, UShort>,
2684 std::pair<int16_t[2], Short2>,
2685 std::pair<int16_t[2], UShort2>,
2686 std::pair<int16_t[4], Short4>,
2687 std::pair<int16_t[4], UShort4>,
2688 std::pair<int16_t[8], Short8>,
2689 std::pair<int16_t[8], UShort8>,
2690 std::pair<int, Int>,
2691 std::pair<int, UInt>,
2692 std::pair<int[2], Int2>,
2693 std::pair<int[2], UInt2>,
2694 std::pair<int[4], Int4>,
2695 std::pair<int[4], UInt4>,
2696 std::pair<int64_t, Long>,
2697 std::pair<int16_t, Half>,
2698 std::pair<float, Float>,
2699 std::pair<float[2], Float2>,
2700 std::pair<float[4], Float4>>;
2701
2702 TYPED_TEST_SUITE(GEPTest, GEPTestTypes);
2703
TYPED_TEST(GEPTest,PtrOffsets)2704 TYPED_TEST(GEPTest, PtrOffsets)
2705 {
2706 using CType = typename TestFixture::CType;
2707 using ReactorType = typename TestFixture::ReactorType;
2708
2709 std::shared_ptr<Routine> routine;
2710
2711 {
2712 Function<Pointer<ReactorType>(Pointer<ReactorType>, Int)> function;
2713 {
2714 Pointer<ReactorType> pointer = function.template Arg<0>();
2715 Int index = function.template Arg<1>();
2716 Return(&pointer[index]);
2717 }
2718
2719 routine = function(testName().c_str());
2720
2721 auto callable = (CType * (*)(CType *, unsigned int)) routine->getEntry();
2722
2723 union PtrInt
2724 {
2725 CType *p;
2726 size_t i;
2727 };
2728
2729 PtrInt base;
2730 base.i = 0x10000;
2731
2732 for(int i = 0; i < 5; i++)
2733 {
2734 PtrInt reference;
2735 reference.p = &base.p[i];
2736
2737 PtrInt result;
2738 result.p = callable(base.p, i);
2739
2740 auto expect = reference.i - base.i;
2741 auto got = result.i - base.i;
2742
2743 EXPECT_EQ(got, expect) << "i:" << i;
2744 }
2745 }
2746 }
2747
2748 static const std::vector<int> fibonacci = {
2749 0,
2750 1,
2751 1,
2752 2,
2753 3,
2754 5,
2755 8,
2756 13,
2757 21,
2758 34,
2759 55,
2760 89,
2761 144,
2762 233,
2763 377,
2764 610,
2765 987,
2766 1597,
2767 2584,
2768 4181,
2769 6765,
2770 10946,
2771 17711,
2772 28657,
2773 46368,
2774 75025,
2775 121393,
2776 196418,
2777 317811,
2778 };
2779
TEST(ReactorUnitTests,Fibonacci)2780 TEST(ReactorUnitTests, Fibonacci)
2781 {
2782 FunctionT<int(int)> function;
2783 {
2784 Int n = function.Arg<0>();
2785 Int current = 0;
2786 Int next = 1;
2787 For(Int i = 0, i < n, i++)
2788 {
2789 auto tmp = current + next;
2790 current = next;
2791 next = tmp;
2792 }
2793 Return(current);
2794 }
2795
2796 auto routine = function(testName().c_str());
2797
2798 for(size_t i = 0; i < fibonacci.size(); i++)
2799 {
2800 EXPECT_EQ(routine(i), fibonacci[i]);
2801 }
2802 }
2803
TEST(ReactorUnitTests,Coroutines_Fibonacci)2804 TEST(ReactorUnitTests, Coroutines_Fibonacci)
2805 {
2806 if(!rr::Caps::coroutinesSupported())
2807 {
2808 SUCCEED() << "Coroutines not supported";
2809 return;
2810 }
2811
2812 Coroutine<int()> function;
2813 {
2814 Yield(Int(0));
2815 Yield(Int(1));
2816 Int current = 1;
2817 Int next = 1;
2818 While(true)
2819 {
2820 Yield(next);
2821 auto tmp = current + next;
2822 current = next;
2823 next = tmp;
2824 }
2825 }
2826 function.finalize(testName().c_str());
2827
2828 auto coroutine = function();
2829
2830 for(size_t i = 0; i < fibonacci.size(); i++)
2831 {
2832 int out = 0;
2833 EXPECT_EQ(coroutine->await(out), true);
2834 EXPECT_EQ(out, fibonacci[i]);
2835 }
2836 }
2837
TEST(ReactorUnitTests,Coroutines_Parameters)2838 TEST(ReactorUnitTests, Coroutines_Parameters)
2839 {
2840 if(!rr::Caps::coroutinesSupported())
2841 {
2842 SUCCEED() << "Coroutines not supported";
2843 return;
2844 }
2845
2846 Coroutine<uint8_t(uint8_t * data, int count)> function;
2847 {
2848 Pointer<Byte> data = function.Arg<0>();
2849 Int count = function.Arg<1>();
2850
2851 For(Int i = 0, i < count, i++)
2852 {
2853 Yield(data[i]);
2854 }
2855 }
2856 function.finalize(testName().c_str());
2857
2858 uint8_t data[] = { 10, 20, 30 };
2859 auto coroutine = function(&data[0], 3);
2860
2861 uint8_t out = 0;
2862 EXPECT_EQ(coroutine->await(out), true);
2863 EXPECT_EQ(out, 10);
2864 out = 0;
2865 EXPECT_EQ(coroutine->await(out), true);
2866 EXPECT_EQ(out, 20);
2867 out = 0;
2868 EXPECT_EQ(coroutine->await(out), true);
2869 EXPECT_EQ(out, 30);
2870 out = 99;
2871 EXPECT_EQ(coroutine->await(out), false);
2872 EXPECT_EQ(out, 99);
2873 EXPECT_EQ(coroutine->await(out), false);
2874 EXPECT_EQ(out, 99);
2875 }
2876
2877 // This test was written because Subzero's handling of vector types
2878 // failed when more than one function is generated, as is the case
2879 // with coroutines.
TEST(ReactorUnitTests,Coroutines_Vectors)2880 TEST(ReactorUnitTests, Coroutines_Vectors)
2881 {
2882 if(!rr::Caps::coroutinesSupported())
2883 {
2884 SUCCEED() << "Coroutines not supported";
2885 return;
2886 }
2887
2888 Coroutine<int()> function;
2889 {
2890 Int4 a{ 1, 2, 3, 4 };
2891 Yield(rr::Extract(a, 2));
2892 Int4 b{ 5, 6, 7, 8 };
2893 Yield(rr::Extract(b, 1));
2894 Int4 c{ 9, 10, 11, 12 };
2895 Yield(rr::Extract(c, 1));
2896 }
2897 function.finalize(testName().c_str());
2898
2899 auto coroutine = function();
2900
2901 int out;
2902 coroutine->await(out);
2903 EXPECT_EQ(out, 3);
2904 coroutine->await(out);
2905 EXPECT_EQ(out, 6);
2906 coroutine->await(out);
2907 EXPECT_EQ(out, 10);
2908 }
2909
2910 // This test was written to make sure a coroutine without a Yield()
2911 // works correctly, by executing like a regular function with no
2912 // return (the return type is ignored).
2913 // We also run it twice to ensure per instance and/or global state
2914 // is properly cleaned up in between.
TEST(ReactorUnitTests,Coroutines_NoYield)2915 TEST(ReactorUnitTests, Coroutines_NoYield)
2916 {
2917 if(!rr::Caps::coroutinesSupported())
2918 {
2919 SUCCEED() << "Coroutines not supported";
2920 return;
2921 }
2922
2923 for(int i = 0; i < 2; ++i)
2924 {
2925 Coroutine<int()> function;
2926 {
2927 Int a;
2928 a = 4;
2929 }
2930 function.finalize(testName().c_str());
2931
2932 auto coroutine = function();
2933 int out;
2934 EXPECT_EQ(coroutine->await(out), false);
2935 }
2936 }
2937
2938 // Test generating one coroutine, and executing it on multiple threads. This makes
2939 // sure the implementation manages per-call instance data correctly.
TEST(ReactorUnitTests,Coroutines_Parallel)2940 TEST(ReactorUnitTests, Coroutines_Parallel)
2941 {
2942 if(!rr::Caps::coroutinesSupported())
2943 {
2944 SUCCEED() << "Coroutines not supported";
2945 return;
2946 }
2947
2948 Coroutine<int()> function;
2949 {
2950 Yield(Int(0));
2951 Yield(Int(1));
2952 Int current = 1;
2953 Int next = 1;
2954 While(true)
2955 {
2956 Yield(next);
2957 auto tmp = current + next;
2958 current = next;
2959 next = tmp;
2960 }
2961 }
2962
2963 // Must call on same thread that creates the coroutine
2964 function.finalize(testName().c_str());
2965
2966 std::vector<std::thread> threads;
2967 const size_t numThreads = 100;
2968
2969 for(size_t t = 0; t < numThreads; ++t)
2970 {
2971 threads.emplace_back([&] {
2972 auto coroutine = function();
2973
2974 for(size_t i = 0; i < fibonacci.size(); i++)
2975 {
2976 int out = 0;
2977 EXPECT_EQ(coroutine->await(out), true);
2978 EXPECT_EQ(out, fibonacci[i]);
2979 }
2980 });
2981 }
2982
2983 for(auto &t : threads)
2984 {
2985 t.join();
2986 }
2987 }
2988
2989 template<typename TestFuncType, typename RefFuncType, typename TestValueType>
2990 struct IntrinsicTestParams
2991 {
2992 std::function<TestFuncType> testFunc; // Function we're testing (Reactor)
2993 std::function<RefFuncType> refFunc; // Reference function to test against (C)
2994 std::vector<TestValueType> testValues; // Values to input to functions
2995 };
2996
2997 using IntrinsicTestParams_Float = IntrinsicTestParams<RValue<Float>(RValue<Float>), float(float), float>;
2998 using IntrinsicTestParams_Float4 = IntrinsicTestParams<RValue<Float4>(RValue<Float4>), float(float), float>;
2999 using IntrinsicTestParams_Float4_Float4 = IntrinsicTestParams<RValue<Float4>(RValue<Float4>, RValue<Float4>), float(float, float), std::pair<float, float>>;
3000
3001 // TODO(b/147818976): Each function has its own precision requirements for Vulkan, sometimes broken down
3002 // by input range. These are currently validated by deqp, but we can improve our own tests as well.
3003 // See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#spirvenv-precision-operation
3004 constexpr double INTRINSIC_PRECISION = 1e-4;
3005
3006 struct IntrinsicTest_Float : public testing::TestWithParam<IntrinsicTestParams_Float>
3007 {
testIntrinsicTest_Float3008 void test()
3009 {
3010 FunctionT<float(float)> function;
3011 {
3012 Return(GetParam().testFunc((Float(function.Arg<0>()))));
3013 }
3014
3015 auto routine = function(testName().c_str());
3016
3017 for(auto &&v : GetParam().testValues)
3018 {
3019 SCOPED_TRACE(v);
3020 EXPECT_NEAR(routine(v), GetParam().refFunc(v), INTRINSIC_PRECISION);
3021 }
3022 }
3023 };
3024
3025 // TODO: Move to Reactor.hpp
3026 template<>
3027 struct rr::CToReactor<int[4]>
3028 {
3029 using type = Int4;
3030 static Int4 cast(float[4]);
3031 };
3032
3033 // Value type wrapper around a <type>[4] (i.e. float4, int4)
3034 template<typename T>
3035 struct type4_value
3036 {
3037 using E = typename std::remove_pointer_t<std::decay_t<T>>;
3038
3039 type4_value() = default;
type4_valuetype4_value3040 explicit type4_value(E rep)
3041 : v{ rep, rep, rep, rep }
3042 {}
type4_valuetype4_value3043 type4_value(E x, E y, E z, E w)
3044 : v{ x, y, z, w }
3045 {}
3046
operator ==type4_value3047 bool operator==(const type4_value &rhs) const
3048 {
3049 return std::equal(std::begin(v), std::end(v), rhs.v);
3050 }
3051
3052 // For gtest printing
operator <<(std::ostream & os,const type4_value & value)3053 friend std::ostream &operator<<(std::ostream &os, const type4_value &value)
3054 {
3055 return os << "[" << value.v[0] << ", " << value.v[1] << ", " << value.v[2] << ", " << value.v[3] << "]";
3056 }
3057
3058 T v;
3059 };
3060
3061 using float4_value = type4_value<float4>;
3062 using int4_value = type4_value<int4>;
3063
3064 // Invoke a void(type4_value<T>*) routine on &v.v, returning wrapped result in v
3065 template<typename RoutineType, typename T>
invokeRoutine(RoutineType & routine,type4_value<T> v)3066 type4_value<T> invokeRoutine(RoutineType &routine, type4_value<T> v)
3067 {
3068 routine(&v.v);
3069 return v;
3070 }
3071
3072 // Invoke a void(type4_value<T>*, type4_value<T>*) routine on &v1.v, &v2.v returning wrapped result in v1
3073 template<typename RoutineType, typename T>
invokeRoutine(RoutineType & routine,type4_value<T> v1,type4_value<T> v2)3074 type4_value<T> invokeRoutine(RoutineType &routine, type4_value<T> v1, type4_value<T> v2)
3075 {
3076 routine(&v1.v, &v2.v);
3077 return v1;
3078 }
3079
3080 struct IntrinsicTest_Float4 : public testing::TestWithParam<IntrinsicTestParams_Float4>
3081 {
testIntrinsicTest_Float43082 void test()
3083 {
3084 FunctionT<void(float4 *)> function;
3085 {
3086 Pointer<Float4> a = function.Arg<0>();
3087 *a = GetParam().testFunc(*a);
3088 Return();
3089 }
3090
3091 auto routine = function(testName().c_str());
3092
3093 for(auto &&v : GetParam().testValues)
3094 {
3095 SCOPED_TRACE(v);
3096 float4_value result = invokeRoutine(routine, float4_value{ v });
3097 float4_value expected = float4_value{ GetParam().refFunc(v) };
3098 EXPECT_NEAR(result.v[0], expected.v[0], INTRINSIC_PRECISION);
3099 EXPECT_NEAR(result.v[1], expected.v[1], INTRINSIC_PRECISION);
3100 EXPECT_NEAR(result.v[2], expected.v[2], INTRINSIC_PRECISION);
3101 EXPECT_NEAR(result.v[3], expected.v[3], INTRINSIC_PRECISION);
3102 }
3103 }
3104 };
3105
3106 struct IntrinsicTest_Float4_Float4 : public testing::TestWithParam<IntrinsicTestParams_Float4_Float4>
3107 {
testIntrinsicTest_Float4_Float43108 void test()
3109 {
3110 FunctionT<void(float4 *, float4 *)> function;
3111 {
3112 Pointer<Float4> a = function.Arg<0>();
3113 Pointer<Float4> b = function.Arg<1>();
3114 *a = GetParam().testFunc(*a, *b);
3115 Return();
3116 }
3117
3118 auto routine = function(testName().c_str());
3119
3120 for(auto &&v : GetParam().testValues)
3121 {
3122 SCOPED_TRACE(v);
3123 float4_value result = invokeRoutine(routine, float4_value{ v.first }, float4_value{ v.second });
3124 float4_value expected = float4_value{ GetParam().refFunc(v.first, v.second) };
3125 EXPECT_NEAR(result.v[0], expected.v[0], INTRINSIC_PRECISION);
3126 EXPECT_NEAR(result.v[1], expected.v[1], INTRINSIC_PRECISION);
3127 EXPECT_NEAR(result.v[2], expected.v[2], INTRINSIC_PRECISION);
3128 EXPECT_NEAR(result.v[3], expected.v[3], INTRINSIC_PRECISION);
3129 }
3130 }
3131 };
3132
3133 // clang-format off
3134 INSTANTIATE_TEST_SUITE_P(IntrinsicTestParams_Float, IntrinsicTest_Float, testing::Values(
__anon5ddac1420e02(Float v) 3135 IntrinsicTestParams_Float{ [](Float v) { return rr::Exp2(v); }, exp2f, {0.f, 1.f, 123.f} },
__anon5ddac1421002(Float v) 3136 IntrinsicTestParams_Float{ [](Float v) { return rr::Log2(v); }, log2f, {1.f, 123.f} },
__anon5ddac1421102(Float v) 3137 IntrinsicTestParams_Float{ [](Float v) { return rr::Sqrt(v); }, sqrtf, {0.f, 1.f, 123.f} }
3138 ));
3139 // clang-format on
3140
3141 // TODO(b/149110874) Use coshf/sinhf when we've implemented SpirV versions at the SpirV level
vulkan_sinhf(float a)3142 float vulkan_sinhf(float a)
3143 {
3144 return ((expf(a) - expf(-a)) / 2);
3145 }
vulkan_coshf(float a)3146 float vulkan_coshf(float a)
3147 {
3148 return ((expf(a) + expf(-a)) / 2);
3149 }
3150
3151 // clang-format off
3152 constexpr float PI = 3.141592653589793f;
3153 INSTANTIATE_TEST_SUITE_P(IntrinsicTestParams_Float4, IntrinsicTest_Float4, testing::Values(
__anon5ddac1421302(RValue<Float4> v) 3154 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Sin(v); }, sinf, {0.f, 1.f, PI, 123.f} },
__anon5ddac1421502(RValue<Float4> v) 3155 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Cos(v); }, cosf, {0.f, 1.f, PI, 123.f} },
__anon5ddac1421702(RValue<Float4> v) 3156 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Tan(v); }, tanf, {0.f, 1.f, PI, 123.f} },
__anon5ddac1421a02(RValue<Float4> v) 3157 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Asin(v); }, asinf, {0.f, 1.f, -1.f} },
__anon5ddac1421b02(RValue<Float4> v) 3158 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Acos(v); }, acosf, {0.f, 1.f, -1.f} },
__anon5ddac1421d02(RValue<Float4> v) 3159 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Atan(v); }, atanf, {0.f, 1.f, PI, 123.f} },
__anon5ddac1422002(RValue<Float4> v) 3160 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Sinh(v); }, vulkan_sinhf, {0.f, 1.f, PI} },
__anon5ddac1422102(RValue<Float4> v) 3161 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Cosh(v); }, vulkan_coshf, {0.f, 1.f, PI} },
__anon5ddac1422302(RValue<Float4> v) 3162 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Tanh(v); }, tanhf, {0.f, 1.f, PI} },
__anon5ddac1422502(RValue<Float4> v) 3163 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Asinh(v); }, asinhf, {0.f, 1.f, PI, 123.f} },
__anon5ddac1422802(RValue<Float4> v) 3164 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Acosh(v); }, acoshf, { 1.f, PI, 123.f} },
__anon5ddac1422a02(RValue<Float4> v) 3165 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Atanh(v); }, atanhf, {0.f, 0.9999f, -0.9999f} },
__anon5ddac1422b02(RValue<Float4> v) 3166 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Exp(v); }, expf, {0.f, 1.f, PI} },
__anon5ddac1422d02(RValue<Float4> v) 3167 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Log(v); }, logf, {1.f, PI, 123.f} },
__anon5ddac1423002(RValue<Float4> v) 3168 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Exp2(v); }, exp2f, {0.f, 1.f, PI, 123.f} },
__anon5ddac1423202(RValue<Float4> v) 3169 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Log2(v); }, log2f, {1.f, PI, 123.f} },
__anon5ddac1423402(RValue<Float4> v) 3170 IntrinsicTestParams_Float4{ [](RValue<Float4> v) { return rr::Sqrt(v); }, sqrtf, {0.f, 1.f, PI, 123.f} }
3171 ));
3172 // clang-format on
3173
3174 // clang-format off
3175 INSTANTIATE_TEST_SUITE_P(IntrinsicTestParams_Float4_Float4, IntrinsicTest_Float4_Float4, testing::Values(
__anon5ddac1423602(RValue<Float4> v1, RValue<Float4> v2) 3176 IntrinsicTestParams_Float4_Float4{ [](RValue<Float4> v1, RValue<Float4> v2) { return Atan2(v1, v2); }, atan2f, { {0.f, 0.f}, {0.f, -1.f}, {-1.f, 0.f}, {123.f, 123.f} } },
__anon5ddac1423802(RValue<Float4> v1, RValue<Float4> v2) 3177 IntrinsicTestParams_Float4_Float4{ [](RValue<Float4> v1, RValue<Float4> v2) { return Pow(v1, v2); }, powf, { {1.f, 0.f}, {1.f, -1.f}, {-1.f, 0.f} } }
3178 ));
3179 // clang-format on
3180
TEST_P(IntrinsicTest_Float,Test)3181 TEST_P(IntrinsicTest_Float, Test)
3182 {
3183 test();
3184 }
TEST_P(IntrinsicTest_Float4,Test)3185 TEST_P(IntrinsicTest_Float4, Test)
3186 {
3187 test();
3188 }
TEST_P(IntrinsicTest_Float4_Float4,Test)3189 TEST_P(IntrinsicTest_Float4_Float4, Test)
3190 {
3191 test();
3192 }
3193
TEST(ReactorUnitTests,Intrinsics_Ctlz)3194 TEST(ReactorUnitTests, Intrinsics_Ctlz)
3195 {
3196 // ctlz: counts number of leading zeros
3197
3198 {
3199 Function<UInt(UInt x)> function;
3200 {
3201 UInt x = function.Arg<0>();
3202 Return(rr::Ctlz(x, false));
3203 }
3204 auto routine = function(testName().c_str());
3205 auto callable = (uint32_t(*)(uint32_t))routine->getEntry();
3206
3207 for(uint32_t i = 0; i < 31; ++i)
3208 {
3209 uint32_t result = callable(1 << i);
3210 EXPECT_EQ(result, 31 - i);
3211 }
3212
3213 // Input 0 should return 32 for isZeroUndef == false
3214 {
3215 uint32_t result = callable(0);
3216 EXPECT_EQ(result, 32u);
3217 }
3218 }
3219
3220 {
3221 Function<Void(Pointer<UInt4>, UInt x)> function;
3222 {
3223 Pointer<UInt4> out = function.Arg<0>();
3224 UInt x = function.Arg<1>();
3225 *out = rr::Ctlz(UInt4(x), false);
3226 }
3227 auto routine = function(testName().c_str());
3228 auto callable = (void (*)(uint32_t *, uint32_t))routine->getEntry();
3229
3230 uint32_t x[4];
3231
3232 for(uint32_t i = 0; i < 31; ++i)
3233 {
3234 callable(x, 1 << i);
3235 EXPECT_EQ(x[0], 31 - i);
3236 EXPECT_EQ(x[1], 31 - i);
3237 EXPECT_EQ(x[2], 31 - i);
3238 EXPECT_EQ(x[3], 31 - i);
3239 }
3240
3241 // Input 0 should return 32 for isZeroUndef == false
3242 {
3243 callable(x, 0);
3244 EXPECT_EQ(x[0], 32u);
3245 EXPECT_EQ(x[1], 32u);
3246 EXPECT_EQ(x[2], 32u);
3247 EXPECT_EQ(x[3], 32u);
3248 }
3249 }
3250 }
3251
TEST(ReactorUnitTests,Intrinsics_Cttz)3252 TEST(ReactorUnitTests, Intrinsics_Cttz)
3253 {
3254 // cttz: counts number of trailing zeros
3255
3256 {
3257 Function<UInt(UInt x)> function;
3258 {
3259 UInt x = function.Arg<0>();
3260 Return(rr::Cttz(x, false));
3261 }
3262 auto routine = function(testName().c_str());
3263 auto callable = (uint32_t(*)(uint32_t))routine->getEntry();
3264
3265 for(uint32_t i = 0; i < 31; ++i)
3266 {
3267 uint32_t result = callable(1 << i);
3268 EXPECT_EQ(result, i);
3269 }
3270
3271 // Input 0 should return 32 for isZeroUndef == false
3272 {
3273 uint32_t result = callable(0);
3274 EXPECT_EQ(result, 32u);
3275 }
3276 }
3277
3278 {
3279 Function<Void(Pointer<UInt4>, UInt x)> function;
3280 {
3281 Pointer<UInt4> out = function.Arg<0>();
3282 UInt x = function.Arg<1>();
3283 *out = rr::Cttz(UInt4(x), false);
3284 }
3285 auto routine = function(testName().c_str());
3286 auto callable = (void (*)(uint32_t *, uint32_t))routine->getEntry();
3287
3288 uint32_t x[4];
3289
3290 for(uint32_t i = 0; i < 31; ++i)
3291 {
3292 callable(x, 1 << i);
3293 EXPECT_EQ(x[0], i);
3294 EXPECT_EQ(x[1], i);
3295 EXPECT_EQ(x[2], i);
3296 EXPECT_EQ(x[3], i);
3297 }
3298
3299 // Input 0 should return 32 for isZeroUndef == false
3300 {
3301 callable(x, 0);
3302 EXPECT_EQ(x[0], 32u);
3303 EXPECT_EQ(x[1], 32u);
3304 EXPECT_EQ(x[2], 32u);
3305 EXPECT_EQ(x[3], 32u);
3306 }
3307 }
3308 }
3309
TEST(ReactorUnitTests,ExtractFromRValue)3310 TEST(ReactorUnitTests, ExtractFromRValue)
3311 {
3312 Function<Void(Pointer<Int4> values, Pointer<Int4> result)> function;
3313 {
3314 Pointer<Int4> vIn = function.Arg<0>();
3315 Pointer<Int4> resultIn = function.Arg<1>();
3316
3317 RValue<Int4> v = *vIn;
3318
3319 Int4 result(678);
3320
3321 If(Extract(v, 0) == 42)
3322 {
3323 result = Insert(result, 1, 0);
3324 }
3325
3326 If(Extract(v, 1) == 42)
3327 {
3328 result = Insert(result, 1, 1);
3329 }
3330
3331 *resultIn = result;
3332
3333 Return();
3334 }
3335
3336 auto routine = function(testName().c_str());
3337 auto entry = (void (*)(int *, int *))routine->getEntry();
3338
3339 int v[4] = { 42, 42, 42, 42 };
3340 int result[4] = { 99, 99, 99, 99 };
3341 entry(v, result);
3342 EXPECT_EQ(result[0], 1);
3343 EXPECT_EQ(result[1], 1);
3344 EXPECT_EQ(result[2], 678);
3345 EXPECT_EQ(result[3], 678);
3346 }
3347
TEST(ReactorUnitTests,AddAtomic)3348 TEST(ReactorUnitTests, AddAtomic)
3349 {
3350 FunctionT<uint32_t(uint32_t * p, uint32_t a)> function;
3351 {
3352 Pointer<UInt> p = function.Arg<0>();
3353 UInt a = function.Arg<1>();
3354 UInt r = rr::AddAtomic(p, a, std::memory_order_relaxed);
3355 Return(r);
3356 }
3357
3358 auto routine = function(testName().c_str());
3359 uint32_t x = 123;
3360 uint32_t y = 456;
3361 uint32_t prevX = routine(&x, y);
3362 EXPECT_EQ(prevX, 123u);
3363 EXPECT_EQ(x, 579u);
3364 }
3365
TEST(ReactorUnitTests,SubAtomic)3366 TEST(ReactorUnitTests, SubAtomic)
3367 {
3368 FunctionT<uint32_t(uint32_t * p, uint32_t a)> function;
3369 {
3370 Pointer<UInt> p = function.Arg<0>();
3371 UInt a = function.Arg<1>();
3372 UInt r = rr::SubAtomic(p, a, std::memory_order_relaxed);
3373 Return(r);
3374 }
3375
3376 auto routine = function(testName().c_str());
3377 uint32_t x = 456;
3378 uint32_t y = 123;
3379 uint32_t prevX = routine(&x, y);
3380 EXPECT_EQ(prevX, 456u);
3381 EXPECT_EQ(x, 333u);
3382 }
3383
TEST(ReactorUnitTests,AndAtomic)3384 TEST(ReactorUnitTests, AndAtomic)
3385 {
3386 FunctionT<uint32_t(uint32_t * p, uint32_t a)> function;
3387 {
3388 Pointer<UInt> p = function.Arg<0>();
3389 UInt a = function.Arg<1>();
3390 UInt r = rr::AndAtomic(p, a, std::memory_order_relaxed);
3391 Return(r);
3392 }
3393
3394 auto routine = function(testName().c_str());
3395 uint32_t x = 0b1111'0000;
3396 uint32_t y = 0b1010'1100;
3397 uint32_t prevX = routine(&x, y);
3398 EXPECT_EQ(prevX, 0b1111'0000u);
3399 EXPECT_EQ(x, 0b1010'0000u);
3400 }
3401
TEST(ReactorUnitTests,OrAtomic)3402 TEST(ReactorUnitTests, OrAtomic)
3403 {
3404 FunctionT<uint32_t(uint32_t * p, uint32_t a)> function;
3405 {
3406 Pointer<UInt> p = function.Arg<0>();
3407 UInt a = function.Arg<1>();
3408 UInt r = rr::OrAtomic(p, a, std::memory_order_relaxed);
3409 Return(r);
3410 }
3411
3412 auto routine = function(testName().c_str());
3413 uint32_t x = 0b1111'0000;
3414 uint32_t y = 0b1010'1100;
3415 uint32_t prevX = routine(&x, y);
3416 EXPECT_EQ(prevX, 0b1111'0000u);
3417 EXPECT_EQ(x, 0b1111'1100u);
3418 }
3419
TEST(ReactorUnitTests,XorAtomic)3420 TEST(ReactorUnitTests, XorAtomic)
3421 {
3422 FunctionT<uint32_t(uint32_t * p, uint32_t a)> function;
3423 {
3424 Pointer<UInt> p = function.Arg<0>();
3425 UInt a = function.Arg<1>();
3426 UInt r = rr::XorAtomic(p, a, std::memory_order_relaxed);
3427 Return(r);
3428 }
3429
3430 auto routine = function(testName().c_str());
3431 uint32_t x = 0b1111'0000;
3432 uint32_t y = 0b1010'1100;
3433 uint32_t prevX = routine(&x, y);
3434 EXPECT_EQ(prevX, 0b1111'0000u);
3435 EXPECT_EQ(x, 0b0101'1100u);
3436 }
3437
TEST(ReactorUnitTests,MinAtomic)3438 TEST(ReactorUnitTests, MinAtomic)
3439 {
3440 {
3441 FunctionT<uint32_t(uint32_t * p, uint32_t a)> function;
3442 {
3443 Pointer<UInt> p = function.Arg<0>();
3444 UInt a = function.Arg<1>();
3445 UInt r = rr::MinAtomic(p, a, std::memory_order_relaxed);
3446 Return(r);
3447 }
3448
3449 auto routine = function(testName().c_str());
3450 uint32_t x = 123;
3451 uint32_t y = 100;
3452 uint32_t prevX = routine(&x, y);
3453 EXPECT_EQ(prevX, 123u);
3454 EXPECT_EQ(x, 100u);
3455 }
3456
3457 {
3458 FunctionT<int32_t(int32_t * p, int32_t a)> function;
3459 {
3460 Pointer<Int> p = function.Arg<0>();
3461 Int a = function.Arg<1>();
3462 Int r = rr::MinAtomic(p, a, std::memory_order_relaxed);
3463 Return(r);
3464 }
3465
3466 auto routine = function(testName().c_str());
3467 int32_t x = -123;
3468 int32_t y = -200;
3469 int32_t prevX = routine(&x, y);
3470 EXPECT_EQ(prevX, -123);
3471 EXPECT_EQ(x, -200);
3472 }
3473 }
3474
TEST(ReactorUnitTests,MaxAtomic)3475 TEST(ReactorUnitTests, MaxAtomic)
3476 {
3477 {
3478 FunctionT<uint32_t(uint32_t * p, uint32_t a)> function;
3479 {
3480 Pointer<UInt> p = function.Arg<0>();
3481 UInt a = function.Arg<1>();
3482 UInt r = rr::MaxAtomic(p, a, std::memory_order_relaxed);
3483 Return(r);
3484 }
3485
3486 auto routine = function(testName().c_str());
3487 uint32_t x = 123;
3488 uint32_t y = 100;
3489 uint32_t prevX = routine(&x, y);
3490 EXPECT_EQ(prevX, 123u);
3491 EXPECT_EQ(x, 123u);
3492 }
3493
3494 {
3495 FunctionT<int32_t(int32_t * p, int32_t a)> function;
3496 {
3497 Pointer<Int> p = function.Arg<0>();
3498 Int a = function.Arg<1>();
3499 Int r = rr::MaxAtomic(p, a, std::memory_order_relaxed);
3500 Return(r);
3501 }
3502
3503 auto routine = function(testName().c_str());
3504 int32_t x = -123;
3505 int32_t y = -200;
3506 int32_t prevX = routine(&x, y);
3507 EXPECT_EQ(prevX, -123);
3508 EXPECT_EQ(x, -123);
3509 }
3510 }
3511
TEST(ReactorUnitTests,ExchangeAtomic)3512 TEST(ReactorUnitTests, ExchangeAtomic)
3513 {
3514 FunctionT<uint32_t(uint32_t * p, uint32_t a)> function;
3515 {
3516 Pointer<UInt> p = function.Arg<0>();
3517 UInt a = function.Arg<1>();
3518 UInt r = rr::ExchangeAtomic(p, a, std::memory_order_relaxed);
3519 Return(r);
3520 }
3521
3522 auto routine = function(testName().c_str());
3523 uint32_t x = 123;
3524 uint32_t y = 456;
3525 uint32_t prevX = routine(&x, y);
3526 EXPECT_EQ(prevX, 123u);
3527 EXPECT_EQ(x, y);
3528 }
3529
TEST(ReactorUnitTests,CompareExchangeAtomic)3530 TEST(ReactorUnitTests, CompareExchangeAtomic)
3531 {
3532 FunctionT<uint32_t(uint32_t * x, uint32_t y, uint32_t compare)> function;
3533 {
3534 Pointer<UInt> x = function.Arg<0>();
3535 UInt y = function.Arg<1>();
3536 UInt compare = function.Arg<2>();
3537 UInt r = rr::CompareExchangeAtomic(x, y, compare, std::memory_order_relaxed, std::memory_order_relaxed);
3538 Return(r);
3539 }
3540
3541 auto routine = function(testName().c_str());
3542 uint32_t x = 123;
3543 uint32_t y = 456;
3544 uint32_t compare = 123;
3545 uint32_t prevX = routine(&x, y, compare);
3546 EXPECT_EQ(prevX, 123u);
3547 EXPECT_EQ(x, y);
3548
3549 x = 123;
3550 y = 456;
3551 compare = 456;
3552 prevX = routine(&x, y, compare);
3553 EXPECT_EQ(prevX, 123u);
3554 EXPECT_EQ(x, 123u);
3555 }
3556
TEST(ReactorUnitTests,SRem)3557 TEST(ReactorUnitTests, SRem)
3558 {
3559 FunctionT<void(int4 *, int4 *)> function;
3560 {
3561 Pointer<Int4> a = function.Arg<0>();
3562 Pointer<Int4> b = function.Arg<1>();
3563 *a = *a % *b;
3564 }
3565
3566 auto routine = function(testName().c_str());
3567
3568 int4_value result = invokeRoutine(routine, int4_value{ 10, 11, 12, 13 }, int4_value{ 3, 3, 3, 3 });
3569 int4_value expected = int4_value{ 10 % 3, 11 % 3, 12 % 3, 13 % 3 };
3570 EXPECT_FLOAT_EQ(result.v[0], expected.v[0]);
3571 EXPECT_FLOAT_EQ(result.v[1], expected.v[1]);
3572 EXPECT_FLOAT_EQ(result.v[2], expected.v[2]);
3573 EXPECT_FLOAT_EQ(result.v[3], expected.v[3]);
3574 }
3575
TEST(ReactorUnitTests,FRem)3576 TEST(ReactorUnitTests, FRem)
3577 {
3578 FunctionT<void(float4 *, float4 *)> function;
3579 {
3580 Pointer<Float4> a = function.Arg<0>();
3581 Pointer<Float4> b = function.Arg<1>();
3582 *a = *a % *b;
3583 }
3584
3585 auto routine = function(testName().c_str());
3586
3587 float4_value result = invokeRoutine(routine, float4_value{ 10.1f, 11.2f, 12.3f, 13.4f }, float4_value{ 3.f, 3.f, 3.f, 3.f });
3588 float4_value expected = float4_value{ fmodf(10.1f, 3.f), fmodf(11.2f, 3.f), fmodf(12.3f, 3.f), fmodf(13.4f, 3.f) };
3589 EXPECT_FLOAT_EQ(result.v[0], expected.v[0]);
3590 EXPECT_FLOAT_EQ(result.v[1], expected.v[1]);
3591 EXPECT_FLOAT_EQ(result.v[2], expected.v[2]);
3592 EXPECT_FLOAT_EQ(result.v[3], expected.v[3]);
3593 }
3594
3595 // Subzero's load instruction assumes that a Constant ptr value is an offset, rather than an absolute
3596 // pointer, and would fail during codegen. This was fixed by casting the constant to a non-const
3597 // variable, and loading from it instead. This test makes sure this works.
TEST(ReactorUnitTests,LoadFromConstantData)3598 TEST(ReactorUnitTests, LoadFromConstantData)
3599 {
3600 const int value = 123;
3601
3602 FunctionT<int()> function;
3603 {
3604 auto p = Pointer<Int>{ ConstantData(&value, sizeof(value)) };
3605 Int v = *p;
3606 Return(v);
3607 }
3608
3609 const int result = function(testName().c_str())();
3610 EXPECT_EQ(result, value);
3611 }
3612
TEST(ReactorUnitTests,Multithreaded_Function)3613 TEST(ReactorUnitTests, Multithreaded_Function)
3614 {
3615 constexpr int numThreads = 8;
3616 constexpr int numLoops = 16;
3617
3618 auto threads = std::unique_ptr<std::thread[]>(new std::thread[numThreads]);
3619 auto results = std::unique_ptr<int[]>(new int[numThreads * numLoops]);
3620
3621 for(int t = 0; t < numThreads; t++)
3622 {
3623 auto threadFunc = [&](int t) {
3624 for(int l = 0; l < numLoops; l++)
3625 {
3626 FunctionT<int(int, int)> function;
3627 {
3628 Int a = function.Arg<0>();
3629 Int b = function.Arg<1>();
3630 Return((a << 16) | b);
3631 }
3632
3633 auto f = function("%s_thread%d_loop%d", testName().c_str(), t, l);
3634 results[t * numLoops + l] = f(t, l);
3635 }
3636 };
3637 threads[t] = std::thread(threadFunc, t);
3638 }
3639
3640 for(int t = 0; t < numThreads; t++)
3641 {
3642 threads[t].join();
3643 }
3644
3645 for(int t = 0; t < numThreads; t++)
3646 {
3647 for(int l = 0; l < numLoops; l++)
3648 {
3649 auto expect = (t << 16) | l;
3650 auto result = results[t * numLoops + l];
3651 EXPECT_EQ(result, expect);
3652 }
3653 }
3654 }
3655
TEST(ReactorUnitTests,Multithreaded_Coroutine)3656 TEST(ReactorUnitTests, Multithreaded_Coroutine)
3657 {
3658 if(!rr::Caps::coroutinesSupported())
3659 {
3660 SUCCEED() << "Coroutines not supported";
3661 return;
3662 }
3663
3664 constexpr int numThreads = 8;
3665 constexpr int numLoops = 16;
3666
3667 struct Result
3668 {
3669 bool yieldReturns[3];
3670 int yieldValues[3];
3671 };
3672
3673 auto threads = std::unique_ptr<std::thread[]>(new std::thread[numThreads]);
3674 auto results = std::unique_ptr<Result[]>(new Result[numThreads * numLoops]);
3675
3676 for(int t = 0; t < numThreads; t++)
3677 {
3678 auto threadFunc = [&](int t) {
3679 for(int l = 0; l < numLoops; l++)
3680 {
3681 Coroutine<int(int, int)> function;
3682 {
3683 Int a = function.Arg<0>();
3684 Int b = function.Arg<1>();
3685 Yield(a);
3686 Yield(b);
3687 }
3688 function.finalize((testName() + "_thread" + std::to_string(t) + "_loop" + std::to_string(l)).c_str());
3689
3690 auto coroutine = function(t, l);
3691
3692 auto &result = results[t * numLoops + l];
3693 result = {};
3694 result.yieldReturns[0] = coroutine->await(result.yieldValues[0]);
3695 result.yieldReturns[1] = coroutine->await(result.yieldValues[1]);
3696 result.yieldReturns[2] = coroutine->await(result.yieldValues[2]);
3697 }
3698 };
3699 threads[t] = std::thread(threadFunc, t);
3700 }
3701
3702 for(int t = 0; t < numThreads; t++)
3703 {
3704 threads[t].join();
3705 }
3706
3707 for(int t = 0; t < numThreads; t++)
3708 {
3709 for(int l = 0; l < numLoops; l++)
3710 {
3711 const auto &result = results[t * numLoops + l];
3712 EXPECT_EQ(result.yieldReturns[0], true);
3713 EXPECT_EQ(result.yieldValues[0], t);
3714 EXPECT_EQ(result.yieldReturns[1], true);
3715 EXPECT_EQ(result.yieldValues[1], l);
3716 EXPECT_EQ(result.yieldReturns[2], false);
3717 EXPECT_EQ(result.yieldValues[2], 0);
3718 }
3719 }
3720 }
3721
3722 // For gtest printing of pairs
3723 namespace std {
3724 template<typename T, typename U>
operator <<(std::ostream & os,const std::pair<T,U> & value)3725 std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &value)
3726 {
3727 return os << "{ " << value.first << ", " << value.second << " }";
3728 }
3729 } // namespace std
3730
3731 class StdOutCapture
3732 {
3733 public:
~StdOutCapture()3734 ~StdOutCapture()
3735 {
3736 stopIfCapturing();
3737 }
3738
start()3739 void start()
3740 {
3741 stopIfCapturing();
3742 capturing = true;
3743 testing::internal::CaptureStdout();
3744 }
3745
stop()3746 std::string stop()
3747 {
3748 assert(capturing);
3749 capturing = false;
3750 return testing::internal::GetCapturedStdout();
3751 }
3752
3753 private:
stopIfCapturing()3754 void stopIfCapturing()
3755 {
3756 if(capturing)
3757 {
3758 // This stops the capture
3759 testing::internal::GetCapturedStdout();
3760 }
3761 }
3762
3763 bool capturing = false;
3764 };
3765
split(const std::string & s)3766 std::vector<std::string> split(const std::string &s)
3767 {
3768 std::vector<std::string> result;
3769 std::istringstream iss(s);
3770 for(std::string line; std::getline(iss, line);)
3771 {
3772 result.push_back(line);
3773 }
3774 return result;
3775 }
3776
TEST(ReactorUnitTests,PrintPrimitiveTypes)3777 TEST(ReactorUnitTests, PrintPrimitiveTypes)
3778 {
3779 #if defined(ENABLE_RR_PRINT) && !defined(ENABLE_RR_EMIT_PRINT_LOCATION)
3780 FunctionT<void()> function;
3781 {
3782 bool b(true);
3783 int8_t i8(-1);
3784 uint8_t ui8(1);
3785 int16_t i16(-1);
3786 uint16_t ui16(1);
3787 int32_t i32(-1);
3788 uint32_t ui32(1);
3789 int64_t i64(-1);
3790 uint64_t ui64(1);
3791 float f(1);
3792 double d(2);
3793 const char *cstr = "const char*";
3794 std::string str = "std::string";
3795 int *p = nullptr;
3796
3797 RR_WATCH(b);
3798 RR_WATCH(i8);
3799 RR_WATCH(ui8);
3800 RR_WATCH(i16);
3801 RR_WATCH(ui16);
3802 RR_WATCH(i32);
3803 RR_WATCH(ui32);
3804 RR_WATCH(i64);
3805 RR_WATCH(ui64);
3806 RR_WATCH(f);
3807 RR_WATCH(d);
3808 RR_WATCH(cstr);
3809 RR_WATCH(str);
3810 RR_WATCH(p);
3811 }
3812
3813 auto routine = function(testName().c_str());
3814
3815 char pNullptr[64];
3816 snprintf(pNullptr, sizeof(pNullptr), " p: %p", nullptr);
3817
3818 const char *expected[] = {
3819 " b: true",
3820 " i8: -1",
3821 " ui8: 1",
3822 " i16: -1",
3823 " ui16: 1",
3824 " i32: -1",
3825 " ui32: 1",
3826 " i64: -1",
3827 " ui64: 1",
3828 " f: 1.000000",
3829 " d: 2.000000",
3830 " cstr: const char*",
3831 " str: std::string",
3832 pNullptr,
3833 };
3834 constexpr size_t expectedSize = sizeof(expected) / sizeof(expected[0]);
3835
3836 StdOutCapture capture;
3837 capture.start();
3838 routine();
3839 auto output = split(capture.stop());
3840 for(size_t i = 0, j = 1; i < expectedSize; ++i, j += 2)
3841 {
3842 ASSERT_EQ(expected[i], output[j]);
3843 }
3844
3845 #endif
3846 }
3847
TEST(ReactorUnitTests,PrintReactorTypes)3848 TEST(ReactorUnitTests, PrintReactorTypes)
3849 {
3850 #if defined(ENABLE_RR_PRINT) && !defined(ENABLE_RR_EMIT_PRINT_LOCATION)
3851 FunctionT<void()> function;
3852 {
3853 Bool b(true);
3854 Int i(-1);
3855 Int2 i2(-1, -2);
3856 Int4 i4(-1, -2, -3, -4);
3857 UInt ui(1);
3858 UInt2 ui2(1, 2);
3859 UInt4 ui4(1, 2, 3, 4);
3860 Short s(-1);
3861 Short4 s4(-1, -2, -3, -4);
3862 UShort us(1);
3863 UShort4 us4(1, 2, 3, 4);
3864 Float f(1);
3865 Float4 f4(1, 2, 3, 4);
3866 Long l(i);
3867 Pointer<Int> pi = nullptr;
3868 RValue<Int> rvi = i;
3869 Byte by('a');
3870 Byte4 by4(i4);
3871
3872 RR_WATCH(b);
3873 RR_WATCH(i);
3874 RR_WATCH(i2);
3875 RR_WATCH(i4);
3876 RR_WATCH(ui);
3877 RR_WATCH(ui2);
3878 RR_WATCH(ui4);
3879 RR_WATCH(s);
3880 RR_WATCH(s4);
3881 RR_WATCH(us);
3882 RR_WATCH(us4);
3883 RR_WATCH(f);
3884 RR_WATCH(f4);
3885 RR_WATCH(l);
3886 RR_WATCH(pi);
3887 RR_WATCH(rvi);
3888 RR_WATCH(by);
3889 RR_WATCH(by4);
3890 }
3891
3892 auto routine = function(testName().c_str());
3893
3894 char piNullptr[64];
3895 snprintf(piNullptr, sizeof(piNullptr), " pi: %p", nullptr);
3896
3897 const char *expected[] = {
3898 " b: true",
3899 " i: -1",
3900 " i2: [-1, -2]",
3901 " i4: [-1, -2, -3, -4]",
3902 " ui: 1",
3903 " ui2: [1, 2]",
3904 " ui4: [1, 2, 3, 4]",
3905 " s: -1",
3906 " s4: [-1, -2, -3, -4]",
3907 " us: 1",
3908 " us4: [1, 2, 3, 4]",
3909 " f: 1.000000",
3910 " f4: [1.000000, 2.000000, 3.000000, 4.000000]",
3911 " l: -1",
3912 piNullptr,
3913 " rvi: -1",
3914 " by: 97",
3915 " by4: [255, 254, 253, 252]",
3916 };
3917 constexpr size_t expectedSize = sizeof(expected) / sizeof(expected[0]);
3918
3919 StdOutCapture capture;
3920 capture.start();
3921 routine();
3922 auto output = split(capture.stop());
3923 for(size_t i = 0, j = 1; i < expectedSize; ++i, j += 2)
3924 {
3925 ASSERT_EQ(expected[i], output[j]);
3926 }
3927
3928 #endif
3929 }
3930
3931 // Test constant <op> variable
3932 template<typename T, typename Func>
Arithmetic_LhsConstArg(T arg1,T arg2,Func f)3933 T Arithmetic_LhsConstArg(T arg1, T arg2, Func f)
3934 {
3935 using ReactorT = CToReactorT<T>;
3936
3937 FunctionT<T(T)> function;
3938 {
3939 ReactorT lhs = arg1;
3940 ReactorT rhs = function.template Arg<0>();
3941 ReactorT result = f(lhs, rhs);
3942 Return(result);
3943 }
3944
3945 auto routine = function(testName().c_str());
3946 return routine(arg2);
3947 }
3948
3949 // Test variable <op> constant
3950 template<typename T, typename Func>
Arithmetic_RhsConstArg(T arg1,T arg2,Func f)3951 T Arithmetic_RhsConstArg(T arg1, T arg2, Func f)
3952 {
3953 using ReactorT = CToReactorT<T>;
3954
3955 FunctionT<T(T)> function;
3956 {
3957 ReactorT lhs = function.template Arg<0>();
3958 ReactorT rhs = arg2;
3959 ReactorT result = f(lhs, rhs);
3960 Return(result);
3961 }
3962
3963 auto routine = function(testName().c_str());
3964 return routine(arg1);
3965 }
3966
3967 // Test constant <op> constant
3968 template<typename T, typename Func>
Arithmetic_TwoConstArgs(T arg1,T arg2,Func f)3969 T Arithmetic_TwoConstArgs(T arg1, T arg2, Func f)
3970 {
3971 using ReactorT = CToReactorT<T>;
3972
3973 FunctionT<T()> function;
3974 {
3975 ReactorT lhs = arg1;
3976 ReactorT rhs = arg2;
3977 ReactorT result = f(lhs, rhs);
3978 Return(result);
3979 }
3980
3981 auto routine = function(testName().c_str());
3982 return routine();
3983 }
3984
3985 template<typename T, typename Func>
Arithmetic_ConstArgs(T arg1,T arg2,T expected,Func f)3986 void Arithmetic_ConstArgs(T arg1, T arg2, T expected, Func f)
3987 {
3988 SCOPED_TRACE(std::to_string(arg1) + " <op> " + std::to_string(arg2) + " = " + std::to_string(expected));
3989 T result{};
3990 result = Arithmetic_LhsConstArg(arg1, arg2, std::forward<Func>(f));
3991 EXPECT_EQ(result, expected);
3992 result = Arithmetic_RhsConstArg(arg1, arg2, std::forward<Func>(f));
3993 EXPECT_EQ(result, expected);
3994 result = Arithmetic_TwoConstArgs(arg1, arg2, std::forward<Func>(f));
3995 EXPECT_EQ(result, expected);
3996 }
3997
3998 // Test that we generate valid code for when one or both args to arithmetic operations
3999 // are constant. In particular, we want to validate the case for two const args, as
4000 // often lowered instructions do not support this case.
TEST(ReactorUnitTests,Arithmetic_ConstantArgs)4001 TEST(ReactorUnitTests, Arithmetic_ConstantArgs)
4002 {
4003 Arithmetic_ConstArgs(2, 3, 5, [](auto c1, auto c2) { return c1 + c2; });
4004 Arithmetic_ConstArgs(5, 3, 2, [](auto c1, auto c2) { return c1 - c2; });
4005 Arithmetic_ConstArgs(2, 3, 6, [](auto c1, auto c2) { return c1 * c2; });
4006 Arithmetic_ConstArgs(6, 3, 2, [](auto c1, auto c2) { return c1 / c2; });
4007 Arithmetic_ConstArgs(0xF0F0, 0xAAAA, 0xA0A0, [](auto c1, auto c2) { return c1 & c2; });
4008 Arithmetic_ConstArgs(0xF0F0, 0xAAAA, 0xFAFA, [](auto c1, auto c2) { return c1 | c2; });
4009 Arithmetic_ConstArgs(0xF0F0, 0xAAAA, 0x5A5A, [](auto c1, auto c2) { return c1 ^ c2; });
4010
4011 Arithmetic_ConstArgs(2.f, 3.f, 5.f, [](auto c1, auto c2) { return c1 + c2; });
4012 Arithmetic_ConstArgs(5.f, 3.f, 2.f, [](auto c1, auto c2) { return c1 - c2; });
4013 Arithmetic_ConstArgs(2.f, 3.f, 6.f, [](auto c1, auto c2) { return c1 * c2; });
4014 Arithmetic_ConstArgs(6.f, 3.f, 2.f, [](auto c1, auto c2) { return c1 / c2; });
4015 }
4016
4017 // Test for Subzero bad code-gen that was fixed in swiftshader-cl/50008
4018 // This tests the case of copying enough arguments to local variables so that the locals
4019 // get spilled to the stack when no more registers remain, and making sure these copies
4020 // are generated correctly. Without the aforementioned fix, this fails 100% on Windows x86.
TEST(ReactorUnitTests,SpillLocalCopiesOfArgs)4021 TEST(ReactorUnitTests, SpillLocalCopiesOfArgs)
4022 {
4023 struct Helpers
4024 {
4025 static bool True() { return true; }
4026 };
4027
4028 const int numLoops = 5; // 2 should be enough, but loop more to make sure
4029
4030 FunctionT<int(int, int, int, int, int, int, int, int, int, int, int, int)> function;
4031 {
4032 Int result = 0;
4033 Int a1 = function.Arg<0>();
4034 Int a2 = function.Arg<1>();
4035 Int a3 = function.Arg<2>();
4036 Int a4 = function.Arg<3>();
4037 Int a5 = function.Arg<4>();
4038 Int a6 = function.Arg<5>();
4039 Int a7 = function.Arg<6>();
4040 Int a8 = function.Arg<7>();
4041 Int a9 = function.Arg<8>();
4042 Int a10 = function.Arg<9>();
4043 Int a11 = function.Arg<10>();
4044 Int a12 = function.Arg<11>();
4045
4046 for(int i = 0; i < numLoops; ++i)
4047 {
4048 // Copy all arguments to locals so that Ice::LocalVariableSplitter::handleSimpleVarAssign
4049 // creates Variable copies of arguments. We loop so that we create enough of these so
4050 // that some spill over to the stack.
4051 Int i1 = a1;
4052 Int i2 = a2;
4053 Int i3 = a3;
4054 Int i4 = a4;
4055 Int i5 = a5;
4056 Int i6 = a6;
4057 Int i7 = a7;
4058 Int i8 = a8;
4059 Int i9 = a9;
4060 Int i10 = a10;
4061 Int i11 = a11;
4062 Int i12 = a12;
4063
4064 // Forcibly materialize all variables so that Ice::Variable instances are created for each
4065 // local; otherwise, Reactor r-value optimizations kick in, and the locals are elided.
4066 Variable::materializeAll();
4067
4068 // We also need to create a separate block that uses the variables declared above
4069 // so that rr::optimize() doesn't optimize them out when attempting to eliminate stores
4070 // followed by a load in the same block.
4071 If(Call(Helpers::True))
4072 {
4073 result += (i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 + i12);
4074 }
4075 }
4076
4077 Return(result);
4078 }
4079
4080 auto routine = function(testName().c_str());
4081 int result = routine(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
4082 int expected = numLoops * (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12);
4083 EXPECT_EQ(result, expected);
4084 }
4085
4086 #if defined(ENABLE_RR_EMIT_ASM_FILE)
TEST(ReactorUnitTests,EmitAsm)4087 TEST(ReactorUnitTests, EmitAsm)
4088 {
4089 // Only supported by LLVM for now
4090 if(Caps::backendName().find("LLVM") == std::string::npos) return;
4091
4092 namespace fs = std::filesystem;
4093
4094 FunctionT<int(void)> function;
4095 {
4096 Int sum;
4097 For(Int i = 0, i < 10, i++)
4098 {
4099 sum += i;
4100 }
4101 Return(sum);
4102 }
4103
4104 auto routine = function(testName().c_str());
4105
4106 // Returns path to first match of filename in current directory
4107 auto findFile = [](const std::string filename) -> fs::path {
4108 for(auto &p : fs::directory_iterator("."))
4109 {
4110 if(!p.is_regular_file())
4111 continue;
4112 auto currFilename = p.path().filename().string();
4113 auto index = currFilename.find(testName());
4114 if(index != std::string::npos)
4115 {
4116 return p.path();
4117 }
4118 }
4119 return {};
4120 };
4121
4122 fs::path path = findFile(testName());
4123 EXPECT_FALSE(path.empty());
4124
4125 // Make sure an asm file was created
4126 std::ifstream fin(path);
4127 EXPECT_TRUE(fin);
4128
4129 // Make sure address of routine is in the file
4130 auto findAddressInFile = [](std::ifstream &fin, size_t address) {
4131 std::string addressString = [&] {
4132 std::stringstream addressSS;
4133 addressSS << "0x" << std::uppercase << std::hex << address;
4134 return addressSS.str();
4135 }();
4136
4137 std::string token;
4138 while(fin >> token)
4139 {
4140 if(token.find(addressString) != std::string::npos)
4141 return true;
4142 }
4143 return false;
4144 };
4145
4146 size_t address = reinterpret_cast<size_t>(routine.getEntry());
4147 EXPECT_TRUE(findAddressInFile(fin, address));
4148
4149 // Delete the file in case subsequent runs generate one with a different sequence number
4150 fin.close();
4151 std::filesystem::remove(path);
4152 }
4153 #endif
4154
4155 ////////////////////////////////
4156 // Trait compile time checks. //
4157 ////////////////////////////////
4158
4159 // Assert CToReactorT resolves to expected types.
4160 static_assert(std::is_same<CToReactorT<void>, Void>::value, "");
4161 static_assert(std::is_same<CToReactorT<bool>, Bool>::value, "");
4162 static_assert(std::is_same<CToReactorT<uint8_t>, Byte>::value, "");
4163 static_assert(std::is_same<CToReactorT<int8_t>, SByte>::value, "");
4164 static_assert(std::is_same<CToReactorT<int16_t>, Short>::value, "");
4165 static_assert(std::is_same<CToReactorT<uint16_t>, UShort>::value, "");
4166 static_assert(std::is_same<CToReactorT<int32_t>, Int>::value, "");
4167 static_assert(std::is_same<CToReactorT<uint64_t>, Long>::value, "");
4168 static_assert(std::is_same<CToReactorT<uint32_t>, UInt>::value, "");
4169 static_assert(std::is_same<CToReactorT<float>, Float>::value, "");
4170
4171 // Assert CToReactorT for known pointer types resolves to expected types.
4172 static_assert(std::is_same<CToReactorT<void *>, Pointer<Byte>>::value, "");
4173 static_assert(std::is_same<CToReactorT<bool *>, Pointer<Bool>>::value, "");
4174 static_assert(std::is_same<CToReactorT<uint8_t *>, Pointer<Byte>>::value, "");
4175 static_assert(std::is_same<CToReactorT<int8_t *>, Pointer<SByte>>::value, "");
4176 static_assert(std::is_same<CToReactorT<int16_t *>, Pointer<Short>>::value, "");
4177 static_assert(std::is_same<CToReactorT<uint16_t *>, Pointer<UShort>>::value, "");
4178 static_assert(std::is_same<CToReactorT<int32_t *>, Pointer<Int>>::value, "");
4179 static_assert(std::is_same<CToReactorT<uint64_t *>, Pointer<Long>>::value, "");
4180 static_assert(std::is_same<CToReactorT<uint32_t *>, Pointer<UInt>>::value, "");
4181 static_assert(std::is_same<CToReactorT<float *>, Pointer<Float>>::value, "");
4182 static_assert(std::is_same<CToReactorT<uint16_t **>, Pointer<Pointer<UShort>>>::value, "");
4183 static_assert(std::is_same<CToReactorT<uint16_t ***>, Pointer<Pointer<Pointer<UShort>>>>::value, "");
4184
4185 // Assert CToReactorT for unknown pointer types resolves to Pointer<Byte>.
4186 struct S
4187 {};
4188 static_assert(std::is_same<CToReactorT<S *>, Pointer<Byte>>::value, "");
4189 static_assert(std::is_same<CToReactorT<S **>, Pointer<Pointer<Byte>>>::value, "");
4190 static_assert(std::is_same<CToReactorT<S ***>, Pointer<Pointer<Pointer<Byte>>>>::value, "");
4191
4192 // Assert IsRValue<> resolves true for RValue<> types.
4193 static_assert(IsRValue<RValue<Void>>::value, "");
4194 static_assert(IsRValue<RValue<Bool>>::value, "");
4195 static_assert(IsRValue<RValue<Byte>>::value, "");
4196 static_assert(IsRValue<RValue<SByte>>::value, "");
4197 static_assert(IsRValue<RValue<Short>>::value, "");
4198 static_assert(IsRValue<RValue<UShort>>::value, "");
4199 static_assert(IsRValue<RValue<Int>>::value, "");
4200 static_assert(IsRValue<RValue<Long>>::value, "");
4201 static_assert(IsRValue<RValue<UInt>>::value, "");
4202 static_assert(IsRValue<RValue<Float>>::value, "");
4203
4204 // Assert IsLValue<> resolves true for LValue types.
4205 static_assert(IsLValue<Bool>::value, "");
4206 static_assert(IsLValue<Byte>::value, "");
4207 static_assert(IsLValue<SByte>::value, "");
4208 static_assert(IsLValue<Short>::value, "");
4209 static_assert(IsLValue<UShort>::value, "");
4210 static_assert(IsLValue<Int>::value, "");
4211 static_assert(IsLValue<Long>::value, "");
4212 static_assert(IsLValue<UInt>::value, "");
4213 static_assert(IsLValue<Float>::value, "");
4214
4215 // Assert IsReference<> resolves true for Reference types.
4216 static_assert(IsReference<Reference<Bool>>::value, "");
4217 static_assert(IsReference<Reference<Byte>>::value, "");
4218 static_assert(IsReference<Reference<SByte>>::value, "");
4219 static_assert(IsReference<Reference<Short>>::value, "");
4220 static_assert(IsReference<Reference<UShort>>::value, "");
4221 static_assert(IsReference<Reference<Int>>::value, "");
4222 static_assert(IsReference<Reference<Long>>::value, "");
4223 static_assert(IsReference<Reference<UInt>>::value, "");
4224 static_assert(IsReference<Reference<Float>>::value, "");
4225
4226 // Assert IsRValue<> resolves false for LValue types.
4227 static_assert(!IsRValue<Void>::value, "");
4228 static_assert(!IsRValue<Bool>::value, "");
4229 static_assert(!IsRValue<Byte>::value, "");
4230 static_assert(!IsRValue<SByte>::value, "");
4231 static_assert(!IsRValue<Short>::value, "");
4232 static_assert(!IsRValue<UShort>::value, "");
4233 static_assert(!IsRValue<Int>::value, "");
4234 static_assert(!IsRValue<Long>::value, "");
4235 static_assert(!IsRValue<UInt>::value, "");
4236 static_assert(!IsRValue<Float>::value, "");
4237
4238 // Assert IsRValue<> resolves false for Reference types.
4239 static_assert(!IsRValue<Reference<Void>>::value, "");
4240 static_assert(!IsRValue<Reference<Bool>>::value, "");
4241 static_assert(!IsRValue<Reference<Byte>>::value, "");
4242 static_assert(!IsRValue<Reference<SByte>>::value, "");
4243 static_assert(!IsRValue<Reference<Short>>::value, "");
4244 static_assert(!IsRValue<Reference<UShort>>::value, "");
4245 static_assert(!IsRValue<Reference<Int>>::value, "");
4246 static_assert(!IsRValue<Reference<Long>>::value, "");
4247 static_assert(!IsRValue<Reference<UInt>>::value, "");
4248 static_assert(!IsRValue<Reference<Float>>::value, "");
4249
4250 // Assert IsRValue<> resolves false for C types.
4251 static_assert(!IsRValue<void>::value, "");
4252 static_assert(!IsRValue<bool>::value, "");
4253 static_assert(!IsRValue<uint8_t>::value, "");
4254 static_assert(!IsRValue<int8_t>::value, "");
4255 static_assert(!IsRValue<int16_t>::value, "");
4256 static_assert(!IsRValue<uint16_t>::value, "");
4257 static_assert(!IsRValue<int32_t>::value, "");
4258 static_assert(!IsRValue<uint64_t>::value, "");
4259 static_assert(!IsRValue<uint32_t>::value, "");
4260 static_assert(!IsRValue<float>::value, "");
4261
4262 // Assert IsLValue<> resolves false for RValue<> types.
4263 static_assert(!IsLValue<RValue<Void>>::value, "");
4264 static_assert(!IsLValue<RValue<Bool>>::value, "");
4265 static_assert(!IsLValue<RValue<Byte>>::value, "");
4266 static_assert(!IsLValue<RValue<SByte>>::value, "");
4267 static_assert(!IsLValue<RValue<Short>>::value, "");
4268 static_assert(!IsLValue<RValue<UShort>>::value, "");
4269 static_assert(!IsLValue<RValue<Int>>::value, "");
4270 static_assert(!IsLValue<RValue<Long>>::value, "");
4271 static_assert(!IsLValue<RValue<UInt>>::value, "");
4272 static_assert(!IsLValue<RValue<Float>>::value, "");
4273
4274 // Assert IsLValue<> resolves false for Void type.
4275 static_assert(!IsLValue<Void>::value, "");
4276
4277 // Assert IsLValue<> resolves false for Reference<> types.
4278 static_assert(!IsLValue<Reference<Void>>::value, "");
4279 static_assert(!IsLValue<Reference<Bool>>::value, "");
4280 static_assert(!IsLValue<Reference<Byte>>::value, "");
4281 static_assert(!IsLValue<Reference<SByte>>::value, "");
4282 static_assert(!IsLValue<Reference<Short>>::value, "");
4283 static_assert(!IsLValue<Reference<UShort>>::value, "");
4284 static_assert(!IsLValue<Reference<Int>>::value, "");
4285 static_assert(!IsLValue<Reference<Long>>::value, "");
4286 static_assert(!IsLValue<Reference<UInt>>::value, "");
4287 static_assert(!IsLValue<Reference<Float>>::value, "");
4288
4289 // Assert IsLValue<> resolves false for C types.
4290 static_assert(!IsLValue<void>::value, "");
4291 static_assert(!IsLValue<bool>::value, "");
4292 static_assert(!IsLValue<uint8_t>::value, "");
4293 static_assert(!IsLValue<int8_t>::value, "");
4294 static_assert(!IsLValue<int16_t>::value, "");
4295 static_assert(!IsLValue<uint16_t>::value, "");
4296 static_assert(!IsLValue<int32_t>::value, "");
4297 static_assert(!IsLValue<uint64_t>::value, "");
4298 static_assert(!IsLValue<uint32_t>::value, "");
4299 static_assert(!IsLValue<float>::value, "");
4300
4301 // Assert IsDefined<> resolves true for RValue<> types.
4302 static_assert(IsDefined<RValue<Void>>::value, "");
4303 static_assert(IsDefined<RValue<Bool>>::value, "");
4304 static_assert(IsDefined<RValue<Byte>>::value, "");
4305 static_assert(IsDefined<RValue<SByte>>::value, "");
4306 static_assert(IsDefined<RValue<Short>>::value, "");
4307 static_assert(IsDefined<RValue<UShort>>::value, "");
4308 static_assert(IsDefined<RValue<Int>>::value, "");
4309 static_assert(IsDefined<RValue<Long>>::value, "");
4310 static_assert(IsDefined<RValue<UInt>>::value, "");
4311 static_assert(IsDefined<RValue<Float>>::value, "");
4312
4313 // Assert IsDefined<> resolves true for LValue types.
4314 static_assert(IsDefined<Void>::value, "");
4315 static_assert(IsDefined<Bool>::value, "");
4316 static_assert(IsDefined<Byte>::value, "");
4317 static_assert(IsDefined<SByte>::value, "");
4318 static_assert(IsDefined<Short>::value, "");
4319 static_assert(IsDefined<UShort>::value, "");
4320 static_assert(IsDefined<Int>::value, "");
4321 static_assert(IsDefined<Long>::value, "");
4322 static_assert(IsDefined<UInt>::value, "");
4323 static_assert(IsDefined<Float>::value, "");
4324
4325 // Assert IsDefined<> resolves true for Reference<> types.
4326 static_assert(IsDefined<Reference<Bool>>::value, "");
4327 static_assert(IsDefined<Reference<Byte>>::value, "");
4328 static_assert(IsDefined<Reference<SByte>>::value, "");
4329 static_assert(IsDefined<Reference<Short>>::value, "");
4330 static_assert(IsDefined<Reference<UShort>>::value, "");
4331 static_assert(IsDefined<Reference<Int>>::value, "");
4332 static_assert(IsDefined<Reference<Long>>::value, "");
4333 static_assert(IsDefined<Reference<UInt>>::value, "");
4334 static_assert(IsDefined<Reference<Float>>::value, "");
4335
4336 // Assert IsDefined<> resolves true for C types.
4337 static_assert(IsDefined<void>::value, "");
4338 static_assert(IsDefined<bool>::value, "");
4339 static_assert(IsDefined<uint8_t>::value, "");
4340 static_assert(IsDefined<int8_t>::value, "");
4341 static_assert(IsDefined<int16_t>::value, "");
4342 static_assert(IsDefined<uint16_t>::value, "");
4343 static_assert(IsDefined<int32_t>::value, "");
4344 static_assert(IsDefined<uint64_t>::value, "");
4345 static_assert(IsDefined<uint32_t>::value, "");
4346 static_assert(IsDefined<float>::value, "");
4347