| 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, 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 | |
| 298 | template <class T> |
| 299 | typename std::enable_if_t<!USE_DEFAULT_EQUALITY, bool> |
| 300 | equalAttrArgs(T A, T B, StructuralEquivalenceContext &Context) { |
| 301 | return false; |
| 302 | } |
| 303 | |
| 304 | template <class T> |
| 305 | typename std::enable_if_t<USE_DEFAULT_EQUALITY, bool> |
| 306 | equalAttrArgs(T A1, T A2, StructuralEquivalenceContext &Context) { |
| 307 | return A1 == A2; |
| 308 | } |
| 309 | |
| 310 | template <class T> |
| 311 | bool 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 | |
| 323 | template <> |
| 324 | bool equalAttrArgs<Attr *>(Attr *A1, Attr *A2, |
| 325 | StructuralEquivalenceContext &Context) { |
| 326 | return A1->isEquivalent(Other: *A2, Context); |
| 327 | } |
| 328 | |
| 329 | template <> |
| 330 | bool equalAttrArgs<Expr *>(Expr *A1, Expr *A2, |
| 331 | StructuralEquivalenceContext &Context) { |
| 332 | return ASTStructuralEquivalence::isEquivalent(Context, S1: A1, S2: A2); |
| 333 | } |
| 334 | |
| 335 | template <> |
| 336 | bool equalAttrArgs<QualType>(QualType T1, QualType T2, |
| 337 | StructuralEquivalenceContext &Context) { |
| 338 | return ASTStructuralEquivalence::isEquivalent(Context, T1, T2); |
| 339 | } |
| 340 | |
| 341 | template <> |
| 342 | bool equalAttrArgs<const IdentifierInfo *>( |
| 343 | const IdentifierInfo *Name1, const IdentifierInfo *Name2, |
| 344 | StructuralEquivalenceContext &Context) { |
| 345 | return ASTStructuralEquivalence::isEquivalent(Name1, Name2); |
| 346 | } |
| 347 | |
| 348 | bool 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 | |