| 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> |
| 20 | using namespace clang; |
| 21 | |
| 22 | void 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. |
| 42 | std::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. |
| 70 | std::string |
| 71 | LoopHintAttr::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 | |
| 88 | void 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 | |
| 138 | void 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 | |
| 154 | std::optional<OMPDeclareTargetDeclAttr *> |
| 155 | OMPDeclareTargetDeclAttr::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 | |
| 173 | std::optional<OMPDeclareTargetDeclAttr::MapTypeTy> |
| 174 | OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(const ValueDecl *VD) { |
| 175 | std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD); |
| 176 | if (ActiveAttr) |
| 177 | return (*ActiveAttr)->getMapType(); |
| 178 | return std::nullopt; |
| 179 | } |
| 180 | |
| 181 | std::optional<OMPDeclareTargetDeclAttr::DevTypeTy> |
| 182 | OMPDeclareTargetDeclAttr::getDeviceType(const ValueDecl *VD) { |
| 183 | std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD); |
| 184 | if (ActiveAttr) |
| 185 | return (*ActiveAttr)->getDevType(); |
| 186 | return std::nullopt; |
| 187 | } |
| 188 | |
| 189 | std::optional<SourceLocation> |
| 190 | OMPDeclareTargetDeclAttr::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 | |
| 197 | namespace clang { |
| 198 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const OMPTraitInfo &TI); |
| 199 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const OMPTraitInfo *TI); |
| 200 | } |
| 201 | |
| 202 | void 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 | |
| 252 | unsigned 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 | |
| 281 | StringLiteral *FormatMatchesAttr::getFormatString() const { |
| 282 | return cast<StringLiteral>(Val: getExpectedFormat()); |
| 283 | } |
| 284 | |
| 285 | namespace { |
| 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 | |
| 297 | template <class T> |
| 298 | typename std::enable_if_t<!USE_DEFAULT_EQUALITY, bool> |
| 299 | equalAttrArgs(T A, T B, StructuralEquivalenceContext &Context) { |
| 300 | return false; |
| 301 | } |
| 302 | |
| 303 | template <class T> |
| 304 | typename std::enable_if_t<USE_DEFAULT_EQUALITY, bool> |
| 305 | equalAttrArgs(T A1, T A2, StructuralEquivalenceContext &Context) { |
| 306 | return A1 == A2; |
| 307 | } |
| 308 | |
| 309 | template <> |
| 310 | bool 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 | |
| 323 | template <class T> |
| 324 | bool 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 | |
| 336 | template <> |
| 337 | bool 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 | |
| 344 | template <> |
| 345 | bool equalAttrArgs<Expr *>(Expr *A1, Expr *A2, |
| 346 | StructuralEquivalenceContext &Context) { |
| 347 | return ASTStructuralEquivalence::isEquivalent(Context, S1: A1, S2: A2); |
| 348 | } |
| 349 | |
| 350 | template <> |
| 351 | bool equalAttrArgs<QualType>(QualType T1, QualType T2, |
| 352 | StructuralEquivalenceContext &Context) { |
| 353 | return ASTStructuralEquivalence::isEquivalent(Context, T1, T2); |
| 354 | } |
| 355 | |
| 356 | template <> |
| 357 | bool equalAttrArgs<const IdentifierInfo *>( |
| 358 | const IdentifierInfo *Name1, const IdentifierInfo *Name2, |
| 359 | StructuralEquivalenceContext &Context) { |
| 360 | return ASTStructuralEquivalence::isEquivalent(Name1, Name2); |
| 361 | } |
| 362 | |
| 363 | bool 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 | |
| 379 | namespace { |
| 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 | |
| 392 | template <class T> |
| 393 | typename std::enable_if_t<!USE_DEFAULT_PROFILE> |
| 394 | profileAttrArg(llvm::FoldingSetNodeID &, const ASTContext &, T) { |
| 395 | llvm_unreachable("profile not implemented for this type" ); |
| 396 | } |
| 397 | |
| 398 | template <class T> |
| 399 | typename std::enable_if_t<USE_DEFAULT_PROFILE> |
| 400 | profileAttrArg(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 | |
| 415 | template <> |
| 416 | inline 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 | |
| 423 | template <class T> |
| 424 | inline 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 | |
| 431 | template <> |
| 432 | inline 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 | |
| 442 | template <> |
| 443 | inline void profileAttrArg<Expr *>(llvm::FoldingSetNodeID &ID, |
| 444 | const ASTContext &Ctx, Expr *E) { |
| 445 | E->Profile(ID, Context: Ctx, /*Canonical=*/true); |
| 446 | } |
| 447 | |
| 448 | template <> |
| 449 | inline void profileAttrArg<QualType>(llvm::FoldingSetNodeID &ID, |
| 450 | const ASTContext &, QualType T) { |
| 451 | ID.AddPointer(Ptr: T.getCanonicalType().getAsOpaquePtr()); |
| 452 | } |
| 453 | |
| 454 | void 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 | |