Skip to content

Use BDDs for encoding symex guards #3730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ jobs:
- cmake --build build -- -j4
script: (cd build; bin/unit "[core][irept]")

# cmake build using g++-7, enable CMAKE_USE_CUDD
# cmake build using g++-7, enable CMAKE_USE_CUDD and BDD_GUARDS
- stage: Test different OS/CXX/Flags
os: linux
sudo: false
Expand All @@ -223,7 +223,7 @@ jobs:
install:
- ccache -z
- ccache --max-size=1G
- cmake -H. -Bbuild '-DCMAKE_BUILD_TYPE=Release' '-DCMAKE_CXX_COMPILER=/usr/bin/g++-7' '-DCMAKE_USE_CUDD=true'
- cmake -H. -Bbuild '-DCMAKE_BUILD_TYPE=Release' '-DCMAKE_CXX_COMPILER=/usr/bin/g++-7' '-DCMAKE_USE_CUDD=true' -DCMAKE_CXX_FLAGS="-DBDD_GUARDS"
- git submodule update --init --recursive
- cmake --build build -- -j4
script: (cd build; ctest -V -L CORE -j2)
Expand Down
19 changes: 19 additions & 0 deletions COMPILING.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,22 @@ If compiling with cmake:
```
cmake --build build
```

## Use BDDs for guards

There are two implementation for symex guards. The default one uses the
internal representation of expression. The other one uses BDDs and
though experimental, it is expected to have better performance,
in particular when used in conjunction with CUDD.

To use the BDD implementation of guards, add the `BDD_GUARDS`
compilation flag:
* If compiling with make:
```
make -C src CXXFLAGS="-O2 -DBDD_GUARDS"
```
* If compiling with CMake:
```
cmake -H. -Bbuild -DCMAKE_CXX_FLAGS="-DBDD_GUARDS"
```
and then `cmake --build build`
21 changes: 15 additions & 6 deletions jbmc/regression/jbmc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@ add_test_pl_tests(
"$<TARGET_FILE:jbmc> --validate-goto-model --validate-ssa-equation"
)

add_test_pl_profile(
"jbmc-symex-driven-lazy-loading"
"$<TARGET_FILE:jbmc> --validate-goto-model --validate-ssa-equation --symex-driven-lazy-loading"
"-C;-X;symex-driven-lazy-loading-expected-failure;-s;symex-driven-loading"
"CORE"
)
if(DEFINED BDD_GUARDS)
add_test_pl_profile(
"jbmc-symex-driven-lazy-loading"
"$<TARGET_FILE:jbmc> --validate-goto-model --validate-ssa-equation --symex-driven-lazy-loading"
"-C;-X;symex-driven-lazy-loading-expected-failure;-s;symex-driven-loading"
"CORE"
)
else()
add_test_pl_profile(
"jbmc-symex-driven-lazy-loading"
"$<TARGET_FILE:jbmc> --validate-goto-model --validate-ssa-equation --symex-driven-lazy-loading"
"-C;-X;symex-driven-lazy-loading-expected-failure;-X;bdd-expected-timeout;-s;symex-driven-loading"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will need some equivalent thereof in the Makefile. Something like ifeq ($(filter %DBDD_GUARDS, $(CXXFLAGS)),)

"CORE"
)
endif()
8 changes: 8 additions & 0 deletions jbmc/regression/jbmc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@ include ../../src/config.inc

test:
@../$(CPROVER_DIR)/regression/test.pl -e -p -c "../../../src/jbmc/jbmc --validate-goto-model --validate-ssa-equation"
ifeq ($(filter %DBDD_GUARDS, $(CXXFLAGS)),)
@../$(CPROVER_DIR)/regression/test.pl -e -p -c "../../../src/jbmc/jbmc --validate-goto-model --validate-ssa-equation --symex-driven-lazy-loading" -X symex-driven-lazy-loading-expected-failure -s symex-driven-loading
else
@../$(CPROVER_DIR)/regression/test.pl -e -p -c "../../../src/jbmc/jbmc --validate-goto-model --validate-ssa-equation --symex-driven-lazy-loading" -X symex-driven-lazy-loading-expected-failure -X bdd-expected-timeout -s symex-driven-loading
endif

