Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Engine heuristic: fix for assumptions unsupported by k-induction #923

Merged
merged 1 commit into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions regression/ebmc/engine-heuristic/basic1.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
basic1.sv

^\[main\.a0\] always not s_eventually !main\.x: ASSUMED$
^\[main\.p0\] always main\.x: PROVED up to bound 5$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
--
8 changes: 8 additions & 0 deletions regression/ebmc/engine-heuristic/basic1.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module main(input clk, input x);

// not supported by k-induction
a0: assume property (not s_eventually !x);

p0: assert property (x);

endmodule
11 changes: 11 additions & 0 deletions regression/ebmc/engine-heuristic/basic2.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CORE
basic2.sv

^\[main\.a0\] always not s_eventually !main\.y: UNSUPPORTED: unsupported by k-induction$
^\[main\.a1\] always !main\.x: ASSUMED$
^\[main\.p0\] always !main\.z: PROVED$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
--
15 changes: 15 additions & 0 deletions regression/ebmc/engine-heuristic/basic2.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module main(input clk, input x, input y);

reg z = 0;
always_ff @(posedge clk) z <= z || x;

// unsupported assumption
a0: assume property (not s_eventually !y);

// supported assumption
a1: assume property (!x);

// inductive property
p0: assert property (!z);

endmodule
10 changes: 10 additions & 0 deletions regression/ebmc/engine-heuristic/basic3.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
basic3.sv

^\[main\.a0\] always not s_eventually !main\.x: ASSUMED$
^\[main\.p0\] always 0: REFUTED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
9 changes: 9 additions & 0 deletions regression/ebmc/engine-heuristic/basic3.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module main(input clk, input x);

// unsupported
a0: assume property (not s_eventually !x);

// should fail
p0: assert property (0);

endmodule
2 changes: 1 addition & 1 deletion regression/ebmc/k-induction/k-induction-unsupported2.desc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ k-induction-unsupported2.sv
--module main --k-induction
^EXIT=10$
^SIGNAL=0$
^\[main\.p0\] always s_eventually main\.x == 10: FAILURE: property unsupported by k-induction$
^\[main\.p0\] always s_eventually main\.x == 10: UNSUPPORTED: unsupported by k-induction$
^\[main\.p1\] always main\.x <= 10: PROVED$
--
^warning: ignoring
7 changes: 4 additions & 3 deletions regression/ebmc/k-induction/k-induction6.desc
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
KNOWNBUG
CORE
k-induction6.sv

--k-induction
^\[main\.a0\] always not s_eventually !main\.x: UNSUPPORTED: unsupported by k-induction$
^\[main\.p0\] always main\.x: INCONCLUSIVE$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
The property should hold, but is reported as refuted.
11 changes: 11 additions & 0 deletions regression/ebmc/k-induction/k-induction7.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CORE
k-induction7.sv
--k-induction
^\[main\.a0\] always not s_eventually !main\.y: UNSUPPORTED: unsupported by k-induction$
^\[main\.a1\] always !main\.x: ASSUMED$
^\[main\.p0\] always !main\.z: PROVED$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
--
15 changes: 15 additions & 0 deletions regression/ebmc/k-induction/k-induction7.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module main(input clk, input x, input y);

reg z = 0;
always_ff @(posedge clk) z <= z || x;

// unsupported assumption
a0: assume property (not s_eventually !y);

// supported assumption
a1: assume property (!x);

// inductive property
p0: assert property (!z);

