Skip to content

Commit e24435f

Browse files
author
Remi Delmas
committed
Function contracts: adding a loop-freeness check before assigns clause checking instrumentation
1 parent 4a4a08c commit e24435f

File tree

2 files changed

+84
-4
lines changed

2 files changed

+84
-4
lines changed

src/goto-instrument/contracts/contracts.cpp

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Date: February 2016
4040
#include <util/pointer_offset_size.h>
4141
#include <util/pointer_predicates.h>
4242
#include <util/replace_symbol.h>
43+
#include <util/simplify_expr.h>
4344
#include <util/std_code.h>
4445

4546
#include "memory_predicates.h"
@@ -1129,7 +1130,9 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11291130
return true;
11301131
}
11311132

1132-
if(check_for_looped_mallocs(function_obj->second.body))
1133+
auto &goto_program = function_obj->second.body;
1134+
1135+
if(check_for_looped_mallocs(goto_program))
11331136
{
11341137
return true;
11351138
}
@@ -1150,14 +1153,17 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11501153
for(const auto &param : to_code_type(target.type).parameters())
11511154
assigns.add_to_write_set(ns.lookup(param.get_identifier()).symbol_expr());
11521155

1153-
auto instruction_it = function_obj->second.body.instructions.begin();
1156+
auto instruction_it = goto_program.instructions.begin();
11541157
for(const auto &car : assigns.get_write_set())
11551158
{
11561159
auto snapshot_instructions = car.generate_snapshot_instructions();
11571160
insert_before_swap_and_advance(
1158-
function_obj->second.body, instruction_it, snapshot_instructions);
1161+
goto_program, instruction_it, snapshot_instructions);
11591162
};
11601163

1164+
// restore internal coherence in the programs
1165+
goto_functions.update();
1166+
11611167
// Full inlining of the function body
11621168
// Inlining is performed so that function calls to a same function
11631169
// occurring under different write sets get instrumented specifically
@@ -1169,9 +1175,17 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11691175
decorated.get_recursive_function_warnings_count() == 0,
11701176
"Recursive functions found during inlining");
11711177

1172-
// restore internal invariants
1178+
// clean up possible fake loops that are due to `IF 0!=0 GOTO i` instructions
1179+
simplify_gotos(goto_program);
1180+
1181+
// restore internal coherence in the programs
11731182
goto_functions.update();
11741183

1184+
INVARIANT(
1185+
is_loop_free(goto_program),
1186+
"Loops remain in function '" + id2string(function) +
1187+
"', assigns clause checking instrumentation cannot be applied.");
1188+
11751189
// Insert write set inclusion checks.
11761190
check_frame_conditions(
11771191
function_obj->first,
@@ -1637,3 +1651,62 @@ bool code_contractst::is_auxiliary_decl_dead_or_assign(
16371651
return false;
16381652
}
16391653

1654+
void code_contractst::simplify_gotos(goto_programt &goto_program)
1655+
{
1656+
for(auto &instruction : goto_program.instructions)
1657+
{
1658+
if(instruction.is_goto())
1659+
{
1660+
const auto &condition = instruction.get_condition();
1661+
const auto &simplified = simplify_expr(condition, ns);
1662+
if(simplified.is_false())
1663+
instruction.turn_into_skip();
1664+
}
1665+
}
1666+
}
1667+
1668+
bool code_contractst::is_loop_free(const goto_programt &goto_program)
1669+
{
1670+
// create cfg from instruction list
1671+
cfg_baset<empty_cfg_nodet> cfg;
1672+
cfg(goto_program);
1673+
1674+
// check that all nodes are there
1675+
INVARIANT(
1676+
goto_program.instructions.size() == cfg.size(),
1677+
"Instruction list vs CFG size mismatch.");
1678+
1679+
// compute SCCs
1680+
std::vector<node_indext> node_to_scc(cfg.size(), -1);
1681+
auto nof_sccs = cfg.SCCs(node_to_scc);
1682+
1683+
// compute size of each SCC
1684+
std::vector<int> scc_size(nof_sccs, 0);
1685+
for(auto scc : node_to_scc)
1686+
{
1687+
INVARIANT(
1688+
0 <= scc && scc < nof_sccs, "Could not determine SCC for instruction");
1689+
scc_size[scc]++;
1690+
}
1691+
1692+
// check they are all of size 1
1693+
for(size_t scc_id = 0; scc_id < nof_sccs; scc_id++)
1694+
{
1695+
auto size = scc_size[scc_id];
1696+
if(size > 1)
1697+
{
1698+
log.error() << "Found CFG SCC with size " << size << messaget::eom;
1699+
for(const auto &node_id : node_to_scc)
1700+
{
1701+
if(node_to_scc[node_id] == scc_id)
1702+
{
1703+
const auto &pc = cfg[node_id].PC;
1704+
goto_program.output_instruction(ns, "", log.error(), *pc);
1705+
log.error() << messaget::eom;
1706+
}
1707+
}
1708+
return false;
1709+
}
1710+
}
1711+
return true;
1712+
}

src/goto-instrument/contracts/contracts.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@ class code_contractst
223223
bool is_auxiliary_decl_dead_or_assign(
224224
const goto_programt::targett &instruction_it);
225225

226+
/// Turns goto instructions `IF cond GOTO label` where the condition
227+
/// statically simplifies to `false` into SKIP instructions.
228+
void simplify_gotos(goto_programt &goto_program);
229+
230+
/// Returns true iff the given program is loop-free,
231+
/// i.e. if each SCC of its CFG contains a single element.
232+
bool is_loop_free(const goto_programt &goto_program);
226233
};
227234

228235
#endif // CPROVER_GOTO_INSTRUMENT_CONTRACTS_CONTRACTS_H

0 commit comments

Comments
 (0)