Skip to content

Commit b8fab46

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

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
@@ -39,6 +39,7 @@ Date: February 2016
3939
#include <util/pointer_offset_size.h>
4040
#include <util/pointer_predicates.h>
4141
#include <util/replace_symbol.h>
42+
#include <util/simplify_expr.h>
4243
#include <util/std_code.h>
4344

4445
#include "memory_predicates.h"
@@ -1105,7 +1106,9 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11051106
return true;
11061107
}
11071108

1108-
if(check_for_looped_mallocs(function_obj->second.body))
1109+
auto &goto_program = function_obj->second.body;
1110+
1111+
if(check_for_looped_mallocs(goto_program))
11091112
{
11101113
return true;
11111114
}
@@ -1126,14 +1129,17 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11261129
for(const auto &param : to_code_type(target.type).parameters())
11271130
assigns.add_to_write_set(ns.lookup(param.get_identifier()).symbol_expr());
11281131

1129-
auto instruction_it = function_obj->second.body.instructions.begin();
1132+
auto instruction_it = goto_program.instructions.begin();
11301133
for(const auto &car : assigns.get_write_set())
11311134
{
11321135
auto snapshot_instructions = car.generate_snapshot_instructions();
11331136
insert_before_swap_and_advance(
1134-
function_obj->second.body, instruction_it, snapshot_instructions);
1137+
goto_program, instruction_it, snapshot_instructions);
11351138
};
11361139

1140+
// restore internal coherence in the programs
1141+
goto_functions.update();
1142+
11371143
// Full inlining of the function body
11381144
// Inlining is performed so that function calls to a same function
11391145
// occurring under different write sets get instrumented specifically
@@ -1145,9 +1151,17 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11451151
decorated.get_recursive_function_warnings_count() == 0,
11461152
"Recursive functions found during inlining");
11471153

1148-
// restore internal invariants
1154+
// clean up possible fake loops that are due to `IF 0!=0 GOTO i` instructions
1155+
simplify_gotos(goto_program);
1156+
1157+
// restore internal coherence in the programs
11491158
goto_functions.update();
11501159

1160+
INVARIANT(
1161+
is_loop_free(goto_program),
1162+
"Loops remain in function '" + id2string(function) +
1163+
"', assigns clause checking instrumentation cannot be applied.");
1164+
11511165
// Insert write set inclusion checks.
11521166
check_frame_conditions(
11531167
function_obj->first,
@@ -1613,3 +1627,62 @@ bool code_contractst::is_auxiliary_decl_dead_or_assign(
16131627
return false;
16141628
}
16151629

1630+
void code_contractst::simplify_gotos(goto_programt &goto_program)
1631+
{
1632+
for(auto &instruction : goto_program.instructions)
1633+
{
1634+
if(instruction.is_goto())
1635+
{
1636+
const auto &condition = instruction.get_condition();
1637+
const auto &simplified = simplify_expr(condition, ns);
1638+
if(simplified.is_false())
1639+
instruction.turn_into_skip();
1640+
}
1641+
}
1642+
}
1643+
1644+
bool code_contractst::is_loop_free(const goto_programt &goto_program)
1645+
{
1646+
// create cfg from instruction list
1647+
cfg_baset<empty_cfg_nodet> cfg;
1648+
cfg(goto_program);
1649+
1650+
// check that all nodes are there
1651+
INVARIANT(
1652+
goto_program.instructions.size() == cfg.size(),
1653+
"Instruction list vs CFG size mismatch.");
1654+
1655+
//compute SCCs
1656+
std::vector<node_indext> node_to_scc(cfg.size(), -1);
1657+
auto nof_sccs = cfg.SCCs(node_to_scc);
1658+
1659+
// compute size of each SCC
1660+
std::vector<int> scc_size(nof_sccs, 0);
1661+
for(auto scc : node_to_scc)
1662+
{
1663+
INVARIANT(
1664+
0 <= scc && scc < nof_sccs, "Could not determine SCC for instruction");
1665+
scc_size[scc]++;
1666+
}
1667+
1668+
// check they are all of size 1
1669+
for(int scc_id = 0; scc_id < nof_sccs; scc_id++)
1670+
{
1671+
auto size = scc_size[scc_id];
1672+
if(size > 1)
1673+
{
1674+
log.error() << "Found CFG SCC with size " << size << messaget::eom;
1675+
for(const auto &node_id : node_to_scc)
1676+
{
1677+
if(node_to_scc[node_id] == scc_id)
1678+
{
1679+
const auto &pc = cfg[node_id].PC;
1680+
goto_program.output_instruction(ns, "", log.error(), *pc);
1681+
log.error() << messaget::eom;
1682+
}
1683+
}
1684+
return false;
1685+
}
1686+
}
1687+
return true;
1688+
}

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)