1//===--- AMDGPU.h - Declare AMDGPU target feature support -------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file declares AMDGPU TargetInfo objects.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_LIB_BASIC_TARGETS_AMDGPU_H
14#define LLVM_CLANG_LIB_BASIC_TARGETS_AMDGPU_H
15
16#include "clang/Basic/TargetID.h"
17#include "clang/Basic/TargetInfo.h"
18#include "clang/Basic/TargetOptions.h"
19#include "llvm/ADT/StringSet.h"
20#include "llvm/ADT/Triple.h"
21#include "llvm/Support/Compiler.h"
22#include "llvm/Support/TargetParser.h"
23#include <optional>
24
25namespace clang {
26namespace targets {
27
28class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo {
29
30  static const char *const GCCRegNames[];
31
32  enum AddrSpace {
33    Generic = 0,
34    Global = 1,
35    Local = 3,
36    Constant = 4,
37    Private = 5
38  };
39  static const LangASMap AMDGPUDefIsGenMap;
40  static const LangASMap AMDGPUDefIsPrivMap;
41
42  llvm::AMDGPU::GPUKind GPUKind;
43  unsigned GPUFeatures;
44  unsigned WavefrontSize;
45
46  /// Target ID is device name followed by optional feature name postfixed
47  /// by plus or minus sign delimitted by colon, e.g. gfx908:xnack+:sramecc-.
48  /// If the target ID contains feature+, map it to true.
49  /// If the target ID contains feature-, map it to false.
50  /// If the target ID does not contain a feature (default), do not map it.
51  llvm::StringMap<bool> OffloadArchFeatures;
52  std::string TargetID;
53
54  bool hasFP64() const {
55    return getTriple().getArch() == llvm::Triple::amdgcn ||
56           !!(GPUFeatures & llvm::AMDGPU::FEATURE_FP64);
57  }
58
59  /// Has fast fma f32
60  bool hasFastFMAF() const {
61    return !!(GPUFeatures & llvm::AMDGPU::FEATURE_FAST_FMA_F32);
62  }
63
64  /// Has fast fma f64
65  bool hasFastFMA() const {
66    return getTriple().getArch() == llvm::Triple::amdgcn;
67  }
68
69  bool hasFMAF() const {
70    return getTriple().getArch() == llvm::Triple::amdgcn ||
71           !!(GPUFeatures & llvm::AMDGPU::FEATURE_FMA);
72  }
73
74  bool hasFullRateDenormalsF32() const {
75    return !!(GPUFeatures & llvm::AMDGPU::FEATURE_FAST_DENORMAL_F32);
76  }
77
78  bool hasLDEXPF() const {
79    return getTriple().getArch() == llvm::Triple::amdgcn ||
80           !!(GPUFeatures & llvm::AMDGPU::FEATURE_LDEXP);
81  }
82
83  static bool isAMDGCN(const llvm::Triple &TT) {
84    return TT.getArch() == llvm::Triple::amdgcn;
85  }
86
87  static bool isR600(const llvm::Triple &TT) {
88    return TT.getArch() == llvm::Triple::r600;
89  }
90
91public:
92  AMDGPUTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
93
94  void setAddressSpaceMap(bool DefaultIsPrivate);
95
96  void adjust(DiagnosticsEngine &Diags, LangOptions &Opts) override;
97
98  uint64_t getPointerWidthV(LangAS AS) const override {
99    if (isR600(getTriple()))
100      return 32;
101    unsigned TargetAS = getTargetAddressSpace(AS);
102
103    if (TargetAS == Private || TargetAS == Local)
104      return 32;
105
106    return 64;
107  }
108
109  uint64_t getPointerAlignV(LangAS AddrSpace) const override {
110    return getPointerWidthV(AddrSpace);
111  }
112
113  uint64_t getMaxPointerWidth() const override {
114    return getTriple().getArch() == llvm::Triple::amdgcn ? 64 : 32;
115  }
116
117  bool hasBFloat16Type() const override { return isAMDGCN(getTriple()); }
118  const char *getBFloat16Mangling() const override { return "u6__bf16"; };
119
120  const char *getClobbers() const override { return ""; }
121
122  ArrayRef<const char *> getGCCRegNames() const override;
123
124  ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override {
125    return std::nullopt;
126  }
127
128  /// Accepted register names: (n, m is unsigned integer, n < m)
129  /// v
130  /// s
131  /// a
132  /// {vn}, {v[n]}
133  /// {sn}, {s[n]}
134  /// {an}, {a[n]}
135  /// {S} , where S is a special register name
136  ////{v[n:m]}
137  /// {s[n:m]}
138  /// {a[n:m]}
139  bool validateAsmConstraint(const char *&Name,
140                             TargetInfo::ConstraintInfo &Info) const override {
141    static const ::llvm::StringSet<> SpecialRegs({
142        "exec", "vcc", "flat_scratch", "m0", "scc", "tba", "tma",
143        "flat_scratch_lo", "flat_scratch_hi", "vcc_lo", "vcc_hi", "exec_lo",
144        "exec_hi", "tma_lo", "tma_hi", "tba_lo", "tba_hi",
145    });
146
147    switch (*Name) {
148    case 'I':
149      Info.setRequiresImmediate(-16, 64);
150      return true;
151    case 'J':
152      Info.setRequiresImmediate(-32768, 32767);
153      return true;
154    case 'A':
155    case 'B':
156    case 'C':
157      Info.setRequiresImmediate();
158      return true;
159    default:
160      break;
161    }
162
163    StringRef S(Name);
164
165    if (S == "DA" || S == "DB") {
166      Name++;
167      Info.setRequiresImmediate();
168      return true;
169    }
170
171    bool HasLeftParen = false;
172    if (S.front() == '{') {
173      HasLeftParen = true;
174      S = S.drop_front();
175    }
176    if (S.empty())
177      return false;
178    if (S.front() != 'v' && S.front() != 's' && S.front() != 'a') {
179      if (!HasLeftParen)
180        return false;
181      auto E = S.find('}');
182      if (!SpecialRegs.count(S.substr(0, E)))
183        return false;
184      S = S.drop_front(E + 1);
185      if (!S.empty())
186        return false;
187      // Found {S} where S is a special register.
188      Info.setAllowsRegister();
189      Name = S.data() - 1;
190      return true;
191    }
192    S = S.drop_front();
193    if (!HasLeftParen) {
194      if (!S.empty())
195        return false;
196      // Found s, v or a.
197      Info.setAllowsRegister();
198      Name = S.data() - 1;
199      return true;
200    }
201    bool HasLeftBracket = false;
202    if (!S.empty() && S.front() == '[') {
203      HasLeftBracket = true;
204      S = S.drop_front();
205    }
206    unsigned long long N;
207    if (S.empty() || consumeUnsignedInteger(S, 10, N))
208      return false;
209    if (!S.empty() && S.front() == ':') {
210      if (!HasLeftBracket)
211        return false;
212      S = S.drop_front();
213      unsigned long long M;
214      if (consumeUnsignedInteger(S, 10, M) || N >= M)
215        return false;
216    }
217    if (HasLeftBracket) {
218      if (S.empty() || S.front() != ']')
219        return false;
220      S = S.drop_front();
221    }
222    if (S.empty() || S.front() != '}')
223      return false;
224    S = S.drop_front();
225    if (!S.empty())
226      return false;
227    // Found {vn}, {sn}, {an}, {v[n]}, {s[n]}, {a[n]}, {v[n:m]}, {s[n:m]}
228    // or {a[n:m]}.
229    Info.setAllowsRegister();
230    Name = S.data() - 1;
231    return true;
232  }
233
234  // \p Constraint will be left pointing at the last character of
235  // the constraint.  In practice, it won't be changed unless the
236  // constraint is longer than one character.
237  std::string convertConstraint(const char *&Constraint) const override {
238
239    StringRef S(Constraint);
240    if (S == "DA" || S == "DB") {
241      return std::string("^") + std::string(Constraint++, 2);
242    }
243
244    const char *Begin = Constraint;
245    TargetInfo::ConstraintInfo Info("", "");
246    if (validateAsmConstraint(Constraint, Info))
247      return std::string(Begin).substr(0, Constraint - Begin + 1);
248
249    Constraint = Begin;
250    return std::string(1, *Constraint);
251  }
252
253  bool
254  initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
255                 StringRef CPU,
256                 const std::vector<std::string> &FeatureVec) const override;
257
258  ArrayRef<Builtin::Info> getTargetBuiltins() const override;
259
260  bool useFP16ConversionIntrinsics() const override { return false; }
261
262  void getTargetDefines(const LangOptions &Opts,
263                        MacroBuilder &Builder) const override;
264
265  BuiltinVaListKind getBuiltinVaListKind() const override {
266    return TargetInfo::CharPtrBuiltinVaList;
267  }
268
269  bool isValidCPUName(StringRef Name) const override {
270    if (getTriple().getArch() == llvm::Triple::amdgcn)
271      return llvm::AMDGPU::parseArchAMDGCN(Name) != llvm::AMDGPU::GK_NONE;
272    return llvm::AMDGPU::parseArchR600(Name) != llvm::AMDGPU::GK_NONE;
273  }
274
275  void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
276
277  bool setCPU(const std::string &Name) override {
278    if (getTriple().getArch() == llvm::Triple::amdgcn) {
279      GPUKind = llvm::AMDGPU::parseArchAMDGCN(Name);
280      GPUFeatures = llvm::AMDGPU::getArchAttrAMDGCN(GPUKind);
281    } else {
282      GPUKind = llvm::AMDGPU::parseArchR600(Name);
283      GPUFeatures = llvm::AMDGPU::getArchAttrR600(GPUKind);
284    }
285
286    return GPUKind != llvm::AMDGPU::GK_NONE;
287  }
288
289  void setSupportedOpenCLOpts() override {
290    auto &Opts = getSupportedOpenCLOpts();
291    Opts["cl_clang_storage_class_specifiers"] = true;
292    Opts["__cl_clang_variadic_functions"] = true;
293    Opts["__cl_clang_function_pointers"] = true;
294    Opts["__cl_clang_non_portable_kernel_param_types"] = true;
295    Opts["__cl_clang_bitfields"] = true;
296
297    bool IsAMDGCN = isAMDGCN(getTriple());
298
299    Opts["cl_khr_fp64"] = hasFP64();
300    Opts["__opencl_c_fp64"] = hasFP64();
301
302    if (IsAMDGCN || GPUKind >= llvm::AMDGPU::GK_CEDAR) {
303      Opts["cl_khr_byte_addressable_store"] = true;
304      Opts["cl_khr_global_int32_base_atomics"] = true;
305      Opts["cl_khr_global_int32_extended_atomics"] = true;
306      Opts["cl_khr_local_int32_base_atomics"] = true;
307      Opts["cl_khr_local_int32_extended_atomics"] = true;
308    }
309
310    if (IsAMDGCN) {
311      Opts["cl_khr_fp16"] = true;
312      Opts["cl_khr_int64_base_atomics"] = true;
313      Opts["cl_khr_int64_extended_atomics"] = true;
314      Opts["cl_khr_mipmap_image"] = true;
315      Opts["cl_khr_mipmap_image_writes"] = true;
316      Opts["cl_khr_subgroups"] = true;
317      Opts["cl_amd_media_ops"] = true;
318      Opts["cl_amd_media_ops2"] = true;
319
320      Opts["__opencl_c_images"] = true;
321      Opts["__opencl_c_3d_image_writes"] = true;
322      Opts["cl_khr_3d_image_writes"] = true;
323    }
324  }
325
326  LangAS getOpenCLTypeAddrSpace(OpenCLTypeKind TK) const override {
327    switch (TK) {
328    case OCLTK_Image:
329      return LangAS::opencl_constant;
330
331    case OCLTK_ClkEvent:
332    case OCLTK_Queue:
333    case OCLTK_ReserveID:
334      return LangAS::opencl_global;
335
336    default:
337      return TargetInfo::getOpenCLTypeAddrSpace(TK);
338    }
339  }
340
341  LangAS getOpenCLBuiltinAddressSpace(unsigned AS) const override {
342    switch (AS) {
343    case 0:
344      return LangAS::opencl_generic;
345    case 1:
346      return LangAS::opencl_global;
347    case 3:
348      return LangAS::opencl_local;
349    case 4:
350      return LangAS::opencl_constant;
351    case 5:
352      return LangAS::opencl_private;
353    default:
354      return getLangASFromTargetAS(AS);
355    }
356  }
357
358  LangAS getCUDABuiltinAddressSpace(unsigned AS) const override {
359    switch (AS) {
360    case 0:
361      return LangAS::Default;
362    case 1:
363      return LangAS::cuda_device;
364    case 3:
365      return LangAS::cuda_shared;
366    case 4:
367      return LangAS::cuda_constant;
368    default:
369      return getLangASFromTargetAS(AS);
370    }
371  }
372
373  std::optional<LangAS> getConstantAddressSpace() const override {
374    return getLangASFromTargetAS(Constant);
375  }
376
377  const llvm::omp::GV &getGridValue() const override {
378    switch (WavefrontSize) {
379    case 32:
380      return llvm::omp::getAMDGPUGridValues<32>();
381    case 64:
382      return llvm::omp::getAMDGPUGridValues<64>();
383    default:
384      llvm_unreachable("getGridValue not implemented for this wavesize");
385    }
386  }
387
388  /// \returns Target specific vtbl ptr address space.
389  unsigned getVtblPtrAddressSpace() const override {
390    return static_cast<unsigned>(Constant);
391  }
392
393  /// \returns If a target requires an address within a target specific address
394  /// space \p AddressSpace to be converted in order to be used, then return the
395  /// corresponding target specific DWARF address space.
396  ///
397  /// \returns Otherwise return std::nullopt and no conversion will be emitted
398  /// in the DWARF.
399  std::optional<unsigned>
400  getDWARFAddressSpace(unsigned AddressSpace) const override {
401    const unsigned DWARF_Private = 1;
402    const unsigned DWARF_Local = 2;
403    if (AddressSpace == Private) {
404      return DWARF_Private;
405    } else if (AddressSpace == Local) {
406      return DWARF_Local;
407    } else {
408      return std::nullopt;
409    }
410  }
411
412  CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
413    switch (CC) {
414    default:
415      return CCCR_Warning;
416    case CC_C:
417    case CC_OpenCLKernel:
418    case CC_AMDGPUKernelCall:
419      return CCCR_OK;
420    }
421  }
422
423  // In amdgcn target the null pointer in global, constant, and generic
424  // address space has value 0 but in private and local address space has
425  // value ~0.
426  uint64_t getNullPointerValue(LangAS AS) const override {
427    // FIXME: Also should handle region.
428    return (AS == LangAS::opencl_local || AS == LangAS::opencl_private)
429      ? ~0 : 0;
430  }
431
432  void setAuxTarget(const TargetInfo *Aux) override;
433
434  bool hasBitIntType() const override { return true; }
435
436  // Record offload arch features since they are needed for defining the
437  // pre-defined macros.
438  bool handleTargetFeatures(std::vector<std::string> &Features,
439                            DiagnosticsEngine &Diags) override {
440    auto TargetIDFeatures =
441        getAllPossibleTargetIDFeatures(getTriple(), getArchNameAMDGCN(GPUKind));
442    for (const auto &F : Features) {
443      assert(F.front() == '+' || F.front() == '-');
444      if (F == "+wavefrontsize64")
445        WavefrontSize = 64;
446      bool IsOn = F.front() == '+';
447      StringRef Name = StringRef(F).drop_front();
448      if (!llvm::is_contained(TargetIDFeatures, Name))
449        continue;
450      assert(OffloadArchFeatures.find(Name) == OffloadArchFeatures.end());
451      OffloadArchFeatures[Name] = IsOn;
452    }
453    return true;
454  }
455
456  std::optional<std::string> getTargetID() const override {
457    if (!isAMDGCN(getTriple()))
458      return std::nullopt;
459    // When -target-cpu is not set, we assume generic code that it is valid
460    // for all GPU and use an empty string as target ID to represent that.
461    if (GPUKind == llvm::AMDGPU::GK_NONE)
462      return std::string("");
463    return getCanonicalTargetID(getArchNameAMDGCN(GPUKind),
464                                OffloadArchFeatures);
465  }
466};
467
468} // namespace targets
469} // namespace clang
470
471#endif // LLVM_CLANG_LIB_BASIC_TARGETS_AMDGPU_H
472