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/Sema/SemaInternal.h"
14 #include "clang/AST/ASTContext.h"
15 #include "clang/Basic/SourceManager.h"
16 #include "clang/Sema/DelayedDiagnostic.h"
17 #include "clang/Sema/Lookup.h"
18 #include "clang/Sema/ScopeInfo.h"
19 #include "llvm/ADT/StringExtras.h"
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(A.getRange(), S.Context,
27                        A.getAttributeSpellingListIndex());
28   if (!isa<NullStmt>(St)) {
29     S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
30         << Attr.getSpelling() << St->getBeginLoc();
31     if (isa<SwitchCase>(St)) {
32       SourceLocation L = S.getLocForEndOfToken(Range.getEnd());
33       S.Diag(L, diag::note_fallthrough_insert_semi_fixit)
34           << FixItHint::CreateInsertion(L, ";");
35     }
36     return nullptr;
37   }
38   auto *FnScope = S.getCurFunction();
39   if (FnScope->SwitchStack.empty()) {
40     S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch);
41     return nullptr;
42   }
43 
44   // If this is spelled as the standard C++17 attribute, but not in C++17, warn
45   // about using it as an extension.
46   if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() &&
47       !A.getScopeName())
48     S.Diag(A.getLoc(), diag::ext_cxx17_attr) << A.getName();
49 
50   FnScope->setHasFallthroughStmt();
51   return ::new (S.Context) auto(Attr);
52 }
53 
54 static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A,
55                                 SourceRange Range) {
56   if (A.getNumArgs() < 1) {
57     S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1;
58     return nullptr;
59   }
60 
61   std::vector<StringRef> DiagnosticIdentifiers;
62   for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) {
63     StringRef RuleName;
64 
65     if (!S.checkStringLiteralArgumentAttr(A, I, RuleName, nullptr))
66       return nullptr;
67 
68     // FIXME: Warn if the rule name is unknown. This is tricky because only
69     // clang-tidy knows about available rules.
70     DiagnosticIdentifiers.push_back(RuleName);
71   }
72 
73   return ::new (S.Context) SuppressAttr(
74       A.getRange(), S.Context, DiagnosticIdentifiers.data(),
75       DiagnosticIdentifiers.size(), A.getAttributeSpellingListIndex());
76 }
77 
78 static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
79                                 SourceRange) {
80   IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
81   IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
82   IdentifierLoc *StateLoc = A.getArgAsIdent(2);
83   Expr *ValueExpr = A.getArgAsExpr(3);
84 
85   bool PragmaUnroll = PragmaNameLoc->Ident->getName() == "unroll";
86   bool PragmaNoUnroll = PragmaNameLoc->Ident->getName() == "nounroll";
87   bool PragmaUnrollAndJam = PragmaNameLoc->Ident->getName() == "unroll_and_jam";
88   bool PragmaNoUnrollAndJam =
89       PragmaNameLoc->Ident->getName() == "nounroll_and_jam";
90   if (St->getStmtClass() != Stmt::DoStmtClass &&
91       St->getStmtClass() != Stmt::ForStmtClass &&
92       St->getStmtClass() != Stmt::CXXForRangeStmtClass &&
93       St->getStmtClass() != Stmt::WhileStmtClass) {
94     const char *Pragma =
95         llvm::StringSwitch<const char *>(PragmaNameLoc->Ident->getName())
96             .Case("unroll", "#pragma unroll")
97             .Case("nounroll", "#pragma nounroll")
98             .Case("unroll_and_jam", "#pragma unroll_and_jam")
99             .Case("nounroll_and_jam", "#pragma nounroll_and_jam")
100             .Default("#pragma clang loop");
101     S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
102     return nullptr;
103   }
104 
105   LoopHintAttr::Spelling Spelling =
106       LoopHintAttr::Spelling(A.getAttributeSpellingListIndex());
107   LoopHintAttr::OptionType Option;
108   LoopHintAttr::LoopHintState State;
109   if (PragmaNoUnroll) {
110     // #pragma nounroll
111     Option = LoopHintAttr::Unroll;
112     State = LoopHintAttr::Disable;
113   } else if (PragmaUnroll) {
114     if (ValueExpr) {
115       // #pragma unroll N
116       Option = LoopHintAttr::UnrollCount;
117       State = LoopHintAttr::Numeric;
118     } else {
119       // #pragma unroll
120       Option = LoopHintAttr::Unroll;
121       State = LoopHintAttr::Enable;
122     }
123   } else if (PragmaNoUnrollAndJam) {
124     // #pragma nounroll_and_jam
125     Option = LoopHintAttr::UnrollAndJam;
126     State = LoopHintAttr::Disable;
127   } else if (PragmaUnrollAndJam) {
128     if (ValueExpr) {
129       // #pragma unroll_and_jam N
130       Option = LoopHintAttr::UnrollAndJamCount;
131       State = LoopHintAttr::Numeric;
132     } else {
133       // #pragma unroll_and_jam
134       Option = LoopHintAttr::UnrollAndJam;
135       State = LoopHintAttr::Enable;
136     }
137   } else {
138     // #pragma clang loop ...
139     assert(OptionLoc && OptionLoc->Ident &&
140            "Attribute must have valid option info.");
141     Option = llvm::StringSwitch<LoopHintAttr::OptionType>(
142                  OptionLoc->Ident->getName())
143                  .Case("vectorize", LoopHintAttr::Vectorize)
144                  .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
145                  .Case("interleave", LoopHintAttr::Interleave)
146                  .Case("interleave_count", LoopHintAttr::InterleaveCount)
147                  .Case("unroll", LoopHintAttr::Unroll)
148                  .Case("unroll_count", LoopHintAttr::UnrollCount)
149                  .Case("pipeline", LoopHintAttr::PipelineDisabled)
150                  .Case("pipeline_initiation_interval",
151                        LoopHintAttr::PipelineInitiationInterval)
152                  .Case("distribute", LoopHintAttr::Distribute)
153                  .Default(LoopHintAttr::Vectorize);
154     if (Option == LoopHintAttr::VectorizeWidth ||
155         Option == LoopHintAttr::InterleaveCount ||
156         Option == LoopHintAttr::UnrollCount ||
157         Option == LoopHintAttr::PipelineInitiationInterval) {
158       assert(ValueExpr && "Attribute must have a valid value expression.");
159       if (S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc()))
160         return nullptr;
161       State = LoopHintAttr::Numeric;
162     } else if (Option == LoopHintAttr::Vectorize ||
163                Option == LoopHintAttr::Interleave ||
164                Option == LoopHintAttr::Unroll ||
165                Option == LoopHintAttr::Distribute ||
166                Option == LoopHintAttr::PipelineDisabled) {
167       assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument");
168       if (StateLoc->Ident->isStr("disable"))
169         State = LoopHintAttr::Disable;
170       else if (StateLoc->Ident->isStr("assume_safety"))
171         State = LoopHintAttr::AssumeSafety;
172       else if (StateLoc->Ident->isStr("full"))
173         State = LoopHintAttr::Full;
174       else if (StateLoc->Ident->isStr("enable"))
175         State = LoopHintAttr::Enable;
176       else
177         llvm_unreachable("bad loop hint argument");
178     } else
179       llvm_unreachable("bad loop hint");
180   }
181 
182   return LoopHintAttr::CreateImplicit(S.Context, Spelling, Option, State,
183                                       ValueExpr, A.getRange());
184 }
185 
186 static void
187 CheckForIncompatibleAttributes(Sema &S,
188                                const SmallVectorImpl<const Attr *> &Attrs) {
189   // There are 6 categories of loop hints attributes: vectorize, interleave,
190   // unroll, unroll_and_jam, pipeline and distribute. Except for distribute they
191   // come in two variants: a state form and a numeric form.  The state form
192   // selectively defaults/enables/disables the transformation for the loop
193   // (for unroll, default indicates full unrolling rather than enabling the
194   // transformation). The numeric form form provides an integer hint (for
195   // example, unroll count) to the transformer. The following array accumulates
196   // the hints encountered while iterating through the attributes to check for
197   // compatibility.
198   struct {
199     const LoopHintAttr *StateAttr;
200     const LoopHintAttr *NumericAttr;
201   } HintAttrs[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
202                    {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}};
203 
204   for (const auto *I : Attrs) {
205     const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
206 
207     // Skip non loop hint attributes
208     if (!LH)
209       continue;
210 
211     LoopHintAttr::OptionType Option = LH->getOption();
212     enum {
213       Vectorize,
214       Interleave,
215       Unroll,
216       UnrollAndJam,
217       Distribute,
218       Pipeline
219     } Category;
220     switch (Option) {
221     case LoopHintAttr::Vectorize:
222     case LoopHintAttr::VectorizeWidth:
223       Category = Vectorize;
224       break;
225     case LoopHintAttr::Interleave:
226     case LoopHintAttr::InterleaveCount:
227       Category = Interleave;
228       break;
229     case LoopHintAttr::Unroll:
230     case LoopHintAttr::UnrollCount:
231       Category = Unroll;
232       break;
233     case LoopHintAttr::UnrollAndJam:
234     case LoopHintAttr::UnrollAndJamCount:
235       Category = UnrollAndJam;
236       break;
237     case LoopHintAttr::Distribute:
238       // Perform the check for duplicated 'distribute' hints.
239       Category = Distribute;
240       break;
241     case LoopHintAttr::PipelineDisabled:
242     case LoopHintAttr::PipelineInitiationInterval:
243       Category = Pipeline;
244       break;
245     };
246 
247     assert(Category < sizeof(HintAttrs) / sizeof(HintAttrs[0]));
248     auto &CategoryState = HintAttrs[Category];
249     const LoopHintAttr *PrevAttr;
250     if (Option == LoopHintAttr::Vectorize ||
251         Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll ||
252         Option == LoopHintAttr::UnrollAndJam ||
253         Option == LoopHintAttr::PipelineDisabled ||
254         Option == LoopHintAttr::Distribute) {
255       // Enable|Disable|AssumeSafety hint.  For example, vectorize(enable).
256       PrevAttr = CategoryState.StateAttr;
257       CategoryState.StateAttr = LH;
258     } else {
259       // Numeric hint.  For example, vectorize_width(8).
260       PrevAttr = CategoryState.NumericAttr;
261       CategoryState.NumericAttr = LH;
262     }
263 
264     PrintingPolicy Policy(S.Context.getLangOpts());
265     SourceLocation OptionLoc = LH->getRange().getBegin();
266     if (PrevAttr)
267       // Cannot specify same type of attribute twice.
268       S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
269           << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)
270           << LH->getDiagnosticName(Policy);
271 
272     if (CategoryState.StateAttr && CategoryState.NumericAttr &&
273         (Category == Unroll || Category == UnrollAndJam ||
274          CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) {
275       // Disable hints are not compatible with numeric hints of the same
276       // category.  As a special case, numeric unroll hints are also not
277       // compatible with enable or full form of the unroll pragma because these
278       // directives indicate full unrolling.
279       S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
280           << /*Duplicate=*/false
281           << CategoryState.StateAttr->getDiagnosticName(Policy)
282           << CategoryState.NumericAttr->getDiagnosticName(Policy);
283     }
284   }
285 }
286 
287 static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
288                                     SourceRange Range) {
289   // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's
290   // useful for OpenCL 1.x too and doesn't require HW support.
291   // opencl_unroll_hint can have 0 arguments (compiler
292   // determines unrolling factor) or 1 argument (the unroll factor provided
293   // by the user).
294 
295   unsigned NumArgs = A.getNumArgs();
296 
297   if (NumArgs > 1) {
298     S.Diag(A.getLoc(), diag::err_attribute_too_many_arguments) << A << 1;
299     return nullptr;
300   }
301 
302   unsigned UnrollFactor = 0;
303 
304   if (NumArgs == 1) {
305     Expr *E = A.getArgAsExpr(0);
306     llvm::APSInt ArgVal(32);
307 
308     if (!E->isIntegerConstantExpr(ArgVal, S.Context)) {
309       S.Diag(A.getLoc(), diag::err_attribute_argument_type)
310           << A << AANT_ArgumentIntegerConstant << E->getSourceRange();
311       return nullptr;
312     }
313 
314     int Val = ArgVal.getSExtValue();
315 
316     if (Val <= 0) {
317       S.Diag(A.getRange().getBegin(),
318              diag::err_attribute_requires_positive_integer)
319           << A << /* positive */ 0;
320       return nullptr;
321     }
322     UnrollFactor = Val;
323   }
324 
325   return OpenCLUnrollHintAttr::CreateImplicit(S.Context, UnrollFactor);
326 }
327 
328 static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
329                                   SourceRange Range) {
330   switch (A.getKind()) {
331   case ParsedAttr::UnknownAttribute:
332     S.Diag(A.getLoc(), A.isDeclspecAttribute()
333                            ? (unsigned)diag::warn_unhandled_ms_attribute_ignored
334                            : (unsigned)diag::warn_unknown_attribute_ignored)
335         << A.getName();
336     return nullptr;
337   case ParsedAttr::AT_FallThrough:
338     return handleFallThroughAttr(S, St, A, Range);
339   case ParsedAttr::AT_LoopHint:
340     return handleLoopHintAttr(S, St, A, Range);
341   case ParsedAttr::AT_OpenCLUnrollHint:
342     return handleOpenCLUnrollHint(S, St, A, Range);
343   case ParsedAttr::AT_Suppress:
344     return handleSuppressAttr(S, St, A, Range);
345   default:
346     // if we're here, then we parsed a known attribute, but didn't recognize
347     // it as a statement attribute => it is declaration attribute
348     S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt)
349         << A.getName() << St->getBeginLoc();
350     return nullptr;
351   }
352 }
353 
354 StmtResult Sema::ProcessStmtAttributes(Stmt *S,
355                                        const ParsedAttributesView &AttrList,
356                                        SourceRange Range) {
357   SmallVector<const Attr*, 8> Attrs;
358   for (const ParsedAttr &AL : AttrList) {
359     if (Attr *a = ProcessStmtAttribute(*this, S, AL, Range))
360       Attrs.push_back(a);
361   }
362 
363   CheckForIncompatibleAttributes(*this, Attrs);
364 
365   if (Attrs.empty())
366     return S;
367 
368   return ActOnAttributedStmt(Range.getBegin(), Attrs, S);
369 }
370