1 | //===--- SemaStmtAttr.cpp - Statement Attribute Handling ------------------===// |
---|---|
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 implements stmt-related attribute processing. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/AST/ASTContext.h" |
14 | #include "clang/AST/EvaluatedExprVisitor.h" |
15 | #include "clang/Basic/TargetInfo.h" |
16 | #include "clang/Sema/DelayedDiagnostic.h" |
17 | #include "clang/Sema/ParsedAttr.h" |
18 | #include "clang/Sema/ScopeInfo.h" |
19 | #include <optional> |
20 | |
21 | using namespace clang; |
22 | using namespace sema; |
23 | |
24 | static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
25 | SourceRange Range) { |
26 | FallThroughAttr Attr(S.Context, A); |
27 | if (isa<SwitchCase>(Val: St)) { |
28 | S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target) |
29 | << A << St->getBeginLoc(); |
30 | SourceLocation L = S.getLocForEndOfToken(Loc: Range.getEnd()); |
31 | S.Diag(L, diag::note_fallthrough_insert_semi_fixit) |
32 | << FixItHint::CreateInsertion(L, ";"); |
33 | return nullptr; |
34 | } |
35 | auto *FnScope = S.getCurFunction(); |
36 | if (FnScope->SwitchStack.empty()) { |
37 | S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch); |
38 | return nullptr; |
39 | } |
40 | |
41 | // If this is spelled as the standard C++17 attribute, but not in C++17, warn |
42 | // about using it as an extension. |
43 | if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() && |
44 | !A.getScopeName()) |
45 | S.Diag(A.getLoc(), diag::ext_cxx17_attr) << A; |
46 | |
47 | FnScope->setHasFallthroughStmt(); |
48 | return ::new (S.Context) FallThroughAttr(S.Context, A); |
49 | } |
50 | |
51 | static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
52 | SourceRange Range) { |
53 | if (A.getAttributeSpellingListIndex() == SuppressAttr::CXX11_gsl_suppress && |
54 | A.getNumArgs() < 1) { |
55 | // Suppression attribute with GSL spelling requires at least 1 argument. |
56 | S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1; |
57 | return nullptr; |
58 | } |
59 | |
60 | std::vector<StringRef> DiagnosticIdentifiers; |
61 | for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) { |
62 | StringRef RuleName; |
63 | |
64 | if (!S.checkStringLiteralArgumentAttr(Attr: A, ArgNum: I, Str&: RuleName, ArgLocation: nullptr)) |
65 | return nullptr; |
66 | |
67 | DiagnosticIdentifiers.push_back(x: RuleName); |
68 | } |
69 | |
70 | return ::new (S.Context) SuppressAttr( |
71 | S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size()); |
72 | } |
73 | |
74 | static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
75 | SourceRange) { |
76 | IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(Arg: 0); |
77 | IdentifierLoc *OptionLoc = A.getArgAsIdent(Arg: 1); |
78 | IdentifierLoc *StateLoc = A.getArgAsIdent(Arg: 2); |
79 | Expr *ValueExpr = A.getArgAsExpr(Arg: 3); |
80 | |
81 | StringRef PragmaName = |
82 | llvm::StringSwitch<StringRef>( |
83 | PragmaNameLoc->getIdentifierInfo()->getName()) |
84 | .Cases(S0: "unroll", S1: "nounroll", S2: "unroll_and_jam", S3: "nounroll_and_jam", |
85 | Value: PragmaNameLoc->getIdentifierInfo()->getName()) |
86 | .Default(Value: "clang loop"); |
87 | |
88 | // This could be handled automatically by adding a Subjects definition in |
89 | // Attr.td, but that would make the diagnostic behavior worse in this case |
90 | // because the user spells this attribute as a pragma. |
91 | if (!isa<DoStmt, ForStmt, CXXForRangeStmt, WhileStmt>(Val: St)) { |
92 | std::string Pragma = "#pragma "+ std::string(PragmaName); |
93 | S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma; |
94 | return nullptr; |
95 | } |
96 | |
97 | LoopHintAttr::OptionType Option; |
98 | LoopHintAttr::LoopHintState State; |
99 | |
100 | auto SetHints = [&Option, &State](LoopHintAttr::OptionType O, |
101 | LoopHintAttr::LoopHintState S) { |
102 | Option = O; |
103 | State = S; |
104 | }; |
105 | |
106 | if (PragmaName == "nounroll") { |
107 | SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable); |
108 | } else if (PragmaName == "unroll") { |
109 | // #pragma unroll N |
110 | if (ValueExpr) { |
111 | if (!ValueExpr->isValueDependent()) { |
112 | auto Value = ValueExpr->EvaluateKnownConstInt(Ctx: S.getASTContext()); |
113 | if (Value.isZero() || Value.isOne()) |
114 | SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable); |
115 | else |
116 | SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric); |
117 | } else |
118 | SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric); |
119 | } else |
120 | SetHints(LoopHintAttr::Unroll, LoopHintAttr::Enable); |
121 | } else if (PragmaName == "nounroll_and_jam") { |
122 | SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Disable); |
123 | } else if (PragmaName == "unroll_and_jam") { |
124 | // #pragma unroll_and_jam N |
125 | if (ValueExpr) |
126 | SetHints(LoopHintAttr::UnrollAndJamCount, LoopHintAttr::Numeric); |
127 | else |
128 | SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Enable); |
129 | } else { |
130 | // #pragma clang loop ... |
131 | assert(OptionLoc && OptionLoc->getIdentifierInfo() && |
132 | "Attribute must have valid option info."); |
133 | Option = llvm::StringSwitch<LoopHintAttr::OptionType>( |
134 | OptionLoc->getIdentifierInfo()->getName()) |
135 | .Case("vectorize", LoopHintAttr::Vectorize) |
136 | .Case("vectorize_width", LoopHintAttr::VectorizeWidth) |
137 | .Case("interleave", LoopHintAttr::Interleave) |
138 | .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate) |
139 | .Case("interleave_count", LoopHintAttr::InterleaveCount) |
140 | .Case("unroll", LoopHintAttr::Unroll) |
141 | .Case("unroll_count", LoopHintAttr::UnrollCount) |
142 | .Case("pipeline", LoopHintAttr::PipelineDisabled) |
143 | .Case("pipeline_initiation_interval", |
144 | LoopHintAttr::PipelineInitiationInterval) |
145 | .Case("distribute", LoopHintAttr::Distribute) |
146 | .Default(LoopHintAttr::Vectorize); |
147 | if (Option == LoopHintAttr::VectorizeWidth) { |
148 | assert((ValueExpr || (StateLoc && StateLoc->getIdentifierInfo())) && |
149 | "Attribute must have a valid value expression or argument."); |
150 | if (ValueExpr && S.CheckLoopHintExpr(E: ValueExpr, Loc: St->getBeginLoc(), |
151 | /*AllowZero=*/false)) |
152 | return nullptr; |
153 | if (StateLoc && StateLoc->getIdentifierInfo() && |
154 | StateLoc->getIdentifierInfo()->isStr("scalable")) |
155 | State = LoopHintAttr::ScalableWidth; |
156 | else |
157 | State = LoopHintAttr::FixedWidth; |
158 | } else if (Option == LoopHintAttr::InterleaveCount || |
159 | Option == LoopHintAttr::UnrollCount || |
160 | Option == LoopHintAttr::PipelineInitiationInterval) { |
161 | assert(ValueExpr && "Attribute must have a valid value expression."); |
162 | if (S.CheckLoopHintExpr(E: ValueExpr, Loc: St->getBeginLoc(), |
163 | /*AllowZero=*/false)) |
164 | return nullptr; |
165 | State = LoopHintAttr::Numeric; |
166 | } else if (Option == LoopHintAttr::Vectorize || |
167 | Option == LoopHintAttr::Interleave || |
168 | Option == LoopHintAttr::VectorizePredicate || |
169 | Option == LoopHintAttr::Unroll || |
170 | Option == LoopHintAttr::Distribute || |
171 | Option == LoopHintAttr::PipelineDisabled) { |
172 | assert(StateLoc && StateLoc->getIdentifierInfo() && |
173 | "Loop hint must have an argument"); |
174 | if (StateLoc->getIdentifierInfo()->isStr(Str: "disable")) |
175 | State = LoopHintAttr::Disable; |
176 | else if (StateLoc->getIdentifierInfo()->isStr(Str: "assume_safety")) |
177 | State = LoopHintAttr::AssumeSafety; |
178 | else if (StateLoc->getIdentifierInfo()->isStr(Str: "full")) |
179 | State = LoopHintAttr::Full; |
180 | else if (StateLoc->getIdentifierInfo()->isStr(Str: "enable")) |
181 | State = LoopHintAttr::Enable; |
182 | else |
183 | llvm_unreachable("bad loop hint argument"); |
184 | } else |
185 | llvm_unreachable("bad loop hint"); |
186 | } |
187 | |
188 | return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A); |
189 | } |
190 | |
191 | namespace { |
192 | class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> { |
193 | bool FoundAsmStmt = false; |
194 | std::vector<const CallExpr *> CallExprs; |
195 | |
196 | public: |
197 | typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited; |
198 | |
199 | CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); } |
200 | |
201 | bool foundCallExpr() { return !CallExprs.empty(); } |
202 | const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; } |
203 | |
204 | bool foundAsmStmt() { return FoundAsmStmt; } |
205 | |
206 | void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(x: E); } |
207 | |
208 | void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; } |
209 | |
210 | void Visit(const Stmt *St) { |
211 | if (!St) |
212 | return; |
213 | ConstEvaluatedExprVisitor<CallExprFinder>::Visit(S: St); |
214 | } |
215 | }; |
216 | } // namespace |
217 | |
218 | static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
219 | SourceRange Range) { |
220 | CallExprFinder CEF(S, St); |
221 | |
222 | if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) { |
223 | S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt) |
224 | << A; |
225 | return nullptr; |
226 | } |
227 | |
228 | return ::new (S.Context) NoMergeAttr(S.Context, A); |
229 | } |
230 | |
231 | static Attr *handleNoConvergentAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
232 | SourceRange Range) { |
233 | CallExprFinder CEF(S, St); |
234 | |
235 | if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) { |
236 | S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt) |
237 | << A; |
238 | return nullptr; |
239 | } |
240 | |
241 | return ::new (S.Context) NoConvergentAttr(S.Context, A); |
242 | } |
243 | |
244 | template <typename OtherAttr, int DiagIdx> |
245 | static bool CheckStmtInlineAttr(Sema &SemaRef, const Stmt *OrigSt, |
246 | const Stmt *CurSt, |
247 | const AttributeCommonInfo &A) { |
248 | CallExprFinder OrigCEF(SemaRef, OrigSt); |
249 | CallExprFinder CEF(SemaRef, CurSt); |
250 | |
251 | // If the call expressions lists are equal in size, we can skip |
252 | // previously emitted diagnostics. However, if the statement has a pack |
253 | // expansion, we have no way of telling which CallExpr is the instantiated |
254 | // version of the other. In this case, we will end up re-diagnosing in the |
255 | // instantiation. |
256 | // ie: [[clang::always_inline]] non_dependent(), (other_call<Pack>()...) |
257 | // will diagnose nondependent again. |
258 | bool CanSuppressDiag = |
259 | OrigSt && CEF.getCallExprs().size() == OrigCEF.getCallExprs().size(); |
260 | |
261 | if (!CEF.foundCallExpr()) { |
262 | return SemaRef.Diag(CurSt->getBeginLoc(), |
263 | diag::warn_attribute_ignored_no_calls_in_stmt) |
264 | << A; |
265 | } |
266 | |
267 | for (const auto &Tup : |
268 | llvm::zip_longest(t: OrigCEF.getCallExprs(), u: CEF.getCallExprs())) { |
269 | // If the original call expression already had a callee, we already |
270 | // diagnosed this, so skip it here. We can't skip if there isn't a 1:1 |
271 | // relationship between the two lists of call expressions. |
272 | if (!CanSuppressDiag || !(*std::get<0>(t: Tup))->getCalleeDecl()) { |
273 | const Decl *Callee = (*std::get<1>(t: Tup))->getCalleeDecl(); |
274 | if (Callee && |
275 | (Callee->hasAttr<OtherAttr>() || Callee->hasAttr<FlattenAttr>())) { |
276 | SemaRef.Diag(CurSt->getBeginLoc(), |
277 | diag::warn_function_stmt_attribute_precedence) |
278 | << A << (Callee->hasAttr<OtherAttr>() ? DiagIdx : 1); |
279 | SemaRef.Diag(Callee->getBeginLoc(), diag::note_conflicting_attribute); |
280 | } |
281 | } |
282 | } |
283 | |
284 | return false; |
285 | } |
286 | |
287 | bool Sema::CheckNoInlineAttr(const Stmt *OrigSt, const Stmt *CurSt, |
288 | const AttributeCommonInfo &A) { |
289 | return CheckStmtInlineAttr<AlwaysInlineAttr, 0>(*this, OrigSt, CurSt, A); |
290 | } |
291 | |
292 | bool Sema::CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt, |
293 | const AttributeCommonInfo &A) { |
294 | return CheckStmtInlineAttr<NoInlineAttr, 2>(*this, OrigSt, CurSt, A); |
295 | } |
296 | |
297 | static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
298 | SourceRange Range) { |
299 | NoInlineAttr NIA(S.Context, A); |
300 | if (!NIA.isStmtNoInline()) { |
301 | S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) |
302 | << "[[clang::noinline]]"; |
303 | return nullptr; |
304 | } |
305 | |
306 | if (S.CheckNoInlineAttr(/*OrigSt=*/nullptr, CurSt: St, A)) |
307 | return nullptr; |
308 | |
309 | return ::new (S.Context) NoInlineAttr(S.Context, A); |
310 | } |
311 | |
312 | static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
313 | SourceRange Range) { |
314 | AlwaysInlineAttr AIA(S.Context, A); |
315 | if (!AIA.isClangAlwaysInline()) { |
316 | S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) |
317 | << "[[clang::always_inline]]"; |
318 | return nullptr; |
319 | } |
320 | |
321 | if (S.CheckAlwaysInlineAttr(/*OrigSt=*/nullptr, CurSt: St, A)) |
322 | return nullptr; |
323 | |
324 | return ::new (S.Context) AlwaysInlineAttr(S.Context, A); |
325 | } |
326 | |
327 | static Attr *handleCXXAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
328 | SourceRange Range) { |
329 | ExprResult Res = S.ActOnCXXAssumeAttr(St, A, Range); |
330 | if (!Res.isUsable()) |
331 | return nullptr; |
332 | |
333 | return ::new (S.Context) CXXAssumeAttr(S.Context, A, Res.get()); |
334 | } |
335 | |
336 | static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
337 | SourceRange Range) { |
338 | // Validation is in Sema::ActOnAttributedStmt(). |
339 | return ::new (S.Context) MustTailAttr(S.Context, A); |
340 | } |
341 | |
342 | static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A, |
343 | SourceRange Range) { |
344 | |
345 | if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName()) |
346 | S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range; |
347 | |
348 | return ::new (S.Context) LikelyAttr(S.Context, A); |
349 | } |
350 | |
351 | static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A, |
352 | SourceRange Range) { |
353 | |
354 | if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName()) |
355 | S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range; |
356 | |
357 | return ::new (S.Context) UnlikelyAttr(S.Context, A); |
358 | } |
359 | |
360 | CodeAlignAttr *Sema::BuildCodeAlignAttr(const AttributeCommonInfo &CI, |
361 | Expr *E) { |
362 | if (!E->isValueDependent()) { |
363 | llvm::APSInt ArgVal; |
364 | ExprResult Res = VerifyIntegerConstantExpression(E, Result: &ArgVal); |
365 | if (Res.isInvalid()) |
366 | return nullptr; |
367 | E = Res.get(); |
368 | |
369 | // This attribute requires an integer argument which is a constant power of |
370 | // two between 1 and 4096 inclusive. |
371 | if (ArgVal < CodeAlignAttr::MinimumAlignment || |
372 | ArgVal > CodeAlignAttr::MaximumAlignment || !ArgVal.isPowerOf2()) { |
373 | if (std::optional<int64_t> Value = ArgVal.trySExtValue()) |
374 | Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range) |
375 | << CI << CodeAlignAttr::MinimumAlignment |
376 | << CodeAlignAttr::MaximumAlignment << Value.value(); |
377 | else |
378 | Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range) |
379 | << CI << CodeAlignAttr::MinimumAlignment |
380 | << CodeAlignAttr::MaximumAlignment << E; |
381 | return nullptr; |
382 | } |
383 | } |
384 | return new (Context) CodeAlignAttr(Context, CI, E); |
385 | } |
386 | |
387 | static Attr *handleCodeAlignAttr(Sema &S, Stmt *St, const ParsedAttr &A) { |
388 | |
389 | Expr *E = A.getArgAsExpr(Arg: 0); |
390 | return S.BuildCodeAlignAttr(A, E); |
391 | } |
392 | |
393 | // Diagnose non-identical duplicates as a 'conflicting' loop attributes |
394 | // and suppress duplicate errors in cases where the two match. |
395 | template <typename LoopAttrT> |
396 | static void CheckForDuplicateLoopAttrs(Sema &S, ArrayRef<const Attr *> Attrs) { |
397 | auto FindFunc = [](const Attr *A) { return isa<const LoopAttrT>(A); }; |
398 | const auto *FirstItr = llvm::find_if(Attrs, FindFunc); |
399 | |
400 | if (FirstItr == Attrs.end()) // no attributes found |
401 | return; |
402 | |
403 | const auto *LastFoundItr = FirstItr; |
404 | std::optional<llvm::APSInt> FirstValue; |
405 | |
406 | const auto *CAFA = |
407 | dyn_cast<ConstantExpr>(cast<LoopAttrT>(*FirstItr)->getAlignment()); |
408 | // Return early if first alignment expression is dependent (since we don't |
409 | // know what the effective size will be), and skip the loop entirely. |
410 | if (!CAFA) |
411 | return; |
412 | |
413 | while (Attrs.end() != (LastFoundItr = std::find_if(LastFoundItr + 1, |
414 | Attrs.end(), FindFunc))) { |
415 | const auto *CASA = |
416 | dyn_cast<ConstantExpr>(cast<LoopAttrT>(*LastFoundItr)->getAlignment()); |
417 | // If the value is dependent, we can not test anything. |
418 | if (!CASA) |
419 | return; |
420 | // Test the attribute values. |
421 | llvm::APSInt SecondValue = CASA->getResultAsAPSInt(); |
422 | if (!FirstValue) |
423 | FirstValue = CAFA->getResultAsAPSInt(); |
424 | |
425 | if (FirstValue != SecondValue) { |
426 | S.Diag((*LastFoundItr)->getLocation(), diag::err_loop_attr_conflict) |
427 | << *FirstItr; |
428 | S.Diag((*FirstItr)->getLocation(), diag::note_previous_attribute); |
429 | } |
430 | } |
431 | } |
432 | |
433 | static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
434 | SourceRange Range) { |
435 | if (!S.getLangOpts().isCompatibleWithMSVC(MajorVersion: LangOptions::MSVC2022_3)) { |
436 | S.Diag(A.getLoc(), diag::warn_unknown_attribute_ignored) |
437 | << A << A.getRange(); |
438 | return nullptr; |
439 | } |
440 | return ::new (S.Context) MSConstexprAttr(S.Context, A); |
441 | } |
442 | |
443 | #define WANT_STMT_MERGE_LOGIC |
444 | #include "clang/Sema/AttrParsedAttrImpl.inc" |
445 | #undef WANT_STMT_MERGE_LOGIC |
446 | |
447 | static void |
448 | CheckForIncompatibleAttributes(Sema &S, |
449 | const SmallVectorImpl<const Attr *> &Attrs) { |
450 | // The vast majority of attributed statements will only have one attribute |
451 | // on them, so skip all of the checking in the common case. |
452 | if (Attrs.size() < 2) |
453 | return; |
454 | |
455 | // First, check for the easy cases that are table-generated for us. |
456 | if (!DiagnoseMutualExclusions(S, Attrs)) |
457 | return; |
458 | |
459 | enum CategoryType { |
460 | // For the following categories, they come in two variants: a state form and |
461 | // a numeric form. The state form may be one of default, enable, and |
462 | // disable. The numeric form provides an integer hint (for example, unroll |
463 | // count) to the transformer. |
464 | Vectorize, |
465 | Interleave, |
466 | UnrollAndJam, |
467 | Pipeline, |
468 | // For unroll, default indicates full unrolling rather than enabling the |
469 | // transformation. |
470 | Unroll, |
471 | // The loop distribution transformation only has a state form that is |
472 | // exposed by #pragma clang loop distribute (enable | disable). |
473 | Distribute, |
474 | // The vector predication only has a state form that is exposed by |
475 | // #pragma clang loop vectorize_predicate (enable | disable). |
476 | VectorizePredicate, |
477 | // This serves as a indicator to how many category are listed in this enum. |
478 | NumberOfCategories |
479 | }; |
480 | // The following array accumulates the hints encountered while iterating |
481 | // through the attributes to check for compatibility. |
482 | struct { |
483 | const LoopHintAttr *StateAttr; |
484 | const LoopHintAttr *NumericAttr; |
485 | } HintAttrs[CategoryType::NumberOfCategories] = {}; |
486 | |
487 | for (const auto *I : Attrs) { |
488 | const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I); |
489 | |
490 | // Skip non loop hint attributes |
491 | if (!LH) |
492 | continue; |
493 | |
494 | CategoryType Category = CategoryType::NumberOfCategories; |
495 | LoopHintAttr::OptionType Option = LH->getOption(); |
496 | switch (Option) { |
497 | case LoopHintAttr::Vectorize: |
498 | case LoopHintAttr::VectorizeWidth: |
499 | Category = Vectorize; |
500 | break; |
501 | case LoopHintAttr::Interleave: |
502 | case LoopHintAttr::InterleaveCount: |
503 | Category = Interleave; |
504 | break; |
505 | case LoopHintAttr::Unroll: |
506 | case LoopHintAttr::UnrollCount: |
507 | Category = Unroll; |
508 | break; |
509 | case LoopHintAttr::UnrollAndJam: |
510 | case LoopHintAttr::UnrollAndJamCount: |
511 | Category = UnrollAndJam; |
512 | break; |
513 | case LoopHintAttr::Distribute: |
514 | // Perform the check for duplicated 'distribute' hints. |
515 | Category = Distribute; |
516 | break; |
517 | case LoopHintAttr::PipelineDisabled: |
518 | case LoopHintAttr::PipelineInitiationInterval: |
519 | Category = Pipeline; |
520 | break; |
521 | case LoopHintAttr::VectorizePredicate: |
522 | Category = VectorizePredicate; |
523 | break; |
524 | }; |
525 | |
526 | assert(Category != NumberOfCategories && "Unhandled loop hint option"); |
527 | auto &CategoryState = HintAttrs[Category]; |
528 | const LoopHintAttr *PrevAttr; |
529 | if (Option == LoopHintAttr::Vectorize || |
530 | Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll || |
531 | Option == LoopHintAttr::UnrollAndJam || |
532 | Option == LoopHintAttr::VectorizePredicate || |
533 | Option == LoopHintAttr::PipelineDisabled || |
534 | Option == LoopHintAttr::Distribute) { |
535 | // Enable|Disable|AssumeSafety hint. For example, vectorize(enable). |
536 | PrevAttr = CategoryState.StateAttr; |
537 | CategoryState.StateAttr = LH; |
538 | } else { |
539 | // Numeric hint. For example, vectorize_width(8). |
540 | PrevAttr = CategoryState.NumericAttr; |
541 | CategoryState.NumericAttr = LH; |
542 | } |
543 | |
544 | PrintingPolicy Policy(S.Context.getLangOpts()); |
545 | SourceLocation OptionLoc = LH->getRange().getBegin(); |
546 | if (PrevAttr) |
547 | // Cannot specify same type of attribute twice. |
548 | S.Diag(OptionLoc, diag::err_pragma_loop_compatibility) |
549 | << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy) |
550 | << LH->getDiagnosticName(Policy); |
551 | |
552 | if (CategoryState.StateAttr && CategoryState.NumericAttr && |
553 | (Category == Unroll || Category == UnrollAndJam || |
554 | CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) { |
555 | // Disable hints are not compatible with numeric hints of the same |
556 | // category. As a special case, numeric unroll hints are also not |
557 | // compatible with enable or full form of the unroll pragma because these |
558 | // directives indicate full unrolling. |
559 | S.Diag(OptionLoc, diag::err_pragma_loop_compatibility) |
560 | << /*Duplicate=*/false |
561 | << CategoryState.StateAttr->getDiagnosticName(Policy) |
562 | << CategoryState.NumericAttr->getDiagnosticName(Policy); |
563 | } |
564 | } |
565 | } |
566 | |
567 | static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A, |
568 | SourceRange Range) { |
569 | // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's |
570 | // useful for OpenCL 1.x too and doesn't require HW support. |
571 | // opencl_unroll_hint can have 0 arguments (compiler |
572 | // determines unrolling factor) or 1 argument (the unroll factor provided |
573 | // by the user). |
574 | unsigned UnrollFactor = 0; |
575 | if (A.getNumArgs() == 1) { |
576 | Expr *E = A.getArgAsExpr(Arg: 0); |
577 | std::optional<llvm::APSInt> ArgVal; |
578 | |
579 | if (!(ArgVal = E->getIntegerConstantExpr(Ctx: S.Context))) { |
580 | S.Diag(A.getLoc(), diag::err_attribute_argument_type) |
581 | << A << AANT_ArgumentIntegerConstant << E->getSourceRange(); |
582 | return nullptr; |
583 | } |
584 | |
585 | int Val = ArgVal->getSExtValue(); |
586 | if (Val <= 0) { |
587 | S.Diag(A.getRange().getBegin(), |
588 | diag::err_attribute_requires_positive_integer) |
589 | << A << /* positive */ 0; |
590 | return nullptr; |
591 | } |
592 | UnrollFactor = static_cast<unsigned>(Val); |
593 | } |
594 | |
595 | return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor); |
596 | } |
597 | |
598 | static Attr *handleHLSLLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
599 | SourceRange Range) { |
600 | |
601 | if (A.getSemanticSpelling() == HLSLLoopHintAttr::Spelling::Microsoft_loop && |
602 | !A.checkAtMostNumArgs(S, 0)) |
603 | return nullptr; |
604 | |
605 | unsigned UnrollFactor = 0; |
606 | if (A.getNumArgs() == 1) { |
607 | Expr *E = A.getArgAsExpr(Arg: 0); |
608 | |
609 | if (S.CheckLoopHintExpr(E, Loc: St->getBeginLoc(), |
610 | /*AllowZero=*/false)) |
611 | return nullptr; |
612 | |
613 | std::optional<llvm::APSInt> ArgVal = E->getIntegerConstantExpr(Ctx: S.Context); |
614 | // CheckLoopHintExpr handles non int const cases |
615 | assert(ArgVal != std::nullopt && "ArgVal should be an integer constant."); |
616 | int Val = ArgVal->getSExtValue(); |
617 | // CheckLoopHintExpr handles negative and zero cases |
618 | assert(Val > 0 && "Val should be a positive integer greater than zero."); |
619 | UnrollFactor = static_cast<unsigned>(Val); |
620 | } |
621 | return ::new (S.Context) HLSLLoopHintAttr(S.Context, A, UnrollFactor); |
622 | } |
623 | |
624 | static Attr *handleHLSLControlFlowHint(Sema &S, Stmt *St, const ParsedAttr &A, |
625 | SourceRange Range) { |
626 | |
627 | return ::new (S.Context) HLSLControlFlowHintAttr(S.Context, A); |
628 | } |
629 | |
630 | static Attr *handleAtomicAttr(Sema &S, Stmt *St, const ParsedAttr &AL, |
631 | SourceRange Range) { |
632 | if (!AL.checkAtLeastNumArgs(S, Num: 1)) |
633 | return nullptr; |
634 | |
635 | SmallVector<AtomicAttr::ConsumedOption, 6> Options; |
636 | for (unsigned ArgIndex = 0; ArgIndex < AL.getNumArgs(); ++ArgIndex) { |
637 | AtomicAttr::ConsumedOption Option; |
638 | StringRef OptionString; |
639 | SourceLocation Loc; |
640 | |
641 | if (!AL.isArgIdent(Arg: ArgIndex)) { |
642 | S.Diag(AL.getArgAsExpr(ArgIndex)->getBeginLoc(), |
643 | diag::err_attribute_argument_type) |
644 | << AL << AANT_ArgumentIdentifier; |
645 | return nullptr; |
646 | } |
647 | |
648 | IdentifierLoc *Ident = AL.getArgAsIdent(Arg: ArgIndex); |
649 | OptionString = Ident->getIdentifierInfo()->getName(); |
650 | Loc = Ident->getLoc(); |
651 | if (!AtomicAttr::ConvertStrToConsumedOption(OptionString, Option)) { |
652 | S.Diag(Loc, diag::err_attribute_invalid_atomic_argument) << OptionString; |
653 | return nullptr; |
654 | } |
655 | Options.push_back(Option); |
656 | } |
657 | |
658 | return ::new (S.Context) |
659 | AtomicAttr(S.Context, AL, Options.data(), Options.size()); |
660 | } |
661 | |
662 | static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A, |
663 | SourceRange Range) { |
664 | if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute) |
665 | return nullptr; |
666 | |
667 | // Unknown attributes are automatically warned on. Target-specific attributes |
668 | // which do not apply to the current target architecture are treated as |
669 | // though they were unknown attributes. |
670 | const TargetInfo *Aux = S.Context.getAuxTargetInfo(); |
671 | if (A.getKind() == ParsedAttr::UnknownAttribute || |
672 | !(A.existsInTarget(Target: S.Context.getTargetInfo()) || |
673 | (S.Context.getLangOpts().SYCLIsDevice && Aux && |
674 | A.existsInTarget(Target: *Aux)))) { |
675 | S.Diag(A.getLoc(), A.isRegularKeywordAttribute() |
676 | ? (unsigned)diag::err_keyword_not_supported_on_target |
677 | : A.isDeclspecAttribute() |
678 | ? (unsigned)diag::warn_unhandled_ms_attribute_ignored |
679 | : (unsigned)diag::warn_unknown_attribute_ignored) |
680 | << A << A.getRange(); |
681 | return nullptr; |
682 | } |
683 | |
684 | if (S.checkCommonAttributeFeatures(S: St, A)) |
685 | return nullptr; |
686 | |
687 | switch (A.getKind()) { |
688 | case ParsedAttr::AT_AlwaysInline: |
689 | return handleAlwaysInlineAttr(S, St, A, Range); |
690 | case ParsedAttr::AT_CXXAssume: |
691 | return handleCXXAssumeAttr(S, St, A, Range); |
692 | case ParsedAttr::AT_FallThrough: |
693 | return handleFallThroughAttr(S, St, A, Range); |
694 | case ParsedAttr::AT_LoopHint: |
695 | return handleLoopHintAttr(S, St, A, Range); |
696 | case ParsedAttr::AT_HLSLLoopHint: |
697 | return handleHLSLLoopHintAttr(S, St, A, Range); |
698 | case ParsedAttr::AT_HLSLControlFlowHint: |
699 | return handleHLSLControlFlowHint(S, St, A, Range); |
700 | case ParsedAttr::AT_OpenCLUnrollHint: |
701 | return handleOpenCLUnrollHint(S, St, A, Range); |
702 | case ParsedAttr::AT_Suppress: |
703 | return handleSuppressAttr(S, St, A, Range); |
704 | case ParsedAttr::AT_NoMerge: |
705 | return handleNoMergeAttr(S, St, A, Range); |
706 | case ParsedAttr::AT_NoInline: |
707 | return handleNoInlineAttr(S, St, A, Range); |
708 | case ParsedAttr::AT_MustTail: |
709 | return handleMustTailAttr(S, St, A, Range); |
710 | case ParsedAttr::AT_Likely: |
711 | return handleLikely(S, St, A, Range); |
712 | case ParsedAttr::AT_Unlikely: |
713 | return handleUnlikely(S, St, A, Range); |
714 | case ParsedAttr::AT_CodeAlign: |
715 | return handleCodeAlignAttr(S, St, A); |
716 | case ParsedAttr::AT_MSConstexpr: |
717 | return handleMSConstexprAttr(S, St, A, Range); |
718 | case ParsedAttr::AT_NoConvergent: |
719 | return handleNoConvergentAttr(S, St, A, Range); |
720 | case ParsedAttr::AT_Annotate: |
721 | return S.CreateAnnotationAttr(AL: A); |
722 | case ParsedAttr::AT_Atomic: |
723 | return handleAtomicAttr(S, St, AL: A, Range); |
724 | default: |
725 | if (Attr *AT = nullptr; A.getInfo().handleStmtAttribute(S, St, Attr: A, Result&: AT) != |
726 | ParsedAttrInfo::NotHandled) { |
727 | return AT; |
728 | } |
729 | // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a |
730 | // declaration attribute is not written on a statement, but this code is |
731 | // needed for attributes in Attr.td that do not list any subjects. |
732 | S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt) |
733 | << A << A.isRegularKeywordAttribute() << St->getBeginLoc(); |
734 | return nullptr; |
735 | } |
736 | } |
737 | |
738 | void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs, |
739 | SmallVectorImpl<const Attr *> &OutAttrs) { |
740 | for (const ParsedAttr &AL : InAttrs) { |
741 | if (const Attr *A = ProcessStmtAttribute(S&: *this, St: S, A: AL, Range: InAttrs.Range)) |
742 | OutAttrs.push_back(Elt: A); |
743 | } |
744 | |
745 | CheckForIncompatibleAttributes(S&: *this, Attrs: OutAttrs); |
746 | CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, OutAttrs); |
747 | } |
748 | |
749 | bool Sema::CheckRebuiltStmtAttributes(ArrayRef<const Attr *> Attrs) { |
750 | CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, Attrs); |
751 | return false; |
752 | } |
753 | |
754 | ExprResult Sema::ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A, |
755 | SourceRange Range) { |
756 | if (A.getNumArgs() != 1 || !A.getArgAsExpr(Arg: 0)) { |
757 | Diag(A.getLoc(), diag::err_attribute_wrong_number_arguments) |
758 | << A.getAttrName() << 1 << Range; |
759 | return ExprError(); |
760 | } |
761 | |
762 | auto *Assumption = A.getArgAsExpr(Arg: 0); |
763 | |
764 | if (DiagnoseUnexpandedParameterPack(E: Assumption)) { |
765 | return ExprError(); |
766 | } |
767 | |
768 | if (Assumption->getDependence() == ExprDependence::None) { |
769 | ExprResult Res = BuildCXXAssumeExpr(Assumption, AttrName: A.getAttrName(), Range); |
770 | if (Res.isInvalid()) |
771 | return ExprError(); |
772 | Assumption = Res.get(); |
773 | } |
774 | |
775 | if (!getLangOpts().CPlusPlus23 && |
776 | A.getSyntax() == AttributeCommonInfo::AS_CXX11) |
777 | Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range; |
778 | |
779 | return Assumption; |
780 | } |
781 | |
782 | ExprResult Sema::BuildCXXAssumeExpr(Expr *Assumption, |
783 | const IdentifierInfo *AttrName, |
784 | SourceRange Range) { |
785 | ExprResult Res = CorrectDelayedTyposInExpr(E: Assumption); |
786 | if (Res.isInvalid()) |
787 | return ExprError(); |
788 | |
789 | Res = CheckPlaceholderExpr(E: Res.get()); |
790 | if (Res.isInvalid()) |
791 | return ExprError(); |
792 | |
793 | Res = PerformContextuallyConvertToBool(From: Res.get()); |
794 | if (Res.isInvalid()) |
795 | return ExprError(); |
796 | |
797 | Assumption = Res.get(); |
798 | if (Assumption->HasSideEffects(Context)) |
799 | Diag(Assumption->getBeginLoc(), diag::warn_assume_side_effects) |
800 | << AttrName << Range; |
801 | |
802 | return Assumption; |
803 | } |
804 |
Definitions
- handleFallThroughAttr
- handleSuppressAttr
- handleLoopHintAttr
- CallExprFinder
- CallExprFinder
- foundCallExpr
- getCallExprs
- foundAsmStmt
- VisitCallExpr
- VisitAsmStmt
- Visit
- handleNoMergeAttr
- handleNoConvergentAttr
- CheckStmtInlineAttr
- CheckNoInlineAttr
- CheckAlwaysInlineAttr
- handleNoInlineAttr
- handleAlwaysInlineAttr
- handleCXXAssumeAttr
- handleMustTailAttr
- handleLikely
- handleUnlikely
- BuildCodeAlignAttr
- handleCodeAlignAttr
- CheckForDuplicateLoopAttrs
- handleMSConstexprAttr
- CheckForIncompatibleAttributes
- handleOpenCLUnrollHint
- handleHLSLLoopHintAttr
- handleHLSLControlFlowHint
- handleAtomicAttr
- ProcessStmtAttribute
- ProcessStmtAttributes
- CheckRebuiltStmtAttributes
- ActOnCXXAssumeAttr
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more