Tyker updated this revision to Diff 193015.
Tyker added a comment.
Herald added a reviewer: martong.
Herald added a reviewer: shafik.
@riccibruno i fixed based on feedback everything except the CFG edit as i still
need to analyse the situation.
added AST and CodeGen for For, While, Do and CXXFor.
added tests for Semantic, AST, PCH
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D59467/new/
https://reviews.llvm.org/D59467
Files:
clang/include/clang/AST/Stmt.h
clang/include/clang/AST/StmtCXX.h
clang/include/clang/AST/TextNodeDumper.h
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Scope.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Stmt.cpp
clang/lib/AST/StmtCXX.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/Analysis/CFG.cpp
clang/lib/CodeGen/CGStmt.cpp
clang/lib/CodeGen/CodeGenFunction.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/Parse/ParseStmt.cpp
clang/lib/Sema/Scope.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaStmt.cpp
clang/lib/Sema/SemaStmtAttr.cpp
clang/lib/Sema/TreeTransform.h
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/test/CodeGenCXX/cxx2a-likelihood-attr.cpp
clang/test/PCH/cxx2a-likelihood-attr.cpp
clang/test/SemaCXX/cxx17-compat.cpp
clang/test/SemaCXX/cxx2a-likelihood-attr.cpp
Index: clang/test/SemaCXX/cxx2a-likelihood-attr.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/cxx2a-likelihood-attr.cpp
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -fsyntax-only %s -verify -std=c++2a
+
+// formating this file will break the test
+// clang-format off
+
+int f(int);
+
+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;
+ }
+}
+
+// clang-format off
Index: clang/test/SemaCXX/cxx17-compat.cpp
===================================================================
--- clang/test/SemaCXX/cxx17-compat.cpp
+++ clang/test/SemaCXX/cxx17-compat.cpp
@@ -56,10 +56,33 @@
};
void ForRangeInit() {
- for (int arr[3] = {1, 2, 3}; int n : arr) {}
+ for (int arr[3] = {1, 2, 3}; int n : arr) {
+ }
#if __cplusplus <= 201703L
- // expected-warning@-2 {{range-based for loop initialization statements are a C++2a extension}}
+ // expected-warning@-2 {{range-based for loop initialization statements are a
+ // C++2a extension}}
#else
- // expected-warning@-4 {{range-based for loop initialization statements are incompatible with C++ standards before C++2a}}
+ // expected-warning@-4 {{range-based for loop initialization statements are
+ // incompatible with C++ standards before C++2a}}
+#endif
+}
+
+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/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
Index: clang/test/CodeGenCXX/cxx2a-likelihood-attr.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/cxx2a-likelihood-attr.cpp
@@ -0,0 +1,25 @@
+// 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;
+}
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<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(S->getBranchHint()));
Code = serialization::STMT_FOR;
}
@@ -1287,6 +1291,7 @@
void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
VisitStmt(S);
+ Record.push_back(static_cast<uint64_t>(S->getBranchHint()));
Record.AddSourceLocation(S->getForLoc());
Record.AddSourceLocation(S->getCoawaitLoc());
Record.AddSourceLocation(S->getColonLoc());
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<BranchHint>(Record.readInt()));
S->setCond(Record.readSubExpr());
S->setThen(Record.readSubStmt());
@@ -272,6 +273,7 @@
VisitStmt(S);
bool HasVar = Record.readInt();
+ S->setBranchHint(static_cast<BranchHint>(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<BranchHint>(Record.readInt()));
}
void ASTStmtReader::VisitForStmt(ForStmt *S) {
@@ -300,6 +303,7 @@
S->setForLoc(ReadSourceLocation());
S->setLParenLoc(ReadSourceLocation());
S->setRParenLoc(ReadSourceLocation());
+ S->setBranchHint(static_cast<BranchHint>(Record.readInt()));
}
void ASTStmtReader::VisitGotoStmt(GotoStmt *S) {
@@ -1324,6 +1328,7 @@
void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
VisitStmt(S);
+ S->setBranchHint(static_cast<BranchHint>(Record.readInt()));
S->ForLoc = ReadSourceLocation();
S->CoawaitLoc = ReadSourceLocation();
S->ColonLoc = ReadSourceLocation();
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,12 +6759,11 @@
return getDerived().RebuildIfStmt(S->getIfLoc(), S->isConstexpr(), Cond,
Init.get(), Then.get(), S->getElseLoc(),
- Else.get());
+ Else.get(), S->getBranchHint());
}
-template<typename Derived>
-StmtResult
-TreeTransform<Derived>::TransformSwitchStmt(SwitchStmt *S) {
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformSwitchStmt(SwitchStmt *S) {
// Transform the initialization statement
StmtResult Init = getDerived().TransformStmt(S->getInit());
if (Init.isInvalid())
@@ -6809,12 +6812,12 @@
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<typename Derived>
-StmtResult
-TreeTransform<Derived>::TransformDoStmt(DoStmt *S) {
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformDoStmt(DoStmt *S) {
// Transform the body
StmtResult Body = getDerived().TransformStmt(S->getBody());
if (Body.isInvalid())
@@ -6831,13 +6834,12 @@
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<typename Derived>
-StmtResult
-TreeTransform<Derived>::TransformForStmt(ForStmt *S) {
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformForStmt(ForStmt *S) {
if (getSema().getLangOpts().OpenMP)
getSema().startOpenMPLoop();
@@ -6872,23 +6874,20 @@
if (Body.isInvalid())
return StmtError();
- if (!getDerived().AlwaysRebuild() &&
- Init.get() == S->getInit() &&
+ if (!getDerived().AlwaysRebuild() && Init.get() == S->getInit() &&
Cond.get() == std::make_pair(S->getConditionVariable(), S->getCond()) &&
- Inc.get() == S->getInc() &&
- Body.get() == S->getBody())
+ Inc.get() == S->getInc() && 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<typename Derived>
-StmtResult
-TreeTransform<Derived>::TransformGotoStmt(GotoStmt *S) {
- Decl *LD = getDerived().TransformDecl(S->getLabel()->getLocation(),
- S->getLabel());
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformGotoStmt(GotoStmt *S) {
+ Decl *LD =
+ getDerived().TransformDecl(S->getLabel()->getLocation(), S->getLabel());
if (!LD)
return StmtError();
@@ -7567,13 +7566,12 @@
if (NewStmt.get() == S)
return S;
- return FinishCXXForRangeStmt(NewStmt.get(), Body.get());
+ return FinishCXXForRangeStmt(NewStmt.get(), Body.get(), S->getBranchHint());
}
-template<typename Derived>
-StmtResult
-TreeTransform<Derived>::TransformMSDependentExistsStmt(
- MSDependentExistsStmt *S) {
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformMSDependentExistsStmt(
+ MSDependentExistsStmt *S) {
// Transform the nested-name-specifier, if any.
NestedNameSpecifierLoc QualifierLoc;
if (S->getQualifierLoc()) {
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<CaseStmt>(St) || isa<DefaultStmt>(St)) {
+ FunctionScopeInfo *FnScope = S.getCurFunction();
+ if (FnScope->SwitchStack.empty()) {
+ S.Diag(A.getLoc(), diag::warn_likelihood_on_case_outside_switch)
+ << Attr->getSpelling() << (isa<CaseStmt>(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/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -519,13 +519,31 @@
EvaluatedExprVisitor<CommaVisitor>::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,25 +577,25 @@
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 {
- struct CaseCompareFunctor {
- bool operator()(const std::pair<llvm::APSInt, CaseStmt*> &LHS,
- const llvm::APSInt &RHS) {
- return LHS.first < RHS;
- }
- bool operator()(const std::pair<llvm::APSInt, CaseStmt*> &LHS,
- const std::pair<llvm::APSInt, CaseStmt*> &RHS) {
- return LHS.first < RHS.first;
- }
- bool operator()(const llvm::APSInt &LHS,
- const std::pair<llvm::APSInt, CaseStmt*> &RHS) {
- return LHS < RHS.first;
- }
- };
-}
+struct CaseCompareFunctor {
+ bool operator()(const std::pair<llvm::APSInt, CaseStmt *> &LHS,
+ const llvm::APSInt &RHS) {
+ return LHS.first < RHS;
+ }
+ bool operator()(const std::pair<llvm::APSInt, CaseStmt *> &LHS,
+ const std::pair<llvm::APSInt, CaseStmt *> &RHS) {
+ return LHS.first < RHS.first;
+ }
+ bool operator()(const llvm::APSInt &LHS,
+ const std::pair<llvm::APSInt, CaseStmt *> &RHS) {
+ return LHS < RHS.first;
+ }
+};
+} // namespace
/// CmpCaseVals - Comparison predicate for sorting case values.
///
@@ -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,178 +1332,163 @@
!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 {
- // Use SetVector since the diagnostic cares about the ordering of the Decl's.
- using DeclSetVector =
- llvm::SetVector<VarDecl *, llvm::SmallVector<VarDecl *, 8>,
- llvm::SmallPtrSet<VarDecl *, 8>>;
-
- // This visitor will traverse a conditional statement and store all
- // the evaluated decls into a vector. Simple is set to true if none
- // of the excluded constructs are used.
- class DeclExtractor : public EvaluatedExprVisitor<DeclExtractor> {
- DeclSetVector &Decls;
- SmallVectorImpl<SourceRange> &Ranges;
- bool Simple;
- public:
- typedef EvaluatedExprVisitor<DeclExtractor> Inherited;
-
- DeclExtractor(Sema &S, DeclSetVector &Decls,
- SmallVectorImpl<SourceRange> &Ranges) :
- Inherited(S.Context),
- Decls(Decls),
- Ranges(Ranges),
- Simple(true) {}
+// Use SetVector since the diagnostic cares about the ordering of the Decl's.
+using DeclSetVector =
+ llvm::SetVector<VarDecl *, llvm::SmallVector<VarDecl *, 8>,
+ llvm::SmallPtrSet<VarDecl *, 8>>;
+
+// This visitor will traverse a conditional statement and store all
+// the evaluated decls into a vector. Simple is set to true if none
+// of the excluded constructs are used.
+class DeclExtractor : public EvaluatedExprVisitor<DeclExtractor> {
+ DeclSetVector &Decls;
+ SmallVectorImpl<SourceRange> &Ranges;
+ bool Simple;
- bool isSimple() { return Simple; }
+public:
+ typedef EvaluatedExprVisitor<DeclExtractor> Inherited;
- // Replaces the method in EvaluatedExprVisitor.
- void VisitMemberExpr(MemberExpr* E) {
- Simple = false;
- }
+ DeclExtractor(Sema &S, DeclSetVector &Decls,
+ SmallVectorImpl<SourceRange> &Ranges)
+ : Inherited(S.Context), Decls(Decls), Ranges(Ranges), Simple(true) {}
- // Any Stmt not whitelisted will cause the condition to be marked complex.
- void VisitStmt(Stmt *S) {
- Simple = false;
- }
+ bool isSimple() { return Simple; }
- void VisitBinaryOperator(BinaryOperator *E) {
- Visit(E->getLHS());
- Visit(E->getRHS());
- }
+ // Replaces the method in EvaluatedExprVisitor.
+ void VisitMemberExpr(MemberExpr *E) { Simple = false; }
- void VisitCastExpr(CastExpr *E) {
- Visit(E->getSubExpr());
- }
+ // Any Stmt not whitelisted will cause the condition to be marked complex.
+ void VisitStmt(Stmt *S) { Simple = false; }
- void VisitUnaryOperator(UnaryOperator *E) {
- // Skip checking conditionals with derefernces.
- if (E->getOpcode() == UO_Deref)
- Simple = false;
- else
- Visit(E->getSubExpr());
- }
+ void VisitBinaryOperator(BinaryOperator *E) {
+ Visit(E->getLHS());
+ Visit(E->getRHS());
+ }
- void VisitConditionalOperator(ConditionalOperator *E) {
- Visit(E->getCond());
- Visit(E->getTrueExpr());
- Visit(E->getFalseExpr());
- }
+ void VisitCastExpr(CastExpr *E) { Visit(E->getSubExpr()); }
- void VisitParenExpr(ParenExpr *E) {
+ void VisitUnaryOperator(UnaryOperator *E) {
+ // Skip checking conditionals with derefernces.
+ if (E->getOpcode() == UO_Deref)
+ Simple = false;
+ else
Visit(E->getSubExpr());
- }
+ }
- void VisitBinaryConditionalOperator(BinaryConditionalOperator *E) {
- Visit(E->getOpaqueValue()->getSourceExpr());
- Visit(E->getFalseExpr());
- }
+ void VisitConditionalOperator(ConditionalOperator *E) {
+ Visit(E->getCond());
+ Visit(E->getTrueExpr());
+ Visit(E->getFalseExpr());
+ }
- void VisitIntegerLiteral(IntegerLiteral *E) { }
- void VisitFloatingLiteral(FloatingLiteral *E) { }
- void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) { }
- void VisitCharacterLiteral(CharacterLiteral *E) { }
- void VisitGNUNullExpr(GNUNullExpr *E) { }
- void VisitImaginaryLiteral(ImaginaryLiteral *E) { }
-
- void VisitDeclRefExpr(DeclRefExpr *E) {
- VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
- if (!VD) {
- // Don't allow unhandled Decl types.
- Simple = false;
- return;
- }
+ void VisitParenExpr(ParenExpr *E) { Visit(E->getSubExpr()); }
- Ranges.push_back(E->getSourceRange());
+ void VisitBinaryConditionalOperator(BinaryConditionalOperator *E) {
+ Visit(E->getOpaqueValue()->getSourceExpr());
+ Visit(E->getFalseExpr());
+ }
- Decls.insert(VD);
+ void VisitIntegerLiteral(IntegerLiteral *E) {}
+ void VisitFloatingLiteral(FloatingLiteral *E) {}
+ void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) {}
+ void VisitCharacterLiteral(CharacterLiteral *E) {}
+ void VisitGNUNullExpr(GNUNullExpr *E) {}
+ void VisitImaginaryLiteral(ImaginaryLiteral *E) {}
+
+ void VisitDeclRefExpr(DeclRefExpr *E) {
+ VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
+ if (!VD) {
+ // Don't allow unhandled Decl types.
+ Simple = false;
+ return;
}
- }; // end class DeclExtractor
+ Ranges.push_back(E->getSourceRange());
- // DeclMatcher checks to see if the decls are used in a non-evaluated
- // context.
- class DeclMatcher : public EvaluatedExprVisitor<DeclMatcher> {
- DeclSetVector &Decls;
- bool FoundDecl;
+ Decls.insert(VD);
+ }
- public:
- typedef EvaluatedExprVisitor<DeclMatcher> Inherited;
+}; // end class DeclExtractor
- DeclMatcher(Sema &S, DeclSetVector &Decls, Stmt *Statement) :
- Inherited(S.Context), Decls(Decls), FoundDecl(false) {
- if (!Statement) return;
+// DeclMatcher checks to see if the decls are used in a non-evaluated
+// context.
+class DeclMatcher : public EvaluatedExprVisitor<DeclMatcher> {
+ DeclSetVector &Decls;
+ bool FoundDecl;
- Visit(Statement);
- }
+public:
+ typedef EvaluatedExprVisitor<DeclMatcher> Inherited;
- void VisitReturnStmt(ReturnStmt *S) {
- FoundDecl = true;
- }
+ DeclMatcher(Sema &S, DeclSetVector &Decls, Stmt *Statement)
+ : Inherited(S.Context), Decls(Decls), FoundDecl(false) {
+ if (!Statement)
+ return;
- void VisitBreakStmt(BreakStmt *S) {
- FoundDecl = true;
- }
+ Visit(Statement);
+ }
- void VisitGotoStmt(GotoStmt *S) {
- FoundDecl = true;
- }
+ void VisitReturnStmt(ReturnStmt *S) { FoundDecl = true; }
- void VisitCastExpr(CastExpr *E) {
- if (E->getCastKind() == CK_LValueToRValue)
- CheckLValueToRValueCast(E->getSubExpr());
- else
- Visit(E->getSubExpr());
- }
+ void VisitBreakStmt(BreakStmt *S) { FoundDecl = true; }
- void CheckLValueToRValueCast(Expr *E) {
- E = E->IgnoreParenImpCasts();
+ void VisitGotoStmt(GotoStmt *S) { FoundDecl = true; }
- if (isa<DeclRefExpr>(E)) {
- return;
- }
+ void VisitCastExpr(CastExpr *E) {
+ if (E->getCastKind() == CK_LValueToRValue)
+ CheckLValueToRValueCast(E->getSubExpr());
+ else
+ Visit(E->getSubExpr());
+ }
- if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
- Visit(CO->getCond());
- CheckLValueToRValueCast(CO->getTrueExpr());
- CheckLValueToRValueCast(CO->getFalseExpr());
- return;
- }
+ void CheckLValueToRValueCast(Expr *E) {
+ E = E->IgnoreParenImpCasts();
- if (BinaryConditionalOperator *BCO =
- dyn_cast<BinaryConditionalOperator>(E)) {
- CheckLValueToRValueCast(BCO->getOpaqueValue()->getSourceExpr());
- CheckLValueToRValueCast(BCO->getFalseExpr());
- return;
- }
+ if (isa<DeclRefExpr>(E)) {
+ return;
+ }
- Visit(E);
+ if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
+ Visit(CO->getCond());
+ CheckLValueToRValueCast(CO->getTrueExpr());
+ CheckLValueToRValueCast(CO->getFalseExpr());
+ return;
}
- void VisitDeclRefExpr(DeclRefExpr *E) {
- if (VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
- if (Decls.count(VD))
- FoundDecl = true;
+ if (BinaryConditionalOperator *BCO =
+ dyn_cast<BinaryConditionalOperator>(E)) {
+ CheckLValueToRValueCast(BCO->getOpaqueValue()->getSourceExpr());
+ CheckLValueToRValueCast(BCO->getFalseExpr());
+ return;
}
- void VisitPseudoObjectExpr(PseudoObjectExpr *POE) {
- // Only need to visit the semantics for POE.
- // SyntaticForm doesn't really use the Decal.
- for (auto *S : POE->semantics()) {
- if (auto *OVE = dyn_cast<OpaqueValueExpr>(S))
- // Look past the OVE into the expression it binds.
- Visit(OVE->getSourceExpr());
- else
- Visit(S);
- }
+ Visit(E);
+ }
+
+ void VisitDeclRefExpr(DeclRefExpr *E) {
+ if (VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
+ if (Decls.count(VD))
+ FoundDecl = true;
+ }
+
+ void VisitPseudoObjectExpr(PseudoObjectExpr *POE) {
+ // Only need to visit the semantics for POE.
+ // SyntaticForm doesn't really use the Decal.
+ for (auto *S : POE->semantics()) {
+ if (auto *OVE = dyn_cast<OpaqueValueExpr>(S))
+ // Look past the OVE into the expression it binds.
+ Visit(OVE->getSourceExpr());
+ else
+ Visit(S);
}
+ }
- bool FoundDeclInUse() { return FoundDecl; }
+ bool FoundDeclInUse() { return FoundDecl; }
- }; // end class DeclMatcher
+}; // end class DeclMatcher
void CheckForLoopConditionalStatement(Sema &S, Expr *Second,
Expr *Third, Stmt *Body) {
@@ -1694,170 +1697,171 @@
if (BreakContinueFinder(S, Body).ContinueFound()) return;
S.Diag(LastDRE->getLocation(), diag::warn_redundant_loop_iteration)
- << LastDRE->getDecl() << LastIncrement;
+ << LastDRE->getDecl() << LastIncrement;
S.Diag(LoopDRE->getLocation(), diag::note_loop_iteration_here)
- << LoopIncrement;
+ << LoopIncrement;
}
-} // end namespace
+ } // end namespace
-
-void Sema::CheckBreakContinueBinding(Expr *E) {
- if (!E || getLangOpts().CPlusPlus)
- return;
- BreakContinueFinder BCFinder(*this, E);
- Scope *BreakParent = CurScope->getBreakParent();
- if (BCFinder.BreakFound() && BreakParent) {
- if (BreakParent->getFlags() & Scope::SwitchScope) {
- Diag(BCFinder.GetBreakLoc(), diag::warn_break_binds_to_switch);
- } else {
- Diag(BCFinder.GetBreakLoc(), diag::warn_loop_ctrl_binds_to_inner)
- << "break";
+ void Sema::CheckBreakContinueBinding(Expr *E) {
+ if (!E || getLangOpts().CPlusPlus)
+ return;
+ BreakContinueFinder BCFinder(*this, E);
+ Scope *BreakParent = CurScope->getBreakParent();
+ if (BCFinder.BreakFound() && BreakParent) {
+ if (BreakParent->getFlags() & Scope::SwitchScope) {
+ Diag(BCFinder.GetBreakLoc(), diag::warn_break_binds_to_switch);
+ } else {
+ Diag(BCFinder.GetBreakLoc(), diag::warn_loop_ctrl_binds_to_inner)
+ << "break";
+ }
+ } else if (BCFinder.ContinueFound() && CurScope->getContinueParent()) {
+ Diag(BCFinder.GetContinueLoc(), diag::warn_loop_ctrl_binds_to_inner)
+ << "continue";
}
- } else if (BCFinder.ContinueFound() && CurScope->getContinueParent()) {
- Diag(BCFinder.GetContinueLoc(), diag::warn_loop_ctrl_binds_to_inner)
- << "continue";
}
-}
-StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
- Stmt *First, ConditionResult Second,
- FullExprArg third, SourceLocation RParenLoc,
- Stmt *Body) {
- if (Second.isInvalid())
- return StmtError();
+ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
+ Stmt *First, ConditionResult Second,
+ FullExprArg third, SourceLocation RParenLoc,
+ Stmt *Body, BranchHint Hint) {
+ if (Second.isInvalid())
+ return StmtError();
- if (!getLangOpts().CPlusPlus) {
- if (DeclStmt *DS = dyn_cast_or_null<DeclStmt>(First)) {
- // C99 6.8.5p3: The declaration part of a 'for' statement shall only
- // declare identifiers for objects having storage class 'auto' or
- // 'register'.
- for (auto *DI : DS->decls()) {
- VarDecl *VD = dyn_cast<VarDecl>(DI);
- if (VD && VD->isLocalVarDecl() && !VD->hasLocalStorage())
- VD = nullptr;
- if (!VD) {
- Diag(DI->getLocation(), diag::err_non_local_variable_decl_in_for);
- DI->setInvalidDecl();
+ if (!getLangOpts().CPlusPlus) {
+ if (DeclStmt *DS = dyn_cast_or_null<DeclStmt>(First)) {
+ // C99 6.8.5p3: The declaration part of a 'for' statement shall only
+ // declare identifiers for objects having storage class 'auto' or
+ // 'register'.
+ for (auto *DI : DS->decls()) {
+ VarDecl *VD = dyn_cast<VarDecl>(DI);
+ if (VD && VD->isLocalVarDecl() && !VD->hasLocalStorage())
+ VD = nullptr;
+ if (!VD) {
+ Diag(DI->getLocation(), diag::err_non_local_variable_decl_in_for);
+ DI->setInvalidDecl();
+ }
}
}
}
- }
- CheckBreakContinueBinding(Second.get().second);
- CheckBreakContinueBinding(third.get());
+ CheckBreakContinueBinding(Second.get().second);
+ CheckBreakContinueBinding(third.get());
- if (!Second.get().first)
- CheckForLoopConditionalStatement(*this, Second.get().second, third.get(),
- Body);
- CheckForRedundantIteration(*this, third.get(), Body);
+ if (!Second.get().first)
+ CheckForLoopConditionalStatement(*this, Second.get().second, third.get(),
+ Body);
+ CheckForRedundantIteration(*this, third.get(), Body);
- if (Second.get().second &&
- !Diags.isIgnored(diag::warn_comma_operator,
- Second.get().second->getExprLoc()))
- CommaVisitor(*this).Visit(Second.get().second);
+ if (Second.get().second &&
+ !Diags.isIgnored(diag::warn_comma_operator,
+ Second.get().second->getExprLoc()))
+ CommaVisitor(*this).Visit(Second.get().second);
- Expr *Third = third.release().getAs<Expr>();
- if (isa<NullStmt>(Body))
- getCurCompoundScope().setHasEmptyLoopBodies();
+ Expr *Third = third.release().getAs<Expr>();
+ if (isa<NullStmt>(Body))
+ getCurCompoundScope().setHasEmptyLoopBodies();
- return new (Context)
- ForStmt(Context, First, Second.get().second, Second.get().first, Third,
- Body, ForLoc, LParenLoc, RParenLoc);
-}
+ return new (Context)
+ ForStmt(Context, First, Second.get().second, Second.get().first, Third,
+ Body, ForLoc, LParenLoc, RParenLoc, Hint);
+ }
-/// In an Objective C collection iteration statement:
-/// for (x in y)
-/// x can be an arbitrary l-value expression. Bind it up as a
-/// full-expression.
-StmtResult Sema::ActOnForEachLValueExpr(Expr *E) {
- // Reduce placeholder expressions here. Note that this rejects the
- // use of pseudo-object l-values in this position.
- ExprResult result = CheckPlaceholderExpr(E);
- if (result.isInvalid()) return StmtError();
- E = result.get();
-
- ExprResult FullExpr = ActOnFinishFullExpr(E, /*DiscardedValue*/ false);
- if (FullExpr.isInvalid())
- return StmtError();
- return StmtResult(static_cast<Stmt*>(FullExpr.get()));
-}
+ /// In an Objective C collection iteration statement:
+ /// for (x in y)
+ /// x can be an arbitrary l-value expression. Bind it up as a
+ /// full-expression.
+ StmtResult Sema::ActOnForEachLValueExpr(Expr *E) {
+ // Reduce placeholder expressions here. Note that this rejects the
+ // use of pseudo-object l-values in this position.
+ ExprResult result = CheckPlaceholderExpr(E);
+ if (result.isInvalid())
+ return StmtError();
+ E = result.get();
-ExprResult
-Sema::CheckObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) {
- if (!collection)
- return ExprError();
+ ExprResult FullExpr = ActOnFinishFullExpr(E, /*DiscardedValue*/ false);
+ if (FullExpr.isInvalid())
+ return StmtError();
+ return StmtResult(static_cast<Stmt *>(FullExpr.get()));
+ }
- ExprResult result = CorrectDelayedTyposInExpr(collection);
- if (!result.isUsable())
- return ExprError();
- collection = result.get();
+ ExprResult Sema::CheckObjCForCollectionOperand(SourceLocation forLoc,
+ Expr *collection) {
+ if (!collection)
+ return ExprError();
- // Bail out early if we've got a type-dependent expression.
- if (collection->isTypeDependent()) return collection;
+ ExprResult result = CorrectDelayedTyposInExpr(collection);
+ if (!result.isUsable())
+ return ExprError();
+ collection = result.get();
- // Perform normal l-value conversion.
- result = DefaultFunctionArrayLvalueConversion(collection);
- if (result.isInvalid())
- return ExprError();
- collection = result.get();
-
- // The operand needs to have object-pointer type.
- // TODO: should we do a contextual conversion?
- const ObjCObjectPointerType *pointerType =
- collection->getType()->getAs<ObjCObjectPointerType>();
- if (!pointerType)
- return Diag(forLoc, diag::err_collection_expr_type)
- << collection->getType() << collection->getSourceRange();
+ // Bail out early if we've got a type-dependent expression.
+ if (collection->isTypeDependent())
+ return collection;
- // Check that the operand provides
- // - countByEnumeratingWithState:objects:count:
- const ObjCObjectType *objectType = pointerType->getObjectType();
- ObjCInterfaceDecl *iface = objectType->getInterface();
-
- // If we have a forward-declared type, we can't do this check.
- // Under ARC, it is an error not to have a forward-declared class.
- if (iface &&
- (getLangOpts().ObjCAutoRefCount
- ? RequireCompleteType(forLoc, QualType(objectType, 0),
- diag::err_arc_collection_forward, collection)
- : !isCompleteType(forLoc, QualType(objectType, 0)))) {
- // Otherwise, if we have any useful type information, check that
- // the type declares the appropriate method.
- } else if (iface || !objectType->qual_empty()) {
- IdentifierInfo *selectorIdents[] = {
- &Context.Idents.get("countByEnumeratingWithState"),
- &Context.Idents.get("objects"),
- &Context.Idents.get("count")
- };
- Selector selector = Context.Selectors.getSelector(3, &selectorIdents[0]);
+ // Perform normal l-value conversion.
+ result = DefaultFunctionArrayLvalueConversion(collection);
+ if (result.isInvalid())
+ return ExprError();
+ collection = result.get();
+
+ // The operand needs to have object-pointer type.
+ // TODO: should we do a contextual conversion?
+ const ObjCObjectPointerType *pointerType =
+ collection->getType()->getAs<ObjCObjectPointerType>();
+ if (!pointerType)
+ return Diag(forLoc, diag::err_collection_expr_type)
+ << collection->getType() << collection->getSourceRange();
- ObjCMethodDecl *method = nullptr;
+ // Check that the operand provides
+ // - countByEnumeratingWithState:objects:count:
+ const ObjCObjectType *objectType = pointerType->getObjectType();
+ ObjCInterfaceDecl *iface = objectType->getInterface();
+
+ // If we have a forward-declared type, we can't do this check.
+ // Under ARC, it is an error not to have a forward-declared class.
+ if (iface &&
+ (getLangOpts().ObjCAutoRefCount
+ ? RequireCompleteType(forLoc, QualType(objectType, 0),
+ diag::err_arc_collection_forward, collection)
+ : !isCompleteType(forLoc, QualType(objectType, 0)))) {
+ // Otherwise, if we have any useful type information, check that
+ // the type declares the appropriate method.
+ } else if (iface || !objectType->qual_empty()) {
+ IdentifierInfo *selectorIdents[] = {
+ &Context.Idents.get("countByEnumeratingWithState"),
+ &Context.Idents.get("objects"), &Context.Idents.get("count")};
+ Selector selector = Context.Selectors.getSelector(3, &selectorIdents[0]);
+
+ ObjCMethodDecl *method = nullptr;
+
+ // If there's an interface, look in both the public and private APIs.
+ if (iface) {
+ method = iface->lookupInstanceMethod(selector);
+ if (!method)
+ method = iface->lookupPrivateMethod(selector);
+ }
- // If there's an interface, look in both the public and private APIs.
- if (iface) {
- method = iface->lookupInstanceMethod(selector);
- if (!method) method = iface->lookupPrivateMethod(selector);
- }
+ // Also check protocol qualifiers.
+ if (!method)
+ method = LookupMethodInQualifiedType(selector, pointerType,
+ /*instance*/ true);
- // Also check protocol qualifiers.
- if (!method)
- method = LookupMethodInQualifiedType(selector, pointerType,
- /*instance*/ true);
+ // If we didn't find it anywhere, give up.
+ if (!method) {
+ Diag(forLoc, diag::warn_collection_expr_type)
+ << collection->getType() << selector
+ << collection->getSourceRange();
+ }
- // If we didn't find it anywhere, give up.
- if (!method) {
- Diag(forLoc, diag::warn_collection_expr_type)
- << collection->getType() << selector << collection->getSourceRange();
+ // TODO: check for an incompatible signature?
}
- // TODO: check for an incompatible signature?
+ // Wrap up any cleanups in the expression.
+ return collection;
}
- // Wrap up any cleanups in the expression.
- return collection;
-}
-
StmtResult
Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc,
Stmt *First, Expr *collection,
@@ -2632,8 +2636,8 @@
return new (Context) CXXForRangeStmt(
InitStmt, RangeDS, cast_or_null<DeclStmt>(BeginDeclStmt.get()),
cast_or_null<DeclStmt>(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 +2809,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 +2818,7 @@
CXXForRangeStmt *ForStmt = cast<CXXForRangeStmt>(S);
ForStmt->setBody(B);
+ ForStmt->setBranchHint(Hint);
DiagnoseEmptyStmtBody(ForStmt->getRParenLoc(), B,
diag::warn_empty_range_based_for_body);
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/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/Parse/ParseStmt.cpp
===================================================================
--- clang/lib/Parse/ParseStmt.cpp
+++ clang/lib/Parse/ParseStmt.cpp
@@ -1169,7 +1169,6 @@
return false;
}
-
/// ParseIfStatement
/// if-statement: [C99 6.8.4.1]
/// 'if' '(' expression ')' statement
@@ -1259,6 +1258,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 +1300,7 @@
} else if (InnerStatementTrailingElseLoc.isValid()) {
Diag(InnerStatementTrailingElseLoc, diag::warn_dangling_else);
}
+ LikelihoodAttr *ElseLikelihoodAttr = getCurScope()->getBranchLikelihoodAttr();
IfScope.Exit();
@@ -1317,8 +1320,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 +1411,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 +1481,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 +1556,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 +1923,8 @@
// Pop the body scope if needed.
InnerScope.Exit();
+ LikelihoodAttr *BranchLikelihoodAttr =
+ getCurScope()->getBranchLikelihoodAttr();
// Leave the for-scope.
ForScope.Exit();
@@ -1909,15 +1932,17 @@
return StmtError();
if (ForEach)
- return Actions.FinishObjCForCollectionStmt(ForEachStmt.get(),
- Body.get());
+ return Actions.FinishObjCForCollectionStmt(ForEachStmt.get(), 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/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<BinaryOperator>(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/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<const Attr *> 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/Analysis/CFG.cpp
===================================================================
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -2194,11 +2194,20 @@
case Stmt::WhileStmtClass:
return VisitWhileStmt(cast<WhileStmt>(S));
- }
+ }
+}
+
+static bool isLikelyAttributedStmt(Stmt *S) {
+ if (!isa<AttributedStmt>(S))
+ return false;
+ AttributedStmt *Attribute = dyn_cast<AttributedStmt>(S);
+ return std::all_of(
+ Attribute->getAttrs().begin(), Attribute->getAttrs().end(),
+ [](const Attr *Elem) { return isa<LikelihoodAttr>(Elem); });
}
CFGBlock *CFGBuilder::VisitStmt(Stmt *S, AddStmtChoice asc) {
- if (asc.alwaysAdd(*this, S)) {
+ if (asc.alwaysAdd(*this, S) && !isLikelyAttributedStmt(S)) {
autoCreateBlock();
appendStmt(Block, S);
}
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/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/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<Stmt *>(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/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/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 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,14 +3895,12 @@
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,
+ StmtResult ActOnGotoStmt(SourceLocation GotoLoc, SourceLocation LabelLoc,
LabelDecl *TheDecl);
StmtResult ActOnIndirectGotoStmt(SourceLocation GotoLoc,
- SourceLocation StarLoc,
- Expr *DestExp);
+ SourceLocation StarLoc, Expr *DestExp);
StmtResult ActOnContinueStmt(SourceLocation ContinueLoc, Scope *CurScope);
StmtResult ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope);
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/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<CXX14>;
def ext_cxx17_attr : Extension<
"use of the %0 attribute is a C++17 extension">, InGroup<CXX17>;
+def ext_cxx2a_attr : Extension<
+ "use of the %0 attribute is a C++2a extension">, InGroup<CXX2a>;
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<ImplicitFallthrough>, 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<IgnoredAttributes>;
+def warn_conflicting_likelihood_attrs : Warning<
+ "attribute %0 conflicts with previous attribute %1">, InGroup<IgnoredAttributes>;
+
def warn_unreachable_default : Warning<
"default label in switch which covers all enumeration values">,
InGroup<CoveredSwitchDefault>, DefaultIgnore;
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/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/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/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<DeclStmt>(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<DeclStmt>(SubExprs[RANGE]);
}
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<Expr *>(getTrailingObjects<Stmt *>()[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<Expr *>(getTrailingObjects<Stmt *>()[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<Stmt *>(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<DeclStmt*>(SubExprs[CONDVAR]);
}
+ BranchHint getBranchHint() const { return ForStmtBits.Hint; }
+ void setBranchHint(BranchHint Hint) { ForStmtBits.Hint = Hint; }
+
Expr *getCond() { return reinterpret_cast<Expr*>(SubExprs[COND]); }
Expr *getInc() { return reinterpret_cast<Expr*>(SubExprs[INC]); }
Stmt *getBody() { return SubExprs[BODY]; }
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits