Pointer,
Struct,
- // Synthetic boolean values are either atomic values or composites that
- // represent conjunctions, disjunctions, and negations.
+ // Synthetic boolean values are either atomic values or logical connectives.
AtomicBool,
Conjunction,
Disjunction,
- Negation
+ Negation,
+ Implication,
+ Biconditional,
};
explicit Value(Kind ValKind) : ValKind(ValKind) {}
return Val->getKind() == Kind::AtomicBool ||
Val->getKind() == Kind::Conjunction ||
Val->getKind() == Kind::Disjunction ||
- Val->getKind() == Kind::Negation;
+ Val->getKind() == Kind::Negation ||
+ Val->getKind() == Kind::Implication ||
+ Val->getKind() == Kind::Biconditional;
}
};
BoolValue &SubVal;
};
+/// Models a boolean implication.
+///
+/// Equivalent to `!LHS v RHS`.
+class ImplicationValue : public BoolValue {
+public:
+ explicit ImplicationValue(BoolValue &LeftSubVal, BoolValue &RightSubVal)
+ : BoolValue(Kind::Implication), LeftSubVal(LeftSubVal),
+ RightSubVal(RightSubVal) {}
+
+ static bool classof(const Value *Val) {
+ return Val->getKind() == Kind::Implication;
+ }
+
+ /// Returns the left sub-value of the implication.
+ BoolValue &getLeftSubValue() const { return LeftSubVal; }
+
+ /// Returns the right sub-value of the implication.
+ BoolValue &getRightSubValue() const { return RightSubVal; }
+
+private:
+ BoolValue &LeftSubVal;
+ BoolValue &RightSubVal;
+};
+
+/// Models a boolean biconditional.
+///
+/// Equivalent to `(LHS ^ RHS) v (!LHS ^ !RHS)`.
+class BiconditionalValue : public BoolValue {
+public:
+ explicit BiconditionalValue(BoolValue &LeftSubVal, BoolValue &RightSubVal)
+ : BoolValue(Kind::Biconditional), LeftSubVal(LeftSubVal),
+ RightSubVal(RightSubVal) {}
+
+ static bool classof(const Value *Val) {
+ return Val->getKind() == Kind::Biconditional;
+ }
+
+ /// Returns the left sub-value of the biconditional.
+ BoolValue &getLeftSubValue() const { return LeftSubVal; }
+
+ /// Returns the right sub-value of the biconditional.
+ BoolValue &getRightSubValue() const { return RightSubVal; }
+
+private:
+ BoolValue &LeftSubVal;
+ BoolValue &RightSubVal;
+};
+
/// Models an integer.
class IntegerValue : public Value {
public:
UnprocessedSubVals.push(&N->getSubVal());
break;
}
+ case Value::Kind::Implication: {
+ auto *I = cast<ImplicationValue>(Val);
+ UnprocessedSubVals.push(&I->getLeftSubValue());
+ UnprocessedSubVals.push(&I->getRightSubValue());
+ break;
+ }
+ case Value::Kind::Biconditional: {
+ auto *B = cast<BiconditionalValue>(Val);
+ UnprocessedSubVals.push(&B->getLeftSubValue());
+ UnprocessedSubVals.push(&B->getRightSubValue());
+ break;
+ }
case Value::Kind::AtomicBool: {
Atomics[Var] = cast<AtomicBoolValue>(Val);
break;
// Visit the sub-values of `Val`.
UnprocessedSubVals.push(&N->getSubVal());
+ } else if (auto *I = dyn_cast<ImplicationValue>(Val)) {
+ const Variable LeftSubVar = GetVar(&I->getLeftSubValue());
+ const Variable RightSubVar = GetVar(&I->getRightSubValue());
+
+ // `X <=> (A => B)` is equivalent to
+ // `(X v A) ^ (X v !B) ^ (!X v !A v B)` which is already in
+ // conjunctive normal form. Below we add each of the conjuncts of the
+ // latter expression to the result.
+ Formula.addClause(posLit(Var), posLit(LeftSubVar));
+ Formula.addClause(posLit(Var), negLit(RightSubVar));
+ Formula.addClause(negLit(Var), negLit(LeftSubVar), posLit(RightSubVar));
+
+ // Visit the sub-values of `Val`.
+ UnprocessedSubVals.push(&I->getLeftSubValue());
+ UnprocessedSubVals.push(&I->getRightSubValue());
+ } else if (auto *B = dyn_cast<BiconditionalValue>(Val)) {
+ const Variable LeftSubVar = GetVar(&B->getLeftSubValue());
+ const Variable RightSubVar = GetVar(&B->getRightSubValue());
+
+ if (LeftSubVar == RightSubVar) {
+ // `X <=> (A <=> A)` is equvalent to `X` which is already in
+ // conjunctive normal form. Below we add each of the conjuncts of the
+ // latter expression to the result.
+ Formula.addClause(posLit(Var));
+
+ // No need to visit the sub-values of `Val`.
+ } else {
+ // `X <=> (A <=> B)` is equivalent to
+ // `(X v A v B) ^ (X v !A v !B) ^ (!X v A v !B) ^ (!X v !A v B)` which is
+ // already in conjunctive normal form. Below we add each of the conjuncts
+ // of the latter expression to the result.
+ Formula.addClause(posLit(Var), posLit(LeftSubVar), posLit(RightSubVar));
+ Formula.addClause(posLit(Var), negLit(LeftSubVar), negLit(RightSubVar));
+ Formula.addClause(negLit(Var), posLit(LeftSubVar), negLit(RightSubVar));
+ Formula.addClause(negLit(Var), negLit(LeftSubVar), posLit(RightSubVar));
+
+ // Visit the sub-values of `Val`.
+ UnprocessedSubVals.push(&B->getLeftSubValue());
+ UnprocessedSubVals.push(&B->getRightSubValue());
+ }
}
}
}
#endif
-TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionContainingAtomic) {
auto &X = Context.createAtomicBoolValue();
auto &True = Context.getBoolLiteralValue(true);
auto &False = Context.getBoolLiteralValue(false);
auto &FC = Context.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC, X);
- // If X is true in FC, FC = X must be true
+ // If X is true, FC is true
auto &FCWithXTrue =
Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, True));
- // If X is false in FC, FC = X must be false
+ // If X is false, FC is false
auto &FC1WithXFalse =
Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
EXPECT_TRUE(Context.equivalentBoolValues(FC1WithXFalse, False));
}
-TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsNegatedFC) {
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionContainingNegation) {
auto &X = Context.createAtomicBoolValue();
auto &True = Context.getBoolLiteralValue(true);
auto &False = Context.getBoolLiteralValue(false);
auto &FC = Context.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC, Context.getOrCreateNegation(X));
- // If X is true in FC, FC = !X must be false
+ // If X is true, FC is false
auto &FCWithXTrue =
Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, False));
- // If X is false in FC, FC = !X must be true
+ // If X is false, FC is true
auto &FC1WithXFalse =
Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
EXPECT_TRUE(Context.equivalentBoolValues(FC1WithXFalse, True));
}
-TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsDisjunctiveFC) {
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionContainingDisjunction) {
auto &X = Context.createAtomicBoolValue();
auto &Y = Context.createAtomicBoolValue();
auto &True = Context.getBoolLiteralValue(true);
auto &FC = Context.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC, Context.getOrCreateDisjunction(X, Y));
- // If X is true in FC, FC = X || Y must be true
+ // If X is true, FC is true
auto &FCWithXTrue =
Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, True));
- // If X is false in FC, FC = X || Y is equivalent to evaluating Y
+ // If X is false, FC is equivalent to Y
auto &FC1WithXFalse =
Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
EXPECT_TRUE(Context.equivalentBoolValues(FC1WithXFalse, Y));
}
-TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsConjunctiveFC) {
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionContainingConjunction) {
auto &X = Context.createAtomicBoolValue();
auto &Y = Context.createAtomicBoolValue();
auto &True = Context.getBoolLiteralValue(true);
auto &FC = Context.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC, Context.getOrCreateConjunction(X, Y));
- // If X is true in FC, FC = X && Y is equivalent to evaluating Y
+ // If X is true, FC is equivalent to Y
auto &FCWithXTrue =
Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, Y));
- // If X is false in FC, FC = X && Y must be false
+ // If X is false, FC is false
auto &FCWithXFalse =
Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
EXPECT_TRUE(Context.equivalentBoolValues(FCWithXFalse, False));
}
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionContainingImplication) {
+ auto &X = Context.createAtomicBoolValue();
+ auto &Y = Context.createAtomicBoolValue();
+ auto &True = Context.getBoolLiteralValue(true);
+ auto &False = Context.getBoolLiteralValue(false);
+
+ // FC = (X => Y)
+ auto &FC = Context.makeFlowConditionToken();
+ Context.addFlowConditionConstraint(FC, Context.getOrCreateImplication(X, Y));
+
+ // If X is true, FC is equivalent to Y
+ auto &FCWithXTrue =
+ Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
+ EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, Y));
+
+ // If X is false, FC is true
+ auto &FC1WithXFalse =
+ Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
+ EXPECT_TRUE(Context.equivalentBoolValues(FC1WithXFalse, True));
+
+ // If Y is true, FC is true
+ auto &FCWithYTrue =
+ Context.buildAndSubstituteFlowCondition(FC, {{&Y, &True}});
+ EXPECT_TRUE(Context.equivalentBoolValues(FCWithYTrue, True));
+
+ // If Y is false, FC is equivalent to !X
+ auto &FCWithYFalse =
+ Context.buildAndSubstituteFlowCondition(FC, {{&Y, &False}});
+ EXPECT_TRUE(Context.equivalentBoolValues(FCWithYFalse,
+ Context.getOrCreateNegation(X)));
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionContainingBiconditional) {
+ auto &X = Context.createAtomicBoolValue();
+ auto &Y = Context.createAtomicBoolValue();
+ auto &True = Context.getBoolLiteralValue(true);
+ auto &False = Context.getBoolLiteralValue(false);
+
+ // FC = (X <=> Y)
+ auto &FC = Context.makeFlowConditionToken();
+ Context.addFlowConditionConstraint(FC, Context.getOrCreateIff(X, Y));
+
+ // If X is true, FC is equivalent to Y
+ auto &FCWithXTrue =
+ Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
+ EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, Y));
+
+ // If X is false, FC is equivalent to !Y
+ auto &FC1WithXFalse =
+ Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
+ EXPECT_TRUE(Context.equivalentBoolValues(FC1WithXFalse,
+ Context.getOrCreateNegation(Y)));
+
+ // If Y is true, FC is equivalent to X
+ auto &FCWithYTrue =
+ Context.buildAndSubstituteFlowCondition(FC, {{&Y, &True}});
+ EXPECT_TRUE(Context.equivalentBoolValues(FCWithYTrue, X));
+
+ // If Y is false, FC is equivalent to !X
+ auto &FCWithYFalse =
+ Context.buildAndSubstituteFlowCondition(FC, {{&Y, &False}});
+ EXPECT_TRUE(Context.equivalentBoolValues(FCWithYFalse,
+ Context.getOrCreateNegation(X)));
+}
+
TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsForkedFC) {
auto &X = Context.createAtomicBoolValue();
auto &Y = Context.createAtomicBoolValue();
ForkedFC, {{&Y, &True}, {&Z, &True}});
EXPECT_TRUE(Context.equivalentBoolValues(ForkedFCWithYAndZTrue, X));
- // If any of X,Y,Z is false in ForkedFC, ForkedFC = X && Y && Z must be false
+ // If any of X,Y,Z is false, ForkedFC is false
auto &ForkedFCWithXFalse =
Context.buildAndSubstituteFlowCondition(ForkedFC, {{&X, &False}});
auto &ForkedFCWithYFalse =
auto &JoinedFC = Context.joinFlowConditions(FC1, FC2);
Context.addFlowConditionConstraint(JoinedFC, Z);
- // If any of X, Y is true in JoinedFC, JoinedFC = (X || Y) && Z is equivalent
- // to evaluating the remaining Z
+ // If any of X, Y is true, JoinedFC is equivalent to Z
auto &JoinedFCWithXTrue =
Context.buildAndSubstituteFlowCondition(JoinedFC, {{&X, &True}});
auto &JoinedFCWithYTrue =
EXPECT_TRUE(Context.equivalentBoolValues(JoinedFCWithXTrue, Z));
EXPECT_TRUE(Context.equivalentBoolValues(JoinedFCWithYTrue, Z));
- // If Z is true in JoinedFC, JoinedFC = (X || Y) && Z is equivalent to
- // evaluating the remaining disjunction (X || Y)
+ // If Z is true, JoinedFC is equivalent to (X || Y)
auto &JoinedFCWithZTrue =
Context.buildAndSubstituteFlowCondition(JoinedFC, {{&Z, &True}});
EXPECT_TRUE(Context.equivalentBoolValues(
JoinedFCWithZTrue, Context.getOrCreateDisjunction(X, Y)));
- // If any of X, Y is false in JoinedFC, JoinedFC = (X || Y) && Z is equivalent
- // to evaluating the conjunction of the other value and Z
+ // If any of X, Y is false, JoinedFC is equivalent to the conjunction of the
+ // other value and Z
auto &JoinedFCWithXFalse =
Context.buildAndSubstituteFlowCondition(JoinedFC, {{&X, &False}});
auto &JoinedFCWithYFalse =
EXPECT_TRUE(Context.equivalentBoolValues(
JoinedFCWithYFalse, Context.getOrCreateConjunction(X, Z)));
- // If Z is false in JoinedFC, JoinedFC = (X || Y) && Z must be false
+ // If Z is false, JoinedFC is false
auto &JoinedFCWithZFalse =
Context.buildAndSubstituteFlowCondition(JoinedFC, {{&Z, &False}});
EXPECT_TRUE(Context.equivalentBoolValues(JoinedFCWithZFalse, False));