//===--- SemaStmtAttr.cpp - Statement Attribute Handling ------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements stmt-related attribute processing. // //===----------------------------------------------------------------------===// #include "clang/Sema/SemaInternal.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/LoopHint.h" #include "clang/Sema/ScopeInfo.h" #include "llvm/ADT/StringExtras.h" using namespace clang; using namespace sema; static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const AttributeList &A, SourceRange Range) { if (!isa(St)) { S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target) << St->getLocStart(); if (isa(St)) { SourceLocation L = S.getLocForEndOfToken(Range.getEnd()); S.Diag(L, diag::note_fallthrough_insert_semi_fixit) << FixItHint::CreateInsertion(L, ";"); } return nullptr; } if (S.getCurFunction()->SwitchStack.empty()) { S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch); return nullptr; } return ::new (S.Context) FallThroughAttr(A.getRange(), S.Context, A.getAttributeSpellingListIndex()); } static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A, SourceRange) { if (St->getStmtClass() != Stmt::DoStmtClass && St->getStmtClass() != Stmt::ForStmtClass && St->getStmtClass() != Stmt::CXXForRangeStmtClass && St->getStmtClass() != Stmt::WhileStmtClass) { S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop); return nullptr; } IdentifierLoc *OptionLoc = A.getArgAsIdent(0); IdentifierInfo *OptionInfo = OptionLoc->Ident; IdentifierLoc *ValueLoc = A.getArgAsIdent(1); IdentifierInfo *ValueInfo = ValueLoc->Ident; Expr *ValueExpr = A.getArgAsExpr(2); assert(OptionInfo && "Attribute must have valid option info."); LoopHintAttr::OptionType Option = llvm::StringSwitch(OptionInfo->getName()) .Case("vectorize", LoopHintAttr::Vectorize) .Case("vectorize_width", LoopHintAttr::VectorizeWidth) .Case("interleave", LoopHintAttr::Interleave) .Case("interleave_count", LoopHintAttr::InterleaveCount) .Case("unroll", LoopHintAttr::Unroll) .Case("unroll_count", LoopHintAttr::UnrollCount) .Default(LoopHintAttr::Vectorize); int ValueInt; if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll) { if (!ValueInfo) { S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword); return nullptr; } if (ValueInfo->isStr("disable")) ValueInt = 0; else if (ValueInfo->isStr("enable")) ValueInt = 1; else { S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword); return nullptr; } } else if (Option == LoopHintAttr::VectorizeWidth || Option == LoopHintAttr::InterleaveCount || Option == LoopHintAttr::UnrollCount) { // FIXME: We should support template parameters for the loop hint value. // See bug report #19610. llvm::APSInt ValueAPS; if (!ValueExpr || !ValueExpr->isIntegerConstantExpr(ValueAPS, S.Context) || (ValueInt = ValueAPS.getSExtValue()) < 1) { S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_value); return nullptr; } } else llvm_unreachable("Unknown loop hint option"); return LoopHintAttr::CreateImplicit(S.Context, Option, ValueInt, A.getRange()); } static void CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl &Attrs) { // There are 3 categories of loop hints: vectorize, interleave, and // unroll. Each comes in two variants: an enable/disable form and a // form which takes a numeric argument. For example: // unroll(enable|disable) and unroll_count(N). The following array // accumulate the hints encountered while iterating through the // attributes to check for compatibility. struct { int EnableOptionId; int NumericOptionId; bool EnabledIsSet; bool ValueIsSet; bool Enabled; int Value; } Options[] = {{LoopHintAttr::Vectorize, LoopHintAttr::VectorizeWidth, false, false, false, 0}, {LoopHintAttr::Interleave, LoopHintAttr::InterleaveCount, false, false, false, 0}, {LoopHintAttr::Unroll, LoopHintAttr::UnrollCount, false, false, false, 0}}; for (const auto *I : Attrs) { const LoopHintAttr *LH = dyn_cast(I); // Skip non loop hint attributes if (!LH) continue; int Option = LH->getOption(); int ValueInt = LH->getValue(); int Category; switch (Option) { case LoopHintAttr::Vectorize: case LoopHintAttr::VectorizeWidth: Category = 0; break; case LoopHintAttr::Interleave: case LoopHintAttr::InterleaveCount: Category = 1; break; case LoopHintAttr::Unroll: case LoopHintAttr::UnrollCount: Category = 2; break; }; auto &CategoryState = Options[Category]; SourceLocation ValueLoc = LH->getRange().getEnd(); if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll) { // Enable|disable hint. For example, vectorize(enable). if (CategoryState.EnabledIsSet) { // Cannot specify enable/disable state twice. S.Diag(ValueLoc, diag::err_pragma_loop_compatibility) << /*Duplicate=*/true << LoopHintAttr::getOptionName(Option) << LoopHintAttr::getValueName(CategoryState.Enabled) << LoopHintAttr::getOptionName(Option) << LoopHintAttr::getValueName(ValueInt); } CategoryState.EnabledIsSet = true; CategoryState.Enabled = ValueInt; } else { // Numeric hint. For example, unroll_count(8). if (CategoryState.ValueIsSet) { // Cannot specify numeric hint twice. S.Diag(ValueLoc, diag::err_pragma_loop_compatibility) << /*Duplicate=*/true << LoopHintAttr::getOptionName(Option) << CategoryState.Value << LoopHintAttr::getOptionName(Option) << ValueInt; } CategoryState.ValueIsSet = true; CategoryState.Value = ValueInt; } if (CategoryState.EnabledIsSet && !CategoryState.Enabled && CategoryState.ValueIsSet) { // Disable hints are not compatible with numeric hints of the // same category. S.Diag(ValueLoc, diag::err_pragma_loop_compatibility) << /*Duplicate=*/false << LoopHintAttr::getOptionName(CategoryState.EnableOptionId) << LoopHintAttr::getValueName(CategoryState.Enabled) << LoopHintAttr::getOptionName(CategoryState.NumericOptionId) << CategoryState.Value; } } } static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A, SourceRange Range) { switch (A.getKind()) { case AttributeList::UnknownAttribute: S.Diag(A.getLoc(), A.isDeclspecAttribute() ? diag::warn_unhandled_ms_attribute_ignored : diag::warn_unknown_attribute_ignored) << A.getName(); return nullptr; case AttributeList::AT_FallThrough: return handleFallThroughAttr(S, St, A, Range); case AttributeList::AT_LoopHint: return handleLoopHintAttr(S, St, A, Range); default: // if we're here, then we parsed a known attribute, but didn't recognize // it as a statement attribute => it is declaration attribute S.Diag(A.getRange().getBegin(), diag::err_attribute_invalid_on_stmt) << A.getName() << St->getLocStart(); return nullptr; } } StmtResult Sema::ProcessStmtAttributes(Stmt *S, AttributeList *AttrList, SourceRange Range) { SmallVector Attrs; for (const AttributeList* l = AttrList; l; l = l->getNext()) { if (Attr *a = ProcessStmtAttribute(*this, S, *l, Range)) Attrs.push_back(a); } CheckForIncompatibleAttributes(*this, Attrs); if (Attrs.empty()) return S; return ActOnAttributedStmt(Range.getBegin(), Attrs, S); }