/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Sema/SemaStmtAttr.cpp
Line | Count | Source (jump to first uncovered line) |
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/SourceManager.h" |
16 | | #include "clang/Basic/TargetInfo.h" |
17 | | #include "clang/Sema/DelayedDiagnostic.h" |
18 | | #include "clang/Sema/Lookup.h" |
19 | | #include "clang/Sema/ScopeInfo.h" |
20 | | #include "clang/Sema/SemaInternal.h" |
21 | | #include "llvm/ADT/StringExtras.h" |
22 | | |
23 | | using namespace clang; |
24 | | using namespace sema; |
25 | | |
26 | | static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
27 | 1.08k | SourceRange Range) { |
28 | 1.08k | FallThroughAttr Attr(S.Context, A); |
29 | 1.08k | if (isa<SwitchCase>(St)) { |
30 | 1 | S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target) |
31 | 1 | << A << St->getBeginLoc(); |
32 | 1 | SourceLocation L = S.getLocForEndOfToken(Range.getEnd()); |
33 | 1 | S.Diag(L, diag::note_fallthrough_insert_semi_fixit) |
34 | 1 | << FixItHint::CreateInsertion(L, ";"); |
35 | 1 | return nullptr; |
36 | 1 | } |
37 | 1.08k | auto *FnScope = S.getCurFunction(); |
38 | 1.08k | if (FnScope->SwitchStack.empty()) { |
39 | 1 | S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch); |
40 | 1 | return nullptr; |
41 | 1 | } |
42 | | |
43 | | // If this is spelled as the standard C++17 attribute, but not in C++17, warn |
44 | | // about using it as an extension. |
45 | 1.07k | if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute()1.03k && |
46 | 1.07k | !A.getScopeName()1.01k ) |
47 | 975 | S.Diag(A.getLoc(), diag::ext_cxx17_attr) << A; |
48 | | |
49 | 1.07k | FnScope->setHasFallthroughStmt(); |
50 | 1.07k | return ::new (S.Context) FallThroughAttr(S.Context, A); |
51 | 1.08k | } |
52 | | |
53 | | static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
54 | 5 | SourceRange Range) { |
55 | 5 | std::vector<StringRef> DiagnosticIdentifiers; |
56 | 11 | for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I6 ) { |
57 | 6 | StringRef RuleName; |
58 | | |
59 | 6 | if (!S.checkStringLiteralArgumentAttr(A, I, RuleName, nullptr)) |
60 | 0 | return nullptr; |
61 | | |
62 | | // FIXME: Warn if the rule name is unknown. This is tricky because only |
63 | | // clang-tidy knows about available rules. |
64 | 6 | DiagnosticIdentifiers.push_back(RuleName); |
65 | 6 | } |
66 | | |
67 | 5 | return ::new (S.Context) SuppressAttr( |
68 | 5 | S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size()); |
69 | 5 | } |
70 | | |
71 | | static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
72 | 352 | SourceRange) { |
73 | 352 | IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0); |
74 | 352 | IdentifierLoc *OptionLoc = A.getArgAsIdent(1); |
75 | 352 | IdentifierLoc *StateLoc = A.getArgAsIdent(2); |
76 | 352 | Expr *ValueExpr = A.getArgAsExpr(3); |
77 | | |
78 | 352 | StringRef PragmaName = |
79 | 352 | llvm::StringSwitch<StringRef>(PragmaNameLoc->Ident->getName()) |
80 | 352 | .Cases("unroll", "nounroll", "unroll_and_jam", "nounroll_and_jam", |
81 | 352 | PragmaNameLoc->Ident->getName()) |
82 | 352 | .Default("clang loop"); |
83 | | |
84 | | // This could be handled automatically by adding a Subjects definition in |
85 | | // Attr.td, but that would make the diagnostic behavior worse in this case |
86 | | // because the user spells this attribute as a pragma. |
87 | 352 | if (!isa<DoStmt, ForStmt, CXXForRangeStmt, WhileStmt>(St)) { |
88 | 10 | std::string Pragma = "#pragma " + std::string(PragmaName); |
89 | 10 | S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma; |
90 | 10 | return nullptr; |
91 | 10 | } |
92 | | |
93 | 342 | LoopHintAttr::OptionType Option; |
94 | 342 | LoopHintAttr::LoopHintState State; |
95 | | |
96 | 342 | auto SetHints = [&Option, &State](LoopHintAttr::OptionType O, |
97 | 342 | LoopHintAttr::LoopHintState S) { |
98 | 79 | Option = O; |
99 | 79 | State = S; |
100 | 79 | }; |
101 | | |
102 | 342 | if (PragmaName == "nounroll") { |
103 | 10 | SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable); |
104 | 332 | } else if (PragmaName == "unroll") { |
105 | | // #pragma unroll N |
106 | 56 | if (ValueExpr) |
107 | 43 | SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric); |
108 | 13 | else |
109 | 13 | SetHints(LoopHintAttr::Unroll, LoopHintAttr::Enable); |
110 | 276 | } else if (PragmaName == "nounroll_and_jam") { |
111 | 5 | SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Disable); |
112 | 271 | } else if (PragmaName == "unroll_and_jam") { |
113 | | // #pragma unroll_and_jam N |
114 | 8 | if (ValueExpr) |
115 | 5 | SetHints(LoopHintAttr::UnrollAndJamCount, LoopHintAttr::Numeric); |
116 | 3 | else |
117 | 3 | SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Enable); |
118 | 263 | } else { |
119 | | // #pragma clang loop ... |
120 | 263 | assert(OptionLoc && OptionLoc->Ident && |
121 | 263 | "Attribute must have valid option info."); |
122 | 0 | Option = llvm::StringSwitch<LoopHintAttr::OptionType>( |
123 | 263 | OptionLoc->Ident->getName()) |
124 | 263 | .Case("vectorize", LoopHintAttr::Vectorize) |
125 | 263 | .Case("vectorize_width", LoopHintAttr::VectorizeWidth) |
126 | 263 | .Case("interleave", LoopHintAttr::Interleave) |
127 | 263 | .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate) |
128 | 263 | .Case("interleave_count", LoopHintAttr::InterleaveCount) |
129 | 263 | .Case("unroll", LoopHintAttr::Unroll) |
130 | 263 | .Case("unroll_count", LoopHintAttr::UnrollCount) |
131 | 263 | .Case("pipeline", LoopHintAttr::PipelineDisabled) |
132 | 263 | .Case("pipeline_initiation_interval", |
133 | 263 | LoopHintAttr::PipelineInitiationInterval) |
134 | 263 | .Case("distribute", LoopHintAttr::Distribute) |
135 | 263 | .Default(LoopHintAttr::Vectorize); |
136 | 263 | if (Option == LoopHintAttr::VectorizeWidth) { |
137 | 52 | assert((ValueExpr || (StateLoc && StateLoc->Ident)) && |
138 | 52 | "Attribute must have a valid value expression or argument."); |
139 | 52 | if (ValueExpr && S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc())46 ) |
140 | 0 | return nullptr; |
141 | 52 | if (StateLoc && StateLoc->Ident12 && StateLoc->Ident->isStr("scalable")12 ) |
142 | 7 | State = LoopHintAttr::ScalableWidth; |
143 | 45 | else |
144 | 45 | State = LoopHintAttr::FixedWidth; |
145 | 211 | } else if (Option == LoopHintAttr::InterleaveCount || |
146 | 211 | Option == LoopHintAttr::UnrollCount178 || |
147 | 211 | Option == LoopHintAttr::PipelineInitiationInterval160 ) { |
148 | 56 | assert(ValueExpr && "Attribute must have a valid value expression."); |
149 | 56 | if (S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc())) |
150 | 0 | return nullptr; |
151 | 56 | State = LoopHintAttr::Numeric; |
152 | 155 | } else if (Option == LoopHintAttr::Vectorize || |
153 | 155 | Option == LoopHintAttr::Interleave102 || |
154 | 155 | Option == LoopHintAttr::VectorizePredicate76 || |
155 | 155 | Option == LoopHintAttr::Unroll59 || |
156 | 155 | Option == LoopHintAttr::Distribute25 || |
157 | 155 | Option == LoopHintAttr::PipelineDisabled4 ) { |
158 | 155 | assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument"); |
159 | 155 | if (StateLoc->Ident->isStr("disable")) |
160 | 76 | State = LoopHintAttr::Disable; |
161 | 79 | else if (StateLoc->Ident->isStr("assume_safety")) |
162 | 19 | State = LoopHintAttr::AssumeSafety; |
163 | 60 | else if (StateLoc->Ident->isStr("full")) |
164 | 10 | State = LoopHintAttr::Full; |
165 | 50 | else if (StateLoc->Ident->isStr("enable")) |
166 | 50 | State = LoopHintAttr::Enable; |
167 | 0 | else |
168 | 0 | llvm_unreachable("bad loop hint argument"); |
169 | 155 | } else |
170 | 0 | llvm_unreachable("bad loop hint"); |
171 | 263 | } |
172 | | |
173 | 342 | return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A); |
174 | 342 | } |
175 | | |
176 | | namespace { |
177 | | class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> { |
178 | | bool FoundAsmStmt = false; |
179 | | std::vector<const CallExpr *> CallExprs; |
180 | | |
181 | | public: |
182 | | typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited; |
183 | | |
184 | 52 | CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); } |
185 | | |
186 | 52 | bool foundCallExpr() { return !CallExprs.empty(); } |
187 | 25 | const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; } |
188 | | |
189 | 7 | bool foundAsmStmt() { return FoundAsmStmt; } |
190 | | |
191 | 49 | void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(E); } |
192 | | |
193 | 3 | void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; } |
194 | | |
195 | 52 | void Visit(const Stmt *St) { |
196 | 52 | if (!St) |
197 | 0 | return; |
198 | 52 | ConstEvaluatedExprVisitor<CallExprFinder>::Visit(St); |
199 | 52 | } |
200 | | }; |
201 | | } // namespace |
202 | | |
203 | | static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
204 | 23 | SourceRange Range) { |
205 | 23 | NoMergeAttr NMA(S.Context, A); |
206 | 23 | CallExprFinder CEF(S, St); |
207 | | |
208 | 23 | if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()7 ) { |
209 | 6 | S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt) |
210 | 6 | << A; |
211 | 6 | return nullptr; |
212 | 6 | } |
213 | | |
214 | 17 | return ::new (S.Context) NoMergeAttr(S.Context, A); |
215 | 23 | } |
216 | | |
217 | | static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
218 | 19 | SourceRange Range) { |
219 | 19 | NoInlineAttr NIA(S.Context, A); |
220 | 19 | if (!NIA.isClangNoInline()) { |
221 | 3 | S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) |
222 | 3 | << "[[clang::noinline]]"; |
223 | 3 | return nullptr; |
224 | 3 | } |
225 | | |
226 | 16 | CallExprFinder CEF(S, St); |
227 | 16 | if (!CEF.foundCallExpr()) { |
228 | 2 | S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt) |
229 | 2 | << A; |
230 | 2 | return nullptr; |
231 | 2 | } |
232 | | |
233 | 16 | for (const auto *CallExpr : CEF.getCallExprs())14 { |
234 | 16 | const Decl *Decl = CallExpr->getCalleeDecl(); |
235 | 16 | if (Decl->hasAttr<AlwaysInlineAttr>() || Decl->hasAttr<FlattenAttr>()15 ) |
236 | 2 | S.Diag(St->getBeginLoc(), diag::warn_function_stmt_attribute_precedence) |
237 | 2 | << A << (Decl->hasAttr<AlwaysInlineAttr>() ? 01 : 11 ); |
238 | 16 | } |
239 | | |
240 | 14 | return ::new (S.Context) NoInlineAttr(S.Context, A); |
241 | 16 | } |
242 | | |
243 | | static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
244 | 16 | SourceRange Range) { |
245 | 16 | AlwaysInlineAttr AIA(S.Context, A); |
246 | 16 | if (!AIA.isClangAlwaysInline()) { |
247 | 3 | S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) |
248 | 3 | << "[[clang::always_inline]]"; |
249 | 3 | return nullptr; |
250 | 3 | } |
251 | | |
252 | 13 | CallExprFinder CEF(S, St); |
253 | 13 | if (!CEF.foundCallExpr()) { |
254 | 2 | S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt) |
255 | 2 | << A; |
256 | 2 | return nullptr; |
257 | 2 | } |
258 | | |
259 | 13 | for (const auto *CallExpr : CEF.getCallExprs())11 { |
260 | 13 | const Decl *Decl = CallExpr->getCalleeDecl(); |
261 | 13 | if (Decl->hasAttr<NoInlineAttr>() || Decl->hasAttr<FlattenAttr>()12 ) |
262 | 2 | S.Diag(St->getBeginLoc(), diag::warn_function_stmt_attribute_precedence) |
263 | 2 | << A << (Decl->hasAttr<NoInlineAttr>() ? 21 : 11 ); |
264 | 13 | } |
265 | | |
266 | 11 | return ::new (S.Context) AlwaysInlineAttr(S.Context, A); |
267 | 13 | } |
268 | | |
269 | | static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
270 | 97 | SourceRange Range) { |
271 | | // Validation is in Sema::ActOnAttributedStmt(). |
272 | 97 | return ::new (S.Context) MustTailAttr(S.Context, A); |
273 | 97 | } |
274 | | |
275 | | static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A, |
276 | 76 | SourceRange Range) { |
277 | | |
278 | 76 | if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute()72 && !A.getScopeName()67 ) |
279 | 67 | S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range; |
280 | | |
281 | 76 | return ::new (S.Context) LikelyAttr(S.Context, A); |
282 | 76 | } |
283 | | |
284 | | static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A, |
285 | 88 | SourceRange Range) { |
286 | | |
287 | 88 | if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute()81 && !A.getScopeName()73 ) |
288 | 73 | S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range; |
289 | | |
290 | 88 | return ::new (S.Context) UnlikelyAttr(S.Context, A); |
291 | 88 | } |
292 | | |
293 | | #define WANT_STMT_MERGE_LOGIC |
294 | | #include "clang/Sema/AttrParsedAttrImpl.inc" |
295 | | #undef WANT_STMT_MERGE_LOGIC |
296 | | |
297 | | static void |
298 | | CheckForIncompatibleAttributes(Sema &S, |
299 | 5.31k | const SmallVectorImpl<const Attr *> &Attrs) { |
300 | | // The vast majority of attributed statements will only have one attribute |
301 | | // on them, so skip all of the checking in the common case. |
302 | 5.31k | if (Attrs.size() < 2) |
303 | 5.22k | return; |
304 | | |
305 | | // First, check for the easy cases that are table-generated for us. |
306 | 87 | if (!DiagnoseMutualExclusions(S, Attrs)) |
307 | 2 | return; |
308 | | |
309 | | // There are 6 categories of loop hints attributes: vectorize, interleave, |
310 | | // unroll, unroll_and_jam, pipeline and distribute. Except for distribute they |
311 | | // come in two variants: a state form and a numeric form. The state form |
312 | | // selectively defaults/enables/disables the transformation for the loop |
313 | | // (for unroll, default indicates full unrolling rather than enabling the |
314 | | // transformation). The numeric form form provides an integer hint (for |
315 | | // example, unroll count) to the transformer. The following array accumulates |
316 | | // the hints encountered while iterating through the attributes to check for |
317 | | // compatibility. |
318 | 85 | struct { |
319 | 85 | const LoopHintAttr *StateAttr; |
320 | 85 | const LoopHintAttr *NumericAttr; |
321 | 85 | } HintAttrs[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, |
322 | 85 | {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, |
323 | 85 | {nullptr, nullptr}}; |
324 | | |
325 | 248 | for (const auto *I : Attrs) { |
326 | 248 | const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I); |
327 | | |
328 | | // Skip non loop hint attributes |
329 | 248 | if (!LH) |
330 | 14 | continue; |
331 | | |
332 | 234 | LoopHintAttr::OptionType Option = LH->getOption(); |
333 | 234 | enum { |
334 | 234 | Vectorize, |
335 | 234 | Interleave, |
336 | 234 | Unroll, |
337 | 234 | UnrollAndJam, |
338 | 234 | Distribute, |
339 | 234 | Pipeline, |
340 | 234 | VectorizePredicate |
341 | 234 | } Category; |
342 | 234 | switch (Option) { |
343 | 36 | case LoopHintAttr::Vectorize: |
344 | 71 | case LoopHintAttr::VectorizeWidth: |
345 | 71 | Category = Vectorize; |
346 | 71 | break; |
347 | 26 | case LoopHintAttr::Interleave: |
348 | 56 | case LoopHintAttr::InterleaveCount: |
349 | 56 | Category = Interleave; |
350 | 56 | break; |
351 | 38 | case LoopHintAttr::Unroll: |
352 | 64 | case LoopHintAttr::UnrollCount: |
353 | 64 | Category = Unroll; |
354 | 64 | break; |
355 | 4 | case LoopHintAttr::UnrollAndJam: |
356 | 5 | case LoopHintAttr::UnrollAndJamCount: |
357 | 5 | Category = UnrollAndJam; |
358 | 5 | break; |
359 | 18 | case LoopHintAttr::Distribute: |
360 | | // Perform the check for duplicated 'distribute' hints. |
361 | 18 | Category = Distribute; |
362 | 18 | break; |
363 | 1 | case LoopHintAttr::PipelineDisabled: |
364 | 4 | case LoopHintAttr::PipelineInitiationInterval: |
365 | 4 | Category = Pipeline; |
366 | 4 | break; |
367 | 16 | case LoopHintAttr::VectorizePredicate: |
368 | 16 | Category = VectorizePredicate; |
369 | 16 | break; |
370 | 234 | }; |
371 | | |
372 | 234 | assert(Category < sizeof(HintAttrs) / sizeof(HintAttrs[0])); |
373 | 0 | auto &CategoryState = HintAttrs[Category]; |
374 | 234 | const LoopHintAttr *PrevAttr; |
375 | 234 | if (Option == LoopHintAttr::Vectorize || |
376 | 234 | Option == LoopHintAttr::Interleave198 || Option == LoopHintAttr::Unroll172 || |
377 | 234 | Option == LoopHintAttr::UnrollAndJam134 || |
378 | 234 | Option == LoopHintAttr::VectorizePredicate130 || |
379 | 234 | Option == LoopHintAttr::PipelineDisabled114 || |
380 | 234 | Option == LoopHintAttr::Distribute113 ) { |
381 | | // Enable|Disable|AssumeSafety hint. For example, vectorize(enable). |
382 | 139 | PrevAttr = CategoryState.StateAttr; |
383 | 139 | CategoryState.StateAttr = LH; |
384 | 139 | } else { |
385 | | // Numeric hint. For example, vectorize_width(8). |
386 | 95 | PrevAttr = CategoryState.NumericAttr; |
387 | 95 | CategoryState.NumericAttr = LH; |
388 | 95 | } |
389 | | |
390 | 234 | PrintingPolicy Policy(S.Context.getLangOpts()); |
391 | 234 | SourceLocation OptionLoc = LH->getRange().getBegin(); |
392 | 234 | if (PrevAttr) |
393 | | // Cannot specify same type of attribute twice. |
394 | 14 | S.Diag(OptionLoc, diag::err_pragma_loop_compatibility) |
395 | 14 | << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy) |
396 | 14 | << LH->getDiagnosticName(Policy); |
397 | | |
398 | 234 | if (CategoryState.StateAttr && CategoryState.NumericAttr148 && |
399 | 234 | (18 Category == Unroll18 || Category == UnrollAndJam10 || |
400 | 18 | CategoryState.StateAttr->getState() == LoopHintAttr::Disable9 )) { |
401 | | // Disable hints are not compatible with numeric hints of the same |
402 | | // category. As a special case, numeric unroll hints are also not |
403 | | // compatible with enable or full form of the unroll pragma because these |
404 | | // directives indicate full unrolling. |
405 | 14 | S.Diag(OptionLoc, diag::err_pragma_loop_compatibility) |
406 | 14 | << /*Duplicate=*/false |
407 | 14 | << CategoryState.StateAttr->getDiagnosticName(Policy) |
408 | 14 | << CategoryState.NumericAttr->getDiagnosticName(Policy); |
409 | 14 | } |
410 | 234 | } |
411 | 85 | } |
412 | | |
413 | | static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A, |
414 | 21 | SourceRange Range) { |
415 | | // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's |
416 | | // useful for OpenCL 1.x too and doesn't require HW support. |
417 | | // opencl_unroll_hint can have 0 arguments (compiler |
418 | | // determines unrolling factor) or 1 argument (the unroll factor provided |
419 | | // by the user). |
420 | 21 | unsigned UnrollFactor = 0; |
421 | 21 | if (A.getNumArgs() == 1) { |
422 | 15 | Expr *E = A.getArgAsExpr(0); |
423 | 15 | Optional<llvm::APSInt> ArgVal; |
424 | | |
425 | 15 | if (!(ArgVal = E->getIntegerConstantExpr(S.Context))) { |
426 | 1 | S.Diag(A.getLoc(), diag::err_attribute_argument_type) |
427 | 1 | << A << AANT_ArgumentIntegerConstant << E->getSourceRange(); |
428 | 1 | return nullptr; |
429 | 1 | } |
430 | | |
431 | 14 | int Val = ArgVal->getSExtValue(); |
432 | 14 | if (Val <= 0) { |
433 | 1 | S.Diag(A.getRange().getBegin(), |
434 | 1 | diag::err_attribute_requires_positive_integer) |
435 | 1 | << A << /* positive */ 0; |
436 | 1 | return nullptr; |
437 | 1 | } |
438 | 13 | UnrollFactor = static_cast<unsigned>(Val); |
439 | 13 | } |
440 | | |
441 | 19 | return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor); |
442 | 21 | } |
443 | | |
444 | | static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A, |
445 | 1.87k | SourceRange Range) { |
446 | 1.87k | if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute1.87k ) |
447 | 2 | return nullptr; |
448 | | |
449 | | // Unknown attributes are automatically warned on. Target-specific attributes |
450 | | // which do not apply to the current target architecture are treated as |
451 | | // though they were unknown attributes. |
452 | 1.87k | const TargetInfo *Aux = S.Context.getAuxTargetInfo(); |
453 | 1.87k | if (A.getKind() == ParsedAttr::UnknownAttribute || |
454 | 1.87k | !(1.84k A.existsInTarget(S.Context.getTargetInfo())1.84k || |
455 | 1.84k | (0 S.Context.getLangOpts().SYCLIsDevice0 && Aux0 && |
456 | 34 | A.existsInTarget(*Aux)0 ))) { |
457 | 34 | S.Diag(A.getLoc(), A.isDeclspecAttribute() |
458 | 34 | ? (unsigned)diag::warn_unhandled_ms_attribute_ignored0 |
459 | 34 | : (unsigned)diag::warn_unknown_attribute_ignored) |
460 | 34 | << A << A.getRange(); |
461 | 34 | return nullptr; |
462 | 34 | } |
463 | | |
464 | 1.84k | if (S.checkCommonAttributeFeatures(St, A)) |
465 | 54 | return nullptr; |
466 | | |
467 | 1.78k | switch (A.getKind()) { |
468 | 16 | case ParsedAttr::AT_AlwaysInline: |
469 | 16 | return handleAlwaysInlineAttr(S, St, A, Range); |
470 | 1.08k | case ParsedAttr::AT_FallThrough: |
471 | 1.08k | return handleFallThroughAttr(S, St, A, Range); |
472 | 352 | case ParsedAttr::AT_LoopHint: |
473 | 352 | return handleLoopHintAttr(S, St, A, Range); |
474 | 21 | case ParsedAttr::AT_OpenCLUnrollHint: |
475 | 21 | return handleOpenCLUnrollHint(S, St, A, Range); |
476 | 5 | case ParsedAttr::AT_Suppress: |
477 | 5 | return handleSuppressAttr(S, St, A, Range); |
478 | 23 | case ParsedAttr::AT_NoMerge: |
479 | 23 | return handleNoMergeAttr(S, St, A, Range); |
480 | 19 | case ParsedAttr::AT_NoInline: |
481 | 19 | return handleNoInlineAttr(S, St, A, Range); |
482 | 97 | case ParsedAttr::AT_MustTail: |
483 | 97 | return handleMustTailAttr(S, St, A, Range); |
484 | 76 | case ParsedAttr::AT_Likely: |
485 | 76 | return handleLikely(S, St, A, Range); |
486 | 88 | case ParsedAttr::AT_Unlikely: |
487 | 88 | return handleUnlikely(S, St, A, Range); |
488 | 9 | default: |
489 | | // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a |
490 | | // declaration attribute is not written on a statement, but this code is |
491 | | // needed for attributes in Attr.td that do not list any subjects. |
492 | 9 | S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt) |
493 | 9 | << A << St->getBeginLoc(); |
494 | 9 | return nullptr; |
495 | 1.78k | } |
496 | 1.78k | } |
497 | | |
498 | | void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs, |
499 | 5.31k | SmallVectorImpl<const Attr *> &OutAttrs) { |
500 | 5.31k | for (const ParsedAttr &AL : InAttrs) { |
501 | 1.87k | if (const Attr *A = ProcessStmtAttribute(*this, S, AL, InAttrs.Range)) |
502 | 1.74k | OutAttrs.push_back(A); |
503 | 1.87k | } |
504 | | |
505 | 5.31k | CheckForIncompatibleAttributes(*this, OutAttrs); |
506 | 5.31k | } |