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