tests.log: ../$(CPROVER_DIR)/regression/test.pl
@../$(CPROVER_DIR)/regression/test.pl -e -p -c "../../../src/jbmc/jbmc --validate-goto-model --validate-ssa-equation"
ifeq ($(filter %DBDD_GUARDS, $(CXXFLAGS)),)
@../$(CPROVER_DIR)/regression/test.pl -e -p -c "../../../src/jbmc/jbmc --validate-goto-model --validate-ssa-equation --symex-driven-lazy-loading" -X symex-driven-lazy-loading-expected-failure -s symex-driven-loading
else
@../$(CPROVER_DIR)/regression/test.pl -e -p -c "../../../src/jbmc/jbmc --validate-goto-model --validate-ssa-equation --symex-driven-lazy-loading" -X symex-driven-lazy-loading-expected-failure -X bdd-expected-timeout -s symex-driven-loading
endif

show:
@for dir in *; do \
Expand Down
7 changes: 6 additions & 1 deletion jbmc/regression/jbmc/class-literals/test.desc
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
CORE
CORE bdd-expected-timeout
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please include a comment at the bottom of this file what the behaviour with BDDs is? Is it taking minutes, hours, days? And maybe also an idea why that's happening on this particular test?

Test.class
--function Test.main
^EXIT=0$
^SIGNAL=0$
^VERIFICATION SUCCESSFUL$
--
^warning: ignoring
--
This test is deactivated for the build with BDDs because it takes more
than 10 minutes for the test to complete.
This is due to the size of the BDD representing the guards growing more than
they do when represented as exprt.
6 changes: 5 additions & 1 deletion src/analyses/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ add_library(analyses ${sources})

generic_includes(analyses)

target_link_libraries(analyses util pointer-analysis)
if(CMAKE_USE_CUDD)
target_include_directories(analyses PUBLIC ${CUDD_INCLUDE}/cudd/)
endif()

target_link_libraries(analyses util pointer-analysis solvers)
2 changes: 2 additions & 0 deletions src/analyses/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ SRC = ai.cpp \
global_may_alias.cpp \
goto_check.cpp \
goto_rw.cpp \
guard_bdd.cpp \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "bdd" should appear before "expr", at least according to my knowledge of the alphabet ;-)

guard_expr.cpp \
interval_analysis.cpp \
interval_domain.cpp \
invariant_propagation.cpp \
Expand Down
11 changes: 6 additions & 5 deletions src/analyses/goto_check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ Author: Daniel Kroening, [email protected]
#include <util/cprover_prefix.h>
#include <util/expr_util.h>
#include <util/find_symbols.h>
#include <util/guard.h>
#include <util/ieee_float.h>
#include <util/invariant.h>
#include <util/make_unique.h>
Expand All @@ -36,6 +35,7 @@ Author: Daniel Kroening, [email protected]

#include <goto-programs/remove_skip.h>

#include "guard.h"
#include "local_bitvector_analysis.h"

class goto_checkt
Expand Down Expand Up @@ -84,6 +84,7 @@ class goto_checkt
const namespacet &ns;
std::unique_ptr<local_bitvector_analysist> local_bitvector_analysis;
goto_programt::const_targett current_target;
guard_managert guard_manager;

void check_rec(
const exprt &expr,
Expand Down Expand Up @@ -1650,7 +1651,7 @@ void goto_checkt::check_rec(const exprt &expr, guardt &guard, bool address)

void goto_checkt::check(const exprt &expr)
{
guardt guard{true_exprt{}};
guardt guard{true_exprt{}, guard_manager};
check_rec(expr, guard, false);
}

Expand Down Expand Up @@ -1782,7 +1783,7 @@ void goto_checkt::goto_check(
"pointer dereference",
i.source_location,
pointer,
guardt(true_exprt()));
guardt(true_exprt(), guard_manager));
}
}

Expand Down Expand Up @@ -1820,7 +1821,7 @@ void goto_checkt::goto_check(
"pointer dereference",
i.source_location,
pointer,
guardt(true_exprt()));
guardt(true_exprt(), guard_manager));
}

// this has no successor
Expand Down Expand Up @@ -1897,7 +1898,7 @@ void goto_checkt::goto_check(
"memory-leak",
source_location,
eq,
guardt(true_exprt()));
guardt(true_exprt(), guard_manager));
}
}

Expand Down
14 changes: 9 additions & 5 deletions src/analyses/goto_rw.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Date: April 2010
#include <map>
#include <memory> // unique_ptr

#include <util/guard.h>
#include "guard.h"

#include <goto-programs/goto_model.h>

