• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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_INTRINSICS_ARGS_H_
18 #define BERBERIS_INTRINSICS_INTRINSICS_ARGS_H_
19 
20 #include <cstddef>
21 #include <cstdio>
22 
23 #include "berberis/base/checks.h"
24 
25 namespace berberis {
26 
27 // Helper classes for the EmbedAsmInstruction "construction class".
28 //
29 // Constructor of this class takes prescribed arguments from IR insn and
30 // calls a single assembler [macro]instruction.  It creates required scratch
31 // register allocations and proper MOVs to preserve semantic of the IR insn.
32 //
33 // Name of a helper class describes argument of the assembler
34 // [macro]instruction.
35 //
36 // You could find many examples in intrinsics_x86.h file but EmbedAsmInstruction
37 // is not x86-specific: in theory it should work with any MachineIRBuilder.
38 //
39 // Each argument must by of one of the following types:
40 //
41 //   InArg<N> - argument comes from <N>th source of the IR insn.
42 //              Must be "use" argument of the assembler [macro]instruction.
43 //              Note: please don't use this argument for specific register
44 //              classes (such as "RDX" or "RCX").  If one operation returns
45 //              result, e.g., in "RCX" and another one accepts it in "RCX"
46 //              then register allocator will not be able to satisfy such
47 //              requirements.  Use InTmpArg<N> for such instructions.
48 //
49 //   OutArg<N> - argument comes from <N>th destination of the IR insn.
50 //               Must be "def" or "def_early_clobber" argument of the assembler
51 //               [macro]instruction.
52 //
53 //   OutTmpArg<N> - argument is copied from temporary register to <N>th
54 //                  destination of the IR insn.
55 //                  Must be "def" or "def_early_clobber" argument of the
56 //                  assembler [macro]instruction.
57 //
58 //   InOutArg<N, M> - argument is copied from <N>th source of the IR insn to
59 //                    <M>th destination of the IR insn, then is passed it to the
60 //                    [macro]instruction.
61 //                    Must be "use_def" argument of the assembler
62 //                    [macro]instruction.
63 //
64 //   InOutTmpArg<N, M> - argument is copied from <N>th source of the IR insn to
65 //                       temporary register, then it's passed to the
66 //                       [macro]instruction. Result is copied to the
67 //                       <M>th destination of the IR insn.
68 //                       Must be "use_def" argument of the assembler
69 //                       [macro]instruction.
70 //
71 //   InTmpArg<N> - argument is copied from <N>th source of the IR insn to the
72 //                 temporary register, then is passed to the [macro]instruction.
73 //                 Must be "use_def" argument of the assembler
74 //                 [macro]instruction.
75 //
76 //   ImmArg<N, uintXX_t> - argument is 8bit/16bit/32bit/64bit immediate and
77 //                         comes as <N>th source of IR insn.
78 //
79 //   TmpArg - argument is temporary register allocated for the
80 //            [macro]instruction.
81 //
82 // TODO(khim): investigate feasibility of adding unconditional copying of
83 // arguments and results. This way we could remove classes InOutTmpArg/InTmpArg
84 // and, more importantly, make sure InArg vs InTmpArg mixup will not lead to
85 // hard to debug errors.
86 
87 struct ArgInfo {
88  public:
89   enum ArgType {
90     IN_ARG,
91     IN_TMP_ARG,
92     OUT_ARG,
93     OUT_TMP_ARG,
94     IN_OUT_ARG,
95     IN_OUT_TMP_ARG,
96     TMP_ARG,
97     IMM_ARG
98   } arg_type;
HaveInputArgInfo99   friend constexpr bool HaveInput(const ArgInfo& arg) {
100     return arg.arg_type == ArgInfo::IN_ARG ||
101            arg.arg_type == ArgInfo::IN_TMP_ARG ||
102            arg.arg_type == ArgInfo::IN_OUT_ARG ||
103            arg.arg_type == ArgInfo::IN_OUT_TMP_ARG;
104   }
HaveOutputArgInfo105   friend constexpr bool HaveOutput(const ArgInfo& arg) {
106     return arg.arg_type == ArgInfo::IN_OUT_ARG ||
107            arg.arg_type == ArgInfo::IN_OUT_TMP_ARG ||
108            arg.arg_type == OUT_ARG ||
109            arg.arg_type == OUT_TMP_ARG;
110   }
IsImmediateArgInfo111   friend constexpr bool IsImmediate(const ArgInfo& arg) {
112     return arg.arg_type == IMM_ARG;
113   }
IsTemporaryArgInfo114   friend constexpr bool IsTemporary(const ArgInfo& arg) {
115     return arg.arg_type == TMP_ARG;
116   }
117   const int from = 0;
118   const int to = 0;
119 };
120 
121 template <int N, typename RegisterClass = void, typename Usage = void>
122 class InArg;
123 
124 template <int N, typename RegisterClass = void, typename Usage = void>
125 class OutArg;
126 
127 template <int N, typename RegisterClass = void, typename Usage = void>
128 class OutTmpArg;
129 
130 template <int N, int M, typename RegisterClass = void, typename Usage = void>
131 class InOutArg;
132 
133 template <int N, int M, typename RegisterClass = void, typename Usage = void>
134 class InOutTmpArg;
135 
136 template <int N, typename RegisterClass = void, typename Usage = void>
137 class InTmpArg;
138 
139 template <int N, typename ImmType, typename ImmediateClass = void>
140 class ImmArg;
141 
142 template <typename RegisterClass = void, typename Usage = void>
143 class TmpArg;
144 
145 template <typename ArgInfo>
146 class ArgTraits;
147 
148 template <int N, typename RegisterClassType, typename UsageType>
149 class ArgTraits<InArg<N, RegisterClassType, UsageType>> {
150  public:
151   using Class = RegisterClassType;
152   using RegisterClass = RegisterClassType;
153   using Usage = UsageType;
154   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::IN_ARG, .from = N};
155 };
156 
157 template <int N, typename RegisterClassType, typename UsageType>
158 class ArgTraits<OutArg<N, RegisterClassType, UsageType>> {
159  public:
160   using Class = RegisterClassType;
161   using RegisterClass = RegisterClassType;
162   using Usage = UsageType;
163   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::OUT_ARG, .to = N};
164 };
165 
166 template <int N, typename RegisterClassType, typename UsageType>
167 class ArgTraits<OutTmpArg<N, RegisterClassType, UsageType>> {
168  public:
169   using Class = RegisterClassType;
170   using RegisterClass = RegisterClassType;
171   using Usage = UsageType;
172   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::OUT_TMP_ARG, .to = N};
173 };
174 
175 template <int N, int M, typename RegisterClassType, typename UsageType>
176 class ArgTraits<InOutArg<N, M, RegisterClassType, UsageType>> {
177  public:
178   using Class = RegisterClassType;
179   using RegisterClass = RegisterClassType;
180   using Usage = UsageType;
181   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::IN_OUT_ARG, .from = N, .to = M};
182 };
183 
184 template <int N, int M, typename RegisterClassType, typename UsageType>
185 class ArgTraits<InOutTmpArg<N, M, RegisterClassType, UsageType>> {
186  public:
187   using Class = RegisterClassType;
188   using RegisterClass = RegisterClassType;
189   using Usage = UsageType;
190   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::IN_OUT_TMP_ARG, .from = N, .to = M};
191 };
192 
193 template <int N, typename RegisterClassType, typename UsageType>
194 class ArgTraits<InTmpArg<N, RegisterClassType, UsageType>> {
195  public:
196   using Class = RegisterClassType;
197   using RegisterClass = RegisterClassType;
198   using Usage = UsageType;
199   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::IN_TMP_ARG, .from = N};
200 };
201 
202 template <int N, typename ImmType, typename ImmediateClassType>
203 class ArgTraits<ImmArg<N, ImmType, ImmediateClassType>> {
204  public:
205   using Class = ImmediateClassType;
206   using ImmediateClass = ImmediateClassType;
207   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::IMM_ARG, .from = N};
208 };
209 
210 template <typename RegisterClassType, typename UsageType>
211 class ArgTraits<TmpArg<RegisterClassType, UsageType>> {
212  public:
213   using Class = RegisterClassType;
214   using RegisterClass = RegisterClassType;
215   using Usage = UsageType;
216   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::TMP_ARG};
217 };
218 
219 // We couldn't use standard "throw std::logic_error(...)" approach here because that code is
220 // compiled with -fno-exceptions.  Thankfully printf(...) produces very similar error messages.
221 //
222 // See https://stackoverflow.com/questions/8626055/c11-static-assert-within-constexpr-function
223 // is you need an explanation for how basic technique works.
224 template <typename MachineInsn, int arguments_count>
IsCompatible(const ArgInfo * arguments)225 constexpr bool IsCompatible(const ArgInfo* arguments) {
226   int reg_arguments = 0;
227   for (size_t argument = 0; argument < arguments_count; ++argument) {
228     if (arguments[argument].arg_type != ArgInfo::IMM_ARG) {
229       if ((arguments[argument].arg_type == ArgInfo::IN_ARG) &&
230           MachineInsn::RegKindAt(reg_arguments).IsDef()) {
231         fprintf(stderr, "Incorrect use of InArg for argument %d", argument);
232         return false;
233       } else if ((arguments[argument].arg_type == ArgInfo::IN_TMP_ARG) &&
234                  !MachineInsn::RegKindAt(reg_arguments).IsDef() &&
235                  !IsFixedRegClass(MachineInsn::RegKindAt(reg_arguments).RegClass())) {
236         fprintf(stderr, "Inefficient use of InTmpArg for argument %d", argument);
237         return false;
238       } else if ((arguments[argument].arg_type == ArgInfo::OUT_ARG) &&
239                  IsFixedRegClass(MachineInsn::RegKindAt(reg_arguments).RegClass())) {
240         fprintf(stderr, "Incorrect use of OutArg for argument %d", argument);
241         return false;
242       } else if ((arguments[argument].arg_type == ArgInfo::OUT_TMP_ARG) &&
243                  !IsFixedRegClass(MachineInsn::RegKindAt(reg_arguments).RegClass())) {
244         fprintf(stderr, "Inefficient use of OutTmpArg for argument %d", argument);
245         return false;
246       } else if ((arguments[argument].arg_type == ArgInfo::IN_OUT_ARG) &&
247                  IsFixedRegClass(MachineInsn::RegKindAt(reg_arguments).RegClass())) {
248         fprintf(stderr, "Incorrect use of InOutArg for argument %d", argument);
249         return false;
250       } else if ((arguments[argument].arg_type == ArgInfo::IN_OUT_TMP_ARG) &&
251                  !IsFixedRegClass(MachineInsn::RegKindAt(reg_arguments).RegClass())) {
252         fprintf(stderr, "Inefficient use of InOutTmpArg for argument %d", argument);
253         return false;
254       }
255       if (HaveInput(arguments[argument]) &&
256           !MachineInsn::RegKindAt(reg_arguments).IsInput()) {
257         fprintf(stderr, "Argument %d does not accept input!", argument);
258         return false;
259       } else if (!HaveInput(arguments[argument]) &&
260                  MachineInsn::RegKindAt(reg_arguments).IsInput()) {
261         fprintf(stderr, "Argument %d requires valid input!", argument);
262         return false;
263       }
264       ++reg_arguments;
265     }
266   }
267   if (MachineInsn::NumRegOperands() != reg_arguments) {
268     fprintf(stderr,
269             "expected %d arguments, got %d arguments",
270             MachineInsn::NumRegOperands(),
271             reg_arguments);
272     return false;
273   }
274   return true;
275 }
276 
277 template <typename MachineInsn, typename... Args>
IsCompatible()278 constexpr bool IsCompatible() {
279   const ArgInfo arguments[] = {ArgTraits<Args>::arg_info...};
280   // Note: we couldn't pass arguments as an array into IsCompatible by reference
281   // because this would cause compilation error in case where we have no arguments.
282   //
283   // Pass pointer and element count instead.
284   return IsCompatible<MachineInsn, sizeof...(Args)>(arguments);
285 }
286 
287 template <typename MachineIRBuilder, typename Arg>
288 class ArgGetterSetter;
289 
290 template <typename Instruction, typename... Args>
291 class EmbedAsmInstruction {
292  public:
293   template <typename MachineIRBuilder, typename IntrinsicInsn>
EmbedAsmInstruction(MachineIRBuilder * builder,const IntrinsicInsn * insn)294   EmbedAsmInstruction(MachineIRBuilder* builder, const IntrinsicInsn* insn) {
295     static_assert(IsCompatible<Instruction, Args...>(), "Incompatible intrinsic embedding");
296     builder->template Gen<Instruction>(ArgGetterSetter<MachineIRBuilder, Args>(builder, insn)...);
297   }
298 };
299 
300 }  // namespace berberis
301 
302 #endif  // BERBERIS_INTRINSICS_INTRINSICS_ARGS_H_
303