forked from OSchip/llvm-project
Implements [[likely]] and [[unlikely]] in IfStmt.
This is the initial part of the implementation of the C++20 likelihood attributes. It handles the attributes in an if statement. Differential Revision: https://reviews.llvm.org/D85091
This commit is contained in:
parent
db7defd9ba
commit
08196e0b2e
|
@ -1098,6 +1098,14 @@ public:
|
|||
/// de-serialization).
|
||||
struct EmptyShell {};
|
||||
|
||||
/// The likelihood of a branch being taken.
|
||||
enum Likelihood {
|
||||
LH_Unlikely = -1, ///< Branch has the [[unlikely]] attribute.
|
||||
LH_None, ///< No attribute set or branches of the IfStmt have
|
||||
///< the same attribute.
|
||||
LH_Likely ///< Branch has the [[likely]] attribute.
|
||||
};
|
||||
|
||||
protected:
|
||||
/// Iterator for iterating over Stmt * arrays that contain only T *.
|
||||
///
|
||||
|
@ -1166,6 +1174,20 @@ public:
|
|||
static void EnableStatistics();
|
||||
static void PrintStats();
|
||||
|
||||
/// \returns the likelihood of a statement.
|
||||
static Likelihood getLikelihood(const Stmt *S);
|
||||
|
||||
/// \returns the likelihood of the 'then' branch of an 'if' statement. The
|
||||
/// 'else' branch is required to determine whether both branches specify the
|
||||
/// same likelihood, which affects the result.
|
||||
static Likelihood getLikelihood(const Stmt *Then, const Stmt *Else);
|
||||
|
||||
/// \returns whether the likelihood of the branches of an if statement are
|
||||
/// conflicting. When the first element is \c true there's a conflict and
|
||||
/// the Attr's are the conflicting attributes of the Then and Else Stmt.
|
||||
static std::tuple<bool, const Attr *, const Attr *>
|
||||
determineLikelihoodConflict(const Stmt *Then, const Stmt *Else);
|
||||
|
||||
/// Dumps the specified AST fragment and all subtrees to
|
||||
/// \c llvm::errs().
|
||||
void dump() const;
|
||||
|
|
|
@ -1288,6 +1288,18 @@ def FallThrough : StmtAttr {
|
|||
let Documentation = [FallthroughDocs];
|
||||
}
|
||||
|
||||
def Likely : StmtAttr {
|
||||
// FIXME: Change the date to 201803 once the implementation is finished.
|
||||
let Spellings = [CXX11<"", "likely", 2>, C2x<"clang", "likely">];
|
||||
let Documentation = [LikelihoodDocs];
|
||||
}
|
||||
|
||||
def Unlikely : StmtAttr {
|
||||
// FIXME: Change the date to 201803 once the implementation is finished.
|
||||
let Spellings = [CXX11<"", "unlikely", 2>, C2x<"clang", "unlikely">];
|
||||
let Documentation = [LikelihoodDocs];
|
||||
}
|
||||
|
||||
def NoMerge : StmtAttr {
|
||||
let Spellings = [Clang<"nomerge">];
|
||||
let Documentation = [NoMergeDocs];
|
||||
|
|
|
@ -1684,6 +1684,101 @@ Here is an example:
|
|||
}];
|
||||
}
|
||||
|
||||
def LikelihoodDocs : Documentation {
|
||||
let Category = DocCatStmt;
|
||||
let Heading = "likely and unlikely";
|
||||
let Content = [{
|
||||
The ``likely`` and ``unlikely`` attributes are used as compiler hints.
|
||||
The attributes are used to aid the compiler to determine which branch is
|
||||
likely or unlikely to be taken. This is done by marking the branch substatement
|
||||
with one of the two attributes.
|
||||
|
||||
It isn't allowed to annotate a single statement with both ``likely`` and
|
||||
``unlikely``. Annotating the ``true`` and ``false`` branch of an ``if``
|
||||
statement with the same likelihood attribute will result in a diagnostic and
|
||||
the attributes are ignored on both branches.
|
||||
|
||||
These attributes have no effect on the generated code when using
|
||||
PGO (Profile-Guided Optimization) or at optimization level 0.
|
||||
|
||||
In Clang, the attributes will be ignored if they're not placed on the
|
||||
substatement of an ``if`` or ``else`` statement. The C++ Standard recommends
|
||||
to honor them on every statement in the path of execution, but that can be
|
||||
confusing:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
if (b) {
|
||||
[[unlikely]] --b; // In the path of execution,
|
||||
// this branch is considered unlikely.
|
||||
}
|
||||
|
||||
if (b) {
|
||||
--b;
|
||||
if(b)
|
||||
return;
|
||||
[[unlikely]] --b; // Not in the path of execution,
|
||||
} // the branch has no likelihood information.
|
||||
|
||||
if (b) {
|
||||
--b;
|
||||
foo(b);
|
||||
// Whether or not the next statement is in the path of execution depends
|
||||
// on the declaration of foo():
|
||||
// In the path of execution: void foo(int);
|
||||
// Not in the path of execution: [[noreturn]] void foo(int);
|
||||
// This means the likelihood of the branch depends on the declaration
|
||||
// of foo().
|
||||
[[unlikely]] --b;
|
||||
}
|
||||
|
||||
|
||||
At the moment the attribute only has effect when used in an ``if`` or ``else``
|
||||
statement.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
if (b) [[likely]] { // Placement on the first statement in the branch.
|
||||
// The compiler will optimize to execute the code here.
|
||||
} else {
|
||||
}
|
||||
|
||||
if (b)
|
||||
[[unlikely]] b++; // Placement on the first statement in the branch.
|
||||
else {
|
||||
// The compiler will optimize to execute the code here.
|
||||
}
|
||||
|
||||
if (b) {
|
||||
[[unlikely]] b++; // Placement on the second statement in the branch.
|
||||
} // The attribute will be ignored.
|
||||
|
||||
if (b) [[likely]] {
|
||||
[[unlikely]] b++; // No contradiction since the second attribute
|
||||
} // is ignored.
|
||||
|
||||
if (b)
|
||||
;
|
||||
else [[likely]] {
|
||||
// The compiler will optimize to execute the code here.
|
||||
}
|
||||
|
||||
if (b)
|
||||
;
|
||||
else
|
||||
// The compiler will optimize to execute the next statement.
|
||||
[[likely]] b = f();
|
||||
|
||||
if (b) [[likely]]; // Both branches are likely. A diagnostic is issued
|
||||
else [[likely]]; // and the attributes are ignored.
|
||||
|
||||
if (b)
|
||||
[[likely]] int i = 5; // Issues a diagnostic since the attribute
|
||||
// isn't allowed on a declaration.
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
def ARMInterruptDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Heading = "interrupt (ARM)";
|
||||
|
|
|
@ -3141,6 +3141,9 @@ def warn_nocf_check_attribute_ignored :
|
|||
def warn_attribute_after_definition_ignored : Warning<
|
||||
"attribute %0 after definition is ignored">,
|
||||
InGroup<IgnoredAttributes>;
|
||||
def warn_attributes_likelihood_ifstmt_conflict
|
||||
: Warning<"conflicting attributes %0 are ignored">,
|
||||
InGroup<IgnoredAttributes>;
|
||||
def warn_cxx11_gnu_attribute_on_type : Warning<
|
||||
"attribute %0 ignored, because it cannot be applied to a type">,
|
||||
InGroup<IgnoredAttributes>;
|
||||
|
|
|
@ -13,11 +13,12 @@
|
|||
#include "clang/AST/Stmt.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/ASTDiagnostic.h"
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclGroup.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/ExprConcepts.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/ExprConcepts.h"
|
||||
#include "clang/AST/ExprObjC.h"
|
||||
#include "clang/AST/ExprOpenMP.h"
|
||||
#include "clang/AST/StmtCXX.h"
|
||||
|
@ -41,8 +42,8 @@
|
|||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using namespace clang;
|
||||
|
||||
|
@ -129,6 +130,51 @@ void Stmt::EnableStatistics() {
|
|||
StatisticsEnabled = true;
|
||||
}
|
||||
|
||||
static std::pair<Stmt::Likelihood, const Attr *> getLikelihood(const Stmt *S) {
|
||||
if (const auto *AS = dyn_cast_or_null<AttributedStmt>(S))
|
||||
for (const auto *A : AS->getAttrs()) {
|
||||
if (isa<LikelyAttr>(A))
|
||||
return std::make_pair(Stmt::LH_Likely, A);
|
||||
|
||||
if (isa<UnlikelyAttr>(A))
|
||||
return std::make_pair(Stmt::LH_Unlikely, A);
|
||||
}
|
||||
|
||||
return std::make_pair(Stmt::LH_None, nullptr);
|
||||
}
|
||||
|
||||
Stmt::Likelihood Stmt::getLikelihood(const Stmt *S) {
|
||||
return ::getLikelihood(S).first;
|
||||
}
|
||||
|
||||
Stmt::Likelihood Stmt::getLikelihood(const Stmt *Then, const Stmt *Else) {
|
||||
Likelihood LHT = ::getLikelihood(Then).first;
|
||||
Likelihood LHE = ::getLikelihood(Else).first;
|
||||
if (LHE == LH_None)
|
||||
return LHT;
|
||||
|
||||
// If the same attribute is used on both branches there's a conflict.
|
||||
if (LHT == LHE)
|
||||
return LH_None;
|
||||
|
||||
if (LHT != LH_None)
|
||||
return LHT;
|
||||
|
||||
// Invert the value of Else to get the value for Then.
|
||||
return LHE == LH_Likely ? LH_Unlikely : LH_Likely;
|
||||
}
|
||||
|
||||
std::tuple<bool, const Attr *, const Attr *>
|
||||
Stmt::determineLikelihoodConflict(const Stmt *Then, const Stmt *Else) {
|
||||
std::pair<Likelihood, const Attr *> LHT = ::getLikelihood(Then);
|
||||
std::pair<Likelihood, const Attr *> LHE = ::getLikelihood(Else);
|
||||
// If the same attribute is used on both branches there's a conflict.
|
||||
if (LHT.first != LH_None && LHT.first == LHE.first)
|
||||
return std::make_tuple(true, LHT.second, LHE.second);
|
||||
|
||||
return std::make_tuple(false, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/// Skip no-op (attributed, compound) container stmts and skip captured
|
||||
/// stmt at the top, if \a IgnoreCaptured is true.
|
||||
Stmt *Stmt::IgnoreContainers(bool IgnoreCaptured) {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/Support/SaveAndRestore.h"
|
||||
#include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace CodeGen;
|
||||
|
@ -651,6 +652,20 @@ void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) {
|
|||
|
||||
EmitBranch(IndGotoBB);
|
||||
}
|
||||
static Optional<std::pair<uint32_t, uint32_t>>
|
||||
getLikelihoodWeights(const IfStmt &If) {
|
||||
switch (Stmt::getLikelihood(If.getThen(), If.getElse())) {
|
||||
case Stmt::LH_Unlikely:
|
||||
return std::pair<uint32_t, uint32_t>(llvm::UnlikelyBranchWeight,
|
||||
llvm::LikelyBranchWeight);
|
||||
case Stmt::LH_None:
|
||||
return None;
|
||||
case Stmt::LH_Likely:
|
||||
return std::pair<uint32_t, uint32_t>(llvm::LikelyBranchWeight,
|
||||
llvm::UnlikelyBranchWeight);
|
||||
}
|
||||
llvm_unreachable("Unknown Likelihood");
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
|
||||
// C99 6.8.4.1: The first substatement is executed if the expression compares
|
||||
|
@ -695,8 +710,20 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
|
|||
if (S.getElse())
|
||||
ElseBlock = createBasicBlock("if.else");
|
||||
|
||||
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock,
|
||||
getProfileCount(S.getThen()));
|
||||
// Prefer the PGO based weights over the likelihood attribute.
|
||||
// When the build isn't optimized the metadata isn't used, so don't generate
|
||||
// it.
|
||||
llvm::MDNode *Weights = nullptr;
|
||||
uint64_t Count = getProfileCount(S.getThen());
|
||||
if (!Count && CGM.getCodeGenOpts().OptimizationLevel) {
|
||||
Optional<std::pair<uint32_t, uint32_t>> LHW = getLikelihoodWeights(S);
|
||||
if (LHW) {
|
||||
llvm::MDBuilder MDHelper(CGM.getLLVMContext());
|
||||
Weights = MDHelper.createBranchWeights(LHW->first, LHW->second);
|
||||
}
|
||||
}
|
||||
|
||||
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, Count, Weights);
|
||||
|
||||
// Emit the 'then' code.
|
||||
EmitBlock(ThenBlock);
|
||||
|
|
|
@ -1462,16 +1462,15 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an if
|
||||
/// statement) to the specified blocks. Based on the condition, this might try
|
||||
/// to simplify the codegen of the conditional based on the branch.
|
||||
///
|
||||
/// \param Weights The weights determined by the likelihood attributes.
|
||||
void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
llvm::BasicBlock *TrueBlock,
|
||||
llvm::BasicBlock *FalseBlock,
|
||||
uint64_t TrueCount) {
|
||||
uint64_t TrueCount,
|
||||
llvm::MDNode *Weights) {
|
||||
Cond = Cond->IgnoreParens();
|
||||
|
||||
if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
|
||||
|
@ -1486,7 +1485,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
// br(1 && X) -> br(X).
|
||||
incrementProfileCounter(CondBOp);
|
||||
return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock,
|
||||
TrueCount);
|
||||
TrueCount, Weights);
|
||||
}
|
||||
|
||||
// If we have "X && 1", simplify the code to use an uncond branch.
|
||||
|
@ -1495,7 +1494,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
ConstantBool) {
|
||||
// br(X && 1) -> br(X).
|
||||
return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock,
|
||||
TrueCount);
|
||||
TrueCount, Weights);
|
||||
}
|
||||
|
||||
// Emit the LHS as a conditional. If the LHS conditional is false, we
|
||||
|
@ -1508,7 +1507,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
ConditionalEvaluation eval(*this);
|
||||
{
|
||||
ApplyDebugLocation DL(*this, Cond);
|
||||
EmitBranchOnBoolExpr(CondBOp->getLHS(), LHSTrue, FalseBlock, RHSCount);
|
||||
EmitBranchOnBoolExpr(CondBOp->getLHS(), LHSTrue, FalseBlock, RHSCount,
|
||||
Weights);
|
||||
EmitBlock(LHSTrue);
|
||||
}
|
||||
|
||||
|
@ -1517,7 +1517,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
|
||||
// Any temporaries created here are conditional.
|
||||
eval.begin(*this);
|
||||
EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, TrueCount);
|
||||
EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, TrueCount,
|
||||
Weights);
|
||||
eval.end(*this);
|
||||
|
||||
return;
|
||||
|
@ -1532,7 +1533,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
// br(0 || X) -> br(X).
|
||||
incrementProfileCounter(CondBOp);
|
||||
return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock,
|
||||
TrueCount);
|
||||
TrueCount, Weights);
|
||||
}
|
||||
|
||||
// If we have "X || 0", simplify the code to use an uncond branch.
|
||||
|
@ -1541,7 +1542,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
!ConstantBool) {
|
||||
// br(X || 0) -> br(X).
|
||||
return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock,
|
||||
TrueCount);
|
||||
TrueCount, Weights);
|
||||
}
|
||||
|
||||
// Emit the LHS as a conditional. If the LHS conditional is true, we
|
||||
|
@ -1557,7 +1558,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
ConditionalEvaluation eval(*this);
|
||||
{
|
||||
ApplyDebugLocation DL(*this, Cond);
|
||||
EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, LHSFalse, LHSCount);
|
||||
EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, LHSFalse, LHSCount,
|
||||
Weights);
|
||||
EmitBlock(LHSFalse);
|
||||
}
|
||||
|
||||
|
@ -1566,7 +1568,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
|
||||
// Any temporaries created here are conditional.
|
||||
eval.begin(*this);
|
||||
EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, RHSCount);
|
||||
EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, RHSCount,
|
||||
Weights);
|
||||
|
||||
eval.end(*this);
|
||||
|
||||
|
@ -1581,7 +1584,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
uint64_t FalseCount = getCurrentProfileCount() - TrueCount;
|
||||
// Negate the condition and swap the destination blocks.
|
||||
return EmitBranchOnBoolExpr(CondUOp->getSubExpr(), FalseBlock, TrueBlock,
|
||||
FalseCount);
|
||||
FalseCount, Weights);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1592,7 +1595,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
|
||||
ConditionalEvaluation cond(*this);
|
||||
EmitBranchOnBoolExpr(CondOp->getCond(), LHSBlock, RHSBlock,
|
||||
getProfileCount(CondOp));
|
||||
getProfileCount(CondOp), Weights);
|
||||
|
||||
// When computing PGO branch weights, we only know the overall count for
|
||||
// the true block. This code is essentially doing tail duplication of the
|
||||
|
@ -1612,14 +1615,14 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
{
|
||||
ApplyDebugLocation DL(*this, Cond);
|
||||
EmitBranchOnBoolExpr(CondOp->getLHS(), TrueBlock, FalseBlock,
|
||||
LHSScaledTrueCount);
|
||||
LHSScaledTrueCount, Weights);
|
||||
}
|
||||
cond.end(*this);
|
||||
|
||||
cond.begin(*this);
|
||||
EmitBlock(RHSBlock);
|
||||
EmitBranchOnBoolExpr(CondOp->getRHS(), TrueBlock, FalseBlock,
|
||||
TrueCount - LHSScaledTrueCount);
|
||||
TrueCount - LHSScaledTrueCount, Weights);
|
||||
cond.end(*this);
|
||||
|
||||
return;
|
||||
|
@ -1650,9 +1653,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
|||
|
||||
// Create branch weights based on the number of times we get here and the
|
||||
// number of times the condition should be true.
|
||||
uint64_t CurrentCount = std::max(getCurrentProfileCount(), TrueCount);
|
||||
llvm::MDNode *Weights =
|
||||
createProfileWeights(TrueCount, CurrentCount - TrueCount);
|
||||
if (!Weights) {
|
||||
uint64_t CurrentCount = std::max(getCurrentProfileCount(), TrueCount);
|
||||
Weights = createProfileWeights(TrueCount, CurrentCount - TrueCount);
|
||||
}
|
||||
|
||||
// Emit the code with the fully general case.
|
||||
llvm::Value *CondV;
|
||||
|
|
|
@ -4361,7 +4361,8 @@ public:
|
|||
/// TrueCount should be the number of times we expect the condition to
|
||||
/// evaluate to true based on PGO data.
|
||||
void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
|
||||
llvm::BasicBlock *FalseBlock, uint64_t TrueCount);
|
||||
llvm::BasicBlock *FalseBlock, uint64_t TrueCount,
|
||||
llvm::MDNode *Weights = nullptr);
|
||||
|
||||
/// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
|
||||
/// nonnull, if \p LHS is marked _Nonnull.
|
||||
|
|
|
@ -4018,6 +4018,8 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
|
|||
case ParsedAttr::AT_FallThrough:
|
||||
case ParsedAttr::AT_CXX11NoReturn:
|
||||
case ParsedAttr::AT_NoUniqueAddress:
|
||||
case ParsedAttr::AT_Likely:
|
||||
case ParsedAttr::AT_Unlikely:
|
||||
return true;
|
||||
case ParsedAttr::AT_WarnUnusedResult:
|
||||
return !ScopeName && AttrName->getName().equals("nodiscard");
|
||||
|
|
|
@ -597,6 +597,18 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
|||
DiagnoseEmptyStmtBody(CondExpr->getEndLoc(), thenStmt,
|
||||
diag::warn_empty_if_body);
|
||||
|
||||
std::tuple<bool, const Attr *, const Attr *> LHC =
|
||||
Stmt::determineLikelihoodConflict(thenStmt, elseStmt);
|
||||
if (std::get<0>(LHC)) {
|
||||
const Attr *ThenAttr = std::get<1>(LHC);
|
||||
const Attr *ElseAttr = std::get<2>(LHC);
|
||||
Diags.Report(ThenAttr->getLocation(),
|
||||
diag::warn_attributes_likelihood_ifstmt_conflict)
|
||||
<< ThenAttr << ThenAttr->getRange();
|
||||
Diags.Report(ElseAttr->getLocation(), diag::note_conflicting_attribute)
|
||||
<< ElseAttr << ElseAttr->getRange();
|
||||
}
|
||||
|
||||
return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc,
|
||||
thenStmt, ElseLoc, elseStmt);
|
||||
}
|
||||
|
|
|
@ -210,6 +210,24 @@ static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
|
|||
return ::new (S.Context) NoMergeAttr(S.Context, A);
|
||||
}
|
||||
|
||||
static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A,
|
||||
SourceRange Range) {
|
||||
|
||||
if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())
|
||||
S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
|
||||
|
||||
return ::new (S.Context) LikelyAttr(S.Context, A);
|
||||
}
|
||||
|
||||
static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,
|
||||
SourceRange Range) {
|
||||
|
||||
if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())
|
||||
S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
|
||||
|
||||
return ::new (S.Context) UnlikelyAttr(S.Context, A);
|
||||
}
|
||||
|
||||
static void
|
||||
CheckForIncompatibleAttributes(Sema &S,
|
||||
const SmallVectorImpl<const Attr *> &Attrs) {
|
||||
|
@ -315,6 +333,32 @@ CheckForIncompatibleAttributes(Sema &S,
|
|||
<< CategoryState.NumericAttr->getDiagnosticName(Policy);
|
||||
}
|
||||
}
|
||||
|
||||
// C++20 [dcl.attr.likelihood]p1 The attribute-token likely shall not appear
|
||||
// in an attribute-specifier-seq that contains the attribute-token unlikely.
|
||||
const LikelyAttr *Likely = nullptr;
|
||||
const UnlikelyAttr *Unlikely = nullptr;
|
||||
for (const auto *I : Attrs) {
|
||||
if (const auto *Attr = dyn_cast<LikelyAttr>(I)) {
|
||||
if (Unlikely) {
|
||||
S.Diag(Attr->getLocation(), diag::err_attributes_are_not_compatible)
|
||||
<< Attr << Unlikely << Attr->getRange();
|
||||
S.Diag(Unlikely->getLocation(), diag::note_conflicting_attribute)
|
||||
<< Unlikely->getRange();
|
||||
return;
|
||||
}
|
||||
Likely = Attr;
|
||||
} else if (const auto *Attr = dyn_cast<UnlikelyAttr>(I)) {
|
||||
if (Likely) {
|
||||
S.Diag(Attr->getLocation(), diag::err_attributes_are_not_compatible)
|
||||
<< Attr << Likely << Attr->getRange();
|
||||
S.Diag(Likely->getLocation(), diag::note_conflicting_attribute)
|
||||
<< Likely->getRange();
|
||||
return;
|
||||
}
|
||||
Unlikely = Attr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
|
||||
|
@ -377,6 +421,10 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
|
|||
return handleSuppressAttr(S, St, A, Range);
|
||||
case ParsedAttr::AT_NoMerge:
|
||||
return handleNoMergeAttr(S, St, A, Range);
|
||||
case ParsedAttr::AT_Likely:
|
||||
return handleLikely(S, St, A, Range);
|
||||
case ParsedAttr::AT_Unlikely:
|
||||
return handleUnlikely(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
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
// RUN: %clang_cc1 -O1 -emit-llvm %s -o - -triple=x86_64-linux-gnu | FileCheck -DLIKELY=2000 -DUNLIKELY=1 %s
|
||||
// RUN: %clang_cc1 -O1 -emit-llvm %s -triple=x86_64-linux-gnu -mllvm -likely-branch-weight=99 -mllvm -unlikely-branch-weight=42 -o - | FileCheck -DLIKELY=99 -DUNLIKELY=42 %s
|
||||
|
||||
extern volatile bool b;
|
||||
extern volatile int i;
|
||||
extern bool A();
|
||||
extern bool B();
|
||||
|
||||
bool f() {
|
||||
// CHECK-LABEL: define zeroext i1 @_Z1fv
|
||||
// CHECK: br {{.*}} !prof !7
|
||||
if (b)
|
||||
[[likely]] {
|
||||
return A();
|
||||
}
|
||||
return B();
|
||||
}
|
||||
|
||||
bool g() {
|
||||
// CHECK-LABEL: define zeroext i1 @_Z1gv
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] {
|
||||
return A();
|
||||
}
|
||||
|
||||
return B();
|
||||
}
|
||||
|
||||
bool h() {
|
||||
// CHECK-LABEL: define zeroext i1 @_Z1hv
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] return A();
|
||||
|
||||
return B();
|
||||
}
|
||||
|
||||
void NullStmt() {
|
||||
// CHECK-LABEL: define{{.*}}NullStmt
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]];
|
||||
else {
|
||||
// Make sure the branches aren't optimized away.
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IfStmt() {
|
||||
// CHECK-LABEL: define{{.*}}IfStmt
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] if (B()) {}
|
||||
|
||||
// CHECK-NOT: br {{.*}} !prof
|
||||
// CHECK: br {{.*}} !prof
|
||||
if (b) {
|
||||
if (B())
|
||||
[[unlikely]] { b = false; }
|
||||
}
|
||||
}
|
||||
|
||||
void WhileStmt() {
|
||||
// CHECK-LABEL: define{{.*}}WhileStmt
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] while (B()) {}
|
||||
|
||||
// CHECK-NOT: br {{.*}} %if.end{{.*}} !prof
|
||||
if (b)
|
||||
while (B())
|
||||
[[unlikely]] { b = false; }
|
||||
}
|
||||
|
||||
void DoStmt() {
|
||||
// CHECK-LABEL: define{{.*}}DoStmt
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] do {}
|
||||
while (B())
|
||||
;
|
||||
|
||||
// CHECK-NOT: br {{.*}} %if.end{{.*}} !prof
|
||||
if (b)
|
||||
do
|
||||
[[unlikely]] {}
|
||||
while (B());
|
||||
}
|
||||
|
||||
void ForStmt() {
|
||||
// CHECK-LABEL: define{{.*}}ForStmt
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] for (; B();) {}
|
||||
|
||||
// CHECK-NOT: br {{.*}} %if.end{{.*}} !prof
|
||||
if (b)
|
||||
for (; B();)
|
||||
[[unlikely]] {}
|
||||
}
|
||||
|
||||
void GotoStmt() {
|
||||
// CHECK-LABEL: define{{.*}}GotoStmt
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] goto end;
|
||||
else {
|
||||
// Make sure the branches aren't optimized away.
|
||||
b = true;
|
||||
}
|
||||
end:;
|
||||
}
|
||||
|
||||
void ReturnStmt() {
|
||||
// CHECK-LABEL: define{{.*}}ReturnStmt
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] return;
|
||||
else {
|
||||
// Make sure the branches aren't optimized away.
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchStmt() {
|
||||
// CHECK-LABEL: define{{.*}}SwitchStmt
|
||||
// CHECK: br {{.*}} !prof !8
|
||||
if (b)
|
||||
[[unlikely]] switch (i) {}
|
||||
else {
|
||||
// Make sure the branches aren't optimized away.
|
||||
b = true;
|
||||
}
|
||||
// CHECK-NOT: br {{.*}} %if.end{{.*}} !prof
|
||||
if (b)
|
||||
switch (i)
|
||||
[[unlikely]] {}
|
||||
else {
|
||||
// Make sure the branches aren't optimized away.
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK: !7 = !{!"branch_weights", i32 [[UNLIKELY]], i32 [[LIKELY]]}
|
||||
// CHECK: !8 = !{!"branch_weights", i32 [[LIKELY]], i32 [[UNLIKELY]]}
|
|
@ -62,13 +62,13 @@ CXX11(unlikely)
|
|||
// FIXME(201806L) CHECK: ensures: 0
|
||||
// FIXME(201806L) CHECK: expects: 0
|
||||
// CHECK: fallthrough: 201603L
|
||||
// FIXME(201803L) CHECK: likely: 0
|
||||
// FIXME(201803L) CHECK: likely: 2L
|
||||
// CHECK: maybe_unused: 201603L
|
||||
// ITANIUM: no_unique_address: 201803L
|
||||
// WINDOWS: no_unique_address: 0
|
||||
// CHECK: nodiscard: 201907L
|
||||
// CHECK: noreturn: 200809L
|
||||
// FIXME(201803L) CHECK: unlikely: 0
|
||||
// FIXME(201803L) CHECK: unlikely: 2L
|
||||
|
||||
// Test for Microsoft __declspec attributes
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// RUN: %clang_cc1 %s -fsyntax-only -fdouble-square-bracket-attributes -verify
|
||||
|
||||
void g() {
|
||||
if (1)
|
||||
[[clang::likely]] {}
|
||||
}
|
||||
void m() {
|
||||
[[clang::likely]] int x = 42; // expected-error {{'likely' attribute cannot be applied to a declaration}}
|
||||
|
||||
if (x)
|
||||
[[clang::unlikely]] {}
|
||||
if (x) {
|
||||
[[clang::unlikely]];
|
||||
}
|
||||
switch (x) {
|
||||
case 1:
|
||||
[[clang::likely]] {}
|
||||
break;
|
||||
[[clang::likely]] case 2 : case 3 : {}
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
[[clang::unlikely]];
|
||||
} while (x);
|
||||
do
|
||||
[[clang::unlikely]] {}
|
||||
while (x);
|
||||
do { // expected-note {{to match this 'do'}}
|
||||
}
|
||||
[[clang::unlikely]] while (x); // expected-error {{expected 'while' in do/while loop}}
|
||||
for (;;)
|
||||
[[clang::unlikely]] {}
|
||||
for (;;) {
|
||||
[[clang::unlikely]];
|
||||
}
|
||||
while (x)
|
||||
[[clang::unlikely]] {}
|
||||
while (x) {
|
||||
[[clang::unlikely]];
|
||||
}
|
||||
|
||||
if (x)
|
||||
goto lbl;
|
||||
|
||||
// FIXME: allow the attribute on the label
|
||||
[[clang::unlikely]] lbl : // expected-error {{'unlikely' attribute cannot be applied to a declaration}}
|
||||
[[clang::likely]] x = x + 1;
|
||||
|
||||
[[clang::likely]]++ x;
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
// RUN: %clang_cc1 %s -fsyntax-only -verify
|
||||
// RUN: %clang_cc1 %s -DPEDANTIC -pedantic -fsyntax-only -verify
|
||||
|
||||
#if PEDANTIC
|
||||
void g() {
|
||||
if (true)
|
||||
[[likely]] {} // expected-warning {{use of the 'likely' attribute is a C++20 extension}}
|
||||
else
|
||||
[[unlikely]] {} // expected-warning {{use of the 'unlikely' attribute is a C++20 extension}}
|
||||
}
|
||||
#else
|
||||
void a() {
|
||||
if (true)
|
||||
[[likely]]; // expected-warning {{conflicting attributes 'likely' are ignored}}
|
||||
else
|
||||
[[likely]]; // expected-note {{conflicting attribute is here}}
|
||||
}
|
||||
|
||||
void b() {
|
||||
if (true)
|
||||
[[unlikely]]; // expected-warning {{conflicting attributes 'unlikely' are ignored}}
|
||||
else
|
||||
[[unlikely]]; // expected-note {{conflicting attribute is here}}
|
||||
}
|
||||
|
||||
void c() {
|
||||
if (true)
|
||||
[[likely]];
|
||||
}
|
||||
|
||||
void d() {
|
||||
if (true)
|
||||
[[unlikely]];
|
||||
}
|
||||
|
||||
void g() {
|
||||
if (true)
|
||||
[[likely]] {}
|
||||
else
|
||||
[[unlikely]] {}
|
||||
}
|
||||
|
||||
void h() {
|
||||
if (true)
|
||||
[[likely]] {}
|
||||
else {
|
||||
}
|
||||
}
|
||||
|
||||
void i() {
|
||||
if (true)
|
||||
[[unlikely]] {}
|
||||
else {
|
||||
}
|
||||
}
|
||||
|
||||
void j() {
|
||||
if (true) {
|
||||
} else
|
||||
[[likely]] {}
|
||||
}
|
||||
|
||||
void k() {
|
||||
if (true) {
|
||||
} else
|
||||
[[likely]] {}
|
||||
}
|
||||
|
||||
void l() {
|
||||
if (true)
|
||||
[[likely]] {}
|
||||
else
|
||||
[[unlikely]] if (false) [[likely]] {}
|
||||
}
|
||||
|
||||
void m() {
|
||||
[[likely]] int x = 42; // expected-error {{'likely' attribute cannot be applied to a declaration}}
|
||||
|
||||
if (x)
|
||||
[[unlikely]] {}
|
||||
if (x) {
|
||||
[[unlikely]];
|
||||
}
|
||||
switch (x) {
|
||||
case 1:
|
||||
[[likely]] {}
|
||||
break;
|
||||
[[likely]] case 2 : case 3 : {}
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
[[unlikely]];
|
||||
} while (x);
|
||||
do
|
||||
[[unlikely]] {}
|
||||
while (x);
|
||||
do { // expected-note {{to match this 'do'}}
|
||||
}
|
||||
[[unlikely]] while (x); // expected-error {{expected 'while' in do/while loop}}
|
||||
for (;;)
|
||||
[[unlikely]] {}
|
||||
for (;;) {
|
||||
[[unlikely]];
|
||||
}
|
||||
while (x)
|
||||
[[unlikely]] {}
|
||||
while (x) {
|
||||
[[unlikely]];
|
||||
}
|
||||
|
||||
switch (x)
|
||||
[[unlikely]] {}
|
||||
|
||||
if (x)
|
||||
goto lbl;
|
||||
|
||||
// FIXME: allow the attribute on the label
|
||||
[[unlikely]] lbl : // expected-error {{'unlikely' attribute cannot be applied to a declaration}}
|
||||
[[likely]] x = x + 1;
|
||||
|
||||
[[likely]]++ x;
|
||||
}
|
||||
|
||||
void n() [[likely]] // expected-error {{'likely' attribute cannot be applied to types}}
|
||||
{
|
||||
try
|
||||
[[likely]] {} // expected-error {{expected '{'}}
|
||||
catch (...) [[likely]] { // expected-error {{expected expression}}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -987,7 +987,7 @@ ISO C++ 2020 Draft International Standard.
|
|||
<tr>
|
||||
<td><tt>[[likely]]</tt> and <tt>[[unlikely]]</tt> attributes</td>
|
||||
<td><a href="https://wg21.link/p0479r5">P0479R5</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="none" align="center">Clang 12 (partial)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><tt>typename</tt> optional in more contexts</td>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
|
@ -31,6 +32,8 @@ struct LowerExpectIntrinsicPass : PassInfoMixin<LowerExpectIntrinsicPass> {
|
|||
PreservedAnalyses run(Function &F, FunctionAnalysisManager &);
|
||||
};
|
||||
|
||||
extern cl::opt<uint32_t> LikelyBranchWeight;
|
||||
extern cl::opt<uint32_t> UnlikelyBranchWeight;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "llvm/IR/Metadata.h"
|
||||
#include "llvm/InitializePasses.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Transforms/Utils/MisExpect.h"
|
||||
|
@ -48,10 +47,10 @@ STATISTIC(ExpectIntrinsicsHandled,
|
|||
// 'select' instructions. It may be worthwhile to hoist these values to some
|
||||
// shared space, so they can be used directly by other passes.
|
||||
|
||||
static cl::opt<uint32_t> LikelyBranchWeight(
|
||||
cl::opt<uint32_t> llvm::LikelyBranchWeight(
|
||||
"likely-branch-weight", cl::Hidden, cl::init(2000),
|
||||
cl::desc("Weight of the branch likely to be taken (default = 2000)"));
|
||||
static cl::opt<uint32_t> UnlikelyBranchWeight(
|
||||
cl::opt<uint32_t> llvm::UnlikelyBranchWeight(
|
||||
"unlikely-branch-weight", cl::Hidden, cl::init(1),
|
||||
cl::desc("Weight of the branch unlikely to be taken (default = 1)"));
|
||||
|
||||
|
|
Loading…
Reference in New Issue