[clang] 506ec85 - [clang][dataflow] Add support for clang's `__builtin_expect`.
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 4 05:20:52 PDT 2022
Author: Yitzhak Mandelbaum
Date: 2022-04-04T12:20:43Z
New Revision: 506ec85ba82a39748f8fb07c15b93e1e958a9611
URL: https://github.com/llvm/llvm-project/commit/506ec85ba82a39748f8fb07c15b93e1e958a9611
DIFF: https://github.com/llvm/llvm-project/commit/506ec85ba82a39748f8fb07c15b93e1e958a9611.diff
LOG: [clang][dataflow] Add support for clang's `__builtin_expect`.
This patch adds basic modeling of `__builtin_expect`, just to propagate the
(first) argument, making the call transparent.
Driveby: adds tests for proper handling of other builtins.
Differential Revision: https://reviews.llvm.org/D122908
Added:
Modified:
clang/lib/Analysis/FlowSensitive/Transfer.cpp
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 1f5de9d67c135..271167b90030d 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -22,6 +22,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Basic/Builtins.h"
#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
@@ -444,6 +445,9 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
}
void VisitCallExpr(const CallExpr *S) {
+ // Of clang's builtins, only `__builtin_expect` is handled explicitly, since
+ // others (like trap, debugtrap, and unreachable) are handled by CFG
+ // construction.
if (S->isCallToStdMove()) {
assert(S->getNumArgs() == 1);
@@ -455,6 +459,18 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
return;
Env.setStorageLocation(*S, *ArgLoc);
+ } else if (S->getDirectCallee() != nullptr &&
+ S->getDirectCallee()->getBuiltinID() ==
+ Builtin::BI__builtin_expect) {
+ assert(S->getNumArgs() > 0);
+ assert(S->getArg(0) != nullptr);
+ // `__builtin_expect` returns by-value, so strip away any potential
+ // references in the argument.
+ auto *ArgLoc = Env.getStorageLocation(
+ *S->getArg(0)->IgnoreParenImpCasts(), SkipPast::Reference);
+ if (ArgLoc == nullptr)
+ return;
+ Env.setStorageLocation(*S, *ArgLoc);
}
}
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 540ed4781aebf..2fa5be182eea5 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2368,6 +2368,131 @@ TEST_F(TransferTest, AssignFromBoolNegation) {
});
}
+TEST_F(TransferTest, BuiltinExpect) {
+ std::string Code = R"(
+ void target(long Foo) {
+ long Bar = __builtin_expect(Foo, true);
+ /*[[p]]*/
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const auto &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None),
+ Env.getValue(*BarDecl, SkipPast::None));
+ });
+}
+
+TEST_F(TransferTest, BuiltinUnreachable) {
+ std::string Code = R"(
+ void target(bool Foo) {
+ bool Bar = false;
+ if (Foo)
+ Bar = Foo;
+ else
+ __builtin_unreachable();
+ (void)0;
+ /*[[p]]*/
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const auto &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ // `__builtin_unreachable` promises that the code is
+ // unreachable, so the compiler treats the "then" branch as the
+ // only possible predecessor of this statement.
+ EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None),
+ Env.getValue(*BarDecl, SkipPast::None));
+ });
+}
+
+TEST_F(TransferTest, BuiltinTrap) {
+ std::string Code = R"(
+ void target(bool Foo) {
+ bool Bar = false;
+ if (Foo)
+ Bar = Foo;
+ else
+ __builtin_trap();
+ (void)0;
+ /*[[p]]*/
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const auto &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ // `__builtin_trap` ensures program termination, so only the
+ // "then" branch is a predecessor of this statement.
+ EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None),
+ Env.getValue(*BarDecl, SkipPast::None));
+ });
+}
+
+TEST_F(TransferTest, BuiltinDebugTrap) {
+ std::string Code = R"(
+ void target(bool Foo) {
+ bool Bar = false;
+ if (Foo)
+ Bar = Foo;
+ else
+ __builtin_debugtrap();
+ (void)0;
+ /*[[p]]*/
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const auto &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ // `__builtin_debugtrap` doesn't ensure program termination.
+ EXPECT_NE(Env.getValue(*FooDecl, SkipPast::None),
+ Env.getValue(*BarDecl, SkipPast::None));
+ });
+}
+
TEST_F(TransferTest, StaticIntSingleVarDecl) {
std::string Code = R"(
void target() {
More information about the cfe-commits
mailing list