Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1147,6 +1147,11 @@ let Documentation = [FallthroughDocs]; } +def Likely : StmtAttr { + let Spellings = [CXX11<"", "likely", 201903>, CXX11<"clang", "likely">]; + let Documentation = [LikelyDocs]; +} + 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 @@ -1492,6 +1492,26 @@ }]; } +def LikelyDocs : Documentation { + let Category = DocCatStmt; + let Heading = "likely"; + let Content = [{ +The ``likely`` (or ``clang::likely``) attribute is used to annotate that a condition is likely to be true +this is supposed to be used as a hint by the optimizer (not yet implemented) + +Here is an example: + +.. code-block:: c++ + + int f(int i) { + if (i == 0) [[likely]] + return 1; //indicate that this branch is very likely to be taken + return 0; + } + + }]; +} + 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 @@ -7339,6 +7339,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">, @@ -8158,6 +8160,9 @@ "fallthrough annotation in unreachable code">, InGroup, DefaultIgnore; +def err_likely_attr_invalid_placement : Error< + "likely annotation can't appear here">; + def warn_unreachable_default : Warning< "default label in switch which covers all enumeration values">, InGroup, DefaultIgnore; Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -3858,6 +3858,7 @@ case ParsedAttr::AT_Deprecated: case ParsedAttr::AT_FallThrough: case ParsedAttr::AT_CXX11NoReturn: + case ParsedAttr::AT_Likely: return true; case ParsedAttr::AT_WarnUnusedResult: return !ScopeName && AttrName->getName().equals("nodiscard"); Index: clang/lib/Parse/ParseStmt.cpp =================================================================== --- clang/lib/Parse/ParseStmt.cpp +++ clang/lib/Parse/ParseStmt.cpp @@ -1216,6 +1216,9 @@ : Sema::ConditionKind::Boolean)) return StmtError(); + ParsedAttributesWithRange Attrs(AttrFactory); + MaybeParseCXX11Attributes(Attrs); + llvm::Optional ConstexprCondition; if (IsConstexpr) ConstexprCondition = Cond.getKnownValue(); @@ -1314,8 +1317,13 @@ if (ElseStmt.isInvalid()) ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc); - return Actions.ActOnIfStmt(IfLoc, IsConstexpr, InitStmt.get(), Cond, - ThenStmt.get(), ElseLoc, ElseStmt.get()); + StmtResult Stmt = Actions.ActOnIfStmt(IfLoc, IsConstexpr, InitStmt.get(), Cond, + ThenStmt.get(), ElseLoc, ElseStmt.get()); + + if (!Attrs.empty()) + return Actions.ProcessStmtAttributes(Stmt.get(), Attrs, Attrs.Range); + + return Stmt; } /// ParseSwitchStatement Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ clang/lib/Sema/SemaStmtAttr.cpp @@ -51,6 +51,21 @@ return ::new (S.Context) auto(Attr); } +static Attr *handleLikelyAttr(Sema &S, Stmt *St, const ParsedAttr &A, + SourceRange Range) { + LikelyAttr Attr(A.getRange(), S.Context, + A.getAttributeSpellingListIndex()); + + if (!llvm::isa(St)) { + S.Diag(A.getLoc(), diag::err_likely_attr_invalid_placement) << A.getName(); + } + + if (!S.getLangOpts().CPlusPlus2a && !A.getScopeName()) + S.Diag(A.getLoc(), diag::ext_cxx2a_attr) << A.getName(); + + return ::new (S.Context) auto(Attr); +} + static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { if (A.getNumArgs() < 1) { @@ -336,6 +351,8 @@ return nullptr; case ParsedAttr::AT_FallThrough: return handleFallThroughAttr(S, St, A, Range); + case ParsedAttr::AT_Likely: + return handleLikelyAttr(S, St, A, Range); case ParsedAttr::AT_LoopHint: return handleLoopHintAttr(S, St, A, Range); case ParsedAttr::AT_OpenCLUnrollHint: Index: clang/test/AST/ast-dump-attr.cpp =================================================================== --- clang/test/AST/ast-dump-attr.cpp +++ clang/test/AST/ast-dump-attr.cpp @@ -23,6 +23,21 @@ // CHECK-NEXT: FallThroughAttr // CHECK-NEXT: NullStmt +int TestLikelyAttribute(int i) { + if (i == 1) [[likely]] { + return 0; + } else if (i == 2) [[likely]] + return 1; + return 2; +} +// CHECK: FunctionDecl{{.*}}TestLikelyAttribute +// CHECK: AttributedStmt +// CHECK-NEXT: LikelyAttr +// CHECK-NEXT: IfStmt +// CHECK: AttributedStmt +// CHECK-NEXT: LikelyAttr +// CHECK-NEXT: IfStmt + [[clang::warn_unused_result]] int TestCXX11DeclAttr(); // CHECK: FunctionDecl{{.*}}TestCXX11DeclAttr // CHECK-NEXT: WarnUnusedResultAttr