Mordante created this revision.
Mordante added reviewers: rsmith, rjmccall.
Mordante added a project: clang.
Herald added subscribers: llvm-commits, hiraditya.
Herald added a reviewer: aaron.ballman.
Herald added a project: LLVM.
Mordante requested review of this revision.

This contains the initial part of the implementation for the C++20 likelihood 
attributes.
For now it only handles them in an IfStmt, I want to add support for other 
statements after this one is done.

I was unsure whether it's preferred to have one patch for both the Sema and 
CodeGen part. If wanted I can split them easily.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D85091

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/CodeGen/CGStmt.cpp
  clang/lib/CodeGen/CodeGenFunction.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/Parse/ParseDeclCXX.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/lib/Sema/SemaStmtAttr.cpp
  clang/test/CodeGenCXX/attr-likelihood-if-branch-weights.cpp
  clang/test/Misc/warning-wall.c
  clang/test/Preprocessor/has_attribute.cpp
  clang/test/SemaCXX/attr-likelihood.cpp
  clang/www/cxx_status.html
  llvm/include/llvm/Transforms/Scalar/LowerExpectIntrinsic.h
  llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp

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<uint32_t> LikelyBranchWeight(
+cl::opt<uint32_t> llvm::LikelyBranchWeight(
     "likely-branch-weight", cl::Hidden, cl::init(2000),
     cl::desc("Weight of the branch likely to be taken (default = 2000)"));
-static cl::opt<uint32_t> UnlikelyBranchWeight(
+cl::opt<uint32_t> llvm::UnlikelyBranchWeight(
     "unlikely-branch-weight", cl::Hidden, cl::init(1),
     cl::desc("Weight of the branch unlikely to be taken (default = 1)"));
 
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<uint32_t> LikelyBranchWeight;
+extern cl::opt<uint32_t> UnlikelyBranchWeight;
 }
 
 #endif
Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -987,7 +987,7 @@
     <tr>
       <td><tt>[[likely]]</tt> and <tt>[[unlikely]]</tt> attributes</td>
       <td><a href="https://wg21.link/p0479r5";>P0479R5</a></td>
-      <td class="none" align="center">No</td>
+      <td class="none" align="center">Clang 12 (partial)</td>
     </tr>
     <tr>
       <td><tt>typename</tt> optional in more contexts</td>
Index: clang/test/SemaCXX/attr-likelihood.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-likelihood.cpp
@@ -0,0 +1,101 @@
+// RUN: %clang_cc1 %s -verify -Wlikelihood-attribute-if
+// RUN: %clang_cc1 %s -verify -Wall
+// RUN: %clang_cc1 %s -DPEDANTIC -pedantic -verify -Wlikelihood-attribute-if
+
+#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-note {{attribute 'likely' declarered here}}
+		[[unlikely]] // expected-error {{incompatible attributes 'unlikely' and 'likely'}}
+		;
+}
+
+void b()
+{
+	if(true)
+		[[unlikely]] // expected-note {{attribute 'unlikely' declarered here}}
+		[[likely]]   // expected-error {{incompatible attributes 'likely' and 'unlikely'}}
+		;
+}
+
+void c()
+{
+	if(true) [[likely]] ;
+}
+
+void d()
+{
+	if(true) [[unlikely]] ;
+}
+
+void e()
+{
+	if(true) // expected-warning {{attribute 'likely' found in both true and false branch}}
+		[[likely]] {} // expected-note {{attribute 'likely' in true branch declared here}}
+	else
+		[[likely]] {} // expected-note {{attribute 'likely' in false branch declared here}}
+}
+
+void f()
+{
+	if(true) // expected-warning {{attribute 'unlikely' found in both true and false branch}}
+		[[unlikely]] {} // expected-note {{attribute 'unlikely' in true branch declared here}}
+	else
+		[[unlikely]] {} // expected-note {{attribute 'unlikely' in false branch declared here}}
+}
+
+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]] {}
+}
+#endif
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
+// CHECK: likely: 201803L
 // 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
+// CHECK: unlikely: 201803L
 
 // Test for Microsoft __declspec attributes
 
