Skip to content

Commit 6bd1bd5

Browse files
committed
Synthesize loop assigns before loop invariants.
Loop invariants synthesis is dependent on loop-assigns clauses. So we target non assignable-violations until all assignable-violations are fixed.
1 parent 019952e commit 6bd1bd5

File tree

9 files changed

+170
-122
lines changed

9 files changed

+170
-122
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <stdlib.h>
2+
#define SIZE 80
3+
4+
void main()
5+
{
6+
unsigned long len;
7+
__CPROVER_assume(len <= SIZE);
8+
__CPROVER_assume(len >= 8);
9+
const char *i = malloc(len);
10+
const char *end = i + len;
11+
char s = 0;
12+
13+
while(i != end)
14+
{
15+
s = *i++;
16+
}
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--pointer-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.pointer\_dereference.\d+\] .* SUCCESS$
7+
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$
8+
^\[main.assigns.\d+\] .* Check that i is assignable: SUCCESS$
9+
^VERIFICATION SUCCESSFUL$
10+
--
11+
--
12+
Check whether assigns clauses are synthesize before invariant clauses.

src/goto-synthesizer/cegis_verifier.cpp

Lines changed: 127 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -586,143 +586,159 @@ optionalt<cext> cegis_verifiert::verify()
586586
}
587587

588588
properties = checker->get_properties();
589-
// Find the violation and construct counterexample from its trace.
590-
for(const auto &property_it : properties)
589+
bool target_violation_found = false;
590+
auto target_violation_info = properties.begin()->second;
591+
592+
// Find target violation---the violation we want to fix next.
593+
// A target violation is an assignable violation or the first violation that
594+
// is not assignable violation.
595+
for(const auto &property : properties)
591596
{
592-
if(property_it.second.status != property_statust::FAIL)
597+
if(property.second.status != property_statust::FAIL)
593598
continue;
594599

595-
// Store the violation that we want to fix with synthesized
596-
// assigns/invariant.
597-
first_violation = property_it.first;
598-
599-
// Type of the violation
600-
cext::violation_typet violation_type = cext::violation_typet::cex_other;
601-
602-
// Decide the violation type from the description of violation
603-
604-
// The violation is a pointer OOB check.
605-
if((property_it.second.description.find(
606-
"dereference failure: pointer outside object bounds in") !=
607-
std::string::npos))
600+
// assignable violation found
601+
if(property.second.description.find("assignable") != std::string::npos)
608602
{
609-
violation_type = cext::violation_typet::cex_out_of_boundary;
610-
}
611-
612-
// The violation is a null pointer check.
613-
if(property_it.second.description.find("pointer NULL") != std::string::npos)
614-
{
615-
violation_type = cext::violation_typet::cex_null_pointer;
603+
target_violation = property.first;
604+
target_violation_info = property.second;
605+
break;
616606
}
617607

618-
// The violation is a loop-invariant-preservation check.
619-
if(property_it.second.description.find("preserved") != std::string::npos)
608+
// Store the violation that we want to fix with synthesized
609+
// assigns/invariant.
610+
if(!target_violation_found)
620611
{
621-
violation_type = cext::violation_typet::cex_not_preserved;
612+
target_violation = property.first;
613+
target_violation_info = property.second;
614+
target_violation_found = true;
622615
}
616+
}
623617

624-
// The violation is a loop-invariant-preservation check.
625-
if(
626-
property_it.second.description.find("invariant before entry") !=
627-
std::string::npos)
628-
{
629-
violation_type = cext::violation_typet::cex_not_hold_upon_entry;
630-
}
618+
// Construct counterexample from first violation's trace.
619+
// Type of the violation
620+
cext::violation_typet violation_type = cext::violation_typet::cex_other;
631621

632-
// The violation is an assignable check.
633-
if(property_it.second.description.find("assignable") != std::string::npos)
634-
{
635-
violation_type = cext::violation_typet::cex_assignable;
636-
}
622+
// Decide the violation type from the description of violation
637623

