• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "gtest/gtest.h"
18 
19 #include <cstdint>
20 #include <type_traits>
21 
22 #include "berberis/assembler/machine_code.h"
23 #include "berberis/assembler/x86_64.h"
24 #include "berberis/base/dependent_false.h"
25 #include "berberis/intrinsics/guest_cpu_flags.h"
26 #include "berberis/intrinsics/intrinsics.h"
27 #include "berberis/intrinsics/macro_assembler.h"
28 #include "berberis/intrinsics/simd_register.h"
29 #include "berberis/intrinsics/vector_intrinsics.h"
30 #include "berberis/runtime_primitives/platform.h"
31 
32 #include "inline_intrinsic.h"
33 
34 namespace berberis {
35 
36 namespace {
37 
38 class RegAlloc {
39  public:
AllocTempReg()40   static x86_64::Assembler::Register AllocTempReg() { return x86_64::Assembler::rax; }
41 
AllocTempSimdReg()42   static x86_64::Assembler::XMMRegister AllocTempSimdReg() { return x86_64::Assembler::xmm0; }
43 };
44 
45 // Helper to split function proto into args and result in the specialization.
46 template <typename Type>
47 class TryInlineIntrinsicWithTestParams {};
48 
49 template <typename Result, typename... Args>
50 class TryInlineIntrinsicWithTestParams<Result (*)(Args...)> {
51  public:
52   template <auto kFunction, typename... ExplicitArgs>
Call(MacroAssembler<x86_64::Assembler> * as,ExplicitArgs &&...args)53   static bool Call(MacroAssembler<x86_64::Assembler>* as, ExplicitArgs&&... args) {
54     return Call<kFunction>(
55         as, std::make_index_sequence<sizeof...(Args)>{}, std::forward<ExplicitArgs>(args)...);
56   }
57 
58  private:
59   template <auto kFunction, std::size_t... I, typename... ExplicitArgs>
Call(MacroAssembler<x86_64::Assembler> * as,std::integer_sequence<std::size_t,I...>,ExplicitArgs &&...args)60   static bool Call(MacroAssembler<x86_64::Assembler>* as,
61                    std::integer_sequence<std::size_t, I...>,
62                    ExplicitArgs&&... args) {
63     RegAlloc reg_alloc;
64     return inline_intrinsic::TryInlineIntrinsic<kFunction>(
65         *as,
66         [&reg_alloc]() { return reg_alloc.AllocTempReg(); },
67         [&reg_alloc]() { return reg_alloc.AllocTempSimdReg(); },
68         AllocResult(),
69         AllocArg<Args, I>(std::tuple{std::forward<ExplicitArgs>(args)...})...);
70   }
71 
72   using Float32 = intrinsics::Float32;
73   using Float64 = intrinsics::Float64;
AllocResult()74   static auto AllocResult() {
75     if constexpr (std::is_same_v<Result, std::tuple<uint32_t>> ||
76                   std::is_same_v<Result, std::tuple<int32_t>> ||
77                   std::is_same_v<Result, std::tuple<uint64_t>> ||
78                   std::is_same_v<Result, std::tuple<int64_t>>) {
79       return x86_64::Assembler::rax;
80     } else if constexpr (std::is_same_v<Result, std::tuple<SIMD128Register, uint32_t>>) {
81       return std::make_tuple(x86_64::Assembler::xmm0, x86_64::Assembler::rax);
82     } else if constexpr (std::is_same_v<Result, std::tuple<SIMD128Register>> ||
83                          std::is_same_v<Result, std::tuple<Float32>> ||
84                          std::is_same_v<Result, std::tuple<Float64>>) {
85       return x86_64::Assembler::xmm0;
86     } else {
87       static_assert(kDependentTypeFalse<Result>);
88     }
89   }
90 
91   template <typename Arg, std::size_t I, typename ExplicitArgs>
AllocArg(ExplicitArgs explicit_args)92   static auto AllocArg(ExplicitArgs explicit_args) {
93     if constexpr (I < std::tuple_size_v<ExplicitArgs>) {
94       return Arg{std::get<I>(explicit_args)};
95     } else if constexpr (std::is_integral_v<Arg>) {
96       Arg value = Arg{};
97       return value;
98     } else if constexpr (std::is_same_v<Arg, SIMD128Register> || std::is_same_v<Arg, Float32> ||
99                          std::is_same_v<Arg, Float64>) {
100       return x86_64::Assembler::xmm0;
101     } else {
102       static_assert(kDependentTypeFalse<Arg>);
103     }
104   }
105 };
106 
107 // Syntax sugar.
108 #define TEST_SUPPORTED(func, ...)                                                       \
109   EXPECT_TRUE((TryInlineIntrinsicWithTestParams<decltype(&func)>::template Call<&func>( \
110       &as __VA_OPT__(, ) __VA_ARGS__)))
111 
112 #define TEST_UNSUPPORTED(func, ...)                                                      \
113   EXPECT_FALSE((TryInlineIntrinsicWithTestParams<decltype(&func)>::template Call<&func>( \
114       &as __VA_OPT__(, ) __VA_ARGS__)))
115 
TEST(InlineIntrinsicRiscv64Test,SupportedInstructions)116 TEST(InlineIntrinsicRiscv64Test, SupportedInstructions) {
117   MachineCode machine_code;
118   MacroAssembler<x86_64::Assembler> as(&machine_code);
119   TEST_SUPPORTED((intrinsics::FMul<intrinsics::Float64>), int8_t{FPFlags::DYN});
120   TEST_UNSUPPORTED((intrinsics::FMul<intrinsics::Float64>), int8_t{FPFlags::RNE});
121   TEST_SUPPORTED((intrinsics::FMul<intrinsics::Float32>), int8_t{FPFlags::DYN});
122   TEST_UNSUPPORTED((intrinsics::FMul<intrinsics::Float32>), int8_t{FPFlags::RNE});
123   TEST_SUPPORTED((intrinsics::FMulHostRounding<intrinsics::Float64>));
124   TEST_SUPPORTED((intrinsics::FAdd<intrinsics::Float64>), int8_t{FPFlags::DYN});
125   TEST_UNSUPPORTED((intrinsics::FAdd<intrinsics::Float64>), int8_t{FPFlags::RNE});
126   TEST_SUPPORTED((intrinsics::FAdd<intrinsics::Float32>), int8_t{FPFlags::DYN});
127   TEST_UNSUPPORTED((intrinsics::FAdd<intrinsics::Float32>), int8_t{FPFlags::RNE});
128   TEST_SUPPORTED((intrinsics::FAddHostRounding<intrinsics::Float64>));
129   TEST_SUPPORTED((intrinsics::FSub<intrinsics::Float64>), int8_t{FPFlags::DYN});
130   TEST_UNSUPPORTED((intrinsics::FSub<intrinsics::Float64>), int8_t{FPFlags::RNE});
131   TEST_SUPPORTED((intrinsics::FSub<intrinsics::Float32>), int8_t{FPFlags::DYN});
132   TEST_UNSUPPORTED((intrinsics::FSub<intrinsics::Float32>), int8_t{FPFlags::RNE});
133   TEST_SUPPORTED((intrinsics::FSubHostRounding<intrinsics::Float64>));
134   TEST_SUPPORTED((intrinsics::FDiv<intrinsics::Float64>), int8_t{FPFlags::DYN});
135   TEST_UNSUPPORTED((intrinsics::FDiv<intrinsics::Float64>), int8_t{FPFlags::RNE});
136   TEST_SUPPORTED((intrinsics::FDiv<intrinsics::Float32>), int8_t{FPFlags::DYN});
137   TEST_UNSUPPORTED((intrinsics::FDiv<intrinsics::Float32>), int8_t{FPFlags::RNE});
138   TEST_SUPPORTED((intrinsics::FDivHostRounding<intrinsics::Float64>));
139   TEST_SUPPORTED((intrinsics::FCvtFloatToInteger<int64_t, intrinsics::Float64>),
140                  int8_t{FPFlags::DYN});
141   TEST_UNSUPPORTED((intrinsics::FCvtFloatToInteger<int64_t, intrinsics::Float64>),
142                    int8_t{FPFlags::RNE});
143   TEST_SUPPORTED((intrinsics::FCvtFloatToIntegerHostRounding<int64_t, intrinsics::Float64>));
144   TEST_SUPPORTED((intrinsics::FCvtFloatToInteger<int64_t, intrinsics::Float32>),
145                  int8_t{FPFlags::DYN});
146   TEST_UNSUPPORTED((intrinsics::FCvtFloatToInteger<int64_t, intrinsics::Float32>),
147                    int8_t{FPFlags::RNE});
148   TEST_SUPPORTED((intrinsics::FCvtFloatToIntegerHostRounding<int64_t, intrinsics::Float32>));
149   TEST_SUPPORTED((intrinsics::FCvtFloatToInteger<int32_t, intrinsics::Float64>),
150                  int8_t{FPFlags::DYN});
151   TEST_UNSUPPORTED((intrinsics::FCvtFloatToInteger<int32_t, intrinsics::Float64>),
152                    int8_t{FPFlags::RNE});
153   TEST_SUPPORTED((intrinsics::FCvtFloatToIntegerHostRounding<int32_t, intrinsics::Float64>));
154   TEST_SUPPORTED((intrinsics::FCvtFloatToInteger<int32_t, intrinsics::Float32>),
155                  int8_t{FPFlags::DYN});
156   TEST_UNSUPPORTED((intrinsics::FCvtFloatToInteger<int32_t, intrinsics::Float32>),
157                    int8_t{FPFlags::RNE});
158   TEST_SUPPORTED((intrinsics::FCvtFloatToIntegerHostRounding<int32_t, intrinsics::Float32>));
159 }
160 
161 #undef TEST_SUPPORTED
162 #undef TEST_UNSUPPORTED
163 
164 }  // namespace
165 
166 }  // namespace berberis
167