
This change adds the new unroll metadata "llvm.loop.unroll.enable" which directs the optimizer to unroll a loop fully if the trip count is known at compile time, and unroll partially if the trip count is not known at compile time. This differs from "llvm.loop.unroll.full" which explicitly does not unroll a loop if the trip count is not known at compile time With this change "#pragma unroll" generates "llvm.loop.unroll.enable" rather than "llvm.loop.unroll.full" metadata. This changes the semantics of "#pragma unroll" slightly to mean "unroll aggressively (fully or partially)" rather than "unroll fully or not at all". The motivating example for this change was some internal code with a loop marked with "#pragma unroll" which only sometimes had a compile-time trip count depending on template magic. When the trip count was a compile-time constant, everything works as expected and the loop is fully unrolled. However, when the trip count was not a compile-time constant the "#pragma unroll" explicitly disabled unrolling of the loop(!). Removing "#pragma unroll" caused the loop to be unrolled partially which was desirable from a performance perspective. llvm-svn: 244467
243 lines
9.3 KiB
C++
243 lines
9.3 KiB
C++
//===--- 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<NullStmt>(St)) {
|
|
S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
|
|
<< St->getLocStart();
|
|
if (isa<SwitchCase>(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) {
|
|
IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
|
|
IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
|
|
IdentifierLoc *StateLoc = A.getArgAsIdent(2);
|
|
Expr *ValueExpr = A.getArgAsExpr(3);
|
|
|
|
bool PragmaUnroll = PragmaNameLoc->Ident->getName() == "unroll";
|
|
bool PragmaNoUnroll = PragmaNameLoc->Ident->getName() == "nounroll";
|
|
if (St->getStmtClass() != Stmt::DoStmtClass &&
|
|
St->getStmtClass() != Stmt::ForStmtClass &&
|
|
St->getStmtClass() != Stmt::CXXForRangeStmtClass &&
|
|
St->getStmtClass() != Stmt::WhileStmtClass) {
|
|
const char *Pragma =
|
|
llvm::StringSwitch<const char *>(PragmaNameLoc->Ident->getName())
|
|
.Case("unroll", "#pragma unroll")
|
|
.Case("nounroll", "#pragma nounroll")
|
|
.Default("#pragma clang loop");
|
|
S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
|
|
return nullptr;
|
|
}
|
|
|
|
LoopHintAttr::Spelling Spelling;
|
|
LoopHintAttr::OptionType Option;
|
|
LoopHintAttr::LoopHintState State;
|
|
if (PragmaNoUnroll) {
|
|
// #pragma nounroll
|
|
Spelling = LoopHintAttr::Pragma_nounroll;
|
|
Option = LoopHintAttr::Unroll;
|
|
State = LoopHintAttr::Disable;
|
|
} else if (PragmaUnroll) {
|
|
Spelling = LoopHintAttr::Pragma_unroll;
|
|
if (ValueExpr) {
|
|
// #pragma unroll N
|
|
Option = LoopHintAttr::UnrollCount;
|
|
State = LoopHintAttr::Numeric;
|
|
} else {
|
|
// #pragma unroll
|
|
Option = LoopHintAttr::Unroll;
|
|
State = LoopHintAttr::Enable;
|
|
}
|
|
} else {
|
|
// #pragma clang loop ...
|
|
Spelling = LoopHintAttr::Pragma_clang_loop;
|
|
assert(OptionLoc && OptionLoc->Ident &&
|
|
"Attribute must have valid option info.");
|
|
Option = llvm::StringSwitch<LoopHintAttr::OptionType>(
|
|
OptionLoc->Ident->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);
|
|
if (Option == LoopHintAttr::VectorizeWidth ||
|
|
Option == LoopHintAttr::InterleaveCount ||
|
|
Option == LoopHintAttr::UnrollCount) {
|
|
assert(ValueExpr && "Attribute must have a valid value expression.");
|
|
if (S.CheckLoopHintExpr(ValueExpr, St->getLocStart()))
|
|
return nullptr;
|
|
State = LoopHintAttr::Numeric;
|
|
} else if (Option == LoopHintAttr::Vectorize ||
|
|
Option == LoopHintAttr::Interleave ||
|
|
Option == LoopHintAttr::Unroll) {
|
|
assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument");
|
|
if (StateLoc->Ident->isStr("disable"))
|
|
State = LoopHintAttr::Disable;
|
|
else if (StateLoc->Ident->isStr("assume_safety"))
|
|
State = LoopHintAttr::AssumeSafety;
|
|
else if (StateLoc->Ident->isStr("full"))
|
|
State = LoopHintAttr::Full;
|
|
else if (StateLoc->Ident->isStr("enable"))
|
|
State = LoopHintAttr::Enable;
|
|
else
|
|
llvm_unreachable("bad loop hint argument");
|
|
} else
|
|
llvm_unreachable("bad loop hint");
|
|
}
|
|
|
|
return LoopHintAttr::CreateImplicit(S.Context, Spelling, Option, State,
|
|
ValueExpr, A.getRange());
|
|
}
|
|
|
|
static void
|
|
CheckForIncompatibleAttributes(Sema &S,
|
|
const SmallVectorImpl<const Attr *> &Attrs) {
|
|
// There are 3 categories of loop hints attributes: vectorize, interleave,
|
|
// and unroll. Each comes in two variants: a state form and a numeric form.
|
|
// The state form selectively defaults/enables/disables the transformation
|
|
// for the loop (for unroll, default indicates full unrolling rather than
|
|
// enabling the transformation). The numeric form form provides an integer
|
|
// hint (for example, unroll count) to the transformer. The following array
|
|
// accumulates the hints encountered while iterating through the attributes
|
|
// to check for compatibility.
|
|
struct {
|
|
const LoopHintAttr *StateAttr;
|
|
const LoopHintAttr *NumericAttr;
|
|
} HintAttrs[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}};
|
|
|
|
for (const auto *I : Attrs) {
|
|
const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
|
|
|
|
// Skip non loop hint attributes
|
|
if (!LH)
|
|
continue;
|
|
|
|
int Option = LH->getOption();
|
|
int Category;
|
|
enum { Vectorize, Interleave, Unroll };
|
|
switch (Option) {
|
|
case LoopHintAttr::Vectorize:
|
|
case LoopHintAttr::VectorizeWidth:
|
|
Category = Vectorize;
|
|
break;
|
|
case LoopHintAttr::Interleave:
|
|
case LoopHintAttr::InterleaveCount:
|
|
Category = Interleave;
|
|
break;
|
|
case LoopHintAttr::Unroll:
|
|
case LoopHintAttr::UnrollCount:
|
|
Category = Unroll;
|
|
break;
|
|
};
|
|
|
|
auto &CategoryState = HintAttrs[Category];
|
|
const LoopHintAttr *PrevAttr;
|
|
if (Option == LoopHintAttr::Vectorize ||
|
|
Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll) {
|
|
// Enable|Disable|AssumeSafety hint. For example, vectorize(enable).
|
|
PrevAttr = CategoryState.StateAttr;
|
|
CategoryState.StateAttr = LH;
|
|
} else {
|
|
// Numeric hint. For example, vectorize_width(8).
|
|
PrevAttr = CategoryState.NumericAttr;
|
|
CategoryState.NumericAttr = LH;
|
|
}
|
|
|
|
PrintingPolicy Policy(S.Context.getLangOpts());
|
|
SourceLocation OptionLoc = LH->getRange().getBegin();
|
|
if (PrevAttr)
|
|
// Cannot specify same type of attribute twice.
|
|
S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
|
|
<< /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)
|
|
<< LH->getDiagnosticName(Policy);
|
|
|
|
if (CategoryState.StateAttr && CategoryState.NumericAttr &&
|
|
(Category == Unroll ||
|
|
CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) {
|
|
// Disable hints are not compatible with numeric hints of the same
|
|
// category. As a special case, numeric unroll hints are also not
|
|
// compatible with enable or full form of the unroll pragma because these
|
|
// directives indicate full unrolling.
|
|
S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
|
|
<< /*Duplicate=*/false
|
|
<< CategoryState.StateAttr->getDiagnosticName(Policy)
|
|
<< CategoryState.NumericAttr->getDiagnosticName(Policy);
|
|
}
|
|
}
|
|
}
|
|
|
|
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<const Attr*, 8> 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);
|
|
}
|