Index: clang/test/Misc/warning-wall.c
===================================================================
--- clang/test/Misc/warning-wall.c
+++ clang/test/Misc/warning-wall.c
@@ -22,6 +22,7 @@
 CHECK-NEXT:      -Wimplicit-int
 CHECK-NEXT:    -Winfinite-recursion
 CHECK-NEXT:    -Wint-in-bool-context
+CHECK-NEXT:    -Wlikelihood-attribute-if
 CHECK-NEXT:    -Wmismatched-tags
 CHECK-NEXT:    -Wmissing-braces
 CHECK-NEXT:    -Wmove
Index: clang/test/CodeGenCXX/attr-likelihood-if-branch-weights.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/attr-likelihood-if-branch-weights.cpp
@@ -0,0 +1,25 @@
+// 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 bool b;
+extern bool A();
+extern bool B();
+
+bool f() {
+  // CHECK: br i1 %tobool.not, label %if.end, label %if.then, !prof !7
+  if (b) [[likely]] {
+      return A();
+    }
+  return B();
+}
+
+bool g() {
+  // CHECK: br i1 %tobool.not, label %if.end, label %if.then, !prof !8
+  if (b) [[unlikely]] {
+      return A();
+    }
+  return B();
+}
+
+// CHECK: !7 = !{!"branch_weights", i32 [[UNLIKELY]], i32 [[LIKELY]]}
+// CHECK: !8 = !{!"branch_weights", i32 [[LIKELY]], i32 [[UNLIKELY]]}
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<const Attr *> &Attrs) {
@@ -315,6 +333,34 @@
           << CategoryState.NumericAttr->getDiagnosticName(Policy);
     }
   }
+
+  // C++20 [dcl.attr.likelihood]p1 The attribute-token likely shall not appear
+  // in an attribute-specifier-seq that contains the attribute-token unlikely.
+  const LikelyAttr *Likely = nullptr;
+  const UnlikelyAttr *Unlikely = nullptr;
+  for (const auto *I : Attrs) {
+    if (const auto *Attr = dyn_cast<LikelyAttr>(I)) {
+      if (Unlikely) {
+        S.Diag(Attr->getLocation(), diag::err_attribute_compatibility)
+            << Attr->getSpelling() << Unlikely->getSpelling()
+            << Attr->getRange();
+        S.Diag(Unlikely->getLocation(), diag::note_attribute_compatibility_here)
+            << Unlikely->getSpelling() << Unlikely->getRange();
+
+        return;
+      }
+      Likely = Attr;
+    } else if (const auto *Attr = dyn_cast<UnlikelyAttr>(I)) {
+      if (Likely) {
+        S.Diag(Attr->getLocation(), diag::err_attribute_compatibility)
+            << Attr->getSpelling() << Likely->getSpelling() << Attr->getRange();
+        S.Diag(Likely->getLocation(), diag::note_attribute_compatibility_here)
+            << Likely->getSpelling() << Likely->getRange();
+        return;
+      }
+      Unlikely = Attr;
+    }
+  }
 }
 
 static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
@@ -377,6 +423,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/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -574,6 +574,50 @@
 };
 }
 
