Skip to content

Commit 888a08a

Browse files
Merge pull request #7627 from remi-delmas-3000/contracts-loop-normal-form
CONTRACTS: check function for loop normal form
2 parents f6f7057 + c575fbf commit 888a08a

File tree

4 files changed

+226
-0
lines changed

4 files changed

+226
-0
lines changed

src/goto-instrument/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ SRC = accelerate/accelerate.cpp \
2222
contracts/dynamic-frames/dfcc_contract_mode.cpp \
2323
contracts/dynamic-frames/dfcc_loop_contract_mode.cpp \
2424
contracts/dynamic-frames/dfcc_loop_tags.cpp \
25+
contracts/dynamic-frames/dfcc_check_loop_normal_form.cpp \
2526
contracts/dynamic-frames/dfcc_library.cpp \
2627
contracts/dynamic-frames/dfcc_is_cprover_symbol.cpp \
2728
contracts/dynamic-frames/dfcc_is_fresh.cpp \
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*******************************************************************\
2+
3+
Module: Dynamic frame condition checking for loop contracts
4+
5+
Author: Qinheping Hu, [email protected]
6+
Author: Remi Delmas, [email protected]
7+
8+
\*******************************************************************/
9+
10+
#include "dfcc_check_loop_normal_form.h"
11+
12+
#include <analyses/natural_loops.h>
13+
#include <goto-instrument/contracts/utils.h>
14+
15+
void dfcc_check_loop_normal_form(goto_programt &goto_program, messaget &log)
16+
{
17+
natural_loops_mutablet natural_loops(goto_program);
18+
19+
// instruction span for each loop
20+
std::vector<std::pair<goto_programt::targett, goto_programt::targett>> spans;
21+
22+
for(auto &loop : natural_loops.loop_map)
23+
{
24+
auto head = loop.first;
25+
auto &loop_instructions = loop.second;
26+
27+
if(loop_instructions.size() <= 1)
28+
{
29+
// Ignore single instruction loops of the form
30+
// `LOOP: IF cond GOTO LOOP;`
31+
continue;
32+
}
33+
34+
auto latch = head;
35+
bool latch_found = false;
36+
37+
// Find latch and check it is unique
38+
for(const auto &t : loop_instructions)
39+
{
40+
if(t->is_goto() && t->get_target() == head)
41+
{
42+
if(latch_found)
43+
{
44+
log.error() << "Loop starting at:"
45+
<< "\n- head: " << head->source_location()
46+
<< "\nhas more than one latch instruction:"
47+
<< "\n- latch1: " << latch->source_location()
48+
<< "\n- latch2: " << t->source_location()
49+
<< messaget::eom;
50+
throw analysis_exceptiont(
51+
"Found loop with more than one latch instruction");
52+
}
53+
latch = t;
54+
latch_found = true;
55+
}
56+
}
57+
58+
INVARIANT(latch_found, "Natural loop latch found");
59+
60+
// Check that instruction spans are not overlapping
61+
for(const auto &span : spans)
62+
{
63+
bool head_in_span =
64+
span.first->location_number <= head->location_number &&
65+
head->location_number <= span.second->location_number;
66+
67+
bool latch_in_span =
68+
span.first->location_number <= latch->location_number &&
69+
latch->location_number <= span.second->location_number;
70+
71+
if(head_in_span != latch_in_span)
72+
{
73+
log.error() << "Loop spanning:"
74+
<< "\n- head: " << head->source_location()
75+
<< "\n- latch: " << latch->source_location()
76+
<< "\noverlaps loop spanning:"
77+
<< "\n- head: " << span.first->source_location()
78+
<< "\n- latch: " << span.second->source_location()
79+
<< messaget::eom;
80+
throw analysis_exceptiont(
81+
"Found loops with overlapping instruction spans");
82+
}
83+
}
84+
85+
spans.push_back({head, latch});
86+
87+
// Check that:
88+
// 1. all loop instructions are in the range [head, latch]
89+
// 2. except for the head instruction, no incoming edge from outside the
90+
// loop
91+
for(const auto &i : loop_instructions)
92+
{
93+
if(
94+
i->location_number < head->location_number ||
95+
i->location_number > latch->location_number)
96+
{
97+
log.error() << "Loop body instruction at:"
98+
<< "\n- " << i->source_location() << "\nis outside of"
99+
<< "\n- head: " << head->source_location()
100+
<< "\n- latch: " << latch->source_location()
101+
<< messaget::eom;
102+
throw analysis_exceptiont(
103+
"Found loop body instruction outside of the [head, latch] "
104+
"instruction span");
105+
}
106+
107+
for(const auto &from : i->incoming_edges)
108+
{
109+
if(i != head && !loop_instructions.contains(from))
110+
{
111+
log.error() << "Loop body instruction at:"
112+
<< "\n- " << i->source_location()
113+
<< "\n has incoming edge from outside the loop at:"
114+
<< "\n- " << from->source_location() << messaget::eom;
115+
116+
throw analysis_exceptiont(
117+
"Found loop body instruction with incoming edge from outside the "
118+
"loop");
119+
}
120+
}
121+
}
122+
123+
// Check if a loop contains another loop's head (resp. latch) then
124+
// it also contains the latch (resp. head)
125+
for(auto &other_loop : natural_loops.loop_map)
126+
{
127+
auto &other_loop_instructions = other_loop.second;
128+
bool contains_head = other_loop_instructions.contains(head);
129+
bool contains_latch = other_loop_instructions.contains(latch);
130+
INVARIANT(
131+
contains_head == contains_latch,
132+
"nested loop head and latch are in outer loop");
133+
}
134+
135+
// all checks passed, now we perform some instruction rewriting
136+
137+
// Convert conditional latch into exiting + unconditional latch.
138+
// ```
139+
// IF g THEN GOTO HEAD
140+
// --------------------------
141+
// IF !g THEN GOTO EXIT
142+
// GOTO HEAD
143+
// EXIT: SKIP
144+
// ```
145+
if(latch->has_condition() && !latch->condition().is_true())
146+
{
147+
const source_locationt &loc = latch->source_location();
148+
const auto &exit =
149+
goto_program.insert_after(latch, goto_programt::make_skip(loc));
150+
151+
insert_before_and_update_jumps(
152+
goto_program,
153+
latch,
154+
goto_programt::make_goto(
155+
exit, not_exprt(latch->condition()), latch->source_location()));
156+
157+
// CAUTION: The condition expression needs to be updated in place because
158+
// this is where loop contract clauses are stored as extra ireps.
159+
// Overwriting this expression with a fresh expression will also lose the
160+
// loop contract.
161+
exprt latch_condition = latch->condition();
162+
latch_condition.set(ID_value, ID_true);
163+
*latch = goto_programt::make_goto(head, latch_condition, loc);
164+
}
165+
166+
// The latch node is now an unconditional jump.
167+
168+
// insert a SKIP pre-head node and reroute all incoming edges to that node,
169+
// except for edge coming from the latch.
170+
insert_before_and_update_jumps(
171+
goto_program, head, goto_programt::make_skip(head->source_location()));
172+
latch->set_target(head);
173+
}
174+
goto_program.update();
175+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*******************************************************************\
2+
3+
Module: Dynamic frame condition checking for loop contracts
4+
5+
Author: Qinheping Hu, [email protected]
6+
Author: Remi Delmas, [email protected]
7+
8+
\*******************************************************************/
9+
10+
/// \file
11+
/// Checks normal form properties of natural loops in a GOTO program.
12+
13+
#ifndef CPROVER_GOTO_INSTRUMENT_CONTRACTS_DYNAMIC_FRAMES_DFCC_CHECK_LOOP_NORMAL_FORM_H
14+
#define CPROVER_GOTO_INSTRUMENT_CONTRACTS_DYNAMIC_FRAMES_DFCC_CHECK_LOOP_NORMAL_FORM_H
15+
16+
class goto_programt;
17+
class messaget;
18+
19+
/// \brief Checks and enforces normal form properties for natural loops of the
20+
/// given \p goto_program.
21+
///
22+
/// If and when the function succeeds, the following properties are established
23+
/// for each natural loop.
24+
///
25+
/// The loop has either a single instruction:
26+
///
27+
/// ```c
28+
/// START: IF expr GOTO START;
29+
/// ```
30+
///
31+
/// OR
32+
///
33+
/// The loop has two or more instructions and:
34+
/// 1. has a unique head instruction;
35+
/// 2. has a unique, unconditional GOTO latch instruction;
36+
/// 3. has a pre-head SKIP instruction that is not part of the loop;
37+
/// 4. the head has two incoming edges from the pre-head and the latch;
38+
/// 5. for all instructions but the head, incoming edges are from instructions
39+
/// in the same loop;
40+
/// 6. all instructions of the loop are found between the head and the latch in
41+
/// the instruction sequence;
42+
/// 7. The span of instructions of any two loops are either nested or disjoint.
43+
///
44+
/// \remark The goto program will be modified by the addition of the pre-head
45+
/// SKIP instructions, and by the rewriting of conditional latch instructions
46+
/// into conditional exit jump + unconditional backjump.
47+
void dfcc_check_loop_normal_form(goto_programt &goto_program, messaget &log);
48+
49+
#endif

src/goto-instrument/contracts/dynamic-frames/module_dependencies.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
analyses
12
ansi-c
23
goto-instrument/contracts
34
goto-instrument/contracts/dynamic-frames

0 commit comments

Comments
 (0)