From b4b738f3272aaf7f2707777e6e9a7a3e7938d88c Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Fri, 2 Jul 2021 18:42:25 -0400 Subject: [PATCH 01/32] Automatically encoded tiniest of fragments! --- include/ilang/ila-mngr/u_unroller_smt.h | 5 + include/ilang/ila-mngr/v_eq_check_progfrag.h | 202 ++++++++++ src/ila-mngr/v_eq_check_pfrag.cc | 379 +++++++++++++++++++ test/t_pfrag.cc | 133 +++++++ 4 files changed, 719 insertions(+) create mode 100644 include/ilang/ila-mngr/v_eq_check_progfrag.h create mode 100644 src/ila-mngr/v_eq_check_pfrag.cc create mode 100644 test/t_pfrag.cc diff --git a/include/ilang/ila-mngr/u_unroller_smt.h b/include/ilang/ila-mngr/u_unroller_smt.h index 446b71a9c..a9b8d05a0 100644 --- a/include/ilang/ila-mngr/u_unroller_smt.h +++ b/include/ilang/ila-mngr/u_unroller_smt.h @@ -59,6 +59,11 @@ template class UnrollerSmt { return smt_gen_.GetShimFunc(func); } + /// Return the deciding variables used for the previous unrolling. + inline const ExprPtrVec& GetLastDecidingVars() { + return deciding_vars_; + } + private: // ------------------------- MEMBERS -------------------------------------- // /// The underlying (templated) SMT generator. diff --git a/include/ilang/ila-mngr/v_eq_check_progfrag.h b/include/ilang/ila-mngr/v_eq_check_progfrag.h new file mode 100644 index 000000000..47fc74058 --- /dev/null +++ b/include/ilang/ila-mngr/v_eq_check_progfrag.h @@ -0,0 +1,202 @@ +/// \file +/// Program-fragment-based ILA equivalence checking using +/// constrained horn clauses. + +#ifndef ILANG_ILA_MNGR_V_EQ_CHECK_PROGFRAG_H__ +#define ILANG_ILA_MNGR_V_EQ_CHECK_PROGFRAG_H__ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +/// \namespace ilang +namespace ilang { + + /// \namespace pfast + /// Defines the program-fragment AST + namespace pfast { + + using Constraint = ExprPtr; + + struct Assert; + struct Assume; + struct Call; + // struct Update; + + using Stmt = std::variant; + + /// Converts to a string for pretty printing + std::string to_string(const Stmt& s); + + struct Block : public std::vector { + using base=std::vector; + using base::base; + using base::operator=; + }; + + /// Checks structural equality + bool operator==(const Block& a, const Block& b); + + /// Converts to a string for pretty printing + std::string to_string(const Block& b); + + struct ProgramFragment { + ExprSet params; + Block body; + }; + + /// Checks structural equality + bool operator==(const ProgramFragment& a, const ProgramFragment& b); + + /// Converts to a string for pretty printing + std::string to_string(const ProgramFragment& pf); + + struct Assert { + const Constraint assertion; + /// Checks structural equality + bool operator==(const Assert& b) const { + return asthub::TopEq(assertion, b.assertion); + } + }; + + struct Assume { + const Constraint assumption; + /// Checks structural equality + bool operator==(const Assume& b) const { + return asthub::TopEq(assumption, b.assumption); + } + }; + + struct Call { + const InstrPtr instr; + const Constraint input_constraint; + + /// Checks structural equality + bool operator==(const Call& b) const { + return (instr == b.instr) + && (bool(input_constraint) == bool(b.input_constraint)) + && (!input_constraint || asthub::TopEq(input_constraint, b.input_constraint)); + } + }; + + /// \class PrettyPrinter + /// A visitor for pretty-printing program-fragment AST elements + class PrettyPrinter { + public: + PrettyPrinter(): buf_ {} {} + ~PrettyPrinter()=default; + void operator()(const ProgramFragment& pf); + void operator()(const Block& b); + + void operator()(const Assert& a); + void operator()(const Assume& a); + void operator()(const Call& c); + + std::string str() { return buf_.str(); } + private: + std::ostringstream buf_; + int indent_; + + std::string reserved_word(const std::string& word); + std::string indent_str(); + }; + + } + + /// \namespace pgraph + /// Expresses program fragments using cut-point graphs + namespace pgraph { + + using Location = std::string; + constexpr char LOC_BEGIN[] = "begin"; + constexpr char LOC_ERROR[] = "fail"; + + using BasicStmt = std::variant; + using StmtSeq = std::vector; + + using Edge = StmtSeq; + + struct EdgeTo { + Edge edge; + Location to; + }; + + using Edges = std::vector; + + using CutPointGraph = std::unordered_map; + + CutPointGraph program_graph(const pfast::ProgramFragment& pf); + + } + + // TODO: Use an unroller to convert sequences of + // instructions to predicates + // Should I create a program fragment unroller? + // Decision: use PathUnroller.Unroll, PathUnroller.GetSMTCurrent + class PFToCHCEncoder { + + using Predicate = z3::func_decl; + + public: + PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pfast::ProgramFragment& pf); + ~PFToCHCEncoder()=default; + + std::string to_string(); + + z3::check_result check_assertions() { + z3::expr q = get_error_query(); + // z3::func_decl_vector q = get_error_query(); + return ctxfp_.query(q); + } + + private: + const InstrLvlAbsPtr ila_; + const pgraph::CutPointGraph pg_; + const ExprSet params_; + ExprPtrVec pred_scope_; + ExprSet total_scope_; + + z3::context ctx_ {}; + z3::fixedpoint ctxfp_ {ctx_}; + + std::unordered_map loc_predicates_; + int max_step_count_ {0}; + + constexpr static char PRED_END_SUFFIX[] = "end"; + + // internal constructor + PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params); + + void encode(); + + void encode_transition( + const pgraph::Location& start, + const pgraph::StmtSeq& transition, + const pgraph::Location& end); + + void record_scopes(const pgraph::CutPointGraph& pg, const ExprSet& params); + void add_unique_vars( + const pgraph::BasicStmt& s, ExprSet& pred_scope, ExprSet& total_scope); + + ExprPtrVec get_scope(const pgraph::Location& l) { + return pred_scope_; // for now, same scope for the whole graph + } + + ExprPtr get_or_make_loc_predicate(const std::string& name); + ExprPtr new_predicate(const std::string& name, const ExprPtrVec& args); + + z3::expr get_error_query(); + // z3::func_decl_vector get_error_query(); + + }; + +} + +#endif \ No newline at end of file diff --git a/src/ila-mngr/v_eq_check_pfrag.cc b/src/ila-mngr/v_eq_check_pfrag.cc new file mode 100644 index 000000000..16979fe16 --- /dev/null +++ b/src/ila-mngr/v_eq_check_pfrag.cc @@ -0,0 +1,379 @@ +/// \file +/// Program fragment-based verification checking driver. + +#include + +#include + +#include +#include + +namespace ilang { + +namespace pfast { + + bool operator==(const Block& a, const Block& b) { + if (a.size() != b.size()) return false; + for (int i = 0; i != a.size(); ++i) { + if (a[i] == b[i]) continue; + return false; + } + return true; + } + + bool operator==(const ProgramFragment& a, const ProgramFragment& b) { + // [TODO] check == operator on unordered sets + return (a.params == b.params) && (a.body == b.body); + } + + std::string to_string(const Stmt& s) { + PrettyPrinter pp {}; + std::visit(pp, s); + return pp.str(); + } + + std::string to_string(const ProgramFragment& pf) { + PrettyPrinter pp {}; + pp(pf); + return pp.str(); + } + + std::string to_string(const Block& b) { + PrettyPrinter pp {}; + pp(b); + return pp.str(); + } + + void PrettyPrinter::operator()(const ProgramFragment& pf) { + buf_ << reserved_word("main") << "("; + bool first = true; + for (const auto& param : pf.params) { + if (first) first = false; + else buf_ << ", "; + buf_ << param; + } + buf_ << ") "; + + this->indent_ = 0; + (*this)(pf.body); + + buf_ << "\n"; + } + + void PrettyPrinter::operator()(const Block& b) { + buf_ << "{\n"; + this->indent_ += 2; + for (int i = 0; i != b.size(); ++i) { + buf_ << indent_str(); + std::visit(*this, b[i]); + buf_ << "\n"; + } + this->indent_ -= 2; + buf_ << indent_str() << "}"; + } + + void PrettyPrinter::operator()(const Assert& a) { + buf_ << reserved_word("assert") << " " << a.assertion; + } + + void PrettyPrinter::operator()(const Assume& a) { + buf_ << reserved_word("assert") << " " << a.assumption; + } + + void PrettyPrinter::operator()(const Call& c) { + buf_ << reserved_word("call") << " " << c.instr->name().str(); + if (c.input_constraint) { + buf_ << " where " << c.input_constraint; + } + } + + std::string PrettyPrinter::reserved_word(const std::string& word) { + std::string res = word; + for (auto & c: res) c = toupper(c); + return res; + } + + std::string PrettyPrinter::indent_str() { return std::string(this->indent_, ' '); } + +} + +namespace pgraph { + + using namespace pfast; + + struct PGraphBuilder { + + CutPointGraph get_pg() { return pg_; } + + void operator()(const ProgramFragment& pf) { + (*this)(pf.body); + } + + void operator()(const Block& b) { + for (const Stmt& s : b) std::visit(*this, s); + running_seq_to_newloc("end"); + } + + void operator()(const Call& c) { + running_seq_.emplace_back(c); + } + + void operator()(const Assume& a) { + running_seq_.emplace_back(a); + } + + void operator()(const Assert& a) { + running_seq_to_newloc("assert"); + add_edge_from_curloc({pfast::Assume{asthub::Not(a.assertion)}}, LOC_ERROR); + running_seq_.clear(); + running_seq_.emplace_back(Assume{a.assertion}); + } + + + private: + + CutPointGraph pg_ {}; + StmtSeq running_seq_ {}; + int loc_ctr_ {0}; + Location curloc_ {LOC_BEGIN}; + + void running_seq_to_newloc(const std::string& loc_ident) { + Location next = newloc(loc_ident); + add_edge_from_curloc(running_seq_, next); + set_curloc(next); + running_seq_.clear(); + } + + void add_edge_from_curloc(const Edge& edge, const Location& loc) { + Location cur = curloc(); + pg_.try_emplace(cur); // creates an Edges object at pg[cur] if none exists + EdgeTo new_edge {edge, loc}; + pg_[cur].push_back(new_edge); + } + + Location curloc() { return curloc_; } + void set_curloc(const Location& loc) { curloc_ = loc; } + + Location newloc(const std::string& loc_ident) { + return "loc" + std::to_string(loc_ctr_++) + "_" + loc_ident; + } + + }; + + CutPointGraph program_graph(const pfast::ProgramFragment& pf) { + PGraphBuilder builder {}; + builder(pf); + return builder.get_pg(); + } + +} + +template +class PathUnrollerCustomDecidingVars: public PathUnroller { + +public: + PathUnrollerCustomDecidingVars( + SmtShim& smt_shim, const ExprPtrVec& deciding_vars, + const std::string& suffix = ""): + PathUnroller(smt_shim, suffix), custom_deciding_vars_ {deciding_vars} {} + + ~PathUnrollerCustomDecidingVars()=default; + +protected: + void SetDecidingVars() { this->deciding_vars_ = custom_deciding_vars_; } + +private: + const ExprPtrVec custom_deciding_vars_; + +}; + + +PFToCHCEncoder::PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pfast::ProgramFragment& pf): + PFToCHCEncoder(ila, pgraph::program_graph(pf), pf.params) {} + +PFToCHCEncoder::PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params): + ila_ {ila}, pg_ {pg}, params_ {params} { + record_scopes(pg_, params_); + encode(); +} + + +std::string PFToCHCEncoder::to_string() { + z3::expr_vector queries {ctx_}; + z3::expr q = get_error_query(); + queries.push_back(q); + return ctxfp_.to_string(queries); // TODO: add query +} + + +void PFToCHCEncoder::encode() { + Z3ExprAdapter z3_adapter {ctx_}; + + // for (const auto& v : pred_scope_) { + // // auto v_start = z3_adapter.GetShimExpr(v, PRED_START_SUFFIX).decl(); + // auto v_end = z3_adapter.GetShimExpr(v, PRED_END_SUFFIX).decl(); + // // ctxfp_.register_relation(v_start); + // ctxfp_.register_relation(v_end); + // } + + for (const auto& [loc, edges] : pg_) { + for (const auto& [edge, next] : edges) { + encode_transition(loc, edge, next); + } + } +} + +void PFToCHCEncoder::encode_transition( + const pgraph::Location& start, + const pgraph::StmtSeq& transition, + const pgraph::Location& end) { + + Z3ExprAdapter z3_adapter {ctx_}; + SmtShim smt_solver {z3_adapter}; + PathUnrollerCustomDecidingVars unroller {smt_solver, pred_scope_}; + + InstrVec seq {}; + int step_ctr = 0; + + unroller.AssertStep(get_or_make_loc_predicate(start), 0); + + for (auto& stmt : transition) { + std::visit([&unroller, &seq, &step_ctr](const auto& s) { + using T = std::decay_t; + constexpr bool is_Assume = std::is_same_v; + constexpr bool is_Call = std::is_same_v; + constexpr bool is_unknown = !(is_Assume || is_Call); + if constexpr (is_Assume) { + unroller.AssertStep(s.assumption, step_ctr); + } else if constexpr (is_Call) { + seq.push_back(s.instr); + if (s.input_constraint) unroller.AssertStep(s.input_constraint, step_ctr); + ++step_ctr; + } else { + // compile-time error + static_assert( + is_unknown, "record_scopes not implemented for given type of BasicStmt." + ); + } + }, stmt); + } + + z3::expr_vector forall_args {ctx_}; + for (int i = 0; i != step_ctr + 1; ++i) { + for (const auto& v : total_scope_) { + forall_args.push_back(unroller.GetSmtCurrent(v, i)); + } + } + // while (max_step_count_ <= step_ctr) { + // for (const auto& v : total_scope_) { + // auto v_step = unroller.GetSmtCurrent(v, max_step_count_).decl(); + // ctxfp_.register_relation(v_step); + // } + // ++max_step_count_; + // } + + z3::expr_vector body {ctx_}; + + body.push_back(unroller.Unroll(seq)); + auto dvs = unroller.GetLastDecidingVars(); + ExprSet deciding_vars {dvs.begin(), dvs.end()}; + + for (const auto& v : get_scope(end)) { + auto end_pred_v = smt_solver.GetShimExpr(v, PRED_END_SUFFIX); + + forall_args.push_back(end_pred_v); + + int last_v_step = (deciding_vars.find(v) == deciding_vars.end()) ? 0 : step_ctr; + auto last_v_update = unroller.GetSmtCurrent(v, last_v_step); + body.push_back(smt_solver.Equal(last_v_update, end_pred_v)); + } + + ExprPtr end_pred = get_or_make_loc_predicate(end); + + z3::expr rule = z3::forall(forall_args, z3::implies( + z3::mk_and(body), smt_solver.GetShimExpr(end_pred, PRED_END_SUFFIX) + )); + + ctxfp_.add_rule(rule, ctx_.str_symbol((start + "_to_" + end).c_str())); +} + +void PFToCHCEncoder::record_scopes(const pgraph::CutPointGraph& pg, const ExprSet& params) { + ExprSet uvars {params}; + for (const auto& [loc, edges] : pg) { + for (const auto& edgeTo : edges) { + for (const auto& stmt : edgeTo.edge) { + add_unique_vars(stmt, uvars, total_scope_); + } + } + } + + pred_scope_ = {uvars.begin(), uvars.end()}; +} + +void PFToCHCEncoder::add_unique_vars( + const pgraph::BasicStmt& s, ExprSet& pred_scope, ExprSet& total_scope +) { + std::visit([&pred_scope, &total_scope](const auto& s) { + using T = std::decay_t; + constexpr bool is_Assume = std::is_same_v; + constexpr bool is_Call = std::is_same_v; + constexpr bool is_unknown = !(is_Assume || is_Call); + if constexpr (is_Assume) { + // Assume should not use any new variables. + // OLD: absknob::InsertVar(s.assumption, scope); + } else if constexpr (is_Call) { + // add ILA state variables to the scope + absknob::InsertStt(s.instr->host(), pred_scope); + absknob::InsertVar(s.instr->host(), total_scope); + // we do not include the state of children unless an instruction + // of the child is called. + // Also, the constraint should not use any new variables. + } else { + // compile-time error + static_assert( + is_unknown, "record_scopes not implemented for given type of BasicStmt." + ); + } + }, s); +} + +ExprPtr PFToCHCEncoder::get_or_make_loc_predicate(const pgraph::Location& loc) { + auto res = loc_predicates_.find(loc); + if (res != loc_predicates_.end()) return res->second; + loc_predicates_[loc] = new_predicate(loc, get_scope(loc)); + return loc_predicates_[loc]; +} + +ExprPtr PFToCHCEncoder::new_predicate(const std::string& name, const ExprPtrVec& args) { + std::vector arg_sorts; + for (const auto& e : args) { + arg_sorts.push_back(e->sort()); + } + FuncPtr f = Func::New(name, Sort::MakeBoolSort(), arg_sorts); + z3::func_decl fd = f->GetZ3FuncDecl(ctx_); + ctxfp_.register_relation(fd); + return asthub::AppFunc(f, args); +} + +z3::expr PFToCHCEncoder::get_error_query() { + Z3ExprAdapter z3_adapter {ctx_}; + + z3::expr_vector exists_args {ctx_}; + for (const auto& v : get_scope(pgraph::LOC_ERROR)) { + exists_args.push_back(z3_adapter.GetShimExpr(v, PRED_END_SUFFIX)); + } + + ExprPtr p = get_or_make_loc_predicate(pgraph::LOC_ERROR); + return z3::exists(exists_args, z3_adapter.GetShimExpr(p, PRED_END_SUFFIX)); + + // z3::func_decl_vector queries {ctx_}; + + // ExprPtr p = get_or_make_loc_predicate(pgraph::LOC_ERROR); + // queries.push_back( + // std::dynamic_pointer_cast(p) + // ->func()->GetZ3FuncDecl(ctx_)); + + // return queries; +} + +} \ No newline at end of file diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc new file mode 100644 index 000000000..15aed1674 --- /dev/null +++ b/test/t_pfrag.cc @@ -0,0 +1,133 @@ +/// \file +/// Unit tests for program fragment checker + +#include +#include +#include + +#include "unit-include/config.h" +#include "unit-include/simple_cpu.h" +#include "unit-include/util.h" + +namespace ilang { + +namespace pf2graph { + + using namespace pfast; + + TEST(Pf2cfg, CutPointGraph) { + + auto file_dir = os_portable_append_dir(ILANG_TEST_DATA_DIR, "aes"); + auto ila_file = os_portable_append_dir(file_dir, "aes.json"); + InstrLvlAbsPtr ila = ImportIlaPortable(ila_file).get(); + + ProgramFragment pf { + {ila->state("aes_address")}, + Block{ + Assume{asthub::Gt(ila->state("aes_key"), 0)}, + Assume{asthub::Gt(ila->state("aes_key"), 0)}, + Call{ila->instr("WRITE_ADDRESS")}, + Call{ila->instr("START_ENCRYPT")}, + Assert{asthub::Gt(ila->state("aes_key"), 0)}, + Call{ila->instr("START_ENCRYPT")}, + Assert{asthub::Gt(ila->state("aes_key"), 0)} + } + }; + + bool cmp_same_fragment = (pf == ProgramFragment{ + {ila->state("aes_address")}, + Block{ + Assume{asthub::Gt(ila->state("aes_key"), 0)}, + Assume{asthub::Gt(ila->state("aes_key"), 0)}, + Call{ila->instr("WRITE_ADDRESS")}, + Call{ila->instr("START_ENCRYPT")}, + Assert{asthub::Gt(ila->state("aes_key"), 0)}, + Call{ila->instr("START_ENCRYPT")}, + Assert{asthub::Gt(ila->state("aes_key"), 0)} + } + }); + EXPECT_TRUE(cmp_same_fragment); + + bool cmp_diff_fragment = (pf == ProgramFragment{ + {ila->state("aes_address")}, + Block{ + Assume{asthub::Gt(ila->state("aes_key"), 0)}, + Call{ila->instr("WRITE_ADDRESS")}, + Assert{asthub::Gt(ila->state("aes_key"), 0)} + } + } + ); + EXPECT_FALSE(cmp_diff_fragment); + } + + TEST(Pf2cfg, Counter) { + + // create ILA + + Ila m {"Counter"}; + + constexpr int OP_INC = 1; + constexpr int OP_DEC = 2; + + auto ctr = m.NewBvState("ctr", 8); + auto op = m.NewBvInput("op", 2); + + InstrPtr inst_inc; + { + auto instr = m.NewInstr("INC"); + instr.SetDecode(op == OP_INC); + instr.SetUpdate(ctr, ctr + 1); + inst_inc = instr.get(); + } + + InstrPtr inst_dec; + { auto instr = m.NewInstr("DEC"); + instr.SetDecode(op == OP_DEC); + instr.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr + 1)); + inst_dec = instr.get(); + } + + // program fragment + ProgramFragment pf {{ /* no params */ }, { + Assume {asthub::Eq(ctr.get(), 0)}, + Call {inst_inc}, + Assert {asthub::Eq(ctr.get(), 1)} + }}; + + PFToCHCEncoder encoder {m.get(), pf}; + + std::cout << "\nEncoded successfully!\n" << std::endl; + + std::cout << encoder.to_string() << std::endl; + + z3::check_result res = encoder.check_assertions(); + EXPECT_EQ(res, z3::unsat); + + } + + // TEST(Pf2cfg, UnrollerSMTTest) { + + // using namespace pfast; + + // auto m = SimpleCpu("m"); + + // Block b; + + // // initial conditions of CPU + // for (size_t i = 0; i != m->init_num(); i++) { + // b.push_back(Assert(m->init(i))); + // } + + // // + // ProgramFragment pf {{ /* no params */ }, { + // Call(m->instr("Load")), + // Call(m->instr("Load")), + // Call(m->instr("Add")), + // Call(m->instr("Store")) + // }}; + + // } + +} + +} \ No newline at end of file From 4ab4d3448e4a130a6a8cf275cd3a6adfb2c600d0 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Mon, 5 Jul 2021 11:59:28 -0400 Subject: [PATCH 02/32] Problem: can't have arrays in CHC rules. --- include/ilang/ila-mngr/v_eq_check_progfrag.h | 20 ++++- src/ila-mngr/CMakeLists.txt | 3 +- test/CMakeLists.txt | 89 ++++++++++---------- test/t_pfrag.cc | 88 ++++++++++++++----- 4 files changed, 131 insertions(+), 69 deletions(-) diff --git a/include/ilang/ila-mngr/v_eq_check_progfrag.h b/include/ilang/ila-mngr/v_eq_check_progfrag.h index 47fc74058..fd8498d0d 100644 --- a/include/ilang/ila-mngr/v_eq_check_progfrag.h +++ b/include/ilang/ila-mngr/v_eq_check_progfrag.h @@ -59,24 +59,30 @@ namespace ilang { std::string to_string(const ProgramFragment& pf); struct Assert { - const Constraint assertion; + Constraint assertion; /// Checks structural equality bool operator==(const Assert& b) const { return asthub::TopEq(assertion, b.assertion); } }; + static_assert(std::is_copy_constructible_v, "assert not copy constructible"); + static_assert(std::is_copy_assignable_v, "assert not copy assignable"); + struct Assume { - const Constraint assumption; + Constraint assumption; /// Checks structural equality bool operator==(const Assume& b) const { return asthub::TopEq(assumption, b.assumption); } }; + static_assert(std::is_copy_constructible_v, "assume not copy constructible"); + static_assert(std::is_copy_assignable_v, "assume not copy assignable"); + struct Call { - const InstrPtr instr; - const Constraint input_constraint; + InstrPtr instr; + Constraint input_constraint; /// Checks structural equality bool operator==(const Call& b) const { @@ -86,6 +92,12 @@ namespace ilang { } }; + static_assert(std::is_copy_constructible_v, "call not copy constructible"); + static_assert(std::is_copy_assignable_v, "assert not copy assignable"); + + static_assert(std::is_copy_assignable_v, "stmt not copy constructible"); + + /// \class PrettyPrinter /// A visitor for pretty-printing program-fragment AST elements class PrettyPrinter { diff --git a/src/ila-mngr/CMakeLists.txt b/src/ila-mngr/CMakeLists.txt index 8c8cd824c..71570ad74 100644 --- a/src/ila-mngr/CMakeLists.txt +++ b/src/ila-mngr/CMakeLists.txt @@ -15,7 +15,8 @@ target_sources(${ILANG_LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/u_rewrite_ila.cc ${CMAKE_CURRENT_SOURCE_DIR}/u_unroller.cc ${CMAKE_CURRENT_SOURCE_DIR}/u_unroller_smt.cc - ${CMAKE_CURRENT_SOURCE_DIR}/v_eq_check_crr.cc ${CMAKE_CURRENT_SOURCE_DIR}/v_eq_check_bmc.cc + ${CMAKE_CURRENT_SOURCE_DIR}/v_eq_check_crr.cc + ${CMAKE_CURRENT_SOURCE_DIR}/v_eq_check_pfrag.cc ${CMAKE_CURRENT_SOURCE_DIR}/v_refinement.cc ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b4cad2fff..cf1ddb69e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -47,50 +47,51 @@ package_add_test(${ILANG_TEST_MAIN} unit-src/simple_cpu.cc unit-src/stream_buffer.cc unit-src/util.cc - t_ast_hub.cc - t_api.cc - t_case_aes_eq.cc - t_copy.cc - t_crr.cc - t_eq_check.cc - t_expr.cc - t_expr_const.cc - t_expr_op.cc - t_expr_var.cc - t_func.cc - t_hash_ast.cc - t_ila.cc - t_ilator.cc - t_instr.cc - t_instr_seq.cc - t_keyvec.cc - t_legacy_bmc.cc - t_log.cc - t_main.cc - t_mapset.cc - t_mcm.cc - t_mngr_absknob.cc - t_pass.cc - t_portable.cc - t_smt_in.cc - t_smt_shim.cc - t_smt_switch_itf.cc - t_smt_trans.cc - t_sort.cc - t_symbol.cc - t_unroll_seq.cc - t_unroller_smt.cc - t_util.cc - t_verilog_analysis.cc - t_verilog_analysis_error.cc - t_verilog_gen.cc - t_verilog_mod.cc - t_vtarget_gen.cc - t_inv_extract.cc - t_z3adapter.cc - t_z3sanity.cc - t_inv_syn.cc - t_inv_obj.cc + # t_ast_hub.cc + # t_api.cc + # t_case_aes_eq.cc + # t_copy.cc + # t_crr.cc + # t_eq_check.cc + # t_expr.cc + # t_expr_const.cc + # t_expr_op.cc + # t_expr_var.cc + # t_func.cc + # t_hash_ast.cc + # t_ila.cc + # t_ilator.cc + # t_instr.cc + # t_instr_seq.cc + # t_keyvec.cc + # t_legacy_bmc.cc + # t_log.cc + # t_main.cc + # t_mapset.cc + # t_mcm.cc + # t_mngr_absknob.cc + # t_pass.cc + t_pfrag.cc + # t_portable.cc + # t_smt_in.cc + # t_smt_shim.cc + # t_smt_switch_itf.cc + # t_smt_trans.cc + # t_sort.cc + # t_symbol.cc + # t_unroll_seq.cc + # t_unroller_smt.cc + # t_util.cc + # t_verilog_analysis.cc + # t_verilog_analysis_error.cc + # t_verilog_gen.cc + # t_verilog_mod.cc + # t_vtarget_gen.cc + # t_inv_extract.cc + # t_z3adapter.cc + # t_z3sanity.cc + # t_inv_syn.cc + # t_inv_obj.cc ) if(${ILANG_BUILD_SYNTH}) diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc index 15aed1674..97fb2da0e 100644 --- a/test/t_pfrag.cc +++ b/test/t_pfrag.cc @@ -91,42 +91,90 @@ namespace pf2graph { ProgramFragment pf {{ /* no params */ }, { Assume {asthub::Eq(ctr.get(), 0)}, Call {inst_inc}, - Assert {asthub::Eq(ctr.get(), 1)} + Assert {asthub::Eq(ctr.get(), 1)}, + Call {inst_dec}, + Call {inst_dec}, + Assert {asthub::Eq(ctr.get(), 0)}, // dec should not go less than 0 + Call {inst_inc}, + Call {inst_inc}, + Call {inst_dec}, + Call {inst_inc}, + Assert {asthub::Eq(ctr.get(), 2)}, }}; PFToCHCEncoder encoder {m.get(), pf}; - std::cout << "\nEncoded successfully!\n" << std::endl; + // std::cout << "\nEncoded successfully!\n" << std::endl; - std::cout << encoder.to_string() << std::endl; + // std::cout << encoder.to_string() << std::endl; z3::check_result res = encoder.check_assertions(); EXPECT_EQ(res, z3::unsat); - } - // TEST(Pf2cfg, UnrollerSMTTest) { + TEST(Pf2cfg, UnrollerSMTTest) { + + using namespace pfast; + using namespace asthub; + + auto m = SimpleCpu("m"); + + Block b; + + // initial conditions of CPU + for (size_t i = 0; i != m->init_num(); i++) { + b.push_back(Assume{m->init(i)}); + } + + MemVal init_mem_val(0); + init_mem_val.set_data(0, GenLoad(0, 0)); + init_mem_val.set_data(1, GenLoad(1, 1)); + init_mem_val.set_data(2, GenAdd(2, 0, 1)); + init_mem_val.set_data(3, GenStore(2, 2)); + + { // instruction memory + auto ir = m->state("ir"); + b.push_back(Assume{Eq(ir, MemConst(init_mem_val, 8, 8))}); + } + + // concretize data + b.insert(b.end(), { + Assume{Eq(m->state("r0"), 7)}, + Assume{Eq(m->state("r1"), 7)}, + Assume{Eq(Load(m->state("mem"), 0), 3)}, + Assume{Eq(Load(m->state("mem"), 1), 3)} + }); + + // program + b.insert(b.end(), { + Call{m->instr("Load")}, + Call{m->instr("Load")}, + Call{m->instr("Add")}, + Call{m->instr("Store")} + }); + + // check stored result + b.push_back(Assert{Eq(Load(m->state("mem"), 2), 6)}); + + ProgramFragment pf {{ }, b}; /* no params */ + + PFToCHCEncoder encoder {m, pf}; - // using namespace pfast; + std::cout << "\nEncoded successfully!\n" << std::endl; - // auto m = SimpleCpu("m"); + std::cout << encoder.to_string() << std::endl; - // Block b; + z3::check_result res = z3::sat; - // // initial conditions of CPU - // for (size_t i = 0; i != m->init_num(); i++) { - // b.push_back(Assert(m->init(i))); - // } + try { + res = encoder.check_assertions(); + } catch (z3::exception e) { + std::cout << "Got error:\n" << e << std::endl; + } - // // - // ProgramFragment pf {{ /* no params */ }, { - // Call(m->instr("Load")), - // Call(m->instr("Load")), - // Call(m->instr("Add")), - // Call(m->instr("Store")) - // }}; + EXPECT_EQ(res, z3::unsat); - // } + } } From d8774a33cde832aab3ee5ff22acfc2a6de95997a Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Mon, 5 Jul 2021 13:08:56 -0400 Subject: [PATCH 03/32] Fixed by changing fixedpoint engine to spacer. --- include/ilang/ila-mngr/v_eq_check_progfrag.h | 13 +++++++++---- src/ila-mngr/v_eq_check_pfrag.cc | 5 +++++ test/t_pfrag.cc | 19 ++++++------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/include/ilang/ila-mngr/v_eq_check_progfrag.h b/include/ilang/ila-mngr/v_eq_check_progfrag.h index fd8498d0d..acc433f9f 100644 --- a/include/ilang/ila-mngr/v_eq_check_progfrag.h +++ b/include/ilang/ila-mngr/v_eq_check_progfrag.h @@ -154,15 +154,20 @@ namespace ilang { // Decision: use PathUnroller.Unroll, PathUnroller.GetSMTCurrent class PFToCHCEncoder { - using Predicate = z3::func_decl; + using Predicate = ExprPtr; public: + using Result = z3::check_result; + + static constexpr Result VALID = z3::unsat; + static constexpr Result INVALID = z3::sat; + PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pfast::ProgramFragment& pf); ~PFToCHCEncoder()=default; std::string to_string(); - z3::check_result check_assertions() { + Result check_assertions() { z3::expr q = get_error_query(); // z3::func_decl_vector q = get_error_query(); return ctxfp_.query(q); @@ -201,8 +206,8 @@ namespace ilang { return pred_scope_; // for now, same scope for the whole graph } - ExprPtr get_or_make_loc_predicate(const std::string& name); - ExprPtr new_predicate(const std::string& name, const ExprPtrVec& args); + Predicate get_or_make_loc_predicate(const std::string& name); + Predicate new_predicate(const std::string& name, const ExprPtrVec& args); z3::expr get_error_query(); // z3::func_decl_vector get_error_query(); diff --git a/src/ila-mngr/v_eq_check_pfrag.cc b/src/ila-mngr/v_eq_check_pfrag.cc index 16979fe16..34dbbb73a 100644 --- a/src/ila-mngr/v_eq_check_pfrag.cc +++ b/src/ila-mngr/v_eq_check_pfrag.cc @@ -193,6 +193,11 @@ PFToCHCEncoder::PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pfast::ProgramFr PFToCHCEncoder::PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params): ila_ {ila}, pg_ {pg}, params_ {params} { + + z3::params p {ctx_}; + p.set("engine", "spacer"); + ctxfp_.set(p); + record_scopes(pg_, params_); encode(); } diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc index 97fb2da0e..459fac2dd 100644 --- a/test/t_pfrag.cc +++ b/test/t_pfrag.cc @@ -108,8 +108,8 @@ namespace pf2graph { // std::cout << encoder.to_string() << std::endl; - z3::check_result res = encoder.check_assertions(); - EXPECT_EQ(res, z3::unsat); + PFToCHCEncoder::Result res = encoder.check_assertions(); + EXPECT_EQ(res, PFToCHCEncoder::VALID); } TEST(Pf2cfg, UnrollerSMTTest) { @@ -160,19 +160,12 @@ namespace pf2graph { PFToCHCEncoder encoder {m, pf}; - std::cout << "\nEncoded successfully!\n" << std::endl; - - std::cout << encoder.to_string() << std::endl; - - z3::check_result res = z3::sat; + // std::cout << "\nEncoded successfully!\n" << std::endl; - try { - res = encoder.check_assertions(); - } catch (z3::exception e) { - std::cout << "Got error:\n" << e << std::endl; - } + // std::cout << encoder.to_string() << std::endl; - EXPECT_EQ(res, z3::unsat); + PFToCHCEncoder::Result res = encoder.check_assertions(); + EXPECT_EQ(res, PFToCHCEncoder::VALID); } From a33e35c38a9aace0c744798ef29297bbf38f4c0f Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Mon, 5 Jul 2021 14:05:55 -0400 Subject: [PATCH 04/32] Fixed all programs being valid. --- include/ilang/ila-mngr/v_eq_check_progfrag.h | 7 +-- src/ila-mngr/v_eq_check_pfrag.cc | 46 ++++++---------- test/t_pfrag.cc | 58 +++++++++++++++++++- 3 files changed, 76 insertions(+), 35 deletions(-) diff --git a/include/ilang/ila-mngr/v_eq_check_progfrag.h b/include/ilang/ila-mngr/v_eq_check_progfrag.h index acc433f9f..4d8027a7d 100644 --- a/include/ilang/ila-mngr/v_eq_check_progfrag.h +++ b/include/ilang/ila-mngr/v_eq_check_progfrag.h @@ -168,8 +168,8 @@ namespace ilang { std::string to_string(); Result check_assertions() { - z3::expr q = get_error_query(); - // z3::func_decl_vector q = get_error_query(); + // z3::expr q = get_error_query(); + z3::func_decl_vector q = get_error_query(); return ctxfp_.query(q); } @@ -209,8 +209,7 @@ namespace ilang { Predicate get_or_make_loc_predicate(const std::string& name); Predicate new_predicate(const std::string& name, const ExprPtrVec& args); - z3::expr get_error_query(); - // z3::func_decl_vector get_error_query(); + z3::func_decl_vector get_error_queries(); }; diff --git a/src/ila-mngr/v_eq_check_pfrag.cc b/src/ila-mngr/v_eq_check_pfrag.cc index 34dbbb73a..3298c2521 100644 --- a/src/ila-mngr/v_eq_check_pfrag.cc +++ b/src/ila-mngr/v_eq_check_pfrag.cc @@ -203,24 +203,20 @@ PFToCHCEncoder::PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pgraph::CutPoint } -std::string PFToCHCEncoder::to_string() { - z3::expr_vector queries {ctx_}; - z3::expr q = get_error_query(); - queries.push_back(q); - return ctxfp_.to_string(queries); // TODO: add query +std::string PFToCHCEncoder::to_string() { + z3::func_decl_vector queries = get_error_queries(); + std::stringstream encoding; + encoding << ctxfp_.to_string(); + for (const z3::func_decl& f : queries) { + encoding << "(query " << f.name() << ")\n"; + } + return encoding.str(); } void PFToCHCEncoder::encode() { Z3ExprAdapter z3_adapter {ctx_}; - // for (const auto& v : pred_scope_) { - // // auto v_start = z3_adapter.GetShimExpr(v, PRED_START_SUFFIX).decl(); - // auto v_end = z3_adapter.GetShimExpr(v, PRED_END_SUFFIX).decl(); - // // ctxfp_.register_relation(v_start); - // ctxfp_.register_relation(v_end); - // } - for (const auto& [loc, edges] : pg_) { for (const auto& [edge, next] : edges) { encode_transition(loc, edge, next); @@ -240,7 +236,9 @@ void PFToCHCEncoder::encode_transition( InstrVec seq {}; int step_ctr = 0; - unroller.AssertStep(get_or_make_loc_predicate(start), 0); + if (start != pgraph::LOC_BEGIN) { + unroller.AssertStep(get_or_make_loc_predicate(start), 0); + } for (auto& stmt : transition) { std::visit([&unroller, &seq, &step_ctr](const auto& s) { @@ -360,25 +358,15 @@ ExprPtr PFToCHCEncoder::new_predicate(const std::string& name, const ExprPtrVec& return asthub::AppFunc(f, args); } -z3::expr PFToCHCEncoder::get_error_query() { - Z3ExprAdapter z3_adapter {ctx_}; - - z3::expr_vector exists_args {ctx_}; - for (const auto& v : get_scope(pgraph::LOC_ERROR)) { - exists_args.push_back(z3_adapter.GetShimExpr(v, PRED_END_SUFFIX)); - } +z3::func_decl_vector PFToCHCEncoder::get_error_queries() { + z3::func_decl_vector queries {ctx_}; ExprPtr p = get_or_make_loc_predicate(pgraph::LOC_ERROR); - return z3::exists(exists_args, z3_adapter.GetShimExpr(p, PRED_END_SUFFIX)); - - // z3::func_decl_vector queries {ctx_}; - - // ExprPtr p = get_or_make_loc_predicate(pgraph::LOC_ERROR); - // queries.push_back( - // std::dynamic_pointer_cast(p) - // ->func()->GetZ3FuncDecl(ctx_)); + queries.push_back( + std::dynamic_pointer_cast(p) + ->func()->GetZ3FuncDecl(ctx_)); - // return queries; + return queries; } } \ No newline at end of file diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc index 459fac2dd..19de6735e 100644 --- a/test/t_pfrag.cc +++ b/test/t_pfrag.cc @@ -83,7 +83,7 @@ namespace pf2graph { InstrPtr inst_dec; { auto instr = m.NewInstr("DEC"); instr.SetDecode(op == OP_DEC); - instr.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr + 1)); + instr.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr - 1)); inst_dec = instr.get(); } @@ -99,7 +99,7 @@ namespace pf2graph { Call {inst_inc}, Call {inst_dec}, Call {inst_inc}, - Assert {asthub::Eq(ctr.get(), 2)}, + Assert {asthub::Eq(ctr.get(), 2)} }}; PFToCHCEncoder encoder {m.get(), pf}; @@ -108,10 +108,64 @@ namespace pf2graph { // std::cout << encoder.to_string() << std::endl; + EXPECT_NO_THROW(encoder.to_string()); + PFToCHCEncoder::Result res = encoder.check_assertions(); EXPECT_EQ(res, PFToCHCEncoder::VALID); } + TEST(Pf2cfg, CounterFail) { + + // create ILA + + Ila m {"Counter"}; + + constexpr int OP_INC = 1; + constexpr int OP_DEC = 2; + + auto ctr = m.NewBvState("ctr", 8); + auto op = m.NewBvInput("op", 2); + + InstrPtr inst_inc; + { + auto instr = m.NewInstr("INC"); + instr.SetDecode(op == OP_INC); + instr.SetUpdate(ctr, ctr + 1); + inst_inc = instr.get(); + } + + InstrPtr inst_dec; + { auto instr = m.NewInstr("DEC"); + instr.SetDecode(op == OP_DEC); + instr.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr + 1)); + inst_dec = instr.get(); + } + + // program fragment + ProgramFragment pf {{ /* no params */ }, { + Assume {asthub::Eq(ctr.get(), 0)}, + Call {inst_inc}, + Assert {asthub::Eq(ctr.get(), 1)}, + Call {inst_dec}, + Call {inst_dec}, + Assert {asthub::Eq(ctr.get(), 3)}, // should fail + Call {inst_inc}, + Call {inst_inc}, + Call {inst_dec}, + Call {inst_inc}, + Assert {asthub::Eq(ctr.get(), 2)}, + }}; + + PFToCHCEncoder encoder {m.get(), pf}; + + // std::cout << "\nEncoded successfully!\n" << std::endl; + + // std::cout << encoder.to_string() << std::endl; + + PFToCHCEncoder::Result res = encoder.check_assertions(); + EXPECT_EQ(res, PFToCHCEncoder::INVALID); + } + TEST(Pf2cfg, UnrollerSMTTest) { using namespace pfast; From 2810dc33631129e681582a7464cf9361b59ebe99 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Wed, 7 Jul 2021 12:08:05 -0400 Subject: [PATCH 05/32] Fixed bug with parameters. --- include/ilang/ila-mngr/v_eq_check_progfrag.h | 2 +- src/ila-mngr/v_eq_check_pfrag.cc | 1 + test/t_pfrag.cc | 165 +++++++++++++------ 3 files changed, 113 insertions(+), 55 deletions(-) diff --git a/include/ilang/ila-mngr/v_eq_check_progfrag.h b/include/ilang/ila-mngr/v_eq_check_progfrag.h index 4d8027a7d..5f0930d78 100644 --- a/include/ilang/ila-mngr/v_eq_check_progfrag.h +++ b/include/ilang/ila-mngr/v_eq_check_progfrag.h @@ -169,7 +169,7 @@ namespace ilang { Result check_assertions() { // z3::expr q = get_error_query(); - z3::func_decl_vector q = get_error_query(); + z3::func_decl_vector q = get_error_queries(); return ctxfp_.query(q); } diff --git a/src/ila-mngr/v_eq_check_pfrag.cc b/src/ila-mngr/v_eq_check_pfrag.cc index 3298c2521..2648f8720 100644 --- a/src/ila-mngr/v_eq_check_pfrag.cc +++ b/src/ila-mngr/v_eq_check_pfrag.cc @@ -198,6 +198,7 @@ PFToCHCEncoder::PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pgraph::CutPoint p.set("engine", "spacer"); ctxfp_.set(p); + total_scope_ = params_; // initialize total_scope_, will add more in record_scopes record_scopes(pg_, params_); encode(); } diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc index 19de6735e..f465a44c5 100644 --- a/test/t_pfrag.cc +++ b/test/t_pfrag.cc @@ -9,12 +9,42 @@ #include "unit-include/simple_cpu.h" #include "unit-include/util.h" +#define COUNTER_INSTR_INC "INC" +#define COUNTER_INSTR_DEC "DEC" + +#define COUNTER_OP_INC 1 +#define COUNTER_OP_DEC 2 + +#define COUNTER_STATE_CTR "ctr" +#define COUNTER_INPUT_OP "op" + namespace ilang { namespace pf2graph { using namespace pfast; + InstrLvlAbsPtr simple_counter_ila() { + Ila m {"Counter"}; + + auto ctr = m.NewBvState(COUNTER_STATE_CTR, 8); + auto op = m.NewBvInput(COUNTER_INPUT_OP, 2); + + { // increment + auto instr = m.NewInstr(COUNTER_INSTR_INC); + instr.SetDecode(op == COUNTER_OP_INC); + instr.SetUpdate(ctr, ctr + 1); + } + + { // decrement + auto instr = m.NewInstr(COUNTER_INSTR_DEC); + instr.SetDecode(op == COUNTER_OP_DEC); + instr.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr - 1)); + } + + return m.get(); + } + TEST(Pf2cfg, CutPointGraph) { auto file_dir = os_portable_append_dir(ILANG_TEST_DATA_DIR, "aes"); @@ -62,47 +92,29 @@ namespace pf2graph { TEST(Pf2cfg, Counter) { - // create ILA - - Ila m {"Counter"}; - - constexpr int OP_INC = 1; - constexpr int OP_DEC = 2; - - auto ctr = m.NewBvState("ctr", 8); - auto op = m.NewBvInput("op", 2); - - InstrPtr inst_inc; - { - auto instr = m.NewInstr("INC"); - instr.SetDecode(op == OP_INC); - instr.SetUpdate(ctr, ctr + 1); - inst_inc = instr.get(); - } + InstrLvlAbsPtr m = simple_counter_ila(); + auto inst_inc = m->instr(COUNTER_INSTR_INC); + auto inst_dec = m->instr(COUNTER_INSTR_DEC); - InstrPtr inst_dec; - { auto instr = m.NewInstr("DEC"); - instr.SetDecode(op == OP_DEC); - instr.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr - 1)); - inst_dec = instr.get(); - } + auto ctr = m->state(COUNTER_STATE_CTR); + auto op = m->input(COUNTER_INPUT_OP); // program fragment ProgramFragment pf {{ /* no params */ }, { - Assume {asthub::Eq(ctr.get(), 0)}, + Assume {asthub::Eq(ctr, 0)}, Call {inst_inc}, - Assert {asthub::Eq(ctr.get(), 1)}, + Assert {asthub::Eq(ctr, 1)}, Call {inst_dec}, Call {inst_dec}, - Assert {asthub::Eq(ctr.get(), 0)}, // dec should not go less than 0 + Assert {asthub::Eq(ctr, 0)}, // dec should not go less than 0 Call {inst_inc}, Call {inst_inc}, Call {inst_dec}, Call {inst_inc}, - Assert {asthub::Eq(ctr.get(), 2)} + Assert {asthub::Eq(ctr, 2)} }}; - PFToCHCEncoder encoder {m.get(), pf}; + PFToCHCEncoder encoder {m, pf}; // std::cout << "\nEncoded successfully!\n" << std::endl; @@ -118,45 +130,29 @@ namespace pf2graph { // create ILA - Ila m {"Counter"}; - - constexpr int OP_INC = 1; - constexpr int OP_DEC = 2; + InstrLvlAbsPtr m = simple_counter_ila(); + auto inst_inc = m->instr(COUNTER_INSTR_INC); + auto inst_dec = m->instr(COUNTER_INSTR_DEC); - auto ctr = m.NewBvState("ctr", 8); - auto op = m.NewBvInput("op", 2); - - InstrPtr inst_inc; - { - auto instr = m.NewInstr("INC"); - instr.SetDecode(op == OP_INC); - instr.SetUpdate(ctr, ctr + 1); - inst_inc = instr.get(); - } - - InstrPtr inst_dec; - { auto instr = m.NewInstr("DEC"); - instr.SetDecode(op == OP_DEC); - instr.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr + 1)); - inst_dec = instr.get(); - } + auto ctr = m->state(COUNTER_STATE_CTR); + auto op = m->input(COUNTER_INPUT_OP); // program fragment ProgramFragment pf {{ /* no params */ }, { - Assume {asthub::Eq(ctr.get(), 0)}, + Assume {asthub::Eq(ctr, 0)}, Call {inst_inc}, - Assert {asthub::Eq(ctr.get(), 1)}, + Assert {asthub::Eq(ctr, 1)}, Call {inst_dec}, Call {inst_dec}, - Assert {asthub::Eq(ctr.get(), 3)}, // should fail + Assert {asthub::Eq(ctr, 3)}, // assertion should fail Call {inst_inc}, Call {inst_inc}, Call {inst_dec}, Call {inst_inc}, - Assert {asthub::Eq(ctr.get(), 2)}, + Assert {asthub::Eq(ctr, 2)} }}; - PFToCHCEncoder encoder {m.get(), pf}; + PFToCHCEncoder encoder {m, pf}; // std::cout << "\nEncoded successfully!\n" << std::endl; @@ -223,6 +219,67 @@ namespace pf2graph { } + TEST(Pf2cfg, UnrollerSMTTestWithParams) { + + using namespace pfast; + using namespace asthub; + + auto m = SimpleCpu("m"); + + // params + ExprPtr x = m->NewBvFreeVar("x", 8); + ExprPtr y = m->NewBvFreeVar("y", 8); + + // fragment body + Block b; + + // initial conditions of CPU + for (size_t i = 0; i != m->init_num(); i++) { + b.push_back(Assume{m->init(i)}); + } + + MemVal init_mem_val(0); + init_mem_val.set_data(0, GenLoad(0, 0)); + init_mem_val.set_data(1, GenLoad(1, 1)); + init_mem_val.set_data(2, GenAdd(2, 0, 1)); + init_mem_val.set_data(3, GenStore(2, 2)); + + { // instruction memory + auto ir = m->state("ir"); + b.push_back(Assume{Eq(ir, MemConst(init_mem_val, 8, 8))}); + } + + // link params and model + b.insert(b.end(), { + Assume{Eq(Load(m->state("mem"), 0), x)}, + Assume{Eq(Load(m->state("mem"), 1), y)} + }); + + // program + b.insert(b.end(), { + Call{m->instr("Load")}, + Call{m->instr("Load")}, + Call{m->instr("Add")}, + Call{m->instr("Store")} + }); + + // check stored result + b.push_back(Assert{Eq(Load(m->state("mem"), 2), asthub::Add(x, y))}); + + ProgramFragment pf {{x, y}, b}; /* no params */ + + PFToCHCEncoder encoder {m, pf}; + + // std::cout << "\nEncoded successfully!\n" << std::endl; + + // std::cout << encoder.to_string() << std::endl; + + PFToCHCEncoder::Result res = encoder.check_assertions(); + + EXPECT_EQ(res, PFToCHCEncoder::VALID); + + } + } } \ No newline at end of file From 45d4ae9b81fbab4e2b2dc7e0054fddc7960fc60a Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Tue, 27 Jul 2021 20:43:00 -0400 Subject: [PATCH 06/32] Loop support! --- include/ilang/ila-mngr/u_progfrag.h | 149 ++++++++++++++++ include/ilang/ila-mngr/v_eq_check_progfrag.h | 121 ++----------- src/ila-mngr/CMakeLists.txt | 1 + src/ila-mngr/u_progfrag.cc | 106 +++++++++++ src/ila-mngr/v_eq_check_pfrag.cc | 176 ++++++------------- test/t_pfrag.cc | 130 +++++++++++++- 6 files changed, 445 insertions(+), 238 deletions(-) create mode 100644 include/ilang/ila-mngr/u_progfrag.h create mode 100644 src/ila-mngr/u_progfrag.cc diff --git a/include/ilang/ila-mngr/u_progfrag.h b/include/ilang/ila-mngr/u_progfrag.h new file mode 100644 index 000000000..70a3c3f0a --- /dev/null +++ b/include/ilang/ila-mngr/u_progfrag.h @@ -0,0 +1,149 @@ +/// \file +/// Defines the program fragment AST + +#ifndef ILANG_ILA_MNGR_U_PROGFRAG_H__ +#define ILANG_ILA_MNGR_U_PROGFRAG_H__ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +/// \namespace ilang +namespace ilang { + + /// \namespace pfast + /// Defines the program-fragment AST + namespace pfast { + + using Constraint = ExprPtr; + + struct Assert; + struct Assume; + struct Call; + struct Update; + struct While; + + using Stmt = std::variant; + + /// Converts to a string for pretty printing + std::string to_string(const Stmt& s); + + struct Block : public std::vector { + using base=std::vector; + using base::base; + using base::operator=; + }; + + /// Converts to a string for pretty printing + std::string to_string(const Block& b); + + struct ProgramFragment { + ExprSet params; + Block body; + }; + + /// Checks structural equality + bool operator==(const ProgramFragment& a, const ProgramFragment& b); + + /// Converts to a string for pretty printing + std::string to_string(const ProgramFragment& pf); + + struct Assert { + Constraint assertion; + /// Checks structural equality + bool operator==(const Assert& b) const { + return asthub::TopEq(assertion, b.assertion); + } + }; + + static_assert(std::is_copy_constructible_v, "assert not copy constructible"); + static_assert(std::is_copy_assignable_v, "assert not copy assignable"); + + struct Assume { + Constraint assumption; + /// Checks structural equality + bool operator==(const Assume& b) const { + return asthub::TopEq(assumption, b.assumption); + } + }; + + static_assert(std::is_copy_constructible_v, "assume not copy constructible"); + static_assert(std::is_copy_assignable_v, "assume not copy assignable"); + + struct Call { + InstrPtr instr; + Constraint input_constraint; + + /// Checks structural equality + bool operator==(const Call& b) const { + return (instr == b.instr) + && (bool(input_constraint) == bool(b.input_constraint)) + && (!input_constraint || asthub::TopEq(input_constraint, b.input_constraint)); + } + }; + + static_assert(std::is_copy_constructible_v, "call not copy constructible"); + static_assert(std::is_copy_assignable_v, "call not copy assignable"); + + struct Update: public ExprMap { + using base=ExprMap; + using base::base; + using base::operator=; + }; + + static_assert(std::is_copy_constructible_v, "update not copy constructible"); + static_assert(std::is_copy_assignable_v, "update not copy assignable"); + + struct While { + Constraint loop_condition; + Block body; + + /// Checks structural equality + bool operator==(const While& w) const { + return (asthub::TopEq(loop_condition, w.loop_condition) + && body == w.body); + } + }; + + static_assert(std::is_copy_constructible_v, "while not copy constructible"); + static_assert(std::is_copy_assignable_v, "while not copy assignable"); + + static_assert(std::is_copy_constructible_v, "stmt not copy constructible"); + static_assert(std::is_copy_assignable_v, "stmt not copy constructible"); + + + /// \class PrettyPrinter + /// A visitor for pretty-printing program-fragment AST elements + class PrettyPrinter { + public: + PrettyPrinter(): buf_ {} {} + ~PrettyPrinter()=default; + void operator()(const ProgramFragment& pf); + void operator()(const Block& b); + + void operator()(const Assert& a); + void operator()(const Assume& a); + void operator()(const Call& c); + void operator()(const Update& u); + void operator()(const While& w); + + std::string str() { return buf_.str(); } + private: + std::ostringstream buf_; + int indent_; + + std::string reserved_word(const std::string& word); + std::string indent_str(); + }; + + } +} + +#endif \ No newline at end of file diff --git a/include/ilang/ila-mngr/v_eq_check_progfrag.h b/include/ilang/ila-mngr/v_eq_check_progfrag.h index 5f0930d78..5cd7a0572 100644 --- a/include/ilang/ila-mngr/v_eq_check_progfrag.h +++ b/include/ilang/ila-mngr/v_eq_check_progfrag.h @@ -9,119 +9,17 @@ #include #include #include -#include #include #include + +#include #include /// \namespace ilang namespace ilang { - /// \namespace pfast - /// Defines the program-fragment AST - namespace pfast { - - using Constraint = ExprPtr; - - struct Assert; - struct Assume; - struct Call; - // struct Update; - - using Stmt = std::variant; - - /// Converts to a string for pretty printing - std::string to_string(const Stmt& s); - - struct Block : public std::vector { - using base=std::vector; - using base::base; - using base::operator=; - }; - - /// Checks structural equality - bool operator==(const Block& a, const Block& b); - - /// Converts to a string for pretty printing - std::string to_string(const Block& b); - - struct ProgramFragment { - ExprSet params; - Block body; - }; - - /// Checks structural equality - bool operator==(const ProgramFragment& a, const ProgramFragment& b); - - /// Converts to a string for pretty printing - std::string to_string(const ProgramFragment& pf); - - struct Assert { - Constraint assertion; - /// Checks structural equality - bool operator==(const Assert& b) const { - return asthub::TopEq(assertion, b.assertion); - } - }; - - static_assert(std::is_copy_constructible_v, "assert not copy constructible"); - static_assert(std::is_copy_assignable_v, "assert not copy assignable"); - - struct Assume { - Constraint assumption; - /// Checks structural equality - bool operator==(const Assume& b) const { - return asthub::TopEq(assumption, b.assumption); - } - }; - - static_assert(std::is_copy_constructible_v, "assume not copy constructible"); - static_assert(std::is_copy_assignable_v, "assume not copy assignable"); - - struct Call { - InstrPtr instr; - Constraint input_constraint; - - /// Checks structural equality - bool operator==(const Call& b) const { - return (instr == b.instr) - && (bool(input_constraint) == bool(b.input_constraint)) - && (!input_constraint || asthub::TopEq(input_constraint, b.input_constraint)); - } - }; - - static_assert(std::is_copy_constructible_v, "call not copy constructible"); - static_assert(std::is_copy_assignable_v, "assert not copy assignable"); - - static_assert(std::is_copy_assignable_v, "stmt not copy constructible"); - - - /// \class PrettyPrinter - /// A visitor for pretty-printing program-fragment AST elements - class PrettyPrinter { - public: - PrettyPrinter(): buf_ {} {} - ~PrettyPrinter()=default; - void operator()(const ProgramFragment& pf); - void operator()(const Block& b); - - void operator()(const Assert& a); - void operator()(const Assume& a); - void operator()(const Call& c); - - std::string str() { return buf_.str(); } - private: - std::ostringstream buf_; - int indent_; - - std::string reserved_word(const std::string& word); - std::string indent_str(); - }; - - } - /// \namespace pgraph /// Expresses program fragments using cut-point graphs namespace pgraph { @@ -129,8 +27,9 @@ namespace ilang { using Location = std::string; constexpr char LOC_BEGIN[] = "begin"; constexpr char LOC_ERROR[] = "fail"; + constexpr char LOC_END[] = "end"; - using BasicStmt = std::variant; + using BasicStmt = std::variant; using StmtSeq = std::vector; using Edge = StmtSeq; @@ -162,7 +61,9 @@ namespace ilang { static constexpr Result VALID = z3::unsat; static constexpr Result INVALID = z3::sat; - PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pfast::ProgramFragment& pf); + PFToCHCEncoder( + z3::context& ctx, z3::fixedpoint& ctxfp, + const InstrLvlAbsPtr& ila, const pfast::ProgramFragment& pf); ~PFToCHCEncoder()=default; std::string to_string(); @@ -180,8 +81,8 @@ namespace ilang { ExprPtrVec pred_scope_; ExprSet total_scope_; - z3::context ctx_ {}; - z3::fixedpoint ctxfp_ {ctx_}; + z3::context& ctx_; + z3::fixedpoint& ctxfp_; std::unordered_map loc_predicates_; int max_step_count_ {0}; @@ -189,7 +90,9 @@ namespace ilang { constexpr static char PRED_END_SUFFIX[] = "end"; // internal constructor - PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params); + PFToCHCEncoder( + z3::context& ctx, z3::fixedpoint& ctxfp, + const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params); void encode(); diff --git a/src/ila-mngr/CMakeLists.txt b/src/ila-mngr/CMakeLists.txt index 71570ad74..47aa1716a 100644 --- a/src/ila-mngr/CMakeLists.txt +++ b/src/ila-mngr/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources(${ILANG_LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/p_simplify_semantic.cc ${CMAKE_CURRENT_SOURCE_DIR}/p_simplify_syntactic.cc ${CMAKE_CURRENT_SOURCE_DIR}/u_abs_knob.cc + ${CMAKE_CURRENT_SOURCE_DIR}/u_progfrag.cc ${CMAKE_CURRENT_SOURCE_DIR}/u_rewrite_expr.cc ${CMAKE_CURRENT_SOURCE_DIR}/u_rewrite_ila.cc ${CMAKE_CURRENT_SOURCE_DIR}/u_unroller.cc diff --git a/src/ila-mngr/u_progfrag.cc b/src/ila-mngr/u_progfrag.cc new file mode 100644 index 000000000..9379d1275 --- /dev/null +++ b/src/ila-mngr/u_progfrag.cc @@ -0,0 +1,106 @@ +/// \file +/// Utilities for handling program fragments + +#include + +#include + +#include + +namespace ilang { + +namespace pfast { + + bool operator==(const ProgramFragment& a, const ProgramFragment& b) { + // [TODO] check == operator on unordered sets + return (a.params == b.params) && (a.body == b.body); + } + + std::string to_string(const Stmt& s) { + PrettyPrinter pp {}; + std::visit(pp, s); + return pp.str(); + } + + std::string to_string(const ProgramFragment& pf) { + PrettyPrinter pp {}; + pp(pf); + return pp.str(); + } + + std::string to_string(const Block& b) { + PrettyPrinter pp {}; + pp(b); + return pp.str(); + } + + void PrettyPrinter::operator()(const ProgramFragment& pf) { + buf_ << reserved_word("main") << "("; + bool first = true; + for (const auto& param : pf.params) { + if (first) first = false; + else buf_ << ", "; + buf_ << param; + } + buf_ << ") "; + + this->indent_ = 0; + (*this)(pf.body); + + buf_ << "\n"; + } + + void PrettyPrinter::operator()(const Block& b) { + buf_ << "{\n"; + this->indent_ += 2; + for (int i = 0; i != b.size(); ++i) { + buf_ << indent_str(); + std::visit(*this, b[i]); + buf_ << "\n"; + } + this->indent_ -= 2; + buf_ << indent_str() << "}"; + } + + void PrettyPrinter::operator()(const Assert& a) { + buf_ << reserved_word("assert") << " " << a.assertion; + } + + void PrettyPrinter::operator()(const Assume& a) { + buf_ << reserved_word("assert") << " " << a.assumption; + } + + void PrettyPrinter::operator()(const Call& c) { + buf_ << reserved_word("call") << " " << c.instr->name().str(); + if (c.input_constraint) { + buf_ << " where " << c.input_constraint; + } + } + + void PrettyPrinter::operator()(const Update& u) { + buf_ << reserved_word("update"); + bool first = true; + for (const auto& [x, v] : u) { + buf_ << (first ? " " : ", ") + << x << " = " << v; + first = false; + } + } + + void PrettyPrinter::operator()(const While& w) { + buf_ << reserved_word("while") << " " + << w.loop_condition << " "; + (*this)(w.body); + } + + std::string PrettyPrinter::reserved_word(const std::string& word) { + std::string res = word; + for (auto & c: res) c = toupper(c); + return res; + } + + std::string PrettyPrinter::indent_str() { return std::string(this->indent_, ' '); } + +} + +} \ No newline at end of file diff --git a/src/ila-mngr/v_eq_check_pfrag.cc b/src/ila-mngr/v_eq_check_pfrag.cc index 2648f8720..cc430b271 100644 --- a/src/ila-mngr/v_eq_check_pfrag.cc +++ b/src/ila-mngr/v_eq_check_pfrag.cc @@ -10,92 +10,7 @@ namespace ilang { -namespace pfast { - - bool operator==(const Block& a, const Block& b) { - if (a.size() != b.size()) return false; - for (int i = 0; i != a.size(); ++i) { - if (a[i] == b[i]) continue; - return false; - } - return true; - } - - bool operator==(const ProgramFragment& a, const ProgramFragment& b) { - // [TODO] check == operator on unordered sets - return (a.params == b.params) && (a.body == b.body); - } - - std::string to_string(const Stmt& s) { - PrettyPrinter pp {}; - std::visit(pp, s); - return pp.str(); - } - - std::string to_string(const ProgramFragment& pf) { - PrettyPrinter pp {}; - pp(pf); - return pp.str(); - } - - std::string to_string(const Block& b) { - PrettyPrinter pp {}; - pp(b); - return pp.str(); - } - - void PrettyPrinter::operator()(const ProgramFragment& pf) { - buf_ << reserved_word("main") << "("; - bool first = true; - for (const auto& param : pf.params) { - if (first) first = false; - else buf_ << ", "; - buf_ << param; - } - buf_ << ") "; - - this->indent_ = 0; - (*this)(pf.body); - - buf_ << "\n"; - } - - void PrettyPrinter::operator()(const Block& b) { - buf_ << "{\n"; - this->indent_ += 2; - for (int i = 0; i != b.size(); ++i) { - buf_ << indent_str(); - std::visit(*this, b[i]); - buf_ << "\n"; - } - this->indent_ -= 2; - buf_ << indent_str() << "}"; - } - - void PrettyPrinter::operator()(const Assert& a) { - buf_ << reserved_word("assert") << " " << a.assertion; - } - - void PrettyPrinter::operator()(const Assume& a) { - buf_ << reserved_word("assert") << " " << a.assumption; - } - - void PrettyPrinter::operator()(const Call& c) { - buf_ << reserved_word("call") << " " << c.instr->name().str(); - if (c.input_constraint) { - buf_ << " where " << c.input_constraint; - } - } - - std::string PrettyPrinter::reserved_word(const std::string& word) { - std::string res = word; - for (auto & c: res) c = toupper(c); - return res; - } - - std::string PrettyPrinter::indent_str() { return std::string(this->indent_, ' '); } - -} +template struct dependent_false : std::false_type {}; namespace pgraph { @@ -105,13 +20,13 @@ namespace pgraph { CutPointGraph get_pg() { return pg_; } - void operator()(const ProgramFragment& pf) { + void operator()(const ProgramFragment& pf) { (*this)(pf.body); + running_seq_to_newloc(pgraph::LOC_END); } void operator()(const Block& b) { for (const Stmt& s : b) std::visit(*this, s); - running_seq_to_newloc("end"); } void operator()(const Call& c) { @@ -125,10 +40,22 @@ namespace pgraph { void operator()(const Assert& a) { running_seq_to_newloc("assert"); add_edge_from_curloc({pfast::Assume{asthub::Not(a.assertion)}}, LOC_ERROR); - running_seq_.clear(); running_seq_.emplace_back(Assume{a.assertion}); } + void operator()(const Update& u) { + running_seq_.emplace_back(u); + } + + void operator()(const While& w) { + running_seq_to_newloc("while"); + Location loop_start = curloc(); + running_seq_.emplace_back(Assume{w.loop_condition}); + (*this)(w.body); // visit loop body + running_seq_to_loc(loop_start); // connect loop body to loop start + // continue after loop + running_seq_.emplace_back(Assume{asthub::Not(w.loop_condition)}); + } private: @@ -137,13 +64,16 @@ namespace pgraph { int loc_ctr_ {0}; Location curloc_ {LOC_BEGIN}; - void running_seq_to_newloc(const std::string& loc_ident) { - Location next = newloc(loc_ident); + void running_seq_to_loc(const Location& next) { add_edge_from_curloc(running_seq_, next); set_curloc(next); running_seq_.clear(); } + void running_seq_to_newloc(const std::string& loc_ident) { + running_seq_to_loc(newloc(loc_ident)); + } + void add_edge_from_curloc(const Edge& edge, const Location& loc) { Location cur = curloc(); pg_.try_emplace(cur); // creates an Edges object at pg[cur] if none exists @@ -184,19 +114,22 @@ class PathUnrollerCustomDecidingVars: public PathUnroller { private: const ExprPtrVec custom_deciding_vars_; - }; -PFToCHCEncoder::PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pfast::ProgramFragment& pf): - PFToCHCEncoder(ila, pgraph::program_graph(pf), pf.params) {} +PFToCHCEncoder::PFToCHCEncoder( + z3::context& ctx, z3::fixedpoint& ctxfp, + const InstrLvlAbsPtr& ila, const pfast::ProgramFragment& pf + ): PFToCHCEncoder(ctx, ctxfp, ila, pgraph::program_graph(pf), pf.params) {} -PFToCHCEncoder::PFToCHCEncoder(const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params): - ila_ {ila}, pg_ {pg}, params_ {params} { +PFToCHCEncoder::PFToCHCEncoder( + z3::context& ctx, z3::fixedpoint& ctxfp, + const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params + ): ctx_{ctx}, ctxfp_{ctxfp}, ila_ {ila}, pg_ {pg}, params_ {params} { - z3::params p {ctx_}; - p.set("engine", "spacer"); - ctxfp_.set(p); + // z3::params p {ctx_}; + // p.set("engine", "spacer"); + // ctxfp_.set(p); total_scope_ = params_; // initialize total_scope_, will add more in record_scopes record_scopes(pg_, params_); @@ -244,19 +177,29 @@ void PFToCHCEncoder::encode_transition( for (auto& stmt : transition) { std::visit([&unroller, &seq, &step_ctr](const auto& s) { using T = std::decay_t; - constexpr bool is_Assume = std::is_same_v; - constexpr bool is_Call = std::is_same_v; - constexpr bool is_unknown = !(is_Assume || is_Call); - if constexpr (is_Assume) { + + if constexpr (std::is_same_v) { unroller.AssertStep(s.assumption, step_ctr); - } else if constexpr (is_Call) { + + } else if constexpr (std::is_same_v) { seq.push_back(s.instr); if (s.input_constraint) unroller.AssertStep(s.input_constraint, step_ctr); ++step_ctr; + + } else if constexpr (std::is_same_v) { + // [HACK] convert the update into an instruction + auto instr = Instr::New("update_at_" + std::to_string(step_ctr)); + instr->set_decode(asthub::BoolConst(true)); + for (const auto& [x, v] : s) { + instr->set_update(x, v); + } + seq.push_back(instr); + ++step_ctr; } else { - // compile-time error + // raise compile-time error static_assert( - is_unknown, "record_scopes not implemented for given type of BasicStmt." + dependent_false::value, + "encode_transition not implemented for given type of BasicStmt." ); } }, stmt); @@ -268,13 +211,6 @@ void PFToCHCEncoder::encode_transition( forall_args.push_back(unroller.GetSmtCurrent(v, i)); } } - // while (max_step_count_ <= step_ctr) { - // for (const auto& v : total_scope_) { - // auto v_step = unroller.GetSmtCurrent(v, max_step_count_).decl(); - // ctxfp_.register_relation(v_step); - // } - // ++max_step_count_; - // } z3::expr_vector body {ctx_}; @@ -319,23 +255,23 @@ void PFToCHCEncoder::add_unique_vars( ) { std::visit([&pred_scope, &total_scope](const auto& s) { using T = std::decay_t; - constexpr bool is_Assume = std::is_same_v; - constexpr bool is_Call = std::is_same_v; - constexpr bool is_unknown = !(is_Assume || is_Call); - if constexpr (is_Assume) { - // Assume should not use any new variables. + if constexpr (std::is_same_v) { + // Assume should only use known variables. // OLD: absknob::InsertVar(s.assumption, scope); - } else if constexpr (is_Call) { + } else if constexpr (std::is_same_v) { // add ILA state variables to the scope absknob::InsertStt(s.instr->host(), pred_scope); absknob::InsertVar(s.instr->host(), total_scope); // we do not include the state of children unless an instruction // of the child is called. // Also, the constraint should not use any new variables. + } else if constexpr (std::is_same_v) { + // Update should only use known variables. } else { // compile-time error static_assert( - is_unknown, "record_scopes not implemented for given type of BasicStmt." + dependent_false::value, + "record_scopes not implemented for given type of BasicStmt." ); } }, s); diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc index f465a44c5..d17651a57 100644 --- a/test/t_pfrag.cc +++ b/test/t_pfrag.cc @@ -45,7 +45,7 @@ namespace pf2graph { return m.get(); } - TEST(Pf2cfg, CutPointGraph) { + TEST(Pf2cfg, StructuralEquality) { auto file_dir = os_portable_append_dir(ILANG_TEST_DATA_DIR, "aes"); auto ila_file = os_portable_append_dir(file_dir, "aes.json"); @@ -99,22 +99,33 @@ namespace pf2graph { auto ctr = m->state(COUNTER_STATE_CTR); auto op = m->input(COUNTER_INPUT_OP); + auto ctr_w = ctr->sort()->bit_width(); + auto lowbound = m->NewBvFreeVar("lowbound", ctr_w); + // program fragment - ProgramFragment pf {{ /* no params */ }, { + ProgramFragment pf {{ lowbound }, { Assume {asthub::Eq(ctr, 0)}, + Assume {asthub::Eq(lowbound, 0)}, Call {inst_inc}, + Update {{lowbound, asthub::Add(lowbound, asthub::BvConst(1, ctr_w))}}, Assert {asthub::Eq(ctr, 1)}, Call {inst_dec}, + Update {{lowbound, asthub::Sub(lowbound, asthub::BvConst(1, ctr_w))}}, Call {inst_dec}, + Update {{lowbound, asthub::Sub(lowbound, asthub::BvConst(1, ctr_w))}}, Assert {asthub::Eq(ctr, 0)}, // dec should not go less than 0 Call {inst_inc}, Call {inst_inc}, Call {inst_dec}, Call {inst_inc}, - Assert {asthub::Eq(ctr, 2)} + Update {{lowbound, asthub::Add(lowbound, asthub::BvConst(3, ctr_w))}}, + Assert {asthub::Eq(ctr, 2)}, + Assert {asthub::Le(lowbound, ctr)} }}; - PFToCHCEncoder encoder {m, pf}; + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + PFToCHCEncoder encoder {ctx, ctxfp, m, pf}; // std::cout << "\nEncoded successfully!\n" << std::endl; @@ -122,7 +133,13 @@ namespace pf2graph { EXPECT_NO_THROW(encoder.to_string()); - PFToCHCEncoder::Result res = encoder.check_assertions(); + PFToCHCEncoder::Result res = PFToCHCEncoder::INVALID; + try { + res = encoder.check_assertions(); + } catch (const z3::exception& e) { + std::cout << "Error: " << e << std::endl; + } + EXPECT_EQ(res, PFToCHCEncoder::VALID); } @@ -152,7 +169,9 @@ namespace pf2graph { Assert {asthub::Eq(ctr, 2)} }}; - PFToCHCEncoder encoder {m, pf}; + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + PFToCHCEncoder encoder {ctx, ctxfp, m, pf}; // std::cout << "\nEncoded successfully!\n" << std::endl; @@ -208,7 +227,15 @@ namespace pf2graph { ProgramFragment pf {{ }, b}; /* no params */ - PFToCHCEncoder encoder {m, pf}; + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + + // use spacer instead of datalog engine + z3::params p {ctx}; + p.set("engine", "spacer"); + ctxfp.set(p); + + PFToCHCEncoder encoder {ctx, ctxfp, m, pf}; // std::cout << "\nEncoded successfully!\n" << std::endl; @@ -268,7 +295,15 @@ namespace pf2graph { ProgramFragment pf {{x, y}, b}; /* no params */ - PFToCHCEncoder encoder {m, pf}; + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + + // use spacer instead of datalog + z3::params p {ctx}; + p.set("engine", "spacer"); + ctxfp.set(p); + + PFToCHCEncoder encoder {ctx, ctxfp, m, pf}; // std::cout << "\nEncoded successfully!\n" << std::endl; @@ -280,6 +315,83 @@ namespace pf2graph { } + TEST(Pf2cfg, CounterLoop) { + + InstrLvlAbsPtr m = simple_counter_ila(); + auto inst_inc = m->instr(COUNTER_INSTR_INC); + auto inst_dec = m->instr(COUNTER_INSTR_DEC); + + auto ctr = m->state(COUNTER_STATE_CTR); + auto op = m->input(COUNTER_INPUT_OP); + + auto ctr_w = ctr->sort()->bit_width(); + auto x = m->NewBvFreeVar("x", ctr_w); + auto y = m->NewBvFreeVar("y", ctr_w); + auto i = m->NewBvFreeVar("i", ctr_w); + + auto inv1 = asthub::And(asthub::Eq(ctr, i), asthub::Le(i, x)); + + auto inv2 = asthub::And( + asthub::Ge(ctr, asthub::BvConst(0, ctr_w)), + asthub::Or(asthub::Eq(ctr, asthub::Sub(x, i)), + asthub::Eq(ctr, asthub::BvConst(0, ctr_w))) + ); + + // program fragment + ProgramFragment pf {{ x, y, i }, { + Assume {asthub::Eq(ctr, 0)}, + Assume {asthub::Gt(x, 0)}, + Assume {asthub::Gt(y, x)}, + Assume {asthub::Lt(y, 15)}, + + Update { {i, asthub::BvConst(0, ctr_w)} }, + Assert { inv1 }, + While { asthub::Lt(i, x), { + Assume { inv1 }, + Call { inst_inc, asthub::BoolConst(true) }, + Update { {i, asthub::Add(i, asthub::BvConst(1, ctr_w))} }, + Assert { inv1 }, + }}, + Assume { inv1 }, + + Update { {i, asthub::BvConst(0, ctr_w)} }, + Assert { inv2 }, + While { asthub::Lt(i, y), { + Assume { inv2 }, + Call { inst_dec, asthub::BoolConst(true) }, + Update { {i, asthub::Add(i, asthub::BvConst(1, ctr_w))} }, + Assert { inv2 }, + }}, + Assume { inv2 }, + Assert { asthub::Eq(ctr, 0) } + }}; + + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + + // use spacer instead of datalog + z3::params p {ctx}; + p.set("engine", "spacer"); + ctxfp.set(p); + + PFToCHCEncoder encoder {ctx, ctxfp, m, pf}; + + // std::cout << "\nEncoded successfully!\n" << std::endl; + + // std::cout << encoder.to_string() << std::endl; + + EXPECT_NO_THROW(encoder.to_string()); + + PFToCHCEncoder::Result res = PFToCHCEncoder::INVALID; + try { + res = encoder.check_assertions(); + } catch (const z3::exception& e) { + std::cout << "Error: " << e << std::endl; + } + + EXPECT_EQ(res, PFToCHCEncoder::VALID); + } + } -} \ No newline at end of file +} From bb24198b7581fba6d181e912a3ff9808a2abd5de Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Wed, 28 Jul 2021 11:15:07 -0400 Subject: [PATCH 07/32] Created hierarchy example. --- test/t_pfrag.cc | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc index d17651a57..c3bd3bd9a 100644 --- a/test/t_pfrag.cc +++ b/test/t_pfrag.cc @@ -392,6 +392,125 @@ namespace pf2graph { EXPECT_EQ(res, PFToCHCEncoder::VALID); } + TEST(Pf2cfg, Hierarchy) { + + Ila m {"Broadcast"}; + + constexpr int msg_bits = 64; + constexpr int port_bits = 8; + + auto next_msg = m.NewBvInput("next_msg", msg_bits); + auto msg = m.NewBvState("msg", msg_bits); + + auto n_ports = m.NewBvState("nports", port_bits); + auto ports = m.NewMemState("ports", port_bits, msg_bits); + + auto cur_port = m.NewBvState("cur_port", port_bits); + + m.SetValid((n_ports > 0) & (msg == 0 | next_msg == 0)); + + auto child = m.NewChild("broadcaster"); + child.SetValid((n_ports > 0) & (msg != 0)); + auto bcast_step = child.NewInstr("bcast_step"); + { + bcast_step.SetDecode((msg != 0) & (cur_port < n_ports)); + bcast_step.SetUpdate(ports, Store(ports, cur_port, msg)); + bcast_step.SetUpdate(cur_port, cur_port + 1); + bcast_step.SetUpdate(msg, + Ite(cur_port + 1 == n_ports, BvConst(0, msg_bits), msg)); + } + + auto broadcast = m.NewInstr("bcast"); + { + broadcast.SetDecode((msg == 0) & (next_msg != 0)); + broadcast.SetUpdate(msg, next_msg); + broadcast.SetUpdate(cur_port, BvConst(0, port_bits)); + broadcast.SetProgram(child); + } + + auto num = m.get()->NewBvFreeVar("num", msg_bits); + auto k = m.get()->NewBvFreeVar("k", port_bits); + auto i = m.get()->NewBvFreeVar("i", port_bits); + + auto inv = asthub::And( + asthub::Le(i, n_ports.get()), + asthub::Or( + asthub::Le(i, k), + asthub::Eq(asthub::Load(ports.get(), k), num) + ) + ); + + /* Program fragment: + FORALL num, k, i { + ASSUME 0 < k < n_ports; + CALL broadcast WHERE next_msg = num; + UPDATE i = 0; + ASSERT inv: (i <= n) && (i <= k || ports[k] == num); + WHILE i < n_ports DO { + ASSUME inv; + CALL bcast_step; + UPDATE i = i + 1; + ASSERT inv; + } END; + ASSUME inv; + ASSERT ports[k] == num; + } + */ + + ProgramFragment pf {{num, k, i}, { + // ASSUME 0 < k < n_ports + Assume { asthub::Gt(k, asthub::BvConst(0, port_bits)) }, + Assume { asthub::Gt(n_ports.get(), k) }, + // CALL broadcast WHERE next_msg = num + Call { broadcast.get(), asthub::Eq(next_msg.get(), num) }, + // UPDATE i = 0 + Update { {i, asthub::BvConst(0, port_bits)} }, + // ASSERT inv: (i <= n) && (i <= k || ports[k] == num) + Assert { inv }, + // WHILE i < n_ports DO + While { asthub::Lt(i, n_ports.get()), { + // ASSUME inv + Assume { inv }, + // CALL bcast_step + Call { bcast_step.get() }, + // UPDATE i = i + 1 + Update { {i, asthub::Add(i, asthub::BvConst(1, port_bits))} }, + // ASSERT inv + Assert { inv } + } }, // END + // ASSUME inv + Assume { inv }, + // ASSERT ports[k] = num + Assert { asthub::Eq(asthub::Load(ports.get(), k), num) } + }}; + + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + + // use spacer instead of datalog + z3::params p {ctx}; + p.set("engine", "spacer"); + ctxfp.set(p); + + PFToCHCEncoder encoder {ctx, ctxfp, m.get(), pf}; + + std::cout << "\nEncoded successfully!\n" << std::endl; + + std::cout << encoder.to_string() << std::endl; + + EXPECT_NO_THROW(encoder.to_string()); + + PFToCHCEncoder::Result res = PFToCHCEncoder::INVALID; + try { + res = encoder.check_assertions(); + } catch (const z3::exception& e) { + std::cout << "Error: " << e << std::endl; + } + + EXPECT_EQ(res, PFToCHCEncoder::VALID); + + } + } } From 8cd668c1aebc4ba3e56490cd3cdc6144df5d9d63 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Mon, 16 Aug 2021 18:19:42 -0400 Subject: [PATCH 08/32] Added user-facing support for program-fragment equivalence. --- include/ilang/ila-mngr/u_progfrag.h | 10 +- include/ilang/ila-mngr/v_eq_check_progfrag.h | 16 +- include/ilang/ilang++.h | 114 ++++++++++++++ src/ila-mngr/u_progfrag.cc | 14 +- src/ila-mngr/v_eq_check_pfrag.cc | 10 +- src/ilang++.cc | 154 +++++++++++++++++++ test/CMakeLists.txt | 88 +++++------ test/t_api.cc | 85 ++++++++++ test/t_pfrag.cc | 141 +++++++++++++---- 9 files changed, 534 insertions(+), 98 deletions(-) diff --git a/include/ilang/ila-mngr/u_progfrag.h b/include/ilang/ila-mngr/u_progfrag.h index 70a3c3f0a..d4397ce23 100644 --- a/include/ilang/ila-mngr/u_progfrag.h +++ b/include/ilang/ila-mngr/u_progfrag.h @@ -29,8 +29,9 @@ namespace ilang { struct Call; struct Update; struct While; + struct Block; - using Stmt = std::variant; + using Stmt = std::variant; /// Converts to a string for pretty printing std::string to_string(const Stmt& s); @@ -41,9 +42,6 @@ namespace ilang { using base::operator=; }; - /// Converts to a string for pretty printing - std::string to_string(const Block& b); - struct ProgramFragment { ExprSet params; Block body; @@ -54,6 +52,7 @@ namespace ilang { /// Converts to a string for pretty printing std::string to_string(const ProgramFragment& pf); + std::ostream& operator<<(std::ostream& out, const ProgramFragment& pf); struct Assert { Constraint assertion; @@ -85,7 +84,8 @@ namespace ilang { bool operator==(const Call& b) const { return (instr == b.instr) && (bool(input_constraint) == bool(b.input_constraint)) - && (!input_constraint || asthub::TopEq(input_constraint, b.input_constraint)); + && (!bool(input_constraint) + || asthub::TopEq(input_constraint, b.input_constraint)); } }; diff --git a/include/ilang/ila-mngr/v_eq_check_progfrag.h b/include/ilang/ila-mngr/v_eq_check_progfrag.h index 5cd7a0572..5e2494aed 100644 --- a/include/ilang/ila-mngr/v_eq_check_progfrag.h +++ b/include/ilang/ila-mngr/v_eq_check_progfrag.h @@ -41,9 +41,9 @@ namespace ilang { using Edges = std::vector; - using CutPointGraph = std::unordered_map; + using CutpointGraph = std::unordered_map; - CutPointGraph program_graph(const pfast::ProgramFragment& pf); + CutpointGraph program_graph(const pfast::ProgramFragment& pf); } @@ -56,10 +56,6 @@ namespace ilang { using Predicate = ExprPtr; public: - using Result = z3::check_result; - - static constexpr Result VALID = z3::unsat; - static constexpr Result INVALID = z3::sat; PFToCHCEncoder( z3::context& ctx, z3::fixedpoint& ctxfp, @@ -68,7 +64,7 @@ namespace ilang { std::string to_string(); - Result check_assertions() { + z3::check_result check_assertions() { // z3::expr q = get_error_query(); z3::func_decl_vector q = get_error_queries(); return ctxfp_.query(q); @@ -76,7 +72,7 @@ namespace ilang { private: const InstrLvlAbsPtr ila_; - const pgraph::CutPointGraph pg_; + const pgraph::CutpointGraph pg_; const ExprSet params_; ExprPtrVec pred_scope_; ExprSet total_scope_; @@ -92,7 +88,7 @@ namespace ilang { // internal constructor PFToCHCEncoder( z3::context& ctx, z3::fixedpoint& ctxfp, - const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params); + const InstrLvlAbsPtr& ila, const pgraph::CutpointGraph& pg, const ExprSet& params); void encode(); @@ -101,7 +97,7 @@ namespace ilang { const pgraph::StmtSeq& transition, const pgraph::Location& end); - void record_scopes(const pgraph::CutPointGraph& pg, const ExprSet& params); + void record_scopes(const pgraph::CutpointGraph& pg, const ExprSet& params); void add_unique_vars( const pgraph::BasicStmt& s, ExprSet& pred_scope, ExprSet& total_scope); diff --git a/include/ilang/ilang++.h b/include/ilang/ilang++.h index 09f7d6616..870c3ff63 100644 --- a/include/ilang/ilang++.h +++ b/include/ilang/ilang++.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #ifdef SMTSWITCH_INTERFACE @@ -56,6 +57,8 @@ class Expr; class Instr; class InstrLvlAbs; class Unroller; +namespace pfast { class ProgramFragment; } +class PFToCHCEncoder; // forward declaration class Ila; @@ -756,6 +759,117 @@ smt::Term ResetAndGetSmtTerm(smt::SmtSolver& solver, const ExprRef& expr, #endif // SMTSWITCH_INTERFACE +namespace programfragment { + + using Constraint = ExprRef; + + struct Assert; + struct Assume; + struct Call; + struct Update; + struct While; + struct Block; + + using Stmt = std::variant; + + struct Block : public std::vector { + using base=std::vector; + using base::base; + using base::operator=; + }; + + struct Assert { + Constraint assertion; + }; + + struct Assume { + Constraint assumption; + }; + + struct Call { + InstrRef instr; + std::map input_map {}; + }; + + struct Update: public std::vector> { + using base=std::vector>; + using base::base; + using base::operator=; + }; + + struct While { + Constraint loop_condition; + Constraint invariant; + Block body; + + While(const Constraint& loop_condition, const Block& body); + While(const Constraint& loop_condition, + const Constraint& invariant, const Block& body); + }; + + class ProgramFragment { + + friend bool operator==(const ProgramFragment& a, const ProgramFragment& b); + friend std::ostream& operator<<(std::ostream& out, const ProgramFragment& pf); + + public: + using PfragPtr = std::shared_ptr; + using PfragConstPtr = std::shared_ptr; + + ProgramFragment(); + ProgramFragment(const Block& b); + ~ProgramFragment()=default; + + ExprRef NewBoolVar(const std::string& name); + ExprRef NewBvVar(const std::string& name, const int& bitwidth); + ExprRef NewMemVar(const std::string& name, + const int& addrwidth, const int& datawidth); + + void RegisterApplicationParam(const ExprRef& p); + void RegisterHardwareParam(const ExprRef& p); + + void AddStatement(const Stmt& s); + void AddStatements(const Block& b); + + PfragConstPtr get() const; + + private: + PfragPtr pf_; + }; + + /// Checks structural equality + bool operator==(const ProgramFragment& a, const ProgramFragment& b); + + /// Prints a program fragment + std::ostream& operator<<(std::ostream& out, const ProgramFragment& pf); + +} // namespace programfragment + +enum class ChcResult: char { + valid=z3::unsat, + invalid=z3::sat, + unknown=z3::unknown +}; + +std::ostream& operator<<(std::ostream& out, const ChcResult& r); + +class IlaToChcEncoder { + public: + IlaToChcEncoder( + z3::context& ctx, z3::fixedpoint& ctxfp, + const Ila& ila, const programfragment::ProgramFragment& pf); + + ~IlaToChcEncoder()=default; + + std::string to_string(); + + ChcResult check_assertions(); + + private: + std::shared_ptr impl_; +}; + + } // namespace ilang #endif // ILANG_ILANG_CPP_H__ diff --git a/src/ila-mngr/u_progfrag.cc b/src/ila-mngr/u_progfrag.cc index 9379d1275..ff651c375 100644 --- a/src/ila-mngr/u_progfrag.cc +++ b/src/ila-mngr/u_progfrag.cc @@ -28,12 +28,16 @@ namespace pfast { return pp.str(); } - std::string to_string(const Block& b) { - PrettyPrinter pp {}; - pp(b); - return pp.str(); + std::ostream& operator<<(std::ostream& out, const ProgramFragment& pf) { + return (out << to_string(pf)); } + // std::string to_string(const Block& b) { + // PrettyPrinter pp {}; + // pp(b); + // return pp.str(); + // } + void PrettyPrinter::operator()(const ProgramFragment& pf) { buf_ << reserved_word("main") << "("; bool first = true; @@ -67,7 +71,7 @@ namespace pfast { } void PrettyPrinter::operator()(const Assume& a) { - buf_ << reserved_word("assert") << " " << a.assumption; + buf_ << reserved_word("assume") << " " << a.assumption; } void PrettyPrinter::operator()(const Call& c) { diff --git a/src/ila-mngr/v_eq_check_pfrag.cc b/src/ila-mngr/v_eq_check_pfrag.cc index cc430b271..4ce6d5592 100644 --- a/src/ila-mngr/v_eq_check_pfrag.cc +++ b/src/ila-mngr/v_eq_check_pfrag.cc @@ -18,7 +18,7 @@ namespace pgraph { struct PGraphBuilder { - CutPointGraph get_pg() { return pg_; } + CutpointGraph get_pg() { return pg_; } void operator()(const ProgramFragment& pf) { (*this)(pf.body); @@ -59,7 +59,7 @@ namespace pgraph { private: - CutPointGraph pg_ {}; + CutpointGraph pg_ {}; StmtSeq running_seq_ {}; int loc_ctr_ {0}; Location curloc_ {LOC_BEGIN}; @@ -90,7 +90,7 @@ namespace pgraph { }; - CutPointGraph program_graph(const pfast::ProgramFragment& pf) { + CutpointGraph program_graph(const pfast::ProgramFragment& pf) { PGraphBuilder builder {}; builder(pf); return builder.get_pg(); @@ -124,7 +124,7 @@ PFToCHCEncoder::PFToCHCEncoder( PFToCHCEncoder::PFToCHCEncoder( z3::context& ctx, z3::fixedpoint& ctxfp, - const InstrLvlAbsPtr& ila, const pgraph::CutPointGraph& pg, const ExprSet& params + const InstrLvlAbsPtr& ila, const pgraph::CutpointGraph& pg, const ExprSet& params ): ctx_{ctx}, ctxfp_{ctxfp}, ila_ {ila}, pg_ {pg}, params_ {params} { // z3::params p {ctx_}; @@ -237,7 +237,7 @@ void PFToCHCEncoder::encode_transition( ctxfp_.add_rule(rule, ctx_.str_symbol((start + "_to_" + end).c_str())); } -void PFToCHCEncoder::record_scopes(const pgraph::CutPointGraph& pg, const ExprSet& params) { +void PFToCHCEncoder::record_scopes(const pgraph::CutpointGraph& pg, const ExprSet& params) { ExprSet uvars {params}; for (const auto& [loc, edges] : pg) { for (const auto& edgeTo : edges) { diff --git a/src/ilang++.cc b/src/ilang++.cc index 6091c964e..2811e17d8 100644 --- a/src/ilang++.cc +++ b/src/ilang++.cc @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -840,6 +842,158 @@ z3::func_decl IlaZ3Unroller::GetZ3FuncDecl(const FuncRef& f) const { return univ_->GetZ3FuncDecl(f.get()); } +namespace programfragment { + + template struct dependent_false : std::false_type {}; + + pfast::Stmt convert_stmt(const Stmt& s); + + pfast::Block convert_block(const Block& b) { + pfast::Block converted; + for (const auto& s : b) { + converted.push_back(convert_stmt(s)); + } + return converted; + } + + pfast::Stmt convert_stmt(const Stmt& s) { + return std::visit([](const auto& s) -> pfast::Stmt { + using T = std::decay_t; + + if constexpr (std::is_same_v) { + return pfast::Assert{s.assertion.get()}; + + } else if constexpr (std::is_same_v) { + return pfast::Assume{s.assumption.get()}; + + } else if constexpr (std::is_same_v) { + pfast::Constraint c = asthub::BoolConst(true); + for (const auto& [input, assignment] : s.input_map) { + c = asthub::And(c, asthub::Eq(input.get(), assignment.get())); + } + return pfast::Call{s.instr.get(), c}; + + } else if constexpr (std::is_same_v) { + pfast::Update u {}; + for (const auto& [param, update] : s) { + u.emplace(param.get(), update.get()); + } + return u; + + } else if constexpr (std::is_same_v) { + pfast::Constraint inv = s.invariant.get(); + pfast::Block body = convert_block(s.body); + if (!bool(inv)) { + return pfast::While{s.loop_condition.get(), body}; + } + return pfast::Block{ + pfast::Assert{inv}, + pfast::While{s.loop_condition.get(), pfast::Block{ + pfast::Assume{inv}, + body, + pfast::Assert{inv} + }}, + pfast::Assume{inv} + }; + + } else if constexpr (std::is_same_v) { + return convert_block(s); + + } else { + // raise compile-time error + static_assert( + dependent_false::value, + "convert_stmt not implemented for given type of Stmt." + ); + } + + }, s); + } + + While::While(const Constraint& loop_condition, const Block& body): + While(loop_condition, {nullptr}, body) {} + + While::While( + const Constraint& loop_condition, const Constraint& invariant, + const Block& body + ): loop_condition {loop_condition}, invariant {invariant}, body {body} {} + + ProgramFragment::ProgramFragment(): ProgramFragment(Block{}) {} + + ProgramFragment::ProgramFragment(const Block& b) + : pf_ {new pfast::ProgramFragment{{}, convert_block(b)}} {} + + ExprRef ProgramFragment::NewBoolVar(const std::string& name) { + auto v = asthub::NewBoolVar(name); + pf_->params.insert(v); + return ExprRef{v}; + } + + ExprRef ProgramFragment::NewBvVar(const std::string& name, const int& bitwidth) { + auto v = asthub::NewBvVar(name, bitwidth); + pf_->params.insert(v); + return ExprRef{v}; + } + + ExprRef ProgramFragment::NewMemVar( + const std::string& name, const int& addrwidth, const int& datawidth) { + auto v = asthub::NewMemVar(name, addrwidth, datawidth); + pf_->params.insert(v); + return ExprRef{v}; + } + + void ProgramFragment::RegisterApplicationParam(const ExprRef& p) { + // TODO: what does this entail? + } + + void ProgramFragment::RegisterHardwareParam(const ExprRef& p) { + // TODO: what does this entail? + } + + void ProgramFragment::AddStatement(const Stmt& s) { + pf_->body.push_back(convert_stmt(s)); + } + + void ProgramFragment::AddStatements(const Block& b) { + for (auto& s : b) { pf_->body.push_back(convert_stmt(s)); } + } + + ProgramFragment::PfragConstPtr ProgramFragment::get() const { + return pf_; + } + + bool operator==(const ProgramFragment& a, const ProgramFragment& b) { + return *(a.pf_) == *(b.pf_); + } + + std::ostream& operator<<(std::ostream& out, const ProgramFragment& pf) { + return (out << *(pf.pf_)); + } + +} // namespace programfragment + +std::ostream& operator<<(std::ostream& out, const ChcResult& r) { + switch (r) { + case ChcResult::valid: return out << "valid"; + case ChcResult::invalid: return out << "invalid"; + case ChcResult::unknown: return out << "unknown"; + } + return out; +} + +IlaToChcEncoder::IlaToChcEncoder( + z3::context& ctx, z3::fixedpoint& ctxfp, + const Ila& ila, const programfragment::ProgramFragment& pf +): impl_ {std::make_shared( + ctx, ctxfp, ila.get(), *(pf.get()) + )} {} + +std::string IlaToChcEncoder::to_string() { return impl_->to_string(); } + +ChcResult IlaToChcEncoder::check_assertions() { + return static_cast(impl_->check_assertions()); +} + void LogLevel(const int& lvl) { SetLogLevel(lvl); } void LogPath(const std::string& path) { SetLogPath(path); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cf1ddb69e..1171ae68c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -47,51 +47,51 @@ package_add_test(${ILANG_TEST_MAIN} unit-src/simple_cpu.cc unit-src/stream_buffer.cc unit-src/util.cc - # t_ast_hub.cc - # t_api.cc - # t_case_aes_eq.cc - # t_copy.cc - # t_crr.cc - # t_eq_check.cc - # t_expr.cc - # t_expr_const.cc - # t_expr_op.cc - # t_expr_var.cc - # t_func.cc - # t_hash_ast.cc - # t_ila.cc - # t_ilator.cc - # t_instr.cc - # t_instr_seq.cc - # t_keyvec.cc - # t_legacy_bmc.cc - # t_log.cc - # t_main.cc - # t_mapset.cc - # t_mcm.cc - # t_mngr_absknob.cc - # t_pass.cc + t_ast_hub.cc + t_api.cc + t_case_aes_eq.cc + t_copy.cc + t_crr.cc + t_eq_check.cc + t_expr.cc + t_expr_const.cc + t_expr_op.cc + t_expr_var.cc + t_func.cc + t_hash_ast.cc + t_ila.cc + t_ilator.cc + t_instr.cc + t_instr_seq.cc + t_keyvec.cc + t_legacy_bmc.cc + t_log.cc + t_main.cc + t_mapset.cc + t_mcm.cc + t_mngr_absknob.cc + t_pass.cc t_pfrag.cc - # t_portable.cc - # t_smt_in.cc - # t_smt_shim.cc - # t_smt_switch_itf.cc - # t_smt_trans.cc - # t_sort.cc - # t_symbol.cc - # t_unroll_seq.cc - # t_unroller_smt.cc - # t_util.cc - # t_verilog_analysis.cc - # t_verilog_analysis_error.cc - # t_verilog_gen.cc - # t_verilog_mod.cc - # t_vtarget_gen.cc - # t_inv_extract.cc - # t_z3adapter.cc - # t_z3sanity.cc - # t_inv_syn.cc - # t_inv_obj.cc + t_portable.cc + t_smt_in.cc + t_smt_shim.cc + t_smt_switch_itf.cc + t_smt_trans.cc + t_sort.cc + t_symbol.cc + t_unroll_seq.cc + t_unroller_smt.cc + t_util.cc + t_verilog_analysis.cc + t_verilog_analysis_error.cc + t_verilog_gen.cc + t_verilog_mod.cc + t_vtarget_gen.cc + t_inv_extract.cc + t_z3adapter.cc + t_z3sanity.cc + t_inv_syn.cc + t_inv_obj.cc ) if(${ILANG_BUILD_SYNTH}) diff --git a/test/t_api.cc b/test/t_api.cc index e2da6facf..fc7051895 100644 --- a/test/t_api.cc +++ b/test/t_api.cc @@ -599,4 +599,89 @@ TEST(TestApi, Portable) { DisableDebug("Portable"); } +TEST(TestApi, Ila2Chc) { + + using namespace programfragment; + + Ila m {"Counter"}; + + auto ctr_w = 8; + + auto ctr = m.NewBvState("ctr", ctr_w); + auto op = m.NewBvInput("op", 2); + + // increment + auto inst_inc = m.NewInstr("inc"); + inst_inc.SetDecode(op == 0); + inst_inc.SetUpdate(ctr, ctr + 1); + + // decrement + auto inst_dec = m.NewInstr("dec"); + inst_dec.SetDecode(op == 1); + inst_dec.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr - 1)); + + // program fragment + ProgramFragment pf {}; + + auto x = pf.NewBvVar("x", ctr_w); // asthub::BvConst(3, ctr_w); + auto y = pf.NewBvVar("y", ctr_w); // asthub::BvConst(4, ctr_w); + auto i = pf.NewBvVar("i", ctr_w); + auto j = pf.NewBvVar("j", ctr_w); + + auto inv1 = ((ctr == i * y) & (i <= x)); + auto inv2 = ((ctr == (i * y + j)) & (j <= y)); + + pf.AddStatements({ + Assume {ctr == 0}, + Assume {x > BvConst(0, ctr_w)}, + Assume {y > BvConst(0, ctr_w)}, + Assume {x < BvConst(3, ctr_w)}, // keeps runtime short + Assume {y < BvConst(4, ctr_w)}, // keeps runtime short + + Update { {i, BvConst(0, ctr_w)} }, + Assert { inv1 }, + While { i < x, { + Assume { inv1 }, + Update { {j, BvConst(0, ctr_w)} }, + Assert { inv2 }, + While { j < y, { + Assume { inv2 }, + Call { inst_inc }, + Update { {j, j + 1} }, + Assert { inv2 } + }}, + Assume { inv2 }, + Update { {i, i + 1} }, + Assert { inv1 }, + }}, + Assume { inv1 }, + Assert { ctr == x * y } + }); + + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + + // use spacer instead of datalog + z3::params p {ctx}; + p.set("engine", "spacer"); + ctxfp.set(p); + + IlaToChcEncoder encoder {ctx, ctxfp, m, pf}; + + // std::cout << "\nEncoded successfully!\n" << std::endl; + + // std::cout << encoder.to_string() << std::endl; + + EXPECT_NO_THROW(encoder.to_string()); + + ChcResult res = ChcResult::invalid; + try { + res = encoder.check_assertions(); + } catch (const z3::exception& e) { + std::cout << "Error: " << e << std::endl; + } + + EXPECT_EQ(res, ChcResult::valid); +} + } // namespace ilang diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc index c3bd3bd9a..111ece387 100644 --- a/test/t_pfrag.cc +++ b/test/t_pfrag.cc @@ -45,7 +45,7 @@ namespace pf2graph { return m.get(); } - TEST(Pf2cfg, StructuralEquality) { + TEST(ProgFrag, StructuralEquality) { auto file_dir = os_portable_append_dir(ILANG_TEST_DATA_DIR, "aes"); auto ila_file = os_portable_append_dir(file_dir, "aes.json"); @@ -90,7 +90,7 @@ namespace pf2graph { EXPECT_FALSE(cmp_diff_fragment); } - TEST(Pf2cfg, Counter) { + TEST(Pf2chc, Counter) { InstrLvlAbsPtr m = simple_counter_ila(); auto inst_inc = m->instr(COUNTER_INSTR_INC); @@ -114,12 +114,14 @@ namespace pf2graph { Call {inst_dec}, Update {{lowbound, asthub::Sub(lowbound, asthub::BvConst(1, ctr_w))}}, Assert {asthub::Eq(ctr, 0)}, // dec should not go less than 0 - Call {inst_inc}, - Call {inst_inc}, - Call {inst_dec}, - Call {inst_inc}, - Update {{lowbound, asthub::Add(lowbound, asthub::BvConst(3, ctr_w))}}, - Assert {asthub::Eq(ctr, 2)}, + Block{ + Call {inst_inc}, + Call {inst_inc}, + Call {inst_dec}, + Call {inst_inc}, + Update {{lowbound, asthub::Add(lowbound, asthub::BvConst(3, ctr_w))}}, + Assert {asthub::Eq(ctr, 2)}, + }, Assert {asthub::Le(lowbound, ctr)} }}; @@ -133,17 +135,17 @@ namespace pf2graph { EXPECT_NO_THROW(encoder.to_string()); - PFToCHCEncoder::Result res = PFToCHCEncoder::INVALID; + z3::check_result res = z3::sat; try { res = encoder.check_assertions(); } catch (const z3::exception& e) { std::cout << "Error: " << e << std::endl; } - EXPECT_EQ(res, PFToCHCEncoder::VALID); + EXPECT_EQ(res, z3::unsat); } - TEST(Pf2cfg, CounterFail) { + TEST(Pf2chc, CounterFail) { // create ILA @@ -177,11 +179,11 @@ namespace pf2graph { // std::cout << encoder.to_string() << std::endl; - PFToCHCEncoder::Result res = encoder.check_assertions(); - EXPECT_EQ(res, PFToCHCEncoder::INVALID); + z3::check_result res = encoder.check_assertions(); + EXPECT_EQ(res, z3::sat); } - TEST(Pf2cfg, UnrollerSMTTest) { + TEST(Pf2chc, UnrollerSMTTest) { using namespace pfast; using namespace asthub; @@ -241,12 +243,12 @@ namespace pf2graph { // std::cout << encoder.to_string() << std::endl; - PFToCHCEncoder::Result res = encoder.check_assertions(); - EXPECT_EQ(res, PFToCHCEncoder::VALID); + z3::check_result res = encoder.check_assertions(); + EXPECT_EQ(res, z3::unsat); } - TEST(Pf2cfg, UnrollerSMTTestWithParams) { + TEST(Pf2chc, UnrollerSMTTestWithParams) { using namespace pfast; using namespace asthub; @@ -256,6 +258,7 @@ namespace pf2graph { // params ExprPtr x = m->NewBvFreeVar("x", 8); ExprPtr y = m->NewBvFreeVar("y", 8); + ExprPtr unused = m->NewBvFreeVar("unused", 8); // fragment body Block b; @@ -293,7 +296,7 @@ namespace pf2graph { // check stored result b.push_back(Assert{Eq(Load(m->state("mem"), 2), asthub::Add(x, y))}); - ProgramFragment pf {{x, y}, b}; /* no params */ + ProgramFragment pf {{x, y, unused}, b}; z3::context ctx; z3::fixedpoint ctxfp {ctx}; @@ -309,13 +312,13 @@ namespace pf2graph { // std::cout << encoder.to_string() << std::endl; - PFToCHCEncoder::Result res = encoder.check_assertions(); + z3::check_result res = encoder.check_assertions(); - EXPECT_EQ(res, PFToCHCEncoder::VALID); + EXPECT_EQ(res, z3::unsat); } - TEST(Pf2cfg, CounterLoop) { + TEST(Pf2chc, CounterLoop) { InstrLvlAbsPtr m = simple_counter_ila(); auto inst_inc = m->instr(COUNTER_INSTR_INC); @@ -342,7 +345,7 @@ namespace pf2graph { Assume {asthub::Eq(ctr, 0)}, Assume {asthub::Gt(x, 0)}, Assume {asthub::Gt(y, x)}, - Assume {asthub::Lt(y, 15)}, + Assume {asthub::Lt(y, 15)}, // keeps runtime short Update { {i, asthub::BvConst(0, ctr_w)} }, Assert { inv1 }, @@ -382,17 +385,17 @@ namespace pf2graph { EXPECT_NO_THROW(encoder.to_string()); - PFToCHCEncoder::Result res = PFToCHCEncoder::INVALID; + z3::check_result res = z3::sat; try { res = encoder.check_assertions(); } catch (const z3::exception& e) { std::cout << "Error: " << e << std::endl; } - EXPECT_EQ(res, PFToCHCEncoder::VALID); + EXPECT_EQ(res, z3::unsat); } - TEST(Pf2cfg, Hierarchy) { + TEST(Pf2chc, Hierarchy) { Ila m {"Broadcast"}; @@ -494,21 +497,101 @@ namespace pf2graph { PFToCHCEncoder encoder {ctx, ctxfp, m.get(), pf}; - std::cout << "\nEncoded successfully!\n" << std::endl; + // std::cout << "\nEncoded successfully!\n" << std::endl; - std::cout << encoder.to_string() << std::endl; + // std::cout << encoder.to_string() << std::endl; EXPECT_NO_THROW(encoder.to_string()); - PFToCHCEncoder::Result res = PFToCHCEncoder::INVALID; + z3::check_result res = z3::sat; + try { res = encoder.check_assertions(); } catch (const z3::exception& e) { std::cout << "Error: " << e << std::endl; } - EXPECT_EQ(res, PFToCHCEncoder::VALID); + EXPECT_EQ(res, z3::unsat); + + } + + TEST(Pf2chc, CounterLoopNested) { + + InstrLvlAbsPtr m = simple_counter_ila(); + auto inst_inc = m->instr(COUNTER_INSTR_INC); + auto inst_dec = m->instr(COUNTER_INSTR_DEC); + + auto ctr = m->state(COUNTER_STATE_CTR); + auto op = m->input(COUNTER_INPUT_OP); + + auto ctr_w = ctr->sort()->bit_width(); + auto x = asthub::NewBvVar("x", ctr_w); // asthub::BvConst(3, ctr_w); + auto y = asthub::NewBvVar("y", ctr_w); // asthub::BvConst(4, ctr_w); + auto i = asthub::NewBvVar("i", ctr_w); + auto j = asthub::NewBvVar("j", ctr_w); + + auto inv1 = asthub::And( + asthub::Eq(ctr, asthub::Mul(i, y)), + asthub::Le(i, x) + ); + + auto inv2 = asthub::And( + asthub::Eq(ctr, asthub::Add(asthub::Mul(i, y), j)), + asthub::Le(j, y) + ); + + // program fragment + ProgramFragment pf {{ x, y, i, j }, { + Assume {asthub::Eq(ctr, 0)}, + Assume {asthub::Gt(x, 0)}, + Assume {asthub::Gt(y, 0)}, + Assume {asthub::Lt(x, 3)}, // keeps runtime short + Assume {asthub::Lt(y, 4)}, // keeps runtime short + + Update { {i, asthub::BvConst(0, ctr_w)} }, + Assert { inv1 }, + While { asthub::Lt(i, x), { + Assume { inv1 }, + Update { {j, asthub::BvConst(0, ctr_w)} }, + Assert { inv2 }, + While { asthub::Lt(j, y), { + Assume { inv2 }, + Call { inst_inc }, + Update { {j, asthub::Add(j, asthub::BvConst(1, ctr_w))} }, + Assert { inv2 } + }}, + Assume { inv2 }, + Update { {i, asthub::Add(i, asthub::BvConst(1, ctr_w))} }, + Assert { inv1 }, + }}, + Assume { inv1 }, + Assert { asthub::Eq(ctr, asthub::Mul(x, y)) } + }}; + + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + + // use spacer instead of datalog + z3::params p {ctx}; + p.set("engine", "spacer"); + ctxfp.set(p); + + PFToCHCEncoder encoder {ctx, ctxfp, m, pf}; + + // std::cout << "\nEncoded successfully!\n" << std::endl; + + // std::cout << encoder.to_string() << std::endl; + + EXPECT_NO_THROW(encoder.to_string()); + + z3::check_result res = z3::sat; + try { + res = encoder.check_assertions(); + } catch (const z3::exception& e) { + std::cout << "Error: " << e << std::endl; + } + EXPECT_EQ(res, z3::unsat); } } From dda3ee7b6c63e29ea3b4e2bae6481e8a02b89ff6 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Tue, 5 Oct 2021 17:16:48 -0400 Subject: [PATCH 09/32] WIP: created front-end language. --- include/ilang/ilang++.h | 108 ++++++++++++++++++++++++++++--- src/ila-mngr/v_eq_check_pfrag.cc | 5 +- src/ilang++.cc | 38 +++++++++-- test/t_api.cc | 12 ++-- 4 files changed, 140 insertions(+), 23 deletions(-) diff --git a/include/ilang/ilang++.h b/include/ilang/ilang++.h index 870c3ff63..c183dea7b 100644 --- a/include/ilang/ilang++.h +++ b/include/ilang/ilang++.h @@ -761,8 +761,40 @@ smt::Term ResetAndGetSmtTerm(smt::SmtSolver& solver, const ExprRef& expr, namespace programfragment { + /// \brief The type of a constraint in the program fragment language. using Constraint = ExprRef; + + /// \brief Wrapper for a bitvector that allows it to be automatically + /// zero/sign extended as part of an Update statement. + struct ExtendableBv { + ExprRef expr; + enum ExtMode {EXT_ZERO, EXT_SIGNED} ext_mode; + + /// \brief Constructs an ExtendableBv from a bitvector expr. + /// \param[in] expr The ILA expression to extend as needed. + explicit ExtendableBv(const ExprRef& expr): ExtendableBv{expr, EXT_ZERO} {} + + /// \brief Constructs an ExtendableBv from a bitvector expr. + /// \param[in] expr The ILA expression to extend as needed. + /// \param[in] ext_mode How the expression should be extended. + explicit ExtendableBv(const ExprRef& expr, ExtMode ext_mode) + : expr{expr}, ext_mode{ext_mode} {} + + /// Default destructor. + ~ExtendableBv()=default; + + /// \brief Extends the wrapped bitvector expression to match the given expr. + /// \param[in] other The ILA expression whose bitwidth to match. + ExprRef extend_to(const ExprRef& other) const; + }; + + /// \brief Valid types for the RHS of an update. + using AssignmentExpr = std::variant; + /// \brief A list of variables and what they should be updated to. + using VarMap = std::vector>; + + /// forward declarations struct Assert; struct Assume; struct Call; @@ -770,67 +802,112 @@ namespace programfragment { struct While; struct Block; + /// \brief A statement within a program fragment. using Stmt = std::variant; + /// \brief A sequence of statements. struct Block : public std::vector { using base=std::vector; using base::base; using base::operator=; }; + /// \brief An assertion that a given constraint holds at a given + /// location of a program fragment. + /// Will be checked once encoded for and sent to a solver. struct Assert { - Constraint assertion; + Constraint assertion; // The constraint that must hold. }; + /// \brief An assumption that a given constraint holds at a given + /// location of a program fragment. struct Assume { - Constraint assumption; + Constraint assumption; // The constraint assumed to hold. }; + /// \brief "Calls" the given instruction with the given inputs. + /// Assumes the decode of the given instruction holds. struct Call { - InstrRef instr; - std::map input_map {}; + InstrRef instr; // The instruction to call. + VarMap inputs {}; // Assignments to the inputs of instr's ILA. }; - struct Update: public std::vector> { - using base=std::vector>; + /// \brief Updates the given variables using the expressions they + /// are mapped to. + struct Update: public VarMap { + using base=VarMap; using base::base; using base::operator=; }; + /// \brief A while loop. Repeatedly executes the body while the + /// given loop condition holds. struct While { - Constraint loop_condition; + Constraint loop_condition; // The loop condition + /// An invariant on the loop provided to help verification. + /// The invariant is asserted before the loop and at the end + /// of the loop body, and assumed at the start of the loop + /// body and after the loop ends. Constraint invariant; - Block body; + Block body; // The body of the loop + /// \brief Constructs a While loop given a loop condition and body. + /// \param[in] loop_condition The loop condition. + /// \param[in] body The loop body. While(const Constraint& loop_condition, const Block& body); + /// \brief Constructs a While loop given a loop condition and body. + /// \param[in] loop_condition The loop condition. + /// \param[in] invariant An invariant on the loop provided + /// to help verification. + /// The invariant is asserted before the loop and at the end + /// of the loop body, and assumed at the start of the loop + /// body and after the loop ends. + /// \param[in] body The loop body. While(const Constraint& loop_condition, const Constraint& invariant, const Block& body); }; + /// \brief A program using an ILA. class ProgramFragment { friend bool operator==(const ProgramFragment& a, const ProgramFragment& b); friend std::ostream& operator<<(std::ostream& out, const ProgramFragment& pf); public: + /// Pointer to the internal representation of a program fragment. using PfragPtr = std::shared_ptr; + /// Pointer to the internal representation of a program fragment. using PfragConstPtr = std::shared_ptr; + /// Constructs an empty program fragment. ProgramFragment(); + + /// Constructs a program fragment consisting of the given block. ProgramFragment(const Block& b); + + /// Destroys a program fragment ~ProgramFragment()=default; + /// Creates a new boolean variable and registers it as a parameter ExprRef NewBoolVar(const std::string& name); + + /// Creates a new bitvector variable and registers it as a parameter ExprRef NewBvVar(const std::string& name, const int& bitwidth); + + /// Creates a new bitvector variable and registers it as a parameter ExprRef NewMemVar(const std::string& name, const int& addrwidth, const int& datawidth); - void RegisterApplicationParam(const ExprRef& p); - void RegisterHardwareParam(const ExprRef& p); + // void RegisterApplicationParam(const ExprRef& p); + // void RegisterHardwareParam(const ExprRef& p); + /// Adds the given statement to the program fragment void AddStatement(const Stmt& s); + + /// Adds the given block of statements to the program fragment void AddStatements(const Block& b); + /// Returns the internal representation of the program fragment PfragConstPtr get() const; private: @@ -845,6 +922,8 @@ namespace programfragment { } // namespace programfragment + +/// An enum representing the result of verifying the system of CHCs enum class ChcResult: char { valid=z3::unsat, invalid=z3::sat, @@ -853,16 +932,25 @@ enum class ChcResult: char { std::ostream& operator<<(std::ostream& out, const ChcResult& r); +/// Encodes a program fragment on an ILA into a CHC class IlaToChcEncoder { public: + // TODO: move parameter registration in program fragments to + // internal representation, rename IlaToChcEncoder to PfToChcIncoder, + // and remove the Ila argument. + + /// Constructs an IlaToChcEncoder IlaToChcEncoder( z3::context& ctx, z3::fixedpoint& ctxfp, const Ila& ila, const programfragment::ProgramFragment& pf); + /// Destructor ~IlaToChcEncoder()=default; + /// Gives the system of CHCs as a string std::string to_string(); + /// Checks the assertion of the system of CHCs ChcResult check_assertions(); private: diff --git a/src/ila-mngr/v_eq_check_pfrag.cc b/src/ila-mngr/v_eq_check_pfrag.cc index 4ce6d5592..95a311012 100644 --- a/src/ila-mngr/v_eq_check_pfrag.cc +++ b/src/ila-mngr/v_eq_check_pfrag.cc @@ -38,8 +38,9 @@ namespace pgraph { } void operator()(const Assert& a) { - running_seq_to_newloc("assert"); - add_edge_from_curloc({pfast::Assume{asthub::Not(a.assertion)}}, LOC_ERROR); + running_seq_.emplace_back(pfast::Assume{asthub::Not(a.assertion)}); + add_edge_from_curloc(running_seq_, LOC_ERROR); + running_seq_.pop_back(); running_seq_.emplace_back(Assume{a.assertion}); } diff --git a/src/ilang++.cc b/src/ilang++.cc index 2811e17d8..186ee104b 100644 --- a/src/ilang++.cc +++ b/src/ilang++.cc @@ -846,6 +846,34 @@ namespace programfragment { template struct dependent_false : std::false_type {}; + ExprRef ExtendableBv::extend_to(const ExprRef& other) const { + int diff = expr.bit_width() - other.bit_width(); + if (diff == 0) return expr; + switch (ext_mode) { + case EXT_ZERO: return ZExt(expr, other.bit_width()); + case EXT_SIGNED: return SExt(expr, other.bit_width()); + } + return expr; + } + + ExprRef try_ext(const AssignmentExpr& expr, const ExprRef& target) { + return std::visit([&target](const auto& to_ext) { + using T = std::decay_t; + if constexpr (std::is_same_v) return to_ext; + else if constexpr (std::is_same_v) { + return BvConst(to_ext, target.bit_width()); + } else if constexpr (std::is_same_v) { + return to_ext.extend_to(target); + } else { + // raise compile-time error + static_assert( + dependent_false::value, + "try_ext not implemented for given variant of AssignmentExpr" + ); + } + }, expr); + } + pfast::Stmt convert_stmt(const Stmt& s); pfast::Block convert_block(const Block& b) { @@ -867,16 +895,16 @@ namespace programfragment { return pfast::Assume{s.assumption.get()}; } else if constexpr (std::is_same_v) { - pfast::Constraint c = asthub::BoolConst(true); - for (const auto& [input, assignment] : s.input_map) { - c = asthub::And(c, asthub::Eq(input.get(), assignment.get())); + ExprRef c = asthub::BoolConst(true); + for (const auto& [input, assignment] : s.inputs) { + c = c & (input == try_ext(assignment, input)); } - return pfast::Call{s.instr.get(), c}; + return pfast::Call{s.instr.get(), c.get()}; } else if constexpr (std::is_same_v) { pfast::Update u {}; for (const auto& [param, update] : s) { - u.emplace(param.get(), update.get()); + u.emplace(param.get(), try_ext(update, param).get()); } return u; diff --git a/test/t_api.cc b/test/t_api.cc index fc7051895..c24258e73 100644 --- a/test/t_api.cc +++ b/test/t_api.cc @@ -633,16 +633,16 @@ TEST(TestApi, Ila2Chc) { pf.AddStatements({ Assume {ctr == 0}, - Assume {x > BvConst(0, ctr_w)}, - Assume {y > BvConst(0, ctr_w)}, - Assume {x < BvConst(3, ctr_w)}, // keeps runtime short - Assume {y < BvConst(4, ctr_w)}, // keeps runtime short + Assume {x > 0}, + Assume {y > 0}, + Assume {x < 3}, // keeps runtime short + Assume {y < 4}, // keeps runtime short - Update { {i, BvConst(0, ctr_w)} }, + Update { {i, 0} }, Assert { inv1 }, While { i < x, { Assume { inv1 }, - Update { {j, BvConst(0, ctr_w)} }, + Update { {j, ExtendableBv(BvConst(0, 2))} }, Assert { inv2 }, While { j < y, { Assume { inv2 }, From 44b893beab4d4532590d6ffa301fb9c0429ee647 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Thu, 21 Oct 2021 13:47:30 -0400 Subject: [PATCH 10/32] Deleted unnecessary function. --- src/ilang++.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ilang++.cc b/src/ilang++.cc index 186ee104b..931e474c7 100644 --- a/src/ilang++.cc +++ b/src/ilang++.cc @@ -970,13 +970,13 @@ namespace programfragment { return ExprRef{v}; } - void ProgramFragment::RegisterApplicationParam(const ExprRef& p) { - // TODO: what does this entail? - } + // void ProgramFragment::RegisterApplicationParam(const ExprRef& p) { + // // TODO: what does this entail? + // } - void ProgramFragment::RegisterHardwareParam(const ExprRef& p) { - // TODO: what does this entail? - } + // void ProgramFragment::RegisterHardwareParam(const ExprRef& p) { + // // TODO: what does this entail? + // } void ProgramFragment::AddStatement(const Stmt& s) { pf_->body.push_back(convert_stmt(s)); From 06d06dd8bf7edc7c89b6d27a5883f161c73b8a42 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Tue, 26 Oct 2021 14:06:09 -0400 Subject: [PATCH 11/32] Update smtswitch for z3 integration --- .gitmodules | 2 +- README.md | 4 ++-- extern/smt-switch | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index f2f3e286e..aeb856b19 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/PrincetonUniversity/ILA-Synthesis-Python.git [submodule "extern/smt-switch"] path = extern/smt-switch - url = https://github.com/makaimann/smt-switch.git + url = https://github.com/stanford-centaur/smt-switch diff --git a/README.md b/README.md index dd536e1b9..dc5bd3874 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ sudo make install - Use `-DILANG_BUILD_TEST=OFF` to disalbe building the unit tests. - Use `-DILANG_BUILD_SYNTH=ON` to enable building the synthesis engine (required [Boost](https://www.boost.org)). - Use `-DILANG_BUILD_INVSYN=OFF` to disable building invariant synthesis feature. -- Use `-DILANG_BUILD_SWITCH=ON` to enable building [smt-switch](https://github.com/makaimann/smt-switch.git) interface support. +- Use `-DILANG_BUILD_SWITCH=ON` to enable building [smt-switch](https://github.com/stanford-centaur/smt-switch) interface support. - Use `-DILANG_BUILD_COSIM=ON` to enable building [Xilinx cosimulation](https://www.linuxsecrets.com/xilinx/QEMU%20SystemC%20and%20TLM%20CoSimulation.html) support. - Use `-DILANG_INSTALL_DEV=ON` to install working features. @@ -233,7 +233,7 @@ Copyright (c) 2018 Ben Marshall. ILAng uses the [SMT parser](https://es-static.fbk.eu/people/griggio/misc/smtlib2parser.html), which is licensed under the [MIT License](https://es-static.fbk.eu/people/griggio/misc/smtlib2parser.html). Copyright (c) 2010 Alberto Griggio. -ILAng uses the [smt-switch](https://github.com/makaimann/smt-switch.git), which is licensed under the [BSD 3-Clause License](https://github.com/makaimann/smt-switch/blob/master/LICENSE). +ILAng uses the [smt-switch](https://github.com/stanford-centaur/smt-switch), which is licensed under the [BSD 3-Clause License](https://github.com/stanford-centaur/smt-switch/blob/master/LICENSE). Copyright (c) 2019-2020 the authors. ILAng uses [ItSy](https://github.com/PrincetonUniversity/ItSy), which is licensed under the [MIT License](https://github.com/PrincetonUniversity/ItSy/blob/master/LICENSE). diff --git a/extern/smt-switch b/extern/smt-switch index 68dad97f7..331a53fe0 160000 --- a/extern/smt-switch +++ b/extern/smt-switch @@ -1 +1 @@ -Subproject commit 68dad97f77282f77d3f5ae93ffc5e521d88e3992 +Subproject commit 331a53fe0f3c66b84c8434152f21f0a086a26044 From c5bee32f57b960544e09b2d098ae366ec552c962 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Tue, 26 Oct 2021 22:14:15 -0400 Subject: [PATCH 12/32] Update header guard --- include/ilang/target-smt/smt_switch_itf.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ilang/target-smt/smt_switch_itf.h b/include/ilang/target-smt/smt_switch_itf.h index e7e317b69..502a808a6 100644 --- a/include/ilang/target-smt/smt_switch_itf.h +++ b/include/ilang/target-smt/smt_switch_itf.h @@ -1,8 +1,8 @@ /// \file /// Class SmtSwitchItf - the interface to external library smt-switch. -#ifndef ILANG_ILA_MNGR_U_SMT_SWITCH_H__ -#define ILANG_ILA_MNGR_U_SMT_SWITCH_H__ +#ifndef ILANG_TARGET_SMT_SMT_SWITCH_H__ +#define ILANG_TARGET_SMT_SMT_SWITCH_H__ #ifdef SMTSWITCH_INTERFACE @@ -90,5 +90,5 @@ class SmtSwitchItf { #endif // SMTSWITCH_INTERFACE -#endif // ILANG_ILA_MNGR_U_SMT_SWITCH_H__ +#endif // ILANG_TARGET_SMT_SMT_SWITCH_H__ From 38f9acf6ec6679259e0857de2c2e960aef5dd915 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Thu, 28 Oct 2021 13:12:41 -0400 Subject: [PATCH 13/32] Link GMP and pthread for smt-switch --- cmake/FindGMP.cmake | 19 +++++++++++++++++++ cmake/Findsmtswitch.cmake | 32 ++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 8 ++++++++ 3 files changed, 59 insertions(+) create mode 100644 cmake/FindGMP.cmake diff --git a/cmake/FindGMP.cmake b/cmake/FindGMP.cmake new file mode 100644 index 000000000..797973064 --- /dev/null +++ b/cmake/FindGMP.cmake @@ -0,0 +1,19 @@ +# Find GMP +# GMP_FOUND - system has GMP lib +# GMP_INCLUDE_DIR - the GMP include directory +# GMP_LIBRARIES - Libraries needed to use GMP + +find_path(GMP_INCLUDE_DIR NAMES gmp.h gmpxx.h) +find_library(GMP_LIBRARIES NAMES gmp) +find_library(GMPXX_LIBRARIES NAMES gmpxx) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GMP DEFAULT_MSG GMP_INCLUDE_DIR GMP_LIBRARIES) + +mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES) +if(GMP_LIBRARIES) + message(STATUS "Found GMP libs: ${GMP_LIBRARIES}") +endif() +if (GMPXX_LIBRARIES) + message(STATUS "Found GMPXX libs: ${GMPXX_LIBRARIES}") +endif() diff --git a/cmake/Findsmtswitch.cmake b/cmake/Findsmtswitch.cmake index 68f352368..db911ae21 100644 --- a/cmake/Findsmtswitch.cmake +++ b/cmake/Findsmtswitch.cmake @@ -11,6 +11,7 @@ # SMTSWITCH_CVC4_FOUND # SMTSWITCH_MSAT_FOUND # SMTSWITCH_YICES2_FOUND +# SMTSWITCH_Z3_FOUND # # and the following imported targets # @@ -19,6 +20,7 @@ # smt-switch::smt-switch-cvc4 # smt-switch::smt-switch-msat # smt-switch::smt-switch-yices2 +# smt-switch::smt-switch-z3 # # XXX smt-switch needs to be built with static type @@ -26,6 +28,7 @@ find_package(PkgConfig) pkg_check_modules(PC_SMTSWITCH QUIET SMTSWITCH) +find_package(GMP REQUIRED) ## ## core smt-switch @@ -119,6 +122,22 @@ endif() set(SMTSWITCH_YICES2_LIBRARY ${SMTSWITCH_YICES_LIBRARY} ${SMTSWITCH_LIBRARY}) mark_as_advanced(SMTSWITCH_YICES2_FOUND) +# z3 +find_library(SMTSWITCH_Z3_LIBRARY + NAMES smt-switch-z3 + HINTS ${PC_SMTSWITCH_LIBDIR} ${PC_SMTSWITCH_LIBRARY_DIRS} + PATH_SUFFIXES smt-switch +) + +if(SMTSWITCH_Z3_LIBRARY) + set(SMTSWITCH_Z3_FOUND TRUE) +else() + set(SMTSWITCH_Z3_FOUND FALSE) +endif() + +set(SMTSWITCH_Z3_LIBRARY ${SMTSWITCH_Z3_LIBRARY} ${SMTSWITCH_LIBRARY}) +mark_as_advanced(SMTSWITCH_Z3_FOUND) + # create imported target smt-switch::smt-switch if(SMTSWITCH_FOUND AND NOT TARGET smt-switch::smt-switch) add_library(smt-switch::smt-switch INTERFACE IMPORTED) @@ -184,3 +203,16 @@ if(SMTSWITCH_YICES2_FOUND AND NOT TARGET smt-switch::smt-switch-yices2) ) endif() +# create imported target smt-switch::smt-switch-z3 +if(SMTSWITCH_Z3_FOUND AND NOT TARGET smt-switch::smt-switch-z3) + add_library(smt-switch::smt-switch-z3 INTERFACE IMPORTED) + set_property( + TARGET smt-switch::smt-switch-z3 + PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SMTSWITCH_INCLUDE_DIR} + ) + set_property( + TARGET smt-switch::smt-switch-z3 + PROPERTY INTERFACE_LINK_LIBRARIES ${SMTSWITCH_Z3_LIBRARY} + ) +endif() + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b80acf95a..3d23ff0f2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -206,6 +206,10 @@ if(${ILANG_BUILD_SWITCH}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch) target_compile_definitions(${ILANG_LIB_NAME} PUBLIC SMTSWITCH_INTERFACE) + find_package(Threads REQUIRED) + target_link_libraries(${ILANG_LIB_NAME} PUBLIC gmp) + target_link_libraries(${ILANG_LIB_NAME} PUBLIC Threads::Threads) + if(${SMTSWITCH_BTOR_FOUND}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-btor) endif() @@ -222,6 +226,10 @@ if(${ILANG_BUILD_SWITCH}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-yices2) endif() + if(${SMTSWITCH_Z3_FOUND}) + target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-z3) + endif() + endif() ## From 49a99075b4f97f24fe067af70551965cfb267099 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Thu, 28 Oct 2021 13:13:51 -0400 Subject: [PATCH 14/32] Add LIA SMT converter (trivial) --- include/ilang/target-smt/lia_cvtr.h | 63 +++++ include/ilang/target-smt/smt_switch_itf.h | 11 +- src/target-smt/CMakeLists.txt | 1 + src/target-smt/lia_cvtr.cc | 269 ++++++++++++++++++++++ src/target-smt/smt_switch_itf.cc | 2 +- test/CMakeLists.txt | 6 +- test/t_smt_shim.cc | 56 +++++ 7 files changed, 401 insertions(+), 7 deletions(-) create mode 100644 include/ilang/target-smt/lia_cvtr.h create mode 100644 src/target-smt/lia_cvtr.cc diff --git a/include/ilang/target-smt/lia_cvtr.h b/include/ilang/target-smt/lia_cvtr.h new file mode 100644 index 000000000..913a92f63 --- /dev/null +++ b/include/ilang/target-smt/lia_cvtr.h @@ -0,0 +1,63 @@ +/// \file +/// Class LiaCvtr - the converter for linear integer arithmetic SMT expressions. + +#ifndef ILANG_TARGET_SMT_LIA_CVTR_H__ +#define ILANG_TARGET_SMT_LIA_CVTR_H__ + +#ifdef SMTSWITCH_INTERFACE + +#include +#include + +#include + +#include +#include + +/// \namepsace ilang +namespace ilang { + +class LiaCvtr : public SmtSwitchItf { +public: + // ------------------------- CONSTRUCTOR/DESTRUCTOR ----------------------- // + /// Default constructor. + LiaCvtr(smt::SmtSolver& solver); + /// Default destructor. + ~LiaCvtr(); + +private: + /// Type for cacheing the generated expressions. + typedef std::unordered_map ExprTermMap; + /// Type for cacheing the generated functions. + typedef std::unordered_map FuncTermMap; + + // ------------------------- MEMBERS -------------------------------------- // + /// The underlying SMT solver. + smt::SmtSolver& solver_; + /// Container for cacheing expression Terms. + ExprTermMap expr_map_; + /// Container for cacheing function Terms. + FuncTermMap func_map_; + /// Name suffix for each expression generation (e.g., time step). + std::string suffix_ = ""; + + // ------------------------- HELPERS -------------------------------------- // + /// Make Term of expr variable. + smt::Term ExprVar2Term(const ExprPtr& expr); + /// Make Term of expr constant. + smt::Term ExprConst2Term(const ExprPtr& expr); + /// Make Term of expr operator. + smt::Term ExprOp2Term(const ExprPtr& expr, const smt::TermVec& arg_terms); + /// Make Term of func. + smt::Term Func2Term(const FuncPtr& func); + /// Make smt::Sort of ilang::SortPtr. + smt::Sort IlaSort2LiaSort(const SortPtr& s); + +}; // class LiaCvtr + +} // namespace ilang + +#endif // SMTSWITCH_INTERFACE + +#endif // ILANG_TARGET_SMT_LIA_CVTR_H__ + diff --git a/include/ilang/target-smt/smt_switch_itf.h b/include/ilang/target-smt/smt_switch_itf.h index 502a808a6..8ceee1670 100644 --- a/include/ilang/target-smt/smt_switch_itf.h +++ b/include/ilang/target-smt/smt_switch_itf.h @@ -74,15 +74,16 @@ class SmtSwitchItf { /// Insert the SMT Term of the given node into the map. void PopulateExprMap(const ExprPtr& expr); /// Make Term of expr variable. - smt::Term ExprVar2Term(const ExprPtr& expr); + virtual smt::Term ExprVar2Term(const ExprPtr& expr); /// Make Term of expr constant. - smt::Term ExprConst2Term(const ExprPtr& expr); + virtual smt::Term ExprConst2Term(const ExprPtr& expr); /// Make Term of expr operator. - smt::Term ExprOp2Term(const ExprPtr& expr, const smt::TermVec& arg_terms); + virtual smt::Term ExprOp2Term(const ExprPtr& expr, + const smt::TermVec& arg_terms); /// Make Term of func. - smt::Term Func2Term(const FuncPtr& func); + virtual smt::Term Func2Term(const FuncPtr& func); /// Make smt::Sort of ilang::SortPtr. - smt::Sort IlaSort2SmtSort(const SortPtr& s); + virtual smt::Sort IlaSort2SmtSort(const SortPtr& s); }; // class SmtSwitchItf diff --git a/src/target-smt/CMakeLists.txt b/src/target-smt/CMakeLists.txt index b9781559d..d908b3b0b 100644 --- a/src/target-smt/CMakeLists.txt +++ b/src/target-smt/CMakeLists.txt @@ -2,6 +2,7 @@ # source # ---------------------------------------------------------------------------- # target_sources(${ILANG_LIB_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/lia_cvtr.cc ${CMAKE_CURRENT_SOURCE_DIR}/smt_switch_itf.cc ${CMAKE_CURRENT_SOURCE_DIR}/z3_expr_adapter.cc ) diff --git a/src/target-smt/lia_cvtr.cc b/src/target-smt/lia_cvtr.cc new file mode 100644 index 000000000..06b2dcc52 --- /dev/null +++ b/src/target-smt/lia_cvtr.cc @@ -0,0 +1,269 @@ +/// \file +/// Source for the LIA converter. + +#ifdef SMTSWITCH_INTERFACE + +#include + +#include + +#include + +#include +#include +#include + +namespace ilang { + +LiaCvtr::LiaCvtr(smt::SmtSolver& solver) + : SmtSwitchItf(solver), solver_(solver) {} + +LiaCvtr::~LiaCvtr() {} + +smt::Term LiaCvtr::ExprVar2Term(const ExprPtr& expr) { + auto prefix = (expr->host()) ? expr->host()->GetRootName() : ""; + auto e_name = expr->name().format_str(prefix, suffix_); + + auto smt_sort = IlaSort2LiaSort(expr->sort()); + return solver_->make_symbol(e_name, smt_sort); +} + +smt::Term LiaCvtr::ExprConst2Term(const ExprPtr& expr) { + auto expr_const = std::static_pointer_cast(expr); + + switch (auto sort_uid = asthub::GetUidSort(expr); sort_uid) { + case AstUidSort::kBool: { + return solver_->make_term(expr_const->val_bool()->val()); + } + case AstUidSort::kBv: { + return solver_->make_term(expr_const->val_bv()->val(), + solver_->make_sort(smt::INT)); + } + default: { + ILA_ASSERT(sort_uid == AstUidSort::kMem); + auto int_sort = solver_->make_sort(smt::INT); + auto mem_sort = solver_->make_sort(smt::ARRAY, int_sort, int_sort); + + // create const memory with default value + auto memory_value = expr_const->val_mem(); + auto def_val_term = solver_->make_term(memory_value->def_val(), int_sort); + auto const_memory = solver_->make_term(def_val_term, mem_sort); + + // write in non-default addr-data pairs + auto& value_map = memory_value->val_map(); + for (const auto& p : value_map) { + auto addr_term = solver_->make_term(p.first, int_sort); + auto data_term = solver_->make_term(p.second, int_sort); + auto memory_wr = solver_->make_term(smt::PrimOp::Store, const_memory, + addr_term, data_term); + const_memory = memory_wr; + } + + return const_memory; + } + }; // switch sort_uid +} + +smt::Term LiaCvtr::ExprOp2Term(const ExprPtr& expr, + const smt::TermVec& arg_terms) { + // construct based on the operator + switch (auto expr_op_uid = asthub::GetUidExprOp(expr); expr_op_uid) { + case AstUidExprOp::kNegate: { + return solver_->make_term(smt::PrimOp::Negate, arg_terms.at(0)); + } + case AstUidExprOp::kNot: { + return solver_->make_term(smt::PrimOp::Not, arg_terms.at(0)); + } +#if 0 + case AstUidExprOp::kComplement: { + return solver_->make_term(smt::PrimOp::BVNot, arg_terms.at(0)); + } +#endif + case AstUidExprOp::kAnd: { + ILA_CHECK(expr->is_bool()) << "BVAnd not supported for LIA"; + return solver_->make_term(smt::PrimOp::And, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kOr: { + ILA_CHECK(expr->is_bool()) << "BVOr not supported for LIA"; + return solver_->make_term(smt::PrimOp::Or, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kXor: { + ILA_CHECK(expr->is_bool()) << "BVXor not supported for LIA"; + return solver_->make_term(smt::PrimOp::Xor, arg_terms.at(0), + arg_terms.at(1)); + } +#if 0 + case AstUidExprOp::kShiftLeft: { + return solver_->make_term(smt::PrimOp::BVShl, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kArithShiftRight: { + return solver_->make_term(smt::PrimOp::BVAshr, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kLogicShiftRight: { + return solver_->make_term(smt::PrimOp::BVLshr, arg_terms.at(0), + arg_terms.at(1)); + } +#endif + case AstUidExprOp::kAdd: { + return solver_->make_term(smt::PrimOp::Plus, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kSubtract: { + return solver_->make_term(smt::PrimOp::Minus, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kDivide: { + return solver_->make_term(smt::PrimOp::IntDiv, arg_terms.at(0), + arg_terms.at(1)); + } +#if 0 + case AstUidExprOp::kSignedRemainder: { + return solver_->make_term(smt::PrimOp::BVSrem, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kUnsignedRemainder: { + return solver_->make_term(smt::PrimOp::BVUrem, arg_terms.at(0), + arg_terms.at(1)); + } +#endif + case AstUidExprOp::kSignedModular: { + return solver_->make_term(smt::PrimOp::Mod, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kMultiply: { + return solver_->make_term(smt::PrimOp::Mult, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kEqual: { + return solver_->make_term(smt::PrimOp::Equal, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kLessThan: { + return solver_->make_term(smt::PrimOp::Lt, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kGreaterThan: { + return solver_->make_term(smt::PrimOp::Gt, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kUnsignedLessThan: { + return solver_->make_term(smt::PrimOp::Lt, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kUnsignedGreaterThan: { + return solver_->make_term(smt::PrimOp::Gt, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kLoad: { + return solver_->make_term(smt::PrimOp::Select, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kStore: { + return solver_->make_term(smt::PrimOp::Store, arg_terms.at(0), + arg_terms.at(1), arg_terms.at(2)); + } +#if 0 + case AstUidExprOp::kConcatenate: { + return solver_->make_term(smt::PrimOp::Concat, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kExtract: { + auto op = smt::Op(smt::PrimOp::Extract, expr->param(0), expr->param(1)); + return solver_->make_term(op, arg_terms.at(0)); + } +#endif + case AstUidExprOp::kZeroExtend: { + return arg_terms.at(0); + } + case AstUidExprOp::kSignedExtend: { + return arg_terms.at(0); + } +#if 0 + case AstUidExprOp::kRotateLeft: { + auto op = smt::Op(smt::PrimOp::Rotate_Left, expr->param(0)); + return solver_->make_term(op, arg_terms.at(0)); + } + case AstUidExprOp::kRotateRight: { + auto op = smt::Op(smt::PrimOp::Rotate_Right, expr->param(0)); + return solver_->make_term(op, arg_terms.at(0)); + } +#endif + case AstUidExprOp::kImply: { + return solver_->make_term(smt::PrimOp::Implies, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kIfThenElse: { + return solver_->make_term(smt::PrimOp::Ite, arg_terms.at(0), + arg_terms.at(1), arg_terms.at(2)); + } + case AstUidExprOp::kApplyFunc: { + auto expr_appfunc = std::static_pointer_cast(expr); + auto func = expr_appfunc->func(); + + // term placeholder for the solver + smt::TermVec func_arg_terms; + + // check if the func has been visited + auto pos = func_map_.find(func); + if (pos != func_map_.end()) { + func_arg_terms.push_back(pos->second); + } else { // fist visit - create new term + auto func_term = Func2Term(func); + func_arg_terms.push_back(func_term); + func_map_[func] = func_term; + } + + // apply func + for (auto arg_i : arg_terms) { + func_arg_terms.push_back(arg_i); + } + return solver_->make_term(smt::PrimOp::Apply, func_arg_terms); + } + default: { + ILA_CHECK(false) << "Op " << expr_op_uid << " not supported for LIA (yet)"; + return solver_->make_term(true); + } + }; // switch expr_op_uid +} + +smt::Term LiaCvtr::Func2Term(const FuncPtr& func) { + // func name (for z3 compatibility) + auto prefix = (func->host()) ? func->host()->GetRootName() : ""; + auto f_name = func->name().format_str(prefix, suffix_); + + // func sort + auto arg_sorts = smt::SortVec(); + for (size_t i = 0; i != func->arg_num(); i++) { + arg_sorts.push_back(IlaSort2LiaSort(func->arg(i))); + } + arg_sorts.push_back(IlaSort2LiaSort(func->out())); // return is the last + auto func_sort = solver_->make_sort(smt::FUNCTION, arg_sorts); + + // func term + auto func_term = solver_->make_symbol(f_name, func_sort); + return func_term; +} + +smt::Sort LiaCvtr::IlaSort2LiaSort(const SortPtr& s) { + switch (auto sort_uid = s->uid(); sort_uid) { + case AstUidSort::kBool: { + return solver_->make_sort(smt::BOOL); + } + case AstUidSort::kBv: { + return solver_->make_sort(smt::INT); + } + default: { + ILA_ASSERT(sort_uid == AstUidSort::kMem); + return solver_->make_sort(smt::ARRAY, solver_->make_sort(smt::INT), + solver_->make_sort(smt::INT)); + } + }; // switch sort uid +} + +} // namespace ilang + +#endif // SMTSWITCH_INTERFACE diff --git a/src/target-smt/smt_switch_itf.cc b/src/target-smt/smt_switch_itf.cc index 14be39119..4ae8d8476 100644 --- a/src/target-smt/smt_switch_itf.cc +++ b/src/target-smt/smt_switch_itf.cc @@ -331,4 +331,4 @@ smt::Sort SmtSwitchItf::IlaSort2SmtSort(const SortPtr& s) { } // namespace ilang -#endif // SMT_SWITCH_INTERFACE +#endif // SMTSWITCH_INTERFACE diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1171ae68c..f16222e28 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -109,8 +109,12 @@ endif() if(${ILANG_BUILD_SWITCH}) find_package(smtswitch REQUIRED) - if(${SMTSWITCH_BTOR_FOUND}) + if(${SMTSWITCH_BTOR_FOUND} AND ${SMTSWITCH_Z3_FOUND}) target_compile_definitions(${ILANG_TEST_MAIN} PRIVATE SMTSWITCH_TEST) + + find_package(Threads REQUIRED) + target_link_libraries(${ILANG_TEST_MAIN} gmp) + target_link_libraries(${ILANG_TEST_MAIN} Threads::Threads) endif() endif() diff --git a/test/t_smt_shim.cc b/test/t_smt_shim.cc index 2d158582d..d6db4cbfb 100644 --- a/test/t_smt_shim.cc +++ b/test/t_smt_shim.cc @@ -4,9 +4,12 @@ #ifdef SMTSWITCH_TEST #include #include +#include +#include #endif // SMTSWITCH_TEST #include +#include #include #include #include @@ -56,6 +59,19 @@ class TestSmtShim : public ::testing::Test { #endif // SMTSWITCH_TEST } + void CheckUnsatLia(const ExprRef& e) { +#ifdef SMTSWITCH_TEST + auto solver = smt::Z3SolverFactory::create(false); + auto lia_cvtr = LiaCvtr(solver); + auto shim = SmtShim(lia_cvtr); + + auto lia_term = shim.GetShimExpr(e.get()); + solver->assert_formula(lia_term); + auto res = solver->check_sat(); + EXPECT_TRUE(res.is_unsat()); +#endif // SMTSWITCH_TEST + } + Ila m = Ila("host"); ExprRef var_bool_a; ExprRef var_bool_b; @@ -71,6 +87,7 @@ TEST_F(TestSmtShim, OpBoolNot) { auto prop = not_not_a != var_bool_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvNeg) { @@ -79,6 +96,7 @@ TEST_F(TestSmtShim, OpBvNeg) { auto prop = neg_neg_a != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvComplement) { @@ -87,12 +105,14 @@ TEST_F(TestSmtShim, OpBvComplement) { auto prop = com_com_a != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolAnd) { auto a_and_not_a = var_bool_a & !var_bool_a; CheckUnsatZ3(a_and_not_a); CheckUnsatSwitch(a_and_not_a); + CheckUnsatLia(a_and_not_a); } TEST_F(TestSmtShim, OpBvAnd) { @@ -100,6 +120,7 @@ TEST_F(TestSmtShim, OpBvAnd) { auto prop = a_and_all1 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolOr) { @@ -107,6 +128,7 @@ TEST_F(TestSmtShim, OpBoolOr) { auto prop = !a_or_not_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvOr) { @@ -114,6 +136,7 @@ TEST_F(TestSmtShim, OpBvOr) { auto prop = a_or_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolXor) { @@ -122,6 +145,7 @@ TEST_F(TestSmtShim, OpBoolXor) { auto prop = a_xor_b & a_eq_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvXor) { @@ -130,6 +154,7 @@ TEST_F(TestSmtShim, OpBvXor) { auto prop = a_eq_b & (a_xor_b == BvConst((1 << BV_SIZE) - 1, BV_SIZE)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvShl) { @@ -137,6 +162,7 @@ TEST_F(TestSmtShim, OpBvShl) { auto prop = a_shl_all != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvAshr) { @@ -146,6 +172,7 @@ TEST_F(TestSmtShim, OpBvAshr) { auto prop = !(is_pos | is_neg); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvLshr) { @@ -153,6 +180,7 @@ TEST_F(TestSmtShim, OpBvLshr) { auto prop = a_ashr_all != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvAdd) { @@ -160,6 +188,7 @@ TEST_F(TestSmtShim, OpBvAdd) { auto prop = a_plus_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSub) { @@ -167,6 +196,7 @@ TEST_F(TestSmtShim, OpBvSub) { auto prop = a_minus_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvAddSub) { @@ -175,6 +205,7 @@ TEST_F(TestSmtShim, OpBvAddSub) { auto prop = a_plus_b_minus_b != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvMul) { @@ -182,6 +213,7 @@ TEST_F(TestSmtShim, OpBvMul) { auto prop = a_times_0 != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvDiv) { @@ -189,6 +221,7 @@ TEST_F(TestSmtShim, OpBvDiv) { auto prop = a_div_1 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSrem) { @@ -197,6 +230,7 @@ TEST_F(TestSmtShim, OpBvSrem) { auto prop = !(Slt(a_srem_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvUrem) { @@ -205,6 +239,7 @@ TEST_F(TestSmtShim, OpBvUrem) { auto prop = !(Ult(a_urem_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvSmod) { @@ -213,6 +248,7 @@ TEST_F(TestSmtShim, OpBvSmod) { auto prop = !(Slt(a_smod_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + // LIA } TEST_F(TestSmtShim, OpBvSltSge) { @@ -221,6 +257,7 @@ TEST_F(TestSmtShim, OpBvSltSge) { auto prop = a_slt_b & a_sge_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSleSgt) { @@ -229,6 +266,7 @@ TEST_F(TestSmtShim, OpBvSleSgt) { auto prop = a_sle_b & a_sgt_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSltSgteq) { @@ -238,6 +276,7 @@ TEST_F(TestSmtShim, OpBvSltSgteq) { auto prop = !(a_slt_b | a_sgt_b | a_eq_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUltUge) { @@ -246,6 +285,7 @@ TEST_F(TestSmtShim, OpBvUltUge) { auto prop = a_ult_b & a_uge_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUleUgt) { @@ -254,6 +294,7 @@ TEST_F(TestSmtShim, OpBvUleUgt) { auto prop = a_ule_b & a_ugt_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUltUgteq) { @@ -263,6 +304,7 @@ TEST_F(TestSmtShim, OpBvUltUgteq) { auto prop = !(a_ult_b | a_ugt_b | a_eq_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpMemLoad) { @@ -271,6 +313,7 @@ TEST_F(TestSmtShim, OpMemLoad) { auto prop = (var_bv_a == var_bv_b) & (data_a != data_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpMemStore) { @@ -279,6 +322,7 @@ TEST_F(TestSmtShim, OpMemStore) { auto prop = data != var_bv_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvConcat) { @@ -287,6 +331,7 @@ TEST_F(TestSmtShim, OpBvConcat) { auto prop = (var_bv_a == var_bv_b) & (a_con_b != b_con_a); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvExtract) { @@ -295,6 +340,7 @@ TEST_F(TestSmtShim, OpBvExtract) { auto prop = extract_a_con_b != var_bv_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvZext) { @@ -303,6 +349,7 @@ TEST_F(TestSmtShim, OpBvZext) { auto prop = zext_a != zero_con_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + // LIA } TEST_F(TestSmtShim, OpBvSext) { @@ -312,6 +359,7 @@ TEST_F(TestSmtShim, OpBvSext) { auto prop = sext_a != zero_con_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + // LIA } TEST_F(TestSmtShim, OpBvRotate) { @@ -320,6 +368,7 @@ TEST_F(TestSmtShim, OpBvRotate) { auto prop = right_rotate_back != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolImply) { @@ -327,6 +376,7 @@ TEST_F(TestSmtShim, OpBoolImply) { auto prop = a_imply_b & var_bool_a & !var_bool_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBoolIte) { @@ -334,6 +384,7 @@ TEST_F(TestSmtShim, OpBoolIte) { auto prop = ite_a_0_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvIte) { @@ -341,6 +392,7 @@ TEST_F(TestSmtShim, OpBvIte) { auto prop = (var_bv_a == var_bv_b) & (ite_c_a_b != var_bv_a); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpApplyFunc) { @@ -350,6 +402,7 @@ TEST_F(TestSmtShim, OpApplyFunc) { auto prop = (var_bv_a == var_bv_b) & (out1 != out2); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstBool) { @@ -358,6 +411,7 @@ TEST_F(TestSmtShim, ConstBool) { auto prop = a_is_0 & b_is_1 & (var_bool_a == var_bool_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstBv) { @@ -366,6 +420,7 @@ TEST_F(TestSmtShim, ConstBv) { auto prop = a_is_0 & b_is_1 & (var_bv_a == var_bv_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstMem) { @@ -380,6 +435,7 @@ TEST_F(TestSmtShim, ConstMem) { auto prop = !Imply(save_addr, is_linear); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, DiscreteUsage) { From d9c842714dee75c62b0c346332125a33d246b1e9 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Thu, 28 Oct 2021 13:47:13 -0400 Subject: [PATCH 15/32] Add test for using z3 in LiaCvtr --- test/t_smt_shim.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/t_smt_shim.cc b/test/t_smt_shim.cc index d6db4cbfb..85daef15a 100644 --- a/test/t_smt_shim.cc +++ b/test/t_smt_shim.cc @@ -5,7 +5,8 @@ #include #include #include -#include +// #include +#include #endif // SMTSWITCH_TEST #include @@ -471,6 +472,25 @@ TEST_F(TestSmtShim, DiscreteUsage) { EXPECT_TRUE(res.is_unsat()); } #endif // SMTSWITCH_TEST + +#ifdef SMTSWITCH_TEST + { // LiaCvtr + auto solver = smt::Z3SolverFactory::create(false); + auto lia = LiaCvtr(solver); + auto shim = SmtShim(lia); + + // Use SmtShim to gen/add formulas + solver->assert_formula(shim.GetShimExpr(a_ult_b.get())); + solver->assert_formula(shim.GetShimExpr(a_ugt_b.get())); + solver->assert_formula(shim.GetShimExpr(a_eq_b.get())); + + // Use z3's command for checking + auto raw = std::static_pointer_cast(solver); + auto slv = raw->get_z3_solver(); // get_z3_context() also available + auto res = slv->check(); + EXPECT_TRUE(res == z3::unsat); + } +#endif // SMTSWITCH_TEST } } // namespace ilang From 819fe8016924498c2b4ce17fc4251955e145fdf3 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Thu, 28 Oct 2021 13:54:40 -0400 Subject: [PATCH 16/32] Revert mistaken push --- .gitmodules | 2 +- README.md | 4 +- cmake/FindGMP.cmake | 19 -- cmake/Findsmtswitch.cmake | 32 --- extern/smt-switch | 2 +- include/ilang/target-smt/lia_cvtr.h | 63 ----- include/ilang/target-smt/smt_switch_itf.h | 17 +- src/CMakeLists.txt | 8 - src/target-smt/CMakeLists.txt | 1 - src/target-smt/lia_cvtr.cc | 269 ---------------------- src/target-smt/smt_switch_itf.cc | 2 +- test/CMakeLists.txt | 6 +- test/t_smt_shim.cc | 76 ------ 13 files changed, 14 insertions(+), 487 deletions(-) delete mode 100644 cmake/FindGMP.cmake delete mode 100644 include/ilang/target-smt/lia_cvtr.h delete mode 100644 src/target-smt/lia_cvtr.cc diff --git a/.gitmodules b/.gitmodules index aeb856b19..f2f3e286e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/PrincetonUniversity/ILA-Synthesis-Python.git [submodule "extern/smt-switch"] path = extern/smt-switch - url = https://github.com/stanford-centaur/smt-switch + url = https://github.com/makaimann/smt-switch.git diff --git a/README.md b/README.md index dc5bd3874..dd536e1b9 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ sudo make install - Use `-DILANG_BUILD_TEST=OFF` to disalbe building the unit tests. - Use `-DILANG_BUILD_SYNTH=ON` to enable building the synthesis engine (required [Boost](https://www.boost.org)). - Use `-DILANG_BUILD_INVSYN=OFF` to disable building invariant synthesis feature. -- Use `-DILANG_BUILD_SWITCH=ON` to enable building [smt-switch](https://github.com/stanford-centaur/smt-switch) interface support. +- Use `-DILANG_BUILD_SWITCH=ON` to enable building [smt-switch](https://github.com/makaimann/smt-switch.git) interface support. - Use `-DILANG_BUILD_COSIM=ON` to enable building [Xilinx cosimulation](https://www.linuxsecrets.com/xilinx/QEMU%20SystemC%20and%20TLM%20CoSimulation.html) support. - Use `-DILANG_INSTALL_DEV=ON` to install working features. @@ -233,7 +233,7 @@ Copyright (c) 2018 Ben Marshall. ILAng uses the [SMT parser](https://es-static.fbk.eu/people/griggio/misc/smtlib2parser.html), which is licensed under the [MIT License](https://es-static.fbk.eu/people/griggio/misc/smtlib2parser.html). Copyright (c) 2010 Alberto Griggio. -ILAng uses the [smt-switch](https://github.com/stanford-centaur/smt-switch), which is licensed under the [BSD 3-Clause License](https://github.com/stanford-centaur/smt-switch/blob/master/LICENSE). +ILAng uses the [smt-switch](https://github.com/makaimann/smt-switch.git), which is licensed under the [BSD 3-Clause License](https://github.com/makaimann/smt-switch/blob/master/LICENSE). Copyright (c) 2019-2020 the authors. ILAng uses [ItSy](https://github.com/PrincetonUniversity/ItSy), which is licensed under the [MIT License](https://github.com/PrincetonUniversity/ItSy/blob/master/LICENSE). diff --git a/cmake/FindGMP.cmake b/cmake/FindGMP.cmake deleted file mode 100644 index 797973064..000000000 --- a/cmake/FindGMP.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# Find GMP -# GMP_FOUND - system has GMP lib -# GMP_INCLUDE_DIR - the GMP include directory -# GMP_LIBRARIES - Libraries needed to use GMP - -find_path(GMP_INCLUDE_DIR NAMES gmp.h gmpxx.h) -find_library(GMP_LIBRARIES NAMES gmp) -find_library(GMPXX_LIBRARIES NAMES gmpxx) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GMP DEFAULT_MSG GMP_INCLUDE_DIR GMP_LIBRARIES) - -mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES) -if(GMP_LIBRARIES) - message(STATUS "Found GMP libs: ${GMP_LIBRARIES}") -endif() -if (GMPXX_LIBRARIES) - message(STATUS "Found GMPXX libs: ${GMPXX_LIBRARIES}") -endif() diff --git a/cmake/Findsmtswitch.cmake b/cmake/Findsmtswitch.cmake index db911ae21..68f352368 100644 --- a/cmake/Findsmtswitch.cmake +++ b/cmake/Findsmtswitch.cmake @@ -11,7 +11,6 @@ # SMTSWITCH_CVC4_FOUND # SMTSWITCH_MSAT_FOUND # SMTSWITCH_YICES2_FOUND -# SMTSWITCH_Z3_FOUND # # and the following imported targets # @@ -20,7 +19,6 @@ # smt-switch::smt-switch-cvc4 # smt-switch::smt-switch-msat # smt-switch::smt-switch-yices2 -# smt-switch::smt-switch-z3 # # XXX smt-switch needs to be built with static type @@ -28,7 +26,6 @@ find_package(PkgConfig) pkg_check_modules(PC_SMTSWITCH QUIET SMTSWITCH) -find_package(GMP REQUIRED) ## ## core smt-switch @@ -122,22 +119,6 @@ endif() set(SMTSWITCH_YICES2_LIBRARY ${SMTSWITCH_YICES_LIBRARY} ${SMTSWITCH_LIBRARY}) mark_as_advanced(SMTSWITCH_YICES2_FOUND) -# z3 -find_library(SMTSWITCH_Z3_LIBRARY - NAMES smt-switch-z3 - HINTS ${PC_SMTSWITCH_LIBDIR} ${PC_SMTSWITCH_LIBRARY_DIRS} - PATH_SUFFIXES smt-switch -) - -if(SMTSWITCH_Z3_LIBRARY) - set(SMTSWITCH_Z3_FOUND TRUE) -else() - set(SMTSWITCH_Z3_FOUND FALSE) -endif() - -set(SMTSWITCH_Z3_LIBRARY ${SMTSWITCH_Z3_LIBRARY} ${SMTSWITCH_LIBRARY}) -mark_as_advanced(SMTSWITCH_Z3_FOUND) - # create imported target smt-switch::smt-switch if(SMTSWITCH_FOUND AND NOT TARGET smt-switch::smt-switch) add_library(smt-switch::smt-switch INTERFACE IMPORTED) @@ -203,16 +184,3 @@ if(SMTSWITCH_YICES2_FOUND AND NOT TARGET smt-switch::smt-switch-yices2) ) endif() -# create imported target smt-switch::smt-switch-z3 -if(SMTSWITCH_Z3_FOUND AND NOT TARGET smt-switch::smt-switch-z3) - add_library(smt-switch::smt-switch-z3 INTERFACE IMPORTED) - set_property( - TARGET smt-switch::smt-switch-z3 - PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SMTSWITCH_INCLUDE_DIR} - ) - set_property( - TARGET smt-switch::smt-switch-z3 - PROPERTY INTERFACE_LINK_LIBRARIES ${SMTSWITCH_Z3_LIBRARY} - ) -endif() - diff --git a/extern/smt-switch b/extern/smt-switch index 331a53fe0..68dad97f7 160000 --- a/extern/smt-switch +++ b/extern/smt-switch @@ -1 +1 @@ -Subproject commit 331a53fe0f3c66b84c8434152f21f0a086a26044 +Subproject commit 68dad97f77282f77d3f5ae93ffc5e521d88e3992 diff --git a/include/ilang/target-smt/lia_cvtr.h b/include/ilang/target-smt/lia_cvtr.h deleted file mode 100644 index 913a92f63..000000000 --- a/include/ilang/target-smt/lia_cvtr.h +++ /dev/null @@ -1,63 +0,0 @@ -/// \file -/// Class LiaCvtr - the converter for linear integer arithmetic SMT expressions. - -#ifndef ILANG_TARGET_SMT_LIA_CVTR_H__ -#define ILANG_TARGET_SMT_LIA_CVTR_H__ - -#ifdef SMTSWITCH_INTERFACE - -#include -#include - -#include - -#include -#include - -/// \namepsace ilang -namespace ilang { - -class LiaCvtr : public SmtSwitchItf { -public: - // ------------------------- CONSTRUCTOR/DESTRUCTOR ----------------------- // - /// Default constructor. - LiaCvtr(smt::SmtSolver& solver); - /// Default destructor. - ~LiaCvtr(); - -private: - /// Type for cacheing the generated expressions. - typedef std::unordered_map ExprTermMap; - /// Type for cacheing the generated functions. - typedef std::unordered_map FuncTermMap; - - // ------------------------- MEMBERS -------------------------------------- // - /// The underlying SMT solver. - smt::SmtSolver& solver_; - /// Container for cacheing expression Terms. - ExprTermMap expr_map_; - /// Container for cacheing function Terms. - FuncTermMap func_map_; - /// Name suffix for each expression generation (e.g., time step). - std::string suffix_ = ""; - - // ------------------------- HELPERS -------------------------------------- // - /// Make Term of expr variable. - smt::Term ExprVar2Term(const ExprPtr& expr); - /// Make Term of expr constant. - smt::Term ExprConst2Term(const ExprPtr& expr); - /// Make Term of expr operator. - smt::Term ExprOp2Term(const ExprPtr& expr, const smt::TermVec& arg_terms); - /// Make Term of func. - smt::Term Func2Term(const FuncPtr& func); - /// Make smt::Sort of ilang::SortPtr. - smt::Sort IlaSort2LiaSort(const SortPtr& s); - -}; // class LiaCvtr - -} // namespace ilang - -#endif // SMTSWITCH_INTERFACE - -#endif // ILANG_TARGET_SMT_LIA_CVTR_H__ - diff --git a/include/ilang/target-smt/smt_switch_itf.h b/include/ilang/target-smt/smt_switch_itf.h index 8ceee1670..e7e317b69 100644 --- a/include/ilang/target-smt/smt_switch_itf.h +++ b/include/ilang/target-smt/smt_switch_itf.h @@ -1,8 +1,8 @@ /// \file /// Class SmtSwitchItf - the interface to external library smt-switch. -#ifndef ILANG_TARGET_SMT_SMT_SWITCH_H__ -#define ILANG_TARGET_SMT_SMT_SWITCH_H__ +#ifndef ILANG_ILA_MNGR_U_SMT_SWITCH_H__ +#define ILANG_ILA_MNGR_U_SMT_SWITCH_H__ #ifdef SMTSWITCH_INTERFACE @@ -74,16 +74,15 @@ class SmtSwitchItf { /// Insert the SMT Term of the given node into the map. void PopulateExprMap(const ExprPtr& expr); /// Make Term of expr variable. - virtual smt::Term ExprVar2Term(const ExprPtr& expr); + smt::Term ExprVar2Term(const ExprPtr& expr); /// Make Term of expr constant. - virtual smt::Term ExprConst2Term(const ExprPtr& expr); + smt::Term ExprConst2Term(const ExprPtr& expr); /// Make Term of expr operator. - virtual smt::Term ExprOp2Term(const ExprPtr& expr, - const smt::TermVec& arg_terms); + smt::Term ExprOp2Term(const ExprPtr& expr, const smt::TermVec& arg_terms); /// Make Term of func. - virtual smt::Term Func2Term(const FuncPtr& func); + smt::Term Func2Term(const FuncPtr& func); /// Make smt::Sort of ilang::SortPtr. - virtual smt::Sort IlaSort2SmtSort(const SortPtr& s); + smt::Sort IlaSort2SmtSort(const SortPtr& s); }; // class SmtSwitchItf @@ -91,5 +90,5 @@ class SmtSwitchItf { #endif // SMTSWITCH_INTERFACE -#endif // ILANG_TARGET_SMT_SMT_SWITCH_H__ +#endif // ILANG_ILA_MNGR_U_SMT_SWITCH_H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d23ff0f2..b80acf95a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -206,10 +206,6 @@ if(${ILANG_BUILD_SWITCH}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch) target_compile_definitions(${ILANG_LIB_NAME} PUBLIC SMTSWITCH_INTERFACE) - find_package(Threads REQUIRED) - target_link_libraries(${ILANG_LIB_NAME} PUBLIC gmp) - target_link_libraries(${ILANG_LIB_NAME} PUBLIC Threads::Threads) - if(${SMTSWITCH_BTOR_FOUND}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-btor) endif() @@ -226,10 +222,6 @@ if(${ILANG_BUILD_SWITCH}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-yices2) endif() - if(${SMTSWITCH_Z3_FOUND}) - target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-z3) - endif() - endif() ## diff --git a/src/target-smt/CMakeLists.txt b/src/target-smt/CMakeLists.txt index d908b3b0b..b9781559d 100644 --- a/src/target-smt/CMakeLists.txt +++ b/src/target-smt/CMakeLists.txt @@ -2,7 +2,6 @@ # source # ---------------------------------------------------------------------------- # target_sources(${ILANG_LIB_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/lia_cvtr.cc ${CMAKE_CURRENT_SOURCE_DIR}/smt_switch_itf.cc ${CMAKE_CURRENT_SOURCE_DIR}/z3_expr_adapter.cc ) diff --git a/src/target-smt/lia_cvtr.cc b/src/target-smt/lia_cvtr.cc deleted file mode 100644 index 06b2dcc52..000000000 --- a/src/target-smt/lia_cvtr.cc +++ /dev/null @@ -1,269 +0,0 @@ -/// \file -/// Source for the LIA converter. - -#ifdef SMTSWITCH_INTERFACE - -#include - -#include - -#include - -#include -#include -#include - -namespace ilang { - -LiaCvtr::LiaCvtr(smt::SmtSolver& solver) - : SmtSwitchItf(solver), solver_(solver) {} - -LiaCvtr::~LiaCvtr() {} - -smt::Term LiaCvtr::ExprVar2Term(const ExprPtr& expr) { - auto prefix = (expr->host()) ? expr->host()->GetRootName() : ""; - auto e_name = expr->name().format_str(prefix, suffix_); - - auto smt_sort = IlaSort2LiaSort(expr->sort()); - return solver_->make_symbol(e_name, smt_sort); -} - -smt::Term LiaCvtr::ExprConst2Term(const ExprPtr& expr) { - auto expr_const = std::static_pointer_cast(expr); - - switch (auto sort_uid = asthub::GetUidSort(expr); sort_uid) { - case AstUidSort::kBool: { - return solver_->make_term(expr_const->val_bool()->val()); - } - case AstUidSort::kBv: { - return solver_->make_term(expr_const->val_bv()->val(), - solver_->make_sort(smt::INT)); - } - default: { - ILA_ASSERT(sort_uid == AstUidSort::kMem); - auto int_sort = solver_->make_sort(smt::INT); - auto mem_sort = solver_->make_sort(smt::ARRAY, int_sort, int_sort); - - // create const memory with default value - auto memory_value = expr_const->val_mem(); - auto def_val_term = solver_->make_term(memory_value->def_val(), int_sort); - auto const_memory = solver_->make_term(def_val_term, mem_sort); - - // write in non-default addr-data pairs - auto& value_map = memory_value->val_map(); - for (const auto& p : value_map) { - auto addr_term = solver_->make_term(p.first, int_sort); - auto data_term = solver_->make_term(p.second, int_sort); - auto memory_wr = solver_->make_term(smt::PrimOp::Store, const_memory, - addr_term, data_term); - const_memory = memory_wr; - } - - return const_memory; - } - }; // switch sort_uid -} - -smt::Term LiaCvtr::ExprOp2Term(const ExprPtr& expr, - const smt::TermVec& arg_terms) { - // construct based on the operator - switch (auto expr_op_uid = asthub::GetUidExprOp(expr); expr_op_uid) { - case AstUidExprOp::kNegate: { - return solver_->make_term(smt::PrimOp::Negate, arg_terms.at(0)); - } - case AstUidExprOp::kNot: { - return solver_->make_term(smt::PrimOp::Not, arg_terms.at(0)); - } -#if 0 - case AstUidExprOp::kComplement: { - return solver_->make_term(smt::PrimOp::BVNot, arg_terms.at(0)); - } -#endif - case AstUidExprOp::kAnd: { - ILA_CHECK(expr->is_bool()) << "BVAnd not supported for LIA"; - return solver_->make_term(smt::PrimOp::And, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kOr: { - ILA_CHECK(expr->is_bool()) << "BVOr not supported for LIA"; - return solver_->make_term(smt::PrimOp::Or, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kXor: { - ILA_CHECK(expr->is_bool()) << "BVXor not supported for LIA"; - return solver_->make_term(smt::PrimOp::Xor, arg_terms.at(0), - arg_terms.at(1)); - } -#if 0 - case AstUidExprOp::kShiftLeft: { - return solver_->make_term(smt::PrimOp::BVShl, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kArithShiftRight: { - return solver_->make_term(smt::PrimOp::BVAshr, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kLogicShiftRight: { - return solver_->make_term(smt::PrimOp::BVLshr, arg_terms.at(0), - arg_terms.at(1)); - } -#endif - case AstUidExprOp::kAdd: { - return solver_->make_term(smt::PrimOp::Plus, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kSubtract: { - return solver_->make_term(smt::PrimOp::Minus, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kDivide: { - return solver_->make_term(smt::PrimOp::IntDiv, arg_terms.at(0), - arg_terms.at(1)); - } -#if 0 - case AstUidExprOp::kSignedRemainder: { - return solver_->make_term(smt::PrimOp::BVSrem, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kUnsignedRemainder: { - return solver_->make_term(smt::PrimOp::BVUrem, arg_terms.at(0), - arg_terms.at(1)); - } -#endif - case AstUidExprOp::kSignedModular: { - return solver_->make_term(smt::PrimOp::Mod, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kMultiply: { - return solver_->make_term(smt::PrimOp::Mult, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kEqual: { - return solver_->make_term(smt::PrimOp::Equal, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kLessThan: { - return solver_->make_term(smt::PrimOp::Lt, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kGreaterThan: { - return solver_->make_term(smt::PrimOp::Gt, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kUnsignedLessThan: { - return solver_->make_term(smt::PrimOp::Lt, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kUnsignedGreaterThan: { - return solver_->make_term(smt::PrimOp::Gt, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kLoad: { - return solver_->make_term(smt::PrimOp::Select, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kStore: { - return solver_->make_term(smt::PrimOp::Store, arg_terms.at(0), - arg_terms.at(1), arg_terms.at(2)); - } -#if 0 - case AstUidExprOp::kConcatenate: { - return solver_->make_term(smt::PrimOp::Concat, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kExtract: { - auto op = smt::Op(smt::PrimOp::Extract, expr->param(0), expr->param(1)); - return solver_->make_term(op, arg_terms.at(0)); - } -#endif - case AstUidExprOp::kZeroExtend: { - return arg_terms.at(0); - } - case AstUidExprOp::kSignedExtend: { - return arg_terms.at(0); - } -#if 0 - case AstUidExprOp::kRotateLeft: { - auto op = smt::Op(smt::PrimOp::Rotate_Left, expr->param(0)); - return solver_->make_term(op, arg_terms.at(0)); - } - case AstUidExprOp::kRotateRight: { - auto op = smt::Op(smt::PrimOp::Rotate_Right, expr->param(0)); - return solver_->make_term(op, arg_terms.at(0)); - } -#endif - case AstUidExprOp::kImply: { - return solver_->make_term(smt::PrimOp::Implies, arg_terms.at(0), - arg_terms.at(1)); - } - case AstUidExprOp::kIfThenElse: { - return solver_->make_term(smt::PrimOp::Ite, arg_terms.at(0), - arg_terms.at(1), arg_terms.at(2)); - } - case AstUidExprOp::kApplyFunc: { - auto expr_appfunc = std::static_pointer_cast(expr); - auto func = expr_appfunc->func(); - - // term placeholder for the solver - smt::TermVec func_arg_terms; - - // check if the func has been visited - auto pos = func_map_.find(func); - if (pos != func_map_.end()) { - func_arg_terms.push_back(pos->second); - } else { // fist visit - create new term - auto func_term = Func2Term(func); - func_arg_terms.push_back(func_term); - func_map_[func] = func_term; - } - - // apply func - for (auto arg_i : arg_terms) { - func_arg_terms.push_back(arg_i); - } - return solver_->make_term(smt::PrimOp::Apply, func_arg_terms); - } - default: { - ILA_CHECK(false) << "Op " << expr_op_uid << " not supported for LIA (yet)"; - return solver_->make_term(true); - } - }; // switch expr_op_uid -} - -smt::Term LiaCvtr::Func2Term(const FuncPtr& func) { - // func name (for z3 compatibility) - auto prefix = (func->host()) ? func->host()->GetRootName() : ""; - auto f_name = func->name().format_str(prefix, suffix_); - - // func sort - auto arg_sorts = smt::SortVec(); - for (size_t i = 0; i != func->arg_num(); i++) { - arg_sorts.push_back(IlaSort2LiaSort(func->arg(i))); - } - arg_sorts.push_back(IlaSort2LiaSort(func->out())); // return is the last - auto func_sort = solver_->make_sort(smt::FUNCTION, arg_sorts); - - // func term - auto func_term = solver_->make_symbol(f_name, func_sort); - return func_term; -} - -smt::Sort LiaCvtr::IlaSort2LiaSort(const SortPtr& s) { - switch (auto sort_uid = s->uid(); sort_uid) { - case AstUidSort::kBool: { - return solver_->make_sort(smt::BOOL); - } - case AstUidSort::kBv: { - return solver_->make_sort(smt::INT); - } - default: { - ILA_ASSERT(sort_uid == AstUidSort::kMem); - return solver_->make_sort(smt::ARRAY, solver_->make_sort(smt::INT), - solver_->make_sort(smt::INT)); - } - }; // switch sort uid -} - -} // namespace ilang - -#endif // SMTSWITCH_INTERFACE diff --git a/src/target-smt/smt_switch_itf.cc b/src/target-smt/smt_switch_itf.cc index 4ae8d8476..14be39119 100644 --- a/src/target-smt/smt_switch_itf.cc +++ b/src/target-smt/smt_switch_itf.cc @@ -331,4 +331,4 @@ smt::Sort SmtSwitchItf::IlaSort2SmtSort(const SortPtr& s) { } // namespace ilang -#endif // SMTSWITCH_INTERFACE +#endif // SMT_SWITCH_INTERFACE diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f16222e28..1171ae68c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -109,12 +109,8 @@ endif() if(${ILANG_BUILD_SWITCH}) find_package(smtswitch REQUIRED) - if(${SMTSWITCH_BTOR_FOUND} AND ${SMTSWITCH_Z3_FOUND}) + if(${SMTSWITCH_BTOR_FOUND}) target_compile_definitions(${ILANG_TEST_MAIN} PRIVATE SMTSWITCH_TEST) - - find_package(Threads REQUIRED) - target_link_libraries(${ILANG_TEST_MAIN} gmp) - target_link_libraries(${ILANG_TEST_MAIN} Threads::Threads) endif() endif() diff --git a/test/t_smt_shim.cc b/test/t_smt_shim.cc index 85daef15a..2d158582d 100644 --- a/test/t_smt_shim.cc +++ b/test/t_smt_shim.cc @@ -4,13 +4,9 @@ #ifdef SMTSWITCH_TEST #include #include -#include -// #include -#include #endif // SMTSWITCH_TEST #include -#include #include #include #include @@ -60,19 +56,6 @@ class TestSmtShim : public ::testing::Test { #endif // SMTSWITCH_TEST } - void CheckUnsatLia(const ExprRef& e) { -#ifdef SMTSWITCH_TEST - auto solver = smt::Z3SolverFactory::create(false); - auto lia_cvtr = LiaCvtr(solver); - auto shim = SmtShim(lia_cvtr); - - auto lia_term = shim.GetShimExpr(e.get()); - solver->assert_formula(lia_term); - auto res = solver->check_sat(); - EXPECT_TRUE(res.is_unsat()); -#endif // SMTSWITCH_TEST - } - Ila m = Ila("host"); ExprRef var_bool_a; ExprRef var_bool_b; @@ -88,7 +71,6 @@ TEST_F(TestSmtShim, OpBoolNot) { auto prop = not_not_a != var_bool_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvNeg) { @@ -97,7 +79,6 @@ TEST_F(TestSmtShim, OpBvNeg) { auto prop = neg_neg_a != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvComplement) { @@ -106,14 +87,12 @@ TEST_F(TestSmtShim, OpBvComplement) { auto prop = com_com_a != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolAnd) { auto a_and_not_a = var_bool_a & !var_bool_a; CheckUnsatZ3(a_and_not_a); CheckUnsatSwitch(a_and_not_a); - CheckUnsatLia(a_and_not_a); } TEST_F(TestSmtShim, OpBvAnd) { @@ -121,7 +100,6 @@ TEST_F(TestSmtShim, OpBvAnd) { auto prop = a_and_all1 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolOr) { @@ -129,7 +107,6 @@ TEST_F(TestSmtShim, OpBoolOr) { auto prop = !a_or_not_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvOr) { @@ -137,7 +114,6 @@ TEST_F(TestSmtShim, OpBvOr) { auto prop = a_or_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolXor) { @@ -146,7 +122,6 @@ TEST_F(TestSmtShim, OpBoolXor) { auto prop = a_xor_b & a_eq_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvXor) { @@ -155,7 +130,6 @@ TEST_F(TestSmtShim, OpBvXor) { auto prop = a_eq_b & (a_xor_b == BvConst((1 << BV_SIZE) - 1, BV_SIZE)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvShl) { @@ -163,7 +137,6 @@ TEST_F(TestSmtShim, OpBvShl) { auto prop = a_shl_all != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvAshr) { @@ -173,7 +146,6 @@ TEST_F(TestSmtShim, OpBvAshr) { auto prop = !(is_pos | is_neg); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvLshr) { @@ -181,7 +153,6 @@ TEST_F(TestSmtShim, OpBvLshr) { auto prop = a_ashr_all != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvAdd) { @@ -189,7 +160,6 @@ TEST_F(TestSmtShim, OpBvAdd) { auto prop = a_plus_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSub) { @@ -197,7 +167,6 @@ TEST_F(TestSmtShim, OpBvSub) { auto prop = a_minus_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvAddSub) { @@ -206,7 +175,6 @@ TEST_F(TestSmtShim, OpBvAddSub) { auto prop = a_plus_b_minus_b != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvMul) { @@ -214,7 +182,6 @@ TEST_F(TestSmtShim, OpBvMul) { auto prop = a_times_0 != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvDiv) { @@ -222,7 +189,6 @@ TEST_F(TestSmtShim, OpBvDiv) { auto prop = a_div_1 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSrem) { @@ -231,7 +197,6 @@ TEST_F(TestSmtShim, OpBvSrem) { auto prop = !(Slt(a_srem_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvUrem) { @@ -240,7 +205,6 @@ TEST_F(TestSmtShim, OpBvUrem) { auto prop = !(Ult(a_urem_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvSmod) { @@ -249,7 +213,6 @@ TEST_F(TestSmtShim, OpBvSmod) { auto prop = !(Slt(a_smod_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - // LIA } TEST_F(TestSmtShim, OpBvSltSge) { @@ -258,7 +221,6 @@ TEST_F(TestSmtShim, OpBvSltSge) { auto prop = a_slt_b & a_sge_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSleSgt) { @@ -267,7 +229,6 @@ TEST_F(TestSmtShim, OpBvSleSgt) { auto prop = a_sle_b & a_sgt_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSltSgteq) { @@ -277,7 +238,6 @@ TEST_F(TestSmtShim, OpBvSltSgteq) { auto prop = !(a_slt_b | a_sgt_b | a_eq_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUltUge) { @@ -286,7 +246,6 @@ TEST_F(TestSmtShim, OpBvUltUge) { auto prop = a_ult_b & a_uge_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUleUgt) { @@ -295,7 +254,6 @@ TEST_F(TestSmtShim, OpBvUleUgt) { auto prop = a_ule_b & a_ugt_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUltUgteq) { @@ -305,7 +263,6 @@ TEST_F(TestSmtShim, OpBvUltUgteq) { auto prop = !(a_ult_b | a_ugt_b | a_eq_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpMemLoad) { @@ -314,7 +271,6 @@ TEST_F(TestSmtShim, OpMemLoad) { auto prop = (var_bv_a == var_bv_b) & (data_a != data_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpMemStore) { @@ -323,7 +279,6 @@ TEST_F(TestSmtShim, OpMemStore) { auto prop = data != var_bv_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvConcat) { @@ -332,7 +287,6 @@ TEST_F(TestSmtShim, OpBvConcat) { auto prop = (var_bv_a == var_bv_b) & (a_con_b != b_con_a); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvExtract) { @@ -341,7 +295,6 @@ TEST_F(TestSmtShim, OpBvExtract) { auto prop = extract_a_con_b != var_bv_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvZext) { @@ -350,7 +303,6 @@ TEST_F(TestSmtShim, OpBvZext) { auto prop = zext_a != zero_con_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - // LIA } TEST_F(TestSmtShim, OpBvSext) { @@ -360,7 +312,6 @@ TEST_F(TestSmtShim, OpBvSext) { auto prop = sext_a != zero_con_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - // LIA } TEST_F(TestSmtShim, OpBvRotate) { @@ -369,7 +320,6 @@ TEST_F(TestSmtShim, OpBvRotate) { auto prop = right_rotate_back != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolImply) { @@ -377,7 +327,6 @@ TEST_F(TestSmtShim, OpBoolImply) { auto prop = a_imply_b & var_bool_a & !var_bool_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBoolIte) { @@ -385,7 +334,6 @@ TEST_F(TestSmtShim, OpBoolIte) { auto prop = ite_a_0_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvIte) { @@ -393,7 +341,6 @@ TEST_F(TestSmtShim, OpBvIte) { auto prop = (var_bv_a == var_bv_b) & (ite_c_a_b != var_bv_a); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpApplyFunc) { @@ -403,7 +350,6 @@ TEST_F(TestSmtShim, OpApplyFunc) { auto prop = (var_bv_a == var_bv_b) & (out1 != out2); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstBool) { @@ -412,7 +358,6 @@ TEST_F(TestSmtShim, ConstBool) { auto prop = a_is_0 & b_is_1 & (var_bool_a == var_bool_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstBv) { @@ -421,7 +366,6 @@ TEST_F(TestSmtShim, ConstBv) { auto prop = a_is_0 & b_is_1 & (var_bv_a == var_bv_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstMem) { @@ -436,7 +380,6 @@ TEST_F(TestSmtShim, ConstMem) { auto prop = !Imply(save_addr, is_linear); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); - CheckUnsatLia(prop); } TEST_F(TestSmtShim, DiscreteUsage) { @@ -472,25 +415,6 @@ TEST_F(TestSmtShim, DiscreteUsage) { EXPECT_TRUE(res.is_unsat()); } #endif // SMTSWITCH_TEST - -#ifdef SMTSWITCH_TEST - { // LiaCvtr - auto solver = smt::Z3SolverFactory::create(false); - auto lia = LiaCvtr(solver); - auto shim = SmtShim(lia); - - // Use SmtShim to gen/add formulas - solver->assert_formula(shim.GetShimExpr(a_ult_b.get())); - solver->assert_formula(shim.GetShimExpr(a_ugt_b.get())); - solver->assert_formula(shim.GetShimExpr(a_eq_b.get())); - - // Use z3's command for checking - auto raw = std::static_pointer_cast(solver); - auto slv = raw->get_z3_solver(); // get_z3_context() also available - auto res = slv->check(); - EXPECT_TRUE(res == z3::unsat); - } -#endif // SMTSWITCH_TEST } } // namespace ilang From da7816bfeacbf27c5deffd4630273ac28c9b5618 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 00:17:18 -0400 Subject: [PATCH 17/32] SMT converter for LIA. Only partial ops are supported. --- .gitmodules | 2 +- README.md | 4 +- cmake/FindGMP.cmake | 19 ++ cmake/Findsmtswitch.cmake | 32 ++ extern/smt-switch | 2 +- include/ilang/ilang++.h | 6 +- include/ilang/target-smt/lia_cvtr.h | 63 ++++ include/ilang/target-smt/smt_switch_itf.h | 17 +- src/CMakeLists.txt | 8 + src/ilang++.cc | 7 +- src/target-smt/CMakeLists.txt | 1 + src/target-smt/lia_cvtr.cc | 270 ++++++++++++++++ src/target-smt/smt_switch_itf.cc | 2 +- test/CMakeLists.txt | 13 +- test/t_api.cc | 35 +++ test/t_smt_shim.cc | 91 +++++- test/t_smt_switch_itf.cc | 367 ---------------------- test/t_unroller_smt.cc | 10 +- 18 files changed, 553 insertions(+), 396 deletions(-) create mode 100644 cmake/FindGMP.cmake create mode 100644 include/ilang/target-smt/lia_cvtr.h create mode 100644 src/target-smt/lia_cvtr.cc delete mode 100644 test/t_smt_switch_itf.cc diff --git a/.gitmodules b/.gitmodules index f2f3e286e..aeb856b19 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/PrincetonUniversity/ILA-Synthesis-Python.git [submodule "extern/smt-switch"] path = extern/smt-switch - url = https://github.com/makaimann/smt-switch.git + url = https://github.com/stanford-centaur/smt-switch diff --git a/README.md b/README.md index dd536e1b9..dc5bd3874 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ sudo make install - Use `-DILANG_BUILD_TEST=OFF` to disalbe building the unit tests. - Use `-DILANG_BUILD_SYNTH=ON` to enable building the synthesis engine (required [Boost](https://www.boost.org)). - Use `-DILANG_BUILD_INVSYN=OFF` to disable building invariant synthesis feature. -- Use `-DILANG_BUILD_SWITCH=ON` to enable building [smt-switch](https://github.com/makaimann/smt-switch.git) interface support. +- Use `-DILANG_BUILD_SWITCH=ON` to enable building [smt-switch](https://github.com/stanford-centaur/smt-switch) interface support. - Use `-DILANG_BUILD_COSIM=ON` to enable building [Xilinx cosimulation](https://www.linuxsecrets.com/xilinx/QEMU%20SystemC%20and%20TLM%20CoSimulation.html) support. - Use `-DILANG_INSTALL_DEV=ON` to install working features. @@ -233,7 +233,7 @@ Copyright (c) 2018 Ben Marshall. ILAng uses the [SMT parser](https://es-static.fbk.eu/people/griggio/misc/smtlib2parser.html), which is licensed under the [MIT License](https://es-static.fbk.eu/people/griggio/misc/smtlib2parser.html). Copyright (c) 2010 Alberto Griggio. -ILAng uses the [smt-switch](https://github.com/makaimann/smt-switch.git), which is licensed under the [BSD 3-Clause License](https://github.com/makaimann/smt-switch/blob/master/LICENSE). +ILAng uses the [smt-switch](https://github.com/stanford-centaur/smt-switch), which is licensed under the [BSD 3-Clause License](https://github.com/stanford-centaur/smt-switch/blob/master/LICENSE). Copyright (c) 2019-2020 the authors. ILAng uses [ItSy](https://github.com/PrincetonUniversity/ItSy), which is licensed under the [MIT License](https://github.com/PrincetonUniversity/ItSy/blob/master/LICENSE). diff --git a/cmake/FindGMP.cmake b/cmake/FindGMP.cmake new file mode 100644 index 000000000..797973064 --- /dev/null +++ b/cmake/FindGMP.cmake @@ -0,0 +1,19 @@ +# Find GMP +# GMP_FOUND - system has GMP lib +# GMP_INCLUDE_DIR - the GMP include directory +# GMP_LIBRARIES - Libraries needed to use GMP + +find_path(GMP_INCLUDE_DIR NAMES gmp.h gmpxx.h) +find_library(GMP_LIBRARIES NAMES gmp) +find_library(GMPXX_LIBRARIES NAMES gmpxx) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GMP DEFAULT_MSG GMP_INCLUDE_DIR GMP_LIBRARIES) + +mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES) +if(GMP_LIBRARIES) + message(STATUS "Found GMP libs: ${GMP_LIBRARIES}") +endif() +if (GMPXX_LIBRARIES) + message(STATUS "Found GMPXX libs: ${GMPXX_LIBRARIES}") +endif() diff --git a/cmake/Findsmtswitch.cmake b/cmake/Findsmtswitch.cmake index 68f352368..db911ae21 100644 --- a/cmake/Findsmtswitch.cmake +++ b/cmake/Findsmtswitch.cmake @@ -11,6 +11,7 @@ # SMTSWITCH_CVC4_FOUND # SMTSWITCH_MSAT_FOUND # SMTSWITCH_YICES2_FOUND +# SMTSWITCH_Z3_FOUND # # and the following imported targets # @@ -19,6 +20,7 @@ # smt-switch::smt-switch-cvc4 # smt-switch::smt-switch-msat # smt-switch::smt-switch-yices2 +# smt-switch::smt-switch-z3 # # XXX smt-switch needs to be built with static type @@ -26,6 +28,7 @@ find_package(PkgConfig) pkg_check_modules(PC_SMTSWITCH QUIET SMTSWITCH) +find_package(GMP REQUIRED) ## ## core smt-switch @@ -119,6 +122,22 @@ endif() set(SMTSWITCH_YICES2_LIBRARY ${SMTSWITCH_YICES_LIBRARY} ${SMTSWITCH_LIBRARY}) mark_as_advanced(SMTSWITCH_YICES2_FOUND) +# z3 +find_library(SMTSWITCH_Z3_LIBRARY + NAMES smt-switch-z3 + HINTS ${PC_SMTSWITCH_LIBDIR} ${PC_SMTSWITCH_LIBRARY_DIRS} + PATH_SUFFIXES smt-switch +) + +if(SMTSWITCH_Z3_LIBRARY) + set(SMTSWITCH_Z3_FOUND TRUE) +else() + set(SMTSWITCH_Z3_FOUND FALSE) +endif() + +set(SMTSWITCH_Z3_LIBRARY ${SMTSWITCH_Z3_LIBRARY} ${SMTSWITCH_LIBRARY}) +mark_as_advanced(SMTSWITCH_Z3_FOUND) + # create imported target smt-switch::smt-switch if(SMTSWITCH_FOUND AND NOT TARGET smt-switch::smt-switch) add_library(smt-switch::smt-switch INTERFACE IMPORTED) @@ -184,3 +203,16 @@ if(SMTSWITCH_YICES2_FOUND AND NOT TARGET smt-switch::smt-switch-yices2) ) endif() +# create imported target smt-switch::smt-switch-z3 +if(SMTSWITCH_Z3_FOUND AND NOT TARGET smt-switch::smt-switch-z3) + add_library(smt-switch::smt-switch-z3 INTERFACE IMPORTED) + set_property( + TARGET smt-switch::smt-switch-z3 + PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SMTSWITCH_INCLUDE_DIR} + ) + set_property( + TARGET smt-switch::smt-switch-z3 + PROPERTY INTERFACE_LINK_LIBRARIES ${SMTSWITCH_Z3_LIBRARY} + ) +endif() + diff --git a/extern/smt-switch b/extern/smt-switch index 68dad97f7..768ed5d05 160000 --- a/extern/smt-switch +++ b/extern/smt-switch @@ -1 +1 @@ -Subproject commit 68dad97f77282f77d3f5ae93ffc5e521d88e3992 +Subproject commit 768ed5d05315d0b652a915adcfbbc7f3e7a4896a diff --git a/include/ilang/ilang++.h b/include/ilang/ilang++.h index c183dea7b..1c3f1e95a 100644 --- a/include/ilang/ilang++.h +++ b/include/ilang/ilang++.h @@ -750,12 +750,12 @@ class IlaZ3Unroller { #ifdef SMTSWITCH_INTERFACE -/// \brief Reset the solver and generate the SMT Term (for smt-switch). +/// \brief Generate the SMT Term (for smt-switch). /// \param[in] solver The SMT solver in smt-switch. /// \param[in] expr The target ILA expression. /// \param[in] suffix The suffix to add on SMT symbols' name. -smt::Term ResetAndGetSmtTerm(smt::SmtSolver& solver, const ExprRef& expr, - const std::string& suffix = ""); +smt::Term GetSmtTerm(smt::SmtSolver& solver, const ExprRef& expr, + const std::string& suffix = ""); #endif // SMTSWITCH_INTERFACE diff --git a/include/ilang/target-smt/lia_cvtr.h b/include/ilang/target-smt/lia_cvtr.h new file mode 100644 index 000000000..913a92f63 --- /dev/null +++ b/include/ilang/target-smt/lia_cvtr.h @@ -0,0 +1,63 @@ +/// \file +/// Class LiaCvtr - the converter for linear integer arithmetic SMT expressions. + +#ifndef ILANG_TARGET_SMT_LIA_CVTR_H__ +#define ILANG_TARGET_SMT_LIA_CVTR_H__ + +#ifdef SMTSWITCH_INTERFACE + +#include +#include + +#include + +#include +#include + +/// \namepsace ilang +namespace ilang { + +class LiaCvtr : public SmtSwitchItf { +public: + // ------------------------- CONSTRUCTOR/DESTRUCTOR ----------------------- // + /// Default constructor. + LiaCvtr(smt::SmtSolver& solver); + /// Default destructor. + ~LiaCvtr(); + +private: + /// Type for cacheing the generated expressions. + typedef std::unordered_map ExprTermMap; + /// Type for cacheing the generated functions. + typedef std::unordered_map FuncTermMap; + + // ------------------------- MEMBERS -------------------------------------- // + /// The underlying SMT solver. + smt::SmtSolver& solver_; + /// Container for cacheing expression Terms. + ExprTermMap expr_map_; + /// Container for cacheing function Terms. + FuncTermMap func_map_; + /// Name suffix for each expression generation (e.g., time step). + std::string suffix_ = ""; + + // ------------------------- HELPERS -------------------------------------- // + /// Make Term of expr variable. + smt::Term ExprVar2Term(const ExprPtr& expr); + /// Make Term of expr constant. + smt::Term ExprConst2Term(const ExprPtr& expr); + /// Make Term of expr operator. + smt::Term ExprOp2Term(const ExprPtr& expr, const smt::TermVec& arg_terms); + /// Make Term of func. + smt::Term Func2Term(const FuncPtr& func); + /// Make smt::Sort of ilang::SortPtr. + smt::Sort IlaSort2LiaSort(const SortPtr& s); + +}; // class LiaCvtr + +} // namespace ilang + +#endif // SMTSWITCH_INTERFACE + +#endif // ILANG_TARGET_SMT_LIA_CVTR_H__ + diff --git a/include/ilang/target-smt/smt_switch_itf.h b/include/ilang/target-smt/smt_switch_itf.h index e7e317b69..8ceee1670 100644 --- a/include/ilang/target-smt/smt_switch_itf.h +++ b/include/ilang/target-smt/smt_switch_itf.h @@ -1,8 +1,8 @@ /// \file /// Class SmtSwitchItf - the interface to external library smt-switch. -#ifndef ILANG_ILA_MNGR_U_SMT_SWITCH_H__ -#define ILANG_ILA_MNGR_U_SMT_SWITCH_H__ +#ifndef ILANG_TARGET_SMT_SMT_SWITCH_H__ +#define ILANG_TARGET_SMT_SMT_SWITCH_H__ #ifdef SMTSWITCH_INTERFACE @@ -74,15 +74,16 @@ class SmtSwitchItf { /// Insert the SMT Term of the given node into the map. void PopulateExprMap(const ExprPtr& expr); /// Make Term of expr variable. - smt::Term ExprVar2Term(const ExprPtr& expr); + virtual smt::Term ExprVar2Term(const ExprPtr& expr); /// Make Term of expr constant. - smt::Term ExprConst2Term(const ExprPtr& expr); + virtual smt::Term ExprConst2Term(const ExprPtr& expr); /// Make Term of expr operator. - smt::Term ExprOp2Term(const ExprPtr& expr, const smt::TermVec& arg_terms); + virtual smt::Term ExprOp2Term(const ExprPtr& expr, + const smt::TermVec& arg_terms); /// Make Term of func. - smt::Term Func2Term(const FuncPtr& func); + virtual smt::Term Func2Term(const FuncPtr& func); /// Make smt::Sort of ilang::SortPtr. - smt::Sort IlaSort2SmtSort(const SortPtr& s); + virtual smt::Sort IlaSort2SmtSort(const SortPtr& s); }; // class SmtSwitchItf @@ -90,5 +91,5 @@ class SmtSwitchItf { #endif // SMTSWITCH_INTERFACE -#endif // ILANG_ILA_MNGR_U_SMT_SWITCH_H__ +#endif // ILANG_TARGET_SMT_SMT_SWITCH_H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b80acf95a..3d23ff0f2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -206,6 +206,10 @@ if(${ILANG_BUILD_SWITCH}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch) target_compile_definitions(${ILANG_LIB_NAME} PUBLIC SMTSWITCH_INTERFACE) + find_package(Threads REQUIRED) + target_link_libraries(${ILANG_LIB_NAME} PUBLIC gmp) + target_link_libraries(${ILANG_LIB_NAME} PUBLIC Threads::Threads) + if(${SMTSWITCH_BTOR_FOUND}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-btor) endif() @@ -222,6 +226,10 @@ if(${ILANG_BUILD_SWITCH}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-yices2) endif() + if(${SMTSWITCH_Z3_FOUND}) + target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-z3) + endif() + endif() ## diff --git a/src/ilang++.cc b/src/ilang++.cc index 931e474c7..53fee212d 100644 --- a/src/ilang++.cc +++ b/src/ilang++.cc @@ -1042,12 +1042,13 @@ void EnableDebug(const std::string& tag) { DebugLog::Enable(tag); } void DisableDebug(const std::string& tag) { DebugLog::Disable(tag); } #ifdef SMTSWITCH_INTERFACE -smt::Term ResetAndGetSmtTerm(smt::SmtSolver& solver, const ExprRef& expr, - const std::string& suffix) { - solver->reset(); + +smt::Term GetSmtTerm(smt::SmtSolver& solver, const ExprRef& expr, + const std::string& suffix) { auto itf = SmtSwitchItf(solver); return itf.GetSmtTerm(expr.get(), suffix); } + #endif // SMTSWITCH_INTERFACE } // namespace ilang diff --git a/src/target-smt/CMakeLists.txt b/src/target-smt/CMakeLists.txt index b9781559d..d908b3b0b 100644 --- a/src/target-smt/CMakeLists.txt +++ b/src/target-smt/CMakeLists.txt @@ -2,6 +2,7 @@ # source # ---------------------------------------------------------------------------- # target_sources(${ILANG_LIB_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/lia_cvtr.cc ${CMAKE_CURRENT_SOURCE_DIR}/smt_switch_itf.cc ${CMAKE_CURRENT_SOURCE_DIR}/z3_expr_adapter.cc ) diff --git a/src/target-smt/lia_cvtr.cc b/src/target-smt/lia_cvtr.cc new file mode 100644 index 000000000..e5fa8bba5 --- /dev/null +++ b/src/target-smt/lia_cvtr.cc @@ -0,0 +1,270 @@ +/// \file +/// Source for the LIA converter. + +#ifdef SMTSWITCH_INTERFACE + +#include + +#include + +#include +#include +#include + +namespace ilang { + +LiaCvtr::LiaCvtr(smt::SmtSolver& solver) + : SmtSwitchItf(solver), solver_(solver) {} + +LiaCvtr::~LiaCvtr() {} + +smt::Term LiaCvtr::ExprVar2Term(const ExprPtr& expr) { + auto prefix = (expr->host()) ? expr->host()->GetRootName() : ""; + auto e_name = expr->name().format_str(prefix, suffix_); + + auto smt_sort = IlaSort2LiaSort(expr->sort()); + return solver_->make_symbol(e_name, smt_sort); +} + +smt::Term LiaCvtr::ExprConst2Term(const ExprPtr& expr) { + auto expr_const = std::static_pointer_cast(expr); + + switch (auto sort_uid = asthub::GetUidSort(expr); sort_uid) { + case AstUidSort::kBool: { + return solver_->make_term(expr_const->val_bool()->val()); + } + case AstUidSort::kBv: { + return solver_->make_term(expr_const->val_bv()->val(), + solver_->make_sort(smt::INT)); + } + default: { + ILA_ASSERT(sort_uid == AstUidSort::kMem); + auto int_sort = solver_->make_sort(smt::INT); + auto mem_sort = solver_->make_sort(smt::ARRAY, int_sort, int_sort); + + // create const memory with default value + auto memory_value = expr_const->val_mem(); + auto def_val_term = solver_->make_term(memory_value->def_val(), int_sort); + auto const_memory = solver_->make_term(def_val_term, mem_sort); + + // write in non-default addr-data pairs + auto& value_map = memory_value->val_map(); + for (const auto& p : value_map) { + auto addr_term = solver_->make_term(p.first, int_sort); + auto data_term = solver_->make_term(p.second, int_sort); + auto memory_wr = solver_->make_term(smt::PrimOp::Store, const_memory, + addr_term, data_term); + const_memory = memory_wr; + } + + return const_memory; + } + }; // switch sort_uid +} + +smt::Term LiaCvtr::ExprOp2Term(const ExprPtr& expr, + const smt::TermVec& arg_terms) { + // construct based on the operator + switch (auto expr_op_uid = asthub::GetUidExprOp(expr); expr_op_uid) { + case AstUidExprOp::kNegate: { + return solver_->make_term(smt::PrimOp::Negate, arg_terms.at(0)); + } + case AstUidExprOp::kNot: { + return solver_->make_term(smt::PrimOp::Not, arg_terms.at(0)); + } +#if 0 + case AstUidExprOp::kComplement: { + return solver_->make_term(smt::PrimOp::BVNot, arg_terms.at(0)); + } +#endif + case AstUidExprOp::kAnd: { + ILA_CHECK(expr->is_bool()) << "BVAnd not supported for LIA"; + return solver_->make_term(smt::PrimOp::And, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kOr: { + ILA_CHECK(expr->is_bool()) << "BVOr not supported for LIA"; + return solver_->make_term(smt::PrimOp::Or, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kXor: { + ILA_CHECK(expr->is_bool()) << "BVXor not supported for LIA"; + return solver_->make_term(smt::PrimOp::Xor, arg_terms.at(0), + arg_terms.at(1)); + } +#if 0 + case AstUidExprOp::kShiftLeft: { + return solver_->make_term(smt::PrimOp::BVShl, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kArithShiftRight: { + return solver_->make_term(smt::PrimOp::BVAshr, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kLogicShiftRight: { + return solver_->make_term(smt::PrimOp::BVLshr, arg_terms.at(0), + arg_terms.at(1)); + } +#endif + case AstUidExprOp::kAdd: { + return solver_->make_term(smt::PrimOp::Plus, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kSubtract: { + return solver_->make_term(smt::PrimOp::Minus, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kDivide: { + return solver_->make_term(smt::PrimOp::IntDiv, arg_terms.at(0), + arg_terms.at(1)); + } +#if 0 + case AstUidExprOp::kSignedRemainder: { + return solver_->make_term(smt::PrimOp::BVSrem, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kUnsignedRemainder: { + return solver_->make_term(smt::PrimOp::BVUrem, arg_terms.at(0), + arg_terms.at(1)); + } +#endif + case AstUidExprOp::kSignedModular: { + return solver_->make_term(smt::PrimOp::Mod, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kMultiply: { + return solver_->make_term(smt::PrimOp::Mult, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kEqual: { + return solver_->make_term(smt::PrimOp::Equal, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kLessThan: { + return solver_->make_term(smt::PrimOp::Lt, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kGreaterThan: { + return solver_->make_term(smt::PrimOp::Gt, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kUnsignedLessThan: { + return solver_->make_term(smt::PrimOp::Lt, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kUnsignedGreaterThan: { + return solver_->make_term(smt::PrimOp::Gt, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kLoad: { + return solver_->make_term(smt::PrimOp::Select, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kStore: { + return solver_->make_term(smt::PrimOp::Store, arg_terms.at(0), + arg_terms.at(1), arg_terms.at(2)); + } + case AstUidExprOp::kConcatenate: { + auto w_exp_v = 1 << expr->arg(1)->sort()->bit_width(); + auto w_exp_t = solver_->make_term(w_exp_v, solver_->make_sort(smt::INT)); + auto a0_lsh_w = + solver_->make_term(smt::PrimOp::Mult, arg_terms.at(0), w_exp_t); + return solver_->make_term(smt::PrimOp::Plus, a0_lsh_w, arg_terms.at(1)); + } +#if 0 + case AstUidExprOp::kExtract: { + auto op = smt::Op(smt::PrimOp::Extract, expr->param(0), expr->param(1)); + return solver_->make_term(op, arg_terms.at(0)); + } +#endif + case AstUidExprOp::kZeroExtend: { + return arg_terms.at(0); + } + case AstUidExprOp::kSignedExtend: { + return arg_terms.at(0); + } +#if 0 + case AstUidExprOp::kRotateLeft: { + auto op = smt::Op(smt::PrimOp::Rotate_Left, expr->param(0)); + return solver_->make_term(op, arg_terms.at(0)); + } + case AstUidExprOp::kRotateRight: { + auto op = smt::Op(smt::PrimOp::Rotate_Right, expr->param(0)); + return solver_->make_term(op, arg_terms.at(0)); + } +#endif + case AstUidExprOp::kImply: { + return solver_->make_term(smt::PrimOp::Implies, arg_terms.at(0), + arg_terms.at(1)); + } + case AstUidExprOp::kIfThenElse: { + return solver_->make_term(smt::PrimOp::Ite, arg_terms.at(0), + arg_terms.at(1), arg_terms.at(2)); + } + case AstUidExprOp::kApplyFunc: { + auto expr_appfunc = std::static_pointer_cast(expr); + auto func = expr_appfunc->func(); + + // term placeholder for the solver + smt::TermVec func_arg_terms; + + // check if the func has been visited + auto pos = func_map_.find(func); + if (pos != func_map_.end()) { + func_arg_terms.push_back(pos->second); + } else { // fist visit - create new term + auto func_term = Func2Term(func); + func_arg_terms.push_back(func_term); + func_map_[func] = func_term; + } + + // apply func + for (auto arg_i : arg_terms) { + func_arg_terms.push_back(arg_i); + } + return solver_->make_term(smt::PrimOp::Apply, func_arg_terms); + } + default: { + ILA_CHECK(false) << "Op " << expr_op_uid << " not supported for LIA (yet)"; + return solver_->make_term(true); + } + }; // switch expr_op_uid +} + +smt::Term LiaCvtr::Func2Term(const FuncPtr& func) { + // func name (for z3 compatibility) + auto prefix = (func->host()) ? func->host()->GetRootName() : ""; + auto f_name = func->name().format_str(prefix, suffix_); + + // func sort + auto arg_sorts = smt::SortVec(); + for (size_t i = 0; i != func->arg_num(); i++) { + arg_sorts.push_back(IlaSort2LiaSort(func->arg(i))); + } + arg_sorts.push_back(IlaSort2LiaSort(func->out())); // return is the last + auto func_sort = solver_->make_sort(smt::FUNCTION, arg_sorts); + + // func term + auto func_term = solver_->make_symbol(f_name, func_sort); + return func_term; +} + +smt::Sort LiaCvtr::IlaSort2LiaSort(const SortPtr& s) { + switch (auto sort_uid = s->uid(); sort_uid) { + case AstUidSort::kBool: { + return solver_->make_sort(smt::BOOL); + } + case AstUidSort::kBv: { + return solver_->make_sort(smt::INT); + } + default: { + ILA_ASSERT(sort_uid == AstUidSort::kMem); + return solver_->make_sort(smt::ARRAY, solver_->make_sort(smt::INT), + solver_->make_sort(smt::INT)); + } + }; // switch sort uid +} + +} // namespace ilang + +#endif // SMTSWITCH_INTERFACE diff --git a/src/target-smt/smt_switch_itf.cc b/src/target-smt/smt_switch_itf.cc index 14be39119..4ae8d8476 100644 --- a/src/target-smt/smt_switch_itf.cc +++ b/src/target-smt/smt_switch_itf.cc @@ -331,4 +331,4 @@ smt::Sort SmtSwitchItf::IlaSort2SmtSort(const SortPtr& s) { } // namespace ilang -#endif // SMT_SWITCH_INTERFACE +#endif // SMTSWITCH_INTERFACE diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1171ae68c..fe61a9ebf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -75,7 +75,6 @@ package_add_test(${ILANG_TEST_MAIN} t_portable.cc t_smt_in.cc t_smt_shim.cc - t_smt_switch_itf.cc t_smt_trans.cc t_sort.cc t_symbol.cc @@ -109,7 +108,19 @@ endif() if(${ILANG_BUILD_SWITCH}) find_package(smtswitch REQUIRED) + find_package(Threads REQUIRED) + target_link_libraries(${ILANG_TEST_MAIN} gmp) + target_link_libraries(${ILANG_TEST_MAIN} Threads::Threads) + + if(${SMTSWITCH_Z3_FOUND}) + target_compile_definitions(${ILANG_TEST_MAIN} PRIVATE SMTSWITCH_Z3) + endif() + if(${SMTSWITCH_BTOR_FOUND}) + target_compile_definitions(${ILANG_TEST_MAIN} PRIVATE SMTSWITCH_BTOR) + endif() + + if(${SMTSWITCH_Z3_FOUND} OR ${SMTSWITCH_BTOR_FOUND}) target_compile_definitions(${ILANG_TEST_MAIN} PRIVATE SMTSWITCH_TEST) endif() endif() diff --git a/test/t_api.cc b/test/t_api.cc index c24258e73..f0621ffb9 100644 --- a/test/t_api.cc +++ b/test/t_api.cc @@ -3,6 +3,18 @@ #include +#ifdef SMTSWITCH_TEST +#include +#endif // SMTSWITCH_TEST + +#ifdef SMTSWITCH_Z3 +#include +#endif // SMTSWITCH_Z3 + +#ifdef SMTSWITCH_BTOR +#include +#endif // SMTSWITCH_BTOR + #include #include @@ -582,6 +594,29 @@ TEST(TestApi, UnrollPathFreeWithFunc) { EXPECT_EQ(solver.check(), z3::unsat); } +TEST(TestApi, GetSmtTerm) { + auto m = Ila("M"); + auto v = m.NewBvState("var", 8); + auto p = ((v + 1) - 1) != v; + + auto CheckViaSolver = [&p](smt::SmtSolver& s) { + auto term = GetSmtTerm(s, p); + s->assert_formula(term); + auto res = s->check_sat(); + EXPECT_TRUE(res.is_unsat()); + }; + +#ifdef SMTSWITCH_BTOR + auto s_btor = smt::BoolectorSolverFactory::create(false); + CheckViaSolver(s_btor); +#endif // SMTSWITCH_BTOR + +#ifdef SMTSWITCH_Z3 + auto s_z3 = smt::Z3SolverFactory::create(false); + CheckViaSolver(s_z3); +#endif // SMTSWITCH_Z3 +} + TEST(TestApi, Portable) { // SetToStdErr(1); EnableDebug("Portable"); diff --git a/test/t_smt_shim.cc b/test/t_smt_shim.cc index 2d158582d..5a217b690 100644 --- a/test/t_smt_shim.cc +++ b/test/t_smt_shim.cc @@ -2,11 +2,20 @@ /// Unit tests for SmtShim #ifdef SMTSWITCH_TEST -#include #include #endif // SMTSWITCH_TEST +#ifdef SMTSWITCH_BTOR +#include +#endif // SMTSWITCH_BTOR + +#ifdef SMTSWITCH_Z3 +#include +#include +#endif // SMTSWITCH_Z3 + #include +#include #include #include #include @@ -44,7 +53,7 @@ class TestSmtShim : public ::testing::Test { } void CheckUnsatSwitch(const ExprRef& e) { -#ifdef SMTSWITCH_TEST +#ifdef SMTSWITCH_BTOR auto solver = smt::BoolectorSolverFactory::create(false); auto switch_itf = SmtSwitchItf(solver); auto shim = SmtShim(switch_itf); @@ -53,7 +62,20 @@ class TestSmtShim : public ::testing::Test { solver->assert_formula(switch_term); auto res = solver->check_sat(); EXPECT_TRUE(res.is_unsat()); -#endif // SMTSWITCH_TEST +#endif // SMTSWITCH_BTOR + } + + void CheckUnsatLia(const ExprRef& e) { +#ifdef SMTSWITCH_Z3 + auto solver = smt::Z3SolverFactory::create(false); + auto lia_cvtr = LiaCvtr(solver); + auto shim = SmtShim(lia_cvtr); + + auto lia_term = shim.GetShimExpr(e.get()); + solver->assert_formula(lia_term); + auto res = solver->check_sat(); + EXPECT_TRUE(res.is_unsat()); +#endif // SMTSWITCH_Z3 } Ila m = Ila("host"); @@ -71,6 +93,7 @@ TEST_F(TestSmtShim, OpBoolNot) { auto prop = not_not_a != var_bool_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvNeg) { @@ -79,6 +102,7 @@ TEST_F(TestSmtShim, OpBvNeg) { auto prop = neg_neg_a != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvComplement) { @@ -87,12 +111,14 @@ TEST_F(TestSmtShim, OpBvComplement) { auto prop = com_com_a != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolAnd) { auto a_and_not_a = var_bool_a & !var_bool_a; CheckUnsatZ3(a_and_not_a); CheckUnsatSwitch(a_and_not_a); + CheckUnsatLia(a_and_not_a); } TEST_F(TestSmtShim, OpBvAnd) { @@ -100,6 +126,7 @@ TEST_F(TestSmtShim, OpBvAnd) { auto prop = a_and_all1 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolOr) { @@ -107,6 +134,7 @@ TEST_F(TestSmtShim, OpBoolOr) { auto prop = !a_or_not_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvOr) { @@ -114,6 +142,7 @@ TEST_F(TestSmtShim, OpBvOr) { auto prop = a_or_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolXor) { @@ -122,6 +151,7 @@ TEST_F(TestSmtShim, OpBoolXor) { auto prop = a_xor_b & a_eq_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvXor) { @@ -130,6 +160,7 @@ TEST_F(TestSmtShim, OpBvXor) { auto prop = a_eq_b & (a_xor_b == BvConst((1 << BV_SIZE) - 1, BV_SIZE)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvShl) { @@ -137,6 +168,7 @@ TEST_F(TestSmtShim, OpBvShl) { auto prop = a_shl_all != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvAshr) { @@ -146,6 +178,7 @@ TEST_F(TestSmtShim, OpBvAshr) { auto prop = !(is_pos | is_neg); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvLshr) { @@ -153,6 +186,7 @@ TEST_F(TestSmtShim, OpBvLshr) { auto prop = a_ashr_all != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvAdd) { @@ -160,6 +194,7 @@ TEST_F(TestSmtShim, OpBvAdd) { auto prop = a_plus_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSub) { @@ -167,6 +202,7 @@ TEST_F(TestSmtShim, OpBvSub) { auto prop = a_minus_0 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvAddSub) { @@ -175,6 +211,7 @@ TEST_F(TestSmtShim, OpBvAddSub) { auto prop = a_plus_b_minus_b != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvMul) { @@ -182,6 +219,7 @@ TEST_F(TestSmtShim, OpBvMul) { auto prop = a_times_0 != BvConst(0, BV_SIZE); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvDiv) { @@ -189,6 +227,7 @@ TEST_F(TestSmtShim, OpBvDiv) { auto prop = a_div_1 != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSrem) { @@ -197,6 +236,7 @@ TEST_F(TestSmtShim, OpBvSrem) { auto prop = !(Slt(a_srem_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvUrem) { @@ -205,6 +245,7 @@ TEST_F(TestSmtShim, OpBvUrem) { auto prop = !(Ult(a_urem_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvSmod) { @@ -213,6 +254,7 @@ TEST_F(TestSmtShim, OpBvSmod) { auto prop = !(Slt(a_smod_3, div)); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + // LIA } TEST_F(TestSmtShim, OpBvSltSge) { @@ -221,6 +263,7 @@ TEST_F(TestSmtShim, OpBvSltSge) { auto prop = a_slt_b & a_sge_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSleSgt) { @@ -229,6 +272,7 @@ TEST_F(TestSmtShim, OpBvSleSgt) { auto prop = a_sle_b & a_sgt_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSltSgteq) { @@ -238,6 +282,7 @@ TEST_F(TestSmtShim, OpBvSltSgteq) { auto prop = !(a_slt_b | a_sgt_b | a_eq_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUltUge) { @@ -246,6 +291,7 @@ TEST_F(TestSmtShim, OpBvUltUge) { auto prop = a_ult_b & a_uge_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUleUgt) { @@ -254,6 +300,7 @@ TEST_F(TestSmtShim, OpBvUleUgt) { auto prop = a_ule_b & a_ugt_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvUltUgteq) { @@ -263,6 +310,7 @@ TEST_F(TestSmtShim, OpBvUltUgteq) { auto prop = !(a_ult_b | a_ugt_b | a_eq_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpMemLoad) { @@ -271,6 +319,7 @@ TEST_F(TestSmtShim, OpMemLoad) { auto prop = (var_bv_a == var_bv_b) & (data_a != data_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpMemStore) { @@ -279,6 +328,7 @@ TEST_F(TestSmtShim, OpMemStore) { auto prop = data != var_bv_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvConcat) { @@ -287,6 +337,7 @@ TEST_F(TestSmtShim, OpBvConcat) { auto prop = (var_bv_a == var_bv_b) & (a_con_b != b_con_a); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvExtract) { @@ -295,6 +346,7 @@ TEST_F(TestSmtShim, OpBvExtract) { auto prop = extract_a_con_b != var_bv_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvZext) { @@ -303,6 +355,7 @@ TEST_F(TestSmtShim, OpBvZext) { auto prop = zext_a != zero_con_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvSext) { @@ -312,6 +365,7 @@ TEST_F(TestSmtShim, OpBvSext) { auto prop = sext_a != zero_con_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBvRotate) { @@ -320,6 +374,7 @@ TEST_F(TestSmtShim, OpBvRotate) { auto prop = right_rotate_back != var_bv_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + EXPECT_DEATH(CheckUnsatLia(prop), ".*"); } TEST_F(TestSmtShim, OpBoolImply) { @@ -327,6 +382,7 @@ TEST_F(TestSmtShim, OpBoolImply) { auto prop = a_imply_b & var_bool_a & !var_bool_b; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBoolIte) { @@ -334,6 +390,7 @@ TEST_F(TestSmtShim, OpBoolIte) { auto prop = ite_a_0_a; CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpBvIte) { @@ -341,6 +398,7 @@ TEST_F(TestSmtShim, OpBvIte) { auto prop = (var_bv_a == var_bv_b) & (ite_c_a_b != var_bv_a); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, OpApplyFunc) { @@ -350,6 +408,7 @@ TEST_F(TestSmtShim, OpApplyFunc) { auto prop = (var_bv_a == var_bv_b) & (out1 != out2); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstBool) { @@ -358,6 +417,7 @@ TEST_F(TestSmtShim, ConstBool) { auto prop = a_is_0 & b_is_1 & (var_bool_a == var_bool_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstBv) { @@ -366,6 +426,7 @@ TEST_F(TestSmtShim, ConstBv) { auto prop = a_is_0 & b_is_1 & (var_bv_a == var_bv_b); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, ConstMem) { @@ -380,6 +441,7 @@ TEST_F(TestSmtShim, ConstMem) { auto prop = !Imply(save_addr, is_linear); CheckUnsatZ3(prop); CheckUnsatSwitch(prop); + CheckUnsatLia(prop); } TEST_F(TestSmtShim, DiscreteUsage) { @@ -401,7 +463,7 @@ TEST_F(TestSmtShim, DiscreteUsage) { EXPECT_TRUE(res == z3::unsat); } -#ifdef SMTSWITCH_TEST +#ifdef SMTSWITCH_BTOR { // switch auto solver = smt::BoolectorSolverFactory::create(false); auto itf = SmtSwitchItf(solver); @@ -414,7 +476,26 @@ TEST_F(TestSmtShim, DiscreteUsage) { auto res = solver->check_sat(); EXPECT_TRUE(res.is_unsat()); } -#endif // SMTSWITCH_TEST +#endif // SMTSWITCH_BTOR + +#ifdef SMTSWITCH_Z3 + { // LiaCvtr + auto solver = smt::Z3SolverFactory::create(false); + auto lia = LiaCvtr(solver); + auto shim = SmtShim(lia); + + // Use SmtShim to gen/add formulas + solver->assert_formula(shim.GetShimExpr(a_ult_b.get())); + solver->assert_formula(shim.GetShimExpr(a_ugt_b.get())); + solver->assert_formula(shim.GetShimExpr(a_eq_b.get())); + + // Use z3's command for checking + auto raw = std::static_pointer_cast(solver); + auto slv = raw->get_z3_solver(); // get_z3_context() also available + auto res = slv->check(); + EXPECT_TRUE(res == z3::unsat); + } +#endif // SMTSWITCH_Z3 } } // namespace ilang diff --git a/test/t_smt_switch_itf.cc b/test/t_smt_switch_itf.cc deleted file mode 100644 index 7965d30d5..000000000 --- a/test/t_smt_switch_itf.cc +++ /dev/null @@ -1,367 +0,0 @@ -/// \file -/// Unit tests for smt-switch interface (with Boolector) - -#ifdef SMTSWITCH_TEST - -#include -#include - -#include -#include - -#include "unit-include/util.h" - -#define BV_SIZE 4 - -namespace ilang { - -class TestSmtSwitch : public ::testing::Test { -public: - TestSmtSwitch() - : var_bool_a(m.NewBoolState("var_bool_a")), - var_bool_b(m.NewBoolState("var_bool_b")), - var_bv_a(m.NewBvState("var_bv_a", BV_SIZE)), - var_bv_b(m.NewBvState("var_bv_b", BV_SIZE)), - var_mem_a(m.NewMemState("var_mem_a", BV_SIZE, BV_SIZE)), - var_mem_b(m.NewMemState("var_mem_b", BV_SIZE, BV_SIZE)) { - // use boolector for the unit tests - s = smt::BoolectorSolverFactory::create(false); - } - - ~TestSmtSwitch() {} - - void SetUp() {} - - void TearDown() { - // reset all created terms etc. - s->reset(); - } - - void CheckUnsat(const ExprRef& e) { - auto term = ResetAndGetSmtTerm(s, e); - s->assert_formula(term); - auto res = s->check_sat(); - EXPECT_TRUE(res.is_unsat()); - } - - smt::SmtSolver s; - - Ila m = Ila("host"); - ExprRef var_bool_a; - ExprRef var_bool_b; - ExprRef var_bv_a; - ExprRef var_bv_b; - ExprRef var_mem_a; - ExprRef var_mem_b; - -}; // class TestSmtSwitch - -TEST_F(TestSmtSwitch, OpBvNeg) { - auto neg_a = -var_bv_a; - auto neg_neg_a = -neg_a; - auto prop = neg_neg_a != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBoolNot) { - auto not_a = !var_bool_a; - auto not_not_a = !not_a; - auto prop = not_not_a != var_bool_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvComplement) { - auto com_a = ~var_bv_a; - auto com_com_a = ~com_a; - auto prop = com_com_a != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBoolAnd) { - auto a_and_not_a = var_bool_a & !var_bool_a; - CheckUnsat(a_and_not_a); -} - -TEST_F(TestSmtSwitch, OpBvAnd) { - auto a_and_all1 = var_bv_a & BvConst((1 << BV_SIZE) - 1, BV_SIZE); - auto prop = a_and_all1 != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBoolOr) { - auto a_or_not_a = var_bool_a | !var_bool_a; - auto prop = !a_or_not_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvOr) { - auto a_or_0 = var_bv_a | BvConst(0, BV_SIZE); - auto prop = a_or_0 != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBoolXor) { - auto a_xor_b = var_bool_a ^ var_bool_b; - auto a_eq_b = var_bool_a == var_bool_b; - auto prop = a_xor_b & a_eq_b; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvXor) { - auto a_xor_b = var_bv_a ^ var_bv_b; - auto a_eq_b = var_bv_a == var_bv_b; - auto prop = a_eq_b & (a_xor_b == BvConst((1 << BV_SIZE) - 1, BV_SIZE)); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvShl) { - auto a_shl_all = var_bv_a << BV_SIZE; - auto prop = a_shl_all != BvConst(0, BV_SIZE); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvAshr) { - auto a_ashr_all = var_bv_a >> BV_SIZE; - auto is_pos = a_ashr_all == BvConst(0, BV_SIZE); - auto is_neg = a_ashr_all == BvConst((1 << BV_SIZE) - 1, BV_SIZE); - auto prop = !(is_pos | is_neg); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvLshr) { - auto a_ashr_all = Lshr(var_bv_a, BvConst(BV_SIZE, BV_SIZE)); - auto prop = a_ashr_all != BvConst(0, BV_SIZE); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvAdd) { - auto a_plus_0 = var_bv_a + BvConst(0, BV_SIZE); - auto prop = a_plus_0 != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvSub) { - auto a_minus_0 = var_bv_a - BvConst(0, BV_SIZE); - auto prop = a_minus_0 != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvAddSub) { - auto a_plus_b = var_bv_a + var_bv_b; - auto a_plus_b_minus_b = a_plus_b - var_bv_b; - auto prop = a_plus_b_minus_b != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvMul) { - auto a_times_0 = var_bv_a * BvConst(0, BV_SIZE); - auto prop = a_times_0 != BvConst(0, BV_SIZE); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvDiv) { - auto a_div_1 = var_bv_a / BvConst(1, BV_SIZE); - auto prop = a_div_1 != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvSrem) { - auto div = BvConst(3, BV_SIZE); - auto a_srem_3 = SRem(var_bv_a, div); - auto prop = !(Slt(a_srem_3, div)); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvUrem) { - auto div = BvConst(3, BV_SIZE); - auto a_urem_3 = URem(var_bv_a, div); - auto prop = !(Ult(a_urem_3, div)); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvSmod) { - auto div = BvConst(3, BV_SIZE); - auto a_smod_3 = SMod(var_bv_a, div); - auto prop = !(Slt(a_smod_3, div)); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvSltSge) { - auto a_slt_b = var_bv_a < var_bv_b; - auto a_sge_b = var_bv_a >= var_bv_b; - auto prop = a_slt_b & a_sge_b; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvSleSgt) { - auto a_sle_b = var_bv_a <= var_bv_b; - auto a_sgt_b = var_bv_a > var_bv_b; - auto prop = a_sle_b & a_sgt_b; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvSltSgteq) { - auto a_slt_b = var_bv_a < var_bv_b; - auto a_sgt_b = var_bv_a > var_bv_b; - auto a_eq_b = var_bv_a == var_bv_b; - auto prop = !(a_slt_b | a_sgt_b | a_eq_b); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvUltUge) { - auto a_ult_b = Ult(var_bv_a, var_bv_b); - auto a_uge_b = Uge(var_bv_a, var_bv_b); - auto prop = a_ult_b & a_uge_b; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvUleUgt) { - auto a_ule_b = Ule(var_bv_a, var_bv_b); - auto a_ugt_b = Ugt(var_bv_a, var_bv_b); - auto prop = a_ule_b & a_ugt_b; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvUltUgteq) { - auto a_ult_b = Ult(var_bv_a, var_bv_b); - auto a_ugt_b = Ugt(var_bv_a, var_bv_b); - auto a_eq_b = var_bv_a == var_bv_b; - auto prop = !(a_ult_b | a_ugt_b | a_eq_b); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpMemLoad) { - auto data_a = Load(var_mem_a, var_bv_a); - auto data_b = Load(var_mem_a, var_bv_b); - auto prop = (var_bv_a == var_bv_b) & (data_a != data_b); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpMemStore) { - auto new_mem = Store(var_mem_a, var_bv_a, var_bv_b); - auto data = Load(new_mem, var_bv_a); - auto prop = data != var_bv_b; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvConcat) { - auto a_con_b = Concat(var_bv_a, var_bv_b); - auto b_con_a = Concat(var_bv_b, var_bv_a); - auto prop = (var_bv_a == var_bv_b) & (a_con_b != b_con_a); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvExtract) { - auto a_con_b = Concat(var_bv_a, var_bv_b); - auto extract_a_con_b = Extract(a_con_b, BV_SIZE - 1, 0); - auto prop = extract_a_con_b != var_bv_b; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvZext) { - auto zext_a = ZExt(var_bv_a, 2 * BV_SIZE); - auto zero_con_a = Concat(BvConst(0, BV_SIZE), var_bv_a); - auto prop = zext_a != zero_con_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvSext) { - auto pos_a = Lshr(var_bv_a, 1); - auto sext_a = SExt(pos_a, 2 * BV_SIZE); - auto zero_con_a = Concat(BvConst(0, BV_SIZE), pos_a); - auto prop = sext_a != zero_con_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvRotate) { - auto left_rotate_1 = LRotate(var_bv_a, 1); - auto right_rotate_back = RRotate(left_rotate_1, 1); - auto prop = right_rotate_back != var_bv_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBoolImply) { - auto a_imply_b = Imply(var_bool_a, var_bool_b); - auto prop = a_imply_b & var_bool_a & !var_bool_b; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBoolIte) { - auto ite_a_0_a = Ite(var_bool_a, BoolConst(false), var_bool_a); - auto prop = ite_a_0_a; - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpBvIte) { - auto ite_c_a_b = Ite(var_bool_a, var_bv_a, var_bv_b); - auto prop = (var_bv_a == var_bv_b) & (ite_c_a_b != var_bv_a); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, OpApplyFunc) { - auto func = FuncRef("func", SortRef::BOOL(), SortRef::BV(BV_SIZE)); - auto out1 = func(var_bv_a); - auto out2 = func(var_bv_b); - auto prop = (var_bv_a == var_bv_b) & (out1 != out2); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, ConstBool) { - auto a_is_0 = var_bool_a == BoolConst(false); - auto b_is_1 = var_bool_b == BoolConst(true); - auto prop = a_is_0 & b_is_1 & (var_bool_a == var_bool_b); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, ConstBv) { - auto a_is_0 = var_bv_a == BvConst(0, BV_SIZE); - auto b_is_1 = var_bv_b == BvConst(1, BV_SIZE); - auto prop = a_is_0 & b_is_1 & (var_bv_a == var_bv_b); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, ConstMem) { - std::map data_pair; - data_pair[0] = 0; - data_pair[1] = 1; - data_pair[2] = 2; - data_pair[3] = 3; - auto const_mem = MemConst(0, data_pair, BV_SIZE, BV_SIZE); - auto save_addr = (var_bv_a <= 3) & (var_bv_a >= 0); - auto is_linear = Load(const_mem, var_bv_a) == var_bv_a; - auto prop = !Imply(save_addr, is_linear); - CheckUnsat(prop); -} - -TEST_F(TestSmtSwitch, DiscreteUsage) { - auto a_ult_b = Ult(var_bv_a, var_bv_b); - auto a_ugt_b = Ugt(var_bv_a, var_bv_b); - auto a_eq_b = var_bv_a == var_bv_b; - - auto itf = SmtSwitchItf(s); - s->assert_formula(itf.GetSmtTerm(a_ult_b.get())); - s->assert_formula(itf.GetSmtTerm(a_ugt_b.get())); - s->assert_formula(itf.GetSmtTerm(a_eq_b.get())); - - auto res = s->check_sat(); - EXPECT_TRUE(res.is_unsat()); -} - -TEST_F(TestSmtSwitch, DISABLED_MultiIssue) { - auto itf = SmtSwitchItf(s); - - auto a_and_not_a = var_bool_a & !var_bool_a; - s->assert_formula(itf.GetSmtTerm(a_and_not_a.get())); - auto res = s->check_sat(); - EXPECT_TRUE(res.is_unsat()); - - itf.Reset(); // XXX solver not properly reseted - - auto a_or_b = var_bool_a | var_bool_b; - s->assert_formula(itf.GetSmtTerm(a_or_b.get())); - EXPECT_TRUE(res.is_sat()); -} - -}; // namespace ilang - -#endif // SMTSWITCH_TEST diff --git a/test/t_unroller_smt.cc b/test/t_unroller_smt.cc index 86c986dd4..f5debc454 100644 --- a/test/t_unroller_smt.cc +++ b/test/t_unroller_smt.cc @@ -2,10 +2,13 @@ /// Unit tests for Unroller using SmtShim #ifdef SMTSWITCH_TEST -#include #include #endif // SMTSWITCH_TEST +#ifdef SMTSWITCH_BTOR +#include +#endif // SMTSWITCH_BTOR + #include #include #include @@ -96,7 +99,7 @@ TEST_F(TestUnrollerSmt, z3) { EXPECT_EQ(res, z3::unsat); } -#ifdef SMTSWITCH_TEST +#ifdef SMTSWITCH_BTOR TEST_F(TestUnrollerSmt, btor) { auto solver = smt::BoolectorSolverFactory::create(false); auto switch_itf = SmtSwitchItf(solver); @@ -108,7 +111,6 @@ TEST_F(TestUnrollerSmt, btor) { auto res = solver->check_sat(); EXPECT_TRUE(res.is_unsat()); } - -#endif // SMTSWITCH_TEST +#endif // SMTSWITCH_BTOR } // namespace ilang From aa6baebd5a4b75bcb1d1daf749a98660661f8fe1 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 02:29:20 -0400 Subject: [PATCH 18/32] Script for setting up smt-switch & update GitHub action --- .github/workflows/cmake.yml | 12 ++++++-- CMakeLists.txt | 2 +- README.md | 28 +++++++++--------- scripts/setup-smtswitch.sh | 57 +++++++++++++++++++++++++++++++++++++ test/t_unroller_smt.cc | 34 ++++++++++++++++------ 5 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 scripts/setup-smtswitch.sh diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 9e7372839..d59aff0cf 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -13,7 +13,15 @@ jobs: - uses: actions/checkout@v2 - name: Install Dependent Packages - run: sudo apt install bison flex z3 libz3-dev + run: sudo apt install bison flex + + - name: Create Dependency Environment + run: cmake -E make_directory ${{runner.workspace}}/deps + + - name: Build and Install Dependency + shell: bash + working-directory: $GITHUB_WORKSPACE + run: source scripts/setup-smtswitch.sh $GITHUB_WORKSPACE ${{runner.workspace}}/deps - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build @@ -21,7 +29,7 @@ jobs: - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=${{runner.workspace}}/deps - name: Build working-directory: ${{runner.workspace}}/build diff --git a/CMakeLists.txt b/CMakeLists.txt index 1498562aa..55e0123d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ option(ILANG_EXPORT_PACKAGE "Export CMake package if enabled." OFF) option(ILANG_COVERITY "Build for Coverity static analysis." OFF) option(BUILD_SHARED_LIBS "Build shared libraries." ON) option(ILANG_BUILD_INVSYN "Build invariant synthesis feature." ON) -option(ILANG_BUILD_SWITCH "Build smt-switch interface." OFF) +option(ILANG_BUILD_SWITCH "Build smt-switch interface." ON) option(ILANG_BUILD_COSIM "Build QEMU-based co-simulation support." OFF) include(CMakeDependentOption) diff --git a/README.md b/README.md index dc5bd3874..757f8b96b 100644 --- a/README.md +++ b/README.md @@ -33,40 +33,43 @@ ### Prerequisites ILAng requires CMake (3.9.6 or above) and compilers with C++17 support. -To install dependencies on Debian-based Linux: +To install dependent packages: ```bash -apt-get install bison flex z3 libz3-dev -``` - -To install dependencies (besides [z3](https://github.com/Z3Prover/z3)) on Fedora-based Linux: +# Debian-based +apt-get install bison flex -```bash +# Fedora-based yum install bison flex + +# OSX +brew install bison flex ``` -To install dependencies on OSX: +To build smt-switch and SMT solvers: -```bash -brew install bison flex z3 +``` bash +mkdir -p +source scripts/setup-smtswitch.sh ``` ### Default Build -To build ILAng with default configuration, create a build directory and make: +To build ILAng with default configuration: ```bash mkdir -p build && cd build -cmake .. +cmake .. -DCMAKE_PREFIX_PATH= make ``` If you are using git older than `1.8.4`, init the submodule from root and disable config-time submodule fetching: + ```bash git submodule update --init --recursive mkdir -p build && cd build -cmake .. -DILANG_FETCH_DEPS=OFF +cmake .. -DILANG_FETCH_DEPS=OFF -DCMAKE_PREFIX_PATH= make ``` @@ -83,7 +86,6 @@ sudo make install - Use `-DILANG_BUILD_TEST=OFF` to disalbe building the unit tests. - Use `-DILANG_BUILD_SYNTH=ON` to enable building the synthesis engine (required [Boost](https://www.boost.org)). - Use `-DILANG_BUILD_INVSYN=OFF` to disable building invariant synthesis feature. -- Use `-DILANG_BUILD_SWITCH=ON` to enable building [smt-switch](https://github.com/stanford-centaur/smt-switch) interface support. - Use `-DILANG_BUILD_COSIM=ON` to enable building [Xilinx cosimulation](https://www.linuxsecrets.com/xilinx/QEMU%20SystemC%20and%20TLM%20CoSimulation.html) support. - Use `-DILANG_INSTALL_DEV=ON` to install working features. diff --git a/scripts/setup-smtswitch.sh b/scripts/setup-smtswitch.sh new file mode 100644 index 000000000..130ee1d84 --- /dev/null +++ b/scripts/setup-smtswitch.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +# ============================================================================== +# MIT License +# +# Copyright (c) 2019 Princeton University +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ============================================================================== + +SRC_DIR=$1 +DEP_DIR=$2 +Z3_VERSION=tags/z3-4.8.12 + +# fetch smt-switch +cd $SRC_DIR +git submodule update --init extern/smt-switch + +# setup dir for SMT solvers +mkdir -p $SRC_DIR/extern/smt-switch/deps + +# fetch/build/install z3 +cd $SRC_DIR/extern/smt-switch/deps +git clone https://github.com/Z3Prover/z3.git +cd z3 +git checkout $Z3_VERSION +python scripts/mk_make.py --staticlib --single-threaded --prefix=$DEP_DIR +cd build +make -j$(nproc) +make install + +#fetch/build boolector +cd $SRC_DIR/extern/smt-switch +source contrib/setup-btor.sh + +#build/install smt-switch +mkdir -p $SRC_DIR/extern/smt-switch/build +cd $SRC_DIR/extern/smt-switch/build +cmake .. -DBUILD_Z3=ON -DBUILD_BTOR=ON -DCMAKE_INSTALL_PREFIX=$DEP_DIR +make -j$(nproc) +make install diff --git a/test/t_unroller_smt.cc b/test/t_unroller_smt.cc index f5debc454..a9ed61cc3 100644 --- a/test/t_unroller_smt.cc +++ b/test/t_unroller_smt.cc @@ -9,6 +9,10 @@ #include #endif // SMTSWITCH_BTOR +#ifdef SMTSWITCH_Z3 +#include +#endif // SMTSWITCH_Z3 + #include #include #include @@ -99,18 +103,30 @@ TEST_F(TestUnrollerSmt, z3) { EXPECT_EQ(res, z3::unsat); } -#ifdef SMTSWITCH_BTOR +#ifdef SMTSWITCH_TEST TEST_F(TestUnrollerSmt, btor) { - auto solver = smt::BoolectorSolverFactory::create(false); - auto switch_itf = SmtSwitchItf(solver); - auto shim = SmtShim(switch_itf); - auto p = UnrollTestSequence(shim); + auto UnrollAndCheck = [this](smt::SmtSolver& solver) { + auto switch_itf = SmtSwitchItf(solver); + auto shim = SmtShim(switch_itf); - solver->assert_formula(p); - auto res = solver->check_sat(); - EXPECT_TRUE(res.is_unsat()); -} + auto p = UnrollTestSequence(shim); + + solver->assert_formula(p); + auto res = solver->check_sat(); + EXPECT_TRUE(res.is_unsat()); + }; + +#ifdef SMTSWITCH_BTOR + auto s_btor = smt::BoolectorSolverFactory::create(false); + UnrollAndCheck(s_btor); #endif // SMTSWITCH_BTOR +#ifdef SMTSWITCH_Z3 + auto s_z3 = smt::Z3SolverFactory::create(false); + UnrollAndCheck(s_z3); +#endif // SMTSWITCH_Z3 +} +#endif // SMTSWITCH_TEST + } // namespace ilang From 2c7a19c4baab0a944ddfb7e30b207ab159fa420d Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 02:32:02 -0400 Subject: [PATCH 19/32] Fix typo --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d59aff0cf..2580b2e94 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -21,7 +21,7 @@ jobs: - name: Build and Install Dependency shell: bash working-directory: $GITHUB_WORKSPACE - run: source scripts/setup-smtswitch.sh $GITHUB_WORKSPACE ${{runner.workspace}}/deps + run: source $GITHUB_WORKSPACE/scripts/setup-smtswitch.sh $GITHUB_WORKSPACE ${{runner.workspace}}/deps - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build From d772a8f4a14fea612f02944a7a3aab180e33b89a Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 02:38:44 -0400 Subject: [PATCH 20/32] Use GA env for script path --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 2580b2e94..5f915ecae 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -21,7 +21,7 @@ jobs: - name: Build and Install Dependency shell: bash working-directory: $GITHUB_WORKSPACE - run: source $GITHUB_WORKSPACE/scripts/setup-smtswitch.sh $GITHUB_WORKSPACE ${{runner.workspace}}/deps + run: ./scripts/setup-smtswitch.sh $GITHUB_WORKSPACE ${{runner.workspace}}/deps - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build From c409a389a0a6c2b5fd98ce1f95921784644495e6 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 02:46:07 -0400 Subject: [PATCH 21/32] Try checking out in steps --- .github/workflows/cmake.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 5f915ecae..d58c04cf9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -19,6 +19,7 @@ jobs: run: cmake -E make_directory ${{runner.workspace}}/deps - name: Build and Install Dependency + uses: actions/checkout@v2 shell: bash working-directory: $GITHUB_WORKSPACE run: ./scripts/setup-smtswitch.sh $GITHUB_WORKSPACE ${{runner.workspace}}/deps From d6eafc36c26df6cc5835a5620d0d794d0cbbec76 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 02:58:00 -0400 Subject: [PATCH 22/32] Update cmake.yml --- .github/workflows/cmake.yml | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d58c04cf9..1d5666bbd 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -15,29 +15,26 @@ jobs: - name: Install Dependent Packages run: sudo apt install bison flex - - name: Create Dependency Environment - run: cmake -E make_directory ${{runner.workspace}}/deps + - name: Create Build Environment + run: | + cmake -E make_directory ${{github.workspace}}/deps + cmake -E make_directory ${{github.workspace}}/build - name: Build and Install Dependency - uses: actions/checkout@v2 shell: bash - working-directory: $GITHUB_WORKSPACE - run: ./scripts/setup-smtswitch.sh $GITHUB_WORKSPACE ${{runner.workspace}}/deps - - - name: Create Build Environment - run: cmake -E make_directory ${{runner.workspace}}/build + run: source ${{github.workspace}}/scripts/setup-smtswitch.sh ${{github.workspace}} ${{github.workspace}}/deps - name: Configure CMake shell: bash - working-directory: ${{runner.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=${{runner.workspace}}/deps + working-directory: ${{github.workspace}}/build + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=${{github.workspace}}/deps - name: Build - working-directory: ${{runner.workspace}}/build shell: bash + working-directory: ${{github.workspace}}/build run: cmake --build . --config $BUILD_TYPE - name: Test - working-directory: ${{runner.workspace}}/build + working-directory: ${{github.workspace}}/build shell: bash run: make run_test From cfe43ffb2e89dd5dc47b75d940ec8a60b43786d8 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 17:30:47 -0400 Subject: [PATCH 23/32] Setup self-hosted runner & update smt-switch build script --- .github/workflows/cmake.yml | 11 ++++------- scripts/setup-smtswitch.sh | 5 +++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 1d5666bbd..164612233 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -7,14 +7,11 @@ env: jobs: build: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v2 - - name: Install Dependent Packages - run: sudo apt install bison flex - - name: Create Build Environment run: | cmake -E make_directory ${{github.workspace}}/deps @@ -27,14 +24,14 @@ jobs: - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=${{github.workspace}}/deps + run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=${{github.workspace}}/deps - name: Build shell: bash working-directory: ${{github.workspace}}/build - run: cmake --build . --config $BUILD_TYPE + run: make -j$(nproc) - name: Test - working-directory: ${{github.workspace}}/build shell: bash + working-directory: ${{github.workspace}}/build run: make run_test diff --git a/scripts/setup-smtswitch.sh b/scripts/setup-smtswitch.sh index 130ee1d84..bdab11330 100644 --- a/scripts/setup-smtswitch.sh +++ b/scripts/setup-smtswitch.sh @@ -51,7 +51,8 @@ source contrib/setup-btor.sh #build/install smt-switch mkdir -p $SRC_DIR/extern/smt-switch/build -cd $SRC_DIR/extern/smt-switch/build -cmake .. -DBUILD_Z3=ON -DBUILD_BTOR=ON -DCMAKE_INSTALL_PREFIX=$DEP_DIR +cd $SRC_DIR/extern/smt-switch +./configure.sh --static --btor --z3 --prefix=$DEP_DIR +cd build make -j$(nproc) make install From bc585fe80d9194042ab678b9e3963f19b981cf4c Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 17:42:52 -0400 Subject: [PATCH 24/32] Specify build toolset --- .github/workflows/cmake.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 164612233..00107c69f 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -12,6 +12,11 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Setup Toolset + run: | + scl enable sclo-git212 /bin/bash + scl enable devtoolset-8 /bin/bash + - name: Create Build Environment run: | cmake -E make_directory ${{github.workspace}}/deps From 983e5cd76edb48d6945c4c2f60a69829abf06e33 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Fri, 29 Oct 2021 18:40:08 -0400 Subject: [PATCH 25/32] Use CentOS8 for self-hosted runner --- .github/workflows/cmake.yml | 5 ----- scripts/setup-smtswitch.sh | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 00107c69f..164612233 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -12,11 +12,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Toolset - run: | - scl enable sclo-git212 /bin/bash - scl enable devtoolset-8 /bin/bash - - name: Create Build Environment run: | cmake -E make_directory ${{github.workspace}}/deps diff --git a/scripts/setup-smtswitch.sh b/scripts/setup-smtswitch.sh index bdab11330..08f50fcf9 100644 --- a/scripts/setup-smtswitch.sh +++ b/scripts/setup-smtswitch.sh @@ -40,7 +40,7 @@ cd $SRC_DIR/extern/smt-switch/deps git clone https://github.com/Z3Prover/z3.git cd z3 git checkout $Z3_VERSION -python scripts/mk_make.py --staticlib --single-threaded --prefix=$DEP_DIR +python3 scripts/mk_make.py --staticlib --single-threaded --prefix=$DEP_DIR cd build make -j$(nproc) make install From 816e8a83139a2202358db1f88d10b47fb50d95a5 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Sat, 30 Oct 2021 00:50:44 -0400 Subject: [PATCH 26/32] Update Boolector build scripts and add action description --- .github/workflows/cmake.yml | 2 ++ scripts/setup-smtswitch.sh | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 164612233..cb42818d3 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -8,6 +8,8 @@ env: jobs: build: runs-on: self-hosted + # OS: CentOs 8 + # Packages: bison flex gmp-devel steps: - uses: actions/checkout@v2 diff --git a/scripts/setup-smtswitch.sh b/scripts/setup-smtswitch.sh index 08f50fcf9..cae7330fe 100644 --- a/scripts/setup-smtswitch.sh +++ b/scripts/setup-smtswitch.sh @@ -27,6 +27,7 @@ SRC_DIR=$1 DEP_DIR=$2 Z3_VERSION=tags/z3-4.8.12 +BTOR_VERSION=tags/3.2.2 # fetch smt-switch cd $SRC_DIR @@ -46,8 +47,16 @@ make -j$(nproc) make install #fetch/build boolector -cd $SRC_DIR/extern/smt-switch -source contrib/setup-btor.sh +cd $SRC_DIR/extern/smt-switch/deps +git clone https://github.com/Boolector/boolector.git +cd boolector +git checkout $BTOR_VERSION +./contrib/setup-btor2tools.sh +./contrib/setup-cadical.sh +mkdir -p build +cd build +cmake .. -DONLY_CADICAL=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON +make -j$(nproc) #build/install smt-switch mkdir -p $SRC_DIR/extern/smt-switch/build From a4038c3ce898725de00b00b669a1ab8982c7db7f Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Sat, 30 Oct 2021 01:38:50 -0400 Subject: [PATCH 27/32] Setup nightly build --- .github/workflows/cmake.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index cb42818d3..7cbffceb9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -1,6 +1,14 @@ name: CMake -on: [push] +on: + push: + + pull_request: + branches: + - main + + schedule: + - cron: '0 9 * * *' env: BUILD_TYPE: Release From 8dfbb6fdbc659165942b72d084cdec06b815c416 Mon Sep 17 00:00:00 2001 From: Bo-Yuan Huang Date: Sat, 30 Oct 2021 02:04:39 -0400 Subject: [PATCH 28/32] Add cleanup step in Actions --- .github/workflows/cmake.yml | 16 +++++++++++++--- scripts/setup-smtswitch.sh | 17 +++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 7cbffceb9..be7d0f804 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -24,17 +24,18 @@ jobs: - name: Create Build Environment run: | - cmake -E make_directory ${{github.workspace}}/deps + cmake -E make_directory ${{github.workspace}}/deps-src + cmake -E make_directory ${{github.workspace}}/deps-bin cmake -E make_directory ${{github.workspace}}/build - name: Build and Install Dependency shell: bash - run: source ${{github.workspace}}/scripts/setup-smtswitch.sh ${{github.workspace}} ${{github.workspace}}/deps + run: source ${{github.workspace}}/scripts/setup-smtswitch.sh ${{github.workspace}}/deps-src ${{github.workspace}}/deps-bin - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build - run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=${{github.workspace}}/deps + run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=${{github.workspace}}/deps-bin - name: Build shell: bash @@ -45,3 +46,12 @@ jobs: shell: bash working-directory: ${{github.workspace}}/build run: make run_test + + clean: + needs: build + runs-on: self-hosted + + steps: + - name: Remove Workspace + shell: bash + run: rm -rf ${{github.workspace}} diff --git a/scripts/setup-smtswitch.sh b/scripts/setup-smtswitch.sh index cae7330fe..d14dcd361 100644 --- a/scripts/setup-smtswitch.sh +++ b/scripts/setup-smtswitch.sh @@ -26,18 +26,19 @@ SRC_DIR=$1 DEP_DIR=$2 +SMTSWITCH_VERSION=768ed5d05315d0b652a915adcfbbc7f3e7a4896a Z3_VERSION=tags/z3-4.8.12 BTOR_VERSION=tags/3.2.2 # fetch smt-switch cd $SRC_DIR -git submodule update --init extern/smt-switch - -# setup dir for SMT solvers -mkdir -p $SRC_DIR/extern/smt-switch/deps +git clone https://github.com/stanford-centaur/smt-switch.git +cd smt-switch +git checkout $SMTSWITCH_VERSION +mkdir -p deps # fetch/build/install z3 -cd $SRC_DIR/extern/smt-switch/deps +cd $SRC_DIR/smt-switch/deps git clone https://github.com/Z3Prover/z3.git cd z3 git checkout $Z3_VERSION @@ -47,7 +48,7 @@ make -j$(nproc) make install #fetch/build boolector -cd $SRC_DIR/extern/smt-switch/deps +cd $SRC_DIR/smt-switch/deps git clone https://github.com/Boolector/boolector.git cd boolector git checkout $BTOR_VERSION @@ -59,8 +60,8 @@ cmake .. -DONLY_CADICAL=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON make -j$(nproc) #build/install smt-switch -mkdir -p $SRC_DIR/extern/smt-switch/build -cd $SRC_DIR/extern/smt-switch +cd $SRC_DIR/smt-switch +mkdir -p build ./configure.sh --static --btor --z3 --prefix=$DEP_DIR cd build make -j$(nproc) From caeb8e367e63af2a0be3679bbc386a1ff471f9a6 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Fri, 17 Dec 2021 20:06:46 +0000 Subject: [PATCH 29/32] Added extra test and shrunk others for speed. --- test/t_pfrag.cc | 96 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/test/t_pfrag.cc b/test/t_pfrag.cc index 111ece387..c43a5b08a 100644 --- a/test/t_pfrag.cc +++ b/test/t_pfrag.cc @@ -24,20 +24,20 @@ namespace pf2graph { using namespace pfast; - InstrLvlAbsPtr simple_counter_ila() { - Ila m {"Counter"}; + InstrLvlAbsPtr simple_counter_ila(std::string suffix="") { + Ila m {"Counter" + suffix}; - auto ctr = m.NewBvState(COUNTER_STATE_CTR, 8); - auto op = m.NewBvInput(COUNTER_INPUT_OP, 2); + auto ctr = m.NewBvState(COUNTER_STATE_CTR + suffix, 8); + auto op = m.NewBvInput(COUNTER_INPUT_OP + suffix, 2); { // increment - auto instr = m.NewInstr(COUNTER_INSTR_INC); + auto instr = m.NewInstr(COUNTER_INSTR_INC + suffix); instr.SetDecode(op == COUNTER_OP_INC); instr.SetUpdate(ctr, ctr + 1); } { // decrement - auto instr = m.NewInstr(COUNTER_INSTR_DEC); + auto instr = m.NewInstr(COUNTER_INSTR_DEC + suffix); instr.SetDecode(op == COUNTER_OP_DEC); instr.SetUpdate(ctr, Ite(ctr == 0, BvConst(0, 8), ctr - 1)); } @@ -395,12 +395,84 @@ namespace pf2graph { EXPECT_EQ(res, z3::unsat); } - TEST(Pf2chc, Hierarchy) { + TEST(Pf2chc, CounterLoop2) { + + InstrLvlAbsPtr m1 = simple_counter_ila("1"); + InstrLvlAbsPtr m2 = simple_counter_ila("2"); + auto inst_inc1 = m1->instr(COUNTER_INSTR_INC "1"); // implicit concat of str literals + auto inst_inc2 = m2->instr(COUNTER_INSTR_INC "2"); // implicit concat of str literals + + auto ctr1 = m1->state(COUNTER_STATE_CTR "1"); // implicit concat of str literals + auto ctr2 = m2->state(COUNTER_STATE_CTR "2"); // implicit concat of str literals + + auto ctr_w = ctr1->sort()->bit_width(); + auto n = m1->NewBvFreeVar("n", ctr_w); + auto i = m1->NewBvFreeVar("i", ctr_w); + + auto inv1 = asthub::And(asthub::Eq(ctr1, i), asthub::Le(i, n)); + auto inv2 = asthub::And(asthub::Eq(ctr2, i), asthub::Le(i, n)); + + // program fragment + ProgramFragment pf {{ n, i }, { + Assume {asthub::Eq(ctr1, 0)}, + Assume {asthub::Eq(ctr2, 0)}, + Assume {asthub::Gt(n, 0)}, + Assume {asthub::Lt(n, 15)}, // keeps runtime short + + Update { {i, asthub::BvConst(0, ctr_w)} }, + Assert { inv1 }, + While { asthub::Lt(i, n), { + Assume { inv1 }, + Call { inst_inc1, asthub::BoolConst(true) }, + Update { {i, asthub::Add(i, asthub::BvConst(1, ctr_w))} }, + Assert { inv1 }, + }}, + Assume { inv1 }, + + Update { {i, asthub::BvConst(0, ctr_w)} }, + Assert { inv2 }, + While { asthub::Lt(i, n), { + Assume { inv2 }, + Call { inst_inc2, asthub::BoolConst(true) }, + Update { {i, asthub::Add(i, asthub::BvConst(1, ctr_w))} }, + Assert { inv2 }, + }}, + Assume { inv2 }, + Assert { asthub::And( + asthub::Eq(ctr1, n), asthub::Eq(ctr1, ctr2)) } + }}; + + z3::context ctx; + z3::fixedpoint ctxfp {ctx}; + // use spacer instead of datalog + z3::params p {ctx}; + p.set("engine", "spacer"); + ctxfp.set(p); + + PFToCHCEncoder encoder {ctx, ctxfp, m1, pf}; + + // std::cout << "\nEncoded successfully!\n" << std::endl; + + // std::cout << encoder.to_string() << std::endl; + + EXPECT_NO_THROW(encoder.to_string()); + + z3::check_result res = z3::sat; + try { + res = encoder.check_assertions(); + } catch (const z3::exception& e) { + std::cout << "Error: " << e << std::endl; + } + + EXPECT_EQ(res, z3::unsat); + } + + TEST(Pf2chc, Hierarchy) { Ila m {"Broadcast"}; - constexpr int msg_bits = 64; - constexpr int port_bits = 8; + constexpr int msg_bits = 8; + constexpr int port_bits = 4; auto next_msg = m.NewBvInput("next_msg", msg_bits); auto msg = m.NewBvState("msg", msg_bits); @@ -491,9 +563,9 @@ namespace pf2graph { z3::fixedpoint ctxfp {ctx}; // use spacer instead of datalog - z3::params p {ctx}; - p.set("engine", "spacer"); - ctxfp.set(p); + // z3::params p {ctx}; + // p.set("engine", "spacer"); + // ctxfp.set(p); PFToCHCEncoder encoder {ctx, ctxfp, m.get(), pf}; From 0fdd890b2ab0936aa26de78182f998129966c0a1 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Tue, 25 Jan 2022 16:28:12 +0000 Subject: [PATCH 30/32] Small modifications to help syncing. --- include/ilang/ila-mngr/v_eq_check_progfrag.h | 2 + include/ilang/ilang++.h | 24 +++++++-- src/ilang++.cc | 57 ++++++++++++++------ 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/include/ilang/ila-mngr/v_eq_check_progfrag.h b/include/ilang/ila-mngr/v_eq_check_progfrag.h index 5e2494aed..d298788dc 100644 --- a/include/ilang/ila-mngr/v_eq_check_progfrag.h +++ b/include/ilang/ila-mngr/v_eq_check_progfrag.h @@ -70,6 +70,8 @@ namespace ilang { return ctxfp_.query(q); } + ExprSet scope() { return total_scope_; } + private: const InstrLvlAbsPtr ila_; const pgraph::CutpointGraph pg_; diff --git a/include/ilang/ilang++.h b/include/ilang/ilang++.h index 1c3f1e95a..b3be28742 100644 --- a/include/ilang/ilang++.h +++ b/include/ilang/ilang++.h @@ -766,7 +766,7 @@ namespace programfragment { /// \brief Wrapper for a bitvector that allows it to be automatically /// zero/sign extended as part of an Update statement. - struct ExtendableBv { + struct ExtendableBv { ExprRef expr; enum ExtMode {EXT_ZERO, EXT_SIGNED} ext_mode; @@ -888,6 +888,8 @@ namespace programfragment { /// Destroys a program fragment ~ProgramFragment()=default; + /// TODO: these aren't parameters, they are helper variables + /// Creates a new boolean variable and registers it as a parameter ExprRef NewBoolVar(const std::string& name); @@ -898,8 +900,20 @@ namespace programfragment { ExprRef NewMemVar(const std::string& name, const int& addrwidth, const int& datawidth); - // void RegisterApplicationParam(const ExprRef& p); - // void RegisterHardwareParam(const ExprRef& p); + /// Returns the number of parameters registered + size_t num_params(); + + /// Returns the nth registered parameter + ExprRef param(size_t n); + + /// Returns registered parameter with the given name + ExprRef param(const std::string& name); + + /// Returns the body of the program fragment + Block body(); + + /// Registers the given parameter in this program fragment + void RegisterParam(const ExprRef& p); /// Adds the given statement to the program fragment void AddStatement(const Stmt& s); @@ -912,6 +926,7 @@ namespace programfragment { private: PfragPtr pf_; + Block body_; }; /// Checks structural equality @@ -947,6 +962,9 @@ class IlaToChcEncoder { /// Destructor ~IlaToChcEncoder()=default; + /// Returns the set of variables read or written by a program fragment + std::vector scope() const; + /// Gives the system of CHCs as a string std::string to_string(); diff --git a/src/ilang++.cc b/src/ilang++.cc index 53fee212d..6297d026f 100644 --- a/src/ilang++.cc +++ b/src/ilang++.cc @@ -952,38 +952,55 @@ namespace programfragment { : pf_ {new pfast::ProgramFragment{{}, convert_block(b)}} {} ExprRef ProgramFragment::NewBoolVar(const std::string& name) { - auto v = asthub::NewBoolVar(name); - pf_->params.insert(v); - return ExprRef{v}; + ExprRef v {asthub::NewBoolVar(name)}; + RegisterParam(v); + return v; } ExprRef ProgramFragment::NewBvVar(const std::string& name, const int& bitwidth) { - auto v = asthub::NewBvVar(name, bitwidth); - pf_->params.insert(v); - return ExprRef{v}; + ExprRef v {asthub::NewBvVar(name, bitwidth)}; + RegisterParam(v); + return v; } ExprRef ProgramFragment::NewMemVar( - const std::string& name, const int& addrwidth, const int& datawidth) { - auto v = asthub::NewMemVar(name, addrwidth, datawidth); - pf_->params.insert(v); - return ExprRef{v}; + const std::string& name, const int& addrwidth, const int& datawidth) { + ExprRef v {asthub::NewMemVar(name, addrwidth, datawidth)}; + RegisterParam(v); + return v; } - // void ProgramFragment::RegisterApplicationParam(const ExprRef& p) { - // // TODO: what does this entail? - // } + size_t ProgramFragment::num_params() { return pf_->params.size(); } - // void ProgramFragment::RegisterHardwareParam(const ExprRef& p) { - // // TODO: what does this entail? - // } + ExprRef ProgramFragment::param(size_t n) { + return {*std::next(pf_->params.begin(), n)}; + } + + ExprRef ProgramFragment::param(const std::string& name) { + for (const ExprPtr& p : pf_->params) { + if (p->name() == name) return {p}; + } + return {nullptr}; + } + + Block ProgramFragment::body() { + return {body_}; + } + + void ProgramFragment::RegisterParam(const ExprRef& p) { + pf_->params.insert(p.get()); + } void ProgramFragment::AddStatement(const Stmt& s) { + body_.push_back(s); pf_->body.push_back(convert_stmt(s)); } void ProgramFragment::AddStatements(const Block& b) { - for (auto& s : b) { pf_->body.push_back(convert_stmt(s)); } + for (auto& s : b) { + body_.push_back(s); + pf_->body.push_back(convert_stmt(s)); + } } ProgramFragment::PfragConstPtr ProgramFragment::get() const { @@ -1016,6 +1033,12 @@ IlaToChcEncoder::IlaToChcEncoder( ctx, ctxfp, ila.get(), *(pf.get()) )} {} +std::vector IlaToChcEncoder::scope() const { + std::vector vars; + for (const auto& v : impl_->scope()) vars.emplace_back(v); + return vars; +} + std::string IlaToChcEncoder::to_string() { return impl_->to_string(); } ChcResult IlaToChcEncoder::check_assertions() { From 6469dd12fb5ace2c5ae299d6789f5006ed1382c5 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Mon, 14 Feb 2022 04:36:52 +0000 Subject: [PATCH 31/32] Added pretty printing for program fragments --- include/ilang/ila/ast/expr.h | 3 +++ include/ilang/ila/ast/expr_const.h | 3 +++ include/ilang/ila/ast/expr_op.h | 3 +++ include/ilang/ila/ast/expr_var.h | 3 +++ src/ila-mngr/u_progfrag.cc | 17 +++++++++++------ src/ila/ast/expr_const.cc | 4 ++++ src/ila/ast/expr_op.cc | 9 +++++++++ src/ila/ast/expr_var.cc | 4 ++++ 8 files changed, 40 insertions(+), 6 deletions(-) diff --git a/include/ilang/ila/ast/expr.h b/include/ilang/ila/ast/expr.h index 1bf4be153..e4fa7b304 100644 --- a/include/ilang/ila/ast/expr.h +++ b/include/ilang/ila/ast/expr.h @@ -90,6 +90,9 @@ class Expr : public Ast, public std::enable_shared_from_this { /// Output to stream. virtual std::ostream& Print(std::ostream& out) const = 0; + /// Pretty output function + virtual std::ostream& PrintPretty(std::ostream& out) const = 0; + /// Overload output stream operator for pointer. friend std::ostream& operator<<(std::ostream& out, const ExprPtr& expr) { return expr->Print(out); diff --git a/include/ilang/ila/ast/expr_const.h b/include/ilang/ila/ast/expr_const.h index 82826c233..9ec5bc4cf 100644 --- a/include/ilang/ila/ast/expr_const.h +++ b/include/ilang/ila/ast/expr_const.h @@ -37,6 +37,9 @@ class ExprConst : public Expr { /// Output to stream. std::ostream& Print(std::ostream& out) const; + /// Pretty output function + std::ostream& PrintPretty(std::ostream& out) const; + /// Return the Boolean value. BoolValPtr val_bool() const; /// Return the Bitvector value. diff --git a/include/ilang/ila/ast/expr_op.h b/include/ilang/ila/ast/expr_op.h index 7de15549a..972a76344 100644 --- a/include/ilang/ila/ast/expr_op.h +++ b/include/ilang/ila/ast/expr_op.h @@ -89,6 +89,9 @@ class ExprOp : public Expr { /// Output to stream. std::ostream& Print(std::ostream& out) const; + /// Pretty output function + std::ostream& PrintPretty(std::ostream& out) const; + protected: // ------------------------- HELPERS -------------------------------------- // /// Derived the sort for binary operations. diff --git a/include/ilang/ila/ast/expr_var.h b/include/ilang/ila/ast/expr_var.h index 38e4331cc..288abbe57 100644 --- a/include/ilang/ila/ast/expr_var.h +++ b/include/ilang/ila/ast/expr_var.h @@ -40,6 +40,9 @@ class ExprVar : public Expr { /// Output to stream. std::ostream& Print(std::ostream& out) const; + /// Pretty output function + std::ostream& PrintPretty(std::ostream& out) const; + private: // ------------------------- MEMBERS -------------------------------------- // diff --git a/src/ila-mngr/u_progfrag.cc b/src/ila-mngr/u_progfrag.cc index ff651c375..6abf7f40a 100644 --- a/src/ila-mngr/u_progfrag.cc +++ b/src/ila-mngr/u_progfrag.cc @@ -67,17 +67,20 @@ namespace pfast { } void PrettyPrinter::operator()(const Assert& a) { - buf_ << reserved_word("assert") << " " << a.assertion; + buf_ << reserved_word("assert") << " "; + a.assertion->PrintPretty(buf_); } void PrettyPrinter::operator()(const Assume& a) { - buf_ << reserved_word("assume") << " " << a.assumption; + buf_ << reserved_word("assume") << " "; + a.assumption->PrintPretty(buf_); } void PrettyPrinter::operator()(const Call& c) { buf_ << reserved_word("call") << " " << c.instr->name().str(); if (c.input_constraint) { - buf_ << " where " << c.input_constraint; + buf_ << " where "; + c.input_constraint->PrintPretty(buf_); } } @@ -86,14 +89,16 @@ namespace pfast { bool first = true; for (const auto& [x, v] : u) { buf_ << (first ? " " : ", ") - << x << " = " << v; + << x << " = "; + v->PrintPretty(buf_); first = false; } } void PrettyPrinter::operator()(const While& w) { - buf_ << reserved_word("while") << " " - << w.loop_condition << " "; + buf_ << reserved_word("while") << " "; + w.loop_condition->PrintPretty(buf_); + buf_ << " "; (*this)(w.body); } diff --git a/src/ila/ast/expr_const.cc b/src/ila/ast/expr_const.cc index 5885276e8..9a23ae397 100644 --- a/src/ila/ast/expr_const.cc +++ b/src/ila/ast/expr_const.cc @@ -86,6 +86,10 @@ std::ostream& ExprConst::Print(std::ostream& out) const { } } +std::ostream& ExprConst::PrintPretty(std::ostream& out) const { + return Print(out); +} + BoolValPtr ExprConst::val_bool() const { ILA_ASSERT(is_bool()) << "Not boolean constant"; return std::static_pointer_cast(val_); diff --git a/src/ila/ast/expr_op.cc b/src/ila/ast/expr_op.cc index 927a4c3a7..2d6e9f04e 100644 --- a/src/ila/ast/expr_op.cc +++ b/src/ila/ast/expr_op.cc @@ -112,6 +112,15 @@ std::ostream& ExprOp::Print(std::ostream& out) const { return out << name().format_str(op_name(), ""); } +std::ostream& ExprOp::PrintPretty(std::ostream& out) const { + out << "(" << op_name(); + for (size_t i = 0; i != arg_num(); ++i) { + out << " "; + arg(i)->PrintPretty(out); + } + return out << ")"; +} + SortPtr ExprOp::GetSortBinaryOperation(const ExprPtr& e0, const ExprPtr& e1) { auto s0 = e0->sort(); auto s1 = e1->sort(); diff --git a/src/ila/ast/expr_var.cc b/src/ila/ast/expr_var.cc index c53d93e44..7863362d2 100644 --- a/src/ila/ast/expr_var.cc +++ b/src/ila/ast/expr_var.cc @@ -44,6 +44,10 @@ std::ostream& ExprVar::Print(std::ostream& out) const { } } +std::ostream& ExprVar::PrintPretty(std::ostream& out) const { + return Print(out); +} + std::ostream& ExprVar::PrintBool(std::ostream& out) const { return out << name(); } From a20036f6e95492d562a56bfb80cbab9576416618 Mon Sep 17 00:00:00 2001 From: Akash Gaonkar Date: Tue, 15 Feb 2022 07:09:16 +0000 Subject: [PATCH 32/32] Fixed Z3 linking bug. --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d23ff0f2..fac02aa63 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -228,6 +228,7 @@ if(${ILANG_BUILD_SWITCH}) if(${SMTSWITCH_Z3_FOUND}) target_link_libraries(${ILANG_LIB_NAME} PUBLIC smt-switch::smt-switch-z3) + target_link_libraries(smt-switch::smt-switch-z3 INTERFACE gmp) endif() endif()