endmodule
2 changes: 1 addition & 1 deletion src/ebmc/bdd_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ property_checker_resultt bdd_enginet::operator()()
try
{
// any properties left?
if(!properties.has_unknown_property())
if(!properties.has_unfinished_property())
return property_checker_resultt{properties};

// possibly apply liveness-to-safety
Expand Down
17 changes: 10 additions & 7 deletions src/ebmc/ebmc_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Author: Daniel Kroening, [email protected]

std::string ebmc_propertiest::propertyt::status_as_string() const
{
auto suffix = failure_reason.has_value() ? ": " + failure_reason.value() : "";

switch(status)
{
case statust::ASSUMED:
Expand All @@ -31,14 +33,15 @@ std::string ebmc_propertiest::propertyt::status_as_string() const
case statust::REFUTED_WITH_BOUND:
return "REFUTED up to bound " + std::to_string(bound);
case statust::UNKNOWN:
return "UNKNOWN";
return "UNKNOWN" + suffix;
case statust::UNSUPPORTED:
return "UNSUPPORTED" + suffix;
case statust::INCONCLUSIVE:
return "INCONCLUSIVE";
return "INCONCLUSIVE" + suffix;
case statust::FAILURE:
return failure_reason.has_value() ? "FAILURE: " + failure_reason.value()
: "FAILURE";
return "FAILURE" + suffix;
case statust::DROPPED:
return "DROPPED";
return "DROPPED" + suffix;
case statust::DISABLED:
default:
UNREACHABLE;
Expand Down Expand Up @@ -78,9 +81,9 @@ ebmc_propertiest ebmc_propertiest::from_transition_system(
id2string(symbol.location.get_comment());

// Don't try to prove assumption properties.
if(symbol.value.id() == ID_sva_assume)
if(properties.properties.back().is_assumption())
{
properties.properties.back().status = propertyt::statust::ASSUMED;
properties.properties.back().assumed();
properties.properties.back().normalized_expr =
normalize_property(to_sva_assume_expr(symbol.value).op());
}
Expand Down
73 changes: 70 additions & 3 deletions src/ebmc/ebmc_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ebmc_propertiest
UNKNOWN, // no work done yet
DISABLED, // turned off by user
ASSUMED, // property is assumed to be true, unbounded
UNSUPPORTED, // property is unsupported
PROVED, // property is true, unbounded
PROVED_WITH_BOUND, // property is true, with bound
REFUTED, // property is false, possibly counterexample
Expand Down Expand Up @@ -98,24 +99,38 @@ class ebmc_propertiest
return status == statust::FAILURE;
}

bool is_unsupported() const
{
return status == statust::UNSUPPORTED;
}

bool is_inconclusive() const
{
return status == statust::INCONCLUSIVE;
}

void assumed()
{
status = statust::ASSUMED;
failure_reason = {};
}

void unknown()
{
status = statust::UNKNOWN;
failure_reason = {};
}

void disable()
{
status = statust::DISABLED;
failure_reason = {};
}

void proved()
{
status = statust::PROVED;
failure_reason = {};
}

void proved_with_bound(std::size_t _bound)
Expand All @@ -127,17 +142,20 @@ class ebmc_propertiest
void refuted()
{
status = statust::REFUTED;
failure_reason = {};
}

void refuted_with_bound(std::size_t _bound)
{
status = statust::REFUTED_WITH_BOUND;
bound = _bound;
failure_reason = {};
}

void drop()
{
status = statust::DROPPED;
failure_reason = {};
}

void failure(const std::optional<std::string> &reason = {})
Expand All @@ -146,9 +164,16 @@ class ebmc_propertiest
failure_reason = reason;
}

void unsupported(const std::optional<std::string> &reason = {})
{
status = statust::UNSUPPORTED;
failure_reason = reason;
}

void inconclusive()
{
status = statust::INCONCLUSIVE;
failure_reason = {};
}

std::string status_as_string() const;
Expand All @@ -164,16 +189,30 @@ class ebmc_propertiest
{
return ::is_exists_path(original_expr);
}

bool is_assumption() const
{
return original_expr.id() == ID_sva_assume;
}
};

typedef std::list<propertyt> propertiest;
propertiest properties;

bool has_unknown_property() const
bool has_unfinished_property() const
{
for(const auto &p : properties)
if(p.is_unknown())
{
if(p.is_assumption())
{
}
else if(
p.is_unknown() || p.is_unsupported() || p.is_failure() ||
p.is_inconclusive())
{
return true;
}
}

return false;
}
Expand Down Expand Up @@ -209,12 +248,40 @@ class ebmc_propertiest
return result;
}

void reset_failure_to_unknown()
/// Resets properties/assumptions in FAILURE state to
/// ASSUMED/UNKNOWN respectively.
void reset_failure()
{
for(auto &p : properties)
if(p.is_failure())
{
if(p.is_assumption())
p.assumed();
else
p.unknown();
}
}

/// Resets properties in INCONCLUSIVE state to UNKNOWN.
void reset_inconclusive()
{
for(auto &p : properties)
if(p.is_inconclusive())
p.unknown();
}

/// Resets properties in UNSUPPORTED state to UNKNOWN/ASSUMED.
void reset_unsupported()
{
for(auto &p : properties)
if(p.is_unsupported())
{
if(p.is_assumption())
p.assumed();
else
p.unknown();
}
}
};

#endif // CPROVER_EBMC_PROPERTIES_H
36 changes: 33 additions & 3 deletions src/ebmc/k_induction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,44 @@ Function: k_inductiont::operator()

void k_inductiont::operator()()
{
// Unsupported assumption? Mark as such.
bool assumption_unsupported = false;
for(auto &property : properties.properties)
{
if(!supported(property) && property.is_assumed())
{
assumption_unsupported = true;
property.unsupported("unsupported by k-induction");
}
}

// Fail unsupported properties
for(auto &property : properties.properties)
{
if(!supported(property))
property.failure("property unsupported by k-induction");
if(
!supported(property) && !property.is_assumed() && !property.is_disabled())
{
property.unsupported("unsupported by k-induction");
}
}

// do induction base
induction_base();

// do induction step
induction_step();

// Any refuted properties are really inconclusive if there are
// unsupported assumptions, as the assumption might have
// proven the property.
if(assumption_unsupported)
{
for(auto &property : properties.properties)
{
if(property.is_refuted())
property.inconclusive();
}
}
}

/*******************************************************************\
Expand Down Expand Up @@ -231,8 +257,12 @@ void k_inductiont::induction_step()

for(auto &p_it : properties.properties)
{
if(p_it.is_disabled() || p_it.is_failure() || p_it.is_assumed())
if(
p_it.is_disabled() || p_it.is_failure() || p_it.is_assumed() ||
p_it.is_unsupported())
{
continue;
}

// If it's not failed, then it's supported.
DATA_INVARIANT(supported(p_it), "property must be supported");
Expand Down
Loading
Loading