Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions regression/k-induction/nested-loops/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Test case for nested loops in k-induction instrumentation.
// This used to cause segmentation faults because the code incorrectly assumed
// loop heads always have conditions and didn't handle nested loop iterator
// invalidation.
int main()
{
unsigned i = 0;
unsigned j;
while(i < 2)
{
j = 0;
while(j < 2)
{
j++;
}
__CPROVER_assert(j == 2, "inner loop terminates at 2");
i++;
}
}
11 changes: 11 additions & 0 deletions regression/k-induction/nested-loops/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CORE
main.c
2
^EXIT=0$
^SIGNAL=0$
^Instrumenting k-induction for k=2, base case$
^Instrumenting k-induction for k=2, step case$
^## Base case passes$
^## Step case passes$
--
^warning: ignoring
75 changes: 65 additions & 10 deletions src/goto-instrument/k_induction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ Author: Daniel Kroening, kroening@kroening.com

#include "k_induction.h"

#include <analyses/natural_loops.h>
#include <analyses/local_may_alias.h>
#include <util/expr_util.h>
#include <util/std_expr.h>

#include <goto-programs/remove_skip.h>

#include <analyses/local_may_alias.h>
#include <analyses/natural_loops.h>

#include "havoc_utils.h"
#include "loop_utils.h"
#include "unwind.h"
Expand Down Expand Up @@ -55,24 +58,61 @@ class k_inductiont

void k_induction();

static exprt find_loop_guard(
goto_programt::targett loop_head,
goto_programt::targett loop_exit,
const loopt &loop);

void process_loop(
const goto_programt::targett loop_head,
const loopt &);
};

/// Find the loop guard expression. The condition can be in one of two places:
/// 1. In the backwards goto at the end of the loop (the backedge)
/// 2. In a forward goto at the loop head that targets the loop exit
exprt k_inductiont::find_loop_guard(
const goto_programt::targett loop_head,
const goto_programt::targett loop_exit,
const loopt &loop)
{
// Find the backwards goto (backedge) for this loop
goto_programt::targett backedge;
bool found_backedge = false;
for(const auto &t : loop)
{
if(t->is_backwards_goto() && t->get_target() == loop_head)
{
backedge = t;
found_backedge = true;
break;
}
}

if(found_backedge && !backedge->condition().is_true())
return backedge->condition();

if(loop_head->is_goto())
{
if(loop_head->get_target() == loop_exit)
return boolean_negate(loop_head->condition());
}

return true_exprt();
}

void k_inductiont::process_loop(
const goto_programt::targett loop_head,
const loopt &loop)
{
PRECONDITION(!loop.empty());

// save the loop guard
const exprt loop_guard = loop_head->condition();

// compute the loop exit
goto_programt::targett loop_exit=
get_loop_exit(loop);

const exprt loop_guard = find_loop_guard(loop_head, loop_exit, loop);

if(base_case)
{
// now unwind k times
Expand Down Expand Up @@ -157,12 +197,27 @@ void k_inductiont::process_loop(
void k_inductiont::k_induction()
{
// iterate over the (natural) loops in the function
// Processing a loop modifies the goto program (inserting instructions and
// removing skips), which invalidates iterators in the natural_loops map for
// nested loops. We therefore only process outermost loops; inner loops are
// handled as part of the outer loop body during unwinding.

for(natural_loops_mutablet::loop_mapt::const_iterator
l_it=natural_loops.loop_map.begin();
l_it!=natural_loops.loop_map.end();
l_it++)
process_loop(l_it->first, l_it->second);
for(const auto &[loop_head, loop] : natural_loops.loop_map)
{
bool is_nested = false;

for(const auto &[other_head, other_loop] : natural_loops.loop_map)
{
if(other_head != loop_head && other_loop.contains(loop_head))
{
is_nested = true;
break;
}
}
Comment thread
tautschnig marked this conversation as resolved.

if(!is_nested)
process_loop(loop_head, loop);
}
Comment thread
tautschnig marked this conversation as resolved.
}

void k_induction(
Expand Down
Loading