Skip to content

Commit dae5af0

Browse files
authored
Merge pull request #8416 from remi-delmas-3000/skip-loops
CONTRACTS: redirect checks to outer write set for loops that get skipped
2 parents 2bef701 + 48aa648 commit dae5af0

File tree

10 files changed

+118
-11
lines changed

10 files changed

+118
-11
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
KNOWNBUG
1+
CORE
22
nested.c
33
--dfcc main --apply-loop-contracts
44
^EXIT=0$
55
^SIGNAL=0$
66
^VERIFICATION SUCCESSFUL$
77
--
88
--
9-
We spuriously report that x is not assignable.
9+
We properly skip the instrumentation of both loops.
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
CORE dfcc-only
22
main.c
33
--dfcc main --apply-loop-contracts
4-
^EXIT=10$
4+
^EXIT=0$
55
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
67
--
7-
^Found loop with more than one latch instruction$
88
--
99
This test checks that our loop contract instrumentation first transforms loops
10-
so as to only have a single loop latch.
10+
so as to only have a single loop latch, and skips instrumentation if the result
11+
has no contract.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
int foo()
2+
{
3+
int x = 0;
4+
while(x < 10)
5+
{
6+
++x;
7+
}
8+
return x;
9+
}
10+
11+
int main()
12+
{
13+
int x = 0;
14+
15+
for(int i = 0; i < 10; ++i)
16+
__CPROVER_loop_invariant(0 <= x && x <= 10)
17+
{
18+
if(x < 10)
19+
x++;
20+
}
21+
22+
x += foo();
23+
24+
__CPROVER_assert(x <= 20, "");
25+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
across_functions.c
3+
--dfcc main --apply-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
This test case checks that when instrumentation of any look is skipped, we
11+
redirect write set checks to the parent write set.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
int global;
2+
3+
int main()
4+
{
5+
global = 0;
6+
int argc = 1;
7+
do
8+
{
9+
int local;
10+
global = 1;
11+
local = 1;
12+
for(int i = 0; i < 1; i++)
13+
{
14+
local = 1;
15+
global = 2;
16+
int j = 0;
17+
while(j < 1)
18+
{
19+
local = 1;
20+
global = 3;
21+
j++;
22+
}
23+
__CPROVER_assert(global == 3, "case3");
24+
}
25+
__CPROVER_assert(global == 3, "case3");
26+
} while(0);
27+
__CPROVER_assert(global == 3, "case1");
28+
return 0;
29+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--dfcc main --apply-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
This test case checks that when the instrumentation of nested loops is skipped, we redirect the write set checks to the
11+
outer write set.

src/goto-instrument/contracts/dynamic-frames/dfcc.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ std::string invalid_function_contract_pair_exceptiont::what() const
7070
return res;
7171
}
7272

73-
#include <iostream>
74-
7573
static std::pair<irep_idt, irep_idt>
7674
parse_function_contract_pair(const irep_idt &cli_flag)
7775
{

src/goto-instrument/contracts/dynamic-frames/dfcc_cfg_info.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,14 +628,25 @@ void dfcc_cfg_infot::output(std::ostream &out) const
628628
}
629629
}
630630