+static void CheckIfStmtLikelihood(Sema &S, SourceLocation IfLoc, Stmt *ThenStmt,
+                                  Stmt *ElseStmt) {
+  auto *Then = dyn_cast<AttributedStmt>(ThenStmt);
+  auto *Else = dyn_cast<AttributedStmt>(ElseStmt);
+  if (!Then || !Else)
+    return;
+
+  // Warn when both the true and false branch of an if statement have the same
+  // likelihood attribute. It's not prohibited, but makes no sense.
+  const LikelyAttr *Likely = nullptr;
+  const UnlikelyAttr *Unlikely = nullptr;
+  for (const auto *A : Then->getAttrs()) {
+    if (auto *LA = dyn_cast<LikelyAttr>(A))
+      Likely = LA;
+    else if (auto *UA = dyn_cast<UnlikelyAttr>(A))
+      Unlikely = UA;
+  }
+
+  for (const auto *A : Else->getAttrs()) {
+    if (isa<LikelyAttr>(A) && Likely) {
+      S.Diag(IfLoc, diag::warn_attribute_likelihood_if_duplicated)
+          << A->getSpelling();
+      S.Diag(Likely->getLocation(),
+             diag::note_attribute_likelihood_if_duplicated_here)
+          << Likely->getSpelling() << /*branch=*/0 << Likely->getRange();
+      S.Diag(A->getLocation(),
+             diag::note_attribute_likelihood_if_duplicated_here)
+          << A->getSpelling() << /*branch=*/1 << A->getRange();
+      return;
+    }
+    if (isa<UnlikelyAttr>(A) && Unlikely) {
+      S.Diag(IfLoc, diag::warn_attribute_likelihood_if_duplicated)
+          << A->getSpelling();
+      S.Diag(Unlikely->getLocation(),
+             diag::note_attribute_likelihood_if_duplicated_here)
+          << Unlikely->getSpelling() << /*branch=*/0 << Unlikely->getRange();
+      S.Diag(A->getLocation(),
+             diag::note_attribute_likelihood_if_duplicated_here)
+          << A->getSpelling() << /*branch=*/1 << A->getRange();
+      return;
+    }
+  }
+}
+
 StmtResult
 Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, Stmt *InitStmt,
                   ConditionResult Cond,
@@ -596,6 +640,8 @@
   if (!elseStmt)
     DiagnoseEmptyStmtBody(CondExpr->getEndLoc(), thenStmt,
                           diag::warn_empty_if_body);
+  else
+    CheckIfStmtLikelihood(*this, IfLoc, thenStmt, elseStmt);
 
   return BuildIfStmt(IfLoc, IsConstexpr, InitStmt, Cond, thenStmt, ElseLoc,
                      elseStmt);
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/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<BinaryOperator>(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/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,50 @@
   EmitBranch(IndGotoBB);
 }
 
+static std::pair<bool, bool> getLikelihood(const Stmt *Stmt) {
+  auto R = std::make_pair(false, false);
+  auto *AS = dyn_cast<AttributedStmt>(Stmt);
+  if (!AS)
+    return R;
+
+  for (const auto *A : AS->getAttrs()) {
+    if (isa<LikelyAttr>(A)) {
+      R.first = true;
+      return R;
+    }
+    if (isa<UnlikelyAttr>(A)) {
+      R.second = true;
+      return R;
+    }
+  }
+  return R;
+}
+
+static Optional<std::pair<uint32_t, uint32_t>>
+getLikelihoodWeights(const Stmt *Then, const Stmt *Else) {
+  assert(Then && "IfStmt without a ThenStmt");
+
+  // The code doesn't protect against the same attribute on both branches.
+  // The frontend already issued a diagnostic.
+  std::pair<bool, bool> LH = getLikelihood(Then);
+  if (LH.first)
+    return std::pair<uint32_t, uint32_t>(llvm::LikelyBranchWeight,
+                                         llvm::UnlikelyBranchWeight);
+  if (LH.second)
+    return std::pair<uint32_t, uint32_t>(llvm::UnlikelyBranchWeight,
+                                         llvm::LikelyBranchWeight);
+  if (Else) {
+    LH = LH = getLikelihood(Else);
+    if (LH.first)
+      return std::pair<uint32_t, uint32_t>(llvm::UnlikelyBranchWeight,
+                                           llvm::LikelyBranchWeight);
+    if (LH.second)
+      return std::pair<uint32_t, uint32_t>(llvm::LikelyBranchWeight,
+                                           llvm::UnlikelyBranchWeight);
+  }
+  return Optional<std::pair<uint32_t, uint32_t>>{};
+}
+
 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 +740,21 @@
   if (S.getElse())
     ElseBlock = createBasicBlock("if.else");
 
