Index: clang/include/clang/AST/Stmt.h =================================================================== --- clang/include/clang/AST/Stmt.h +++ clang/include/clang/AST/Stmt.h @@ -55,6 +55,11 @@ class StringLiteral; class Token; class VarDecl; +class CXXForRangeStmt; + +/// BranchHint - This indicate if a conditional statement is expected to be +/// taken, not taken or no indication. +enum class BranchHint : unsigned { BH_NoHint, BH_Taken, BH_NotTaken }; //===----------------------------------------------------------------------===// // AST classes for statements. @@ -175,6 +180,9 @@ /// True if this if statement has storage for an init statement. unsigned HasInit : 1; + /// holds information about likeliness of the branch being taken + BranchHint Hint : 2; + /// The location of the "if". SourceLocation IfLoc; }; @@ -208,6 +216,9 @@ /// True if the WhileStmt has storage for a condition variable. unsigned HasVar : 1; + /// holds information about likeliness of the branch being taken + BranchHint Hint : 2; + /// The location of the "while". SourceLocation WhileLoc; }; @@ -217,6 +228,9 @@ unsigned : NumStmtBits; + /// holds information about likeliness of the branch being taken + BranchHint Hint : 2; + /// The location of the "do". SourceLocation DoLoc; }; @@ -226,6 +240,9 @@ unsigned : NumStmtBits; + /// holds information about likeliness of the branch being taken + BranchHint Hint : 2; + /// The location of the "for". SourceLocation ForLoc; }; @@ -284,6 +301,15 @@ SourceLocation KeywordLoc; }; + class CXXForRangeStmtBitfields { + friend class CXXForRangeStmt; + + unsigned : NumStmtBits; + + /// holds information about likeliness of the branch being taken + BranchHint Hint : 2; + }; + //===--- Expression bitfields classes ---===// class ExprBitfields { @@ -917,6 +943,9 @@ ReturnStmtBitfields ReturnStmtBits; SwitchCaseBitfields SwitchCaseBits; + // C++ Statements + CXXForRangeStmtBitfields CXXForRangeStmtBits; + // Expressions ExprBitfields ExprBits; PredefinedExprBitfields PredefinedExprBits; @@ -1766,7 +1795,8 @@ /// Build an if/then/else statement. IfStmt(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr, Stmt *Init, - VarDecl *Var, Expr *Cond, Stmt *Then, SourceLocation EL, Stmt *Else); + VarDecl *Var, Expr *Cond, Stmt *Then, SourceLocation EL, Stmt *Else, + BranchHint Hint); /// Build an empty if/then/else statement. explicit IfStmt(EmptyShell Empty, bool HasElse, bool HasVar, bool HasInit); @@ -1776,7 +1806,8 @@ static IfStmt *Create(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond, Stmt *Then, SourceLocation EL = SourceLocation(), - Stmt *Else = nullptr); + Stmt *Else = nullptr, + BranchHint Hint = BranchHint::BH_NoHint); /// Create an empty IfStmt optionally with storage for an else statement, /// condition variable and init expression. @@ -1792,6 +1823,9 @@ /// True if this IfStmt has storage for an else statement. bool hasElseStorage() const { return IfStmtBits.HasElse; } + BranchHint getBranchHint() const { return IfStmtBits.Hint; } + void setBranchHint(BranchHint Hint) { IfStmtBits.Hint = Hint; } + Expr *getCond() { return reinterpret_cast(getTrailingObjects()[condOffset()]); } @@ -2125,7 +2159,7 @@ /// Build a while statement. WhileStmt(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, Stmt *Body, - SourceLocation WL); + SourceLocation WL, BranchHint Hint); /// Build an empty while statement. explicit WhileStmt(EmptyShell Empty, bool HasVar); @@ -2133,7 +2167,7 @@ public: /// Create a while statement. static WhileStmt *Create(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, - Stmt *Body, SourceLocation WL); + Stmt *Body, SourceLocation WL, BranchHint Hint); /// Create an empty while statement optionally with storage for /// a condition variable. @@ -2142,6 +2176,9 @@ /// True if this WhileStmt has storage for a condition variable. bool hasVarStorage() const { return WhileStmtBits.HasVar; } + BranchHint getBranchHint() const { return WhileStmtBits.Hint; } + void setBranchHint(BranchHint Hint) { WhileStmtBits.Hint = Hint; } + Expr *getCond() { return reinterpret_cast(getTrailingObjects()[condOffset()]); } @@ -2223,11 +2260,12 @@ public: DoStmt(Stmt *Body, Expr *Cond, SourceLocation DL, SourceLocation WL, - SourceLocation RP) + SourceLocation RP, BranchHint Hint) : Stmt(DoStmtClass), WhileLoc(WL), RParenLoc(RP) { setCond(Cond); setBody(Body); setDoLoc(DL); + setBranchHint(Hint); } /// Build an empty do-while statement. @@ -2240,6 +2278,9 @@ void setCond(Expr *Cond) { SubExprs[COND] = reinterpret_cast(Cond); } + BranchHint getBranchHint() const { return DoStmtBits.Hint; } + void setBranchHint(BranchHint Hint) { DoStmtBits.Hint = Hint; } + Stmt *getBody() { return SubExprs[BODY]; } const Stmt *getBody() const { return SubExprs[BODY]; } void setBody(Stmt *Body) { SubExprs[BODY] = Body; } @@ -2275,7 +2316,7 @@ public: ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, - SourceLocation RP); + SourceLocation RP, BranchHint Hint); /// Build an empty for statement. explicit ForStmt(EmptyShell Empty) : Stmt(ForStmtClass, Empty) {} @@ -2299,6 +2340,9 @@ return reinterpret_cast(SubExprs[CONDVAR]); } + BranchHint getBranchHint() const { return ForStmtBits.Hint; } + void setBranchHint(BranchHint Hint) { ForStmtBits.Hint = Hint; } + Expr *getCond() { return reinterpret_cast(SubExprs[COND]); } Expr *getInc() { return reinterpret_cast(SubExprs[INC]); } Stmt *getBody() { return SubExprs[BODY]; } Index: clang/include/clang/AST/StmtCXX.h =================================================================== --- clang/include/clang/AST/StmtCXX.h +++ clang/include/clang/AST/StmtCXX.h @@ -138,7 +138,7 @@ CXXForRangeStmt(Stmt *InitStmt, DeclStmt *Range, DeclStmt *Begin, DeclStmt *End, Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body, SourceLocation FL, SourceLocation CAL, - SourceLocation CL, SourceLocation RPL); + SourceLocation CL, SourceLocation RPL, BranchHint Hint); CXXForRangeStmt(EmptyShell Empty) : Stmt(CXXForRangeStmtClass, Empty) { } Stmt *getInit() { return SubExprs[INIT]; } @@ -160,6 +160,9 @@ DeclStmt *getLoopVarStmt() { return cast(SubExprs[LOOPVAR]); } Stmt *getBody() { return SubExprs[BODY]; } + BranchHint getBranchHint() const { return CXXForRangeStmtBits.Hint; } + void setBranchHint(BranchHint Hint) { CXXForRangeStmtBits.Hint = Hint; } + const DeclStmt *getRangeStmt() const { return cast(SubExprs[RANGE]); } Index: clang/include/clang/AST/TextNodeDumper.h =================================================================== --- clang/include/clang/AST/TextNodeDumper.h +++ clang/include/clang/AST/TextNodeDumper.h @@ -225,6 +225,9 @@ void VisitIfStmt(const IfStmt *Node); void VisitSwitchStmt(const SwitchStmt *Node); void VisitWhileStmt(const WhileStmt *Node); + void VisitForStmt(const ForStmt *Node); + void VisitDoStmt(const DoStmt *Node); + void VisitCXXForRangeStmt(const CXXForRangeStmt *Node); void VisitLabelStmt(const LabelStmt *Node); void VisitGotoStmt(const GotoStmt *Node); void VisitCaseStmt(const CaseStmt *Node); Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1147,6 +1147,23 @@ let Documentation = [FallthroughDocs]; } +def Likelihood : StmtAttr { + let Spellings = [CXX11<"", "likely", 201803>, Clang<"likely">, CXX11<"", "unlikely", 201803>, Clang<"unlikely">]; +// let Subjects = [Stmt, LabelStmt]; + let AdditionalMembers = [{ + bool isLikely() { + return getSpelling()[0] == 'l'; + } + bool isUnlikely() { + return getSpelling()[0] == 'u'; + } + bool isEqual(LikelihoodAttr* OtherAttr) { + return getSpelling()[0] == OtherAttr->getSpelling()[0]; + } + }]; + let Documentation = [LikelihoodDocs]; +} + def FastCall : DeclOrTypeAttr { let Spellings = [GCC<"fastcall">, Keyword<"__fastcall">, Keyword<"_fastcall">]; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -1502,6 +1502,39 @@ }]; } +def LikelihoodDocs : Documentation { + let Category = DocCatStmt; + let Heading = "likely / unlikely"; + let Content = [{ + +The ``likely`` or ``unlikely`` attribute is used to annotate that a statement or label is likely or unlikely to executed + +Here is an example: + +.. code-block:: c++ + + void g(int); + int f(int n) { + if (n > 5) [[unlikely]] { // n > 5 is considered to be arbitrarily unlikely + g(0); + return n * 2 + 1; + } + + switch (n) { + case 1: + g(1); + [[fallthrough]]; + + [[likely]] case 2: // n == 2 is considered to be arbitrarily more + g(2); // likely than any other value of n + break; + } + return 3; + } + + }]; +} + 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 @@ -7348,6 +7348,8 @@ "use of the %0 attribute is a C++14 extension">, InGroup; def ext_cxx17_attr : Extension< "use of the %0 attribute is a C++17 extension">, InGroup; +def ext_cxx2a_attr : Extension< + "use of the %0 attribute is a C++2a extension">, InGroup; def warn_unused_comparison : Warning< "%select{equality|inequality|relational|three-way}0 comparison result unused">, @@ -8167,6 +8169,13 @@ "fallthrough annotation in unreachable code">, InGroup, DefaultIgnore; +def warn_likelihood_on_case_outside_switch : Error< + "%0 attribute on %1 statement is outside switch statement">; +def warn_no_likelihood_attr_associated_branch : Warning< + "attribute %0 is not associated with a branch and is ignored">, InGroup; +def warn_conflicting_likelihood_attrs : Warning< + "attribute %0 conflicts with previous attribute %1">, InGroup; + def warn_unreachable_default : Warning< "default label in switch which covers all enumeration values">, InGroup, DefaultIgnore; Index: clang/include/clang/Sema/Scope.h =================================================================== --- clang/include/clang/Sema/Scope.h +++ clang/include/clang/Sema/Scope.h @@ -33,6 +33,7 @@ class DeclContext; class UsingDirectiveDecl; class VarDecl; +class LikelihoodAttr; /// Scope - A scope is a transient data structure that is used while parsing the /// program. It assists with resolving identifiers to the appropriate @@ -163,6 +164,10 @@ /// declared in this scope. unsigned short PrototypeIndex; + /// BranchLikelihoodAttr - This is the Likelihood attribute associated with + /// this Branch or a nullptr. + LikelihoodAttr *BranchLikelihoodAttr; + /// FnParent - If this scope has a parent scope that is a function body, this /// pointer is non-null and points to it. This is used for label processing. Scope *FnParent; @@ -224,6 +229,17 @@ /// isBlockScope - Return true if this scope correspond to a closure. bool isBlockScope() const { return Flags & BlockScope; } + /// getBranchLikelihoodAttr - Return the branching attribute associated with + /// this scope. + const LikelihoodAttr *getBranchLikelihoodAttr() const { + return BranchLikelihoodAttr; + } + LikelihoodAttr *getBranchLikelihoodAttr() { return BranchLikelihoodAttr; } + + void setBranchLikelihoodAttr(LikelihoodAttr *Attribute) { + BranchLikelihoodAttr = Attribute; + } + /// getParent - Return the scope that this is nested in. const Scope *getParent() const { return AnyParent; } Scope *getParent() { return AnyParent; } Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3834,32 +3834,33 @@ Stmt *SubStmt); class ConditionResult; - StmtResult ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, - Stmt *InitStmt, + /// Diagnose conflicting attribute and determinate the hint. + BranchHint HandleIfStmtHint(LikelihoodAttr *ThenLikelihoodAttr, + LikelihoodAttr *ElseLikelihoodAttr); + + StmtResult ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, Stmt *InitStmt, ConditionResult Cond, Stmt *ThenVal, - SourceLocation ElseLoc, Stmt *ElseVal); - StmtResult BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr, - Stmt *InitStmt, + SourceLocation ElseLoc, Stmt *ElseVal, + BranchHint Hint); + StmtResult BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr, Stmt *InitStmt, ConditionResult Cond, Stmt *ThenVal, - SourceLocation ElseLoc, Stmt *ElseVal); - StmtResult ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, - Stmt *InitStmt, + SourceLocation ElseLoc, Stmt *ElseVal, + BranchHint Hint); + StmtResult ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, Stmt *InitStmt, ConditionResult Cond); StmtResult ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, Stmt *Body); StmtResult ActOnWhileStmt(SourceLocation WhileLoc, ConditionResult Cond, - Stmt *Body); + Stmt *Body, BranchHint Hint); StmtResult ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, SourceLocation WhileLoc, SourceLocation CondLParen, - Expr *Cond, SourceLocation CondRParen); - - StmtResult ActOnForStmt(SourceLocation ForLoc, - SourceLocation LParenLoc, - Stmt *First, - ConditionResult Second, - FullExprArg Third, - SourceLocation RParenLoc, - Stmt *Body); + Expr *Cond, SourceLocation CondRParen, + BranchHint Hint); + + StmtResult ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, + Stmt *First, ConditionResult Second, + FullExprArg Third, SourceLocation RParenLoc, + Stmt *Body, BranchHint Hint); ExprResult CheckObjCForCollectionOperand(SourceLocation forLoc, Expr *collection); StmtResult ActOnObjCForCollectionStmt(SourceLocation ForColLoc, @@ -3894,7 +3895,7 @@ Stmt *LoopVarDecl, SourceLocation RParenLoc, BuildForRangeKind Kind); - StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body); + StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body, BranchHint Hint); StmtResult ActOnGotoStmt(SourceLocation GotoLoc, SourceLocation LabelLoc, Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -5682,7 +5682,7 @@ return IfStmt::Create(Importer.getToContext(), ToIfLoc, S->isConstexpr(), ToInit, ToConditionVariable, ToCond, ToThen, ToElseLoc, - ToElse); + ToElse, S->getBranchHint()); } ExpectedStmt ASTNodeImporter::VisitSwitchStmt(SwitchStmt *S) { @@ -5733,7 +5733,7 @@ std::tie(ToConditionVariable, ToCond, ToBody, ToWhileLoc) = *Imp; return WhileStmt::Create(Importer.getToContext(), ToConditionVariable, ToCond, - ToBody, ToWhileLoc); + ToBody, ToWhileLoc, S->getBranchHint()); } ExpectedStmt ASTNodeImporter::VisitDoStmt(DoStmt *S) { @@ -5749,7 +5749,7 @@ std::tie(ToBody, ToCond, ToDoLoc, ToWhileLoc, ToRParenLoc) = *Imp; return new (Importer.getToContext()) DoStmt( - ToBody, ToCond, ToDoLoc, ToWhileLoc, ToRParenLoc); + ToBody, ToCond, ToDoLoc, ToWhileLoc, ToRParenLoc, S->getBranchHint()); } ExpectedStmt ASTNodeImporter::VisitForStmt(ForStmt *S) { @@ -5764,14 +5764,12 @@ VarDecl *ToConditionVariable; Stmt *ToBody; SourceLocation ToForLoc, ToLParenLoc, ToRParenLoc; - std::tie( - ToInit, ToCond, ToConditionVariable, ToInc, ToBody, ToForLoc, - ToLParenLoc, ToRParenLoc) = *Imp; + std::tie(ToInit, ToCond, ToConditionVariable, ToInc, ToBody, ToForLoc, + ToLParenLoc, ToRParenLoc) = *Imp; return new (Importer.getToContext()) ForStmt( - Importer.getToContext(), - ToInit, ToCond, ToConditionVariable, ToInc, ToBody, ToForLoc, ToLParenLoc, - ToRParenLoc); + Importer.getToContext(), ToInit, ToCond, ToConditionVariable, ToInc, + ToBody, ToForLoc, ToLParenLoc, ToRParenLoc, S->getBranchHint()); } ExpectedStmt ASTNodeImporter::VisitGotoStmt(GotoStmt *S) { @@ -5886,9 +5884,10 @@ SourceLocation ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc; std::tie(ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc) = *Imp2; - return new (Importer.getToContext()) CXXForRangeStmt( - ToInit, ToRangeStmt, ToBeginStmt, ToEndStmt, ToCond, ToInc, ToLoopVarStmt, - ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc); + return new (Importer.getToContext()) + CXXForRangeStmt(ToInit, ToRangeStmt, ToBeginStmt, ToEndStmt, ToCond, + ToInc, ToLoopVarStmt, ToBody, ToForLoc, ToCoawaitLoc, + ToColonLoc, ToRParenLoc, S->getBranchHint()); } ExpectedStmt Index: clang/lib/AST/Stmt.cpp =================================================================== --- clang/lib/AST/Stmt.cpp +++ clang/lib/AST/Stmt.cpp @@ -798,7 +798,7 @@ IfStmt::IfStmt(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond, Stmt *Then, - SourceLocation EL, Stmt *Else) + SourceLocation EL, Stmt *Else, BranchHint Hint) : Stmt(IfStmtClass) { bool HasElse = Else != nullptr; bool HasVar = Var != nullptr; @@ -806,6 +806,7 @@ IfStmtBits.HasElse = HasElse; IfStmtBits.HasVar = HasVar; IfStmtBits.HasInit = HasInit; + IfStmtBits.Hint = Hint; setConstexpr(IsConstexpr); @@ -832,7 +833,8 @@ IfStmt *IfStmt::Create(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond, - Stmt *Then, SourceLocation EL, Stmt *Else) { + Stmt *Then, SourceLocation EL, Stmt *Else, + BranchHint Hint) { bool HasElse = Else != nullptr; bool HasVar = Var != nullptr; bool HasInit = Init != nullptr; @@ -841,7 +843,7 @@ NumMandatoryStmtPtr + HasElse + HasVar + HasInit, HasElse), alignof(IfStmt)); return new (Mem) - IfStmt(Ctx, IL, IsConstexpr, Init, Var, Cond, Then, EL, Else); + IfStmt(Ctx, IL, IsConstexpr, Init, Var, Cond, Then, EL, Else, Hint); } IfStmt *IfStmt::CreateEmpty(const ASTContext &Ctx, bool HasElse, bool HasVar, @@ -880,15 +882,15 @@ ForStmt::ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, - SourceLocation RP) - : Stmt(ForStmtClass), LParenLoc(LP), RParenLoc(RP) -{ + SourceLocation RP, BranchHint Hint) + : Stmt(ForStmtClass), LParenLoc(LP), RParenLoc(RP) { SubExprs[INIT] = Init; setConditionVariable(C, condVar); SubExprs[COND] = Cond; SubExprs[INC] = Inc; SubExprs[BODY] = Body; ForStmtBits.ForLoc = FL; + ForStmtBits.Hint = Hint; } VarDecl *ForStmt::getConditionVariable() const { @@ -976,10 +978,11 @@ } WhileStmt::WhileStmt(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, - Stmt *Body, SourceLocation WL) + Stmt *Body, SourceLocation WL, BranchHint Hint) : Stmt(WhileStmtClass) { bool HasVar = Var != nullptr; WhileStmtBits.HasVar = HasVar; + WhileStmtBits.Hint = Hint; setCond(Cond); setBody(Body); @@ -995,12 +998,12 @@ } WhileStmt *WhileStmt::Create(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, - Stmt *Body, SourceLocation WL) { + Stmt *Body, SourceLocation WL, BranchHint Hint) { bool HasVar = Var != nullptr; void *Mem = Ctx.Allocate(totalSizeToAlloc(NumMandatoryStmtPtr + HasVar), alignof(WhileStmt)); - return new (Mem) WhileStmt(Ctx, Var, Cond, Body, WL); + return new (Mem) WhileStmt(Ctx, Var, Cond, Body, WL, Hint); } WhileStmt *WhileStmt::CreateEmpty(const ASTContext &Ctx, bool HasVar) { Index: clang/lib/AST/StmtCXX.cpp =================================================================== --- clang/lib/AST/StmtCXX.cpp +++ clang/lib/AST/StmtCXX.cpp @@ -49,7 +49,7 @@ Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body, SourceLocation FL, SourceLocation CAL, SourceLocation CL, - SourceLocation RPL) + SourceLocation RPL, BranchHint Hint) : Stmt(CXXForRangeStmtClass), ForLoc(FL), CoawaitLoc(CAL), ColonLoc(CL), RParenLoc(RPL) { SubExprs[INIT] = Init; @@ -60,6 +60,7 @@ SubExprs[INC] = Inc; SubExprs[LOOPVAR] = LoopVar; SubExprs[BODY] = Body; + setBranchHint(Hint); } Expr *CXXForRangeStmt::getRangeInit() { Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -658,6 +658,9 @@ OS << " has_var"; if (Node->hasElseStorage()) OS << " has_else"; + if (Node->getBranchHint() != BranchHint::BH_NoHint) + OS << ((Node->getBranchHint() == BranchHint::BH_Taken) ? " likely" + : " unlikely"); } void TextNodeDumper::VisitSwitchStmt(const SwitchStmt *Node) { @@ -670,6 +673,27 @@ void TextNodeDumper::VisitWhileStmt(const WhileStmt *Node) { if (Node->hasVarStorage()) OS << " has_var"; + if (Node->getBranchHint() != BranchHint::BH_NoHint) + OS << (Node->getBranchHint() == BranchHint::BH_Taken ? " likely" + : " unlikely"); +} + +void TextNodeDumper::VisitForStmt(const ForStmt *Node) { + if (Node->getBranchHint() != BranchHint::BH_NoHint) + OS << (Node->getBranchHint() == BranchHint::BH_Taken ? " likely" + : " unlikely"); +} + +void TextNodeDumper::VisitDoStmt(const DoStmt *Node) { + if (Node->getBranchHint() != BranchHint::BH_NoHint) + OS << (Node->getBranchHint() == BranchHint::BH_Taken ? " likely" + : " unlikely"); +} + +void TextNodeDumper::VisitCXXForRangeStmt(const CXXForRangeStmt *Node) { + if (Node->getBranchHint() != BranchHint::BH_NoHint) + OS << (Node->getBranchHint() == BranchHint::BH_Taken ? " likely" + : " unlikely"); } void TextNodeDumper::VisitLabelStmt(const LabelStmt *Node) { Index: clang/lib/Analysis/CFG.cpp =================================================================== --- clang/lib/Analysis/CFG.cpp +++ clang/lib/Analysis/CFG.cpp @@ -584,6 +584,7 @@ CFGBlock *VisitWhileStmt(WhileStmt *W); CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd); + CFGBlock *VisitAttributedStmt(AttributedStmt *S, AddStmtChoice asc); CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc); CFGBlock *VisitChildren(Stmt *S); CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc); @@ -2186,6 +2187,9 @@ case Stmt::StmtExprClass: return VisitStmtExpr(cast(S), asc); + case Stmt::AttributedStmtClass: + return VisitAttributedStmt(cast(S), asc); + case Stmt::SwitchStmtClass: return VisitSwitchStmt(cast(S)); @@ -2197,6 +2201,15 @@ } } +CFGBlock *CFGBuilder::VisitAttributedStmt(AttributedStmt *S, + AddStmtChoice asc) { + if (asc.alwaysAdd(*this, S) && isa(S->getSubStmt())) { + autoCreateBlock(); + appendStmt(Block, S); + } + return Visit(S->getSubStmt()); +} + CFGBlock *CFGBuilder::VisitStmt(Stmt *S, AddStmtChoice asc) { if (asc.alwaysAdd(*this, S)) { autoCreateBlock(); Index: clang/lib/CodeGen/CGStmt.cpp =================================================================== --- clang/lib/CodeGen/CGStmt.cpp +++ clang/lib/CodeGen/CGStmt.cpp @@ -658,7 +658,7 @@ ElseBlock = createBasicBlock("if.else"); EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, - getProfileCount(S.getThen())); + getProfileCount(S.getThen()), S.getBranchHint()); // Emit the 'then' code. EmitBlock(ThenBlock); @@ -691,6 +691,20 @@ EmitBlock(ContBlock, true); } +llvm::Value *CodeGenFunction::MaybeEmitLikelihoodHint(llvm::Value *CondV, + BranchHint Hint) { + if (Hint != BranchHint::BH_NoHint && + CGM.getCodeGenOpts().OptimizationLevel != 0) { + llvm::Constant *ExpectedValue = + llvm::ConstantInt::get(llvm::Type::getInt1Ty(this->getLLVMContext()), + Hint == BranchHint::BH_Taken); + llvm::Function *FnExpect = + CGM.getIntrinsic(llvm::Intrinsic::expect, CondV->getType()); + return Builder.CreateCall(FnExpect, {CondV, ExpectedValue}, "expval"); + } + return CondV; +} + void CodeGenFunction::EmitWhileStmt(const WhileStmt &S, ArrayRef WhileAttrs) { // Emit the header for the loop, which will also become @@ -740,6 +754,7 @@ llvm::BasicBlock *ExitBlock = LoopExit.getBlock(); if (ConditionScope.requiresCleanups()) ExitBlock = createBasicBlock("while.exit"); + BoolCondVal = MaybeEmitLikelihoodHint(BoolCondVal, S.getBranchHint()); Builder.CreateCondBr( BoolCondVal, LoopBody, ExitBlock, createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody()))); @@ -825,6 +840,7 @@ // As long as the condition is true, iterate the loop. if (EmitBoolCondBranch) { uint64_t BackedgeCount = getProfileCount(S.getBody()) - ParentCount; + BoolCondVal = MaybeEmitLikelihoodHint(BoolCondVal, S.getBranchHint()); Builder.CreateCondBr( BoolCondVal, LoopBody, LoopExit.getBlock(), createProfileWeightsForLoop(S.getCond(), BackedgeCount)); @@ -895,6 +911,7 @@ // C99 6.8.5p2/p4: The first substatement is executed if the expression // compares unequal to 0. The condition must be a scalar type. llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); + BoolCondVal = MaybeEmitLikelihoodHint(BoolCondVal, S.getBranchHint()); Builder.CreateCondBr( BoolCondVal, ForBody, ExitBlock, createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody()))); @@ -976,6 +993,7 @@ // The body is executed if the expression, contextually converted // to bool, is true. llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); + MaybeEmitLikelihoodHint(BoolCondVal, S.getBranchHint()); Builder.CreateCondBr( BoolCondVal, ForBody, ExitBlock, createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody()))); Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -2847,10 +2847,12 @@ Address EmitCompoundStmt(const CompoundStmt &S, bool GetLast = false, AggValueSlot AVS = AggValueSlot::ignored()); - Address EmitCompoundStmtWithoutScope(const CompoundStmt &S, - bool GetLast = false, - AggValueSlot AVS = - AggValueSlot::ignored()); + Address + EmitCompoundStmtWithoutScope(const CompoundStmt &S, bool GetLast = false, + AggValueSlot AVS = AggValueSlot::ignored()); + + /// MaybeEmitLikelihoodHint - Emit and llvm.expect if a hint is present. + llvm::Value *MaybeEmitLikelihoodHint(llvm::Value *CondV, BranchHint Hint); /// EmitLabel - Emit the block for the given label. It is legal to call this /// function even if there is no current insertion point. @@ -4037,7 +4039,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, + BranchHint hint = BranchHint::BH_NoHint); /// 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 @@ -1531,7 +1531,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock, - uint64_t TrueCount) { + uint64_t TrueCount, + BranchHint Hint) { Cond = Cond->IgnoreParens(); if (const BinaryOperator *CondBOp = dyn_cast(Cond)) { @@ -1720,6 +1721,16 @@ ApplyDebugLocation DL(*this, Cond); CondV = EvaluateExprAsBool(Cond); } + + if (Hint != BranchHint::BH_NoHint && + CGM.getCodeGenOpts().OptimizationLevel != 0) { + llvm::Constant *ExpectedValue = + llvm::ConstantInt::get(llvm::Type::getInt1Ty(this->getLLVMContext()), + Hint == BranchHint::BH_Taken); + llvm::Function *FnExpect = + CGM.getIntrinsic(llvm::Intrinsic::expect, CondV->getType()); + CondV = Builder.CreateCall(FnExpect, {CondV, ExpectedValue}, "expval"); + } Builder.CreateCondBr(CondV, TrueBlock, FalseBlock, Weights, Unpredictable); } Index: clang/lib/Parse/ParseStmt.cpp =================================================================== --- clang/lib/Parse/ParseStmt.cpp +++ clang/lib/Parse/ParseStmt.cpp @@ -1259,6 +1259,9 @@ // Pop the 'if' scope if needed. InnerScope.Exit(); + LikelihoodAttr *ThenLikelihoodAttr = getCurScope()->getBranchLikelihoodAttr(); + getCurScope()->setBranchLikelihoodAttr(nullptr); + // If it has an else, parse it. SourceLocation ElseLoc; SourceLocation ElseStmtLoc; @@ -1298,6 +1301,7 @@ } else if (InnerStatementTrailingElseLoc.isValid()) { Diag(InnerStatementTrailingElseLoc, diag::warn_dangling_else); } + LikelihoodAttr *ElseLikelihoodAttr = getCurScope()->getBranchLikelihoodAttr(); IfScope.Exit(); @@ -1317,8 +1321,10 @@ if (ElseStmt.isInvalid()) ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc); - return Actions.ActOnIfStmt(IfLoc, IsConstexpr, InitStmt.get(), Cond, - ThenStmt.get(), ElseLoc, ElseStmt.get()); + return Actions.ActOnIfStmt( + IfLoc, IsConstexpr, InitStmt.get(), Cond, ThenStmt.get(), ElseLoc, + ElseStmt.get(), + Actions.HandleIfStmtHint(ThenLikelihoodAttr, ElseLikelihoodAttr)); } /// ParseSwitchStatement @@ -1406,6 +1412,15 @@ return Actions.ActOnFinishSwitchStmt(SwitchLoc, Switch.get(), Body.get()); } +/// Converts a potentially null Likelihood attribute in a BranchHint. +static BranchHint +getHintFromLikelihoodAttr(LikelihoodAttr *BranchLikelihoodAttr) { + if (BranchLikelihoodAttr) + return (BranchLikelihoodAttr->isLikely() ? BranchHint::BH_Taken + : BranchHint::BH_NotTaken); + return BranchHint::BH_NoHint; +} + /// ParseWhileStatement /// while-statement: [C99 6.8.5.1] /// 'while' '(' expression ')' statement @@ -1467,12 +1482,16 @@ // Pop the body scope if needed. InnerScope.Exit(); + LikelihoodAttr *BranchLikelihoodAttr = + getCurScope()->getBranchLikelihoodAttr(); WhileScope.Exit(); if (Cond.isInvalid() || Body.isInvalid()) return StmtError(); - return Actions.ActOnWhileStmt(WhileLoc, Cond, Body.get()); + return Actions.ActOnWhileStmt( + WhileLoc, Cond, Body.get(), + getHintFromLikelihoodAttr(BranchLikelihoodAttr)); } /// ParseDoStatement @@ -1538,13 +1557,16 @@ if (Cond.isUsable()) Cond = Actions.CorrectDelayedTyposInExpr(Cond); T.consumeClose(); + LikelihoodAttr *BranchLikelihoodAttr = + getCurScope()->getBranchLikelihoodAttr(); DoScope.Exit(); if (Cond.isInvalid() || Body.isInvalid()) return StmtError(); return Actions.ActOnDoStmt(DoLoc, Body.get(), WhileLoc, T.getOpenLocation(), - Cond.get(), T.getCloseLocation()); + Cond.get(), T.getCloseLocation(), + getHintFromLikelihoodAttr(BranchLikelihoodAttr)); } bool Parser::isForRangeIdentifier() { @@ -1902,6 +1924,8 @@ // Pop the body scope if needed. InnerScope.Exit(); + LikelihoodAttr *BranchLikelihoodAttr = + getCurScope()->getBranchLikelihoodAttr(); // Leave the for-scope. ForScope.Exit(); @@ -1913,11 +1937,14 @@ Body.get()); if (ForRangeInfo.ParsedForRangeDecl()) - return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get()); + return Actions.FinishCXXForRangeStmt( + ForRangeStmt.get(), Body.get(), + getHintFromLikelihoodAttr(BranchLikelihoodAttr)); return Actions.ActOnForStmt(ForLoc, T.getOpenLocation(), FirstPart.get(), SecondPart, ThirdPart, T.getCloseLocation(), - Body.get()); + Body.get(), + getHintFromLikelihoodAttr(BranchLikelihoodAttr)); } /// ParseGotoStatement Index: clang/lib/Sema/Scope.cpp =================================================================== --- clang/lib/Sema/Scope.cpp +++ clang/lib/Sema/Scope.cpp @@ -87,6 +87,7 @@ void Scope::Init(Scope *parent, unsigned flags) { setFlags(parent, flags); + BranchLikelihoodAttr = nullptr; DeclsInScope.clear(); UsingDirectives.clear(); Entity = nullptr; Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -11821,13 +11821,15 @@ return S.ActOnForStmt( Loc, Loc, InitStmt, S.ActOnCondition(nullptr, Loc, Comparison, Sema::ConditionKind::Boolean), - S.MakeFullDiscardedValueExpr(Increment), Loc, Copy.get()); + S.MakeFullDiscardedValueExpr(Increment), Loc, Copy.get(), + BranchHint::BH_NoHint); } -static StmtResult -buildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, - const ExprBuilder &To, const ExprBuilder &From, - bool CopyingBaseSubobject, bool Copying) { +static StmtResult buildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, + const ExprBuilder &To, + const ExprBuilder &From, + bool CopyingBaseSubobject, + bool Copying) { // Maybe we should use a memcpy? if (T->isArrayType() && !T.isConstQualified() && !T.isVolatileQualified() && T.isTriviallyCopyableType(S.Context)) Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -519,13 +519,31 @@ EvaluatedExprVisitor::VisitBinaryOperator(E); } }; +} // namespace + +BranchHint Sema::HandleIfStmtHint(LikelihoodAttr *ThenLikelihoodAttr, + LikelihoodAttr *ElseLikelihoodAttr) { + if (ThenLikelihoodAttr) { + if (ElseLikelihoodAttr && ThenLikelihoodAttr->isEqual(ElseLikelihoodAttr)) { + Diag(ElseLikelihoodAttr->getLocation(), + diag::warn_conflicting_likelihood_attrs) + << ElseLikelihoodAttr->getSpelling() + << ThenLikelihoodAttr->getSpelling(); + Diag(ThenLikelihoodAttr->getLocation(), diag::note_conflicting_attribute); + } else + return (ThenLikelihoodAttr->isLikely() ? BranchHint::BH_Taken + : BranchHint::BH_NotTaken); + } else if (ElseLikelihoodAttr) { + return (ElseLikelihoodAttr->isUnlikely() ? BranchHint::BH_Taken + : BranchHint::BH_NotTaken); + } + return BranchHint::BH_NoHint; } -StmtResult -Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, Stmt *InitStmt, - ConditionResult Cond, - Stmt *thenStmt, SourceLocation ElseLoc, - Stmt *elseStmt) { +StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, + Stmt *InitStmt, ConditionResult Cond, + Stmt *thenStmt, SourceLocation ElseLoc, + Stmt *elseStmt, BranchHint Hint) { if (Cond.isInvalid()) Cond = ConditionResult( *this, nullptr, @@ -545,13 +563,13 @@ diag::warn_empty_if_body); return BuildIfStmt(IfLoc, IsConstexpr, InitStmt, Cond, thenStmt, ElseLoc, - elseStmt); + elseStmt, Hint); } StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr, Stmt *InitStmt, ConditionResult Cond, Stmt *thenStmt, SourceLocation ElseLoc, - Stmt *elseStmt) { + Stmt *elseStmt, BranchHint Hint) { if (Cond.isInvalid()) return StmtError(); @@ -559,7 +577,7 @@ setFunctionHasBranchProtectedScope(); return IfStmt::Create(Context, IfLoc, IsConstexpr, InitStmt, Cond.get().first, - Cond.get().second, thenStmt, ElseLoc, elseStmt); + Cond.get().second, thenStmt, ElseLoc, elseStmt, Hint); } namespace { @@ -1274,7 +1292,7 @@ } StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc, ConditionResult Cond, - Stmt *Body) { + Stmt *Body, BranchHint Hint) { if (Cond.isInvalid()) return StmtError(); @@ -1289,13 +1307,13 @@ getCurCompoundScope().setHasEmptyLoopBodies(); return WhileStmt::Create(Context, CondVal.first, CondVal.second, Body, - WhileLoc); + WhileLoc, Hint); } -StmtResult -Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, - SourceLocation WhileLoc, SourceLocation CondLParen, - Expr *Cond, SourceLocation CondRParen) { +StmtResult Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, + SourceLocation WhileLoc, SourceLocation CondLParen, + Expr *Cond, SourceLocation CondRParen, + BranchHint Hint) { assert(Cond && "ActOnDoStmt(): missing expression"); CheckBreakContinueBinding(Cond); @@ -1314,7 +1332,7 @@ !Diags.isIgnored(diag::warn_comma_operator, Cond->getExprLoc())) CommaVisitor(*this).Visit(Cond); - return new (Context) DoStmt(Body, Cond, DoLoc, WhileLoc, CondRParen); + return new (Context) DoStmt(Body, Cond, DoLoc, WhileLoc, CondRParen, Hint); } namespace { @@ -1723,7 +1741,7 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, Stmt *First, ConditionResult Second, FullExprArg third, SourceLocation RParenLoc, - Stmt *Body) { + Stmt *Body, BranchHint Hint) { if (Second.isInvalid()) return StmtError(); @@ -1763,7 +1781,7 @@ return new (Context) ForStmt(Context, First, Second.get().second, Second.get().first, Third, - Body, ForLoc, LParenLoc, RParenLoc); + Body, ForLoc, LParenLoc, RParenLoc, Hint); } /// In an Objective C collection iteration statement: @@ -2632,8 +2650,8 @@ return new (Context) CXXForRangeStmt( InitStmt, RangeDS, cast_or_null(BeginDeclStmt.get()), cast_or_null(EndDeclStmt.get()), NotEqExpr.get(), - IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, CoawaitLoc, - ColonLoc, RParenLoc); + IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, CoawaitLoc, ColonLoc, + RParenLoc, BranchHint::BH_NoHint); } /// FinishObjCForCollectionStmt - Attach the body to a objective-C foreach @@ -2805,7 +2823,7 @@ /// This is a separate step from ActOnCXXForRangeStmt because analysis of the /// body cannot be performed until after the type of the range variable is /// determined. -StmtResult Sema::FinishCXXForRangeStmt(Stmt *S, Stmt *B) { +StmtResult Sema::FinishCXXForRangeStmt(Stmt *S, Stmt *B, BranchHint Hint) { if (!S || !B) return StmtError(); @@ -2814,6 +2832,7 @@ CXXForRangeStmt *ForStmt = cast(S); ForStmt->setBody(B); + ForStmt->setBranchHint(Hint); DiagnoseEmptyStmtBody(ForStmt->getRParenLoc(), B, diag::warn_empty_range_based_for_body); Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ clang/lib/Sema/SemaStmtAttr.cpp @@ -51,6 +51,58 @@ return ::new (S.Context) auto(Attr); } +static Attr *handleLikelihoodAttr(Sema &S, Stmt *St, const ParsedAttr &A, + SourceRange Range) { + LikelihoodAttr *Attr = ::new (S.Context) LikelihoodAttr( + A.getRange(), S.Context, A.getAttributeSpellingListIndex()); + + if (!S.getLangOpts().CPlusPlus2a && A.isCXX11Attribute()) + S.Diag(A.getLoc(), diag::ext_cxx2a_attr) << A.getName(); + + // Handle attribute on case/default statement. + if (isa(St) || isa(St)) { + FunctionScopeInfo *FnScope = S.getCurFunction(); + if (FnScope->SwitchStack.empty()) { + S.Diag(A.getLoc(), diag::warn_likelihood_on_case_outside_switch) + << Attr->getSpelling() << (isa(St) ? "case" : "default"); + } + return Attr; + } + + // Handle attribute on If/For/while/Do/Catch statements. + Scope *CurScope = S.getCurScope(); + if (CurScope) { + Scope *ControlScope = CurScope->getParent(); + if (!ControlScope || + !(ControlScope->getFlags() & Scope::ControlScope || + ControlScope->getFlags() & Scope::BreakScope) || + ControlScope->getFlags() & Scope::SwitchScope || + ControlScope->getFlags() & Scope::SEHExceptScope || + ControlScope->getFlags() & Scope::SEHTryScope || + ControlScope->getFlags() & Scope::TryScope || + ControlScope->getFlags() & Scope::FnTryCatchScope) { + S.Diag(A.getLoc(), diag::warn_no_likelihood_attr_associated_branch) + << A.getName(); + return Attr; + } + + if (ControlScope->getBranchLikelihoodAttr()) { + if (ControlScope->getBranchLikelihoodAttr()->getSpelling()[0] == + Attr->getSpelling()[0]) + S.Diag(Attr->getLocation(), diag::err_repeat_attribute) + << Attr->getSpelling(); + else + S.Diag(Attr->getLocation(), diag::err_attributes_are_not_compatible) + << Attr->getSpelling() + << ControlScope->getBranchLikelihoodAttr()->getSpelling(); + S.Diag(ControlScope->getBranchLikelihoodAttr()->getLocation(), + diag::note_previous_attribute); + } else + ControlScope->setBranchLikelihoodAttr(Attr); + } + return Attr; +} + static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { if (A.getNumArgs() < 1) { @@ -336,6 +388,8 @@ return nullptr; case ParsedAttr::AT_FallThrough: return handleFallThroughAttr(S, St, A, Range); + case ParsedAttr::AT_Likelihood: + return handleLikelihoodAttr(S, St, A, Range); case ParsedAttr::AT_LoopHint: return handleLoopHintAttr(S, St, A, Range); case ParsedAttr::AT_OpenCLUnrollHint: Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -1264,9 +1264,10 @@ /// Subclasses may override this routine to provide different behavior. StmtResult RebuildIfStmt(SourceLocation IfLoc, bool IsConstexpr, Sema::ConditionResult Cond, Stmt *Init, Stmt *Then, - SourceLocation ElseLoc, Stmt *Else) { - return getSema().ActOnIfStmt(IfLoc, IsConstexpr, Init, Cond, Then, - ElseLoc, Else); + SourceLocation ElseLoc, Stmt *Else, + BranchHint Hint) { + return getSema().ActOnIfStmt(IfLoc, IsConstexpr, Init, Cond, Then, ElseLoc, + Else, Hint); } /// Start building a new switch statement. @@ -1292,8 +1293,9 @@ /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. StmtResult RebuildWhileStmt(SourceLocation WhileLoc, - Sema::ConditionResult Cond, Stmt *Body) { - return getSema().ActOnWhileStmt(WhileLoc, Cond, Body); + Sema::ConditionResult Cond, Stmt *Body, + BranchHint Hint) { + return getSema().ActOnWhileStmt(WhileLoc, Cond, Body, Hint); } /// Build a new do-while statement. @@ -1302,9 +1304,10 @@ /// Subclasses may override this routine to provide different behavior. StmtResult RebuildDoStmt(SourceLocation DoLoc, Stmt *Body, SourceLocation WhileLoc, SourceLocation LParenLoc, - Expr *Cond, SourceLocation RParenLoc) { - return getSema().ActOnDoStmt(DoLoc, Body, WhileLoc, LParenLoc, - Cond, RParenLoc); + Expr *Cond, SourceLocation RParenLoc, + BranchHint Hint) { + return getSema().ActOnDoStmt(DoLoc, Body, WhileLoc, LParenLoc, Cond, + RParenLoc, Hint); } /// Build a new for statement. @@ -1314,9 +1317,9 @@ StmtResult RebuildForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, Stmt *Init, Sema::ConditionResult Cond, Sema::FullExprArg Inc, SourceLocation RParenLoc, - Stmt *Body) { - return getSema().ActOnForStmt(ForLoc, LParenLoc, Init, Cond, - Inc, RParenLoc, Body); + Stmt *Body, BranchHint Hint) { + return getSema().ActOnForStmt(ForLoc, LParenLoc, Init, Cond, Inc, RParenLoc, + Body, Hint); } /// Build a new goto statement. @@ -2102,8 +2105,9 @@ /// /// By default, performs semantic analysis to finish the new statement. /// Subclasses may override this routine to provide different behavior. - StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body) { - return getSema().FinishCXXForRangeStmt(ForRange, Body); + StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body, + BranchHint Hint) { + return getSema().FinishCXXForRangeStmt(ForRange, Body, Hint); } StmtResult RebuildSEHTryStmt(bool IsCXXTry, SourceLocation TryLoc, @@ -6755,7 +6759,7 @@ return getDerived().RebuildIfStmt(S->getIfLoc(), S->isConstexpr(), Cond, Init.get(), Then.get(), S->getElseLoc(), - Else.get()); + Else.get(), S->getBranchHint()); } template @@ -6809,7 +6813,8 @@ Body.get() == S->getBody()) return Owned(S); - return getDerived().RebuildWhileStmt(S->getWhileLoc(), Cond, Body.get()); + return getDerived().RebuildWhileStmt(S->getWhileLoc(), Cond, Body.get(), + S->getBranchHint()); } template @@ -6831,8 +6836,8 @@ return S; return getDerived().RebuildDoStmt(S->getDoLoc(), Body.get(), S->getWhileLoc(), - /*FIXME:*/S->getWhileLoc(), Cond.get(), - S->getRParenLoc()); + /*FIXME:*/ S->getWhileLoc(), Cond.get(), + S->getRParenLoc(), S->getBranchHint()); } template @@ -6879,9 +6884,9 @@ Body.get() == S->getBody()) return S; - return getDerived().RebuildForStmt(S->getForLoc(), S->getLParenLoc(), - Init.get(), Cond, FullInc, - S->getRParenLoc(), Body.get()); + return getDerived().RebuildForStmt( + S->getForLoc(), S->getLParenLoc(), Init.get(), Cond, FullInc, + S->getRParenLoc(), Body.get(), S->getBranchHint()); } template @@ -7567,7 +7572,7 @@ if (NewStmt.get() == S) return S; - return FinishCXXForRangeStmt(NewStmt.get(), Body.get()); + return FinishCXXForRangeStmt(NewStmt.get(), Body.get(), S->getBranchHint()); } template Index: clang/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- clang/lib/Serialization/ASTReaderStmt.cpp +++ clang/lib/Serialization/ASTReaderStmt.cpp @@ -223,6 +223,7 @@ bool HasElse = Record.readInt(); bool HasVar = Record.readInt(); bool HasInit = Record.readInt(); + S->setBranchHint(static_cast(Record.readInt())); S->setCond(Record.readSubExpr()); S->setThen(Record.readSubStmt()); @@ -272,6 +273,7 @@ VisitStmt(S); bool HasVar = Record.readInt(); + S->setBranchHint(static_cast(Record.readInt())); S->setCond(Record.readSubExpr()); S->setBody(Record.readSubStmt()); @@ -288,6 +290,7 @@ S->setDoLoc(ReadSourceLocation()); S->setWhileLoc(ReadSourceLocation()); S->setRParenLoc(ReadSourceLocation()); + S->setBranchHint(static_cast(Record.readInt())); } void ASTStmtReader::VisitForStmt(ForStmt *S) { @@ -300,6 +303,7 @@ S->setForLoc(ReadSourceLocation()); S->setLParenLoc(ReadSourceLocation()); S->setRParenLoc(ReadSourceLocation()); + S->setBranchHint(static_cast(Record.readInt())); } void ASTStmtReader::VisitGotoStmt(GotoStmt *S) { @@ -1324,6 +1328,7 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) { VisitStmt(S); + S->setBranchHint(static_cast(Record.readInt())); S->ForLoc = ReadSourceLocation(); S->CoawaitLoc = ReadSourceLocation(); S->ColonLoc = ReadSourceLocation(); Index: clang/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- clang/lib/Serialization/ASTWriterStmt.cpp +++ clang/lib/Serialization/ASTWriterStmt.cpp @@ -140,6 +140,7 @@ Record.push_back(HasElse); Record.push_back(HasVar); Record.push_back(HasInit); + Record.push_back(static_cast(S->getBranchHint())); Record.AddStmt(S->getCond()); Record.AddStmt(S->getThen()); @@ -186,6 +187,7 @@ bool HasVar = S->getConditionVariableDeclStmt() != nullptr; Record.push_back(HasVar); + Record.push_back(static_cast(S->getBranchHint())); Record.AddStmt(S->getCond()); Record.AddStmt(S->getBody()); @@ -203,6 +205,7 @@ Record.AddSourceLocation(S->getDoLoc()); Record.AddSourceLocation(S->getWhileLoc()); Record.AddSourceLocation(S->getRParenLoc()); + Record.push_back(static_cast(S->getBranchHint())); Code = serialization::STMT_DO; } @@ -216,6 +219,7 @@ Record.AddSourceLocation(S->getForLoc()); Record.AddSourceLocation(S->getLParenLoc()); Record.AddSourceLocation(S->getRParenLoc()); + Record.push_back(static_cast(S->getBranchHint())); Code = serialization::STMT_FOR; } @@ -1287,6 +1291,7 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { VisitStmt(S); + Record.push_back(static_cast(S->getBranchHint())); Record.AddSourceLocation(S->getForLoc()); Record.AddSourceLocation(S->getCoawaitLoc()); Record.AddSourceLocation(S->getColonLoc()); Index: clang/test/CodeGenCXX/cxx2a-likelihood-attr.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/cxx2a-likelihood-attr.cpp @@ -0,0 +1,25 @@ +// clang-format off +// RUN: %clang_cc1 -std=c++2a -cc1 -emit-llvm -disable-llvm-passes -O3 %s -o - -triple %itanium_abi_triple | FileCheck %s + +int test(int i) { + if (i == 0) { + // CHECK: %expval = call i1 @llvm.expect.i1(i1 %cmp, i1 false) + // CHECK-NEXT: br i1 %expval + i = i + 1; + } else + [[likely]] return 1; + while (i == 1) + [[unlikely]] { return 2; } + // CHECK: %[[expval:.*]] = call i1 @llvm.expect.i1(i1 %[[cmp:.*]], i1 + // false) CHECK-NEXT: br i1 %expval + for (; i == 4;) + [[unlikely]] { return 2; } + // CHECK: %[[expval:.*]] = call i1 @llvm.expect.i1(i1 %[[cmp:.*]], i1 false) + // CHECK-NEXT: br i1 %expval + do + [[likely]] { return 2; } + while (i == 3); + // CHECK: %[[expval:.*]] = call i1 @llvm.expect.i1(i1 %[[cmp:.*]], i1 true) + // CHECK-NEXT: br i1 %expval + return 0; +} \ No newline at end of file Index: clang/test/PCH/cxx2a-likelihood-attr.cpp =================================================================== --- /dev/null +++ clang/test/PCH/cxx2a-likelihood-attr.cpp @@ -0,0 +1,57 @@ +// Test this without pch. +// RUN: %clang_cc1 -std=c++2a -fsyntax-only -verify %s -ast-dump | FileCheck %s + +// Test with pch. Use '-ast-dump' to force deserialization of function bodies. +// RUN: %clang_cc1 -x c++-header -std=c++2a -emit-pch -o %t %s +// RUN: %clang_cc1 -std=c++2a -include-pch %t -fsyntax-only -verify %s +// -ast-dump-all | FileCheck %s + +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +void test(int i) { + if (i) + [[likely]] { return; } + else + [[unlikely]] if (i) { + while (i) + [[likely]] {} + do { + for (; i;) + [[likely]] {} + [[unlikely]]; + } while (i); + } +} + +#endif + +// CHECK: IfStmt {{.*}} likely + +// CHECK: AttributedStmt +// CHECK-NEXT: LikelihoodAttr {{.*}} likely + +// CHECK: AttributedStmt +// CHECK-NEXT: LikelihoodAttr {{.*}} unlikely +// CHECK-NEXT: IfStmt + +// CHECK: WhileStmt {{.*}} likely + +// CHECK: AttributedStmt +// CHECK-NEXT: LikelihoodAttr {{.*}} likely + +// CHECk-NEXT: CompoundStmt + +// CHECK: DoStmt {{.*}} unlikely + +// CHECk-NEXT: CompoundStmt + +// CHECk-NEXT: ForStmt {{.*}} likely + +// CHECK: AttributedStmt +// CHECK-NEXT: LikelihoodAttr {{.*}} likely + +// CHECK: AttributedStmt +// CHECK-NEXT: LikelihoodAttr {{.*}} unlikely \ No newline at end of file Index: clang/test/SemaCXX/cxx17-compat.cpp =================================================================== --- clang/test/SemaCXX/cxx17-compat.cpp +++ clang/test/SemaCXX/cxx17-compat.cpp @@ -63,3 +63,23 @@ // expected-warning@-4 {{range-based for loop initialization statements are incompatible with C++ standards before C++2a}} #endif } + +//clang-format off + +int f(int i) { + if (i == 1) + [[unlikely]] +#if __cplusplus <= 201703L +// expected-warning@-2 {{use of the 'unlikely' attribute is a C++2a extension}} +#endif + { + f(i); + } + else if (i == 2) + [[likely]] +#if __cplusplus <= 201703L +// expected-warning@-2 {{use of the 'likely' attribute is a C++2a extension}} +#endif + return f(i); + return i; +} Index: clang/test/SemaCXX/cxx2a-likelihood-attr.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/cxx2a-likelihood-attr.cpp @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -fsyntax-only %s -verify -std=c++2a + +// formating this file will break the test +// clang-format off + +int f(int i) { + if (i == 1) + [[unlikely]] { f(i); } + else if (i == 2) + [[likely]] return f(i); + else + [[unlikely]] { return f(i + 1); } + return i; +} + +[[likely]] typedef int n1; +// expected-error@-1 {{'likely' attribute cannot be applied to a declaration}} +typedef int [[likely]] n2; +// expected-error@-1 {{'likely' attribute cannot be applied to types}} +typedef int n3 [[likely]]; +// expected-error@-1 {{'likely' attribute cannot be applied to a declaration}} + +enum [[likely]] E{One}; +// expected-error@-1 {{'likely' attribute cannot be applied to a declaration}} + +[[likely]] +// expected-error@-1 {{'likely' attribute cannot be applied to a declaration}} + +void test(int i) { + [[likely]] + // expected-warning@-1 {{attribute 'likely' is not associated with a branch and is ignored}} + if (f(i)) [[likely, likely]] { + // expected-error@-1 {{likely attribute cannot be repeated}} + // expected-note@-2 {{previous attribute is here}} + [[unlikely]] return; + // expected-warning@-1 {{attribute 'unlikely' is not associated with a branch and is ignored}} + } + else [[unlikely]] if (f(i)) { + while (f(i)) + [[likely]] { + switch (i) { + [[likely]] case 1 : default : [[likely]]; + // expected-warning@-1 {{attribute 'likely' is not associated with a branch and is ignored}} + [[unlikely]] case 3 : f(i); + [[fallthrough]]; + case 4: + return; + } + } + for (;;) + [[likely, unlikely]] + // expected-error@-1 {{unlikely and likely attributes are not compatible}} + // expected-note@-2 {{previous attribute is here}} + [[likely]] return; + // expected-error@-1 {{likely attribute cannot be repeated}} + // expected-note@-5 {{previous attribute is here}} + } + try { // expected-error {{cannot use 'try' with exceptions disabled}} + [[likely]]; + // expected-warning@-1 {{attribute 'likely' is not associated with a branch and is ignored}} + } catch (int) { + [[likely]] test : + // expected-error@-1 {{'likely' attribute cannot be applied to a declaration}} + [[unlikely]] return; + } +}