Expand Down Expand Up @@ -352,8 +352,11 @@ class rw_guarded_range_set_value_sett:public rw_range_set_value_sett
public:
rw_guarded_range_set_value_sett(
const namespacet &_ns,
value_setst &_value_sets):
rw_range_set_value_sett(_ns, _value_sets)
value_setst &_value_sets,
guard_managert &guard_manager)
: rw_range_set_value_sett(_ns, _value_sets),
guard_manager(guard_manager),
guard(true_exprt(), guard_manager)
{
}

Expand All @@ -370,7 +373,7 @@ class rw_guarded_range_set_value_sett:public rw_range_set_value_sett
get_modet mode,
const exprt &expr) override
{
guard = guardt(true_exprt());
guard = guardt(true_exprt(), guard_manager);

rw_range_set_value_sett::get_objects_rec(_function, _target, mode, expr);
}
Expand All @@ -384,7 +387,8 @@ class rw_guarded_range_set_value_sett:public rw_range_set_value_sett
}

protected:
guardt guard = guardt(true_exprt());
guard_managert &guard_manager;
guardt guard;

using rw_range_sett::get_objects_rec;

Expand Down
31 changes: 31 additions & 0 deletions src/analyses/guard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*******************************************************************\

Module: Guard Data Structure

Author: Daniel Kroening, [email protected]

\*******************************************************************/

/// \file
/// Guard Data Structure

#ifndef CPROVER_ANALYSES_GUARD_H
#define CPROVER_ANALYSES_GUARD_H

#ifdef BDD_GUARDS

#include "guard_bdd.h"

using guard_managert = bdd_exprt;
using guardt = guard_bddt;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible for them to please have a shared interface? Otherwise inconsistencies will creep in that can only be detected by compiling both with and without BDD_GUARDS set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm reluctant to use virtual functions since they are not really needed here. I could try doing that using CRTP if you think that's a good idea (guard_bdd_managert would inherit from guard_interfacet<guard_bdd_managert>).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we had the same discussion on the CUDD/miniBDD BDD PR :-) It's not the most important of all issues and can be left to a future PR - but I will remind you about it whenever there is a change to these interfaces and someone forgets to update both of them :-P

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough


#else

#include "guard_expr.h"

using guard_managert = guard_expr_managert;
using guardt = guard_exprt;

#endif // BDD_GUARDS

#endif // CPROVER_ANALYSES_GUARD_H
94 changes: 94 additions & 0 deletions src/analyses/guard_bdd.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*******************************************************************\

Module: Guard Data Structure

Author: Romain Brenguier, [email protected]

\*******************************************************************/

/// \file
/// Implementation of guards using BDDs

#include "guard_bdd.h"

#include <algorithm>
#include <ostream>
#include <set>

#include <solvers/bdd/bdd.h>
#include <solvers/prop/bdd_expr.h>
#include <util/expr_util.h>
#include <util/invariant.h>
#include <util/make_unique.h>
#include <util/namespace.h>
#include <util/simplify_utils.h>
#include <util/std_expr.h>
#include <util/symbol_table.h>

guard_bddt::guard_bddt(const exprt &e, bdd_exprt &manager)
: manager(manager), bdd(manager.from_expr(e))
{
}

guard_bddt &guard_bddt::operator=(const guard_bddt &other)
{
PRECONDITION(&manager == &other.manager);
bdd = other.bdd;
return *this;
}

guard_bddt &guard_bddt::operator=(guard_bddt &&other)
{
PRECONDITION(&manager == &other.manager);
std::swap(bdd, other.bdd);
return *this;
}

exprt guard_bddt::guard_expr(exprt expr) const
{
if(is_true())
{
// do nothing
return expr;
}
else
{
if(expr.is_false())
{
return boolean_negate(as_expr());
}
else
{
return implies_exprt{as_expr(), expr};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::move(expr) since it's consumed by value.

And I would get rid of a lot of braces now that the branches are one-liners.

}
}
}

guard_bddt &guard_bddt::append(const guard_bddt &guard)
{
bdd = bdd.bdd_and(guard.bdd);
return *this;
}

guard_bddt &guard_bddt::add(const exprt &expr)
{
bdd = bdd.bdd_and(manager.from_expr(expr));
return *this;
}

guard_bddt &operator-=(guard_bddt &g1, const guard_bddt &g2)
{
g1.bdd = g1.bdd.constrain(g2.bdd.bdd_or(g1.bdd));
return g1;
}

guard_bddt &operator|=(guard_bddt &g1, const guard_bddt &g2)
{
g1.bdd = g1.bdd.bdd_or(g2.bdd);
return g1;
}

exprt guard_bddt::as_expr() const
{
return manager.as_expr(bdd);
}
Loading