| // 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<Float4> val, Pointer<Int4> offsets)> function; |
| { |
| Pointer<Float> base = function.Arg<0>(); |
| Pointer<Float4> val = function.Arg<1>(); |
| Pointer<Int4> offsets = function.Arg<2>(); |
| |
| auto mask = Int4(~0, ~0, ~0, ~0); |
| unsigned int alignment = 1; |
| Scatter(base, *val, *offsets, mask, alignment); |
| } |
| |
| float buffer[16] = { 0 }; |
| |
| constexpr auto elemSize = sizeof(buffer[0]); |
| |
| int offsets[] = { |
| 1 * elemSize, |
| 6 * elemSize, |
| 11 * elemSize, |
| 13 * elemSize |
| }; |
| |
| float val[4] = { 10, 60, 110, 130 }; |
| |
| auto routine = function(testName().c_str()); |
| auto entry = (void (*)(float *, float *, int *))routine->getEntry(); |
| |
| entry(buffer, val, offsets); |
| |
| EXPECT_EQ(buffer[offsets[0] / sizeof(buffer[0])], 10); |
| EXPECT_EQ(buffer[offsets[1] / sizeof(buffer[0])], 60); |
| EXPECT_EQ(buffer[offsets[2] / sizeof(buffer[0])], 110); |
| EXPECT_EQ(buffer[offsets[3] / sizeof(buffer[0])], 130); |
| } |
| |
| TEST(ReactorUnitTests, Intrinsics_Gather) |
| { |
| Function<Void(Pointer<Float> base, Pointer<Int4> offsets, Pointer<Float4> result)> function; |
| { |
| Pointer<Float> base = function.Arg<0>(); |
| Pointer<Int4> offsets = function.Arg<1>(); |
| Pointer<Float4> result = function.Arg<2>(); |
| |
| auto mask = Int4(~0, ~0, ~0, ~0); |
| unsigned int alignment = 1; |
| bool zeroMaskedLanes = true; |
| *result = Gather(base, *offsets, mask, alignment, zeroMaskedLanes); |
| } |
| |
| float buffer[] = { |
| 0, 10, 20, 30, |
| 40, 50, 60, 70, |
| 80, 90, 100, 110, |
| 120, 130, 140, 150 |
| }; |
| |
| constexpr auto elemSize = sizeof(buffer[0]); |
| |
| int offsets[] = { |
| 1 * elemSize, |
| 6 * elemSize, |
| 11 * elemSize, |
| 13 * elemSize |
| }; |
| |
| auto routine = function(testName().c_str()); |
| auto entry = (void (*)(float *, int *, float *))routine->getEntry(); |
| |
| float result[4] = {}; |
| entry(buffer, offsets, result); |
| |
| EXPECT_EQ(result[0], 10); |
| EXPECT_EQ(result[1], 60); |
| EXPECT_EQ(result[2], 110); |
| EXPECT_EQ(result[3], 130); |
| } |