Index: clang/include/clang/AST/Stmt.h =================================================================== --- clang/include/clang/AST/Stmt.h +++ clang/include/clang/AST/Stmt.h @@ -172,6 +172,10 @@ /// True if this if statement has storage for an init statement. unsigned HasInit : 1; + /// The likelihood of taking the ThenStmt. + /// One of the enumeration values in Stmt::Likelihood. + unsigned ThenLikelihood : 2; + /// The location of the "if". SourceLocation IfLoc; }; @@ -1098,6 +1102,14 @@ /// de-serialization). struct EmptyShell {}; + /// The likelihood of a branch being taken. + enum Likelihood { + LH_None, ///< No attribute set. + LH_Likely, ///< Branch has the [[likely]] attribute. + LH_Unlikely, ///< Branch has the [[unlikely]] attribute. + LH_Conflict ///< Both branches of the IfStmt have the same attribute. + }; + protected: /// Iterator for iterating over Stmt * arrays that contain only T *. /// @@ -1918,7 +1930,8 @@ /// Build an if/then/else statement. IfStmt(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond, SourceLocation LParenLoc, - SourceLocation RParenLoc, Stmt *Then, SourceLocation EL, Stmt *Else); + SourceLocation RParenLoc, Stmt *Then, SourceLocation EL, Stmt *Else, + Likelihood LH); /// Build an empty if/then/else statement. explicit IfStmt(EmptyShell Empty, bool HasElse, bool HasVar, bool HasInit); @@ -1929,7 +1942,7 @@ bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond, SourceLocation LPL, SourceLocation RPL, Stmt *Then, SourceLocation EL = SourceLocation(), - Stmt *Else = nullptr); + Stmt *Else = nullptr, Likelihood LH = LH_None); /// Create an empty IfStmt optionally with storage for an else statement, /// condition variable and init expression. @@ -2046,6 +2059,13 @@ bool isConstexpr() const { return IfStmtBits.IsConstexpr; } void setConstexpr(bool C) { IfStmtBits.IsConstexpr = C; } + Likelihood getThenLikelihood() const { + return static_cast(IfStmtBits.ThenLikelihood); + } + void setThenLikelihood(Likelihood LH) { + IfStmtBits.ThenLikelihood = static_cast(LH); + } + /// If this is an 'if constexpr', determine which substatement will be taken. /// Otherwise, or if the condition is value-dependent, returns None. Optional getNondiscardedCase(const ASTContext &Ctx) const; Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ 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]; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -1684,6 +1684,100 @@ }]; } +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 first statement in +the branch with one of the two attributes. + +It isn't allowed to annotate a single statement with both ``likely`` and +``unlikely``. It's allowed to create a contradictions by marking +the first statement in the ``true`` and ``false`` branch with the same +attribute. These contradictions will result in a diagnostic and the compiler +will ignore the attributes. + +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 first +statement in a branch. 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`` 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)"; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3138,6 +3138,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; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -4386,7 +4386,8 @@ StmtResult BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr, SourceLocation LParenLoc, Stmt *InitStmt, ConditionResult Cond, SourceLocation RParenLoc, - Stmt *ThenVal, SourceLocation ElseLoc, Stmt *ElseVal); + Stmt *ThenVal, SourceLocation ElseLoc, Stmt *ElseVal, + Stmt::Likelihood LH); StmtResult ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, SourceLocation LParenLoc, Stmt *InitStmt, ConditionResult Cond, Index: clang/lib/AST/JSONNodeDumper.cpp =================================================================== --- clang/lib/AST/JSONNodeDumper.cpp +++ clang/lib/AST/JSONNodeDumper.cpp @@ -6,6 +6,24 @@ using namespace clang; +static void dumpLikelihood(llvm::json::OStream &JOS, StringRef Key, + Stmt::Likelihood LH) { + switch (LH) { + case Stmt::LH_None: + return; + case Stmt::LH_Likely: + JOS.attribute(Key, "likely"); + return; + case Stmt::LH_Unlikely: + JOS.attribute(Key, "unlikely"); + return; + case Stmt::LH_Conflict: + JOS.attribute(Key, "conflict"); + return; + } + llvm_unreachable("Invalid likelihood value"); +} + void JSONNodeDumper::addPreviousDeclaration(const Decl *D) { switch (D->getKind()) { #define DECL(DERIVED, BASE) \ @@ -1431,6 +1449,7 @@ attributeOnlyIfTrue("hasVar", IS->hasVarStorage()); attributeOnlyIfTrue("hasElse", IS->hasElseStorage()); attributeOnlyIfTrue("isConstexpr", IS->isConstexpr()); + dumpLikelihood(JOS, "thenLikelihood", IS->getThenLikelihood()); } void JSONNodeDumper::VisitSwitchStmt(const SwitchStmt *SS) { Index: clang/lib/AST/Stmt.cpp =================================================================== --- clang/lib/AST/Stmt.cpp +++ clang/lib/AST/Stmt.cpp @@ -828,7 +828,8 @@ IfStmt::IfStmt(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond, SourceLocation LPL, - SourceLocation RPL, Stmt *Then, SourceLocation EL, Stmt *Else) + SourceLocation RPL, Stmt *Then, SourceLocation EL, Stmt *Else, + Likelihood LH) : Stmt(IfStmtClass), LParenLoc(LPL), RParenLoc(RPL) { bool HasElse = Else != nullptr; bool HasVar = Var != nullptr; @@ -836,6 +837,7 @@ IfStmtBits.HasElse = HasElse; IfStmtBits.HasVar = HasVar; IfStmtBits.HasInit = HasInit; + IfStmtBits.ThenLikelihood = LH; setConstexpr(IsConstexpr); @@ -858,12 +860,13 @@ IfStmtBits.HasElse = HasElse; IfStmtBits.HasVar = HasVar; IfStmtBits.HasInit = HasInit; + IfStmtBits.ThenLikelihood = LH_None; } IfStmt *IfStmt::Create(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond, SourceLocation LPL, SourceLocation RPL, Stmt *Then, - SourceLocation EL, Stmt *Else) { + SourceLocation EL, Stmt *Else, Likelihood LH) { bool HasElse = Else != nullptr; bool HasVar = Var != nullptr; bool HasInit = Init != nullptr; @@ -871,8 +874,8 @@ totalSizeToAlloc( NumMandatoryStmtPtr + HasElse + HasVar + HasInit, HasElse), alignof(IfStmt)); - return new (Mem) - IfStmt(Ctx, IL, IsConstexpr, Init, Var, Cond, LPL, RPL, Then, EL, Else); + return new (Mem) IfStmt(Ctx, IL, IsConstexpr, Init, Var, Cond, LPL, RPL, Then, + EL, Else, LH); } IfStmt *IfStmt::CreateEmpty(const ASTContext &Ctx, bool HasElse, bool HasVar, Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -56,6 +56,23 @@ llvm_unreachable("Decl that isn't part of DeclNodes.inc!"); } +static void dumpLikelihood(raw_ostream &OS, Stmt::Likelihood LH) { + switch (LH) { + case Stmt::LH_None: + return; + case Stmt::LH_Likely: + OS << " likely"; + return; + case Stmt::LH_Unlikely: + OS << " unlikely"; + return; + case Stmt::LH_Conflict: + OS << " likelihood_conflict"; + return; + } + llvm_unreachable("Invalid likelihood value"); +} + TextNodeDumper::TextNodeDumper(raw_ostream &OS, const ASTContext &Context, bool ShowColors) : TextTreeStructure(OS, ShowColors), OS(OS), ShowColors(ShowColors), @@ -907,6 +924,7 @@ OS << " has_var"; if (Node->hasElseStorage()) OS << " has_else"; + dumpLikelihood(OS, Node->getThenLikelihood()); } void TextNodeDumper::VisitSwitchStmt(const SwitchStmt *Node) { Index: clang/lib/CodeGen/CGStmt.cpp =================================================================== --- clang/lib/CodeGen/CGStmt.cpp +++ 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; @@ -652,6 +653,24 @@ EmitBranch(IndGotoBB); } +static Optional> +getLikelihoodWeights(const IfStmt &If) { + switch (If.getThenLikelihood()) { + case Stmt::LH_None: + case Stmt::LH_Conflict: + return None; + + case Stmt::LH_Likely: + return std::pair(llvm::LikelyBranchWeight, + llvm::UnlikelyBranchWeight); + + case Stmt::LH_Unlikely: + return std::pair(llvm::UnlikelyBranchWeight, + llvm::LikelyBranchWeight); + } + llvm_unreachable("Unknown Likelihood"); +} + void CodeGenFunction::EmitIfStmt(const IfStmt &S) { // C99 6.8.4.1: The first substatement is executed if the expression compares // unequal to 0. The condition must be a scalar type. @@ -695,8 +714,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); Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -4360,7 +4360,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. Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ 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; Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ 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"); Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -574,6 +574,55 @@ }; } +static std::pair +getLikelihood(const Stmt *Stmt) { + if (const auto *AS = dyn_cast(Stmt)) + 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); +} + +static Stmt::Likelihood calculateLikelihood(Sema &S, const Stmt *Then, + const Stmt *Else) { + assert(Then && "IfStmt without a ThenStmt"); + + std::pair ThenLH = getLikelihood(Then); + if (!Else) + return ThenLH.first; + + std::pair ElseLH = getLikelihood(Else); + if (ThenLH.first == Stmt::LH_None && ElseLH.first == Stmt::LH_None) + return Stmt::LH_None; + + // The returned state is never LH_Conflict, now determine whether there's a + // conflict. A conflict occurs if both branches contain the same attribute. + // The condition where both are LH_None can't happen here. + if (ThenLH.first == ElseLH.first) { + S.Diag(ThenLH.second->getLocation(), + diag::warn_attributes_likelihood_ifstmt_conflict) + << ThenLH.second << ThenLH.second->getRange(); + S.Diag(ElseLH.second->getLocation(), diag::note_conflicting_attribute) + << ElseLH.second << ElseLH.second->getRange(); + + return Stmt::LH_Conflict; + } + if (ThenLH.first != Stmt::LH_None) + return ThenLH.first; + + // The function returs the likelihood of Then so invert the likelihood of + // Else. + if (ElseLH.first == Stmt::LH_Likely) + return Stmt::LH_Unlikely; + + return Stmt::LH_Likely; +} + StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, SourceLocation LParenLoc, Stmt *InitStmt, ConditionResult Cond, SourceLocation RParenLoc, @@ -597,15 +646,16 @@ DiagnoseEmptyStmtBody(CondExpr->getEndLoc(), thenStmt, diag::warn_empty_if_body); + Stmt::Likelihood LH = calculateLikelihood(*this, thenStmt, elseStmt); return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc, - thenStmt, ElseLoc, elseStmt); + thenStmt, ElseLoc, elseStmt, LH); } StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr, SourceLocation LParenLoc, Stmt *InitStmt, ConditionResult Cond, SourceLocation RParenLoc, Stmt *thenStmt, SourceLocation ElseLoc, - Stmt *elseStmt) { + Stmt *elseStmt, Stmt::Likelihood LH) { if (Cond.isInvalid()) return StmtError(); @@ -614,7 +664,7 @@ return IfStmt::Create(Context, IfLoc, IsConstexpr, InitStmt, Cond.get().first, Cond.get().second, LParenLoc, RParenLoc, thenStmt, - ElseLoc, elseStmt); + ElseLoc, elseStmt, LH); } namespace { Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ 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 Index: clang/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- clang/lib/Serialization/ASTReaderStmt.cpp +++ clang/lib/Serialization/ASTReaderStmt.cpp @@ -215,6 +215,7 @@ bool HasElse = Record.readInt(); bool HasVar = Record.readInt(); bool HasInit = Record.readInt(); + S->setThenLikelihood(static_cast(Record.readInt())); S->setCond(Record.readSubExpr()); S->setThen(Record.readSubStmt()); Index: clang/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- clang/lib/Serialization/ASTWriterStmt.cpp +++ clang/lib/Serialization/ASTWriterStmt.cpp @@ -141,6 +141,7 @@ Record.push_back(HasElse); Record.push_back(HasVar); Record.push_back(HasInit); + Record.push_back(S->getThenLikelihood()); Record.AddStmt(S->getCond()); Record.AddStmt(S->getThen()); Index: clang/test/AST/ast-dump-if-json.cpp =================================================================== --- clang/test/AST/ast-dump-if-json.cpp +++ clang/test/AST/ast-dump-if-json.cpp @@ -24,6 +24,16 @@ if (int i = 12; i) ; + + if (val) [[likely]] + ; + + if (val) [[likely]] + ; + if (val) [[unlikely]] + ; + else [[unlikely]] + ; } // NOTE: CHECK lines have been autogenerated by gen_ast_dump_json_test.py @@ -1004,3 +1014,505 @@ // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK-NEXT: } + + +// CHECK: "kind": "IfStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 298, +// CHECK-NEXT: "line": 28, +// CHECK-NEXT: "col": 3, +// CHECK-NEXT: "tokLen": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 322, +// CHECK-NEXT: "line": 29, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "thenLikelihood": "likely", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ImplicitCastExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 302, +// CHECK-NEXT: "line": 28, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 302, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "bool" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "rvalue", +// CHECK-NEXT: "castKind": "IntegralToBoolean", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ImplicitCastExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 302, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 302, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "rvalue", +// CHECK-NEXT: "castKind": "LValueToRValue", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "DeclRefExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 302, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 302, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "lvalue", +// CHECK-NEXT: "referencedDecl": { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ParmVarDecl", +// CHECK-NEXT: "name": "val", +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "AttributedStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 307, +// CHECK-NEXT: "col": 12, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 322, +// CHECK-NEXT: "line": 29, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "LikelyAttr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 309, +// CHECK-NEXT: "line": 28, +// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "tokLen": 6 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 309, +// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "tokLen": 6 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "NullStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 322, +// CHECK-NEXT: "line": 29, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 322, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } + + +// CHECK: "kind": "IfStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 327, +// CHECK-NEXT: "line": 31, +// CHECK-NEXT: "col": 3, +// CHECK-NEXT: "tokLen": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 351, +// CHECK-NEXT: "line": 32, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "thenLikelihood": "likely", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ImplicitCastExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 331, +// CHECK-NEXT: "line": 31, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 331, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "bool" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "rvalue", +// CHECK-NEXT: "castKind": "IntegralToBoolean", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ImplicitCastExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 331, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 331, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "rvalue", +// CHECK-NEXT: "castKind": "LValueToRValue", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "DeclRefExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 331, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 331, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "lvalue", +// CHECK-NEXT: "referencedDecl": { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ParmVarDecl", +// CHECK-NEXT: "name": "val", +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "AttributedStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 336, +// CHECK-NEXT: "col": 12, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 351, +// CHECK-NEXT: "line": 32, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "LikelyAttr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 338, +// CHECK-NEXT: "line": 31, +// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "tokLen": 6 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 338, +// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "tokLen": 6 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "NullStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 351, +// CHECK-NEXT: "line": 32, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 351, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } + + +// CHECK: "kind": "IfStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 355, +// CHECK-NEXT: "line": 33, +// CHECK-NEXT: "col": 3, +// CHECK-NEXT: "tokLen": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 407, +// CHECK-NEXT: "line": 36, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "hasElse": true, +// CHECK-NEXT: "thenLikelihood": "conflict", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ImplicitCastExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 359, +// CHECK-NEXT: "line": 33, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 359, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "bool" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "rvalue", +// CHECK-NEXT: "castKind": "IntegralToBoolean", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ImplicitCastExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 359, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 359, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "rvalue", +// CHECK-NEXT: "castKind": "LValueToRValue", +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "DeclRefExpr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 359, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 359, +// CHECK-NEXT: "col": 7, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "lvalue", +// CHECK-NEXT: "referencedDecl": { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ParmVarDecl", +// CHECK-NEXT: "name": "val", +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "AttributedStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 364, +// CHECK-NEXT: "col": 12, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 381, +// CHECK-NEXT: "line": 34, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "UnlikelyAttr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 366, +// CHECK-NEXT: "line": 33, +// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "tokLen": 8 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 366, +// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "tokLen": 8 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "NullStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 381, +// CHECK-NEXT: "line": 34, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 381, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "AttributedStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 390, +// CHECK-NEXT: "line": 35, +// CHECK-NEXT: "col": 8, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 407, +// CHECK-NEXT: "line": 36, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "UnlikelyAttr", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 392, +// CHECK-NEXT: "line": 35, +// CHECK-NEXT: "col": 10, +// CHECK-NEXT: "tokLen": 8 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 392, +// CHECK-NEXT: "col": 10, +// CHECK-NEXT: "tokLen": 8 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "NullStmt", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 407, +// CHECK-NEXT: "line": 36, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 407, +// CHECK-NEXT: "col": 5, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } Index: clang/test/AST/ast-dump-stmt.cpp =================================================================== --- clang/test/AST/ast-dump-stmt.cpp +++ clang/test/AST/ast-dump-stmt.cpp @@ -126,6 +126,47 @@ // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'bool' lvalue ParmVar 0x{{[^ ]*}} 'b' 'bool' // CHECK-NEXT: NullStmt + if (int i = 12; b) [[likely]] + ; + // CHECK: IfStmt 0x{{[^ ]*}} has_init likely + // CHECK-NEXT: DeclStmt + // CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:11 i 'int' cinit + // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 12 + // CHECK-NEXT: ImplicitCastExpr + // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'bool' lvalue ParmVar 0x{{[^ ]*}} 'b' 'bool' + // CHECK-NEXT: AttributedStmt + // CHECK-NEXT: LikelyAttr + // CHECK-NEXT: NullStmt + + if (int i = 12; b) [[unlikely]] + ; + // CHECK: IfStmt 0x{{[^ ]*}} has_init unlikely + // CHECK-NEXT: DeclStmt + // CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:11 i 'int' cinit + // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 12 + // CHECK-NEXT: ImplicitCastExpr + // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'bool' lvalue ParmVar 0x{{[^ ]*}} 'b' 'bool' + // CHECK-NEXT: AttributedStmt + // CHECK-NEXT: UnlikelyAttr + // CHECK-NEXT: NullStmt + + if (int i = 12; b) [[likely]] + ; + else [[likely]] + ; + // CHECK: IfStmt 0x{{[^ ]*}} has_init has_else likelihood_conflict + // CHECK-NEXT: DeclStmt + // CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:11 i 'int' cinit + // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 12 + // CHECK-NEXT: ImplicitCastExpr + // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'bool' lvalue ParmVar 0x{{[^ ]*}} 'b' 'bool' + // CHECK-NEXT: AttributedStmt + // CHECK-NEXT: LikelyAttr + // CHECK-NEXT: NullStmt + // CHECK-NEXT: AttributedStmt + // CHECK-NEXT: LikelyAttr + // CHECK-NEXT: NullStmt + if constexpr (sizeof(b) == 1) ; // CHECK: IfStmt 0x{{[^ ]*}} Index: clang/test/CodeGenCXX/attr-likelihood-if-branch-weights.cpp =================================================================== --- /dev/null +++ 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]]} Index: clang/test/Preprocessor/has_attribute.cpp =================================================================== --- clang/test/Preprocessor/has_attribute.cpp +++ 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 Index: clang/test/Sema/attr-likelihood.c =================================================================== --- /dev/null +++ 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; +} Index: clang/test/SemaCXX/attr-likelihood.cpp =================================================================== --- /dev/null +++ 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 Index: clang/www/cxx_status.html =================================================================== --- clang/www/cxx_status.html +++ clang/www/cxx_status.html @@ -987,7 +987,7 @@ [[likely]] and [[unlikely]] attributes P0479R5 - No + Clang 12 (partial) typename optional in more contexts Index: llvm/include/llvm/Transforms/Scalar/LowerExpectIntrinsic.h =================================================================== --- llvm/include/llvm/Transforms/Scalar/LowerExpectIntrinsic.h +++ 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 Index: llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp =================================================================== --- llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp +++ 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)"));