Skip to content

Commit 649dbb3

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 e911fa9 commit 649dbb3

File tree

2 files changed

+57
-25
lines changed

2 files changed

+57
-25
lines changed

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
// ```

0 commit comments

Comments
 (0)