| // Copyright 2022 The SwiftShader Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "Reactor.hpp" |
| #include "SIMD.hpp" |
| |
| #include "gtest/gtest.h" |
| |
| using namespace rr; |
| |
| static std::string testName() |
| { |
| auto info = ::testing::UnitTest::GetInstance()->current_test_info(); |
| return std::string{ info->test_suite_name() } + "_" + info->name(); |
| } |
| |
| TEST(ReactorSIMD, Add) |
| { |
| ASSERT_GE(SIMD::Width, 4); |
| |
| constexpr int arrayLength = 1024; |
| |
| FunctionT<void(int *, int *, int *)> function; |
| { |
| Pointer<Int> r = Pointer<Int>(function.Arg<0>()); |
| Pointer<Int> a = Pointer<Int>(function.Arg<1>()); |
| Pointer<Int> b = Pointer<Int>(function.Arg<2>()); |
| |
| For(Int i = 0, i < arrayLength, i += SIMD::Width) |
| { |
| SIMD::Int x = *Pointer<SIMD::Int>(&a[i]); |
| SIMD::Int y = *Pointer<SIMD::Int>(&b[i]); |
| |
| SIMD::Int z = x + y; |
| |
| *Pointer<SIMD::Int>(&r[i]) = z; |
| } |
| } |
| |
| auto routine = function(testName().c_str()); |
| |
| int r[arrayLength] = {}; |
| int a[arrayLength]; |
| int b[arrayLength]; |
| |
| for(int i = 0; i < arrayLength; i++) |
| { |
| a[i] = i; |
| b[i] = arrayLength + i; |
| } |
| |
| routine(r, a, b); |
| |
| for(int i = 0; i < arrayLength; i++) |
| { |
| ASSERT_EQ(r[i], arrayLength + 2 * i); |
| } |
| } |
| |
| TEST(ReactorSIMD, Broadcast) |
| { |
| FunctionT<void(int *, int)> function; |
| { |
| Pointer<Int> r = Pointer<Int>(function.Arg<0>()); |
| Int a = function.Arg<1>(); |
| |
| SIMD::Int x = a; |
| |
| *Pointer<SIMD::Int>(r) = x; |
| } |
| |
| auto routine = function(testName().c_str()); |
| |
| std::vector<int> r(SIMD::Width); |
| |
| for(int a = -2; a <= 2; a++) |
| { |
| routine(r.data(), a); |
| |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| ASSERT_EQ(r[i], a); |
| } |
| } |
| } |
| |
| TEST(ReactorSIMD, InsertExtract128) |
| { |
| FunctionT<void(int *, int *)> function; |
| { |
| Pointer<Int> r = Pointer<Int>(function.Arg<0>()); |
| Pointer<Int> a = Pointer<Int>(function.Arg<1>()); |
| |
| SIMD::Int x = *Pointer<SIMD::Int>(a); |
| SIMD::Int y = *Pointer<SIMD::Int>(r); |
| |
| x -= y; |
| |
| for(int i = 0; i < SIMD::Width / 4; i++) |
| { |
| y = Insert128(y, Extract128(x, i) << (i + 1), i); |
| } |
| |
| *Pointer<SIMD::Int>(r) = y; |
| } |
| |
| auto routine = function(testName().c_str()); |
| |
| std::vector<int> r(SIMD::Width); |
| std::vector<int> a(SIMD::Width); |
| |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| r[i] = 0; |
| a[i] = 1 + i; |
| } |
| |
| routine(r.data(), a.data()); |
| |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| ASSERT_EQ(r[i], a[i] << (i / 4 + 1)); |
| } |
| } |
| |
| TEST(ReactorSIMD, Intrinsics_Scatter) |
| { |
| Function<Void(Pointer<Float> base, Pointer<SIMD::Float> val, Pointer<SIMD::Int> offsets)> function; |
| { |
| Pointer<Float> base = function.Arg<0>(); |
| Pointer<SIMD::Float> val = function.Arg<1>(); |
| Pointer<SIMD::Int> offsets = function.Arg<2>(); |
| |
| SIMD::Int mask = ~0; |
| unsigned int alignment = 1; |
| Scatter(base, *val, *offsets, mask, alignment); |
| } |
| |
| std::vector<float> buffer(10 + 10 * SIMD::Width); |
| std::vector<int> offsets(SIMD::Width); |
| std::vector<float> val(SIMD::Width); |
| |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| offsets[i] = (3 + 7 * i) * sizeof(float); |
| val[i] = 13.0f + 17.0f * i; |
| } |
| |
| auto routine = function(testName().c_str()); |
| auto entry = (void (*)(float *, float *, int *))routine->getEntry(); |
| |
| entry(buffer.data(), val.data(), offsets.data()); |
| |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| EXPECT_EQ(buffer[offsets[i] / sizeof(float)], val[i]); |
| } |
| } |
| |
| TEST(ReactorSIMD, Intrinsics_Gather) |
| { |
| Function<Void(Pointer<Float> base, Pointer<SIMD::Int> offsets, Pointer<SIMD::Float> result)> function; |
| { |
| Pointer<Float> base = function.Arg<0>(); |
| Pointer<SIMD::Int> offsets = function.Arg<1>(); |
| Pointer<SIMD::Float> result = function.Arg<2>(); |
| |
| SIMD::Int mask = ~0; |
| unsigned int alignment = 1; |
| bool zeroMaskedLanes = true; |
| *result = Gather(base, *offsets, mask, alignment, zeroMaskedLanes); |
| } |
| |
| std::vector<float> buffer(10 + 10 * SIMD::Width); |
| std::vector<int> offsets(SIMD::Width); |
| |
| std::vector<float> val(SIMD::Width); |
| |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| offsets[i] = (3 + 7 * i) * sizeof(float); |
| val[i] = 13.0f + 17.0f * i; |
| |
| buffer[offsets[i] / sizeof(float)] = val[i]; |
| } |
| |
| auto routine = function(testName().c_str()); |
| auto entry = (void (*)(float *, int *, float *))routine->getEntry(); |
| |
| std::vector<float> result(SIMD::Width); |
| entry(buffer.data(), offsets.data(), result.data()); |
| |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| EXPECT_EQ(result[i], val[i]); |
| } |
| } |