638-
// Compute the cause loop---the loop for which we synthesize loop contracts,
639-
// and the counterexample.
624+
// The violation is a pointer OOB check.
625+
if((target_violation_info.description.find(
626+
"dereference failure: pointer outside object bounds in") !=
627+
std::string::npos))
628+
{
629+
violation_type = cext::violation_typet::cex_out_of_boundary;
630+
}
640631

641-
// If the violation is an assignable check, we synthesize assigns targets.
642-
// In the case, cause loops are all loops the violation is in. We keep
643-
// adding the new assigns target to the most-inner loop that does not
644-
// contain the new target until the assignable violation is resolved.
632+
// The violation is a null pointer check.
633+
if(
634+
target_violation_info.description.find("pointer NULL") != std::string::npos)
635+
{
636+
violation_type = cext::violation_typet::cex_null_pointer;
637+
}
645638

646-
// For other cases, we synthesize loop invariant clauses. We synthesize
647-
// invariants for one loop at a time. So we return only the first cause loop
648-
// although there can be multiple ones.
639+
// The violation is a loop-invariant-preservation check.
640+
if(target_violation_info.description.find("preserved") != std::string::npos)
641+
{
642+
violation_type = cext::violation_typet::cex_not_preserved;
643+
}
649644

650-
log.debug() << "Start to compute cause loop ids." << messaget::eom;
645+
// The violation is a loop-invariant-preservation check.
646+
if(
647+
target_violation_info.description.find("invariant before entry") !=
648+
std::string::npos)
649+
{
650+
violation_type = cext::violation_typet::cex_not_hold_upon_entry;
651+
}
651652

652-
const auto &trace = checker->get_traces()[property_it.first];
653+
// The violation is an assignable check.
654+
if(target_violation_info.description.find("assignable") != std::string::npos)
655+
{
656+
violation_type = cext::violation_typet::cex_assignable;
657+
}
653658

654-
// Doing assigns-synthesis or invariant-synthesis
655-
if(violation_type == cext::violation_typet::cex_assignable)
656-
{
657-
cext result(violation_type);
658-
result.cause_loop_ids = get_cause_loop_id_for_assigns(trace);
659-
result.checked_pointer = static_cast<const exprt &>(
660-
property_it.second.pc->condition().find(ID_checked_assigns));
661-
restore_functions();
662-
return result;
663-
}
659+
// Compute the cause loop---the loop for which we synthesize loop contracts,
660+
// and the counterexample.
664661

665-
// We construct the full counterexample only for violations other than
666-
// assignable checks.
662+
// If the violation is an assignable check, we synthesize assigns targets.
663+
// In the case, cause loops are all loops the violation is in. We keep
664+
// adding the new assigns target to the most-inner loop that does not
665+
// contain the new target until the assignable violation is resolved.
667666

668-
// Although there can be multiple cause loop ids. We only synthesize
669-
// loop invariants for the first cause loop.
670-
const std::list<loop_idt> cause_loop_ids =
671-
get_cause_loop_id(trace, property_it.second.pc);
667+
// For other cases, we synthesize loop invariant clauses. We synthesize
668+
// invariants for one loop at a time. So we return only the first cause loop
669+
// although there can be multiple ones.
672670

673-
if(cause_loop_ids.empty())
674-
{
675-
log.debug() << "No cause loop found!" << messaget::eom;
676-
restore_functions();
671+
log.debug() << "Start to compute cause loop ids." << messaget::eom;
677672

678-
return cext(violation_type);
679-
}
673+
const auto &trace = checker->get_traces()[target_violation];
674+
// Doing assigns-synthesis or invariant-synthesis
675+
if(violation_type == cext::violation_typet::cex_assignable)
676+
{
677+
cext result(violation_type);
678+
result.cause_loop_ids = get_cause_loop_id_for_assigns(trace);
679+
result.checked_pointer = static_cast<const exprt &>(
680+
target_violation_info.pc->condition().find(ID_checked_assigns));
681+
restore_functions();
682+
return result;
683+
}
680684

