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, char *> || \
295 std::is_enum_v<T> || std::is_integral_v<T>)
296
297template <class T>
298typename std::enable_if_t<!USE_DEFAULT_EQUALITY, bool>
299equalAttrArgs(T A, T B, StructuralEquivalenceContext &Context) {
300 return false;
301}
302
303template <class T>
304typename std::enable_if_t<USE_DEFAULT_EQUALITY, bool>
305equalAttrArgs(T A1, T A2, StructuralEquivalenceContext &Context) {
306 return A1 == A2;
307}
308
309template <>
310bool equalAttrArgs<ParamIdx>(ParamIdx P1, ParamIdx P2,
311 StructuralEquivalenceContext &) {
312 // ParamIdx can be invalid when representing an optional parameter that was
313 // not specified (e.g. the second argument of alloc_size(N)).
314 // ParamIdx::operator== asserts both sides are valid, so guard against the
315 // invalid case before delegating to it.
316 if (P1.isValid() != P2.isValid())
317 return false;
318 if (!P1.isValid())
319 return true;
320 return P1 == P2;
321}
322
323template <class T>
324bool equalAttrArgs(T *A1_B, T *A1_E, T *A2_B, T *A2_E,
325 StructuralEquivalenceContext &Context) {
326 if (A1_E - A1_B != A2_E - A2_B)
327 return false;
328
329 for (; A1_B != A1_E; ++A1_B, ++A2_B)
330 if (!equalAttrArgs(*A1_B, *A2_B, Context))
331 return false;
332
333 return true;
334}
335
336template <>
337bool equalAttrArgs<Attr *>(Attr *A1, Attr *A2,
338 StructuralEquivalenceContext &Context) {
339 if (!A1 || !A2)
340 return A1 == A2;
341 return A1->isEquivalent(Other: *A2, Context);
342}
343
344template <>
345bool equalAttrArgs<Expr *>(Expr *A1, Expr *A2,
346 StructuralEquivalenceContext &Context) {
347 return ASTStructuralEquivalence::isEquivalent(Context, S1: A1, S2: A2);
348}
349
350template <>
351bool equalAttrArgs<QualType>(QualType T1, QualType T2,
352 StructuralEquivalenceContext &Context) {
353 return ASTStructuralEquivalence::isEquivalent(Context, T1, T2);
354}
355
356template <>
357bool equalAttrArgs<const IdentifierInfo *>(
358 const IdentifierInfo *Name1, const IdentifierInfo *Name2,
359 StructuralEquivalenceContext &Context) {
360 return ASTStructuralEquivalence::isEquivalent(Name1, Name2);
361}
362
363bool areAlignedAttrsEqual(const AlignedAttr &A1, const AlignedAttr &A2,
364 StructuralEquivalenceContext &Context) {
365 if (A1.getSpelling() != A2.getSpelling())
366 return false;
367
368 if (A1.isAlignmentExpr() != A2.isAlignmentExpr())
369 return false;
370
371 if (A1.isAlignmentExpr())
372 return equalAttrArgs(A1: A1.getAlignmentExpr(), A2: A2.getAlignmentExpr(), Context);
373
374 return equalAttrArgs(T1: A1.getAlignmentType()->getType(),
375 T2: A2.getAlignmentType()->getType(), Context);
376}
377} // namespace
378
379namespace {
380// Machinery to unique attributes based on the arguments.
381// The construction mirrors the equivalent testing code above.
382// The content of the arguments are added to the FoldingSetNodeID instance,
383// which allows the AttributedTypes to unique the attributes based on
384// the value of the arguments.
385
386#define USE_DEFAULT_PROFILE \
387 (std::is_same_v<T, StringRef> || std::is_same_v<T, VersionTuple> || \
388 std::is_same_v<T, IdentifierInfo *> || \
389 std::is_same_v<T, const IdentifierInfo *> || std::is_enum_v<T> || \
390 std::is_integral_v<T>)
391
392template <class T>
393typename std::enable_if_t<!USE_DEFAULT_PROFILE>
394profileAttrArg(llvm::FoldingSetNodeID &, const ASTContext &, T) {
395 llvm_unreachable("profile not implemented for this type");
396}
397
398template <class T>
399typename std::enable_if_t<USE_DEFAULT_PROFILE>
400profileAttrArg(llvm::FoldingSetNodeID &ID, const ASTContext &, T V) {
401 if constexpr (std::is_same_v<T, StringRef>)
402 ID.AddString(String: V);
403 else if constexpr (std::is_same_v<T, VersionTuple>) {
404 ID.AddInteger(V.getMajor());
405 ID.AddInteger(V.getMinor().value_or(0));
406 ID.AddInteger(V.getSubminor().value_or(0));
407 ID.AddInteger(V.getBuild().value_or(0));
408 } else if constexpr (std::is_same_v<T, IdentifierInfo *> ||
409 std::is_same_v<T, const IdentifierInfo *>)
410 ID.AddPointer(Ptr: V);
411 else
412 ID.AddInteger(I: static_cast<long long>(V));
413}
414
415template <>
416inline void profileAttrArg<ParamIdx>(llvm::FoldingSetNodeID &ID,
417 const ASTContext &, ParamIdx P) {
418 ID.AddBoolean(B: P.isValid());
419 if (P.isValid())
420 ID.AddInteger(I: P.getASTIndex());
421}
422
423template <class T>
424inline void profileAttrArg(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx,
425 T *Begin, T *End) {
426 ID.AddInteger(End - Begin);
427 for (; Begin != End; ++Begin)
428 profileAttrArg(ID, Ctx, *Begin);
429}
430
431template <>
432inline void profileAttrArg<Attr *>(llvm::FoldingSetNodeID &ID,
433 const ASTContext &Ctx, Attr *A) {
434 if (!A) {
435 ID.AddPointer(Ptr: nullptr);
436 return;
437 }
438 ID.AddInteger(I: A->getKind());
439 A->Profile(ID, Ctx);
440}
441
442template <>
443inline void profileAttrArg<Expr *>(llvm::FoldingSetNodeID &ID,
444 const ASTContext &Ctx, Expr *E) {
445 E->Profile(ID, Context: Ctx, /*Canonical=*/true);
446}
447
448template <>
449inline void profileAttrArg<QualType>(llvm::FoldingSetNodeID &ID,
450 const ASTContext &, QualType T) {
451 ID.AddPointer(Ptr: T.getCanonicalType().getAsOpaquePtr());
452}
453
454void profileAlignedAttr(const AlignedAttr &A, llvm::FoldingSetNodeID &ID,
455 const ASTContext &Ctx) {
456 ID.AddInteger(I: A.getSpellingListIndex());
457 ID.AddBoolean(B: A.isAlignmentExpr());
458 if (A.isAlignmentExpr())
459 profileAttrArg(ID, Ctx, E: A.getAlignmentExpr());
460 else
461 profileAttrArg(ID, Ctx, T: A.getAlignmentType()->getType());
462}
463} // namespace
464
465#include "clang/AST/AttrImpl.inc"
466