/// 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 *.
///
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;
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];
}];
}
+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)";
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>;
#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"
#include <cassert>
#include <cstring>
#include <string>
-#include <utility>
#include <type_traits>
+#include <utility>
using namespace clang;
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) {
#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;
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
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);
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)) {
// 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.
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
ConditionalEvaluation eval(*this);
{
ApplyDebugLocation DL(*this, Cond);
- EmitBranchOnBoolExpr(CondBOp->getLHS(), LHSTrue, FalseBlock, RHSCount);
+ EmitBranchOnBoolExpr(CondBOp->getLHS(), LHSTrue, FalseBlock, RHSCount,
+ Weights);
EmitBlock(LHSTrue);
}
// 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;
// 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.
!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
ConditionalEvaluation eval(*this);
{
ApplyDebugLocation DL(*this, Cond);
- EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, LHSFalse, LHSCount);
+ EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, LHSFalse, LHSCount,
+ Weights);
EmitBlock(LHSFalse);
}
// 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);
uint64_t FalseCount = getCurrentProfileCount() - TrueCount;
// Negate the condition and swap the destination blocks.
return EmitBranchOnBoolExpr(CondUOp->getSubExpr(), FalseBlock, TrueBlock,
- FalseCount);
+ FalseCount, Weights);
}
}
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
{
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;
// 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;
/// 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.
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");
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);
}
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) {
<< 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,
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
--- /dev/null
+// 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]]}
// 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
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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
<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>
#include "llvm/IR/Function.h"
#include "llvm/IR/PassManager.h"
+#include "llvm/Support/CommandLine.h"
namespace llvm {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &);
};
+extern cl::opt<uint32_t> LikelyBranchWeight;
+extern cl::opt<uint32_t> UnlikelyBranchWeight;
}
#endif
#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"
// '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)"));