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