1 // Copyright 2022 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 "Reactor.hpp"
16 #include "SIMD.hpp"
17
18 #include "gtest/gtest.h"
19
20 using namespace rr;
21
testName()22 static std::string testName()
23 {
24 auto info = ::testing::UnitTest::GetInstance()->current_test_info();
25 return std::string{ info->test_suite_name() } + "_" + info->name();
26 }
27
TEST(ReactorSIMD,Add)28 TEST(ReactorSIMD, Add)
29 {
30 ASSERT_GE(SIMD::Width, 4);
31
32 constexpr int arrayLength = 1024;
33
34 FunctionT<void(int *, int *, int *)> function;
35 {
36 Pointer<Int> r = Pointer<Int>(function.Arg<0>());
37 Pointer<Int> a = Pointer<Int>(function.Arg<1>());
38 Pointer<Int> b = Pointer<Int>(function.Arg<2>());
39
40 For(Int i = 0, i < arrayLength, i += SIMD::Width)
41 {
42 SIMD::Int x = *Pointer<SIMD::Int>(&a[i]);
43 SIMD::Int y = *Pointer<SIMD::Int>(&b[i]);
44
45 SIMD::Int z = x + y;
46
47 *Pointer<SIMD::Int>(&r[i]) = z;
48 }
49 }
50
51 auto routine = function(testName().c_str());
52
53 int r[arrayLength] = {};
54 int a[arrayLength];
55 int b[arrayLength];
56
57 for(int i = 0; i < arrayLength; i++)
58 {
59 a[i] = i;
60 b[i] = arrayLength + i;
61 }
62
63 routine(r, a, b);
64
65 for(int i = 0; i < arrayLength; i++)
66 {
67 ASSERT_EQ(r[i], arrayLength + 2 * i);
68 }
69 }
70
TEST(ReactorSIMD,Broadcast)71 TEST(ReactorSIMD, Broadcast)
72 {
73 FunctionT<void(int *, int)> function;
74 {
75 Pointer<Int> r = Pointer<Int>(function.Arg<0>());
76 Int a = function.Arg<1>();
77
78 SIMD::Int x = a;
79
80 *Pointer<SIMD::Int>(r) = x;
81 }
82
83 auto routine = function(testName().c_str());
84
85 std::vector<int> r(SIMD::Width);
86
87 for(int a = -2; a <= 2; a++)
88 {
89 routine(r.data(), a);
90
91 for(int i = 0; i < SIMD::Width; i++)
92 {
93 ASSERT_EQ(r[i], a);
94 }
95 }
96 }
97
TEST(ReactorSIMD,InsertExtract128)98 TEST(ReactorSIMD, InsertExtract128)
99 {
100 FunctionT<void(int *, int *)> function;
101 {
102 Pointer<Int> r = Pointer<Int>(function.Arg<0>());
103 Pointer<Int> a = Pointer<Int>(function.Arg<1>());
104
105 SIMD::Int x = *Pointer<SIMD::Int>(a);
106 SIMD::Int y = *Pointer<SIMD::Int>(r);
107
108 x -= y;
109
110 for(int i = 0; i < SIMD::Width / 4; i++)
111 {
112 y = Insert128(y, Extract128(x, i) << (i + 1), i);
113 }
114
115 *Pointer<SIMD::Int>(r) = y;
116 }
117
118 auto routine = function(testName().c_str());
119
120 std::vector<int> r(SIMD::Width);
121 std::vector<int> a(SIMD::Width);
122
123 for(int i = 0; i < SIMD::Width; i++)
124 {
125 r[i] = 0;
126 a[i] = 1 + i;
127 }
128
129 routine(r.data(), a.data());
130
131 for(int i = 0; i < SIMD::Width; i++)
132 {
133 ASSERT_EQ(r[i], a[i] << (i / 4 + 1));
134 }
135 }
136
TEST(ReactorSIMD,Intrinsics_Scatter)137 TEST(ReactorSIMD, Intrinsics_Scatter)
138 {
139 Function<Void(Pointer<Float> base, Pointer<SIMD::Float> val, Pointer<SIMD::Int> offsets)> function;
140 {
141 Pointer<Float> base = function.Arg<0>();
142 Pointer<SIMD::Float> val = function.Arg<1>();
143 Pointer<SIMD::Int> offsets = function.Arg<2>();
144
145 SIMD::Int mask = ~0;
146 unsigned int alignment = 1;
147 Scatter(base, *val, *offsets, mask, alignment);
148 }
149
150 std::vector<float> buffer(10 + 10 * SIMD::Width);
151 std::vector<int> offsets(SIMD::Width);
152 std::vector<float> val(SIMD::Width);
153
154 for(int i = 0; i < SIMD::Width; i++)
155 {
156 offsets[i] = (3 + 7 * i) * sizeof(float);
157 val[i] = 13.0f + 17.0f * i;
158 }
159
160 auto routine = function(testName().c_str());
161 auto entry = (void (*)(float *, float *, int *))routine->getEntry();
162
163 entry(buffer.data(), val.data(), offsets.data());
164
165 for(int i = 0; i < SIMD::Width; i++)
166 {
167 EXPECT_EQ(buffer[offsets[i] / sizeof(float)], val[i]);
168 }
169 }
170
TEST(ReactorSIMD,Intrinsics_Gather)171 TEST(ReactorSIMD, Intrinsics_Gather)
172 {
173 Function<Void(Pointer<Float> base, Pointer<SIMD::Int> offsets, Pointer<SIMD::Float> result)> function;
174 {
175 Pointer<Float> base = function.Arg<0>();
176 Pointer<SIMD::Int> offsets = function.Arg<1>();
177 Pointer<SIMD::Float> result = function.Arg<2>();
178
179 SIMD::Int mask = ~0;
180 unsigned int alignment = 1;
181 bool zeroMaskedLanes = true;
182 *result = Gather(base, *offsets, mask, alignment, zeroMaskedLanes);
183 }
184
185 std::vector<float> buffer(10 + 10 * SIMD::Width);
186 std::vector<int> offsets(SIMD::Width);
187
188 std::vector<float> val(SIMD::Width);
189
190 for(int i = 0; i < SIMD::Width; i++)
191 {
192 offsets[i] = (3 + 7 * i) * sizeof(float);
193 val[i] = 13.0f + 17.0f * i;
194
195 buffer[offsets[i] / sizeof(float)] = val[i];
196 }
197
198 auto routine = function(testName().c_str());
199 auto entry = (void (*)(float *, int *, float *))routine->getEntry();
200
201 std::vector<float> result(SIMD::Width);
202 entry(buffer.data(), offsets.data(), result.data());
203
204 for(int i = 0; i < SIMD::Width; i++)
205 {
206 EXPECT_EQ(result[i], val[i]);
207 }
208 }
209