-  EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock,
-                       getProfileCount(S.getThen()));
+  // Prefer the PGO based weights over the likelihood attribute.
+  // When the build isn't optimized the metadata isn't used, so don't generate
+  // it.
+  llvm::MDNode *Weights = nullptr;
+  uint64_t Count = getProfileCount(S.getThen());
+  if (!Count && CGM.getCodeGenOpts().OptimizationLevel) {
+    Optional<std::pair<uint32_t, uint32_t>> LHW =
+        getLikelihoodWeights(S.getThen(), S.getElse());
+    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/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2396,6 +2396,15 @@
 
 // C++11 attributes
 def err_repeat_attribute : Error<"%0 attribute cannot be repeated">;
+def err_attribute_compatibility : Error<
+  "incompatible attributes '%0' and '%1'">;
+def note_attribute_compatibility_here : Note<
+  "attribute '%0' declarered here">;
+def warn_attribute_likelihood_if_duplicated : Warning<
+  "attribute '%0' found in both true and false branch">,
+  InGroup<LikelihoodAttributeIf>, DefaultIgnore;
+def note_attribute_likelihood_if_duplicated_here : Note<
+  "attribute '%0' in %select{true|false}1 branch declared here">;
 
 // C++11 final
 def err_final_function_overridden : Error<
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ clang/include/clang/Basic/DiagnosticGroups.td
@@ -656,8 +656,10 @@
 def IndependentClassAttribute : DiagGroup<"IndependentClass-attribute">;
 def UnknownAttributes : DiagGroup<"unknown-attributes">;
 def IgnoredAttributes : DiagGroup<"ignored-attributes">;
+def LikelihoodAttributeIf : DiagGroup<"likelihood-attribute-if">;
 def Attributes : DiagGroup<"attributes", [UnknownAttributes,
-                                          IgnoredAttributes]>;
+                                          IgnoredAttributes,
+                                          LikelihoodAttributeIf]>;
 def UnknownSanitizers : DiagGroup<"unknown-sanitizers">;
 def UnnamedTypeTemplateArgs : DiagGroup<"unnamed-type-template-args",
                                         [CXX98CompatUnnamedTypeTemplateArgs]>;
@@ -889,6 +891,7 @@
     Implicit,
     InfiniteRecursion,
     IntInBoolContext,
+    LikelihoodAttributeIf,
     MismatchedTags,
     MissingBraces,
     Move,
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -1683,6 +1683,46 @@
   }];
 }
 
+def LikelihoodDocs : Documentation {
+  let Category = DocCatStmt;
+  let Heading = "likely and unlikely";
+  let Content = [{
+The ``likely`` and ``unlikely`` attributes are used as compiler hints. When
+the next executed statement depends on a condition this attribute can
+annotate all possible statements with either ``likely`` or ``unlikely``.
+Using ``likely`` will hint the statement is more likely to be executed.
+Using ``unlikely`` will hint the statement is less likely to be executed.
+
+It's not allowed to annotate a statement with both ``likely`` and
+``unlikely``.  It's not recommended to annotate both branches of an ``if``
+statement with an attribute.
+
+These attributes have no effect when using PGO or optimization level 0.
+
+At the moment the attribute is only implemented for an ``if`` statement.
+
+Here is an example:
+
+.. code-block:: c++
+
+  // compile with -Wimplicit-fallthrough
+  if (b) [[likely]] {
+    // The compiler will optimize to execute the code here.
+  } else {
+  }
+
+  if (b) [[unlikely]] {
+  } else {
+    // The compiler will optimize to execute the code here.
+  }
+
+  if (b) {
+  } else [[likely]] {
+    // The compiler will optimize to execute the code here.
+  }
+  }];
+}
+
 def ARMInterruptDocs : Documentation {
   let Category = DocCatFunction;
   let Heading = "interrupt (ARM)";
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -1284,6 +1284,16 @@
   let Documentation = [FallthroughDocs];
 }
 
+def Likely : StmtAttr {
+  let Spellings = [CXX11<"", "likely", 201803>];
+  let Documentation = [LikelihoodDocs];
+}
+
+def Unlikely : StmtAttr {
+  let Spellings = [CXX11<"", "unlikely", 201803>];
+  let Documentation = [LikelihoodDocs];
+}
+
 def NoMerge : StmtAttr {
   let Spellings = [Clang<"nomerge">];
   let Documentation = [NoMergeDocs];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to