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).
|
/// de-serialization).
|
||||||
struct EmptyShell {};
|
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:
|
protected:
|
||||||
/// Iterator for iterating over Stmt * arrays that contain only T *.
|
/// Iterator for iterating over Stmt * arrays that contain only T *.
|
||||||
///
|
///
|
||||||
|
@ -1166,6 +1174,20 @@ public:
|
||||||
static void EnableStatistics();
|
static void EnableStatistics();
|
||||||
static void PrintStats();
|
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
|
/// Dumps the specified AST fragment and all subtrees to
|
||||||
/// \c llvm::errs().
|
/// \c llvm::errs().
|
||||||
void dump() const;
|
void dump() const;
|
||||||
|
|
|
@ -1288,6 +1288,18 @@ def FallThrough : StmtAttr {
|
||||||
let Documentation = [FallthroughDocs];
|
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 {
|
def NoMerge : StmtAttr {
|
||||||
let Spellings = [Clang<"nomerge">];
|
let Spellings = [Clang<"nomerge">];
|
||||||
let Documentation = [NoMergeDocs];
|
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 {
|
def ARMInterruptDocs : Documentation {
|
||||||
let Category = DocCatFunction;
|
let Category = DocCatFunction;
|
||||||
let Heading = "interrupt (ARM)";
|
let Heading = "interrupt (ARM)";
|
||||||
|
|
|
@ -3141,6 +3141,9 @@ def warn_nocf_check_attribute_ignored :
|
||||||
def warn_attribute_after_definition_ignored : Warning<
|
def warn_attribute_after_definition_ignored : Warning<
|
||||||
"attribute %0 after definition is ignored">,
|
"attribute %0 after definition is ignored">,
|
||||||
InGroup<IgnoredAttributes>;
|
InGroup<IgnoredAttributes>;
|
||||||
|
def warn_attributes_likelihood_ifstmt_conflict
|
||||||
|
: Warning<"conflicting attributes %0 are ignored">,
|
||||||
|
InGroup<IgnoredAttributes>;
|
||||||
def warn_cxx11_gnu_attribute_on_type : Warning<
|
def warn_cxx11_gnu_attribute_on_type : Warning<
|
||||||
"attribute %0 ignored, because it cannot be applied to a type">,
|
"attribute %0 ignored, because it cannot be applied to a type">,
|
||||||
InGroup<IgnoredAttributes>;
|
InGroup<IgnoredAttributes>;
|
||||||
|
|
|
@ -13,11 +13,12 @@
|
||||||
#include "clang/AST/Stmt.h"
|
#include "clang/AST/Stmt.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/ASTDiagnostic.h"
|
#include "clang/AST/ASTDiagnostic.h"
|
||||||
|
#include "clang/AST/Attr.h"
|
||||||
#include "clang/AST/Decl.h"
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclGroup.h"
|
#include "clang/AST/DeclGroup.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
#include "clang/AST/ExprConcepts.h"
|
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
#include "clang/AST/ExprOpenMP.h"
|
#include "clang/AST/ExprOpenMP.h"
|
||||||
#include "clang/AST/StmtCXX.h"
|
#include "clang/AST/StmtCXX.h"
|
||||||
|
@ -41,8 +42,8 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|
||||||
|
@ -129,6 +130,51 @@ void Stmt::EnableStatistics() {
|
||||||
StatisticsEnabled = true;
|
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
|
/// Skip no-op (attributed, compound) container stmts and skip captured
|
||||||
/// stmt at the top, if \a IgnoreCaptured is true.
|
/// stmt at the top, if \a IgnoreCaptured is true.
|
||||||
Stmt *Stmt::IgnoreContainers(bool IgnoreCaptured) {
|
Stmt *Stmt::IgnoreContainers(bool IgnoreCaptured) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "llvm/IR/Intrinsics.h"
|
#include "llvm/IR/Intrinsics.h"
|
||||||
#include "llvm/IR/MDBuilder.h"
|
#include "llvm/IR/MDBuilder.h"
|
||||||
#include "llvm/Support/SaveAndRestore.h"
|
#include "llvm/Support/SaveAndRestore.h"
|
||||||
|
#include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h"
|
||||||
|
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace CodeGen;
|
using namespace CodeGen;
|
||||||
|
@ -651,6 +652,20 @@ void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) {
|
||||||
|
|
||||||
EmitBranch(IndGotoBB);
|
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) {
|
void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
|
||||||
// C99 6.8.4.1: The first substatement is executed if the expression compares
|
// 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())
|
if (S.getElse())
|
||||||
ElseBlock = createBasicBlock("if.else");
|
ElseBlock = createBasicBlock("if.else");
|
||||||
|
|
||||||
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock,
|
// Prefer the PGO based weights over the likelihood attribute.
|
||||||
getProfileCount(S.getThen()));
|
// 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.
|
// Emit the 'then' code.
|
||||||
EmitBlock(ThenBlock);
|
EmitBlock(ThenBlock);
|
||||||
|
|
|
@ -1462,16 +1462,15 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an if
|
/// 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
|
/// statement) to the specified blocks. Based on the condition, this might try
|
||||||
/// to simplify the codegen of the conditional based on the branch.
|
/// 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,
|
void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
llvm::BasicBlock *TrueBlock,
|
llvm::BasicBlock *TrueBlock,
|
||||||
llvm::BasicBlock *FalseBlock,
|
llvm::BasicBlock *FalseBlock,
|
||||||
uint64_t TrueCount) {
|
uint64_t TrueCount,
|
||||||
|
llvm::MDNode *Weights) {
|
||||||
Cond = Cond->IgnoreParens();
|
Cond = Cond->IgnoreParens();
|
||||||
|
|
||||||
if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
|
if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
|
||||||
|
@ -1486,7 +1485,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
// br(1 && X) -> br(X).
|
// br(1 && X) -> br(X).
|
||||||
incrementProfileCounter(CondBOp);
|
incrementProfileCounter(CondBOp);
|
||||||
return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock,
|
return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock,
|
||||||
TrueCount);
|
TrueCount, Weights);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have "X && 1", simplify the code to use an uncond branch.
|
// If we have "X && 1", simplify the code to use an uncond branch.
|
||||||
|
@ -1495,7 +1494,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
ConstantBool) {
|
ConstantBool) {
|
||||||
// br(X && 1) -> br(X).
|
// br(X && 1) -> br(X).
|
||||||
return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock,
|
return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock,
|
||||||
TrueCount);
|
TrueCount, Weights);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit the LHS as a conditional. If the LHS conditional is false, we
|
// 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);
|
ConditionalEvaluation eval(*this);
|
||||||
{
|
{
|
||||||
ApplyDebugLocation DL(*this, Cond);
|
ApplyDebugLocation DL(*this, Cond);
|
||||||
EmitBranchOnBoolExpr(CondBOp->getLHS(), LHSTrue, FalseBlock, RHSCount);
|
EmitBranchOnBoolExpr(CondBOp->getLHS(), LHSTrue, FalseBlock, RHSCount,
|
||||||
|
Weights);
|
||||||
EmitBlock(LHSTrue);
|
EmitBlock(LHSTrue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1517,7 +1517,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
|
|
||||||
// Any temporaries created here are conditional.
|
// Any temporaries created here are conditional.
|
||||||
eval.begin(*this);
|
eval.begin(*this);
|
||||||
EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, TrueCount);
|
EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, TrueCount,
|
||||||
|
Weights);
|
||||||
eval.end(*this);
|
eval.end(*this);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -1532,7 +1533,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
// br(0 || X) -> br(X).
|
// br(0 || X) -> br(X).
|
||||||
incrementProfileCounter(CondBOp);
|
incrementProfileCounter(CondBOp);
|
||||||
return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock,
|
return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock,
|
||||||
TrueCount);
|
TrueCount, Weights);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have "X || 0", simplify the code to use an uncond branch.
|
// If we have "X || 0", simplify the code to use an uncond branch.
|
||||||
|
@ -1541,7 +1542,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
!ConstantBool) {
|
!ConstantBool) {
|
||||||
// br(X || 0) -> br(X).
|
// br(X || 0) -> br(X).
|
||||||
return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock,
|
return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock,
|
||||||
TrueCount);
|
TrueCount, Weights);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit the LHS as a conditional. If the LHS conditional is true, we
|
// 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);
|
ConditionalEvaluation eval(*this);
|
||||||
{
|
{
|
||||||
ApplyDebugLocation DL(*this, Cond);
|
ApplyDebugLocation DL(*this, Cond);
|
||||||
EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, LHSFalse, LHSCount);
|
EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, LHSFalse, LHSCount,
|
||||||
|
Weights);
|
||||||
EmitBlock(LHSFalse);
|
EmitBlock(LHSFalse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1566,7 +1568,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
|
|
||||||
// Any temporaries created here are conditional.
|
// Any temporaries created here are conditional.
|
||||||
eval.begin(*this);
|
eval.begin(*this);
|
||||||
EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, RHSCount);
|
EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, RHSCount,
|
||||||
|
Weights);
|
||||||
|
|
||||||
eval.end(*this);
|
eval.end(*this);
|
||||||
|
|
||||||
|
@ -1581,7 +1584,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
uint64_t FalseCount = getCurrentProfileCount() - TrueCount;
|
uint64_t FalseCount = getCurrentProfileCount() - TrueCount;
|
||||||
// Negate the condition and swap the destination blocks.
|
// Negate the condition and swap the destination blocks.
|
||||||
return EmitBranchOnBoolExpr(CondUOp->getSubExpr(), FalseBlock, TrueBlock,
|
return EmitBranchOnBoolExpr(CondUOp->getSubExpr(), FalseBlock, TrueBlock,
|
||||||
FalseCount);
|
FalseCount, Weights);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1592,7 +1595,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||||
|
|
||||||
ConditionalEvaluation cond(*this);
|
ConditionalEvaluation cond(*this);
|
||||||
EmitBranchOnBoolExpr(CondOp->getCond(), LHSBlock, RHSBlock,
|
EmitBranchOnBoolExpr(CondOp->getCond(), LHSBlock, RHSBlock,
|
||||||
getProfileCount(CondOp));
|
getProfileCount(CondOp), Weights);
|
||||||
|
|
||||||
// When computing PGO branch weights, we only know the overall count for
|
// When computing PGO branch weights, we only know the overall count for
|
||||||
// the true block. This code is essentially doing tail duplication of the
|
// 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);
|
ApplyDebugLocation DL(*this, Cond);
|
||||||
EmitBranchOnBoolExpr(CondOp->getLHS(), TrueBlock, FalseBlock,
|
EmitBranchOnBoolExpr(CondOp->getLHS(), TrueBlock, FalseBlock,
|
||||||
LHSScaledTrueCount);
|
LHSScaledTrueCount, Weights);
|
||||||
}
|
}
|
||||||
cond.end(*this);
|
cond.end(*this);
|
||||||
|
|
||||||
cond.begin(*this);
|
cond.begin(*this);
|
||||||
EmitBlock(RHSBlock);
|
EmitBlock(RHSBlock);
|
||||||
EmitBranchOnBoolExpr(CondOp->getRHS(), TrueBlock, FalseBlock,
|
EmitBranchOnBoolExpr(CondOp->getRHS(), TrueBlock, FalseBlock,
|
||||||
TrueCount - LHSScaledTrueCount);
|
TrueCount - LHSScaledTrueCount, Weights);
|
||||||
cond.end(*this);
|
cond.end(*this);
|
||||||
|
|
||||||
return;
|
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
|
// Create branch weights based on the number of times we get here and the
|
||||||
// number of times the condition should be true.
|
// number of times the condition should be true.
|
||||||
uint64_t CurrentCount = std::max(getCurrentProfileCount(), TrueCount);
|
if (!Weights) {
|
||||||
llvm::MDNode *Weights =
|
uint64_t CurrentCount = std::max(getCurrentProfileCount(), TrueCount);
|
||||||
createProfileWeights(TrueCount, CurrentCount - TrueCount);
|
Weights = createProfileWeights(TrueCount, CurrentCount - TrueCount);
|
||||||
|
}
|
||||||
|
|
||||||
// Emit the code with the fully general case.
|
// Emit the code with the fully general case.
|
||||||
llvm::Value *CondV;
|
llvm::Value *CondV;
|
||||||
|
|
|
@ -4361,7 +4361,8 @@ public:
|
||||||
/// TrueCount should be the number of times we expect the condition to
|
/// TrueCount should be the number of times we expect the condition to
|
||||||
/// evaluate to true based on PGO data.
|
/// evaluate to true based on PGO data.
|
||||||
void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
|
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
|
/// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
|
||||||
/// nonnull, if \p LHS is marked _Nonnull.
|
/// nonnull, if \p LHS is marked _Nonnull.
|
||||||
|
|
|
@ -4018,6 +4018,8 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
|
||||||
case ParsedAttr::AT_FallThrough:
|
case ParsedAttr::AT_FallThrough:
|
||||||
case ParsedAttr::AT_CXX11NoReturn:
|
case ParsedAttr::AT_CXX11NoReturn:
|
||||||
case ParsedAttr::AT_NoUniqueAddress:
|
case ParsedAttr::AT_NoUniqueAddress:
|
||||||
|
case ParsedAttr::AT_Likely:
|
||||||
|
case ParsedAttr::AT_Unlikely:
|
||||||
return true;
|
return true;
|
||||||
case ParsedAttr::AT_WarnUnusedResult:
|
case ParsedAttr::AT_WarnUnusedResult:
|
||||||
return !ScopeName && AttrName->getName().equals("nodiscard");
|
return !ScopeName && AttrName->getName().equals("nodiscard");
|
||||||
|
|
|
@ -597,6 +597,18 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
||||||
DiagnoseEmptyStmtBody(CondExpr->getEndLoc(), thenStmt,
|
DiagnoseEmptyStmtBody(CondExpr->getEndLoc(), thenStmt,
|
||||||
diag::warn_empty_if_body);
|
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,
|
return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc,
|
||||||
thenStmt, ElseLoc, elseStmt);
|
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);
|
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
|
static void
|
||||||
CheckForIncompatibleAttributes(Sema &S,
|
CheckForIncompatibleAttributes(Sema &S,
|
||||||
const SmallVectorImpl<const Attr *> &Attrs) {
|
const SmallVectorImpl<const Attr *> &Attrs) {
|
||||||
|
@ -315,6 +333,32 @@ CheckForIncompatibleAttributes(Sema &S,
|
||||||
<< CategoryState.NumericAttr->getDiagnosticName(Policy);
|
<< 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,
|
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);
|
return handleSuppressAttr(S, St, A, Range);
|
||||||
case ParsedAttr::AT_NoMerge:
|
case ParsedAttr::AT_NoMerge:
|
||||||
return handleNoMergeAttr(S, St, A, Range);
|
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:
|
default:
|
||||||
// if we're here, then we parsed a known attribute, but didn't recognize
|
// if we're here, then we parsed a known attribute, but didn't recognize
|
||||||
// it as a statement attribute => it is declaration attribute
|
// 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: ensures: 0
|
||||||
// FIXME(201806L) CHECK: expects: 0
|
// FIXME(201806L) CHECK: expects: 0
|
||||||
// CHECK: fallthrough: 201603L
|
// CHECK: fallthrough: 201603L
|
||||||
// FIXME(201803L) CHECK: likely: 0
|
// FIXME(201803L) CHECK: likely: 2L
|
||||||
// CHECK: maybe_unused: 201603L
|
// CHECK: maybe_unused: 201603L
|
||||||
// ITANIUM: no_unique_address: 201803L
|
// ITANIUM: no_unique_address: 201803L
|
||||||
// WINDOWS: no_unique_address: 0
|
// WINDOWS: no_unique_address: 0
|
||||||
// CHECK: nodiscard: 201907L
|
// CHECK: nodiscard: 201907L
|
||||||
// CHECK: noreturn: 200809L
|
// CHECK: noreturn: 200809L
|
||||||
// FIXME(201803L) CHECK: unlikely: 0
|
// FIXME(201803L) CHECK: unlikely: 2L
|
||||||
|
|
||||||
// Test for Microsoft __declspec attributes
|
// 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>
|
<tr>
|
||||||
<td><tt>[[likely]]</tt> and <tt>[[unlikely]]</tt> attributes</td>
|
<td><tt>[[likely]]</tt> and <tt>[[unlikely]]</tt> attributes</td>
|
||||||
<td><a href="https://wg21.link/p0479r5">P0479R5</a></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>
|
||||||
<tr>
|
<tr>
|
||||||
<td><tt>typename</tt> optional in more contexts</td>
|
<td><tt>typename</tt> optional in more contexts</td>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "llvm/IR/Function.h"
|
#include "llvm/IR/Function.h"
|
||||||
#include "llvm/IR/PassManager.h"
|
#include "llvm/IR/PassManager.h"
|
||||||
|
#include "llvm/Support/CommandLine.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
||||||
|
@ -31,6 +32,8 @@ struct LowerExpectIntrinsicPass : PassInfoMixin<LowerExpectIntrinsicPass> {
|
||||||
PreservedAnalyses run(Function &F, FunctionAnalysisManager &);
|
PreservedAnalyses run(Function &F, FunctionAnalysisManager &);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern cl::opt<uint32_t> LikelyBranchWeight;
|
||||||
|
extern cl::opt<uint32_t> UnlikelyBranchWeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "llvm/IR/Metadata.h"
|
#include "llvm/IR/Metadata.h"
|
||||||
#include "llvm/InitializePasses.h"
|
#include "llvm/InitializePasses.h"
|
||||||
#include "llvm/Pass.h"
|
#include "llvm/Pass.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
#include "llvm/Transforms/Scalar.h"
|
#include "llvm/Transforms/Scalar.h"
|
||||||
#include "llvm/Transforms/Utils/MisExpect.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
|
// 'select' instructions. It may be worthwhile to hoist these values to some
|
||||||
// shared space, so they can be used directly by other passes.
|
// 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),
|
"likely-branch-weight", cl::Hidden, cl::init(2000),
|
||||||
cl::desc("Weight of the branch likely to be taken (default = 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),
|
"unlikely-branch-weight", cl::Hidden, cl::init(1),
|
||||||
cl::desc("Weight of the branch unlikely to be taken (default = 1)"));
|
cl::desc("Weight of the branch unlikely to be taken (default = 1)"));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue