diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -1098,6 +1098,14 @@ /// 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 @@ 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 + determineLikelihoodConflict(const Stmt *Then, const Stmt *Else); + /// Dumps the specified AST fragment and all subtrees to /// \c llvm::errs(). void dump() const; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1288,6 +1288,18 @@ 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]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1684,6 +1684,101 @@ }]; } +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)"; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3141,6 +3141,9 @@ def warn_attribute_after_definition_ignored : Warning< "attribute %0 after definition is ignored">, InGroup; +def warn_attributes_likelihood_ifstmt_conflict + : Warning<"conflicting attributes %0 are ignored">, + InGroup; def warn_cxx11_gnu_attribute_on_type : Warning< "attribute %0 ignored, because it cannot be applied to a type">, InGroup; diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -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 #include #include -#include #include +#include using namespace clang; @@ -129,6 +130,51 @@ StatisticsEnabled = true; } +static std::pair getLikelihood(const Stmt *S) { + if (const auto *AS = dyn_cast_or_null(S)) + for (const auto *A : AS->getAttrs()) { + if (isa(A)) + return std::make_pair(Stmt::LH_Likely, A); + + if (isa(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 +Stmt::determineLikelihoodConflict(const Stmt *Then, const Stmt *Else) { + std::pair LHT = ::getLikelihood(Then); + std::pair 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) { diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -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 @@ EmitBranch(IndGotoBB); } +static Optional> +getLikelihoodWeights(const IfStmt &If) { + switch (Stmt::getLikelihood(If.getThen(), If.getElse())) { + case Stmt::LH_Unlikely: + return std::pair(llvm::UnlikelyBranchWeight, + llvm::LikelyBranchWeight); + case Stmt::LH_None: + return None; + case Stmt::LH_Likely: + return std::pair(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 @@ 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> 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); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4361,7 +4361,8 @@ /// 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. diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1462,16 +1462,15 @@ 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(Cond)) { @@ -1486,7 +1485,7 @@ // 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 @@ 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 @@ 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 @@ // 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 @@ // 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 @@ !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 @@ 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 @@ // 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 @@ 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 @@ 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 @@ { 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 @@ // 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; diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -4018,6 +4018,8 @@ 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"); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -597,6 +597,18 @@ DiagnoseEmptyStmtBody(CondExpr->getEndLoc(), thenStmt, diag::warn_empty_if_body); + std::tuple 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); } diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -210,6 +210,24 @@ 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 &Attrs) { @@ -315,6 +333,32 @@ << 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(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(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 @@ 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 diff --git a/clang/test/CodeGenCXX/attr-likelihood-if-branch-weights.cpp b/clang/test/CodeGenCXX/attr-likelihood-if-branch-weights.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/attr-likelihood-if-branch-weights.cpp @@ -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]]} diff --git a/clang/test/Preprocessor/has_attribute.cpp b/clang/test/Preprocessor/has_attribute.cpp --- a/clang/test/Preprocessor/has_attribute.cpp +++ b/clang/test/Preprocessor/has_attribute.cpp @@ -62,13 +62,13 @@ // 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 diff --git a/clang/test/Sema/attr-likelihood.c b/clang/test/Sema/attr-likelihood.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-likelihood.c @@ -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; +} diff --git a/clang/test/SemaCXX/attr-likelihood.cpp b/clang/test/SemaCXX/attr-likelihood.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/attr-likelihood.cpp @@ -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 diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -987,7 +987,7 @@ [[likely]] and [[unlikely]] attributes P0479R5 - No + Clang 12 (partial) typename optional in more contexts diff --git a/llvm/include/llvm/Transforms/Scalar/LowerExpectIntrinsic.h b/llvm/include/llvm/Transforms/Scalar/LowerExpectIntrinsic.h --- a/llvm/include/llvm/Transforms/Scalar/LowerExpectIntrinsic.h +++ b/llvm/include/llvm/Transforms/Scalar/LowerExpectIntrinsic.h @@ -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 @@ PreservedAnalyses run(Function &F, FunctionAnalysisManager &); }; +extern cl::opt LikelyBranchWeight; +extern cl::opt UnlikelyBranchWeight; } #endif diff --git a/llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp b/llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp --- a/llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp +++ b/llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp @@ -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 @@ // '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 LikelyBranchWeight( +cl::opt 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 UnlikelyBranchWeight( +cl::opt llvm::UnlikelyBranchWeight( "unlikely-branch-weight", cl::Hidden, cl::init(1), cl::desc("Weight of the branch unlikely to be taken (default = 1)"));