Skip to content

Commit c9c23be

Browse files
committed
Code contracts: do not interleave checking and instrumenting
Loop normalisation must first check that all loops described via `natural_loopst` are of the expected shape and then start inserting or transforming instructions. Else, and depending on loop shapes and loop nesting, the instruction iterators held in the `natural_loopst` may no longer be adequate descriptions of the loops. (The iterators weren't invalidated, but would no longer point to heads or loop ends.)
1 parent fd28cb2 commit c9c23be

File tree

6 files changed

+90
-31
lines changed

6 files changed

+90
-31
lines changed

Diff for: regression/contracts-dfcc/loop_contracts_do_while/main.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ int main()
44
{
55
int x = 0;
66

7-
do
7+
do __CPROVER_loop_invariant(0 <= x && x <= 10)
88
{
99
x++;
10-
} while(x < 10) __CPROVER_loop_invariant(0 <= x && x <= 10);
10+
} while(x < 10);
1111

12-
assert(x == 10);
12+
assert(x <= 10);
1313
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
int x = 0;
6+
7+
do
8+
{
9+
do
10+
{
11+
x++;
12+
} while(0);
13+
} while(0);
14+
15+
assert(x <= 10);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
KNOWNBUG
2+
nested.c
3+
--dfcc main --apply-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
We spuriously report that x is not assignable.

Diff for: src/goto-instrument/contracts/contracts.cpp

+43-25
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,12 @@ void code_contractst::apply_loop_contract(
888888

889889
std::list<size_t> to_check_contracts_on_children;
890890

891+
std::map<
892+
goto_programt::targett,
893+
std::pair<goto_programt::targett, natural_loops_mutablet::loopt>,
894+
goto_programt::target_less_than>
895+
loop_head_ends;
896+
891897
for(const auto &loop_head_and_content : natural_loops.loop_map)
892898
{
893899
const auto &loop_content = loop_head_and_content.second;
@@ -914,6 +920,42 @@ void code_contractst::apply_loop_contract(
914920
throw 0;
915921
}
916922

923+
// By definition the `loop_content` is a set of instructions computed
924+
// by `natural_loops` based on the CFG.
925+
// Since we perform assigns clause instrumentation by sequentially
926+
// traversing instructions from `loop_head` to `loop_end`,
927+
// here we ensure that all instructions in `loop_content` belong within
928+
// the [loop_head, loop_end] target range.
929+
930+
// Check 1. (i \in loop_content) ==> loop_head <= i <= loop_end
931+
for(const auto &i : loop_content)
932+
{
933+
if(
934+
loop_head->location_number > i->location_number ||
935+
i->location_number > loop_end->location_number)
936+
{
937+
log.conditional_output(
938+
log.error(), [&i, &loop_head](messaget::mstreamt &mstream) {
939+
mstream << "Computed loop at " << loop_head->source_location()
940+
<< "contains an instruction beyond [loop_head, loop_end]:"
941+
<< messaget::eom;
942+
i->output(mstream);
943+
mstream << messaget::eom;
944+
});
945+
throw 0;
946+
}
947+
}
948+
949+
if(!loop_head_ends
950+
.emplace(loop_head, std::make_pair(loop_end, loop_content))
951+
.second)
952+
UNREACHABLE;
953+
}
954+
955+
for(auto &loop_head_end : loop_head_ends)
956+
{
957+
auto loop_head = loop_head_end.first;
958+
auto loop_end = loop_head_end.second.first;
917959
// After loop-contract instrumentation, jumps to the `loop_head` will skip
918960
// some instrumented instructions. So we want to make sure that there is
919961
// only one jump targeting `loop_head` from `loop_end` before loop-contract
@@ -961,7 +1003,7 @@ void code_contractst::apply_loop_contract(
9611003
}
9621004

9631005
const auto idx = loop_nesting_graph.add_node(
964-
loop_content,
1006+
loop_head_end.second.second,
9651007
loop_head,
9661008
loop_end,
9671009
assigns_clause,
@@ -974,30 +1016,6 @@ void code_contractst::apply_loop_contract(
9741016
continue;
9751017

9761018
to_check_contracts_on_children.push_back(idx);
977-
978-
// By definition the `loop_content` is a set of instructions computed
979-
// by `natural_loops` based on the CFG.
980-
// Since we perform assigns clause instrumentation by sequentially
981-
// traversing instructions from `loop_head` to `loop_end`,
982-
// here we ensure that all instructions in `loop_content` belong within
983-
// the [loop_head, loop_end] target range
984-
985-
// Check 1. (i \in loop_content) ==> loop_head <= i <= loop_end
986-
for(const auto &i : loop_content)
987-
{
988-
if(std::distance(loop_head, i) < 0 || std::distance(i, loop_end) < 0)
989-
{
990-
log.conditional_output(
991-
log.error(), [&i, &loop_head](messaget::mstreamt &mstream) {
992-
mstream << "Computed loop at " << loop_head->source_location()
993-
<< "contains an instruction beyond [loop_head, loop_end]:"
994-
<< messaget::eom;
995-
i->output(mstream);
996-
mstream << messaget::eom;
997-
});
998-
throw 0;
999-
}
1000-
}
10011019
}
10021020

10031021
for(size_t outer = 0; outer < loop_nesting_graph.size(); ++outer)

Diff for: src/goto-instrument/contracts/dynamic-frames/dfcc_check_loop_normal_form.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ void dfcc_check_loop_normal_form(goto_programt &goto_program, messaget &log)
1919
// instruction span for each loop
2020
std::vector<std::pair<goto_programt::targett, goto_programt::targett>> spans;
2121

22+
std::map<
23+
goto_programt::targett,
24+
goto_programt::targett,
25+
goto_programt::target_less_than>
26+
latch_head_pairs;
27+
2228
for(auto &loop : natural_loops.loop_map)
2329
{
2430
auto head = loop.first;
@@ -132,7 +138,15 @@ void dfcc_check_loop_normal_form(goto_programt &goto_program, messaget &log)
132138
"nested loop head and latch are in outer loop");
133139
}
134140

141+
if(!latch_head_pairs.emplace(latch, head).second)
142+
UNREACHABLE;
143+
}
144+
135145
// all checks passed, now we perform some instruction rewriting
146+
for(auto &entry_pair : latch_head_pairs)
147+
{
148+
auto latch = entry_pair.first;
149+
auto head = entry_pair.second;
136150

137151
// Convert conditional latch into exiting + unconditional latch.
138152
// ```

Diff for: src/goto-instrument/goto_instrument_parse_options.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,8 @@ void goto_instrument_parse_optionst::instrument_goto_program()
11741174
}
11751175

11761176
do_indirect_call_and_rtti_removal();
1177+
log.status() << "Trying to force one backedge per target" << messaget::eom;
1178+
ensure_one_backedge_per_target(goto_model);
11771179

11781180
const irep_idt harness_id(cmdline.get_value(FLAG_DFCC));
11791181

@@ -1232,13 +1234,13 @@ void goto_instrument_parse_optionst::instrument_goto_program()
12321234
to_exclude_from_nondet_static,
12331235
log.get_message_handler());
12341236
}
1235-
1236-
if(
1237-
!use_dfcc &&
1237+
else if(
12381238
(cmdline.isset(FLAG_LOOP_CONTRACTS) || cmdline.isset(FLAG_REPLACE_CALL) ||
12391239
cmdline.isset(FLAG_ENFORCE_CONTRACT)))
12401240
{
12411241
do_indirect_call_and_rtti_removal();
1242+
log.status() << "Trying to force one backedge per target" << messaget::eom;
1243+
ensure_one_backedge_per_target(goto_model);
12421244
code_contractst contracts(goto_model, log);
12431245

12441246
std::set<std::string> to_replace(

0 commit comments

Comments
 (0)