631+
std::size_t dfcc_cfg_infot::get_first_id_not_skipped_or_top_level_id(
632+
const std::size_t loop_id) const
633+
{
634+
if(is_top_level_id(loop_id) || !get_loop_info(loop_id).must_skip())
635+
{
636+
return loop_id;
637+
}
638+
return get_first_id_not_skipped_or_top_level_id(
639+
get_outer_loop_identifier(loop_id).value_or(top_level_id()));
640+
}
641+
631642
const exprt &
632643
dfcc_cfg_infot::get_write_set(goto_programt::const_targett target) const
633644
{
634645
auto loop_id_opt = dfcc_get_loop_id(target);
635646
PRECONDITION(
636647
loop_id_opt.has_value() &&
637648
is_valid_loop_or_top_level_id(loop_id_opt.value()));
638-
auto loop_id = loop_id_opt.value();
649+
auto loop_id = get_first_id_not_skipped_or_top_level_id(loop_id_opt.value());
639650
if(is_top_level_id(loop_id))
640651
{
641652
return top_level_write_set;
@@ -734,6 +745,11 @@ bool dfcc_cfg_infot::is_top_level_id(const std::size_t id) const
734745
return id == loop_info_map.size();
735746
}
736747

748+
size_t dfcc_cfg_infot::top_level_id() const
749+
{
750+
return loop_info_map.size();
751+
}
752+
737753
bool dfcc_cfg_infot::must_track_decl_or_dead(
738754
goto_programt::const_targett target) const
739755
{
@@ -743,7 +759,6 @@ bool dfcc_cfg_infot::must_track_decl_or_dead(
743759
auto &tracked = get_tracked_set(target);
744760
return tracked.find(ident) != tracked.end();
745761
}
746-
#include <iostream>
747762

748763
/// Returns true if the lhs to an assignment must be checked against its write
749764
/// set. The set of locally declared identifiers and the subset of that that

src/goto-instrument/contracts/dynamic-frames/dfcc_cfg_info.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ class dfcc_loop_infot
144144
/// this loop. This is the one that must be passed as argument to write set
145145
/// functions.
146146
const symbol_exprt addr_of_write_set_var;
147+
148+
/// Returns true if the loop has no invariant and no decreases clause
149+
/// and its instrumentation must be skipped.
150+
const bool must_skip() const
151+
{
152+
return invariant.is_nil() && decreases.empty();
153+
}
147154
};
148155

149156
/// Computes natural loops, enforces normal form conditions, computes the
@@ -248,7 +255,9 @@ class dfcc_cfg_infot
248255
/// \brief Returns the loop info for that loop_id.
249256
const dfcc_loop_infot &get_loop_info(const std::size_t loop_id) const;
250257

251-
/// \brief Returns the write set variable for that instruction.
258+
/// \brief Returns the write set variable to use for the given instruction
259+
/// Returns the write set for the loop, or one of the outer loops,
260+
/// or top level, passing through all loops that are [must_skip]
252261
const exprt &get_write_set(goto_programt::const_targett target) const;
253262

254263
/// \brief Returns the set of local variable for the scope where that
@@ -270,6 +279,11 @@ class dfcc_cfg_infot
270279
return topsorted_loops;
271280
}
272281

282+
/// \brief Returns the id of the first outer loop (including this one) that
283+
/// is not skipped, or the top level id.
284+
std::size_t
285+
get_first_id_not_skipped_or_top_level_id(const std::size_t loop_id) const;
286+
273287
/// Finds the DFCC id of the loop that contains the given loop, returns
274288
/// nullopt when the loop has no outer loop.
275289
const std::optional<std::size_t>
@@ -314,6 +328,9 @@ class dfcc_cfg_infot
314328
/// True iff \p id is in the valid range for a loop id for this function.
315329
bool is_top_level_id(const std::size_t id) const;
316330

331+
/// Returns the top level ID
332+
size_t top_level_id() const;
333+
317334
/// Loop identifiers sorted from most deeply nested to less deeply nested
318335
std::vector<std::size_t> topsorted_loops;
319336

src/goto-instrument/contracts/dynamic-frames/dfcc_instrument.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,7 @@ void dfcc_instrumentt::apply_loop_contracts(
12391239
for(const auto &loop_id : cfg_info.get_loops_toposorted())
12401240
{
12411241
const auto &loop = cfg_info.get_loop_info(loop_id);
1242-
if(loop.invariant.is_nil() && loop.decreases.empty())
1242+
if(loop.must_skip())
12431243
{
12441244
// skip loops that do not have contracts
12451245
log.warning() << "loop " << function_id << "." << loop.cbmc_loop_id

0 commit comments

Comments
 (0)