193ce23adSGabor Marton //===- unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp --===//
293ce23adSGabor Marton //
393ce23adSGabor Marton // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
493ce23adSGabor Marton // See https://llvm.org/LICENSE.txt for license information.
593ce23adSGabor Marton // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
693ce23adSGabor Marton //
793ce23adSGabor Marton //===----------------------------------------------------------------------===//
893ce23adSGabor Marton //
993ce23adSGabor Marton // This file defines a simplistic version of Sign Analysis as a demo of a
1093ce23adSGabor Marton // forward, monotonic dataflow analysis. The implementation uses 3 boolean
1193ce23adSGabor Marton // values to represent the sign lattice (negative, zero, positive). In
1293ce23adSGabor Marton // practice, 2 booleans would be enough, however, this approach has the
1393ce23adSGabor Marton // advantage of clarity over the optimized solution.
1493ce23adSGabor Marton //
1593ce23adSGabor Marton //===----------------------------------------------------------------------===//
1693ce23adSGabor Marton
1793ce23adSGabor Marton #include "TestingSupport.h"
1893ce23adSGabor Marton #include "clang/ASTMatchers/ASTMatchFinder.h"
1993ce23adSGabor Marton #include "clang/ASTMatchers/ASTMatchers.h"
2093ce23adSGabor Marton #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
2193ce23adSGabor Marton #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
2293ce23adSGabor Marton #include "clang/Analysis/FlowSensitive/NoopLattice.h"
2393ce23adSGabor Marton #include "llvm/ADT/StringRef.h"
243432f4bfSJordan Rupprecht #include "llvm/Testing/Annotations/Annotations.h"
2593ce23adSGabor Marton #include "llvm/Testing/Support/Error.h"
2693ce23adSGabor Marton #include "gtest/gtest.h"
2793ce23adSGabor Marton #include <memory>
2893ce23adSGabor Marton
2993ce23adSGabor Marton namespace {
3093ce23adSGabor Marton
3193ce23adSGabor Marton using namespace clang;
3293ce23adSGabor Marton using namespace dataflow;
3393ce23adSGabor Marton using namespace ast_matchers;
3493ce23adSGabor Marton using namespace test;
3593ce23adSGabor Marton using ::testing::UnorderedElementsAre;
3693ce23adSGabor Marton
3793ce23adSGabor Marton enum class Sign : int { Negative, Zero, Positive };
3893ce23adSGabor Marton
getSign(int64_t V)3993ce23adSGabor Marton Sign getSign(int64_t V) {
4093ce23adSGabor Marton return V == 0 ? Sign::Zero : (V < 0 ? Sign::Negative : Sign::Positive);
4193ce23adSGabor Marton }
4293ce23adSGabor Marton
4393ce23adSGabor Marton using LatticeTransferState = TransferState<NoopLattice>;
4493ce23adSGabor Marton
4593ce23adSGabor Marton constexpr char kVar[] = "var";
4693ce23adSGabor Marton
initNegative(Value & Val,Environment & Env)4793ce23adSGabor Marton void initNegative(Value &Val, Environment &Env) {
4893ce23adSGabor Marton Val.setProperty("neg", Env.getBoolLiteralValue(true));
4993ce23adSGabor Marton Val.setProperty("zero", Env.getBoolLiteralValue(false));
5093ce23adSGabor Marton Val.setProperty("pos", Env.getBoolLiteralValue(false));
5193ce23adSGabor Marton }
initPositive(Value & Val,Environment & Env)5293ce23adSGabor Marton void initPositive(Value &Val, Environment &Env) {
5393ce23adSGabor Marton Val.setProperty("neg", Env.getBoolLiteralValue(false));
5493ce23adSGabor Marton Val.setProperty("zero", Env.getBoolLiteralValue(false));
5593ce23adSGabor Marton Val.setProperty("pos", Env.getBoolLiteralValue(true));
5693ce23adSGabor Marton }
initZero(Value & Val,Environment & Env)5793ce23adSGabor Marton void initZero(Value &Val, Environment &Env) {
5893ce23adSGabor Marton Val.setProperty("neg", Env.getBoolLiteralValue(false));
5993ce23adSGabor Marton Val.setProperty("zero", Env.getBoolLiteralValue(true));
6093ce23adSGabor Marton Val.setProperty("pos", Env.getBoolLiteralValue(false));
6193ce23adSGabor Marton }
6293ce23adSGabor Marton
6393ce23adSGabor Marton // The boolean properties that are associated to a Value. If a property is not
6493ce23adSGabor Marton // set then these are null pointers, otherwise, the pointed BoolValues are
6593ce23adSGabor Marton // owned by the Environment.
6693ce23adSGabor Marton struct SignProperties {
6793ce23adSGabor Marton BoolValue *Neg, *Zero, *Pos;
6893ce23adSGabor Marton };
setSignProperties(Value & Val,const SignProperties & Ps)6993ce23adSGabor Marton void setSignProperties(Value &Val, const SignProperties &Ps) {
7093ce23adSGabor Marton Val.setProperty("neg", *Ps.Neg);
7193ce23adSGabor Marton Val.setProperty("zero", *Ps.Zero);
7293ce23adSGabor Marton Val.setProperty("pos", *Ps.Pos);
7393ce23adSGabor Marton }
initUnknown(Value & Val,Environment & Env)7493ce23adSGabor Marton SignProperties initUnknown(Value &Val, Environment &Env) {
7593ce23adSGabor Marton SignProperties Ps{&Env.makeAtomicBoolValue(), &Env.makeAtomicBoolValue(),
7693ce23adSGabor Marton &Env.makeAtomicBoolValue()};
7793ce23adSGabor Marton setSignProperties(Val, Ps);
7893ce23adSGabor Marton return Ps;
7993ce23adSGabor Marton }
getSignProperties(const Value & Val,const Environment & Env)8093ce23adSGabor Marton SignProperties getSignProperties(const Value &Val, const Environment &Env) {
8193ce23adSGabor Marton return {dyn_cast_or_null<BoolValue>(Val.getProperty("neg")),
8293ce23adSGabor Marton dyn_cast_or_null<BoolValue>(Val.getProperty("zero")),
8393ce23adSGabor Marton dyn_cast_or_null<BoolValue>(Val.getProperty("pos"))};
8493ce23adSGabor Marton }
8593ce23adSGabor Marton
transferUninitializedInt(const DeclStmt * D,const MatchFinder::MatchResult & M,LatticeTransferState & State)8693ce23adSGabor Marton void transferUninitializedInt(const DeclStmt *D,
8793ce23adSGabor Marton const MatchFinder::MatchResult &M,
8893ce23adSGabor Marton LatticeTransferState &State) {
8993ce23adSGabor Marton const auto *Var = M.Nodes.getNodeAs<clang::VarDecl>(kVar);
9093ce23adSGabor Marton assert(Var != nullptr);
919940fac7SMartin Braenne const StorageLocation *Loc = State.Env.getStorageLocation(*Var);
9293ce23adSGabor Marton Value *Val = State.Env.getValue(*Loc);
9393ce23adSGabor Marton initUnknown(*Val, State.Env);
9493ce23adSGabor Marton }
9593ce23adSGabor Marton
9693ce23adSGabor Marton // Get the Value (1), the properties for the operand (2), and the properties
9793ce23adSGabor Marton // for the unary operator (3). The return value is a tuple of (1,2,3).
9893ce23adSGabor Marton //
9993ce23adSGabor Marton // The returned Value (1) is a nullptr, if there is no Value associated to the
10093ce23adSGabor Marton // operand of the unary operator, or if the properties are not set for that
10193ce23adSGabor Marton // operand.
10293ce23adSGabor Marton // Other than that, new sign properties are created for the Value of the
10393ce23adSGabor Marton // unary operator and a new Value is created for the unary operator itself if
10493ce23adSGabor Marton // it hadn't have any previously.
10593ce23adSGabor Marton std::tuple<Value *, SignProperties, SignProperties>
getValueAndSignProperties(const UnaryOperator * UO,const MatchFinder::MatchResult & M,LatticeTransferState & State)10693ce23adSGabor Marton getValueAndSignProperties(const UnaryOperator *UO,
10793ce23adSGabor Marton const MatchFinder::MatchResult &M,
10893ce23adSGabor Marton LatticeTransferState &State) {
10993ce23adSGabor Marton // The DeclRefExpr refers to this variable in the operand.
11093ce23adSGabor Marton const auto *OperandVar = M.Nodes.getNodeAs<clang::VarDecl>(kVar);
11193ce23adSGabor Marton assert(OperandVar != nullptr);
1120c852dc8SMartin Braenne const auto *OperandValue = State.Env.getValue(*OperandVar);
11393ce23adSGabor Marton if (!OperandValue)
11493ce23adSGabor Marton return {nullptr, {}, {}};
11593ce23adSGabor Marton
11693ce23adSGabor Marton // Value of the unary op.
117e95134b9SMartin Braenne auto *UnaryOpValue = State.Env.getValue(*UO);
11893ce23adSGabor Marton if (!UnaryOpValue) {
11993ce23adSGabor Marton UnaryOpValue = &State.Env.makeAtomicBoolValue();
120b244b6aeSMartin Braenne State.Env.setValue(*UO, *UnaryOpValue);
12193ce23adSGabor Marton }
12293ce23adSGabor Marton
12393ce23adSGabor Marton // Properties for the operand (sub expression).
12493ce23adSGabor Marton SignProperties OperandProps = getSignProperties(*OperandValue, State.Env);
12593ce23adSGabor Marton if (OperandProps.Neg == nullptr)
12693ce23adSGabor Marton return {nullptr, {}, {}};
12793ce23adSGabor Marton // Properties for the operator expr itself.
12893ce23adSGabor Marton SignProperties UnaryOpProps = initUnknown(*UnaryOpValue, State.Env);
12993ce23adSGabor Marton return {UnaryOpValue, UnaryOpProps, OperandProps};
13093ce23adSGabor Marton }
13193ce23adSGabor Marton
transferBinary(const BinaryOperator * BO,const MatchFinder::MatchResult & M,LatticeTransferState & State)13293ce23adSGabor Marton void transferBinary(const BinaryOperator *BO, const MatchFinder::MatchResult &M,
13393ce23adSGabor Marton LatticeTransferState &State) {
1346272226bSSam McCall auto &A = State.Env.arena();
1356272226bSSam McCall const Formula *Comp;
1362ee396b0Smartinboehme if (BoolValue *V = State.Env.get<BoolValue>(*BO)) {
1376272226bSSam McCall Comp = &V->formula();
1386272226bSSam McCall } else {
1396272226bSSam McCall Comp = &A.makeAtomRef(A.makeAtom());
140b244b6aeSMartin Braenne State.Env.setValue(*BO, A.makeBoolValue(*Comp));
14193ce23adSGabor Marton }
14293ce23adSGabor Marton
14393ce23adSGabor Marton // FIXME Use this as well:
14493ce23adSGabor Marton // auto *NegatedComp = &State.Env.makeNot(*Comp);
14593ce23adSGabor Marton
146e95134b9SMartin Braenne auto *LHS = State.Env.getValue(*BO->getLHS());
147e95134b9SMartin Braenne auto *RHS = State.Env.getValue(*BO->getRHS());
14893ce23adSGabor Marton
14993ce23adSGabor Marton if (!LHS || !RHS)
15093ce23adSGabor Marton return;
15193ce23adSGabor Marton
15293ce23adSGabor Marton SignProperties LHSProps = getSignProperties(*LHS, State.Env);
15393ce23adSGabor Marton SignProperties RHSProps = getSignProperties(*RHS, State.Env);
15493ce23adSGabor Marton if (LHSProps.Neg == nullptr || RHSProps.Neg == nullptr)
15593ce23adSGabor Marton return;
15693ce23adSGabor Marton
15793ce23adSGabor Marton switch (BO->getOpcode()) {
15893ce23adSGabor Marton case BO_GT:
15993ce23adSGabor Marton // pos > pos
160526c9b7eSmartinboehme State.Env.assume(
1616272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Pos->formula(),
1626272226bSSam McCall LHSProps.Pos->formula())));
16393ce23adSGabor Marton // pos > zero
164526c9b7eSmartinboehme State.Env.assume(
1656272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Zero->formula(),
1666272226bSSam McCall LHSProps.Pos->formula())));
16793ce23adSGabor Marton break;
16893ce23adSGabor Marton case BO_LT:
16993ce23adSGabor Marton // neg < neg
170526c9b7eSmartinboehme State.Env.assume(
1716272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Neg->formula(),
1726272226bSSam McCall LHSProps.Neg->formula())));
17393ce23adSGabor Marton // neg < zero
174526c9b7eSmartinboehme State.Env.assume(
1756272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Zero->formula(),
1766272226bSSam McCall LHSProps.Neg->formula())));
17793ce23adSGabor Marton break;
17893ce23adSGabor Marton case BO_GE:
17993ce23adSGabor Marton // pos >= pos
180526c9b7eSmartinboehme State.Env.assume(
1816272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Pos->formula(),
1826272226bSSam McCall LHSProps.Pos->formula())));
18393ce23adSGabor Marton break;
18493ce23adSGabor Marton case BO_LE:
18593ce23adSGabor Marton // neg <= neg
186526c9b7eSmartinboehme State.Env.assume(
1876272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Neg->formula(),
1886272226bSSam McCall LHSProps.Neg->formula())));
18993ce23adSGabor Marton break;
19093ce23adSGabor Marton case BO_EQ:
191526c9b7eSmartinboehme State.Env.assume(
1926272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Neg->formula(),
1936272226bSSam McCall LHSProps.Neg->formula())));
194526c9b7eSmartinboehme State.Env.assume(
1956272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Zero->formula(),
1966272226bSSam McCall LHSProps.Zero->formula())));
197526c9b7eSmartinboehme State.Env.assume(
1986272226bSSam McCall A.makeImplies(*Comp, A.makeImplies(RHSProps.Pos->formula(),
1996272226bSSam McCall LHSProps.Pos->formula())));
20093ce23adSGabor Marton break;
20193ce23adSGabor Marton case BO_NE: // Noop.
20293ce23adSGabor Marton break;
20393ce23adSGabor Marton default:
20493ce23adSGabor Marton llvm_unreachable("not implemented");
20593ce23adSGabor Marton }
20693ce23adSGabor Marton }
20793ce23adSGabor Marton
transferUnaryMinus(const UnaryOperator * UO,const MatchFinder::MatchResult & M,LatticeTransferState & State)20893ce23adSGabor Marton void transferUnaryMinus(const UnaryOperator *UO,
20993ce23adSGabor Marton const MatchFinder::MatchResult &M,
21093ce23adSGabor Marton LatticeTransferState &State) {
2116272226bSSam McCall auto &A = State.Env.arena();
21293ce23adSGabor Marton auto [UnaryOpValue, UnaryOpProps, OperandProps] =
21393ce23adSGabor Marton getValueAndSignProperties(UO, M, State);
21493ce23adSGabor Marton if (!UnaryOpValue)
21593ce23adSGabor Marton return;
21693ce23adSGabor Marton
21793ce23adSGabor Marton // a is pos ==> -a is neg
218526c9b7eSmartinboehme State.Env.assume(
2196272226bSSam McCall A.makeImplies(OperandProps.Pos->formula(), UnaryOpProps.Neg->formula()));
22093ce23adSGabor Marton // a is neg ==> -a is pos
221526c9b7eSmartinboehme State.Env.assume(
2226272226bSSam McCall A.makeImplies(OperandProps.Neg->formula(), UnaryOpProps.Pos->formula()));
22393ce23adSGabor Marton // a is zero ==> -a is zero
224526c9b7eSmartinboehme State.Env.assume(A.makeImplies(OperandProps.Zero->formula(),
2256272226bSSam McCall UnaryOpProps.Zero->formula()));
22693ce23adSGabor Marton }
22793ce23adSGabor Marton
transferUnaryNot(const UnaryOperator * UO,const MatchFinder::MatchResult & M,LatticeTransferState & State)22893ce23adSGabor Marton void transferUnaryNot(const UnaryOperator *UO,
22993ce23adSGabor Marton const MatchFinder::MatchResult &M,
23093ce23adSGabor Marton LatticeTransferState &State) {
2316272226bSSam McCall auto &A = State.Env.arena();
23293ce23adSGabor Marton auto [UnaryOpValue, UnaryOpProps, OperandProps] =
23393ce23adSGabor Marton getValueAndSignProperties(UO, M, State);
23493ce23adSGabor Marton if (!UnaryOpValue)
23593ce23adSGabor Marton return;
23693ce23adSGabor Marton
23793ce23adSGabor Marton // a is neg or pos ==> !a is zero
238526c9b7eSmartinboehme State.Env.assume(A.makeImplies(
2396272226bSSam McCall A.makeOr(OperandProps.Pos->formula(), OperandProps.Neg->formula()),
2406272226bSSam McCall UnaryOpProps.Zero->formula()));
24193ce23adSGabor Marton
24293ce23adSGabor Marton // FIXME Handle this logic universally, not just for unary not. But Where to
24393ce23adSGabor Marton // put the generic handler, transferExpr maybe?
24493ce23adSGabor Marton if (auto *UOBoolVal = dyn_cast<BoolValue>(UnaryOpValue)) {
24593ce23adSGabor Marton // !a <==> a is zero
246526c9b7eSmartinboehme State.Env.assume(
2476272226bSSam McCall A.makeEquals(UOBoolVal->formula(), OperandProps.Zero->formula()));
24893ce23adSGabor Marton // !a <==> !a is not zero
249526c9b7eSmartinboehme State.Env.assume(A.makeEquals(UOBoolVal->formula(),
250526c9b7eSmartinboehme A.makeNot(UnaryOpProps.Zero->formula())));
25193ce23adSGabor Marton }
25293ce23adSGabor Marton }
25393ce23adSGabor Marton
25496b22e1cSMartin Braenne // Returns the `Value` associated with `E` (which may be either a prvalue or
25596b22e1cSMartin Braenne // glvalue). Creates a `Value` or `StorageLocation` as needed if `E` does not
25696b22e1cSMartin Braenne // have either of these associated with it yet.
25796b22e1cSMartin Braenne //
25896b22e1cSMartin Braenne // If this functionality turns out to be needed in more cases, this function
25996b22e1cSMartin Braenne // should be moved to a more central location.
getOrCreateValue(const Expr * E,Environment & Env)26096b22e1cSMartin Braenne Value *getOrCreateValue(const Expr *E, Environment &Env) {
26196b22e1cSMartin Braenne Value *Val = nullptr;
26296b22e1cSMartin Braenne if (E->isGLValue()) {
263b244b6aeSMartin Braenne StorageLocation *Loc = Env.getStorageLocation(*E);
26496b22e1cSMartin Braenne if (!Loc) {
26596b22e1cSMartin Braenne Loc = &Env.createStorageLocation(*E);
266b244b6aeSMartin Braenne Env.setStorageLocation(*E, *Loc);
26796b22e1cSMartin Braenne }
26896b22e1cSMartin Braenne Val = Env.getValue(*Loc);
26996b22e1cSMartin Braenne if (!Val) {
27096b22e1cSMartin Braenne Val = Env.createValue(E->getType());
27196b22e1cSMartin Braenne Env.setValue(*Loc, *Val);
27296b22e1cSMartin Braenne }
27396b22e1cSMartin Braenne } else {
274e95134b9SMartin Braenne Val = Env.getValue(*E);
27596b22e1cSMartin Braenne if (!Val) {
27696b22e1cSMartin Braenne Val = Env.createValue(E->getType());
277b244b6aeSMartin Braenne Env.setValue(*E, *Val);
27896b22e1cSMartin Braenne }
27996b22e1cSMartin Braenne }
28096b22e1cSMartin Braenne assert(Val != nullptr);
28196b22e1cSMartin Braenne
28296b22e1cSMartin Braenne return Val;
28396b22e1cSMartin Braenne }
28496b22e1cSMartin Braenne
transferExpr(const Expr * E,const MatchFinder::MatchResult & M,LatticeTransferState & State)28593ce23adSGabor Marton void transferExpr(const Expr *E, const MatchFinder::MatchResult &M,
28693ce23adSGabor Marton LatticeTransferState &State) {
28793ce23adSGabor Marton const ASTContext &Context = *M.Context;
28896b22e1cSMartin Braenne
28996b22e1cSMartin Braenne Value *Val = getOrCreateValue(E, State.Env);
29096b22e1cSMartin Braenne
29193ce23adSGabor Marton // The sign symbolic values have been initialized already.
29293ce23adSGabor Marton if (Val->getProperty("neg"))
29393ce23adSGabor Marton return;
29493ce23adSGabor Marton
29593ce23adSGabor Marton Expr::EvalResult R;
29693ce23adSGabor Marton // An integer expression which we cannot evaluate.
29793ce23adSGabor Marton if (!(E->EvaluateAsInt(R, Context) && R.Val.isInt())) {
29893ce23adSGabor Marton initUnknown(*Val, State.Env);
29993ce23adSGabor Marton return;
30093ce23adSGabor Marton }
30193ce23adSGabor Marton
30293ce23adSGabor Marton const Sign S = getSign(R.Val.getInt().getExtValue());
30393ce23adSGabor Marton switch (S) {
30493ce23adSGabor Marton case Sign::Negative:
30593ce23adSGabor Marton initNegative(*Val, State.Env);
30693ce23adSGabor Marton break;
30793ce23adSGabor Marton case Sign::Zero:
30893ce23adSGabor Marton initZero(*Val, State.Env);
30993ce23adSGabor Marton break;
31093ce23adSGabor Marton case Sign::Positive:
31193ce23adSGabor Marton initPositive(*Val, State.Env);
31293ce23adSGabor Marton break;
31393ce23adSGabor Marton }
31493ce23adSGabor Marton }
31593ce23adSGabor Marton
refToVar()31693ce23adSGabor Marton auto refToVar() { return declRefExpr(to(varDecl().bind(kVar))); }
31793ce23adSGabor Marton
buildTransferMatchSwitch()31893ce23adSGabor Marton auto buildTransferMatchSwitch() {
31993ce23adSGabor Marton // Note, the order of the cases is important, the most generic should be
32093ce23adSGabor Marton // added last.
32193ce23adSGabor Marton // FIXME Discover what happens if there are multiple matching ASTMatchers for
32293ce23adSGabor Marton // one Stmt? All matching case's handler should be called and in what order?
32393ce23adSGabor Marton return CFGMatchSwitchBuilder<LatticeTransferState>()
32493ce23adSGabor Marton // a op b (comparison)
32593ce23adSGabor Marton .CaseOfCFGStmt<BinaryOperator>(binaryOperator(isComparisonOperator()),
32693ce23adSGabor Marton transferBinary)
32793ce23adSGabor Marton
32893ce23adSGabor Marton // FIXME handle binop +,-,*,/
32993ce23adSGabor Marton
33093ce23adSGabor Marton // -a
33193ce23adSGabor Marton .CaseOfCFGStmt<UnaryOperator>(
33293ce23adSGabor Marton unaryOperator(hasOperatorName("-"),
33393ce23adSGabor Marton hasUnaryOperand(hasDescendant(refToVar()))),
33493ce23adSGabor Marton transferUnaryMinus)
33593ce23adSGabor Marton
33693ce23adSGabor Marton // !a
33793ce23adSGabor Marton .CaseOfCFGStmt<UnaryOperator>(
33893ce23adSGabor Marton unaryOperator(hasOperatorName("!"),
33993ce23adSGabor Marton hasUnaryOperand(hasDescendant(refToVar()))),
34093ce23adSGabor Marton transferUnaryNot)
34193ce23adSGabor Marton
34293ce23adSGabor Marton // int a;
34393ce23adSGabor Marton .CaseOfCFGStmt<DeclStmt>(declStmt(hasSingleDecl(varDecl(
34493ce23adSGabor Marton decl().bind(kVar), hasType(isInteger()),
34593ce23adSGabor Marton unless(hasInitializer(expr()))))),
34693ce23adSGabor Marton transferUninitializedInt)
34793ce23adSGabor Marton
34893ce23adSGabor Marton // constexpr int
34993ce23adSGabor Marton .CaseOfCFGStmt<Expr>(expr(hasType(isInteger())), transferExpr)
35093ce23adSGabor Marton
35193ce23adSGabor Marton .Build();
35293ce23adSGabor Marton }
35393ce23adSGabor Marton
35493ce23adSGabor Marton class SignPropagationAnalysis
35593ce23adSGabor Marton : public DataflowAnalysis<SignPropagationAnalysis, NoopLattice> {
35693ce23adSGabor Marton public:
SignPropagationAnalysis(ASTContext & Context)35793ce23adSGabor Marton SignPropagationAnalysis(ASTContext &Context)
35893ce23adSGabor Marton : DataflowAnalysis<SignPropagationAnalysis, NoopLattice>(Context),
35993ce23adSGabor Marton TransferMatchSwitch(buildTransferMatchSwitch()) {}
36093ce23adSGabor Marton
initialElement()36193ce23adSGabor Marton static NoopLattice initialElement() { return {}; }
36293ce23adSGabor Marton
transfer(const CFGElement & Elt,NoopLattice & L,Environment & Env)3636b991ba4SYitzhak Mandelbaum void transfer(const CFGElement &Elt, NoopLattice &L, Environment &Env) {
36493ce23adSGabor Marton LatticeTransferState State(L, Env);
3656b991ba4SYitzhak Mandelbaum TransferMatchSwitch(Elt, getASTContext(), State);
36693ce23adSGabor Marton }
367*672fb27bSYitzhak Mandelbaum void join(QualType Type, const Value &Val1, const Environment &Env1,
36893ce23adSGabor Marton const Value &Val2, const Environment &Env2, Value &MergedVal,
36993ce23adSGabor Marton Environment &MergedEnv) override;
37093ce23adSGabor Marton
37193ce23adSGabor Marton private:
37293ce23adSGabor Marton CFGMatchSwitch<TransferState<NoopLattice>> TransferMatchSwitch;
37393ce23adSGabor Marton };
37493ce23adSGabor Marton
joinBoolValues(BoolValue & Bool1,const Environment & Env1,BoolValue & Bool2,const Environment & Env2,Environment & JoinedEnv)375*672fb27bSYitzhak Mandelbaum BoolValue &joinBoolValues(BoolValue &Bool1, const Environment &Env1,
37693ce23adSGabor Marton BoolValue &Bool2, const Environment &Env2,
377*672fb27bSYitzhak Mandelbaum Environment &JoinedEnv) {
37893ce23adSGabor Marton if (&Bool1 == &Bool2) {
37993ce23adSGabor Marton return Bool1;
38093ce23adSGabor Marton }
38193ce23adSGabor Marton
3826272226bSSam McCall auto &B1 = Bool1.formula();
3836272226bSSam McCall auto &B2 = Bool2.formula();
3846272226bSSam McCall
385*672fb27bSYitzhak Mandelbaum auto &A = JoinedEnv.arena();
386*672fb27bSYitzhak Mandelbaum auto &JoinedBool = JoinedEnv.makeAtomicBoolValue();
38793ce23adSGabor Marton
38893ce23adSGabor Marton // If `Bool1` and `Bool2` is constrained to the same true / false value,
389*672fb27bSYitzhak Mandelbaum // `JoinedBool` can be constrained similarly without needing to consider the
390*672fb27bSYitzhak Mandelbaum // path taken - this simplifies the flow condition tracked in `JoinedEnv`.
39193ce23adSGabor Marton // Otherwise, information about which path was taken is used to associate
392*672fb27bSYitzhak Mandelbaum // `JoinedBool` with `Bool1` and `Bool2`.
393526c9b7eSmartinboehme if (Env1.proves(B1) && Env2.proves(B2)) {
394*672fb27bSYitzhak Mandelbaum JoinedEnv.assume(JoinedBool.formula());
395526c9b7eSmartinboehme } else if (Env1.proves(A.makeNot(B1)) && Env2.proves(A.makeNot(B2))) {
396*672fb27bSYitzhak Mandelbaum JoinedEnv.assume(A.makeNot(JoinedBool.formula()));
39793ce23adSGabor Marton }
398*672fb27bSYitzhak Mandelbaum return JoinedBool;
39993ce23adSGabor Marton }
40093ce23adSGabor Marton
join(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2,Value & JoinedVal,Environment & JoinedEnv)401*672fb27bSYitzhak Mandelbaum void SignPropagationAnalysis::join(QualType Type, const Value &Val1,
40293ce23adSGabor Marton const Environment &Env1, const Value &Val2,
403*672fb27bSYitzhak Mandelbaum const Environment &Env2, Value &JoinedVal,
404*672fb27bSYitzhak Mandelbaum Environment &JoinedEnv) {
40593ce23adSGabor Marton if (!Type->isIntegerType())
406*672fb27bSYitzhak Mandelbaum return;
40793ce23adSGabor Marton SignProperties Ps1 = getSignProperties(Val1, Env1);
40893ce23adSGabor Marton SignProperties Ps2 = getSignProperties(Val2, Env2);
40993ce23adSGabor Marton if (!Ps1.Neg || !Ps2.Neg)
410*672fb27bSYitzhak Mandelbaum return;
411*672fb27bSYitzhak Mandelbaum BoolValue &JoinedNeg =
412*672fb27bSYitzhak Mandelbaum joinBoolValues(*Ps1.Neg, Env1, *Ps2.Neg, Env2, JoinedEnv);
413*672fb27bSYitzhak Mandelbaum BoolValue &JoinedZero =
414*672fb27bSYitzhak Mandelbaum joinBoolValues(*Ps1.Zero, Env1, *Ps2.Zero, Env2, JoinedEnv);
415*672fb27bSYitzhak Mandelbaum BoolValue &JoinedPos =
416*672fb27bSYitzhak Mandelbaum joinBoolValues(*Ps1.Pos, Env1, *Ps2.Pos, Env2, JoinedEnv);
417*672fb27bSYitzhak Mandelbaum setSignProperties(JoinedVal,
418*672fb27bSYitzhak Mandelbaum SignProperties{&JoinedNeg, &JoinedZero, &JoinedPos});
41993ce23adSGabor Marton }
42093ce23adSGabor Marton
42193ce23adSGabor Marton template <typename Matcher>
runDataflow(llvm::StringRef Code,Matcher Match,LangStandard::Kind Std=LangStandard::lang_cxx17,llvm::StringRef TargetFun="fun")42293ce23adSGabor Marton void runDataflow(llvm::StringRef Code, Matcher Match,
42393ce23adSGabor Marton LangStandard::Kind Std = LangStandard::lang_cxx17,
42493ce23adSGabor Marton llvm::StringRef TargetFun = "fun") {
42593ce23adSGabor Marton using ast_matchers::hasName;
42693ce23adSGabor Marton ASSERT_THAT_ERROR(
42793ce23adSGabor Marton checkDataflow<SignPropagationAnalysis>(
42893ce23adSGabor Marton AnalysisInputs<SignPropagationAnalysis>(
42993ce23adSGabor Marton Code, hasName(TargetFun),
43093ce23adSGabor Marton [](ASTContext &C, Environment &) {
43193ce23adSGabor Marton return SignPropagationAnalysis(C);
43293ce23adSGabor Marton })
43393ce23adSGabor Marton .withASTBuildArgs(
43493ce23adSGabor Marton {"-fsyntax-only", "-fno-delayed-template-parsing",
43593ce23adSGabor Marton "-std=" +
43693ce23adSGabor Marton std::string(LangStandard::getLangStandardForKind(Std)
43793ce23adSGabor Marton .getName())}),
43893ce23adSGabor Marton /*VerifyResults=*/
43993ce23adSGabor Marton [&Match](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
44093ce23adSGabor Marton &Results,
44193ce23adSGabor Marton const AnalysisOutputs &AO) { Match(Results, AO.ASTCtx); }),
44293ce23adSGabor Marton llvm::Succeeded());
44393ce23adSGabor Marton }
44493ce23adSGabor Marton
44593ce23adSGabor Marton // FIXME add this to testing support.
44693ce23adSGabor Marton template <typename NodeType, typename MatcherType>
findFirst(ASTContext & ASTCtx,const MatcherType & M)44793ce23adSGabor Marton const NodeType *findFirst(ASTContext &ASTCtx, const MatcherType &M) {
44893ce23adSGabor Marton auto TargetNodes = match(M.bind("v"), ASTCtx);
44993ce23adSGabor Marton assert(TargetNodes.size() == 1 && "Match must be unique");
45093ce23adSGabor Marton auto *const Result = selectFirst<NodeType>("v", TargetNodes);
45193ce23adSGabor Marton assert(Result != nullptr);
45293ce23adSGabor Marton return Result;
45393ce23adSGabor Marton }
45493ce23adSGabor Marton
45593ce23adSGabor Marton template <typename Node>
45693ce23adSGabor Marton std::pair<testing::AssertionResult, Value *>
getProperty(const Environment & Env,ASTContext & ASTCtx,const Node * N,StringRef Property)45793ce23adSGabor Marton getProperty(const Environment &Env, ASTContext &ASTCtx, const Node *N,
45893ce23adSGabor Marton StringRef Property) {
45993ce23adSGabor Marton if (!N)
46093ce23adSGabor Marton return {testing::AssertionFailure() << "No node", nullptr};
4619940fac7SMartin Braenne const StorageLocation *Loc = Env.getStorageLocation(*N);
46293ce23adSGabor Marton if (!isa_and_nonnull<ScalarStorageLocation>(Loc))
46393ce23adSGabor Marton return {testing::AssertionFailure() << "No location", nullptr};
46493ce23adSGabor Marton const Value *Val = Env.getValue(*Loc);
46593ce23adSGabor Marton if (!Val)
46693ce23adSGabor Marton return {testing::AssertionFailure() << "No value", nullptr};
46793ce23adSGabor Marton auto *Prop = Val->getProperty(Property);
46893ce23adSGabor Marton if (!isa_and_nonnull<BoolValue>(Prop))
46993ce23adSGabor Marton return {testing::AssertionFailure() << "No property for " << Property,
47093ce23adSGabor Marton nullptr};
47193ce23adSGabor Marton return {testing::AssertionSuccess(), Prop};
47293ce23adSGabor Marton }
47393ce23adSGabor Marton
47493ce23adSGabor Marton // Test if the given property of the given node is implied by the flow
47593ce23adSGabor Marton // condition. If 'Implies' is false then check if it is not implied.
47693ce23adSGabor Marton template <typename Node>
isPropertyImplied(const Environment & Env,ASTContext & ASTCtx,const Node * N,StringRef Property,bool Implies)47793ce23adSGabor Marton testing::AssertionResult isPropertyImplied(const Environment &Env,
47893ce23adSGabor Marton ASTContext &ASTCtx, const Node *N,
47993ce23adSGabor Marton StringRef Property, bool Implies) {
48093ce23adSGabor Marton auto [Result, Prop] = getProperty(Env, ASTCtx, N, Property);
48193ce23adSGabor Marton if (!Prop)
48293ce23adSGabor Marton return Result;
48393ce23adSGabor Marton auto *BVProp = cast<BoolValue>(Prop);
484526c9b7eSmartinboehme if (Env.proves(BVProp->formula()) != Implies)
48593ce23adSGabor Marton return testing::AssertionFailure()
48693ce23adSGabor Marton << Property << " is " << (Implies ? "not" : "") << " implied"
48793ce23adSGabor Marton << ", but should " << (Implies ? "" : "not ") << "be";
48893ce23adSGabor Marton return testing::AssertionSuccess();
48993ce23adSGabor Marton }
49093ce23adSGabor Marton
49193ce23adSGabor Marton template <typename Node>
isNegative(const Node * N,ASTContext & ASTCtx,const Environment & Env)49293ce23adSGabor Marton testing::AssertionResult isNegative(const Node *N, ASTContext &ASTCtx,
49393ce23adSGabor Marton const Environment &Env) {
49493ce23adSGabor Marton testing::AssertionResult R = isPropertyImplied(Env, ASTCtx, N, "neg", true);
49593ce23adSGabor Marton if (!R)
49693ce23adSGabor Marton return R;
49793ce23adSGabor Marton R = isPropertyImplied(Env, ASTCtx, N, "zero", false);
49893ce23adSGabor Marton if (!R)
49993ce23adSGabor Marton return R;
50093ce23adSGabor Marton return isPropertyImplied(Env, ASTCtx, N, "pos", false);
50193ce23adSGabor Marton }
50293ce23adSGabor Marton template <typename Node>
isPositive(const Node * N,ASTContext & ASTCtx,const Environment & Env)50393ce23adSGabor Marton testing::AssertionResult isPositive(const Node *N, ASTContext &ASTCtx,
50493ce23adSGabor Marton const Environment &Env) {
50593ce23adSGabor Marton testing::AssertionResult R = isPropertyImplied(Env, ASTCtx, N, "pos", true);
50693ce23adSGabor Marton if (!R)
50793ce23adSGabor Marton return R;
50893ce23adSGabor Marton R = isPropertyImplied(Env, ASTCtx, N, "zero", false);
50993ce23adSGabor Marton if (!R)
51093ce23adSGabor Marton return R;
51193ce23adSGabor Marton return isPropertyImplied(Env, ASTCtx, N, "neg", false);
51293ce23adSGabor Marton }
51393ce23adSGabor Marton template <typename Node>
isZero(const Node * N,ASTContext & ASTCtx,const Environment & Env)51493ce23adSGabor Marton testing::AssertionResult isZero(const Node *N, ASTContext &ASTCtx,
51593ce23adSGabor Marton const Environment &Env) {
51693ce23adSGabor Marton testing::AssertionResult R = isPropertyImplied(Env, ASTCtx, N, "zero", true);
51793ce23adSGabor Marton if (!R)
51893ce23adSGabor Marton return R;
51993ce23adSGabor Marton R = isPropertyImplied(Env, ASTCtx, N, "pos", false);
52093ce23adSGabor Marton if (!R)
52193ce23adSGabor Marton return R;
52293ce23adSGabor Marton return isPropertyImplied(Env, ASTCtx, N, "neg", false);
52393ce23adSGabor Marton }
52493ce23adSGabor Marton template <typename Node>
isTop(const Node * N,ASTContext & ASTCtx,const Environment & Env)52593ce23adSGabor Marton testing::AssertionResult isTop(const Node *N, ASTContext &ASTCtx,
52693ce23adSGabor Marton const Environment &Env) {
52793ce23adSGabor Marton testing::AssertionResult R = isPropertyImplied(Env, ASTCtx, N, "zero", false);
52893ce23adSGabor Marton if (!R)
52993ce23adSGabor Marton return R;
53093ce23adSGabor Marton R = isPropertyImplied(Env, ASTCtx, N, "pos", false);
53193ce23adSGabor Marton if (!R)
53293ce23adSGabor Marton return R;
53393ce23adSGabor Marton return isPropertyImplied(Env, ASTCtx, N, "neg", false);
53493ce23adSGabor Marton }
53593ce23adSGabor Marton
TEST(SignAnalysisTest,Init)53693ce23adSGabor Marton TEST(SignAnalysisTest, Init) {
53793ce23adSGabor Marton std::string Code = R"(
53893ce23adSGabor Marton int foo();
53993ce23adSGabor Marton void fun() {
54093ce23adSGabor Marton int a = -1;
54193ce23adSGabor Marton int b = 0;
54293ce23adSGabor Marton int c = 1;
54393ce23adSGabor Marton int d;
54493ce23adSGabor Marton int e = foo();
54593ce23adSGabor Marton int f = c;
54693ce23adSGabor Marton // [[p]]
54793ce23adSGabor Marton }
54893ce23adSGabor Marton )";
54993ce23adSGabor Marton runDataflow(
55093ce23adSGabor Marton Code,
55193ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
55293ce23adSGabor Marton ASTContext &ASTCtx) {
55393ce23adSGabor Marton // ASTCtx.getTranslationUnitDecl()->dump();
55493ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
55593ce23adSGabor Marton const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
55693ce23adSGabor Marton
55793ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
55893ce23adSGabor Marton const ValueDecl *B = findValueDecl(ASTCtx, "b");
55993ce23adSGabor Marton const ValueDecl *C = findValueDecl(ASTCtx, "c");
56093ce23adSGabor Marton const ValueDecl *D = findValueDecl(ASTCtx, "d");
56193ce23adSGabor Marton const ValueDecl *E = findValueDecl(ASTCtx, "e");
56293ce23adSGabor Marton const ValueDecl *F = findValueDecl(ASTCtx, "f");
56393ce23adSGabor Marton
56493ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, Env));
56593ce23adSGabor Marton EXPECT_TRUE(isZero(B, ASTCtx, Env));
56693ce23adSGabor Marton EXPECT_TRUE(isPositive(C, ASTCtx, Env));
56793ce23adSGabor Marton EXPECT_TRUE(isTop(D, ASTCtx, Env));
56893ce23adSGabor Marton EXPECT_TRUE(isTop(E, ASTCtx, Env));
56993ce23adSGabor Marton EXPECT_TRUE(isPositive(F, ASTCtx, Env));
57093ce23adSGabor Marton },
57193ce23adSGabor Marton LangStandard::lang_cxx17);
57293ce23adSGabor Marton }
57393ce23adSGabor Marton
TEST(SignAnalysisTest,UnaryMinus)57493ce23adSGabor Marton TEST(SignAnalysisTest, UnaryMinus) {
57593ce23adSGabor Marton std::string Code = R"(
57693ce23adSGabor Marton void fun() {
57793ce23adSGabor Marton int a = 1;
57893ce23adSGabor Marton int b = -a;
57993ce23adSGabor Marton // [[p]]
58093ce23adSGabor Marton }
58193ce23adSGabor Marton )";
58293ce23adSGabor Marton runDataflow(
58393ce23adSGabor Marton Code,
58493ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
58593ce23adSGabor Marton ASTContext &ASTCtx) {
58693ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
58793ce23adSGabor Marton const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
58893ce23adSGabor Marton
58993ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
59093ce23adSGabor Marton const ValueDecl *B = findValueDecl(ASTCtx, "b");
59193ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, Env));
59293ce23adSGabor Marton EXPECT_TRUE(isNegative(B, ASTCtx, Env));
59393ce23adSGabor Marton },
59493ce23adSGabor Marton LangStandard::lang_cxx17);
59593ce23adSGabor Marton }
59693ce23adSGabor Marton
TEST(SignAnalysisTest,UnaryNot)59793ce23adSGabor Marton TEST(SignAnalysisTest, UnaryNot) {
59893ce23adSGabor Marton std::string Code = R"(
59993ce23adSGabor Marton void fun() {
60093ce23adSGabor Marton int a = 2;
60193ce23adSGabor Marton int b = !a;
60293ce23adSGabor Marton // [[p]]
60393ce23adSGabor Marton }
60493ce23adSGabor Marton )";
60593ce23adSGabor Marton runDataflow(
60693ce23adSGabor Marton Code,
60793ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
60893ce23adSGabor Marton ASTContext &ASTCtx) {
60993ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
61093ce23adSGabor Marton const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
61193ce23adSGabor Marton
61293ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
61393ce23adSGabor Marton const ValueDecl *B = findValueDecl(ASTCtx, "b");
61493ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, Env));
61593ce23adSGabor Marton EXPECT_TRUE(isZero(B, ASTCtx, Env));
61693ce23adSGabor Marton },
61793ce23adSGabor Marton LangStandard::lang_cxx17);
61893ce23adSGabor Marton }
61993ce23adSGabor Marton
TEST(SignAnalysisTest,UnaryNotInIf)62093ce23adSGabor Marton TEST(SignAnalysisTest, UnaryNotInIf) {
62193ce23adSGabor Marton std::string Code = R"(
62293ce23adSGabor Marton int foo();
62393ce23adSGabor Marton void fun() {
62493ce23adSGabor Marton int a = foo();
62593ce23adSGabor Marton if (!a) {
62693ce23adSGabor Marton int b1;
62793ce23adSGabor Marton int p_a = a;
62893ce23adSGabor Marton int p_not_a = !a;
62993ce23adSGabor Marton // [[p]]
63093ce23adSGabor Marton } else {
63193ce23adSGabor Marton int q_a = a;
63293ce23adSGabor Marton int q_not_a = !a;
63393ce23adSGabor Marton // [[q]]
63493ce23adSGabor Marton }
63593ce23adSGabor Marton }
63693ce23adSGabor Marton )";
63793ce23adSGabor Marton runDataflow(
63893ce23adSGabor Marton Code,
63993ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
64093ce23adSGabor Marton ASTContext &ASTCtx) {
64193ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q"));
64293ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
64393ce23adSGabor Marton const Environment &EnvQ = getEnvironmentAtAnnotation(Results, "q");
64493ce23adSGabor Marton
64593ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
64693ce23adSGabor Marton const ValueDecl *PA = findValueDecl(ASTCtx, "p_a");
64793ce23adSGabor Marton const ValueDecl *PNA = findValueDecl(ASTCtx, "p_not_a");
64893ce23adSGabor Marton const ValueDecl *QA = findValueDecl(ASTCtx, "q_a");
64993ce23adSGabor Marton const ValueDecl *QNA = findValueDecl(ASTCtx, "q_not_a");
65093ce23adSGabor Marton
65193ce23adSGabor Marton // p
65293ce23adSGabor Marton EXPECT_TRUE(isZero(A, ASTCtx, EnvP));
65393ce23adSGabor Marton EXPECT_TRUE(isZero(PA, ASTCtx, EnvP));
65493ce23adSGabor Marton EXPECT_TRUE(isTop(PNA, ASTCtx, EnvP));
65593ce23adSGabor Marton
65693ce23adSGabor Marton // q
65793ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvQ));
65893ce23adSGabor Marton EXPECT_TRUE(isTop(QA, ASTCtx, EnvQ));
65993ce23adSGabor Marton EXPECT_TRUE(isZero(QNA, ASTCtx, EnvQ));
66093ce23adSGabor Marton },
66193ce23adSGabor Marton LangStandard::lang_cxx17);
66293ce23adSGabor Marton }
66393ce23adSGabor Marton
TEST(SignAnalysisTest,BinaryGT)66493ce23adSGabor Marton TEST(SignAnalysisTest, BinaryGT) {
66593ce23adSGabor Marton std::string Code = R"(
66693ce23adSGabor Marton int foo();
66793ce23adSGabor Marton void fun() {
66893ce23adSGabor Marton int a = foo();
66993ce23adSGabor Marton int b = 1;
67093ce23adSGabor Marton if (a > 1) {
67193ce23adSGabor Marton (void)0;
67293ce23adSGabor Marton // [[p]]
67393ce23adSGabor Marton }
67493ce23adSGabor Marton if (a > 0) {
67593ce23adSGabor Marton (void)0;
67693ce23adSGabor Marton // [[q]]
67793ce23adSGabor Marton }
67893ce23adSGabor Marton if (a > -1) {
67993ce23adSGabor Marton (void)0;
68093ce23adSGabor Marton // [[r]]
68193ce23adSGabor Marton }
68293ce23adSGabor Marton if (a > b) {
68393ce23adSGabor Marton (void)0;
68493ce23adSGabor Marton // [[s]]
68593ce23adSGabor Marton }
68693ce23adSGabor Marton }
68793ce23adSGabor Marton )";
68893ce23adSGabor Marton runDataflow(
68993ce23adSGabor Marton Code,
69093ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
69193ce23adSGabor Marton ASTContext &ASTCtx) {
69293ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q", "r", "s"));
69393ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
69493ce23adSGabor Marton const Environment &EnvQ = getEnvironmentAtAnnotation(Results, "q");
69593ce23adSGabor Marton const Environment &EnvR = getEnvironmentAtAnnotation(Results, "r");
69693ce23adSGabor Marton const Environment &EnvS = getEnvironmentAtAnnotation(Results, "s");
69793ce23adSGabor Marton
69893ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
69993ce23adSGabor Marton
70093ce23adSGabor Marton // p
70193ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, EnvP));
70293ce23adSGabor Marton // q
70393ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, EnvQ));
70493ce23adSGabor Marton // r
70593ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvR));
70693ce23adSGabor Marton // s
70793ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, EnvS));
70893ce23adSGabor Marton },
70993ce23adSGabor Marton LangStandard::lang_cxx17);
71093ce23adSGabor Marton }
71193ce23adSGabor Marton
TEST(SignAnalysisTest,BinaryLT)71293ce23adSGabor Marton TEST(SignAnalysisTest, BinaryLT) {
71393ce23adSGabor Marton std::string Code = R"(
71493ce23adSGabor Marton int foo();
71593ce23adSGabor Marton void fun() {
71693ce23adSGabor Marton int a = foo();
71793ce23adSGabor Marton int b = -1;
71893ce23adSGabor Marton if (a < -1) {
71993ce23adSGabor Marton (void)0;
72093ce23adSGabor Marton // [[p]]
72193ce23adSGabor Marton }
72293ce23adSGabor Marton if (a < 0) {
72393ce23adSGabor Marton (void)0;
72493ce23adSGabor Marton // [[q]]
72593ce23adSGabor Marton }
72693ce23adSGabor Marton if (a < 1) {
72793ce23adSGabor Marton (void)0;
72893ce23adSGabor Marton // [[r]]
72993ce23adSGabor Marton }
73093ce23adSGabor Marton if (a < b) {
73193ce23adSGabor Marton (void)0;
73293ce23adSGabor Marton // [[s]]
73393ce23adSGabor Marton }
73493ce23adSGabor Marton }
73593ce23adSGabor Marton )";
73693ce23adSGabor Marton runDataflow(
73793ce23adSGabor Marton Code,
73893ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
73993ce23adSGabor Marton ASTContext &ASTCtx) {
74093ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q", "r", "s"));
74193ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
74293ce23adSGabor Marton const Environment &EnvQ = getEnvironmentAtAnnotation(Results, "q");
74393ce23adSGabor Marton const Environment &EnvR = getEnvironmentAtAnnotation(Results, "r");
74493ce23adSGabor Marton const Environment &EnvS = getEnvironmentAtAnnotation(Results, "s");
74593ce23adSGabor Marton
74693ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
74793ce23adSGabor Marton
74893ce23adSGabor Marton // p
74993ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvP));
75093ce23adSGabor Marton // q
75193ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvQ));
75293ce23adSGabor Marton // r
75393ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvR));
75493ce23adSGabor Marton // s
75593ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvS));
75693ce23adSGabor Marton },
75793ce23adSGabor Marton LangStandard::lang_cxx17);
75893ce23adSGabor Marton }
75993ce23adSGabor Marton
TEST(SignAnalysisTest,BinaryGE)76093ce23adSGabor Marton TEST(SignAnalysisTest, BinaryGE) {
76193ce23adSGabor Marton std::string Code = R"(
76293ce23adSGabor Marton int foo();
76393ce23adSGabor Marton void fun() {
76493ce23adSGabor Marton int a = foo();
76593ce23adSGabor Marton int b = 1;
76693ce23adSGabor Marton if (a >= 1) {
76793ce23adSGabor Marton (void)0;
76893ce23adSGabor Marton // [[p]]
76993ce23adSGabor Marton }
77093ce23adSGabor Marton if (a >= 0) {
77193ce23adSGabor Marton (void)0;
77293ce23adSGabor Marton // [[q]]
77393ce23adSGabor Marton }
77493ce23adSGabor Marton if (a >= -1) {
77593ce23adSGabor Marton (void)0;
77693ce23adSGabor Marton // [[r]]
77793ce23adSGabor Marton }
77893ce23adSGabor Marton if (a >= b) {
77993ce23adSGabor Marton (void)0;
78093ce23adSGabor Marton // [[s]]
78193ce23adSGabor Marton }
78293ce23adSGabor Marton }
78393ce23adSGabor Marton )";
78493ce23adSGabor Marton runDataflow(
78593ce23adSGabor Marton Code,
78693ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
78793ce23adSGabor Marton ASTContext &ASTCtx) {
78893ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q", "r", "s"));
78993ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
79093ce23adSGabor Marton const Environment &EnvQ = getEnvironmentAtAnnotation(Results, "q");
79193ce23adSGabor Marton const Environment &EnvR = getEnvironmentAtAnnotation(Results, "r");
79293ce23adSGabor Marton const Environment &EnvS = getEnvironmentAtAnnotation(Results, "s");
79393ce23adSGabor Marton
79493ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
79593ce23adSGabor Marton
79693ce23adSGabor Marton // p
79793ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, EnvP));
79893ce23adSGabor Marton // q
79993ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvQ));
80093ce23adSGabor Marton // r
80193ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvR));
80293ce23adSGabor Marton // s
80393ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, EnvS));
80493ce23adSGabor Marton },
80593ce23adSGabor Marton LangStandard::lang_cxx17);
80693ce23adSGabor Marton }
80793ce23adSGabor Marton
TEST(SignAnalysisTest,BinaryLE)80893ce23adSGabor Marton TEST(SignAnalysisTest, BinaryLE) {
80993ce23adSGabor Marton std::string Code = R"(
81093ce23adSGabor Marton int foo();
81193ce23adSGabor Marton void fun() {
81293ce23adSGabor Marton int a = foo();
81393ce23adSGabor Marton int b = -1;
81493ce23adSGabor Marton if (a <= -1) {
81593ce23adSGabor Marton (void)0;
81693ce23adSGabor Marton // [[p]]
81793ce23adSGabor Marton }
81893ce23adSGabor Marton if (a <= 0) {
81993ce23adSGabor Marton (void)0;
82093ce23adSGabor Marton // [[q]]
82193ce23adSGabor Marton }
82293ce23adSGabor Marton if (a <= 1) {
82393ce23adSGabor Marton (void)0;
82493ce23adSGabor Marton // [[r]]
82593ce23adSGabor Marton }
82693ce23adSGabor Marton if (a <= b) {
82793ce23adSGabor Marton (void)0;
82893ce23adSGabor Marton // [[s]]
82993ce23adSGabor Marton }
83093ce23adSGabor Marton }
83193ce23adSGabor Marton )";
83293ce23adSGabor Marton runDataflow(
83393ce23adSGabor Marton Code,
83493ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
83593ce23adSGabor Marton ASTContext &ASTCtx) {
83693ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q", "r", "s"));
83793ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
83893ce23adSGabor Marton const Environment &EnvQ = getEnvironmentAtAnnotation(Results, "q");
83993ce23adSGabor Marton const Environment &EnvR = getEnvironmentAtAnnotation(Results, "r");
84093ce23adSGabor Marton const Environment &EnvS = getEnvironmentAtAnnotation(Results, "s");
84193ce23adSGabor Marton
84293ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
84393ce23adSGabor Marton
84493ce23adSGabor Marton // p
84593ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvP));
84693ce23adSGabor Marton // q
84793ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvQ));
84893ce23adSGabor Marton // r
84993ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvR));
85093ce23adSGabor Marton // s
85193ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvS));
85293ce23adSGabor Marton },
85393ce23adSGabor Marton LangStandard::lang_cxx17);
85493ce23adSGabor Marton }
85593ce23adSGabor Marton
TEST(SignAnalysisTest,BinaryEQ)85693ce23adSGabor Marton TEST(SignAnalysisTest, BinaryEQ) {
85793ce23adSGabor Marton std::string Code = R"(
85893ce23adSGabor Marton int foo();
85993ce23adSGabor Marton void fun() {
86093ce23adSGabor Marton int a = foo();
86193ce23adSGabor Marton if (a == -1) {
86293ce23adSGabor Marton (void)0;
86393ce23adSGabor Marton // [[n]]
86493ce23adSGabor Marton }
86593ce23adSGabor Marton if (a == 0) {
86693ce23adSGabor Marton (void)0;
86793ce23adSGabor Marton // [[z]]
86893ce23adSGabor Marton }
86993ce23adSGabor Marton if (a == 1) {
87093ce23adSGabor Marton (void)0;
87193ce23adSGabor Marton // [[p]]
87293ce23adSGabor Marton }
87393ce23adSGabor Marton }
87493ce23adSGabor Marton )";
87593ce23adSGabor Marton runDataflow(
87693ce23adSGabor Marton Code,
87793ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
87893ce23adSGabor Marton ASTContext &ASTCtx) {
87993ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("n", "z", "p"));
88093ce23adSGabor Marton const Environment &EnvN = getEnvironmentAtAnnotation(Results, "n");
88193ce23adSGabor Marton const Environment &EnvZ = getEnvironmentAtAnnotation(Results, "z");
88293ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
88393ce23adSGabor Marton
88493ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
88593ce23adSGabor Marton
88693ce23adSGabor Marton // n
88793ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvN));
88893ce23adSGabor Marton // z
88993ce23adSGabor Marton EXPECT_TRUE(isZero(A, ASTCtx, EnvZ));
89093ce23adSGabor Marton // p
89193ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, EnvP));
89293ce23adSGabor Marton },
89393ce23adSGabor Marton LangStandard::lang_cxx17);
89493ce23adSGabor Marton }
89593ce23adSGabor Marton
TEST(SignAnalysisTest,ComplexLoopCondition)896ccf1e322Smartinboehme TEST(SignAnalysisTest, ComplexLoopCondition) {
897ccf1e322Smartinboehme std::string Code = R"(
898ccf1e322Smartinboehme int foo();
899ccf1e322Smartinboehme void fun() {
900ccf1e322Smartinboehme int a, b;
901ccf1e322Smartinboehme while ((a = foo()) > 0 && (b = foo()) > 0) {
902ccf1e322Smartinboehme a;
903ccf1e322Smartinboehme b;
904ccf1e322Smartinboehme // [[p]]
905ccf1e322Smartinboehme }
906ccf1e322Smartinboehme }
907ccf1e322Smartinboehme )";
908ccf1e322Smartinboehme runDataflow(
909ccf1e322Smartinboehme Code,
910ccf1e322Smartinboehme [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
911ccf1e322Smartinboehme ASTContext &ASTCtx) {
912ccf1e322Smartinboehme const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
913ccf1e322Smartinboehme
914ccf1e322Smartinboehme const ValueDecl *A = findValueDecl(ASTCtx, "a");
915ccf1e322Smartinboehme const ValueDecl *B = findValueDecl(ASTCtx, "b");
916ccf1e322Smartinboehme
917ccf1e322Smartinboehme EXPECT_TRUE(isPositive(A, ASTCtx, Env));
918ccf1e322Smartinboehme EXPECT_TRUE(isPositive(B, ASTCtx, Env));
919ccf1e322Smartinboehme },
920ccf1e322Smartinboehme LangStandard::lang_cxx17);
921ccf1e322Smartinboehme }
922ccf1e322Smartinboehme
TEST(SignAnalysisTest,JoinToTop)92393ce23adSGabor Marton TEST(SignAnalysisTest, JoinToTop) {
92493ce23adSGabor Marton std::string Code = R"(
92593ce23adSGabor Marton int foo();
92693ce23adSGabor Marton void fun(bool b) {
92793ce23adSGabor Marton int a = foo();
92893ce23adSGabor Marton if (b) {
92993ce23adSGabor Marton a = -1;
93093ce23adSGabor Marton (void)0;
93193ce23adSGabor Marton // [[p]]
93293ce23adSGabor Marton } else {
93393ce23adSGabor Marton a = 1;
93493ce23adSGabor Marton (void)0;
93593ce23adSGabor Marton // [[q]]
93693ce23adSGabor Marton }
93793ce23adSGabor Marton (void)0;
93893ce23adSGabor Marton // [[r]]
93993ce23adSGabor Marton }
94093ce23adSGabor Marton )";
94193ce23adSGabor Marton runDataflow(
94293ce23adSGabor Marton Code,
94393ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
94493ce23adSGabor Marton ASTContext &ASTCtx) {
94593ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q", "r"));
94693ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
94793ce23adSGabor Marton const Environment &EnvQ = getEnvironmentAtAnnotation(Results, "q");
94893ce23adSGabor Marton const Environment &EnvR = getEnvironmentAtAnnotation(Results, "r");
94993ce23adSGabor Marton
95093ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
95193ce23adSGabor Marton
95293ce23adSGabor Marton // p
95393ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvP));
95493ce23adSGabor Marton // q
95593ce23adSGabor Marton EXPECT_TRUE(isPositive(A, ASTCtx, EnvQ));
95693ce23adSGabor Marton // r
95793ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvR));
95893ce23adSGabor Marton },
95993ce23adSGabor Marton LangStandard::lang_cxx17);
96093ce23adSGabor Marton }
96193ce23adSGabor Marton
TEST(SignAnalysisTest,JoinToNeg)96293ce23adSGabor Marton TEST(SignAnalysisTest, JoinToNeg) {
96393ce23adSGabor Marton std::string Code = R"(
96493ce23adSGabor Marton int foo();
96593ce23adSGabor Marton void fun() {
96693ce23adSGabor Marton int a = foo();
96793ce23adSGabor Marton if (a < 1) {
96893ce23adSGabor Marton a = -1;
96993ce23adSGabor Marton (void)0;
97093ce23adSGabor Marton // [[p]]
97193ce23adSGabor Marton } else {
97293ce23adSGabor Marton a = -1;
97393ce23adSGabor Marton (void)0;
97493ce23adSGabor Marton // [[q]]
97593ce23adSGabor Marton }
97693ce23adSGabor Marton (void)0;
97793ce23adSGabor Marton // [[r]]
97893ce23adSGabor Marton }
97993ce23adSGabor Marton )";
98093ce23adSGabor Marton runDataflow(
98193ce23adSGabor Marton Code,
98293ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
98393ce23adSGabor Marton ASTContext &ASTCtx) {
98493ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q", "r"));
98593ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
98693ce23adSGabor Marton const Environment &EnvQ = getEnvironmentAtAnnotation(Results, "q");
98793ce23adSGabor Marton const Environment &EnvR = getEnvironmentAtAnnotation(Results, "r");
98893ce23adSGabor Marton
98993ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
99093ce23adSGabor Marton
99193ce23adSGabor Marton // p
99293ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvP));
99393ce23adSGabor Marton // q
99493ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvQ));
99593ce23adSGabor Marton // r
99693ce23adSGabor Marton EXPECT_TRUE(isNegative(A, ASTCtx, EnvR));
99793ce23adSGabor Marton },
99893ce23adSGabor Marton LangStandard::lang_cxx17);
99993ce23adSGabor Marton }
100093ce23adSGabor Marton
TEST(SignAnalysisTest,NestedIfs)100193ce23adSGabor Marton TEST(SignAnalysisTest, NestedIfs) {
100293ce23adSGabor Marton std::string Code = R"(
100393ce23adSGabor Marton int foo();
100493ce23adSGabor Marton void fun() {
100593ce23adSGabor Marton int a = foo();
100693ce23adSGabor Marton if (a >= 0) {
100793ce23adSGabor Marton (void)0;
100893ce23adSGabor Marton // [[p]]
100993ce23adSGabor Marton if (a == 0) {
101093ce23adSGabor Marton (void)0;
101193ce23adSGabor Marton // [[q]]
101293ce23adSGabor Marton }
101393ce23adSGabor Marton }
101493ce23adSGabor Marton (void)0;
101593ce23adSGabor Marton // [[r]]
101693ce23adSGabor Marton }
101793ce23adSGabor Marton )";
101893ce23adSGabor Marton runDataflow(
101993ce23adSGabor Marton Code,
102093ce23adSGabor Marton [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
102193ce23adSGabor Marton ASTContext &ASTCtx) {
102293ce23adSGabor Marton ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q", "r"));
102393ce23adSGabor Marton const Environment &EnvP = getEnvironmentAtAnnotation(Results, "p");
102493ce23adSGabor Marton const Environment &EnvQ = getEnvironmentAtAnnotation(Results, "q");
102593ce23adSGabor Marton const Environment &EnvR = getEnvironmentAtAnnotation(Results, "r");
102693ce23adSGabor Marton
102793ce23adSGabor Marton const ValueDecl *A = findValueDecl(ASTCtx, "a");
102893ce23adSGabor Marton
102993ce23adSGabor Marton // p
103093ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvP));
103193ce23adSGabor Marton // q
103293ce23adSGabor Marton EXPECT_TRUE(isZero(A, ASTCtx, EnvQ));
103393ce23adSGabor Marton // r
103493ce23adSGabor Marton EXPECT_TRUE(isTop(A, ASTCtx, EnvR));
103593ce23adSGabor Marton },
103693ce23adSGabor Marton LangStandard::lang_cxx17);
103793ce23adSGabor Marton }
103893ce23adSGabor Marton
103993ce23adSGabor Marton } // namespace
1040