https://github.com/martinboehme updated https://github.com/llvm/llvm-project/pull/75170
>From 0ec84267ca5c76ff733c4a5907df9226260a4a76 Mon Sep 17 00:00:00 2001 From: Martin Braenne <mboe...@google.com> Date: Mon, 6 May 2024 10:06:29 +0000 Subject: [PATCH] [clang][dataflow] Strengthen pointer comparison. - Instead of comparing the identity of the `PointerValue`s, compare the underlying `StorageLocation`s. - If the `StorageLocation`s are the same, return a definite "true" as the result of the comparison. Before, if the `PointerValue`s were different, we would return an atom, even if the storage locations themselves were the same. - If the `StorageLocation`s are different, return an atom (as before). Pointers that have different storage locations may still alias, so we can't return a definite "false" in this case. The application-level gains from this are relatively modest. For the Crubit nullability check running on an internal codebase, this change reduces the number of functions on which the SAT solver times out from 223 to 221; the number of "pointer expression not modeled" errors reduces from 3815 to 3778. Still, it seems that the gain in precision is generally worthwhile. @Xazax-hun inspired me to think about this with his [comments](https://github.com/llvm/llvm-project/pull/73860#pullrequestreview-1761484615) on a different PR. --- clang/lib/Analysis/FlowSensitive/Transfer.cpp | 8 ++ .../Analysis/FlowSensitive/TransferTest.cpp | 88 +++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index fd224aeb79b151..0ff0ca74ffd033 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -68,6 +68,14 @@ static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS, if (auto *RHSBool = dyn_cast_or_null<BoolValue>(RHSValue)) return Env.makeIff(*LHSBool, *RHSBool); + if (auto *LHSPtr = dyn_cast_or_null<PointerValue>(LHSValue)) + if (auto *RHSPtr = dyn_cast_or_null<PointerValue>(RHSValue)) + // If the storage locations are the same, the pointers definitely compare + // the same. If the storage locations are different, they may still alias, + // so we fall through to the case below that returns an atom. + if (&LHSPtr->getPointeeLoc() == &RHSPtr->getPointeeLoc()) + return Env.getBoolLiteralValue(true); + return Env.makeAtomicBoolValue(); } diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 301bec32c0cf1d..555f3f2024f8b8 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -4586,6 +4586,94 @@ TEST(TransferTest, BooleanInequality) { }); } +TEST(TransferTest, PointerEquality) { + std::string Code = R"cc( + void target() { + int i = 0; + int i_other = 0; + int *p1 = &i; + int *p2 = &i; + int *p_other = &i_other; + int *null = nullptr; + + bool p1_eq_p1 = (p1 == p1); + bool p1_eq_p2 = (p1 == p2); + bool p1_eq_p_other = (p1 == p_other); + + bool p1_eq_null = (p1 == null); + bool p1_eq_nullptr = (p1 == nullptr); + bool null_eq_nullptr = (null == nullptr); + bool nullptr_eq_nullptr = (nullptr == nullptr); + + // We won't duplicate all of the tests above with `!=`, as we know that + // the implementation simply negates the result of the `==` comparison. + // Instaed, just spot-check one case. + bool p1_ne_p1 = (p1 != p1); + + (void)0; // [[p]] + } + )cc"; + runDataflow( + Code, + [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + // Check the we have indeed set things up so that `p1` and `p2` have + // different pointer values. + EXPECT_NE(&getValueForDecl<PointerValue>(ASTCtx, Env, "p1"), + &getValueForDecl<PointerValue>(ASTCtx, Env, "p2")); + + EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p1"), + &Env.getBoolLiteralValue(true)); + EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p2"), + &Env.getBoolLiteralValue(true)); + EXPECT_TRUE(isa<AtomicBoolValue>( + getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p_other"))); + + EXPECT_TRUE(isa<AtomicBoolValue>( + getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_null"))); + EXPECT_TRUE(isa<AtomicBoolValue>( + getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_nullptr"))); + EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "null_eq_nullptr"), + &Env.getBoolLiteralValue(true)); + EXPECT_EQ( + &getValueForDecl<BoolValue>(ASTCtx, Env, "nullptr_eq_nullptr"), + &Env.getBoolLiteralValue(true)); + + EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_ne_p1"), + &Env.getBoolLiteralValue(false)); + }); +} + +TEST(TransferTest, PointerEqualityUnionMembers) { + std::string Code = R"cc( + union U { + int i1; + int i2; + }; + void target() { + U u; + bool i1_eq_i2 = (&u.i1 == &u.i2); + + (void)0; // [[p]] + } + )cc"; + runDataflow( + Code, + [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + // FIXME: By the standard, `u.i1` and `u.i2` should have the same + // address, but we don't yet model this property of union members + // correctly. The result is therefore weaker than it could be (just an + // atom rather than a true literal), though not wrong. + EXPECT_TRUE(isa<AtomicBoolValue>( + getValueForDecl<BoolValue>(ASTCtx, Env, "i1_eq_i2"))); + }); +} + TEST(TransferTest, IntegerLiteralEquality) { std::string Code = R"( void target() { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits