Skip to content

Commit 08196e0

Browse files
committed
Implements [[likely]] and [[unlikely]] in IfStmt.
This is the initial part of the implementation of the C++20 likelihood attributes. It handles the attributes in an if statement. Differential Revision: https://reviews.llvm.org/D85091
1 parent db7defd commit 08196e0

File tree

18 files changed

+633
-30
lines changed

18 files changed

+633
-30
lines changed

clang/include/clang/AST/Stmt.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,14 @@ class alignas(void *) Stmt {
10981098
/// de-serialization).
10991099
struct EmptyShell {};
11001100

1101+
/// The likelihood of a branch being taken.
1102+
enum Likelihood {
1103+
LH_Unlikely = -1, ///< Branch has the [[unlikely]] attribute.
1104+
LH_None, ///< No attribute set or branches of the IfStmt have
1105+
///< the same attribute.
1106+
LH_Likely ///< Branch has the [[likely]] attribute.
1107+
};
1108+
11011109
protected:
11021110
/// Iterator for iterating over Stmt * arrays that contain only T *.
11031111
///
@@ -1166,6 +1174,20 @@ class alignas(void *) Stmt {
11661174
static void EnableStatistics();
11671175
static void PrintStats();
11681176

1177+
/// \returns the likelihood of a statement.
1178+
static Likelihood getLikelihood(const Stmt *S);
1179+
1180+
/// \returns the likelihood of the 'then' branch of an 'if' statement. The
1181+
/// 'else' branch is required to determine whether both branches specify the
1182+
/// same likelihood, which affects the result.
1183+
static Likelihood getLikelihood(const Stmt *Then, const Stmt *Else);
1184+
1185+
/// \returns whether the likelihood of the branches of an if statement are
1186+
/// conflicting. When the first element is \c true there's a conflict and
1187+
/// the Attr's are the conflicting attributes of the Then and Else Stmt.
1188+
static std::tuple<bool, const Attr *, const Attr *>
1189+
determineLikelihoodConflict(const Stmt *Then, const Stmt *Else);
1190+
11691191
/// Dumps the specified AST fragment and all subtrees to
11701192
/// \c llvm::errs().
11711193
void dump() const;

clang/include/clang/Basic/Attr.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,18 @@ def FallThrough : StmtAttr {
12881288
let Documentation = [FallthroughDocs];
12891289
}
12901290

1291+
def Likely : StmtAttr {
1292+
// FIXME: Change the date to 201803 once the implementation is finished.
1293+
let Spellings = [CXX11<"", "likely", 2>, C2x<"clang", "likely">];
1294+
let Documentation = [LikelihoodDocs];
1295+
}
1296+
1297+
def Unlikely : StmtAttr {
1298+
// FIXME: Change the date to 201803 once the implementation is finished.
1299+
let Spellings = [CXX11<"", "unlikely", 2>, C2x<"clang", "unlikely">];
1300+
let Documentation = [LikelihoodDocs];
1301+
}
1302+
12911303
def NoMerge : StmtAttr {
12921304
let Spellings = [Clang<"nomerge">];
12931305
let Documentation = [NoMergeDocs];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,6 +1684,101 @@ Here is an example:
16841684
}];
16851685
}
16861686

