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:
Mark de Wever 2020-09-09 19:12:32 +02:00
parent db7defd9ba
commit 08196e0b2e
18 changed files with 633 additions and 30 deletions

View File

@ -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;

View File

@ -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];

View File

@ -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)";

View File

@ -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>;

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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.

View File

@ -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");

View File

@ -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);
}

View File

@ -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

View File

@ -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]]}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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)"));