681-
log.debug() << "Found cause loop with function id: "
682-
<< cause_loop_ids.front().function_id
683-
<< ", and loop number: " << cause_loop_ids.front().loop_number
684-
<< messaget::eom;
685+
// We construct the full counterexample only for violations other than
686+
// assignable checks.
685687

686-
auto violation_location = cext::violation_locationt::in_loop;
687-
// We always strengthen in_clause if the violation is
688-
// invariant-not-preserved.
689-
if(violation_type != cext::violation_typet::cex_not_preserved)
690-
{
691-
// Get the location of the violation
692-
violation_location = get_violation_location(
693-
cause_loop_ids.front(),
694-
goto_model.get_goto_function(cause_loop_ids.front().function_id),
695-
property_it.second.pc->location_number);
696-
}
688+
// Although there can be multiple cause loop ids. We only synthesize
689+
// loop invariants for the first cause loop.
690+
const std::list<loop_idt> cause_loop_ids =
691+
get_cause_loop_id(trace, target_violation_info.pc);
697692

693+
if(cause_loop_ids.empty())
694+
{
695+
log.debug() << "No cause loop found!" << messaget::eom;
698696
restore_functions();
699697

700-
auto return_cex = build_cex(
701-
trace,
702-
get_loop_head(
703-
cause_loop_ids.front().loop_number,
704-
goto_model.goto_functions
705-
.function_map[cause_loop_ids.front().function_id])
706-
->source_location());
707-
return_cex.violated_predicate = property_it.second.pc->condition();
708-
return_cex.cause_loop_ids = cause_loop_ids;
709-
return_cex.violation_location = violation_location;
710-
return_cex.violation_type = violation_type;
711-
712-
// The pointer checked in the null-pointer-check violation.
713-
if(violation_type == cext::violation_typet::cex_null_pointer)
714-
{
715-
return_cex.checked_pointer = property_it.second.pc->condition()
716-
.operands()[0]
717-
.operands()[1]
718-
.operands()[0];
719-
INVARIANT(
720-
return_cex.checked_pointer.id() == ID_symbol,
721-
"Checking pointer symbol");
722-
}
698+
return cext(violation_type);
699+
}
723700

724-
return return_cex;
701+
log.debug() << "Found cause loop with function id: "
702+
<< cause_loop_ids.front().function_id
703+
<< ", and loop number: " << cause_loop_ids.front().loop_number
704+
<< messaget::eom;
705+
706+
auto violation_location = cext::violation_locationt::in_loop;
707+
// We always strengthen in_clause if the violation is
708+
// invariant-not-preserved.
709+
if(violation_type != cext::violation_typet::cex_not_preserved)
710+
{
711+
// Get the location of the violation
712+
violation_location = get_violation_location(
713+
cause_loop_ids.front(),
714+
goto_model.get_goto_function(cause_loop_ids.front().function_id),
715+
target_violation_info.pc->location_number);
716+
}
717+
718+
restore_functions();
719+
720+
auto return_cex = build_cex(
721+
trace,
722+
get_loop_head(
723+
cause_loop_ids.front().loop_number,
724+
goto_model.goto_functions
725+
.function_map[cause_loop_ids.front().function_id])
726+
->source_location());
727+
return_cex.violated_predicate = target_violation_info.pc->condition();
728+
return_cex.cause_loop_ids = cause_loop_ids;
729+
return_cex.violation_location = violation_location;
730+
return_cex.violation_type = violation_type;
731+
732+
// The pointer checked in the null-pointer-check violation.
733+
if(violation_type == cext::violation_typet::cex_null_pointer)
734+
{
735+
return_cex.checked_pointer = target_violation_info.pc->condition()
736+
.operands()[0]
737+
.operands()[1]
738+
.operands()[0];
739+
INVARIANT(
740+
return_cex.checked_pointer.id() == ID_symbol, "Checking pointer symbol");
725741
}
726742

