• Home
  • History
  • Annotate
  • 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