1687+
def LikelihoodDocs : Documentation {
1688+
let Category = DocCatStmt;
1689+
let Heading = "likely and unlikely";
1690+
let Content = [{
1691+
The ``likely`` and ``unlikely`` attributes are used as compiler hints.
1692+
The attributes are used to aid the compiler to determine which branch is
1693+
likely or unlikely to be taken. This is done by marking the branch substatement
1694+
with one of the two attributes.
1695+
1696+
It isn't allowed to annotate a single statement with both ``likely`` and
1697+
``unlikely``. Annotating the ``true`` and ``false`` branch of an ``if``
1698+
statement with the same likelihood attribute will result in a diagnostic and
1699+
the attributes are ignored on both branches.
1700+
1701+
These attributes have no effect on the generated code when using
1702+
PGO (Profile-Guided Optimization) or at optimization level 0.
1703+
1704+
In Clang, the attributes will be ignored if they're not placed on the
1705+
substatement of an ``if`` or ``else`` statement. The C++ Standard recommends
1706+
to honor them on every statement in the path of execution, but that can be
1707+
confusing:
1708+
1709+
.. code-block:: c++
1710+
1711+
if (b) {
1712+
[[unlikely]] --b; // In the path of execution,
1713+
// this branch is considered unlikely.
1714+
}
1715+
1716+
if (b) {
1717+
--b;
1718+
if(b)
1719+
return;
1720+
[[unlikely]] --b; // Not in the path of execution,
1721+
} // the branch has no likelihood information.
1722+
1723+
if (b) {
1724+
--b;
1725+
foo(b);
1726+
// Whether or not the next statement is in the path of execution depends
1727+
// on the declaration of foo():
1728+
// In the path of execution: void foo(int);
1729+
// Not in the path of execution: [[noreturn]] void foo(int);
1730+
// This means the likelihood of the branch depends on the declaration
1731+
// of foo().
1732+
[[unlikely]] --b;
1733+
}
1734+
1735+
1736+
At the moment the attribute only has effect when used in an ``if`` or ``else``
1737+
statement.
1738+
1739+
.. code-block:: c++
1740+
1741+
if (b) [[likely]] { // Placement on the first statement in the branch.
1742+
// The compiler will optimize to execute the code here.
1743+
} else {
1744+
}
1745+
1746+
if (b)
1747+
[[unlikely]] b++; // Placement on the first statement in the branch.
1748+
else {
1749+
// The compiler will optimize to execute the code here.
1750+
}
1751+
1752+
if (b) {
1753+
[[unlikely]] b++; // Placement on the second statement in the branch.
1754+
} // The attribute will be ignored.
1755+
1756+
if (b) [[likely]] {
1757+
[[unlikely]] b++; // No contradiction since the second attribute
1758+
} // is ignored.
1759+
1760+
if (b)
1761+
;
1762+
else [[likely]] {
1763+
// The compiler will optimize to execute the code here.
1764+
}
1765+
1766+
if (b)
1767+
;
1768+
else
1769+
// The compiler will optimize to execute the next statement.
1770+
[[likely]] b = f();
1771+
1772+
if (b) [[likely]]; // Both branches are likely. A diagnostic is issued
1773+
else [[likely]]; // and the attributes are ignored.
1774+
1775+
if (b)
1776+
[[likely]] int i = 5; // Issues a diagnostic since the attribute
1777+
// isn't allowed on a declaration.
1778+
1779+
}];
1780+
}
1781+
16871782
def ARMInterruptDocs : Documentation {
16881783
let Category = DocCatFunction;
16891784
let Heading = "interrupt (ARM)";

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3141,6 +3141,9 @@ def warn_nocf_check_attribute_ignored :
31413141
def warn_attribute_after_definition_ignored : Warning<
31423142
"attribute %0 after definition is ignored">,
31433143
InGroup<IgnoredAttributes>;
3144+
def warn_attributes_likelihood_ifstmt_conflict
3145+
: Warning<"conflicting attributes %0 are ignored">,
3146+
InGroup<IgnoredAttributes>;
31443147
def warn_cxx11_gnu_attribute_on_type : Warning<
31453148
"attribute %0 ignored, because it cannot be applied to a type">,
31463149
InGroup<IgnoredAttributes>;

clang/lib/AST/Stmt.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
#include "clang/AST/Stmt.h"
1414
#include "clang/AST/ASTContext.h"
1515
#include "clang/AST/ASTDiagnostic.h"
16+
#include "clang/AST/Attr.h"
1617
#include "clang/AST/Decl.h"
1718
#include "clang/AST/DeclGroup.h"
1819
#include "clang/AST/Expr.h"
19-
#include "clang/AST/ExprConcepts.h"
2020
#include "clang/AST/ExprCXX.h"
21+
#include "clang/AST/ExprConcepts.h"
2122
#include "clang/AST/ExprObjC.h"
2223
#include "clang/AST/ExprOpenMP.h"
2324
#include "clang/AST/StmtCXX.h"
@@ -41,8 +42,8 @@
4142
#include <cassert>
4243
#include <cstring>
4344
#include <string>
44-
#include <utility>
4545
#include <type_traits>
46+
#include <utility>
4647

4748
using namespace clang;
4849

@@ -129,6 +130,51 @@ void Stmt::EnableStatistics() {
129130
StatisticsEnabled = true;
130131
}
131132

133+
static std::pair<Stmt::Likelihood, const Attr *> getLikelihood(const Stmt *S) {
134+
if (const auto *AS = dyn_cast_or_null<AttributedStmt>(S))
135+
for (const auto *A : AS->getAttrs()) {
136+
if (isa<LikelyAttr>(A))
137+
return std::make_pair(Stmt::LH_Likely, A);
138+
139+
if (isa<UnlikelyAttr>(A))
140+
return std::make_pair(Stmt::LH_Unlikely, A);
141+
}
142+
143+
return std::make_pair(Stmt::LH_None, nullptr);
144+
}
145+
146+
Stmt::Likelihood Stmt::getLikelihood(const Stmt *S) {
147+
return ::getLikelihood(S).first;
148+
}
149+
150+
Stmt::Likelihood Stmt::getLikelihood(const Stmt *Then, const Stmt *Else) {
151+
Likelihood LHT = ::getLikelihood(Then).first;
152+
Likelihood LHE = ::getLikelihood(Else).first;
153+
if (LHE == LH_None)
154+
return LHT;
155+
156+
// If the same attribute is used on both branches there's a conflict.
157+
if (LHT == LHE)
158+
return LH_None;
159+
160+
if (LHT != LH_None)
161+
return LHT;
162+
163+
// Invert the value of Else to get the value for Then.
164+
return LHE == LH_Likely ? LH_Unlikely : LH_Likely;
165+
}
166+
167+
std::tuple<bool, const Attr *, const Attr *>
168+
Stmt::determineLikelihoodConflict(const Stmt *Then, const Stmt *Else) {
169+
std::pair<Likelihood, const Attr *> LHT = ::getLikelihood(Then);
170+
std::pair<Likelihood, const Attr *> LHE = ::getLikelihood(Else);
171+
// If the same attribute is used on both branches there's a conflict.
172+
if (LHT.first != LH_None && LHT.first == LHE.first)
173+
return std::make_tuple(true, LHT.second, LHE.second);
174+
175+
return std::make_tuple(false, nullptr, nullptr);
176+
}
177+
132178
/// Skip no-op (attributed, compound) container stmts and skip captured
133179
/// stmt at the top, if \a IgnoreCaptured is true.
134180
Stmt *Stmt::IgnoreContainers(bool IgnoreCaptured) {

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "llvm/IR/Intrinsics.h"
2828
#include "llvm/IR/MDBuilder.h"
2929
#include "llvm/Support/SaveAndRestore.h"
30+
#include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h"
3031

3132
using namespace clang;
3233
using namespace CodeGen;
@@ -651,6 +652,20 @@ void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) {
651652

652653
EmitBranch(IndGotoBB);
653654
}
655+
static Optional<std::pair<uint32_t, uint32_t>>
656+
getLikelihoodWeights(const IfStmt &If) {
657+
switch (Stmt::getLikelihood(If.getThen(), If.getElse())) {
658+
case Stmt::LH_Unlikely:
659+
return std::pair<uint32_t, uint32_t>(llvm::UnlikelyBranchWeight,
660+
llvm::LikelyBranchWeight);
661+
case Stmt::LH_None:
662+
return None;
663+
case Stmt::LH_Likely:
664+
return std::pair<uint32_t, uint32_t>(llvm::LikelyBranchWeight,
665+
llvm::UnlikelyBranchWeight);
666+
}
667+
llvm_unreachable("Unknown Likelihood");
668+
}
654669

655670
void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
656671
// C99 6.8.4.1: The first substatement is executed if the expression compares
@@ -695,8 +710,20 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
695710
if (S.getElse())
696711
ElseBlock = createBasicBlock("if.else");
697712

698-
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock,
699-
getProfileCount(S.getThen()));
713+
// Prefer the PGO based weights over the likelihood attribute.
714+
// When the build isn't optimized the metadata isn't used, so don't generate
715+
// it.
716+
llvm::MDNode *Weights = nullptr;
717+
uint64_t Count = getProfileCount(S.getThen());
718+
if (!Count && CGM.getCodeGenOpts().OptimizationLevel) {
719+
Optional<std::pair<uint32_t, uint32_t>> LHW = getLikelihoodWeights(S);
720+
if (LHW) {
721+
llvm::MDBuilder MDHelper(CGM.getLLVMContext());
722+
Weights = MDHelper.createBranchWeights(LHW->first, LHW->second);
723+
}
724+
}
725+
726+
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, Count, Weights);
700727

701728
// Emit the 'then' code.
702729
EmitBlock(ThenBlock);

0 commit comments

Comments
 (0)