727-
UNREACHABLE;
743+
return return_cex;
728744
}

src/goto-synthesizer/cegis_verifier.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class cegis_verifiert
117117

118118
/// Result counterexample.
119119
propertiest properties;
120-
irep_idt first_violation;
120+
irep_idt target_violation;
121121

122122
protected:
123123
// Get the options same as using CBMC api without any flag, and

src/goto-synthesizer/dump_loop_contracts.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,16 @@ void dump_loop_contracts(
9191
"loop " + std::to_string(invariant_entry.first.loop_number + 1) +
9292
" assigns ";
9393

94-
bool in_fisrt_iter = true;
94+
bool in_first_iter = true;
9595
for(const auto &a : it_assigns->second)
9696
{
97-
if(!in_fisrt_iter)
97+
if(!in_first_iter)
9898
{
9999
assign_string += ",";
100100
}
101101
else
102102
{
103-
in_fisrt_iter = false;
103+
in_first_iter = false;
104104
}
105105
assign_string += expr2c(
106106
simplify_expr(a, ns), ns, expr2c_configurationt::clean_configuration);

src/goto-synthesizer/enumerative_loop_contracts_synthesizer.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ void enumerative_loop_contracts_synthesizert::init_candidates()
9494

9595
loop_idt new_id(function_p.first, loop_end->loop_number);
9696

97+
log.debug() << "Initialize candidates for the loop at "
98+
<< loop_end->source_location() << messaget::eom;
99+
97100
// we only synthesize invariants and assigns for unannotated loops
98101
if(loop_end->condition().find(ID_C_spec_loop_invariant).is_nil())
99102
{
@@ -372,7 +375,7 @@ invariant_mapt enumerative_loop_contracts_synthesizert::synthesize_all()
372375
new_invariant_clause = synthesize_strengthening_clause(
373376
terminal_symbols,
374377
return_cex->cause_loop_ids.front(),
375-
verifier.first_violation);
378+
verifier.target_violation);
376379
break;
377380

378381
case cext::violation_typet::cex_assignable:

src/goto-synthesizer/enumerative_loop_contracts_synthesizer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class enumerative_loop_contracts_synthesizert
5353
/// to their original variables.
5454
void build_tmp_post_map();
5555

56-
/// Compute the depedent symbols for a loop with invariant-not-preserved
56+
/// Compute the dependent symbols for a loop with invariant-not-preserved
5757
/// violation which happen after `new_clause` was added.
5858
std::set<symbol_exprt> compute_dependent_symbols(
5959
const loop_idt &cause_loop_id,

src/goto-synthesizer/expr_enumerator.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ std::vector<std::size_t> get_ones_pos(std::size_t v)
208208
return result;
209209
}
210210

211-
/// Construct parition of `n` elements from a bit vector `v`.
211+
/// Construct partition of `n` elements from a bit vector `v`.
212212
/// For a bit vector with ones at positions (computed by `get_ones_pos`)
213213
/// (ones[0], ones[1], ..., ones[k-2]),
214214
/// the corresponding partition is
@@ -255,7 +255,7 @@ std::list<partitiont> non_leaf_enumeratort::get_partitions(
255255

256256
// Initial `v` is with ones at positions (n-k+1, n-k+2, ..., n-2, n-1).
257257
std::size_t v = 0;
258-
// Initial `end` (the last bit vectorr we enumerate) is with ones at
258+
// Initial `end` (the last bit vector we enumerate) is with ones at
259259
// positions (1, 2, 3, ..., k-1).
260260
std::size_t end = 0;
261261
for(size_t i = 0; i < k - 1; i++)
@@ -412,5 +412,5 @@ void enumerator_factoryt::attach_productions(
412412
{
413413
const auto &ret = productions_map.insert({id, enumerators});
414414
INVARIANT(
415-
ret.second, "Cannnot attach enumerators to a non-existing nonterminal.");
415+
ret.second, "Cannot attach enumerators to a non-existing nonterminal.");
416416
}

0 commit comments

Comments
 (0)