1//===--- AttrImpl.cpp - Classes for representing attributes -----*- 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 contains out-of-line methods for Attr classes.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/AST/ASTContext.h"
14#include "clang/AST/ASTStructuralEquivalence.h"
15#include "clang/AST/Attr.h"
16#include "clang/AST/Expr.h"
17#include "clang/AST/Type.h"
18#include <optional>
19#include <type_traits>
20using namespace clang;
21
22void LoopHintAttr::printPrettyPragma(raw_ostream &OS,
23 const PrintingPolicy &Policy) const {
24 unsigned SpellingIndex = getAttributeSpellingListIndex();
25 // For "#pragma unroll" and "#pragma nounroll" the string "unroll" or
26 // "nounroll" is already emitted as the pragma name.
27 if (SpellingIndex == Pragma_nounroll ||
28 SpellingIndex == Pragma_nounroll_and_jam)
29 return;
30 else if (SpellingIndex == Pragma_unroll ||
31 SpellingIndex == Pragma_unroll_and_jam) {
32 OS << ' ' << getValueString(Policy);
33 return;
34 }
35
36 assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling");
37 OS << ' ' << getOptionName(Option: option) << getValueString(Policy);
38}
39
40// Return a string containing the loop hint argument including the
41// enclosing parentheses.
42std::string LoopHintAttr::getValueString(const PrintingPolicy &Policy) const {
43 std::string ValueName;
44 llvm::raw_string_ostream OS(ValueName);
45 OS << "(";
46 if (state == Numeric)
47 value->printPretty(OS, Helper: nullptr, Policy);
48 else if (state == FixedWidth || state == ScalableWidth) {
49 if (value) {
50 value->printPretty(OS, Helper: nullptr, Policy);
51 if (state == ScalableWidth)
52 OS << ", scalable";
53 } else if (state == ScalableWidth)
54 OS << "scalable";
55 else
56 OS << "fixed";
57 } else if (state == Enable)
58 OS << "enable";
59 else if (state == Full)
60 OS << "full";
61 else if (state == AssumeSafety)
62 OS << "assume_safety";
63 else
64 OS << "disable";
65 OS << ")";
66 return ValueName;
67}
68
69// Return a string suitable for identifying this attribute in diagnostics.
70std::string
71LoopHintAttr::getDiagnosticName(const PrintingPolicy &Policy) const {
72 unsigned SpellingIndex = getAttributeSpellingListIndex();
73 if (SpellingIndex == Pragma_nounroll)
74 return "#pragma nounroll";
75 else if (SpellingIndex == Pragma_unroll)
76 return "#pragma unroll" +
77 (option == UnrollCount ? getValueString(Policy) : "");
78 else if (SpellingIndex == Pragma_nounroll_and_jam)
79 return "#pragma nounroll_and_jam";
80 else if (SpellingIndex == Pragma_unroll_and_jam)
81 return "#pragma unroll_and_jam" +
82 (option == UnrollAndJamCount ? getValueString(Policy) : "");
83
84 assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling");
85 return getOptionName(Option: option) + getValueString(Policy);
86}
87
88void OMPDeclareSimdDeclAttr::printPrettyPragma(
89 raw_ostream &OS, const PrintingPolicy &Policy) const {
90 if (getBranchState() != BS_Undefined)
91 OS << ' ' << ConvertBranchStateTyToStr(Val: getBranchState());
92 if (auto *E = getSimdlen()) {
93 OS << " simdlen(";
94 E->printPretty(OS, Helper: nullptr, Policy);
95 OS << ")";
96 }
97 if (uniforms_size() > 0) {
98 OS << " uniform";
99 StringRef Sep = "(";
100 for (auto *E : uniforms()) {
101 OS << Sep;
102 E->printPretty(OS, Helper: nullptr, Policy);
103 Sep = ", ";
104 }
105 OS << ")";
106 }
107 alignments_iterator NI = alignments_begin();
108 for (auto *E : aligneds()) {
109 OS << " aligned(";
110 E->printPretty(OS, Helper: nullptr, Policy);
111 if (*NI) {
112 OS << ": ";
113 (*NI)->printPretty(OS, Helper: nullptr, Policy);
114 }
115 OS << ")";
116 ++NI;
117 }
118 steps_iterator I = steps_begin();
119 modifiers_iterator MI = modifiers_begin();
120 for (auto *E : linears()) {
121 OS << " linear(";
122 if (*MI != OMPC_LINEAR_unknown)
123 OS << getOpenMPSimpleClauseTypeName(Kind: llvm::omp::Clause::OMPC_linear, Type: *MI)
124 << "(";
125 E->printPretty(OS, Helper: nullptr, Policy);
126 if (*MI != OMPC_LINEAR_unknown)
127 OS << ")";
128 if (*I) {
129 OS << ": ";
130 (*I)->printPretty(OS, Helper: nullptr, Policy);
131 }
132 OS << ")";
133 ++I;
134 ++MI;
135 }
136}
137
138void OMPDeclareTargetDeclAttr::printPrettyPragma(
139 raw_ostream &OS, const PrintingPolicy &Policy) const {
140 // Use fake syntax because it is for testing and debugging purpose only.
141 if (getDevType() != DT_Any)
142 OS << " device_type(" << ConvertDevTypeTyToStr(Val: getDevType()) << ")";
143 if (getMapType() != MT_To && getMapType() != MT_Enter)
144 OS << ' ' << ConvertMapTypeTyToStr(Val: getMapType());
145 if (Expr *E = getIndirectExpr()) {
146 OS << " indirect(";
147 E->printPretty(OS, Helper: nullptr, Policy);
148 OS << ")";
149 } else if (getIndirect()) {
150 OS << " indirect";
151 }
152}
153
154std::optional<OMPDeclareTargetDeclAttr *>
155OMPDeclareTargetDeclAttr::getActiveAttr(const ValueDecl *VD) {
156 if (llvm::all_of(Range: VD->redecls(), P: [](const Decl *D) { return !D->hasAttrs(); }))
157 return std::nullopt;
158 unsigned Level = 0;
159 OMPDeclareTargetDeclAttr *FoundAttr = nullptr;
160 for (const Decl *D : VD->redecls()) {
161 for (auto *Attr : D->specific_attrs<OMPDeclareTargetDeclAttr>()) {
162 if (Level <= Attr->getLevel()) {
163 Level = Attr->getLevel();
164 FoundAttr = Attr;
165 }
166 }
167 }
168 if (FoundAttr)
169 return FoundAttr;
170 return std::nullopt;
171}
172
173std::optional<OMPDeclareTargetDeclAttr::MapTypeTy>
174OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(const ValueDecl *VD) {
175 std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD);
176 if (ActiveAttr)
177 return (*ActiveAttr)->getMapType();
178 return std::nullopt;
179}
180
181std::optional<OMPDeclareTargetDeclAttr::DevTypeTy>
182OMPDeclareTargetDeclAttr::getDeviceType(const ValueDecl *VD) {
183 std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD);
184 if (ActiveAttr)
185 return (*ActiveAttr)->getDevType();
186 return std::nullopt;
187}
188
189std::optional<SourceLocation>
190OMPDeclareTargetDeclAttr::getLocation(const ValueDecl *VD) {
191 std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD);
192 if (ActiveAttr)
193 return (*ActiveAttr)->getRange().getBegin();
194 return std::nullopt;
195}
196
197namespace clang {
198llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const OMPTraitInfo &TI);
199llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const OMPTraitInfo *TI);
200}
201
202void OMPDeclareVariantAttr::printPrettyPragma(
203 raw_ostream &OS, const PrintingPolicy &Policy) const {
204 if (const Expr *E = getVariantFuncRef()) {
205 OS << "(";
206 E->printPretty(OS, Helper: nullptr, Policy);
207 OS << ")";
208 }
209 OS << " match(" << traitInfos << ")";
210
211 auto PrintExprs = [&OS, &Policy](Expr **Begin, Expr **End) {
212 for (Expr **I = Begin; I != End; ++I) {
213 assert(*I && "Expected non-null Stmt");
214 if (I != Begin)
215 OS << ",";
216 (*I)->printPretty(OS, Helper: nullptr, Policy);
217 }
218 };
219 if (adjustArgsNothing_size()) {
220 OS << " adjust_args(nothing:";
221 PrintExprs(adjustArgsNothing_begin(), adjustArgsNothing_end());
222 OS << ")";
223 }
224 if (adjustArgsNeedDevicePtr_size()) {
225 OS << " adjust_args(need_device_ptr:";
226 PrintExprs(adjustArgsNeedDevicePtr_begin(), adjustArgsNeedDevicePtr_end());
227 OS << ")";
228 }
229 if (adjustArgsNeedDeviceAddr_size()) {
230 OS << " adjust_args(need_device_addr:";
231 PrintExprs(adjustArgsNeedDeviceAddr_begin(),
232 adjustArgsNeedDeviceAddr_end());
233 OS << ")";
234 }
235
236 auto PrintInteropInfo = [&OS](OMPInteropInfo *Begin, OMPInteropInfo *End) {
237 for (OMPInteropInfo *I = Begin; I != End; ++I) {
238 if (I != Begin)
239 OS << ", ";
240 OS << "interop(";
241 OS << getInteropTypeString(I);
242 OS << ")";
243 }
244 };
245 if (appendArgs_size()) {
246 OS << " append_args(";
247 PrintInteropInfo(appendArgs_begin(), appendArgs_end());
248 OS << ")";
249 }
250}
251
252unsigned AlignedAttr::getAlignment(ASTContext &Ctx) const {
253 assert(!isAlignmentDependent());
254 if (getCachedAlignmentValue())
255 return *getCachedAlignmentValue();
256
257 // Handle alignmentType case.
258 if (!isAlignmentExpr()) {
259 QualType T = getAlignmentType()->getType();
260
261 // C++ [expr.alignof]p3:
262 // When alignof is applied to a reference type, the result is the
263 // alignment of the referenced type.
264 T = T.getNonReferenceType();
265
266 if (T.getQualifiers().hasUnaligned())
267 return Ctx.getCharWidth();
268
269 return Ctx.getTypeAlignInChars(T: T.getTypePtr()).getQuantity() *
270 Ctx.getCharWidth();
271 }
272
273 // Handle alignmentExpr case.
274 if (alignmentExpr)
275 return alignmentExpr->EvaluateKnownConstInt(Ctx).getZExtValue() *
276 Ctx.getCharWidth();
277
278 return Ctx.getTargetDefaultAlignForAttributeAligned();
279}
280
281StringLiteral *FormatMatchesAttr::getFormatString() const {
282 return cast<StringLiteral>(Val: getExpectedFormat());
283}
284
285namespace {
286// Arguments whose types fail this test never compare equal unless there's a
287// specialization of equalAttrArgs for the type. Specilization for the following
288// arguments haven't been implemented yet:
289// - DeclArgument
290// - OMPTraitInfoArgument
291// - VariadicOMPInteropInfoArgument
292#define USE_DEFAULT_EQUALITY \
293 (std::is_same_v<T, StringRef> || std::is_same_v<T, VersionTuple> || \
294 std::is_same_v<T, IdentifierInfo *> || std::is_same_v<T, ParamIdx> || \
295 std::is_same_v<T, Attr *> || std::is_same_v<T, char *> || \
296 std::is_enum_v<T> || std::is_integral_v<T>)
297
298template <class T>
299typename std::enable_if_t<!USE_DEFAULT_EQUALITY, bool>
300equalAttrArgs(T A, T B, StructuralEquivalenceContext &Context) {
301 return false;
302}
303
304template <class T>
305typename std::enable_if_t<USE_DEFAULT_EQUALITY, bool>
306equalAttrArgs(T A1, T A2, StructuralEquivalenceContext &Context) {
307 return A1 == A2;
308}
309
310template <class T>
311bool equalAttrArgs(T *A1_B, T *A1_E, T *A2_B, T *A2_E,
312 StructuralEquivalenceContext &Context) {
313 if (A1_E - A1_B != A2_E - A2_B)
314 return false;
315
316 for (; A1_B != A1_E; ++A1_B, ++A2_B)
317 if (!equalAttrArgs(*A1_B, *A2_B, Context))
318 return false;
319
320 return true;
321}
322
323template <>
324bool equalAttrArgs<Attr *>(Attr *A1, Attr *A2,
325 StructuralEquivalenceContext &Context) {
326 return A1->isEquivalent(Other: *A2, Context);
327}
328
329template <>
330bool equalAttrArgs<Expr *>(Expr *A1, Expr *A2,
331 StructuralEquivalenceContext &Context) {
332 return ASTStructuralEquivalence::isEquivalent(Context, S1: A1, S2: A2);
333}
334
335template <>
336bool equalAttrArgs<QualType>(QualType T1, QualType T2,
337 StructuralEquivalenceContext &Context) {
338 return ASTStructuralEquivalence::isEquivalent(Context, T1, T2);
339}
340
341template <>
342bool equalAttrArgs<const IdentifierInfo *>(
343 const IdentifierInfo *Name1, const IdentifierInfo *Name2,
344 StructuralEquivalenceContext &Context) {
345 return ASTStructuralEquivalence::isEquivalent(Name1, Name2);
346}
347
348bool areAlignedAttrsEqual(const AlignedAttr &A1, const AlignedAttr &A2,
349 StructuralEquivalenceContext &Context) {
350 if (A1.getSpelling() != A2.getSpelling())
351 return false;
352
353 if (A1.isAlignmentExpr() != A2.isAlignmentExpr())
354 return false;
355
356 if (A1.isAlignmentExpr())
357 return equalAttrArgs(A1: A1.getAlignmentExpr(), A2: A2.getAlignmentExpr(), Context);
358
359 return equalAttrArgs(T1: A1.getAlignmentType()->getType(),
360 T2: A2.getAlignmentType()->getType(), Context);
361}
362} // namespace
363
364#include "clang/AST/AttrImpl.inc"
365