Skip to content

Commit

Permalink
Engine heuristic: fix for assumptions unsupported by k-induction
Browse files Browse the repository at this point in the history
The k-induction engine now correctly reports unsupported assumptions, and is
then skipped by the engine selection heuristic.

Fixes #921.
  • Loading branch information
kroening committed Jan 15, 2025
1 parent c93d1e8 commit d343c67
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 26 deletions.
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
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
61 changes: 58 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,11 +99,21 @@ 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;
}

void unknown()
{
status = statust::UNKNOWN;
Expand Down Expand Up @@ -146,6 +157,12 @@ 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;
Expand All @@ -164,16 +181,26 @@ 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 +236,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
33 changes: 30 additions & 3 deletions src/ebmc/k_induction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,39 @@ 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.unsupported("unsupported by k-induction");
}

// do induction base
induction_base();

// 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.unknown();
}
}

// do induction step
induction_step();
}
Expand Down Expand Up @@ -231,8 +254,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
17 changes: 10 additions & 7 deletions src/ebmc/property_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ property_checker_resultt engine_heuristic(

auto solver_factory = ebmc_solver_factory(cmdline);

if(!properties.has_unknown_property())
if(!properties.has_unfinished_property())
return property_checker_resultt{properties}; // done

message.status() << "No engine given, attempting heuristic engine selection"
Expand All @@ -384,12 +384,15 @@ property_checker_resultt engine_heuristic(
k_induction(
1, transition_system, properties, solver_factory, message_handler);

properties.reset_failure_to_unknown();

if(!properties.has_unknown_property())
if(!properties.has_unfinished_property())
return property_checker_resultt{properties}; // done

properties.reset_failure();
properties.reset_inconclusive();
properties.reset_unsupported();

// Now try BMC with bound 5, word-level
message.status() << "Attempting BMC with bound 5" << messaget::eom;

bmc(
5, // bound
Expand All @@ -400,11 +403,11 @@ property_checker_resultt engine_heuristic(
solver_factory,
message_handler);

properties.reset_failure_to_unknown();

if(!properties.has_unknown_property())
if(!properties.has_unfinished_property())
return property_checker_resultt{properties}; // done

properties.reset_failure();

// Give up
return property_checker_resultt{properties}; // done
}
Expand Down
8 changes: 7 additions & 1 deletion src/ebmc/property_checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,18 @@ class property_checker_resultt
bool all_properties_proved() const
{
for(const auto &p : properties)
if(
{
if(p.is_assumption())
{
// ignore
}
else if(
!p.is_proved() && !p.is_proved_with_bound() && !p.is_disabled() &&
!p.is_assumed())
{
return false;
}
}

return true;
}
Expand Down
1 change: 1 addition & 0 deletions src/ebmc/report_results.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ void report_results(
case statust::DROPPED: message.status() << messaget::red; break;
case statust::FAILURE: message.status() << messaget::red; break;
case statust::UNKNOWN: message.status() << messaget::yellow; break;
case statust::UNSUPPORTED: message.status() << messaget::yellow; break;
case statust::DISABLED: break;
case statust::INCONCLUSIVE: message.status() << messaget::yellow; break;
}
Expand Down

0 comments on commit d343c67

Please sign in to comment.