1 /*
2  * Copyright (C) 2024 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 #ifndef BERBERIS_INTRINSICS_COMMON_INTRINSICS_BINDINGS_H_
18 #define BERBERIS_INTRINSICS_COMMON_INTRINSICS_BINDINGS_H_
19 
20 #include <cstdint>
21 
22 #include "berberis/base/dependent_false.h"
23 #include "berberis/intrinsics/intrinsics_args.h"
24 #include "berberis/intrinsics/type_traits.h"
25 
26 namespace berberis::intrinsics::bindings {
27 
28 class FLAGS {
29  public:
30   static constexpr bool kIsImmediate = false;
31   static constexpr bool kIsImplicitReg = true;
32   static constexpr char kAsRegister = 0;
33   template <typename MachineInsnArch>
34   static constexpr auto kRegClass = MachineInsnArch::kFLAGS;
35 };
36 
37 class Mem8 {
38  public:
39   using Type = uint8_t;
40   static constexpr bool kIsImmediate = false;
41   static constexpr char kAsRegister = 'm';
42 };
43 
44 class Mem16 {
45  public:
46   using Type = uint16_t;
47   static constexpr bool kIsImmediate = false;
48   static constexpr char kAsRegister = 'm';
49 };
50 
51 class Mem32 {
52  public:
53   using Type = uint32_t;
54   static constexpr bool kIsImmediate = false;
55   static constexpr char kAsRegister = 'm';
56 };
57 
58 class Mem64 {
59  public:
60   using Type = uint64_t;
61   static constexpr bool kIsImmediate = false;
62   static constexpr char kAsRegister = 'm';
63 };
64 
65 // Tag classes. They are never instantioned, only used as tags to pass information about
66 // bindings.
67 class Def;
68 class DefEarlyClobber;
69 class Use;
70 class UseDef;
71 
72 template <typename Tag, typename MachineRegKind>
ToRegKind()73 constexpr auto ToRegKind() {
74   if constexpr (std::is_same_v<Tag, Def>) {
75     return MachineRegKind::kDef;
76   } else if constexpr (std::is_same_v<Tag, DefEarlyClobber>) {
77     return MachineRegKind::kDefEarlyClobber;
78   } else if constexpr (std::is_same_v<Tag, Use>) {
79     return MachineRegKind::kUse;
80   } else if constexpr (std::is_same_v<Tag, UseDef>) {
81     return MachineRegKind::kUseDef;
82   } else {
83     static_assert(kDependentTypeFalse<Tag>);
84   }
85 }
86 
87 template <typename Tag, typename MachineRegKind>
88 inline constexpr auto kRegKind = ToRegKind<Tag, MachineRegKind>();
89 
90 // Tag classes. They are never instantioned, only used as tags to pass information about
91 // bindings.
92 class NoCPUIDRestriction;  // All CPUs have at least “no CPUID restriction” mode.
93 
94 // Tag classes. They are never instantioned, only used as tags to pass information about
95 // bindings.
96 class NoNansOperation;
97 class PreciseNanOperationsHandling;
98 class ImpreciseNanOperationsHandling;
99 
100 template <auto kIntrinsicTemplateName,
101           auto kMacroInstructionTemplateName,
102           auto kMnemo,
103           typename GetOpcode,
104           typename CPUIDRestrictionTemplateValue,
105           typename PreciseNanOperationsHandlingTemplateValue,
106           bool kSideEffectsTemplateValue,
107           typename... Types>
108 class AsmCallInfo;
109 
110 template <auto kIntrinsicTemplateName,
111           auto kMacroInstructionTemplateName,
112           auto kMnemo,
113           typename GetOpcode,
114           typename CPUIDRestrictionTemplateValue,
115           typename PreciseNanOperationsHandlingTemplateValue,
116           bool kSideEffectsTemplateValue,
117           typename... InputArgumentsTypes,
118           typename... OutputArgumentsTypes,
119           typename... BindingsTypes>
120 class AsmCallInfo<kIntrinsicTemplateName,
121                   kMacroInstructionTemplateName,
122                   kMnemo,
123                   GetOpcode,
124                   CPUIDRestrictionTemplateValue,
125                   PreciseNanOperationsHandlingTemplateValue,
126                   kSideEffectsTemplateValue,
127                   std::tuple<InputArgumentsTypes...>,
128                   std::tuple<OutputArgumentsTypes...>,
129                   BindingsTypes...>
130     final {
131  public:
132   static constexpr auto kIntrinsic = kIntrinsicTemplateName;
133   static constexpr auto kMacroInstruction = kMacroInstructionTemplateName;
134   // TODO(b/260725458): Use lambda template argument after C++20 becomes available.
135   template <typename Opcode>
136   static constexpr auto kOpcode = GetOpcode{}.template operator()<Opcode>();
137   using CPUIDRestriction = CPUIDRestrictionTemplateValue;
138   using PreciseNanOperationsHandling = PreciseNanOperationsHandlingTemplateValue;
139   static constexpr bool kSideEffects = kSideEffectsTemplateValue;
140   static constexpr const char* InputArgumentsTypeNames[] = {
141       TypeTraits<InputArgumentsTypes>::kName...};
142   static constexpr const char* OutputArgumentsTypeNames[] = {
143       TypeTraits<OutputArgumentsTypes>::kName...};
144   template <typename Callback, typename... Args>
ProcessBindings(Callback && callback,Args &&...args)145   constexpr static void ProcessBindings(Callback&& callback, Args&&... args) {
146     (callback(ArgTraits<BindingsTypes>(), std::forward<Args>(args)...), ...);
147   }
148   template <typename Callback, typename... Args>
VerifyBindings(Callback && callback,Args &&...args)149   constexpr static bool VerifyBindings(Callback&& callback, Args&&... args) {
150     return (callback(ArgTraits<BindingsTypes>(), std::forward<Args>(args)...) && ...);
151   }
152   template <typename Callback, typename... Args>
MakeTuplefromBindings(Callback && callback,Args &&...args)153   constexpr static auto MakeTuplefromBindings(Callback&& callback, Args&&... args) {
154     return std::tuple_cat(callback(ArgTraits<BindingsTypes>(), std::forward<Args>(args)...)...);
155   }
156   using InputArguments = std::tuple<InputArgumentsTypes...>;
157   using OutputArguments = std::tuple<OutputArgumentsTypes...>;
158   using Bindings = std::tuple<BindingsTypes...>;
159   using IntrinsicType = std::conditional_t<std::tuple_size_v<OutputArguments> == 0,
160                                            void (*)(InputArgumentsTypes...),
161                                            OutputArguments (*)(InputArgumentsTypes...)>;
162   template <template <typename, auto, auto, typename...> typename MachineInsnType,
163             template <typename...>
164             typename ConstructorArgs,
165             typename Opcode>
166   using MachineInsn = MachineInsnType<AsmCallInfo,
167                                       kMnemo,
168                                       kOpcode<Opcode>,
169                                       ConstructorArgs<BindingsTypes...>,
170                                       BindingsTypes...>;
171 };
172 
173 }  // namespace berberis::intrinsics::bindings
174 
175 #endif  // BERBERIS_INTRINSICS_COMMON_INTRINSICS_BINDINGS_H_
176