diff --git a/src/2ls/2ls_parse_options.cpp b/src/2ls/2ls_parse_options.cpp index b1363d104..8c9a46be4 100644 --- a/src/2ls/2ls_parse_options.cpp +++ b/src/2ls/2ls_parse_options.cpp @@ -51,10 +51,12 @@ Author: Daniel Kroening, Peter Schrammel #include "summary_checker_ai.h" #include "summary_checker_bmc.h" #include "summary_checker_kind.h" +#include "summary_checker_nonterm.h" +#include "summary_checker_rect.h" #include "show.h" #include "horn_encoding.h" -#define UNWIND_GOTO_INTO_LOOP 1 +#define UNWIND_GOTO_INTO_LOOP 0 #define REMOVE_MULTIPLE_DEREFERENCES 1 #define IGNORE_RECURSION 1 #define IGNORE_THREADS 1 @@ -74,11 +76,12 @@ Function: twols_parse_optionst::twols_parse_optionst \*******************************************************************/ twols_parse_optionst::twols_parse_optionst(int argc, const char **argv): -parse_options_baset(TWOLS_OPTIONS, argc, argv), -language_uit(cmdline, ui_message_handler), + parse_options_baset(TWOLS_OPTIONS, argc, argv), + language_uit(cmdline, ui_message_handler), ui_message_handler(cmdline, "2LS " TWOLS_VERSION), recursion_detected(false), - threads_detected(false) + threads_detected(false), + dynamic_memory_detected(false) { } @@ -181,6 +184,11 @@ void twols_parse_optionst::get_command_line_options(optionst &options) else options.set_option("std-invariants", false); + if(cmdline.isset("no-propagation")) + options.set_option("constant-propagation", false); + else + options.set_option("constant-propagation", true); + // magic error label if(cmdline.isset("error-label")) options.set_option("error-label", cmdline.get_value("error-label")); @@ -192,6 +200,14 @@ void twols_parse_optionst::get_command_line_options(optionst &options) options.set_option("equalities", true); options.set_option("std-invariants", true); } + else if(cmdline.isset("heap")) + { + options.set_option("heap", true); + } + else if(cmdline.isset("heap-interval")) + { + options.set_option("heap-interval", true); + } else { if(cmdline.isset("zones")) @@ -263,7 +279,17 @@ void twols_parse_optionst::get_command_line_options(optionst &options) options.set_option("k-induction", true); options.set_option("inline", true); if(!cmdline.isset("unwind")) - options.set_option("unwind", UINT_MAX); + options.set_option("unwind", std::numeric_limits::max()); + } + + // compute singleton recurrence set - simple nontermination + if(cmdline.isset("nontermination")) + { + options.set_option("nontermination", true); + options.set_option("all-properties", true); + options.set_option("inline", true); + if(!cmdline.isset("unwind")) + options.set_option("unwind", std::numeric_limits::max()); } // do incremental bmc @@ -273,8 +299,16 @@ void twols_parse_optionst::get_command_line_options(optionst &options) options.set_option("inline", true); options.set_option("havoc", true); if(!cmdline.isset("unwind")) - options.set_option("unwind", UINT_MAX); + options.set_option("unwind", std::numeric_limits::max()); + } + + //............................................ + // handle recursive functions + if(cmdline.isset("has-recursion")) + { + options.set_option("has-recursion", true); } + //............................................. // check for spuriousness of assertion failures if(cmdline.isset("no-spurious-check")) @@ -300,6 +334,7 @@ void twols_parse_optionst::get_command_line_options(optionst &options) options.set_option("competition-mode", true); options.set_option("all-properties", false); options.set_option("inline", true); + options.set_option("give-up-invariants", "2"); } // instrumentation / output @@ -321,8 +356,8 @@ void twols_parse_optionst::get_command_line_options(optionst &options) } #endif - if(cmdline.isset("show-trace")) - options.set_option("show-trace", true); + if(cmdline.isset("trace")) + options.set_option("trace", true); if(cmdline.isset("graphml-witness")) options.set_option("graphml-witness", cmdline.get_value("graphml-witness")); if(cmdline.isset("json-cex")) @@ -371,6 +406,16 @@ int twols_parse_optionst::doit() if(get_goto_program(options, goto_model)) return 6; + const namespacet ns(goto_model.symbol_table); + ssa_heap_analysist heap_analysis(ns); + if((options.get_bool_option("heap") || + options.get_bool_option("heap-interval")) && + !options.get_bool_option("inline")) + { + heap_analysis(goto_model.goto_functions); + add_dynamic_object_symbols(heap_analysis, goto_model); + } + if(cmdline.isset("show-stats")) { show_stats(goto_model, std::cout); @@ -383,7 +428,13 @@ int twols_parse_optionst::doit() { bool simplify=!cmdline.isset("no-simplify"); irep_idt function=cmdline.get_value("function"); - show_ssa(goto_model, function, simplify, std::cout, ui_message_handler); + show_ssa( + goto_model, + heap_analysis, + function, + simplify, + std::cout, + ui_message_handler); return 7; } @@ -420,10 +471,24 @@ int twols_parse_optionst::doit() options.set_option("show-invariants", true); } + if(cmdline.isset("nontermination")) + { + // turn assertions (from generic checks) into assumptions + Forall_goto_functions(f_it, goto_model.goto_functions) + { + goto_programt &body=f_it->second.body; + Forall_goto_program_instructions(i_it, body) + { + if(i_it->is_assert()) + i_it->type=goto_program_instruction_typet::ASSUME; + } + } + } + #if IGNORE_RECURSION - if(recursion_detected) + if(recursion_detected && !cmdline.isset("has-recursion")) { - status() << "Recursion not supported" << eom; + status() << "Recursion not supported without --has-recursion option" << eom; report_unknown(); return 5; } @@ -456,6 +521,10 @@ int twols_parse_optionst::doit() status() << "Havocking loops and function calls" << eom; else if(options.get_bool_option("equalities")) status() << "Using (dis)equalities domain" << eom; + else if(options.get_bool_option("heap")) + status() << "Using heap domain" << eom; + else if(options.get_bool_option("heap-interval")) + status() << "Using heap domain with interval domain for values" << eom; else { if(options.get_bool_option("intervals")) @@ -477,21 +546,52 @@ int twols_parse_optionst::doit() status() << eom; } + // don't use k-induction with dynamic memory + if(options.get_bool_option("competition-mode") && + options.get_bool_option("k-induction") && + dynamic_memory_detected) + { + options.set_option("k-induction", false); + options.set_option("std-invariants", false); + options.set_option("incremental-bmc", false); + options.set_option("unwind", 0); + } + // don't do nontermination with dynamic memory + if(options.get_bool_option("competition-mode") && + (options.get_bool_option("termination") || + options.get_bool_option("nontermination")) && + dynamic_memory_detected) + { + error() << "Termination analysis does not support " + << "dynamic memory allocation" << eom; + report_unknown(); + return 5; + } + try { std::unique_ptr checker; if(!options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) - checker=std::unique_ptr( - new summary_checker_ait(options)); + { + if(!options.get_bool_option("has-recursion")) + checker=std::unique_ptr( + new summary_checker_ait(options, heap_analysis)); + else + checker=std::unique_ptr( + new summary_checker_rect(options, heap_analysis)); + } if(options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) checker=std::unique_ptr( - new summary_checker_kindt(options)); + new summary_checker_kindt(options, heap_analysis)); if(!options.get_bool_option("k-induction") && options.get_bool_option("incremental-bmc")) checker=std::unique_ptr( - new summary_checker_bmct(options)); + new summary_checker_bmct(options, heap_analysis)); + if(options.get_bool_option("nontermination")) + checker=std::unique_ptr( + new summary_checker_nontermt(options, heap_analysis)); checker->set_message_handler(get_message_handler()); checker->simplify=!cmdline.isset("no-simplify"); @@ -540,7 +640,8 @@ int twols_parse_optionst::doit() bool report_assertions= !options.get_bool_option("preconditions") && - !options.get_bool_option("termination"); + !options.get_bool_option("termination") && + !options.get_bool_option("nontermination"); // do actual analysis switch((*checker)(goto_model)) { @@ -548,23 +649,50 @@ int twols_parse_optionst::doit() if(report_assertions) report_properties(options, goto_model, checker->property_map); report_success(); - if(cmdline.isset("graphml-witness") && - !options.get_bool_option("termination")) + if(cmdline.isset("graphml-witness")) output_graphml_proof(options, goto_model, *checker); retval=0; break; case property_checkert::FAIL: + { if(report_assertions) report_properties(options, goto_model, checker->property_map); - report_failure(); + + // validate trace + bool trace_valid=false; + for(const auto &p : checker->property_map) + { + if(p.second.result!=property_checkert::FAIL) + continue; + + if(options.get_bool_option("trace")) + show_counterexample(goto_model, p.second.error_trace); + + trace_valid= + !p.second.error_trace.steps.empty() && + (options.get_bool_option("nontermination") || + p.second.error_trace.steps.back().is_assert()); + break; + } + if(cmdline.isset("graphml-witness")) { +#if 1 + if(!trace_valid) + { + retval=5; + error() << "Internal witness validation failed" << eom; + report_unknown(); + break; + } +#endif output_graphml_cex(options, goto_model, *checker); } + report_failure(); retval=10; break; - + } case property_checkert::UNKNOWN: if(report_assertions) report_properties(options, goto_model, checker->property_map); @@ -745,8 +873,8 @@ void twols_parse_optionst::show_stats( const code_assignt &assign=to_code_assign(instruction.code); expr_stats_rec(assign.lhs(), stats); expr_stats_rec(assign.rhs(), stats); + break; } - break; case ASSUME: expr_stats_rec(instruction.guard, stats); break; @@ -840,7 +968,7 @@ void twols_parse_optionst::require_entry( if(goto_model.symbol_table.symbols.find(entry_point)== symbol_table.symbols.end()) - throw "The program has no entry point; please complete linking"; + throw "the program has no entry point; please complete linking"; } /*******************************************************************\ @@ -1015,7 +1143,8 @@ bool twols_parse_optionst::process_goto_program( { status() << "Function Pointer Removal" << eom; remove_function_pointers( - goto_model, cmdline.isset("pointer-check")); + goto_model, + cmdline.isset("pointer-check")); // do partial inlining if(options.get_bool_option("inline-partial")) @@ -1047,7 +1176,9 @@ bool twols_parse_optionst::process_goto_program( status() << "Performing full inlining" << eom; const namespacet ns(goto_model.symbol_table); goto_inlinet goto_inline( - goto_model.goto_functions, ns, ui_message_handler); + goto_model.goto_functions, + ns, + ui_message_handler); goto_inline(); #if IGNORE_RECURSION recursion_detected=goto_inline.recursion_detected(); @@ -1064,6 +1195,13 @@ bool twols_parse_optionst::process_goto_program( #if UNWIND_GOTO_INTO_LOOP unwind_goto_into_loop(goto_model, 2); +#else + if(unwind_goto_into_loop(goto_model, 2)) + { + status() << "Irreducible control flow not supported" << eom; + report_unknown(); + return 5; + } #endif remove_skip(goto_model.goto_functions); @@ -1078,8 +1216,26 @@ bool twols_parse_optionst::process_goto_program( #endif #if 1 + // Find, inline and remove malloc function // TODO: find a better place for that - replace_malloc(goto_model, ""); + Forall_goto_functions(it, goto_model.goto_functions) + { + if(it->first=="malloc" || it->first=="free") + it->second.type.set(ID_C_inlined, true); + } + goto_partial_inline(goto_model, ui_message_handler, 0); + Forall_goto_functions(it, goto_model.goto_functions) + { + if(it->first=="malloc" || it->first=="free") + it->second.body.clear(); + } +#endif + + // create symbols for objects pointed by parameters + create_dynamic_objects(goto_model); + +#if REMOVE_MULTIPLE_DEREFERENCES + remove_multiple_dereferences(goto_model); #endif // recalculate numbers, etc. @@ -1088,6 +1244,12 @@ bool twols_parse_optionst::process_goto_program( // add loop ids goto_model.goto_functions.compute_loop_numbers(); + // Replace malloc + dynamic_memory_detected=replace_malloc(goto_model, ""); + + // remove loop heads from function entries + remove_loops_in_entry(goto_model); + // inline __CPROVER_initialize and main if(cmdline.isset("inline-main")) { @@ -1103,7 +1265,9 @@ bool twols_parse_optionst::process_goto_program( filter_assertions(goto_model); #endif - if(!cmdline.isset("no-propagation")) + if(options.get_bool_option("constant-propagation") && + !(options.get_bool_option("competition-mode") && + dynamic_memory_detected)) { status() << "Constant Propagation" << eom; propagate_constants(goto_model); @@ -1204,7 +1368,8 @@ void twols_parse_optionst::report_properties( xmlt xml_result("result"); xml_result.set_attribute("property", id2string(it->first)); xml_result.set_attribute( - "status", property_checkert::as_string(it->second.result)); + "status", + property_checkert::as_string(it->second.result)); std::cout << xml_result << "\n"; } else @@ -1216,7 +1381,7 @@ void twols_parse_optionst::report_properties( << eom; } - if(cmdline.isset("show-trace") && + if(options.get_bool_option("trace") && it->second.result==property_checkert::FAIL) show_counterexample(goto_model, it->second.error_trace); if(cmdline.isset("json-cex") && @@ -1282,8 +1447,8 @@ void twols_parse_optionst::report_success() xml.data="SUCCESS"; std::cout << xml; std::cout << std::endl; + break; } - break; default: assert(false); @@ -1579,6 +1744,7 @@ void twols_parse_optionst::help() "Backend options:\n" " --all-functions check each function as entry point\n" " --stop-on-fail stop on first failing assertion\n" + " --trace give a counterexample trace for failed properties\n" //NOLINT(*) " --context-sensitive context-sensitive analysis from entry point\n" // NOLINT(*) " --termination compute ranking functions to prove termination\n" // NOLINT(*) " --k-induction use k-induction\n" @@ -1588,8 +1754,10 @@ void twols_parse_optionst::help() " --havoc havoc loops and function calls\n" " --intervals use interval domain\n" " --equalities use equalities and disequalities domain\n" + " --heap use heap domain\n" " --zones use zone domain\n" " --octagons use octagon domain\n" + " --heap-interval use heap domain with interval domain for values\n" // NOLINT(*) " --enum-solver use solver based on model enumeration\n" " --binsearch-solver use solver based on binary search\n" " --arrays do not ignore array contents\n" diff --git a/src/2ls/2ls_parse_options.h b/src/2ls/2ls_parse_options.h index d564b1279..ca2a0d782 100644 --- a/src/2ls/2ls_parse_options.h +++ b/src/2ls/2ls_parse_options.h @@ -31,21 +31,22 @@ class optionst; "(non-incremental)" \ "(no-assertions)(no-assumptions)" \ "(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \ + "(object-bits):" \ "(little-endian)(big-endian)" \ "(error-label):(verbosity):(no-library)" \ "(version)" \ "(i386-linux)(i386-macos)(i386-win32)(win32)(winx64)(gcc)" \ "(ppc-macos)(unsigned-char)" \ - "(havoc)(intervals)(zones)(octagons)(equalities)"\ + "(havoc)(intervals)(zones)(octagons)(equalities)(heap)(heap-interval)"\ "(enum-solver)(binsearch-solver)(arrays)"\ "(string-abstraction)(no-arch)(arch):(floatbv)(fixedbv)" \ "(round-to-nearest)(round-to-plus-inf)(round-to-minus-inf)(round-to-zero)" \ "(inline)(inline-main)(inline-partial):" \ - "(context-sensitive)(termination)" \ + "(context-sensitive)(termination)(nontermination)" \ "(lexicographic-ranking-function):(monolithic-ranking-function)" \ "(max-inner-ranking-iterations):" \ "(preconditions)(sufficient)" \ - "(show-locs)(show-vcc)(show-properties)(show-trace)(show-stats)" \ + "(show-locs)(show-vcc)(show-properties)(trace)(show-stats)" \ "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)" \ "(show-invariants)(std-invariants)" \ "(property):(all-properties)(k-induction)(incremental-bmc)" \ @@ -54,7 +55,7 @@ class optionst; "(graphml-witness):(json-cex):" \ "(no-spurious-check)(stop-on-fail)" \ "(competition-mode)(slice)(no-propagation)(independent-properties)" \ - "(no-unwinding-assertions)" + "(no-unwinding-assertions)(has-recursion)"////////////////////////// // the last line is for CBMC-regression testing only class twols_parse_optionst: @@ -75,6 +76,7 @@ class twols_parse_optionst: ui_message_handlert ui_message_handler; bool recursion_detected; bool threads_detected; + bool dynamic_memory_detected; virtual void register_languages(); void get_command_line_options(optionst &options); @@ -157,11 +159,11 @@ class twols_parse_optionst: void inline_main(goto_modelt &goto_model); void propagate_constants(goto_modelt &goto_model); void nondet_locals(goto_modelt &goto_model); - void unwind_goto_into_loop(goto_modelt &goto_model, unsigned k); + bool unwind_goto_into_loop(goto_modelt &goto_model, unsigned k); void replace_types_rec(const replace_symbolt &replace_const, exprt &expr); exprt evaluate_casts_in_constants( const exprt &expr, - const typet& parent_type, + const typet &parent_type, bool &valid); void remove_multiple_dereferences(goto_modelt &goto_model); void remove_multiple_dereferences( @@ -174,6 +176,12 @@ class twols_parse_optionst: void add_assumptions_after_assertions(goto_modelt &goto_model); void filter_assertions(goto_modelt &goto_model); void split_loopheads(goto_modelt &goto_model); + void remove_loops_in_entry(goto_modelt &goto_model); + void create_dynamic_objects(goto_modelt &goto_model); + void add_dynamic_object_rec(exprt &expr, symbol_tablet &symbol_table); + void add_dynamic_object_symbols( + const ssa_heap_analysist &heap_analysis, + goto_modelt &goto_model); }; #endif diff --git a/src/2ls/Makefile b/src/2ls/Makefile index 06b9d4486..c9c5e1ad5 100644 --- a/src/2ls/Makefile +++ b/src/2ls/Makefile @@ -6,11 +6,11 @@ SRC = 2ls_main.cpp 2ls_parse_options.cpp \ 2ls_languages.cpp \ show.cpp summary_checker_base.cpp \ summary_checker_ai.cpp summary_checker_bmc.cpp \ - summary_checker_kind.cpp \ + summary_checker_kind.cpp summary_checker_nonterm.cpp \ cover_goals_ext.cpp horn_encoding.cpp \ preprocessing_util.cpp \ instrument_goto.cpp dynamic_cfg.cpp \ - graphml_witness_ext.cpp + graphml_witness_ext.cpp summary_checker_rect.cpp OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/linking/linking$(LIBEXT) \ diff --git a/src/2ls/a.dep b/src/2ls/a.dep new file mode 100644 index 000000000..c4f90d197 --- /dev/null +++ b/src/2ls/a.dep @@ -0,0 +1,344 @@ +summary_checker_rect.o: summary_checker_rect.cpp \ + ../solver/summarizer_fw.h \ + /home/sarbojit/2ls-master/cbmc/src/util/message.h \ + /home/sarbojit/2ls-master/cbmc/src/util/source_location.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep.h \ + /home/sarbojit/2ls-master/cbmc/src/util/dstring.h \ + /home/sarbojit/2ls-master/cbmc/src/util/string_container.h \ + /home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h \ + /home/sarbojit/2ls-master/cbmc/src/util/string_hash.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep_ids.h \ + /home/sarbojit/2ls-master/cbmc/src/util/options.h \ + /home/sarbojit/2ls-master/cbmc/src/util/time_stopping.h \ + ../ssa/ssa_inliner.h ../solver/summary.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_types.h \ + /home/sarbojit/2ls-master/cbmc/src/util/expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/type.h \ + /home/sarbojit/2ls-master/cbmc/src/util/source_location.h \ + /home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h \ + /home/sarbojit/2ls-master/cbmc/src/big-int/bigint.hh ../ssa/local_ssa.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_code.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program_template.h \ + /home/sarbojit/2ls-master/cbmc/src/util/namespace.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol_table.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol.h \ + /home/sarbojit/2ls-master/cbmc/src/langapi/language_util.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions_template.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_types.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol.h \ + ../domains/list_iterator.h ../domains/incremental_solver.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_pointers.h \ + /home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv.h \ + /home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h \ + /home/sarbojit/2ls-master/cbmc/src/util/expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/byte_operators.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_utils.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h \ + /home/sarbojit/2ls-master/cbmc/src/util/threeval.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_assignment.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_width.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_map.h \ + /home/sarbojit/2ls-master/cbmc/src/util/type.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_type.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/arrays.h \ + /home/sarbojit/2ls-master/cbmc/src/util/union_find.h \ + /home/sarbojit/2ls-master/cbmc/src/util/numbering.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/equality.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_conv.h \ + /home/sarbojit/2ls-master/cbmc/src/util/decision_procedure.h \ + /home/sarbojit/2ls-master/cbmc/src/util/message.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/functions.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/pointer_logic.h \ + /home/sarbojit/2ls-master/cbmc/src/util/numbering.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/refinement/bv_refinement.h \ + /home/sarbojit/2ls-master/cbmc/src/langapi/language_ui.h \ + /home/sarbojit/2ls-master/cbmc/src/util/language_file.h \ + /home/sarbojit/2ls-master/cbmc/src/util/ui_message.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck_glucose.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/cnf.h ../domains/domain.h \ + /home/sarbojit/2ls-master/cbmc/src/util/i2string.h \ + /home/sarbojit/2ls-master/cbmc/src/util/replace_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_expr.h ../domains/util.h \ + /home/sarbojit/2ls-master/cbmc/src/util/arith_tools.h \ + /home/sarbojit/2ls-master/cbmc/src/util/ieee_float.h \ + /home/sarbojit/2ls-master/cbmc/src/util/format_spec.h \ + ../ssa/ssa_domain.h /home/sarbojit/2ls-master/cbmc/src/analyses/ai.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h \ + ../ssa/assignments.h ../ssa/ssa_object.h ../ssa/ssa_pointed_objects.h \ + ../ssa/ssa_heap_domain.h \ + /home/sarbojit/2ls-master/cbmc/src/analyses/static_analysis.h \ + ../ssa/ssa_value_set.h ../ssa/guard_map.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h \ + ../ssa/may_alias_analysis.h ../solver/summary_db.h ../solver/summary.h \ + /home/sarbojit/2ls-master/cbmc/src/util/json.h ../ssa/ssa_db.h \ + ../ssa/unwindable_local_ssa.h ../ssa/local_ssa.h ../ssa/ssa_unwinder.h \ + ../ssa/unwindable_local_ssa.h ../ssa/ssa_db.h \ + ../solver/summarizer_base.h ../solver/summarizer_fw_term.h \ + ../solver/summarizer_fw.h ../solver/summarizer_bw.h \ + ../solver/summarizer_bw_term.h ../domains/template_generator_summary.h \ + ../domains/template_generator_base.h ../domains/strategy_solver_base.h \ + ../domains/incremental_solver.h ../domains/template_generator_ranking.h \ + ../solver/summarizer_bw.h ../solver/summarizer_rec_fw.h \ + summary_checker_rect.h summary_checker_ai.h summary_checker_base.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/property_checker.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_trace.h \ + /home/sarbojit/2ls-master/cbmc/src/util/ssa_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h \ + ../ssa/ssa_heap_domain.h cover_goals_ext.h ../ssa/local_ssa.h \ + ../ssa/unwindable_local_ssa.h ../domains/incremental_solver.h + +../solver/summarizer_fw.h: + +/home/sarbojit/2ls-master/cbmc/src/util/message.h: + +/home/sarbojit/2ls-master/cbmc/src/util/source_location.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep.h: + +/home/sarbojit/2ls-master/cbmc/src/util/dstring.h: + +/home/sarbojit/2ls-master/cbmc/src/util/string_container.h: + +/home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h: + +/home/sarbojit/2ls-master/cbmc/src/util/string_hash.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep_ids.h: + +/home/sarbojit/2ls-master/cbmc/src/util/options.h: + +/home/sarbojit/2ls-master/cbmc/src/util/time_stopping.h: + +../ssa/ssa_inliner.h: + +../solver/summary.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_types.h: + +/home/sarbojit/2ls-master/cbmc/src/util/expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/type.h: + +/home/sarbojit/2ls-master/cbmc/src/util/source_location.h: + +/home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h: + +/home/sarbojit/2ls-master/cbmc/src/big-int/bigint.hh: + +../ssa/local_ssa.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_code.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program_template.h: + +/home/sarbojit/2ls-master/cbmc/src/util/namespace.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol_table.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol.h: + +/home/sarbojit/2ls-master/cbmc/src/langapi/language_util.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions_template.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_types.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol.h: + +../domains/list_iterator.h: + +../domains/incremental_solver.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_pointers.h: + +/home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv.h: + +/home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h: + +/home/sarbojit/2ls-master/cbmc/src/util/expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/byte_operators.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_utils.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h: + +/home/sarbojit/2ls-master/cbmc/src/util/threeval.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_assignment.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_width.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_map.h: + +/home/sarbojit/2ls-master/cbmc/src/util/type.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_type.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/arrays.h: + +/home/sarbojit/2ls-master/cbmc/src/util/union_find.h: + +/home/sarbojit/2ls-master/cbmc/src/util/numbering.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/equality.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_conv.h: + +/home/sarbojit/2ls-master/cbmc/src/util/decision_procedure.h: + +/home/sarbojit/2ls-master/cbmc/src/util/message.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/functions.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/pointer_logic.h: + +/home/sarbojit/2ls-master/cbmc/src/util/numbering.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/refinement/bv_refinement.h: + +/home/sarbojit/2ls-master/cbmc/src/langapi/language_ui.h: + +/home/sarbojit/2ls-master/cbmc/src/util/language_file.h: + +/home/sarbojit/2ls-master/cbmc/src/util/ui_message.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck_glucose.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/cnf.h: + +../domains/domain.h: + +/home/sarbojit/2ls-master/cbmc/src/util/i2string.h: + +/home/sarbojit/2ls-master/cbmc/src/util/replace_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_expr.h: + +../domains/util.h: + +/home/sarbojit/2ls-master/cbmc/src/util/arith_tools.h: + +/home/sarbojit/2ls-master/cbmc/src/util/ieee_float.h: + +/home/sarbojit/2ls-master/cbmc/src/util/format_spec.h: + +../ssa/ssa_domain.h: + +/home/sarbojit/2ls-master/cbmc/src/analyses/ai.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h: + +../ssa/assignments.h: + +../ssa/ssa_object.h: + +../ssa/ssa_pointed_objects.h: + +../ssa/ssa_heap_domain.h: + +/home/sarbojit/2ls-master/cbmc/src/analyses/static_analysis.h: + +../ssa/ssa_value_set.h: + +../ssa/guard_map.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h: + +../ssa/may_alias_analysis.h: + +../solver/summary_db.h: + +../solver/summary.h: + +/home/sarbojit/2ls-master/cbmc/src/util/json.h: + +../ssa/ssa_db.h: + +../ssa/unwindable_local_ssa.h: + +../ssa/local_ssa.h: + +../ssa/ssa_unwinder.h: + +../ssa/unwindable_local_ssa.h: + +../ssa/ssa_db.h: + +../solver/summarizer_base.h: + +../solver/summarizer_fw_term.h: + +../solver/summarizer_fw.h: + +../solver/summarizer_bw.h: + +../solver/summarizer_bw_term.h: + +../domains/template_generator_summary.h: + +../domains/template_generator_base.h: + +../domains/strategy_solver_base.h: + +../domains/incremental_solver.h: + +../domains/template_generator_ranking.h: + +../solver/summarizer_bw.h: + +../solver/summarizer_rec_fw.h: + +summary_checker_rect.h: + +summary_checker_ai.h: + +summary_checker_base.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/property_checker.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_trace.h: + +/home/sarbojit/2ls-master/cbmc/src/util/ssa_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h: + +../ssa/ssa_heap_domain.h: + +cover_goals_ext.h: + +../ssa/local_ssa.h: + +../ssa/unwindable_local_ssa.h: + +../domains/incremental_solver.h: diff --git a/src/2ls/cover_goals_ext.h b/src/2ls/cover_goals_ext.h index d777a8a66..a0e9fb3c1 100644 --- a/src/2ls/cover_goals_ext.h +++ b/src/2ls/cover_goals_ext.h @@ -52,7 +52,8 @@ class cover_goals_extt:public messaget incremental_solvert &_solver, const exprt::operandst& _loophead_selects, property_checkert::property_mapt &_property_map, - bool _spurious_check, bool _all_properties, + bool _spurious_check, + bool _all_properties, bool _build_error_trace): SSA(_SSA), solver(_solver), diff --git a/src/2ls/dynamic_cfg.cpp b/src/2ls/dynamic_cfg.cpp index 807ec5ed3..f7cd221b4 100644 --- a/src/2ls/dynamic_cfg.cpp +++ b/src/2ls/dynamic_cfg.cpp @@ -33,7 +33,7 @@ void dynamic_cfgt::operator()( build_cfg(goto_program, ssa_unwinder); assumptionst assumptions; - build_from_invariants(ssa, summary, assumptions); + build_from_invariants(ssa, summary.fw_invariant, assumptions); add_assumptions(assumptions); } @@ -193,6 +193,23 @@ void dynamic_cfgt::build_cfg( nodes[node].id.iteration_stack=iteration_stack; nodes[node].is_loop_head=true; } + else + { + // alternative loop head detection when unwinder was not used + for(const auto &incoming : it->incoming_edges) + { + if(incoming->is_backwards_goto() && + incoming!=it) + { + iteration_stack.push_back(0); + loop_node_stack.push_back(node); + max_iteration_stack.push_back(0); + nodes[node].id.iteration_stack=iteration_stack; + nodes[node].is_loop_head=true; + break; + } + } + } const std::set &iedges=incoming_edges[it]; for(const auto &from : iedges) @@ -273,29 +290,20 @@ Function: dynamic_cfgt::build_from_invariants void dynamic_cfgt::build_from_invariants( const unwindable_local_SSAt &ssa, - const summaryt &summary, + const exprt &invariants, assumptionst &assumptions) { - if(summary.fw_invariant.is_nil() || - summary.fw_invariant.is_true()) + if(invariants.is_nil() || invariants.is_true()) return; // expected format /\_i g_i=> inv_i - if(summary.fw_invariant.id()==ID_implies) + if(invariants.id()==ID_implies) { - build_from_invariant( - ssa, summary.fw_invariant, - assumptions); + build_from_invariant(ssa, invariants, assumptions); } - else if(summary.fw_invariant.id()==ID_and) + else if(invariants.id()==ID_and) { - for(unsigned i=0; i void build_from_invariants( const unwindable_local_SSAt &ssa, - const summaryt &summary, + const exprt &invariants, assumptionst &assumptions); void build_from_invariant( const unwindable_local_SSAt &ssa, diff --git a/src/2ls/graphml_witness_ext.cpp b/src/2ls/graphml_witness_ext.cpp index 11fa4bc3b..4ce2952a4 100644 --- a/src/2ls/graphml_witness_ext.cpp +++ b/src/2ls/graphml_witness_ext.cpp @@ -42,7 +42,6 @@ void graphml_witness_extt::operator()( // CFG to CFA const graphmlt::node_indext sink=graphml.add_node(); graphml[sink].node_name="sink"; - graphml[sink].thread_nr=0; graphml[sink].is_violation=false; graphml[sink].has_invariant=false; diff --git a/src/2ls/graphml_witness_ext.h b/src/2ls/graphml_witness_ext.h index df62a2b6d..6294ffc1b 100644 --- a/src/2ls/graphml_witness_ext.h +++ b/src/2ls/graphml_witness_ext.h @@ -28,7 +28,8 @@ class graphml_witness_extt:public graphml_witnesst protected: graphmlt::node_indext add_node( - std::map &loc_to_node, + std::map &loc_to_node, goto_programt::const_targett it); void add_edge( diff --git a/src/2ls/horn_encoding.cpp b/src/2ls/horn_encoding.cpp index fb986a691..cacffe305 100644 --- a/src/2ls/horn_encoding.cpp +++ b/src/2ls/horn_encoding.cpp @@ -81,7 +81,8 @@ void horn_encodingt::translate( ";\n"; // compute SSA - local_SSAt local_SSA(f_it->second, ns, ""); + ssa_heap_analysist heap_analysis(ns); + local_SSAt local_SSA(f_it->second, ns, heap_analysis, ""); const goto_programt &body=f_it->second.body; diff --git a/src/2ls/instrument_goto.cpp b/src/2ls/instrument_goto.cpp index 3e550c6e1..647aaab10 100644 --- a/src/2ls/instrument_goto.cpp +++ b/src/2ls/instrument_goto.cpp @@ -115,8 +115,7 @@ Function: instrument_gotot::instrument_body void instrument_gotot::instrument_body( const local_SSAt &SSA, const exprt &expr, - goto_functionst::goto_functiont &function -) + goto_functionst::goto_functiont &function) { // expected format (/\_j g_j)=> inv const exprt &impl=expr.op0(); diff --git a/src/2ls/preprocessing_util.cpp b/src/2ls/preprocessing_util.cpp index 590827693..b31229afb 100644 --- a/src/2ls/preprocessing_util.cpp +++ b/src/2ls/preprocessing_util.cpp @@ -15,7 +15,6 @@ Author: Peter Schrammel #include "2ls_parse_options.h" - /*******************************************************************\ Function: twols_parse_optionst::inline_main @@ -138,10 +137,11 @@ Function: twols_parse_optionst::unwind_goto_into_loop \*******************************************************************/ -void twols_parse_optionst::unwind_goto_into_loop( +bool twols_parse_optionst::unwind_goto_into_loop( goto_modelt &goto_model, unsigned k) { + bool result=false; typedef std::vector > loopst; @@ -171,6 +171,7 @@ void twols_parse_optionst::unwind_goto_into_loop( (*s_it)->location_numberlocation_number) { has_goto_into_loop=true; + result=true; break; } } @@ -205,6 +206,8 @@ void twols_parse_optionst::unwind_goto_into_loop( } goto_model.goto_functions.update(); goto_model.goto_functions.compute_loop_numbers(); + + return result; } /*******************************************************************\ @@ -466,3 +469,160 @@ void twols_parse_optionst::split_loopheads(goto_modelt &goto_model) } } } + +/*******************************************************************\ + +Function: twols_parse_optionst::remove_loops_in_entry + + Inputs: + + Outputs: + + Purpose: Remove loop head from entry instruction of a function - + causes problems with input variables naming. If first + instruction is target of back-jump, insert SKIP instruction + before. + +\*******************************************************************/ + +void twols_parse_optionst::remove_loops_in_entry(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + if(f_it->second.body_available() && + f_it->second.body.instructions.begin()->is_target()) + { + auto new_entry= + f_it->second.body.insert_before(f_it->second.body.instructions.begin()); + new_entry->function=f_it->first; + new_entry->make_skip(); + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::create_dynamic_objects + + Inputs: + + Outputs: + + Purpose: Create symbols for objects pointed by parameters of a function. + +\*******************************************************************/ + +void twols_parse_optionst::create_dynamic_objects(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_assign()) + { + code_assignt &code_assign=to_code_assign(i_it->code); + add_dynamic_object_rec(code_assign.lhs(), goto_model.symbol_table); + add_dynamic_object_rec(code_assign.rhs(), goto_model.symbol_table); + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::add_dynamic_object_rec + + Inputs: + + Outputs: + + Purpose: For each pointer-typed symbol in an expression which is a parameter, + create symbol for pointed object in the symbol table. + +\*******************************************************************/ + +void twols_parse_optionst::add_dynamic_object_rec( + exprt &expr, symbol_tablet &symbol_table) +{ + if(expr.id()==ID_symbol) + { + const symbolt &symbol= + symbol_table.lookup(to_symbol_expr(expr).get_identifier()); + if(symbol.is_parameter && symbol.type.id()==ID_pointer) + { + // New symbol + symbolt object_symbol; + + object_symbol.base_name=id2string(symbol.base_name)+"'obj"; + object_symbol.name=id2string(symbol.name)+"'obj"; + const typet &pointed_type=symbol.type.subtype(); + // Follow pointed type + if(pointed_type.id()==ID_symbol) + { + const symbolt type_symbol=symbol_table.lookup( + to_symbol_type(pointed_type).get_identifier()); + object_symbol.type=type_symbol.type; + } + else + object_symbol.type=pointed_type; + object_symbol.mode=ID_C; + + symbol_table.add(object_symbol); + } + } + else + { + Forall_operands(it, expr) + add_dynamic_object_rec(*it, symbol_table); + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::add_dynamic_object_symbols + + Inputs: + + Outputs: + + Purpose: Add symbols for all dynamic objects in the program into + the symbol table. + +\*******************************************************************/ + +void twols_parse_optionst::add_dynamic_object_symbols( + const ssa_heap_analysist &heap_analysis, + goto_modelt &goto_model) +{ + forall_goto_functions(f_it, goto_model.goto_functions) + { + forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_function_call()) + { + auto &fun_call=to_code_function_call(i_it->code); + const irep_idt fname= + to_symbol_expr(fun_call.function()).get_identifier(); + auto n_it=i_it; + ++n_it; + for(const symbol_exprt &o : + heap_analysis[n_it].new_caller_objects(fname, i_it)) + { + // New symbol + symbolt object_symbol; + + object_symbol.name=o.get_identifier(); + object_symbol.base_name=id2string(object_symbol.name).substr(5); + object_symbol.is_lvalue=true; + + object_symbol.type=o.type(); + object_symbol.type.set("#dynamic", true); + + object_symbol.mode=ID_C; + + goto_model.symbol_table.add(object_symbol); + } + } + } + } +} diff --git a/src/2ls/show.cpp b/src/2ls/show.cpp index c81d0db95..ce0b64e94 100644 --- a/src/2ls/show.cpp +++ b/src/2ls/show.cpp @@ -18,7 +18,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include +#include #include #include @@ -41,11 +41,17 @@ Function: show_assignments void show_assignments( const goto_functionst::goto_functiont &goto_function, const namespacet &ns, - std::ostream &out) + std::ostream &out, + const ssa_heap_analysist &heap_analysis) { - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); + ssa_objectst ssa_objects(goto_function, ns, heap_analysis); + ssa_value_ait ssa_value_ai(goto_function, ns, heap_analysis); + assignmentst assignments( + goto_function.body, + ns, + ssa_objects, + ssa_value_ai, + heap_analysis); assignments.output(ns, goto_function.body, out); } @@ -69,6 +75,8 @@ void show_assignments( { const namespacet ns(goto_model.symbol_table); + ssa_heap_analysist heap_analysis(ns); + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -76,7 +84,7 @@ void show_assignments( if(f_it==goto_model.goto_functions.function_map.end()) out << "function " << function << " not found\n"; else - show_assignments(f_it->second, ns, out); + show_assignments(f_it->second, ns, out, heap_analysis); } else { @@ -84,7 +92,7 @@ void show_assignments( { out << ">>>> Function " << f_it->first << "\n"; - show_assignments(f_it->second, ns, out); + show_assignments(f_it->second, ns, out, heap_analysis); out << "\n"; } @@ -106,11 +114,17 @@ Function: show_defs void show_defs( const goto_functionst::goto_functiont &goto_function, const namespacet &ns, - std::ostream &out) + std::ostream &out, + const ssa_heap_analysist &heap_analysis) { - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); + ssa_objectst ssa_objects(goto_function, ns, heap_analysis); + ssa_value_ait ssa_value_ai(goto_function, ns, heap_analysis); + assignmentst assignments( + goto_function.body, + ns, + ssa_objects, + ssa_value_ai, + heap_analysis); ssa_ait ssa_analysis(assignments); ssa_analysis(goto_function, ns); ssa_analysis.output(ns, goto_function.body, out); @@ -136,6 +150,8 @@ void show_defs( { const namespacet ns(goto_model.symbol_table); + ssa_heap_analysist heap_analysis(ns); + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -143,7 +159,7 @@ void show_defs( if(f_it==goto_model.goto_functions.function_map.end()) out << "function " << function << " not found\n"; else - show_defs(f_it->second, ns, out); + show_defs(f_it->second, ns, out, heap_analysis); } else { @@ -151,7 +167,7 @@ void show_defs( { out << ">>>> Function " << f_it->first << "\n"; - show_defs(f_it->second, ns, out); + show_defs(f_it->second, ns, out, heap_analysis); out << "\n"; } @@ -235,11 +251,15 @@ Function: show_ssa void show_ssa( const goto_functionst::goto_functiont &goto_function, + const ssa_heap_analysist &heap_analysis, bool simplify, const namespacet &ns, std::ostream &out) { - local_SSAt local_SSA(goto_function, ns); + if(!goto_function.body_available()) + return; + + unwindable_local_SSAt local_SSA(goto_function, ns, heap_analysis); if(simplify) ::simplify(local_SSA, ns); local_SSA.output_verbose(out); @@ -259,6 +279,7 @@ Function: show_ssa void show_ssa( const goto_modelt &goto_model, + const ssa_heap_analysist &heap_analysis, const irep_idt &function, bool simplify, std::ostream &out, @@ -274,7 +295,7 @@ void show_ssa( if(f_it==goto_model.goto_functions.function_map.end()) out << "function " << function << " not found\n"; else - show_ssa(f_it->second, simplify, ns, out); + show_ssa(f_it->second, heap_analysis, simplify, ns, out); } else { @@ -287,7 +308,7 @@ void show_ssa( out << ">>>> Function " << f_it->first << "\n"; - show_ssa(f_it->second, simplify, ns, out); + show_ssa(f_it->second, heap_analysis, simplify, ns, out); out << "\n"; } @@ -558,10 +579,11 @@ Function: show_value_set void show_value_set( const goto_functionst::goto_functiont &goto_function, const namespacet &ns, - std::ostream &out) + std::ostream &out, + const ssa_heap_analysist &heap_analysis) { - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); + ssa_objectst ssa_objects(goto_function, ns, heap_analysis); + ssa_value_ait ssa_value_ai(goto_function, ns, heap_analysis); ssa_value_ai.output(ns, goto_function, out); } @@ -585,6 +607,8 @@ void show_value_sets( { const namespacet ns(goto_model.symbol_table); + ssa_heap_analysist heap_analysis(ns); + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -592,7 +616,7 @@ void show_value_sets( if(f_it==goto_model.goto_functions.function_map.end()) out << "function " << function << " not found\n"; else - show_value_set(f_it->second, ns, out); + show_value_set(f_it->second, ns, out, heap_analysis); } else { @@ -600,10 +624,9 @@ void show_value_sets( { out << ">>>> Function " << f_it->first << "\n"; - show_value_set(f_it->second, ns, out); + show_value_set(f_it->second, ns, out, heap_analysis); out << "\n"; } } } - diff --git a/src/2ls/show.h b/src/2ls/show.h index 2e9557eac..7051e56a5 100644 --- a/src/2ls/show.h +++ b/src/2ls/show.h @@ -19,9 +19,11 @@ Author: Daniel Kroening, kroening@kroening.com #include class message_handlert; +class ssa_heap_analysist; void show_ssa( const goto_modelt &, + const ssa_heap_analysist &, const irep_idt &function, bool simplify, std::ostream &, diff --git a/src/2ls/summary_checker_ai.cpp b/src/2ls/summary_checker_ai.cpp index 6cda09787..6f36857ab 100644 --- a/src/2ls/summary_checker_ai.cpp +++ b/src/2ls/summary_checker_ai.cpp @@ -9,8 +9,6 @@ Author: Peter Schrammel #include "summary_checker_ai.h" #include -#define TERM_CEX 1 - /*******************************************************************\ Function: summary_checker_ait::operator() @@ -28,7 +26,7 @@ property_checkert::resultt summary_checker_ait::operator()( { const namespacet ns(goto_model.symbol_table); - SSA_functions(goto_model, ns); + SSA_functions(goto_model, ns, heap_analysis); ssa_unwinder.init(false, false); @@ -158,23 +156,12 @@ property_checkert::resultt summary_checker_ait::report_termination() return property_checkert::PASS; if(one_nonterminate) { -#if TERM_CEX - if(options.get_option("graphml-witness")!="" && - !functions.empty()) - { - property_map.clear(); - incremental_solvert &solver=ssa_db.get_solver(functions.begin()->first); - if(solver()==decision_proceduret::D_SATISFIABLE) - { - irep_idt pid="non-termination"; - property_map[pid].result=property_checkert::FAIL; - ssa_build_goto_tracet build_goto_trace( - *functions.begin()->second, solver.get_solver(), true); - build_goto_trace(property_map[pid].error_trace); - } - } -#endif +#if 0 return property_checkert::FAIL; +#else + // rely on nontermination checker to find counterexample + return property_checkert::UNKNOWN; +#endif } return property_checkert::UNKNOWN; } diff --git a/src/2ls/summary_checker_ai.h b/src/2ls/summary_checker_ai.h index 84eab2bf0..87b477902 100644 --- a/src/2ls/summary_checker_ai.h +++ b/src/2ls/summary_checker_ai.h @@ -14,8 +14,10 @@ Author: Peter Schrammel class summary_checker_ait:public summary_checker_baset { public: - explicit summary_checker_ait(optionst &_options): - summary_checker_baset(_options) + inline summary_checker_ait( + optionst &_options, + const ssa_heap_analysist &heap_analysis): + summary_checker_baset(_options, heap_analysis) { } diff --git a/src/2ls/summary_checker_base.cpp b/src/2ls/summary_checker_base.cpp index c910b184c..c84eafc9c 100644 --- a/src/2ls/summary_checker_base.cpp +++ b/src/2ls/summary_checker_base.cpp @@ -38,6 +38,620 @@ Author: Peter Schrammel #include "instrument_goto.h" #include "summary_checker_base.h" +#include + + + + + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +std::string extract_var(std::string in) +{ + std::string::iterator i; + std::string s=""; + for(i=in.begin();*i!='#';i++) + { + s.append(1,*i); + } + return s; +} + +int is_var(std::vector vars,std::string in) +{ + std::string var; + if(in.at(0)=='$') + return -1; + else + { + std::string::iterator i; + int count=-1,pos=-1; + for(i=in.begin();i!=in.end();i++) + { + if(*i=='#') + { + std::vector::iterator i1; + for(i1=vars.begin();i1!=vars.end();i1++) + { + std::string var1=extract_var(from_expr(*i1)); + count++; + if(var.compare(var1)==0) pos=count; + } + if(pos==-1) return pos; + } + var.append(1,*i); + } + return pos; + } +} +bool construct_cnf(std::vector& P,std::ofstream& out,std::string in,std::vector pre_vars,unsigned loophead)//reads the input formula and construct an cnf object +{ + int flag=0,sign=0,size=0; + std::string::iterator i; + std::string *s=new std::string(),*clause=new std::string(pre_vars.size(),'X'); + for (i=in.begin();i!=in.end();i++) + { + if(*i=='&') + { + std::vector::iterator i1; + if((s->compare("True")==0&&sign==0)||(s->compare("False")==0&&sign==1)) + { + s->clear(); + clause->insert(0,'X',pre_vars.size()); + size=0; + continue; + } + else if((s->compare("False")==0&&sign==0)||(s->compare("True")==0&&sign==1)) + { + if(size==0) + { + P.clear(); + out<<"P is FALSE.\n"; + return false; + } + } + else + { + std::vector::iterator i1; + int pos=0; + *s=*s+"#phi"+std::to_string(loophead); + for(i1=pre_vars.begin();i1!=pre_vars.end();i1++) + { + if(from_expr(*i1).compare(*s)==0) + { + if(sign==0 && clause->at(pos)!='F') (*clause)[pos]='T'; + else if(clause->at(pos)!='T') (*clause)[pos]='F'; + else + { + out<<"Contradiction in I.\n"; + return false; + } + break; + } + pos++; + } + sign=0; + if(pos==pre_vars.size()) return false; + P.push_back(*clause); + s->clear(); + clause=new std::string(pre_vars.size(),'X'); + size=0; + } + } + else if(*i=='|') + { + std::vector::iterator i1; + if((s->compare("True")==0&&sign==0)||(s->compare("False")==0&&sign==1)) + { + while(*i!='&'||i!=in.end()) + { + i++; + } + i--; + clause->insert(0,'X',pre_vars.size()); + size=0; + } + else if((s->compare("False")==0&&sign==0)||(s->compare("True")==0&&sign==1)) + { + + } + else + { + std::vector::iterator i1; + int pos=0; + *s=*s+"#phi"+std::to_string(loophead); + for(i1=pre_vars.begin();i1!=pre_vars.end();i1++) + { + if(from_expr(*i1).compare(*s)==0) + { + if(sign==0 && clause->at(pos)!='F') (*clause)[pos]='T'; + else if(clause->at(pos)!='T') (*clause)[pos]='F'; + else + { + out<<"Contradiction in I.\n"; + return false; + } + break; + } + pos++; + } + sign=0; + if(pos==pre_vars.size()) return false; + } + s->clear(); + } + else if(*i == '(') + { + if(!(s->empty())) + { + out<<"Error in the formula\n"; + return false; + } + if(flag==1) + { + out<<"Parenthesis mismatch\n"; + return false; + } + flag=1; + } + else if(*i == ')') + { + if(flag==0) + { + out<<"Parenthesis mismatch\n"; + return false; + } + flag=0; + } + else if(*i == '~') sign=1; + else + { + s->append(1,*i); + } + } + if(flag!=0){ + out<<"Parenthesis mismatch\n"; + return false; + } + std::vector::iterator i1; + if((s->compare("True")==0&&sign==0)||(s->compare("False")==0&&sign==1)) + { + + } + else if((s->compare("False")==0&&sign==0)||(s->compare("True")==0&&sign==1)) + { + if(size==0) + { + P.clear(); + out<<"P is FALSE.\n"; + return false; + } + } + else + { + std::vector::iterator i1; + int pos=0; + *s=*s+"#phi"+std::to_string(loophead); + for(i1=pre_vars.begin();i1!=pre_vars.end();i1++) + { + if(from_expr(*i1).compare(*s)==0) + { + if(sign==0 && clause->at(pos)!='F') (*clause)[pos]='T'; + else if(clause->at(pos)!='T') (*clause)[pos]='F'; + else + { + out<<"Contradiction in I.\n"; + return false; + } + break; + } + pos++; + } + if(pos==pre_vars.size()) return false; + P.push_back(*clause); + } + if(P.empty()) + { + clause->clear(); + out<<"Property is True.\n"; + P.clear(); + return false; + } + return true; +} + +exprt clause_to_exprt(std::string clause,std::vector vars) +{ + std::vector lits; + unsigned i2; + constant_exprt f("00000000",vars.at(0).type()); + for(i2 = 0;i2 to_exprt_vec(std::vector P,std::vector vars)//adds clauses of the caller cnf to the Solver object +{ + std::vector conjuncts; + std::vector< std::string >::iterator i1; + for(i1 = P.begin();i1 != P.end();i1++) + { + conjuncts.push_back(clause_to_exprt(*i1,vars)); + //std::cout<<"\n"; + } + //std::cout<<"solver::"<> &Frames,int no,std::vector pre_vars,std::vector post_vars,const namespacet &ns,unsigned loophead) +{ + constant_exprt f("00000000",pre_vars.at(0).type()); + incremental_solvert solver(ns),solver1(ns); + solver< conjuncts,disjuncts; + std::vector::iterator iter; + out<<"At frame "<\n"; + int pos=0; + for(iter=pre_vars.begin();iter!=pre_vars.end();iter++) + { + exprt e1=solver.get(*iter); + if(from_expr(ns,"",e1).compare("TRUE")==0) + { + disjuncts.push_back(equal_exprt(*iter,f)); + conjuncts.push_back(equal_exprt(post_vars.at(pos),e1)); + blocked_clause[pos]='F'; + } + else if(from_expr(ns,"",e1).compare("FALSE")==0) + { + disjuncts.push_back(notequal_exprt(*iter,f)); + conjuncts.push_back(equal_exprt(post_vars.at(pos),e1)); + blocked_clause[pos]='T'; + } + pos++; + } + out<<" CTI "<::iterator iter1=Frames.at(i).begin();iter1!=Frames.at(i).end();iter1++) + { + if(subsumes(*iter1,blocked_clause)) subsumes_flag=true; + } + if(!subsumes_flag) Frames.at(i).push_back(blocked_clause); + } + out<<"Blocked clause(Blocked upto frame "< frame1,std::vector frame2) +{ + +}*/ + +bool propagate_clauses(std::ofstream& out,exprt T,std::vector> &Frames,std::vector pre_vars,std::vector post_vars,int framesize,const namespacet &ns) +{ + out<<"Propagation phase |-------------------------------------------->\n\n"; + int i1; + for(i1=0;i1<=framesize;i1++) + { + std::vector Clauses1=Frames.at(i1),Clauses2=Frames.at(i1+1); + unsigned i2,i3; + for(i2=0;i2 P_pre,std::vector P,std::vector pre_vars,std::vector post_vars,unsigned loophead) +{ + incremental_solvert solver(ns); + solver< P_post=to_exprt_vec(P,post_vars); + solver1<> Frames; + Frames.push_back(std::vector(P)); + int iter=0; + out<<"\n=======================IC3 iteration "<=0) + { + incremental_solvert solver2(ns); + solver2<\n\n"; + for(std::vector::iterator i=P_post.begin();i!=P_post.end();i++) + { + if(!(block_recursively(out,I,T,not_exprt(*i),Frames,iter,pre_vars,post_vars,ns,loophead))) + { + return; + } + } + } + else + { + Frames.push_back(std::vector()); + if(propagate_clauses(out,T,Frames,pre_vars,post_vars,iter,ns)) return; + int i=0; + while(i<=iter+1) + { + out<<"Frame"<& pre_vars,std::vector& post_vars,unsigned& loophead,exprt& I,exprt& T) +{ + std::vector conjuncts,conjuncts1; + unsigned loopend=0; + loophead=0; + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) + { + loophead=n_it->loophead->location->location_number; + loopend=n_it->location->location_number; + break; + } + } + if(loophead==loopend&&loopend==0) + { + out<<"No loop in the program\n"; + return false; + } + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->location->location_number==loophead) + { + exprt loop_exit_cond,temp_exp; + for(local_SSAt::nodet::equalitiest::const_iterator c_it=n_it->equalities.begin(); + c_it!=n_it->equalities.end(); + c_it++) + { + pre_vars.push_back((*c_it).op0()); + loop_exit_cond=temp_exp; + temp_exp=*c_it; + } + pre_vars.pop_back(); + pre_vars.pop_back(); + conjuncts.push_back(loop_exit_cond); + conjuncts.push_back(equal_exprt(temp_exp.op0(),true_exprt())); + } + } + exprt post_vars1[pre_vars.size()]; + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->location->location_numberequalities.begin(); + c_it!=n_it->equalities.end(); + c_it++) + { + conjuncts1.push_back(*c_it); + } + } + else if(n_it->location->location_number==loophead) + { + for(local_SSAt::nodet::equalitiest::const_iterator c_it=n_it->equalities.begin(); + c_it!=n_it->equalities.end(); + c_it++) + { + conjuncts1.push_back(*c_it); + } + local_SSAt::nodet::equalitiest::const_iterator c_it=n_it->equalities.begin(); + conjuncts1.pop_back(); + conjuncts1.pop_back(); + conjuncts1.push_back(equal_exprt(c_it->op1().op0(),false_exprt())); + I=conjunction(conjuncts1); + } + else if(n_it->location->location_number>loophead && n_it->location->location_number<=loopend) + { + for(local_SSAt::nodet::equalitiest::const_iterator c_it=n_it->equalities.begin(); + c_it!=n_it->equalities.end(); + c_it++) + { + int pos=is_var(pre_vars,from_expr(c_it->op0())); + if(pos!=-1) + { + post_vars1[pos]=c_it->op0(); + } + conjuncts.push_back(*c_it); + } + } + } + unsigned j; + for(j=0;j pre_vars,post_vars; + unsigned loophead=0; + exprt I,T; + if(!get_I_and_T(out,SSA,ns,pre_vars,post_vars,loophead,I,T)) return; + out<<"T:= "<>prop; + std::vector P; + if(!construct_cnf(P,out,prop,pre_vars,loophead)) + { + out<<"Error in the given property\n"; + return; + } + std::vector P_pre=to_exprt_vec(P,pre_vars); + out<<"\nP:= "<equalities.begin(); + exprt t=c_it->op1(); + n_it++; + n_it++; + c_it=n_it->equalities.begin(); + exprt f=c_it->op1(); + //constant_exprt t("00000001",pre_vars.at(0).type()),f("00000000",pre_vars.at(0).type()); + incremental_solvert solver(ns); + //solver<op0(),t)); + if(solver()==D_SATISFIABLE) + { + out<<"SAT"; + out<equalities.begin(); + c_it!=n_it->equalities.end(); + c_it++) + { + print_symbol_values(SSA,solver,out,*c_it); + } + } + incremental_solvert solver1(ns); + solver1<first << messaget::eom; - ssa_db.create(f_it->first, f_it->second, ns); + ssa_db.create(f_it->first, f_it->second, ns, heap_analysis); local_SSAt &SSA=ssa_db.get(f_it->first); // simplify, if requested @@ -75,10 +690,11 @@ void summary_checker_baset::SSA_functions( } SSA.output(debug()); debug() << eom; - } - + } +local_SSAt &SSA=ssa_db.get(dstring("main")); // properties initialize_property_map(goto_model.goto_functions); + CustomSSAOperation(SSA,ns,""); } /*******************************************************************\ @@ -228,6 +844,12 @@ void summary_checker_baset::check_properties( { solver << summary_db.get(f_it->first).fw_invariant; solver << summary_db.get(f_it->first).fw_precondition; + + if(options.get_bool_option("heap") && + summary_db.get(f_it->first).aux_precondition.is_not_nil()) + { + solver << summary_db.get(f_it->first).aux_precondition; + } } // callee summaries @@ -248,7 +870,7 @@ void summary_checker_baset::check_properties( SSA, solver, loophead_selects, property_map, !fully_unwound && options.get_bool_option("spurious-check"), all_properties, - options.get_bool_option("show-trace") || + options.get_bool_option("trace") || options.get_option("graphml-witness")!="" || options.get_option("json-cex")!=""); diff --git a/src/2ls/summary_checker_base.h b/src/2ls/summary_checker_base.h index d4b1ad0e5..6475517be 100644 --- a/src/2ls/summary_checker_base.h +++ b/src/2ls/summary_checker_base.h @@ -14,6 +14,7 @@ Author: Peter Schrammel #include #include +#include #include #include #include @@ -27,7 +28,9 @@ class graphml_witness_extt; class summary_checker_baset:public property_checkert { public: - explicit summary_checker_baset(optionst &_options): + summary_checker_baset( + optionst &_options, + const ssa_heap_analysist &_heap_analysis): show_vcc(false), simplify(false), fixed_point(false), @@ -35,6 +38,7 @@ class summary_checker_baset:public property_checkert ssa_db(_options), summary_db(), ssa_unwinder(ssa_db), ssa_inliner(summary_db), + heap_analysis(_heap_analysis), solver_instances(0), solver_calls(0), summaries_used(0), @@ -62,6 +66,8 @@ class summary_checker_baset:public property_checkert ssa_unwindert ssa_unwinder; ssa_inlinert ssa_inliner; + const ssa_heap_analysist &heap_analysis; + unsigned solver_instances; unsigned solver_calls; unsigned summaries_used; @@ -73,24 +79,31 @@ class summary_checker_baset:public property_checkert const goto_programt::const_targett, const local_SSAt::nodet::assertionst::const_iterator &); - void SSA_functions(const goto_modelt &, const namespacet &ns); + void SSA_functions( + const goto_modelt &, + const namespacet &ns, + const ssa_heap_analysist &heap_analysis); - void summarize( + virtual void summarize( const goto_modelt &, bool forward=true, bool termination=false); property_checkert::resultt check_properties(); - void check_properties( + virtual void check_properties( const ssa_dbt::functionst::const_iterator f_it); exprt::operandst get_loophead_selects( - const irep_idt &function_name, const local_SSAt &, prop_convt &); + const irep_idt &function_name, + const local_SSAt &, + prop_convt &); bool is_spurious( const exprt::operandst& loophead_selects, incremental_solvert&); exprt::operandst get_loop_continues( - const irep_idt &function_name, const local_SSAt &, prop_convt &); + const irep_idt &function_name, + const local_SSAt &, + prop_convt &); bool is_fully_unwound( const exprt::operandst& loop_continues, const exprt::operandst& loophead_selects, diff --git a/src/2ls/summary_checker_bmc.cpp b/src/2ls/summary_checker_bmc.cpp index 38e9e925e..4682e2cf8 100644 --- a/src/2ls/summary_checker_bmc.cpp +++ b/src/2ls/summary_checker_bmc.cpp @@ -26,7 +26,7 @@ property_checkert::resultt summary_checker_bmct::operator()( { const namespacet ns(goto_model.symbol_table); - SSA_functions(goto_model, ns); + SSA_functions(goto_model, ns, heap_analysis); ssa_unwinder.init(false, true); diff --git a/src/2ls/summary_checker_bmc.h b/src/2ls/summary_checker_bmc.h index 5885f6b36..907b873cf 100644 --- a/src/2ls/summary_checker_bmc.h +++ b/src/2ls/summary_checker_bmc.h @@ -14,8 +14,10 @@ Author: Peter Schrammel class summary_checker_bmct:public summary_checker_baset { public: - explicit summary_checker_bmct(optionst &_options): - summary_checker_baset(_options) + summary_checker_bmct( + optionst &_options, + const ssa_heap_analysist &heap_analysis): + summary_checker_baset(_options, heap_analysis) { } diff --git a/src/2ls/summary_checker_kind.cpp b/src/2ls/summary_checker_kind.cpp index 5c13a3a92..0fe1c9110 100644 --- a/src/2ls/summary_checker_kind.cpp +++ b/src/2ls/summary_checker_kind.cpp @@ -6,10 +6,9 @@ Author: Peter Schrammel \*******************************************************************/ +#include #include "summary_checker_kind.h" -#define GIVE_UP_INVARIANTS 4 - /*******************************************************************\ Function: summary_checker_kindt::operator() @@ -27,12 +26,14 @@ property_checkert::resultt summary_checker_kindt::operator()( { const namespacet ns(goto_model.symbol_table); - SSA_functions(goto_model, ns); + SSA_functions(goto_model, ns, heap_analysis); ssa_unwinder.init(true, false); property_checkert::resultt result=property_checkert::UNKNOWN; unsigned max_unwind=options.get_unsigned_int_option("unwind"); + unsigned give_up_invariants= + options.get_unsigned_int_option("give-up-invariants"); status() << "Max-unwind is " << max_unwind << eom; ssa_unwinder.init_localunwinders(); @@ -47,7 +48,7 @@ property_checkert::resultt summary_checker_kindt::operator()( result=check_properties(); bool magic_limit_not_reached= - unwind +#include +#include + +#include +#include <2ls/show.h> + +#include + +/*******************************************************************\ + +Function: summary_checker_nontermt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_nontermt::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model, ns, heap_analysis); + + ssa_unwinder.init(false, true); + + property_checkert::resultt result=property_checkert::UNKNOWN; + unsigned max_unwind=options.get_unsigned_int_option("unwind"); + status() << "Max-unwind is " << max_unwind << eom; + ssa_unwinder.init_localunwinders(); + + for(unsigned unwind=1; unwind<=max_unwind; unwind++) + { + status() << "Unwinding (k=" << unwind << ")" << messaget::eom; + ssa_unwinder.unwind_all(unwind); + if(unwind==51) // use a different nontermination technique + { + result=check_nonterm_linear(); + if(result==property_checkert::PASS) + { + status() << "Termination proved after " + << unwind << " unwinding(s)" << messaget::eom; + report_statistics(); + return result; + } + else if(result==property_checkert::FAIL) + { + status() << "Nonterminating program execution proved after " + << unwind << " unwinding(s)" << messaget::eom; + report_statistics(); + return result; + } + } + result=summary_checker_baset::check_properties(); + if(result==property_checkert::PASS) + { + status() << "Termination proved after " + << unwind << " unwinding(s)" << messaget::eom; + break; + } + else if(result==property_checkert::FAIL) + { + status() << "Nonterminating program execution proved after " + << unwind << " unwinding(s)" << messaget::eom; + break; + } + } + + report_statistics(); + return result; +} + +/*******************************************************************\ + +Function: summary_checker_baset::check_properties + + Inputs: + + Outputs: + + Purpose: checks the property loop instead of assertion + +\*******************************************************************/ + +void summary_checker_nontermt::check_properties( + const ssa_dbt::functionst::const_iterator f_it) +{ + unwindable_local_SSAt &SSA=*f_it->second; + + ssa_local_unwindert &ssa_local_unwinder=ssa_unwinder.get(f_it->first); + +#if 0 + SSA.output_verbose(std::cout); +#endif + + // solver + incremental_solvert &solver=ssa_db.get_solver(f_it->first); + solver.set_message_handler(get_message_handler()); + + // give SSA to solver + solver << SSA; + SSA.mark_nodes(); + + solver.new_context(); + + // freeze loop head selects + exprt::operandst loophead_selects= + get_loophead_selects(f_it->first, SSA, *solver.solver); + + exprt enabling_expr=SSA.get_enabling_exprs(); + solver << enabling_expr; + + cover_goals_extt cover_goals( + SSA, + solver, + loophead_selects, + property_map, + false, + false, + options.get_bool_option("trace") || + options.get_option("graphml-witness")!="" || + options.get_option("json-cex")!=""); + + property_map.clear(); + + exprt::operandst ls_guards; + + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + irep_idt property_id( + id2string(n_it->location->function)+"."+ + std::to_string(n_it->location->loop_number)+".term"); + + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), *n_it, true); + + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[n_it->loophead->location].phi_nodes; + + long store_unwinding=SSA.current_unwinding; + exprt::operandst loop_check_operands; + + symbol_exprt lhguard=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(lhguard, *n_it, false); + + for(SSA.current_unwinding=1; + SSA.current_unwinding<=store_unwinding; + SSA.current_unwinding++) + { + exprt::operandst loop_vars; + loop_vars.push_back(lhguard); + + for(local_SSAt::objectst::const_iterator + o_it=SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); + o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it= + phi_nodes.find(o_it->get_identifier()); + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop + + symbol_exprt post_var= + SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(post_var, *n_it->loophead, false); + + symbol_exprt phi_var; + phi_var=SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var, *n_it->loophead, true); + loop_vars.push_back(equal_exprt(post_var, phi_var)); + } + + loop_check_operands.push_back(conjunction(loop_vars)); + } + SSA.current_unwinding=store_unwinding; + + property_map[property_id].location=n_it->loophead->location; + property_map[property_id].result=UNKNOWN; + cover_goals.goal_map[property_id].conjuncts.push_back( + disjunction(loop_check_operands)); + ls_guards.push_back(not_exprt(lsguard)); + } + } + + solver << conjunction(ls_guards); + + for(cover_goals_extt::goal_mapt::const_iterator + it=cover_goals.goal_map.begin(); + it!=cover_goals.goal_map.end(); + it++) + { + literalt p=solver.convert(disjunction(it->second.conjuncts)); + cover_goals.add(p); + } + + status() << "Running " << solver.solver->decision_procedure_text() + << messaget::eom; + + cover_goals(); + + solver.pop_context(); + + status() << "** " << cover_goals.number_covered() + << " of " << cover_goals.size() << " failed (" + << cover_goals.iterations() << " iterations)" + << messaget::eom; +} + +/*******************************************************************\ + +Function: summary_checker_baset::check_properties_linear + + Inputs: + + Outputs: + + Purpose: searching for periodical recurrence set + +\*******************************************************************/ + +void summary_checker_nontermt::check_properties_linear( + const ssa_dbt::functionst::const_iterator f_it) +{ + unwindable_local_SSAt &SSA=*f_it->second; + + ssa_local_unwindert &ssa_local_unwinder=ssa_unwinder.get(f_it->first); + + // solver + incremental_solvert &solver=ssa_db.get_solver(f_it->first); + solver.set_message_handler(get_message_handler()); + + // give SSA to solver + solver << SSA; + SSA.mark_nodes(); + + solver.new_context(); + + // freeze loop head selects + exprt::operandst loophead_selects= + get_loophead_selects(f_it->first, SSA, *solver.solver); + + exprt enabling_expr=SSA.get_enabling_exprs(); + solver << enabling_expr; + + property_map.clear(); + + exprt::operandst ls_guards; + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), *n_it, true); + ls_guards.push_back(lsguard); + } + } + + unsigned loop_counter=std::numeric_limits::max(); + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + // we use continues further, therefore we put incrementation here + loop_counter++; + + irep_idt property_id( + id2string(n_it->location->function)+"."+ + std::to_string(n_it->location->loop_number)+".term"); + + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[n_it->loophead->location].phi_nodes; + + SSA.current_unwinding-=1; + symbol_exprt lhguard_second=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(lhguard_second, *n_it, true); + SSA.current_unwinding+=1; + + symbol_exprt lhguard=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(lhguard, *n_it, true); + + exprt::operandst first_linearity_check; + exprt::operandst second_linearity_check; + exprt::operandst constants_computation; + exprt::operandst constants; + exprt::operandst loopback_vars; + exprt::operandst loop_exit_cond; + exprt::operandst neg_loop_exit_cond; + + property_map[property_id].location=n_it->loophead->location; + property_map[property_id].result=UNKNOWN; + + unsigned const_number=0; + for(local_SSAt::objectst::const_iterator + o_it=SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); + o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it= + phi_nodes.find(o_it->get_identifier()); + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop + + // first linearity check data preparation + + SSA.current_unwinding-=1; + symbol_exprt phi_var1; + phi_var1=SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var1, *n_it->loophead, true); + SSA.current_unwinding+=1; + symbol_exprt phi_var2; + phi_var2=SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var2, *n_it->loophead, true); + + // works only for bitvectors + + if((phi_var2.type().id()!=ID_unsignedbv) && + (phi_var2.type().id()!=ID_signedbv)) + { + first_linearity_check.clear(); + break; + } + + // x_k = x_0 + C*k, const$k - k, const$i - C + symbol_exprt constk("const$k", phi_var2.type()); + symbol_exprt const1( + "const$"+std::to_string(const_number++), phi_var2.type()); + + first_linearity_check.push_back( + equal_exprt( + phi_var1, + plus_exprt( + phi_var2, + const1))); + + // get constants data preparation + + constants_computation.push_back( + equal_exprt( + minus_exprt(phi_var1, phi_var2), + const1)); + constants.push_back(const1); + + // loopback vars data preparation + + exprt init_expr; + exprt lb_var; + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + n_it->loophead->equalities.begin(); + e_it!=n_it->loophead->equalities.end(); e_it++) + { + if(e_it->rhs().id()==ID_if && + to_symbol_expr(e_it->lhs()).get_identifier()== + phi_var2.get_identifier()) + { + const if_exprt &if_expr=to_if_expr(e_it->rhs()); + init_expr=if_expr.false_case(); + lb_var=if_expr.true_case(); + // should already be renamed for inner loops + break; + } + } + + loopback_vars.push_back(lb_var); + loopback_vars.push_back(init_expr); + loopback_vars.push_back(constk); + } + + if(first_linearity_check.empty()) // nothing to be checked + continue; + + neg_loop_exit_cond.push_back(lhguard); + neg_loop_exit_cond.push_back(not_exprt(lhguard_second)); + loop_exit_cond.push_back(lhguard); + + solver.new_context(); + solver << conjunction(first_linearity_check); + switch(solver()) + { + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + continue; + + case decision_proceduret::D_SATISFIABLE: + for(exprt::operandst::iterator it=first_linearity_check.begin(); + it!=first_linearity_check.end(); + ++it) + { + exprt ex=solver.get(it->op1().op1()); + second_linearity_check.push_back( + and_exprt( + *it, + not_exprt(equal_exprt(to_constant_expr(ex), it->op1().op1())))); + } + + solver.pop_context(); + break; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + return; + } + + solver.new_context(); + solver << disjunction(second_linearity_check); + switch(solver()) + { + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + break; + + case decision_proceduret::D_SATISFIABLE: + solver.pop_context(); + continue; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + return; + } + + // constants extraction + exprt::operandst solver_consts; + + solver.new_context(); + solver << conjunction(constants_computation); + switch(solver()) + { + case decision_proceduret::D_UNSATISFIABLE: // should never happen + solver.pop_context(); + solver.pop_context(); + return; + + case decision_proceduret::D_SATISFIABLE: + for(auto constant : constants) + { + exprt ex=solver.solver->get(constant); + if(ex.type().id()==ID_unsignedbv) + { + // if (constant>UINT_MAX/2)?0-constant:constant + constant_exprt cex=to_constant_expr(ex); + constant_exprt zero=constant_exprt("0", ex.type()); + constant_exprt cex2= + to_constant_expr(simplify_expr(minus_exprt(zero, cex), SSA.ns)); + if_exprt ifex=if_exprt( + binary_relation_exprt(cex, ID_gt, cex2), + cex2, + cex, + ex.type()); + ex=simplify_expr(ifex, SSA.ns); + } + solver_consts.push_back(to_constant_expr((ex))); + } + solver.pop_context(); + break; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + return; + } + + // loop exit conditions satisfiable check + + solver.new_context(); + + for(unsigned i=0; iget(*(lbv_it+1)); + + // TODO: make this in different way + if(!from_expr(SSA.ns, "", *slvc_it).compare("0")) + { + eq_expr=equal_exprt( + *(lbv_it+1), + to_constant_expr( + simplify_expr( + old_xinit, + SSA.ns))); + } + else + { + // Exists xinit % const != old_xinit % const + eq_expr=equal_exprt( + mod_exprt(*(lbv_it+1), *slvc_it), + mod_exprt( + to_constant_expr(simplify_expr(old_xinit, SSA.ns)), + *slvc_it)); + } + local_constraints.push_back(eq_expr); + ++slvc_it; + lbv_it+=3; + } + constraints.push_back(not_exprt(conjunction(local_constraints))); + solver << not_exprt(conjunction(local_constraints)); + break; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + solver.pop_context(); + return; + } + } + while(result!=decision_proceduret::D_UNSATISFIABLE); + + solver.pop_context(); + + solver << conjunction(loop_exit_cond); + solver << conjunction(constraints); + + switch(solver()) + { + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + break; + + case decision_proceduret::D_SATISFIABLE: + // found nontermination + property_map[property_id].result=FAIL; + solver.pop_context(); + solver.pop_context(); + return; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + return; + } + } + } + solver.pop_context(); +} + +/*******************************************************************\ + +Function: summary_checker_nontermt::check_nonterm_linear + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +summary_checker_baset::resultt summary_checker_nontermt::check_nonterm_linear() +{ + // analyze all the functions + for(ssa_dbt::functionst::const_iterator f_it=ssa_db.functions().begin(); + f_it!=ssa_db.functions().end(); f_it++) + { + status() << "Checking properties of " << f_it->first << messaget::eom; + + check_properties_linear(f_it); + } + + summary_checker_baset::resultt result=property_checkert::PASS; + for(property_mapt::const_iterator + p_it=property_map.begin(); p_it!=property_map.end(); p_it++) + { + if(p_it->second.result==FAIL) + return property_checkert::FAIL; + if(p_it->second.result==UNKNOWN) + result=property_checkert::UNKNOWN; + } + + return result; +} diff --git a/src/2ls/summary_checker_nonterm.h b/src/2ls/summary_checker_nonterm.h new file mode 100644 index 000000000..d8a87afa3 --- /dev/null +++ b/src/2ls/summary_checker_nonterm.h @@ -0,0 +1,36 @@ +/*******************************************************************\ + +Module: Summarizer for Nontermination Bit-level analysis + +Author: Stefan Marticek + +\*******************************************************************/ + +#ifndef CPROVER_2LS_2LS_SUMMARY_CHECKER_NONTERM_H +#define CPROVER_2LS_2LS_SUMMARY_CHECKER_NONTERM_H + +#include "summary_checker_base.h" + +class summary_checker_nontermt:public summary_checker_baset +{ +public: + explicit summary_checker_nontermt( + optionst &_options, + const ssa_heap_analysist &heap_analysis): + summary_checker_baset(_options, heap_analysis) + { + } + + virtual resultt operator()(const goto_modelt &); + + void check_properties( + const ssa_dbt::functionst::const_iterator f_it); + + void check_properties_linear( + const ssa_dbt::functionst::const_iterator f_it); + +protected: + summary_checker_baset::resultt check_nonterm_linear(); +}; + +#endif // CPROVER_2LS_2LS_SUMMARY_CHECKER_NONTERM_H diff --git a/src/2ls/summary_checker_rect.cpp b/src/2ls/summary_checker_rect.cpp new file mode 100644 index 000000000..5e69d0d25 --- /dev/null +++ b/src/2ls/summary_checker_rect.cpp @@ -0,0 +1,108 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +/* + * File: summary_checker_rect.cpp + * Author: sarbojit + * + * Created on 21 March, 2018, 7:13 PM + */ + +#include +#include +#include +#include +#include + +#include "summary_checker_rect.h" + +property_checkert::resultt summary_checker_rect::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model, ns, heap_analysis); + + ssa_unwinder.init(false, false); + + unsigned unwind=options.get_unsigned_int_option("unwind"); + if(unwind>0) + { + status() << "Unwinding" << messaget::eom; + + ssa_unwinder.init_localunwinders(); + + ssa_unwinder.unwind_all(unwind); + } + + // properties + initialize_property_map(goto_model.goto_functions); + + bool preconditions=options.get_bool_option("preconditions"); + bool termination=options.get_bool_option("termination"); + bool trivial_domain=options.get_bool_option("havoc"); + if(!trivial_domain || termination) + { + // forward analysis + summarize(goto_model, true, termination); + } + /*if(preconditions) + { + report_statistics(); + report_preconditions(); + return property_checkert::UNKNOWN; + } + + if(termination) + { + report_statistics(); + return report_termination(); + } + +#ifdef SHOW_CALLINGCONTEXTS + if(options.get_bool_option("show-calling-contexts")) + return property_checkert::UNKNOWN; +#endif + + property_checkert::resultt result=check_properties(); + report_statistics(); + return result;*/ + return UNKNOWN; +} + +void summary_checker_rect::summarize( + const goto_modelt &goto_model, + bool forward, + bool termination) +{ + summarizer_baset *summarizer=NULL; +#ifdef SHOW_CALLING_CONTEXTS + if(options.get_bool_option("show-calling-contexts")) + summarizer=new summarizer_fw_contextst( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); + else // NOLINT(*) +#endif + { + if(forward && !termination) + summarizer=new summarizer_rec_fwt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); + } + assert(summarizer!=NULL); + summarizer->set_message_handler(get_message_handler()); + if(!options.get_bool_option("context-sensitive") && + options.get_bool_option("all-functions")) + summarizer->summarize(); + else + summarizer->summarize(goto_model.goto_functions.entry_point()); + + // statistics + /*solver_instances+=summarizer->get_number_of_solver_instances(); + solver_calls+=summarizer->get_number_of_solver_calls(); + summaries_used+=summarizer->get_number_of_summaries_used(); + termargs_computed+=summarizer->get_number_of_termargs_computed();*/ + + delete summarizer; +} diff --git a/src/2ls/summary_checker_rect.h b/src/2ls/summary_checker_rect.h new file mode 100644 index 000000000..9e70c52e6 --- /dev/null +++ b/src/2ls/summary_checker_rect.h @@ -0,0 +1,28 @@ +/* + * File: summary_checker_rect.h + * Author: sarbojit + * + * Created on 21 March, 2018, 7:13 PM + */ + +#ifndef SUMMARY_CHECKER_RECT_H +#define SUMMARY_CHECKER_RECT_H + +#include "summary_checker_ai.h" + +class summary_checker_rect:public summary_checker_ait +{ +public: + explicit summary_checker_rect(optionst &_options, + const ssa_heap_analysist &heap_analysis): + summary_checker_ait(_options, heap_analysis) + { + } + + virtual resultt operator()(const goto_modelt &); +protected: + void summarize(const goto_modelt &, bool, bool); +}; + +#endif /* SUMMARY_CHECKER_RECT_H */ + diff --git a/src/2ls/version.h b/src/2ls/version.h index 132ef4309..fb5db29f8 100644 --- a/src/2ls/version.h +++ b/src/2ls/version.h @@ -9,6 +9,6 @@ Author: Peter Schrammel #ifndef CPROVER_2LS_2LS_VERSION_H #define CPROVER_2LS_2LS_VERSION_H -#define TWOLS_VERSION "0.5.1" +#define TWOLS_VERSION "0.6.0" #endif diff --git a/src/config.inc.bak b/src/config.inc.bak new file mode 100644 index 000000000..236bfee5e --- /dev/null +++ b/src/config.inc.bak @@ -0,0 +1,10 @@ +CBMC = ~/my-cbmc + +# Variables you may want to override +#CXXFLAGS = -Wall -O0 -g -Werror -Wno-long-long -Wno-sign-compare -Wno-parentheses -Wno-strict-aliasing -pedantic +CXXFLAGS = -g -MF a.dep +#static compilation +#LINKFLAGS += -static-libgcc -static-libstdc++ + +#2LS switches +TWOLS_FLAGS = diff --git a/src/config.inc.template b/src/config.inc.template index 002688fa8..236bfee5e 100644 --- a/src/config.inc.template +++ b/src/config.inc.template @@ -2,7 +2,7 @@ CBMC = ~/my-cbmc # Variables you may want to override #CXXFLAGS = -Wall -O0 -g -Werror -Wno-long-long -Wno-sign-compare -Wno-parentheses -Wno-strict-aliasing -pedantic - +CXXFLAGS = -g -MF a.dep #static compilation #LINKFLAGS += -static-libgcc -static-libstdc++ diff --git a/src/domains/Makefile b/src/domains/Makefile index 82b6a23b8..02667c180 100644 --- a/src/domains/Makefile +++ b/src/domains/Makefile @@ -1,4 +1,6 @@ -SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ +SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp \ + predabs_domain.cpp heap_domain.cpp list_iterator.cpp \ + heap_interval_domain.cpp \ ssa_analyzer.cpp util.cpp incremental_solver.cpp \ strategy_solver_base.cpp strategy_solver_equality.cpp \ linrank_domain.cpp lexlinrank_domain.cpp\ @@ -7,7 +9,8 @@ SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ template_generator_base.cpp template_generator_summary.cpp \ template_generator_callingcontext.cpp template_generator_ranking.cpp \ strategy_solver_binsearch2.cpp strategy_solver_binsearch3.cpp \ - strategy_solver_predabs.cpp + strategy_solver_predabs.cpp strategy_solver_heap.cpp \ + strategy_solver_heap_interval.cpp template_gen_rec_summary.cpp #solver_enumeration.cpp include ../config.inc diff --git a/src/domains/a.dep b/src/domains/a.dep new file mode 100644 index 000000000..3247da043 --- /dev/null +++ b/src/domains/a.dep @@ -0,0 +1,326 @@ +ssa_analyzer.o: ssa_analyzer.cpp \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck_glucose.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/cnf.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h \ + /home/sarbojit/2ls-master/cbmc/src/util/message.h \ + /home/sarbojit/2ls-master/cbmc/src/util/source_location.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep.h \ + /home/sarbojit/2ls-master/cbmc/src/util/dstring.h \ + /home/sarbojit/2ls-master/cbmc/src/util/string_container.h \ + /home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h \ + /home/sarbojit/2ls-master/cbmc/src/util/string_hash.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep_ids.h \ + /home/sarbojit/2ls-master/cbmc/src/util/threeval.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_assignment.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_pointers.h \ + /home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv.h \ + /home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h \ + /home/sarbojit/2ls-master/cbmc/src/big-int/bigint.hh \ + /home/sarbojit/2ls-master/cbmc/src/util/expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/type.h \ + /home/sarbojit/2ls-master/cbmc/src/util/source_location.h \ + /home/sarbojit/2ls-master/cbmc/src/util/byte_operators.h \ + /home/sarbojit/2ls-master/cbmc/src/util/expr.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_utils.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_width.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_types.h \ + /home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h \ + /home/sarbojit/2ls-master/cbmc/src/util/namespace.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_map.h \ + /home/sarbojit/2ls-master/cbmc/src/util/type.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_type.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/arrays.h \ + /home/sarbojit/2ls-master/cbmc/src/util/union_find.h \ + /home/sarbojit/2ls-master/cbmc/src/util/numbering.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/equality.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_conv.h \ + /home/sarbojit/2ls-master/cbmc/src/util/decision_procedure.h \ + /home/sarbojit/2ls-master/cbmc/src/util/message.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_types.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/functions.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/pointer_logic.h \ + /home/sarbojit/2ls-master/cbmc/src/util/numbering.h \ + /home/sarbojit/2ls-master/cbmc/src/util/find_symbols.h \ + /home/sarbojit/2ls-master/cbmc/src/util/arith_tools.h \ + /home/sarbojit/2ls-master/cbmc/src/util/simplify_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/options.h strategy_solver_base.h \ + domain.h /home/sarbojit/2ls-master/cbmc/src/util/i2string.h \ + /home/sarbojit/2ls-master/cbmc/src/langapi/language_util.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep.h \ + /home/sarbojit/2ls-master/cbmc/src/util/replace_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_expr.h incremental_solver.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/refinement/bv_refinement.h \ + /home/sarbojit/2ls-master/cbmc/src/langapi/language_ui.h \ + /home/sarbojit/2ls-master/cbmc/src/util/language_file.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol_table.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol.h \ + /home/sarbojit/2ls-master/cbmc/src/util/ui_message.h util.h \ + /home/sarbojit/2ls-master/cbmc/src/util/ieee_float.h \ + /home/sarbojit/2ls-master/cbmc/src/util/format_spec.h \ + strategy_solver_enumeration.h tpolyhedra_domain.h \ + strategy_solver_binsearch.h strategy_solver_binsearch2.h \ + strategy_solver_binsearch3.h ../ssa/local_ssa.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_code.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program_template.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions_template.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol.h \ + ../domains/list_iterator.h ../domains/incremental_solver.h \ + ../ssa/ssa_domain.h /home/sarbojit/2ls-master/cbmc/src/analyses/ai.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h \ + ../ssa/assignments.h ../ssa/ssa_object.h ../ssa/ssa_pointed_objects.h \ + ../ssa/ssa_heap_domain.h \ + /home/sarbojit/2ls-master/cbmc/src/analyses/static_analysis.h \ + ../ssa/ssa_value_set.h ../ssa/guard_map.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h \ + ../ssa/may_alias_analysis.h strategy_solver_equality.h equality_domain.h \ + linrank_domain.h lexlinrank_domain.h ranking_solver_enumeration.h \ + lexlinrank_solver_enumeration.h ../domains/incremental_solver.h \ + template_generator_ranking.h template_generator_base.h \ + ../ssa/ssa_unwinder.h ../ssa/unwindable_local_ssa.h ../ssa/local_ssa.h \ + ../ssa/ssa_db.h ../ssa/unwindable_local_ssa.h strategy_solver_predabs.h \ + predabs_domain.h ssa_analyzer.h ../solver/summary.h \ + strategy_solver_heap.h heap_domain.h strategy_solver_heap_interval.h \ + heap_interval_domain.h + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck_glucose.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/cnf.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h: + +/home/sarbojit/2ls-master/cbmc/src/util/message.h: + +/home/sarbojit/2ls-master/cbmc/src/util/source_location.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep.h: + +/home/sarbojit/2ls-master/cbmc/src/util/dstring.h: + +/home/sarbojit/2ls-master/cbmc/src/util/string_container.h: + +/home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h: + +/home/sarbojit/2ls-master/cbmc/src/util/string_hash.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep_ids.h: + +/home/sarbojit/2ls-master/cbmc/src/util/threeval.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_assignment.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_pointers.h: + +/home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv.h: + +/home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h: + +/home/sarbojit/2ls-master/cbmc/src/big-int/bigint.hh: + +/home/sarbojit/2ls-master/cbmc/src/util/expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/type.h: + +/home/sarbojit/2ls-master/cbmc/src/util/source_location.h: + +/home/sarbojit/2ls-master/cbmc/src/util/byte_operators.h: + +/home/sarbojit/2ls-master/cbmc/src/util/expr.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_utils.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_width.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_types.h: + +/home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h: + +/home/sarbojit/2ls-master/cbmc/src/util/namespace.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_map.h: + +/home/sarbojit/2ls-master/cbmc/src/util/type.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_type.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/arrays.h: + +/home/sarbojit/2ls-master/cbmc/src/util/union_find.h: + +/home/sarbojit/2ls-master/cbmc/src/util/numbering.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/equality.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_conv.h: + +/home/sarbojit/2ls-master/cbmc/src/util/decision_procedure.h: + +/home/sarbojit/2ls-master/cbmc/src/util/message.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_types.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/functions.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/pointer_logic.h: + +/home/sarbojit/2ls-master/cbmc/src/util/numbering.h: + +/home/sarbojit/2ls-master/cbmc/src/util/find_symbols.h: + +/home/sarbojit/2ls-master/cbmc/src/util/arith_tools.h: + +/home/sarbojit/2ls-master/cbmc/src/util/simplify_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/options.h: + +strategy_solver_base.h: + +domain.h: + +/home/sarbojit/2ls-master/cbmc/src/util/i2string.h: + +/home/sarbojit/2ls-master/cbmc/src/langapi/language_util.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep.h: + +/home/sarbojit/2ls-master/cbmc/src/util/replace_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_expr.h: + +incremental_solver.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/refinement/bv_refinement.h: + +/home/sarbojit/2ls-master/cbmc/src/langapi/language_ui.h: + +/home/sarbojit/2ls-master/cbmc/src/util/language_file.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol_table.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol.h: + +/home/sarbojit/2ls-master/cbmc/src/util/ui_message.h: + +util.h: + +/home/sarbojit/2ls-master/cbmc/src/util/ieee_float.h: + +/home/sarbojit/2ls-master/cbmc/src/util/format_spec.h: + +strategy_solver_enumeration.h: + +tpolyhedra_domain.h: + +strategy_solver_binsearch.h: + +strategy_solver_binsearch2.h: + +strategy_solver_binsearch3.h: + +../ssa/local_ssa.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_code.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program_template.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions_template.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol.h: + +../domains/list_iterator.h: + +../domains/incremental_solver.h: + +../ssa/ssa_domain.h: + +/home/sarbojit/2ls-master/cbmc/src/analyses/ai.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h: + +../ssa/assignments.h: + +../ssa/ssa_object.h: + +../ssa/ssa_pointed_objects.h: + +../ssa/ssa_heap_domain.h: + +/home/sarbojit/2ls-master/cbmc/src/analyses/static_analysis.h: + +../ssa/ssa_value_set.h: + +../ssa/guard_map.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h: + +../ssa/may_alias_analysis.h: + +strategy_solver_equality.h: + +equality_domain.h: + +linrank_domain.h: + +lexlinrank_domain.h: + +ranking_solver_enumeration.h: + +lexlinrank_solver_enumeration.h: + +../domains/incremental_solver.h: + +template_generator_ranking.h: + +template_generator_base.h: + +../ssa/ssa_unwinder.h: + +../ssa/unwindable_local_ssa.h: + +../ssa/local_ssa.h: + +../ssa/ssa_db.h: + +../ssa/unwindable_local_ssa.h: + +strategy_solver_predabs.h: + +predabs_domain.h: + +ssa_analyzer.h: + +../solver/summary.h: + +strategy_solver_heap.h: + +heap_domain.h: + +strategy_solver_heap_interval.h: + +heap_interval_domain.h: diff --git a/src/domains/domain.h b/src/domains/domain.h index cdc10fa83..ffed1d66c 100644 --- a/src/domains/domain.h +++ b/src/domains/domain.h @@ -39,7 +39,7 @@ class domaint typedef std::vector var_listt; typedef std::set var_sett; - typedef enum {LOOP, IN, OUT, OUTL} kindt; + typedef enum {LOOP, IN, OUT, OUTL, OUTHEAP} kindt; typedef exprt guardt; @@ -65,6 +65,9 @@ class domaint }; virtual void initialize(valuet &value) { value.basic_value=valuet::BOTTOM; } + virtual void initialize_in_templates(valuet &value, + std::map context_bounds= + std::map()) {assert(false);} // returns true as long as further refinements are possible virtual void reset_refinements() { } diff --git a/src/domains/heap_domain.cpp b/src/domains/heap_domain.cpp new file mode 100644 index 000000000..46a535d70 --- /dev/null +++ b/src/domains/heap_domain.cpp @@ -0,0 +1,1533 @@ +/*******************************************************************\ + +Module: Abstract domain for representing heap + +Author: Viktor Malik + +\*******************************************************************/ + +#include "heap_domain.h" +#include +#include + +/*******************************************************************\ + +Function: heap_domaint::initialize + + Inputs: + + Outputs: + + Purpose: Initialize abstract value. + Clears value with empty value rows corresponding to template. + +\*******************************************************************/ + +void heap_domaint::initialize(domaint::valuet &value) +{ + heap_valuet &val=static_cast(value); + + for(const template_rowt &templ_row : templ) + { + if(templ_row.mem_kind==STACK) + val.emplace_back(new stack_row_valuet()); + else if(templ_row.mem_kind==HEAP) + val.emplace_back( + new heap_row_valuet( + std::make_pair( + templ_row.dyn_obj, + templ_row.expr))); + else + assert(false); + } +} + +/*******************************************************************\ + +Function: heap_domaint::make_template + + Inputs: + + Outputs: + + Purpose: Create domain template for given set of variables. + Template contains a row for each pointer-typed variable and + field of a dynamic object. + +\*******************************************************************/ + +void heap_domaint::make_template( + const domaint::var_specst &var_specs, + const namespacet &ns) +{ + unsigned long size=var_specs.size(); + templ.clear(); + templ.reserve(size); + + for(const var_spect &v : var_specs) + { + if(v.kind==IN) + continue; + + // Create template for each pointer + const vart &var=v.var; + if(var.type().id()==ID_pointer) + { + const typet &pointed_type=ns.follow(var.type().subtype()); + add_template_row(v, pointed_type); + } + } +} + +/*******************************************************************\ + +Function: heap_domaint::add_template_row + + Inputs: var_spec Variable specification + + Outputs: + + Purpose: Add a template row. + +\*******************************************************************/ + +void heap_domaint::add_template_row( + const var_spect &var_spec, + const typet &pointed_type) +{ + const vart &var=var_spec.var; + + templ.push_back(template_rowt()); + template_rowt &templ_row=templ.back(); + templ_row.expr=var; + templ_row.pre_guard=var_spec.pre_guard; + templ_row.post_guard=var_spec.post_guard; + templ_row.aux_expr=var_spec.aux_expr; + templ_row.kind=var_spec.kind; + + templ_row.mem_kind=STACK; + if(pointed_type.id()==ID_struct) + { + // Check if var is member field of heap object + const std::string identifier=id2string( + to_symbol_expr(var_spec.var).get_identifier()); + for(auto &component : to_struct_type(pointed_type).components()) + { + if(identifier.find("."+id2string(component.get_name()))!= + std::string::npos) + { + templ_row.mem_kind=HEAP; + templ_row.member=component.get_name(); + + std::string var_id=id2string(to_symbol_expr(var).get_identifier()); + std::string do_id=var_id.substr(0, var_id.find_last_of('.')); + templ_row.dyn_obj=symbol_exprt(do_id, var.type().subtype()); + } + } + } +} + +/*******************************************************************\ + +Function: heap_domaint::to_pre_constraints + + Inputs: + + Outputs: Entry constraints expression for a value. + + Purpose: Create entry constraints as a conjuction of entry + expressions for each template row. + +\*******************************************************************/ + +exprt heap_domaint::to_pre_constraints(const heap_valuet &value) const +{ + assert(value.size()==templ.size()); + exprt::operandst c; + for(rowt row=0; row(value[from]); + heap_row_valuet &heap_val_to=static_cast(value[to]); + + bool result=false; + if(heap_val_from.add_all_paths( + heap_val_to, + std::make_pair(templ[to].dyn_obj, templ[to].expr))) + { + result=true; + } + if(from!=to) + { + if(heap_val_to.add_pointed_by(from)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::add_points_to + + Inputs: + + Outputs: + + Purpose: Add new object pointed by a row. + Calls add_points_to of the given row. + For stack rows, the destination is simply added into pointed + objects set. + For heap rows, a new path is added. + +\*******************************************************************/ + +bool heap_domaint::add_points_to( + const rowt &row, + heap_valuet &value, + const exprt &dest) +{ + assert(row(value); + for(rowt row=0; row " << std::endl + << " "; + break; + case IN: + out << "(IN) "; + break; + case OUT: + case OUTL: + out << "(OUT) "; + break; + case OUTHEAP: + out << "(HEAP) "; + break; + default: + assert(false); + } + out << "( " << from_expr(ns, "", templ_row.expr) << " == " + << from_expr(ns, "", val[row].get_row_expr(templ_row.expr, false)) + << " )" + << std::endl; + } +} + +/*******************************************************************\ + +Function: heap_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_domaint::output_domain(std::ostream &out, const namespacet &ns) const +{ + for(unsigned i=0; i " << std::endl << " "; + break; + case IN: + out << "(IN) "; + out << from_expr(ns, "", templ_row.pre_guard) << " ===> " + << std::endl << " "; + break; + case OUT: + case OUTL: + out << "(OUT) "; + out << from_expr(ns, "", templ_row.post_guard) << " ===> " + << std::endl << " "; + break; + case OUTHEAP: + out << "(HEAP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) + << " ] ===> " << std::endl << " "; + break; + default: + assert(false); + } + const vart &var=templ_row.expr; + + const std::string info= + templ_row.mem_kind==STACK + ? " --points_to--> Locations" + : " --paths--> Destinations"; + out << i << ": " << from_expr(ns, "", var) << info << std::endl; + } +} + +/*******************************************************************\ + +Function: heap_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_domaint::project_on_vars( + domaint::valuet &value, + const domaint::var_sett &vars, + exprt &result) +{ + const heap_valuet &val=static_cast(value); + assert(val.size()==templ.size()); + + exprt::operandst c; + for(rowt row=0; row(value1); + const heap_valuet &val2=static_cast(value2); + assert(val1.size()==templ.size()); + assert(val2.size()==val1.size()); +} + +/*******************************************************************\ + +Function: heap_domaint::get_symbol_loc + + Inputs: Symbol expression. + + Outputs: Number of location, or -1 if symbol is input. + + Purpose: Get location number of a given symbol. + +\*******************************************************************/ + +int heap_domaint::get_symbol_loc(const exprt &expr) +{ + assert(expr.id()==ID_symbol); + std::string expr_id=id2string(to_symbol_expr(expr).get_identifier()); + if(expr_id.find('#')==std::string::npos) + return -1; + std::string loc_str=expr_id.substr(expr_id.find_last_not_of("0123456789")+1); + assert(!loc_str.empty()); + return std::stoi(loc_str); +} + +/*******************************************************************\ + +Function: heap_domaint::stack_row_valuet::get_row_expr + + Inputs: templ_expr Template expression + + Outputs: Formula corresponding to the template row + + Purpose: Stack row is a disjuction of equalities between templ_expr + and addresses of dynamic objects from points_to set. + +\*******************************************************************/ + +exprt heap_domaint::stack_row_valuet::get_row_expr( + const vart &templ_expr, + bool rename_templ_expr) const +{ + if(nondet) + return true_exprt(); + + if(empty()) + return false_exprt(); + else + { + // Points to expression + exprt::operandst result; + for(const exprt &pt : points_to) + { + result.push_back( + equal_exprt( + templ_expr, + templ_expr.type()==pt.type() ? pt : address_of_exprt(pt))); + } + return disjunction(result); + } +} + +/*******************************************************************\ + +Function: heap_domaint::stack_row_valuet::add_points_to + + Inputs: + + Outputs: + + Purpose: Add new object to the value of a stack row. The object is + simply added to the set. + +\*******************************************************************/ + +bool heap_domaint::stack_row_valuet::add_points_to(const exprt &expr) +{ + if(points_to.find(expr)==points_to.end()) + points_to.insert(expr); + else + nondet=true; + return true; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::get_row_expr + + Inputs: templ_expr Template expression + rename_templ_expr True if templ_expr should be renamed + (the corresponding template row is of + OUTHEAP type) + + Outputs: Formula corresponding to the template row + + Purpose: Heap row is disjunction of path sets, where each path set is a conjunction of paths. + + nondet is TRUE + empty is FALSE + +\*******************************************************************/ + +exprt heap_domaint::heap_row_valuet::get_row_expr( + const vart &templ_expr_, + bool rename_templ_expr) const +{ + if(nondet) + return true_exprt(); + + exprt templ_expr=templ_expr_; + if(rename_templ_expr) + templ_expr=rename_outheap(to_symbol_expr(templ_expr_)); + + if(paths.empty()) + { + if(self_linkage) + { + return equal_exprt(templ_expr, address_of_exprt(dyn_obj.first)); + } + else + return false_exprt(); + } + else + { + exprt::operandst result; + for(const pathsett &path_set : paths) + { + exprt::operandst path_set_expr; + for(const patht &path : path_set) + { // path(o.m, d)[O] + const exprt &dest=templ_expr.type()==path.destination.type() ? + path.destination : address_of_exprt(path.destination); + exprt::operandst path_expr; + + // o.m = d + path_expr.push_back(equal_exprt(templ_expr, dest)); + + for(const dyn_objt &obj1 : path.dyn_objects) + { + // o.m = &o' + exprt equ_exprt=equal_exprt(templ_expr, address_of_exprt(obj1.first)); + + exprt::operandst steps_expr; + exprt member_expr=obj1.second; + // o'.m = d + steps_expr.push_back(equal_exprt(member_expr, dest)); + + for(const dyn_objt &obj2 : path.dyn_objects) + { + // o'.m = o'' + steps_expr.push_back( + equal_exprt( + member_expr, + address_of_exprt(obj2.first))); + } + + path_expr.push_back(and_exprt(equ_exprt, disjunction(steps_expr))); + } + + path_set_expr.push_back(disjunction(path_expr)); + } + result.push_back(conjunction(path_set_expr)); + } + return disjunction(result); + } +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_points_to + + Inputs: + + Outputs: + + Purpose: Add new object to heap row - create new path, or set + self_linkage flag in case the object is same as the row + object. + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_points_to(const exprt &dest) +{ + if(dest==dyn_obj.first) + { + if(!add_self_linkage()) + nondet=true; + } + else + { + const dyn_objt through= + self_linkage ? dyn_obj : std::make_pair(nil_exprt(), nil_exprt()); + if(!add_path(dest, through)) + nondet=true; + } + return true; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_path + + Inputs: dest Path destination + dyn_obj Dynamic object that the path goes through + + Outputs: True if the value was changed (a path was added) + + Purpose: Add new path set if any path set does not contain + the given destination. + + If any path set already contains dest, do nothing since + the pathset will be updated from other rows. + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_path( + const exprt &dest, + const dyn_objt &dyn_obj) +{ + bool new_path=true; + for(const pathsett &path_set : paths) + { + auto p_it=path_set.find(dest); + if(p_it!=path_set.end() && p_it->dyn_objects.empty()) + { + new_path=false; + break; + } + } + if(new_path) + { + pathsett new_path_set; + std::set dyn_obj_set; + if(dyn_obj.first.id()!=ID_nil) + { + dyn_obj_set.insert(dyn_obj); + } + if(self_linkage) + { + dyn_obj_set.insert(this->dyn_obj); + } + new_path_set.emplace(dest, dyn_obj_set); + paths.push_back(new_path_set); + } + return new_path; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_path + + Inputs: dest Path destination + dyn_obj Dynamic object that the path goes through + path_set Path set to add the path to + + Outputs: True if the value was changed (a path was added) + + Purpose: Add new path to a path set + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_path( + const exprt &dest, + const dyn_objt &dyn_obj, + pathsett &path_set) +{ + if(path_set.find(dest)==path_set.end()) + { + // Path does not exist yet + std::set dyn_obj_set; + if(dyn_obj.first.id()!=ID_nil) + { // Path doesn't have zero length + dyn_obj_set.insert(dyn_obj); + } + if(self_linkage) + { + dyn_obj_set.insert(this->dyn_obj); + } + path_set.emplace(dest, dyn_obj_set); + return true; + } + else + { + // Path exists already + if(dyn_obj.first.id()!=ID_nil) + // Try to insert new dynamic object on the path + return path_set.find(dest)->dyn_objects.insert(dyn_obj).second; + else + return false; + } +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::join_path_sets + + Inputs: src Source path set + dest Destination path set + + Outputs: True if the value was changed (dest was added) + + Purpose: Join two path sets. Add all paths from src to dest. + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::join_path_sets( + pathsett &dest, + const pathsett &src, + const dyn_objt &through) +{ + bool result=false; + for(const patht &path : src) + { + if(add_path(path.destination, through, dest)) + result=true; + for(const dyn_objt &o : path.dyn_objects) + { // Add all dynamic objects of the original path + if(add_path(path.destination, o, dest)) + result=true; + } + } + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_all_paths + + Inputs: + + Outputs: True if this has changed + + Purpose: Add all paths from other heap row. + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_all_paths( + const heap_row_valuet &other_val, + const dyn_objt &dyn_obj) +{ + bool result=false; + + auto other_it=other_val.paths.begin(); + if(other_it!=other_val.paths.end()) + { + for(auto it=paths.begin(); it!=paths.end(); ++it) + { + if(it->find(other_val.dyn_obj.first)!=it->end()) + { + auto next_it=other_it; + ++next_it; + if(next_it!=other_val.paths.end()) + { // Duplicate element pointed by it + auto n_it=it; + ++n_it; + paths.insert(n_it, *it); + } + + // Add all paths to *it + + if(join_path_sets(*it, *other_it, dyn_obj)) + result=true; + + // Move other_it to next, or to first if next doesn't exist + other_it= + next_it==other_val.paths.end() ? other_val.paths.begin() : next_it; + } + } + } + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_pointed_by + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_pointed_by(const rowt &row) +{ + auto new_pb=pointed_by.insert(row); + return new_pb.second; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_self_linkage + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_self_linkage() +{ + bool result; + result=!self_linkage; + self_linkage=true; + if(result) + { + for(const pathsett &path_set : paths) + { + for(const patht &path : path_set) + { + path.dyn_objects.insert(dyn_obj); + } + } + } + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::rename_outheap + + Inputs: expr Expression to be renamed + + Outputs: Renamed expression + + Purpose: Rename OUTHEAP row expression (used for post-expr). + Simply remove 'lb' from suffix. + +\*******************************************************************/ + +exprt heap_domaint::heap_row_valuet::rename_outheap(const symbol_exprt &expr) +{ + const std::string id=id2string(expr.get_identifier()); + return symbol_exprt( + id.substr(0, id.rfind("lb"))+id.substr(id.rfind("lb")+2), + expr.type()); +} + +/*******************************************************************\ + +Function: heap_domaint::get_new_heap_vars + + Inputs: + + Outputs: List of variables (symbols) that were added to template + during analysis. + + Purpose: + +\*******************************************************************/ + +const std::list heap_domaint::get_new_heap_vars() +{ + std::list result; + for(const template_rowt &row : templ) + { + if(row.kind==OUTHEAP) + { + assert(row.expr.id()==ID_symbol); + symbol_exprt expr=to_symbol_expr(row.expr); + rename(expr); + result.push_back(expr); + } + } + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::initialize_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_domaint::initialize_domain( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator) +{ + // Bind list iterators + bind_iterators(SSA, precondition, template_generator); + + // Create preconditions for input variables if not exist + exprt::operandst equs; + for(const symbol_exprt ¶m : SSA.params) + create_precondition(param, precondition); + for(const symbol_exprt &global_in : SSA.globals_in) + create_precondition(global_in, precondition); +} + +/*******************************************************************\ + +Function: heap_domaint::bind_iterators + + Inputs: SSA + precondition Calling context + template_generator + + Outputs: + + Purpose: Bind iterators from SSA to actual heap objects from the + calling context. + +\*******************************************************************/ + +void heap_domaint::bind_iterators( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator) +{ + new_heap_row_specs.clear(); + for(const list_iteratort &iterator : SSA.iterators) + { + for(const list_iteratort::accesst &access : iterator.accesses) + { + exprt access_binding=iterator_access_bindings( + iterator.pointer, + iterator.init_pointer, + iterator.iterator_symbol(), + iterator.fields, + access, + 0, + exprt::operandst(), + precondition, + SSA); + + // Special treatment for first element in the list + // @TODO this should be handled better + if(access.fields.size()>1 && access.location!=list_iteratort::IN_LOC) + { + const std::set first=collect_preconditions_rec( + iterator.init_pointer, + precondition); + for(const exprt &value : first) + { + if(value.id()==ID_address_of) + { + assert(to_address_of_expr(value).object().id()==ID_symbol); + const symbol_exprt &first_obj= + to_symbol_expr(to_address_of_expr(value).object()); + const symbol_exprt new_value= + recursive_member_symbol( + first_obj, + access.fields.back(), + access.location, + ns); + const symbol_exprt old_value= + recursive_member_symbol( + first_obj, + access.fields.back(), + list_iteratort::IN_LOC, + ns); + const exprt binding=equal_exprt(new_value, old_value); + access_binding=or_exprt(access_binding, binding); + + add_new_heap_row_spec( + old_value, + static_cast(access.location), + binding); + } + } + } + + iterator_bindings.push_back(access_binding); + } + } + + // Add template rows for bound heap objects + for(const heap_row_spect &row_spec : new_heap_row_specs) + { + new_output_template_row( + row_spec.expr, + row_spec.location_number, + row_spec.post_guard, + SSA, + template_generator); + } +} + +/*******************************************************************\ + +Function: heap_domaint::new_output_template_row + + Inputs: + + Outputs: + + Purpose: Insert new output template row into the template. + +\*******************************************************************/ + +void heap_domaint::new_output_template_row( + const symbol_exprt &var, + const unsigned location_number, + const exprt &post_guard, + const local_SSAt &SSA, + template_generator_baset &template_generator) +{ + template_generator.var_specs.push_back(domaint::var_spect()); + domaint::var_spect &var_spec=template_generator.var_specs.back(); + + local_SSAt::locationt loc=SSA.get_location(location_number); + + const exprt pre_guard=SSA.guard_symbol(loc); + + const symbol_exprt pre_var= + SSA.name(ssa_objectt(var, SSA.ns), local_SSAt::LOOP_BACK, loc); + const symbol_exprt post_var= + SSA.name(ssa_objectt(var, SSA.ns), local_SSAt::OUT, loc); + + var_spec.var=pre_var; + var_spec.pre_guard=pre_guard; + var_spec.post_guard=post_guard; + var_spec.aux_expr=true_exprt(); + var_spec.kind=OUTHEAP; + + renaming_map[pre_var]=post_var; + + assert(var.type().id()==ID_pointer); + const typet &pointed_type=ns.follow(var.type().subtype()); + add_template_row(var_spec, pointed_type); +} + +/*******************************************************************\ + +Function: heap_domaint::create_precondition + + Inputs: Variable and a calling context (precondition) + + Outputs: + + Purpose: Create precondition for given variable at the input of the + function if it does not exist in given calling context. + +\*******************************************************************/ + +void heap_domaint::create_precondition( + const symbol_exprt &var, + const exprt &precondition) +{ + if(var.type().id()==ID_pointer) + { + auto pre=collect_preconditions_rec(var, precondition); + if(pre.empty() || (pre.size()==1 && *(pre.begin())==var)) + { + if(id2string(var.get_identifier()).find('.')==std::string::npos) + { + // For variables, create abstract address + const symbolt *symbol; + if(ns.lookup(id2string(var.get_identifier()), symbol)) + return; + + address_of_exprt init_value(symbol->symbol_expr()); + init_value.type()=symbol->type; + aux_bindings.push_back(equal_exprt(var, init_value)); + } + else + { + // For members of structs, find corresponding object in the calling + // context and return its member + std::string var_id_str=id2string(var.get_identifier()); + const symbol_exprt pointer( + var_id_str.substr(0, var_id_str.rfind("'obj")), + var.type()); + const irep_idt member=var_id_str.substr(var_id_str.rfind(".")); + + exprt::operandst d; + std::set pointed_objs= + collect_preconditions_rec(pointer, precondition); + for(exprt pointed : pointed_objs) + { + if(pointed.id()==ID_address_of) + { + const exprt pointed_object=to_address_of_expr(pointed).object(); + if(pointed_object.id()==ID_symbol) + { + symbol_exprt pointed_member( + id2string(to_symbol_expr(pointed_object).get_identifier())+ + id2string(member), + var.type()); + d.push_back(equal_exprt(var, pointed_member)); + } + } + } + if(!d.empty()) + { + iterator_bindings.push_back(disjunction(d)); + } + } + } + } +} + +/*******************************************************************\ + +Function: heap_domaint::get_iterator_bindings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt heap_domaint::get_iterator_bindings() const +{ + return conjunction(iterator_bindings); +} + +/*******************************************************************\ + +Function: heap_domaint::get_aux_bindings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt heap_domaint::get_aux_bindings() const +{ + return conjunction(aux_bindings); +} + +/*******************************************************************\ + +Function: heap_domaint::get_input_bindings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt heap_domaint::get_input_bindings() const +{ + return and_exprt(get_iterator_bindings(), get_aux_bindings()); +} + +/*******************************************************************\ + +Function: heap_domaint::iterator_access_bindings + + Inputs: src Source pointer (symbolic value) + init_pointed Actual value of the source pointer + iterator_sym Corresponding iterator symbol + fields Set of fields to follow + access Iterator access to be bound + level Current access level + guards + precondition Calling context + SSA + + Outputs: Formula corresponding to bindings + + Purpose: Create bindings of iterator with corresponding dynamic objects. + Function is called recursively, if there is access with multiple + fields. + +\*******************************************************************/ + +const exprt heap_domaint::iterator_access_bindings( + const symbol_exprt &src, + const exprt &init_pointer, + const symbol_exprt &iterator_sym, + const std::vector &fields, + const list_iteratort::accesst &access, + const unsigned level, + exprt::operandst guards, + const exprt &precondition, + const local_SSAt &SSA) +{ + const std::set reachable= + reachable_objects(init_pointer, fields, precondition); + + exprt::operandst d; + for(const symbol_exprt &r : reachable) + { + exprt::operandst c; + + equal_exprt points_to_eq(src, address_of_exprt(r)); + c.push_back(points_to_eq); + + if(level==0) + { + equal_exprt address_eq( + address_canonizer(address_of_exprt(iterator_sym), ns), + address_of_exprt(r)); + c.push_back(address_eq); + } + + equal_exprt access_eq=access.binding(iterator_sym, r, level, ns); + c.push_back(access_eq); + + guards.push_back(conjunction(c)); + + if(level(access.location), + conjunction(guards)); + } + + guards.pop_back(); + + d.push_back(conjunction(c)); + } + + if(!d.empty()) + return disjunction(d); + else + return true_exprt(); +} + +/*******************************************************************\ + +Function: heap_domaint::reachable_objects + + Inputs: src Source expression + fields Set of fields to follow + precondition Calling context + + Outputs: Set of reachable objects + + Purpose: Find all objects reachable from source via the vector of fields + +\*******************************************************************/ + +const std::set heap_domaint::reachable_objects( + const exprt &src, + const std::vector &fields, + const exprt &precondition) const +{ + std::set result; + + if(!(src.id()==ID_symbol || src.id()==ID_member)) + return result; + + std::set pointed_objs; + if(src.id()==ID_member && to_member_expr(src).compound().get_bool(ID_pointed)) + { + const member_exprt &member=to_member_expr(src); + const exprt pointer= + get_pointer(member.compound(), pointed_level(member.compound())-1); + + std::set r= + reachable_objects(pointer, {member.get_component_name()}, precondition); + pointed_objs.insert(r.begin(), r.end()); + } + else + { + if(src.type().id()==ID_pointer) + { + std::set values=collect_preconditions_rec(src, precondition); + for(const exprt &v : values) + { + if(v.id()==ID_address_of) + { + assert(to_address_of_expr(v).object().id()==ID_symbol); + pointed_objs.insert(to_symbol_expr(to_address_of_expr(v).object())); + } + } + } + else + { + assert(src.type().get_bool("#dynamic")); + pointed_objs.insert(to_symbol_expr(src)); + } + } + + for(std::size_t i=0; i reachable_objs=collect_preconditions_rec( + obj_member, + precondition); + for(const exprt &reachable : reachable_objs) + { + if(reachable.id()==ID_address_of) + { + const exprt &reachable_obj=to_address_of_expr(reachable).object(); + assert(reachable_obj.id()==ID_symbol); + + result.insert(to_symbol_expr(reachable_obj)); + } + } + } + if(i!=fields.size()-1) + pointed_objs=result; + } + + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::add_new_heap_row_spec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_domaint::add_new_heap_row_spec( + const symbol_exprt &expr, + const unsigned location_number, + const exprt &post_guard) +{ + auto it=new_heap_row_specs.emplace(expr, location_number, post_guard); + if(!it.second) + { + if(it.first->post_guard!=post_guard) + it.first->post_guard=or_exprt(it.first->post_guard, post_guard); + } +} + +/*******************************************************************\ + +Function: heap_domaint::collect_preconditions_rec + + Inputs: Expression and calling context (precondition) + + Outputs: Set of preconditions corresponding to given expression. + + Purpose: Recursively find all preconditions for the given expression + in the calling context. + Returns right-hand sides of equalities where expr is left-hand + side. + +\*******************************************************************/ + +const std::set heap_domaint::collect_preconditions_rec( + const exprt &expr, + const exprt &precondition) +{ + std::set result; + if(precondition.id()==ID_equal) + { + const equal_exprt &eq=to_equal_expr(precondition); + if((eq.lhs()==expr && eq.rhs()!=expr) || + (eq.lhs().id()==ID_symbol && + expr.id()==ID_symbol && + to_symbol_expr(eq.lhs()).get_identifier()== + to_symbol_expr(expr).get_identifier())) + { + result.insert(eq.rhs()); + } + } + else + { + forall_operands(it, precondition) + { + std::set op_result=collect_preconditions_rec(expr, *it); + result.insert(op_result.begin(), op_result.end()); + } + } + return result; +} diff --git a/src/domains/heap_domain.h b/src/domains/heap_domain.h new file mode 100644 index 000000000..97b19a51c --- /dev/null +++ b/src/domains/heap_domain.h @@ -0,0 +1,348 @@ +/*******************************************************************\ + +Module: Abstract domain for representing heap + +Author: Viktor Malik + +\*******************************************************************/ +#ifndef CPROVER_2LS_DOMAINS_HEAP_DOMAIN_H +#define CPROVER_2LS_DOMAINS_HEAP_DOMAIN_H + +#include + +#include +#include + +#include + +#include "domain.h" +#include "template_generator_base.h" + +class heap_domaint:public domaint +{ +public: + typedef unsigned rowt; + // Field of a dynamic object (a variable) + typedef vart member_fieldt; + // We represent dynamic object by the object itself and its member field + typedef std::pair dyn_objt; + + typedef enum { STACK, HEAP } mem_kindt; + + heap_domaint( + unsigned int _domain_number, + replace_mapt &_renaming_map, + const var_specst &var_specs, + const namespacet &_ns): + domaint(_domain_number, _renaming_map, _ns) + { + make_template(var_specs, ns); + } + + struct template_rowt + { + vart expr; + guardt pre_guard; + guardt post_guard; + exprt aux_expr; + kindt kind; + mem_kindt mem_kind; + exprt dyn_obj; + irep_idt member; + }; + typedef std::vector templatet; + + /*******************************************************************\ + Base class for a value of a row + \*******************************************************************/ + struct row_valuet + { + // Row is nondeterministic - row expression is TRUE + bool nondet=false; + + virtual exprt get_row_expr( + const vart &templ_expr, + bool rename_templ_expr) const=0; + + virtual bool empty() const=0; + + virtual bool add_points_to(const exprt &dest)=0; + }; + + /*******************************************************************\ + Stack row - used for pointer-typed stack objects (variables). + Value is a set of objects that the pointer can point to. + \*******************************************************************/ + struct stack_row_valuet:public row_valuet + { + // Set of objects (or NULL) the row variable can point to + std::set points_to; + + virtual exprt get_row_expr( + const vart &templ_expr, + bool rename_templ_expr) const override; + + virtual bool add_points_to(const exprt &expr) override; + + virtual bool empty() const override + { + return points_to.empty(); + } + }; + + /*******************************************************************\ + Heap row - used for pointer-typed fields of dynamic objects. + + Value is a disjunction of conjunctions of paths leading from the dynamic + object via the field. + \*******************************************************************/ + struct heap_row_valuet:public row_valuet + { + /*******************************************************************\ + Path in a heap. Contains: + - destination object + - set of dynamic objects - set of SSA objects that the path is composed of + + Paths are ordered by destination only as it is unique within a value row. + \*******************************************************************/ + struct patht + { + exprt destination; + mutable std::set dyn_objects; + + patht(const exprt &dest_):destination(dest_) {} // NOLINT(*) + + patht(const exprt &dest_, const std::set &dyn_objs_): + destination(dest_), dyn_objects(dyn_objs_) {} + + bool operator<(const patht &rhs) const + { + return destination pathsett; + + // Set of pathsets interpreted as a disjnuction of pathsets + std::list paths; + // Set of rows whose variables point to this row + std::set pointed_by; + + // Dynamic obejct corresponding to the row (contains both object and field) + dyn_objt dyn_obj; + // Self link on an abstract dynamic object + bool self_linkage=false; + + explicit heap_row_valuet(const dyn_objt &dyn_obj_):dyn_obj(dyn_obj_) {} + + virtual exprt get_row_expr( + const vart &templ_expr_, + bool rename_templ_expr) const override; + + virtual bool add_points_to(const exprt &dest) override; + + virtual bool empty() const override + { + return paths.empty() && !self_linkage; + } + + bool add_path(const exprt &dest, const dyn_objt &dyn_obj); + + bool add_path( + const exprt &dest, + const dyn_objt &dyn_obj, + pathsett &path_set); + + bool join_path_sets( + pathsett &dest, + const pathsett &src, + const dyn_objt &through); + + bool add_all_paths( + const heap_row_valuet &other_val, + const dyn_objt &dyn_obj); + + bool add_pointed_by(const rowt &row); + + bool add_self_linkage(); + + protected: + static exprt rename_outheap(const symbol_exprt &expr); + }; + + class heap_valuet: + public valuet, + public std::vector> + { + public: + row_valuet &operator[](const rowt &row) const + { + return *(this->at(row).get()); + } + }; + + // Initialize value and domain + virtual void initialize(valuet &value) override; + + void initialize_domain( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator); + + // Value -> constraints + exprt to_pre_constraints(const heap_valuet &value) const; + + void make_not_post_constraints( + const heap_valuet &value, + exprt::operandst &cond_exprs, + exprt::operandst &value_exprs); + + // Row -> constraints + exprt get_row_pre_constraint( + const rowt &row, + const row_valuet &row_value) const; + + exprt get_row_post_constraint(const rowt &row, const row_valuet &row_value); + + // Row modifications + bool add_transitivity(const rowt &from, const rowt &to, heap_valuet &value); + + bool add_points_to(const rowt &row, heap_valuet &value, const exprt &dest); + + bool set_nondet(const rowt &row, heap_valuet &value); + + // Printing + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const override; + + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const override; + + // Projection + virtual void + project_on_vars(valuet &value, const var_sett &vars, exprt &result) override; + + // Conversion of solver value to expression + static exprt value_to_ptr_exprt(const exprt &expr); + + // Join of values + virtual void join(valuet &value1, const valuet &value2) override; + + // Getters for protected fields + const std::list get_new_heap_vars(); + + exprt get_iterator_bindings() const; + exprt get_aux_bindings() const; + exprt get_input_bindings() const; + + bool empty() const + { + return templ.empty(); + } + +protected: + templatet templ; + + // Bindings computed during interprocedural analysis + exprt::operandst iterator_bindings; + exprt::operandst aux_bindings; + + /*******************************************************************\ + Specification of a new heap row that is added dynamically + at the beginning of the analysis, after binding of iterators to the actual + dynamic objects from the calling context. + + Contains row expression and a location where the corresponding + iterator occured. + \*******************************************************************/ + class heap_row_spect + { + public: + symbol_exprt expr; + unsigned location_number; + + mutable exprt post_guard; + + heap_row_spect( + const symbol_exprt &expr, + unsigned location_number, + const exprt &post_guard): + expr(expr), location_number(location_number), post_guard(post_guard) {} + + bool operator<(const heap_row_spect &rhs) const + { + return std::tie(expr, location_number)< + std::tie(rhs.expr, rhs.location_number); + } + + bool operator==(const heap_row_spect &rhs) const + { + return std::tie(expr, location_number)== + std::tie(rhs.expr, rhs.location_number); + } + }; + + // Set of new heap rows added during analysis (used for interprocedural) + std::set new_heap_row_specs; + + void make_template(const var_specst &var_specs, const namespacet &ns); + + void add_template_row(const var_spect &var_spec, const typet &pointed_type); + + // Initializing functions + void bind_iterators( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator); + + void create_precondition(const symbol_exprt &var, const exprt &precondition); + + void new_output_template_row( + const symbol_exprt &var, + const unsigned location_number, + const exprt &post_guard, + const local_SSAt &SSA, + template_generator_baset &template_generator); + + const exprt iterator_access_bindings( + const symbol_exprt &src, + const exprt &init_pointer, + const symbol_exprt &iterator_sym, + const std::vector &fields, + const list_iteratort::accesst &access, + const unsigned level, + exprt::operandst guards, + const exprt &precondition, + const local_SSAt &SSA); + + const std::set reachable_objects( + const exprt &src, + const std::vector &fields, + const exprt &precondition) const; + + static const std::set collect_preconditions_rec( + const exprt &expr, + const exprt &precondition); + + + void add_new_heap_row_spec( + const symbol_exprt &expr, + const unsigned location_number, + const exprt &post_guard); + + // Utility functions + static int get_symbol_loc(const exprt &expr); + + friend class strategy_solver_heapt; +}; + +#endif // CPROVER_2LS_DOMAINS_HEAP_DOMAIN_H diff --git a/src/domains/heap_interval_domain.cpp b/src/domains/heap_interval_domain.cpp new file mode 100644 index 000000000..95ca71fac --- /dev/null +++ b/src/domains/heap_interval_domain.cpp @@ -0,0 +1,102 @@ +/*******************************************************************\ + +Module: Combination of heap and interval abstract domains + +Author: Viktor Malik + +\*******************************************************************/ + +#include "heap_interval_domain.h" + +/*******************************************************************\ + +Function: heap_interval_domaint::initialize + + Inputs: + + Outputs: + + Purpose: Initialize abstract value. + +\*******************************************************************/ + +void heap_interval_domaint::initialize(domaint::valuet &value) +{ + heap_interval_valuet &v=static_cast(value); + + heap_domain.initialize(v.heap_value); + interval_domain.initialize(v.interval_value); +} + +/*******************************************************************\ + +Function: heap_interval_domaint::output_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_interval_domaint::output_value( + std::ostream &out, + const domaint::valuet &value, + const namespacet &ns) const +{ + const heap_interval_valuet &v= + static_cast(value); + + heap_domain.output_value(out, v.heap_value, ns); + interval_domain.output_value(out, v.interval_value, ns); +} + +/*******************************************************************\ + +Function: heap_interval_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_interval_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const +{ + heap_domain.output_domain(out, ns); + interval_domain.output_domain(out, ns); +} + +/*******************************************************************\ + +Function: heap_interval_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_interval_domaint::project_on_vars( + domaint::valuet &value, + const domaint::var_sett &vars, + exprt &result) +{ + heap_interval_valuet &v=static_cast(value); + + exprt heap_result; + heap_domain.project_on_vars(v.heap_value, vars, heap_result); + exprt interval_result; + interval_domain.project_on_vars(v.interval_value, vars, interval_result); + + result=heap_result; + if(interval_result!=true_exprt()) + result=and_exprt(result, interval_result); +} diff --git a/src/domains/heap_interval_domain.h b/src/domains/heap_interval_domain.h new file mode 100644 index 000000000..8699873e0 --- /dev/null +++ b/src/domains/heap_interval_domain.h @@ -0,0 +1,58 @@ +/*******************************************************************\ + +Module: Combination of heap and interval abstract domains + +Author: Viktor Malik + +\*******************************************************************/ +#ifndef CPROVER_2LS_DOMAINS_HEAP_INTERVAL_DOMAIN_H +#define CPROVER_2LS_DOMAINS_HEAP_INTERVAL_DOMAIN_H + + +#include "domain.h" +#include "tpolyhedra_domain.h" +#include "heap_domain.h" + +class heap_interval_domaint:public domaint +{ +public: + heap_domaint heap_domain; + tpolyhedra_domaint interval_domain; + + heap_interval_domaint( + unsigned int _domain_number, + replace_mapt &_renaming_map, + const var_specst &var_specs, + const namespacet &ns): + domaint(_domain_number, _renaming_map, ns), + heap_domain(_domain_number, _renaming_map, var_specs, ns), + interval_domain(_domain_number, _renaming_map, ns) + { + interval_domain.add_interval_template(var_specs, ns); + } + + class heap_interval_valuet:public valuet + { + public: + heap_domaint::heap_valuet heap_value; + tpolyhedra_domaint::templ_valuet interval_value; + }; + + virtual void initialize(valuet &value) override; + + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const override; + + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const override; + + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) override; +}; + +#endif // CPROVER_2LS_DOMAINS_HEAP_INTERVAL_DOMAIN_H diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 08f5b1ae2..b4a0599c4 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -203,8 +203,10 @@ static inline incremental_solvert &operator<<( *dest.solver << src; #else if(!dest.activation_literals.empty()) - dest.debug_add_to_formula( - or_exprt(src, literal_exprt(!dest.activation_literals.back()))); + { + literal_exprt act_lit(!dest.activation_literals.back()); + dest.debug_add_to_formula(or_exprt(src, act_lit)); + } else dest.debug_add_to_formula(src); #endif diff --git a/src/domains/lexlinrank_domain.cpp b/src/domains/lexlinrank_domain.cpp index 0fc5aa5ea..60424d65e 100644 --- a/src/domains/lexlinrank_domain.cpp +++ b/src/domains/lexlinrank_domain.cpp @@ -611,16 +611,18 @@ void lexlinrank_domaint::project_on_vars( if(is_row_value_false(v[row])) { // (g=> false) - c.push_back(implies_exprt( - and_exprt(templ[row].pre_guard, templ[row].post_guard), - false_exprt())); + c.push_back( + implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), + false_exprt())); } else if(is_row_value_true(v[row])) { // (g=> true) - c.push_back(implies_exprt( - and_exprt(templ[row].pre_guard, templ[row].post_guard), - true_exprt())); + c.push_back( + implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), + true_exprt())); } else { diff --git a/src/domains/linrank_domain.cpp b/src/domains/linrank_domain.cpp index 064ce3859..fe87be000 100644 --- a/src/domains/linrank_domain.cpp +++ b/src/domains/linrank_domain.cpp @@ -457,8 +457,10 @@ void linrank_domaint::project_on_vars( else if(is_row_value_false(v[row])) { // (g=> false) - c.push_back(implies_exprt( - and_exprt(templ[row].pre_guard, templ[row].post_guard), false_exprt())); + c.push_back( + implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), + false_exprt())); } else { @@ -504,9 +506,10 @@ void linrank_domaint::project_on_vars( #endif exprt decreasing=binary_relation_exprt(sum_pre, ID_gt, sum_post); #endif - c.push_back(implies_exprt( - and_exprt(templ[row].pre_guard, templ[row].post_guard), - decreasing)); + c.push_back( + implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), + decreasing)); } } result=conjunction(c); diff --git a/src/domains/list_iterator.cpp b/src/domains/list_iterator.cpp new file mode 100644 index 000000000..cdd70b94e --- /dev/null +++ b/src/domains/list_iterator.cpp @@ -0,0 +1,174 @@ +/*******************************************************************\ + +Module: List iterator - abstraction for iterative access to a linked + list. + +Author: Viktor Malik + +\*******************************************************************/ + +#include +#include +#include "list_iterator.h" + +/*******************************************************************\ + +Function: list_iteratort::add_access + + Inputs: + + Outputs: + + Purpose: Add new access to the iterator corresponding to + an expression from SSA. + +\*******************************************************************/ + +void list_iteratort::add_access( + const member_exprt &expr, + unsigned location_number) const +{ + assert( + expr.compound().get_bool(ID_iterator) && + expr.compound().get_bool(ID_pointed)); + + accesst access; + access.location=location_number; + + unsigned level=pointed_level(expr.compound()); + unsigned iterator_level=it_value_level(expr.compound()); + for(unsigned l=iterator_level; l +#include +#include + +class list_iteratort +{ +public: + // No location (used for input variables) + static const unsigned IN_LOC=std::numeric_limits::max(); + + /*******************************************************************\ + Access to an object from a list iterator. + Contains: + - sequence of fields that are used to access the object from the + iterator + - location: + IN_LOC for read access + location number for write access + \*******************************************************************/ + class accesst + { + public: + std::vector fields; + unsigned location; + + equal_exprt binding( + const symbol_exprt &lhs, + const symbol_exprt &rhs, + const unsigned level, + const namespacet &ns) const; + }; + + // Pointer variable used to iterate the list (induction pointer) + symbol_exprt pointer; + // Initial value of the induction pointer (before the first iteration) + exprt init_pointer; + // Set of fields through which a step is done after each iteration + std::vector fields; + mutable std::list accesses; + + list_iteratort( + const symbol_exprt &pointer, + const exprt &init_pointer, + const std::vector &fields): + pointer(pointer), init_pointer(init_pointer), fields(fields) {} + + bool operator<(const list_iteratort &rhs) const + { + return std::tie(pointer, fields)(domain), solver, SSA.ns) #if 0 +// NOLINTNEXTLINE(*) #define BINSEARCH_SOLVER strategy_solver_binsearch2t(\ *static_cast(domain), solver, SSA.ns) +// NOLINTNEXTLINE(*) #define BINSEARCH_SOLVER strategy_solver_binsearch3t(\ *static_cast(domain), solver, SSA, SSA.ns) #endif @@ -57,7 +62,10 @@ void ssa_analyzert::operator()( incremental_solvert &solver, local_SSAt &SSA, const exprt &precondition, - template_generator_baset &template_generator) + template_generator_baset &template_generator, + bool recursive, + std::map context_bounds, + tmpl_rename_mapt templ_maps) { if(SSA.goto_function.body.instructions.empty()) return; @@ -68,8 +76,11 @@ void ssa_analyzert::operator()( solver.new_context(); solver << SSA.get_enabling_exprs(); + if(!template_generator.options.get_bool_option("has-recursion") || + !recursive || + !template_generator.options.get_bool_option("context-sensitive")) // add precondition (or conjunction of asssertion in backward analysis) - solver << precondition; + solver << precondition; domain=template_generator.domain(); @@ -103,6 +114,28 @@ void ssa_analyzert::operator()( *static_cast(domain), solver, SSA.ns); result=new equality_domaint::equ_valuet(); } + else if(template_generator.options.get_bool_option("heap")) + { + strategy_solver=new strategy_solver_heapt( + *static_cast(domain), + solver, + SSA, + precondition, + get_message_handler(), + template_generator); + result=new heap_domaint::heap_valuet(); + } + else if(template_generator.options.get_bool_option("heap-interval")) + { + strategy_solver=new strategy_solver_heap_intervalt( + *static_cast(domain), + solver, + SSA, + precondition, + get_message_handler(), + template_generator); + result=new heap_interval_domaint::heap_interval_valuet(); + } else { if(template_generator.options.get_bool_option("enum-solver")) @@ -130,9 +163,20 @@ void ssa_analyzert::operator()( // initialize inv domain->initialize(*result); + if(recursive && // initialize input arguments and input global variables with calling context + template_generator.options.get_bool_option("context-sensitive")) + domain->initialize_in_templates(*result, context_bounds); // iterate - while(strategy_solver->iterate(*result)) {} + if(recursive)//iterate for recursive function + { + assert(template_generator.options.get_bool_option("binsearch-solver")); + //while(strategy_solver->iterate_for_recursive(*result,templ_maps, + //template_generator.options.get_bool_option("context-sensitive"))) {} + while(strategy_solver->iterate(*result)) {}//need to change + } + else//iterate for non-recursive function + while(strategy_solver->iterate(*result)) {} solver.pop_context(); @@ -160,3 +204,38 @@ void ssa_analyzert::get_result(exprt &_result, const domaint::var_sett &vars) { domain->project_on_vars(*result, vars, _result); } + +/*******************************************************************\ + +Function: ssa_analyzert::update_heap_out + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void ssa_analyzert::update_heap_out(summaryt::var_sett &out) +{ + heap_domaint &heap_domain=static_cast(*domain); + + auto new_heap_vars=heap_domain.get_new_heap_vars(); + out.insert(new_heap_vars.begin(), new_heap_vars.end()); +} + +/*******************************************************************\ + +Function: ssa_analyzert::input_heap_bindings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +const exprt ssa_analyzert::input_heap_bindings() +{ + return static_cast(*domain).get_iterator_bindings(); +} \ No newline at end of file diff --git a/src/domains/ssa_analyzer.h b/src/domains/ssa_analyzer.h index 1758f4711..bd7bb8adc 100644 --- a/src/domains/ssa_analyzer.h +++ b/src/domains/ssa_analyzer.h @@ -11,6 +11,7 @@ Author: Peter Schrammel #include +#include #include #include "strategy_solver_base.h" @@ -39,10 +40,17 @@ class ssa_analyzert:public messaget incremental_solvert &solver, local_SSAt &SSA, const exprt &precondition, - template_generator_baset &template_generator); + template_generator_baset &template_generator, + bool recursive=false, + std::map context_bounds= + std::map(), + tmpl_rename_mapt templ_maps=tmpl_rename_mapt()); void get_result(exprt &result, const domaint::var_sett &vars); + void update_heap_out(summaryt::var_sett &out); + const exprt input_heap_bindings(); + inline unsigned get_number_of_solver_instances() { return solver_instances; } inline unsigned get_number_of_solver_calls() { return solver_calls; } diff --git a/src/domains/strategy_solver_base.h b/src/domains/strategy_solver_base.h index 2e33c0946..56d61d222 100644 --- a/src/domains/strategy_solver_base.h +++ b/src/domains/strategy_solver_base.h @@ -30,6 +30,8 @@ class strategy_solver_baset:public messaget {} virtual bool iterate(invariantt &inv) { assert(false); } + virtual bool iterate_for_recursive( + invariantt &inv, tmpl_rename_mapt templ_maps,bool cntxt_sensitive) {assert(false);} inline unsigned get_number_of_solver_calls() { return solver_calls; } inline unsigned get_number_of_solver_instances() { return solver_instances; } diff --git a/src/domains/strategy_solver_binsearch.cpp b/src/domains/strategy_solver_binsearch.cpp index 398d66ddd..143e0013e 100644 --- a/src/domains/strategy_solver_binsearch.cpp +++ b/src/domains/strategy_solver_binsearch.cpp @@ -35,11 +35,11 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) solver.new_context(); // for improvement check exprt inv_expr=tpolyhedra_domain.to_pre_constraints(inv); - -#if 0 + +//#if 0 debug() << "improvement check: " << eom; debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; -#endif +//#endif solver << inv_expr; @@ -49,21 +49,21 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) strategy_cond_literals.resize(strategy_cond_exprs.size()); -#if 0 +//#if 0 debug() << "post-inv: "; -#endif +//#endif for(std::size_t i=0; i0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); -#endif +//#endif strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); // solver.set_frozen(strategy_cond_literals[i]); strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } -#if 0 +//#if 0 debug() << eom; -#endif +//#endif solver << disjunction(strategy_cond_exprs); @@ -73,9 +73,9 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) if(solver()==decision_proceduret::D_SATISFIABLE) // improvement check { -#if 0 +//#if 0 debug() << "SAT" << eom; -#endif +//#endif #if 0 for(std::size_t i=0; iis_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; + } +#endif + + if(!tpolyhedra_domain.less_than(middle, upper)) + middle=lower; + upper=middle; + } + solver.pop_context(); // binary search iteration + } + + debug() << "update value: " << from_expr(ns, "", lower) << eom; + + solver.pop_context(); // symbolic value system + + tpolyhedra_domain.set_row_value(row, lower, inv); + improved=true; + } + else + { +//#if 0 + debug() << "UNSAT" << eom; +//#endif + +#ifdef DEBUG_FORMULA + for(std::size_t i=0; iis_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; + } +#endif + + solver.pop_context(); // improvement check + } + + return improved; +} + +bool strategy_solver_binsearcht::iterate_for_recursive(invariantt &_inv, + tmpl_rename_mapt templ_maps,bool cntxt_sensitive) +{ + tpolyhedra_domaint::templ_valuet &inv= + static_cast(_inv); + + bool improved=false; + + solver.new_context(); // for improvement check + + exprt inv_expr=tpolyhedra_domain.to_pre_constraints(inv); + inv_expr=and_exprt(inv_expr,get_rec_call_pre_constraints(inv,templ_maps)); + +//#if 0 + debug() << "improvement check: " << eom; + debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; +//#endif + + solver << inv_expr; + + exprt::operandst strategy_cond_exprs; + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + + strategy_cond_literals.resize(strategy_cond_exprs.size()); + +//#if 0 + debug() << "post-inv: "; +//#endif + for(std::size_t i=0; i0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); +//#endif + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + // solver.set_frozen(strategy_cond_literals[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); + } + + if(cntxt_sensitive) + { + std::vector> constraints= + get_rec_call_post_constraints(inv,templ_maps); + std::size_t i=0; + while(tpolyhedra_domain.templ[i].kind!=domaint::IN) {i++;} + for(std::vector exprs:constraints) + { + strategy_cond_literals[i]=solver.convert(disjunction(exprs)); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); + i++; + } + assert(strategy_cond_literals.size()==tpolyhedra_domain.templ.size()); + } +//#if 0 + debug() << eom; +//#endif + + solver << disjunction(strategy_cond_exprs); + +//#if 0 + debug() << "solve(): "; +//#endif + + if(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { +//#if 0 + debug() << "SAT" << eom; +//#endif + +#if 0 + for(std::size_t i=0; i improve_rows; + improve_rows.insert(row); + if(tpolyhedra_domain.templ[row].kind==domaint::IN) + get_max_lower(templ_maps,row); + + tpolyhedra_domaint::row_valuet upper= + tpolyhedra_domain.get_max_row_value(row); + tpolyhedra_domaint::row_valuet lower= + simplify_const(solver.get(strategy_value_exprs[row])); + + solver.pop_context(); // improvement check + + solver.new_context(); // symbolic value system + + exprt pre_inv_expr= + tpolyhedra_domain.to_symb_pre_constraints(inv, improve_rows); + pre_inv_expr=and_exprt(pre_inv_expr, + get_rec_call_pre_constraints(inv,templ_maps,improve_rows)); + + solver << pre_inv_expr; + + exprt post_inv_expr; + if(tpolyhedra_domain.templ[row].kind!=domaint::IN) + post_inv_expr=tpolyhedra_domain.get_row_symb_post_constraint(row); + else + post_inv_expr=get_rec_call_symb_post_constraints( + inv,templ_maps,row); + + solver << post_inv_expr; + +//#if 0 + debug() << "symbolic value system: " << eom; + debug() << "pre-inv: " << from_expr(ns, "", pre_inv_expr) << eom; + debug() << "post-inv: " << from_expr(ns, "", post_inv_expr) << eom; +//#endif + + while(tpolyhedra_domain.less_than(lower, upper)) + { + tpolyhedra_domaint::row_valuet middle= + tpolyhedra_domain.between(lower, upper); + if(!tpolyhedra_domain.less_than(lower, middle)) + middle=upper; + + // row_symb_value >= middle + exprt c= + tpolyhedra_domain.get_row_symb_value_constraint(row, middle, true); + +//#if 0 + debug() << "upper: " << from_expr(ns, "", upper) << eom; + debug() << "middle: " << from_expr(ns, "", middle) << eom; + debug() << "lower: " << from_expr(ns, "", lower) << eom; +//#endif + + solver.new_context(); // binary search iteration + +//#if 0 + debug() << "constraint: " << from_expr(ns, "", c) << eom; +//#endif + + solver << c; + + if(solver()==decision_proceduret::D_SATISFIABLE) + { +//#if 0 + debug() << "SAT" << eom; +//#endif + #if 0 for(std::size_t i=0; i &symb_rows) +{ + assert(val.size()==tpolyhedra_domain.templ.size()); + std::vector constraints; + for(auto map:templ_maps) + { + std::vector vars=map.second; + std::vector::iterator it=vars.begin(); + for(std::size_t i=0;itype())))); + } + if(symb_rows.find(i+1)==symb_rows.end()) + { + if(val[i+1].get(ID_value)==ID_false) + constraints.push_back(implies_exprt(map.first,false_exprt())); + else if(val[i+1].get(ID_value)==ID_true) + constraints.push_back(implies_exprt(map.first,true_exprt())); + else constraints.push_back(implies_exprt(map.first, + binary_relation_exprt(unary_minus_exprt(typecast_exprt(*it, + tpolyhedra_domain.templ.at(i+1).expr.type())), + ID_le, val[i+1]))); + } + else + { + constraints.push_back(implies_exprt(map.first, + binary_relation_exprt(unary_minus_exprt(typecast_exprt(*it, + tpolyhedra_domain.templ.at(i+1).expr.type())), + ID_le, symbol_exprt("symb_bound#"+i2string(tpolyhedra_domain.domain_number)+"$"+ + i2string(i+1), it->type())))); + } + } + } + } + return conjunction(constraints); +} + +std::vector> strategy_solver_binsearcht:: + get_rec_call_post_constraints( + const tpolyhedra_domaint::templ_valuet &val, + tmpl_rename_mapt templ_maps) +{ + assert(val.size()==tpolyhedra_domain.templ.size()); + std::vector> constraints; + std::size_t i=0; + for(auto temp:tpolyhedra_domain.templ) + { + if(temp.kind==domaint::IN) i++; + } + constraints.resize(i); + constraints[2].push_back(true_exprt()); + for(auto map:templ_maps) + { + std::vector vars=map.second; + std::vector::iterator it=vars.begin(); + std::size_t j=0; + for(std::size_t i=0;i post; + for(auto map:templ_maps) + { + std::vector vars=map.second; + if(row%2==0) + post.push_back(and_exprt(tpolyhedra_domain.templ[row].aux_expr,not_exprt( + implies_exprt(map.first,binary_relation_exprt(vars[row/2],ID_le, + symbol_exprt("symb_bound#"+i2string(tpolyhedra_domain.domain_number)+"$"+ + i2string(row), vars[row/2].type())))))); + else + post.push_back(and_exprt(tpolyhedra_domain.templ[row].aux_expr,not_exprt( + implies_exprt(map.first,binary_relation_exprt(unary_minus_exprt + (typecast_exprt(vars[(row/2)+1],tpolyhedra_domain.templ[row].expr.type())),ID_le, + symbol_exprt("symb_bound#"+i2string(tpolyhedra_domain.domain_number)+"$"+ + i2string(row), vars[(row/2)+1].type())))))); + } + return disjunction(post); +} + +void strategy_solver_binsearcht::get_max_lower(tmpl_rename_mapt templ_maps, + std::size_t row) +{ + tpolyhedra_domaint::row_valuet val=tpolyhedra_domain.get_min_row_value(row),tmp; + for(tmpl_rename_mapt::iterator it=templ_maps.begin(); + it!=templ_maps.end();it++) + { + if(solver.get(it->first).is_false()) continue; + std::size_t i=0; + while(tpolyhedra_domain.templ[i].kind!=domaint::IN) {i++;} + tmp=to_constant_expr(solver.get(it->second[row-i])); + if(tpolyhedra_domain.less_than(val,tmp)) + { + val=tmp; + strategy_value_exprs[i]=it->second[row-i]; + } + } +} \ No newline at end of file diff --git a/src/domains/strategy_solver_binsearch.h b/src/domains/strategy_solver_binsearch.h index 8bcdf7f4b..bc94ffd2d 100644 --- a/src/domains/strategy_solver_binsearch.h +++ b/src/domains/strategy_solver_binsearch.h @@ -25,9 +25,25 @@ class strategy_solver_binsearcht:public strategy_solver_baset } virtual bool iterate(invariantt &inv); + virtual bool iterate_for_recursive( + invariantt &inv, tmpl_rename_mapt templ_maps,bool cntxt_sensitive); protected: tpolyhedra_domaint &tpolyhedra_domain; + exprt get_rec_call_pre_constraints( + const tpolyhedra_domaint::templ_valuet &val, + tmpl_rename_mapt templ_maps, + const std::set &symb_rows= + std::set()); + + std::vector> get_rec_call_post_constraints( + const tpolyhedra_domaint::templ_valuet &val, + tmpl_rename_mapt templ_maps); + exprt get_rec_call_symb_post_constraints( + const tpolyhedra_domaint::templ_valuet &val, + tmpl_rename_mapt templ_maps, + std::size_t row); + void get_max_lower(tmpl_rename_mapt templ_maps,std::size_t row); }; #endif diff --git a/src/domains/strategy_solver_heap.cpp b/src/domains/strategy_solver_heap.cpp new file mode 100644 index 000000000..caa9b3922 --- /dev/null +++ b/src/domains/strategy_solver_heap.cpp @@ -0,0 +1,413 @@ +/*******************************************************************\ + +Module: Strategy solver for heap shape analysis + +Author: Viktor Malik + +\*******************************************************************/ + +// #define DEBUG_OUTPUT + +#include +#include "strategy_solver_heap.h" + +/*******************************************************************\ + +Function: strategy_solver_heapt::iterate + + Inputs: + + Outputs: + + Purpose: Single iteration of invariant inference using heap shape + domain. + +\*******************************************************************/ + +bool strategy_solver_heapt::iterate(invariantt &_inv) +{ + heap_domaint::heap_valuet &inv=static_cast(_inv); + + bool improved=false; + + solver.new_context(); + + // Entry value constraints + exprt pre_expr=heap_domain.to_pre_constraints(inv); +#ifdef DEBUG_OUTPUT + debug() << "pre-inv: " << from_expr(ns, "", pre_expr) << eom; +#endif + solver << pre_expr; + + // Exit value constraints + exprt::operandst strategy_cond_exprs; + heap_domain.make_not_post_constraints( + inv, + strategy_cond_exprs, + strategy_value_exprs); + + strategy_cond_literals.resize(strategy_cond_exprs.size()); + +#ifdef DEBUG_OUTPUT + debug() << "post-inv: "; +#endif + for(unsigned i=0; i0 ? " || " : "") + << from_expr(ns, "", strategy_cond_exprs[i]); +#endif + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); + } +#ifdef DEBUG_OUTPUT + debug() << eom; +#endif + solver << disjunction(strategy_cond_exprs); + +#ifdef DEBUG_OUTPUT + debug() << "solve(): "; +#endif + + if(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { +#ifdef DEBUG_OUTPUT + debug() << "SAT" << eom; +#endif + +#ifdef DEBUG_OUTPUT + for(unsigned i=0; i=0 && !inv[member_val_index].nondet) + { + // Add all paths from obj.next to p + if(heap_domain.add_transitivity( + row, + static_cast(member_val_index), + inv)) + { + improved=true; + const std::string expr_str= + from_expr(ns, "", heap_domain.templ[member_val_index].expr); + debug() << "Add all paths: " << expr_str + << ", through: " << from_expr(ns, "", obj) << eom; + } + } + } + } + else + { + if(heap_domain.set_nondet(row, inv)) + { + improved=true; + debug() << "Set nondet" << eom; + } + } + + // Recursively update all rows that are dependent on this row + if(templ_row.mem_kind==heap_domaint::HEAP) + { + updated_rows.clear(); + if(!inv[row].nondet) + update_rows_rec(row, inv); + } + } + } + } + + else + { + debug() << "UNSAT" << eom; + +#ifdef DEBUG_OUTPUT + for(unsigned i=0; iis_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; + } + + for(unsigned i=0; imax_loc && + (kind==domaint::OUT || kind==domaint::OUTHEAP || loc<=actual_loc)) + { + max_loc=loc; + result=i; + } + } + } + } + return result; +} + +/*******************************************************************\ + +Function: strategy_solver_heapt::update_rows_rec + + Inputs: + + Outputs: + + Purpose: Recursively update rows that point to given row. + +\*******************************************************************/ + +bool strategy_solver_heapt::update_rows_rec( + const heap_domaint::rowt &row, + heap_domaint::heap_valuet &value) +{ + heap_domaint::heap_row_valuet &row_value= + static_cast(value[row]); + const heap_domaint::template_rowt &templ_row=heap_domain.templ[row]; + + updated_rows.insert(row); + bool result=false; + for(const heap_domaint::rowt &ptr : row_value.pointed_by) + { + if(heap_domain.templ[ptr].mem_kind==heap_domaint::HEAP && + heap_domain.templ[ptr].member==templ_row.member) + { + if(heap_domain.add_transitivity(ptr, row, value)) + result=true; + + debug() << "recursively updating row: " << ptr << eom; + debug() << "add all paths: " << from_expr(ns, "", templ_row.expr) + << ", through: " + << from_expr(ns, "", templ_row.dyn_obj) << eom; + // Recursive update is called for each row only once + if(updated_rows.find(ptr)==updated_rows.end()) + result=update_rows_rec(ptr, value) || result; + } + } + return result; +} + +/*******************************************************************\ + +Function: strategy_solver_heapt::print_solver_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_heapt::print_solver_expr(const exprt &expr) +{ + debug() << from_expr(ns, "", expr) << ": " + << from_expr(ns, "", solver.get(expr)) << eom; + forall_operands(it, expr) + print_solver_expr(*it); +} + +/*******************************************************************\ + +Function: strategy_solver_heapt::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_heapt::initialize( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator) +{ + heap_domain.initialize_domain(SSA, precondition, template_generator); + + const exprt input_bindings=heap_domain.get_input_bindings(); + if(!input_bindings.is_true()) + { + solver << input_bindings; + debug() << "Input bindings:" << eom; + debug() << from_expr(ns, "", input_bindings) << eom; + } + + if(!heap_domain.new_heap_row_specs.empty()) + { + debug() << "New template:" << eom; + heap_domain.output_domain(debug(), ns); + } +} diff --git a/src/domains/strategy_solver_heap.h b/src/domains/strategy_solver_heap.h new file mode 100644 index 000000000..51435396d --- /dev/null +++ b/src/domains/strategy_solver_heap.h @@ -0,0 +1,57 @@ +/*******************************************************************\ + +Module: Strategy solver for heap shape analysis + +Author: Viktor Malik + +\*******************************************************************/ +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_H + +#include +#include "strategy_solver_base.h" +#include "heap_domain.h" +#include "template_generator_base.h" + +class strategy_solver_heapt:public strategy_solver_baset +{ +public: + strategy_solver_heapt( + heap_domaint &_heap_domain, + incremental_solvert &_solver, + const local_SSAt &SSA, + const exprt &precondition, + message_handlert &message_handler, + template_generator_baset &template_generator): + strategy_solver_baset(_solver, SSA.ns), heap_domain(_heap_domain) + { + set_message_handler(message_handler); + initialize(SSA, precondition, template_generator); + } + + virtual bool iterate(invariantt &_inv) override; + + void initialize( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator); + +protected: + heap_domaint &heap_domain; + std::set updated_rows; + + int find_member_row( + const exprt &obj, + const irep_idt &member, + int actual_loc, + const domaint::kindt &kind); + + bool update_rows_rec( + const heap_domaint::rowt &row, + heap_domaint::heap_valuet &value); + + void print_solver_expr(const exprt &expr); +}; + + +#endif // CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_H diff --git a/src/domains/strategy_solver_heap_interval.cpp b/src/domains/strategy_solver_heap_interval.cpp new file mode 100644 index 000000000..ba0f61650 --- /dev/null +++ b/src/domains/strategy_solver_heap_interval.cpp @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: Strategy solver for combination of shape and interval domains. + +Author: Viktor Malik + +\*******************************************************************/ + +#include "strategy_solver_heap_interval.h" + +/*******************************************************************\ + +Function: strategy_solver_heap_intervalt::iterate + + Inputs: + + Outputs: + + Purpose: Since template rows for the shape part and the interval part + are disjoint, we simply call iterate method for each part + separately + +\*******************************************************************/ + +bool strategy_solver_heap_intervalt::iterate( + strategy_solver_baset::invariantt &_inv) +{ + heap_interval_domaint::heap_interval_valuet &inv= + static_cast(_inv); + + bool heap_improved=heap_solver.iterate(inv.heap_value); + bool interval_improved=interval_solver.iterate(inv.interval_value); + + return heap_improved || interval_improved; +} + +/*******************************************************************\ + +Function: strategy_solver_heap_intervalt::set_message_handler + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_heap_intervalt::set_message_handler( + message_handlert &_message_handler) +{ + heap_solver.set_message_handler(_message_handler); + interval_solver.set_message_handler(_message_handler); +} diff --git a/src/domains/strategy_solver_heap_interval.h b/src/domains/strategy_solver_heap_interval.h new file mode 100644 index 000000000..8005a821c --- /dev/null +++ b/src/domains/strategy_solver_heap_interval.h @@ -0,0 +1,53 @@ +/*******************************************************************\ + +Module: Strategy solver for combination of shape and interval domains. + +Author: Viktor Malik + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_INTERVAL_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_INTERVAL_H + + +#include "strategy_solver_base.h" +#include "heap_interval_domain.h" +#include "strategy_solver_heap.h" +#include "strategy_solver_binsearch.h" + +class strategy_solver_heap_intervalt:public strategy_solver_baset +{ +public: + strategy_solver_heap_intervalt( + heap_interval_domaint &_heap_interval_domain, + incremental_solvert &_solver, + const local_SSAt &SSA, + const exprt &precondition, + message_handlert &message_handler, + template_generator_baset &template_generator): + strategy_solver_baset(_solver, SSA.ns), + heap_interval_domain(_heap_interval_domain), + heap_solver( + heap_interval_domain.heap_domain, + _solver, + SSA, + precondition, + message_handler, + template_generator), + interval_solver(heap_interval_domain.interval_domain, _solver, SSA.ns) + { + } + + virtual bool iterate(invariantt &_inv) override; + + virtual void set_message_handler(message_handlert &_message_handler) override; + +protected: + heap_interval_domaint &heap_interval_domain; + + strategy_solver_heapt heap_solver; + strategy_solver_binsearcht interval_solver; +}; + + +#endif // CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_INTERVAL_H diff --git a/src/domains/template_gen_rec_summary.cpp b/src/domains/template_gen_rec_summary.cpp new file mode 100644 index 000000000..74818cc3e --- /dev/null +++ b/src/domains/template_gen_rec_summary.cpp @@ -0,0 +1,69 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +/* + * File: template_gen_rec_summary.cpp + * Author: sarbojit + * + * Created on 19 March, 2018, 10:19 PM + */ + +#define SHOW_TEMPLATE +#include "tpolyhedra_domain.h" + +#include "template_gen_rec_summary.h" + +void template_gen_rec_summaryt::operator()(const irep_idt &function_name, + unsigned _domain_number, + const local_SSAt &SSA, tmpl_rename_mapt &templ_maps, + bool forward, bool recursive) +{ + domain_number=_domain_number; + handle_special_functions(SSA); // we have to call that to prevent trouble! + + collect_variables_loop(SSA, forward); + + // do not compute summary for entry-point + if(SSA.goto_function.body.instructions.front().function!=ID__start || + options.get_bool_option("preconditions")) + { + collect_variables_inout(SSA, forward); + } + if(recursive) get_renaming_maps(function_name,SSA,templ_maps); + // either use standard templates or user-supplied ones + if(!instantiate_custom_templates(SSA)) + instantiate_standard_domains(SSA,recursive); + +#ifdef SHOW_TEMPLATE_VARIABLES + debug() << "Template variables: " << eom; + domaint::output_var_specs(debug(), var_specs, SSA.ns); debug() << eom; +#endif +#ifdef SHOW_TEMPLATE + debug() << "Template: " << eom; + domain_ptr->output_domain(debug(), SSA.ns); debug() << eom; +#endif +} + +void template_gen_rec_summaryt::get_renaming_maps(const irep_idt &function_name, + local_SSAt SSA,tmpl_rename_mapt &templ_maps) +{ + for(local_SSAt::nodet node:SSA.nodes) + for(function_application_exprt f_call:node.function_calls) + if(function_name==to_symbol_expr(f_call.function()).get_identifier()) + { + std::pair> p(SSA.guard_symbol(node.location), + std::vector(f_call.arguments())); + std::set globals_in,globals_out; + SSA.get_globals(node.location,globals_in); + for(exprt e:globals_in) {p.second.push_back(e);} + SSA.get_globals(node.location,globals_out,false); + for(exprt e:globals_out) {p.second.push_back(e);} + for(domaint::var_spect s:var_specs) {std::cout< #include +#include + #include "template_generator_base.h" #include "equality_domain.h" #include "tpolyhedra_domain.h" #include "predabs_domain.h" +#include "heap_domain.h" +#include "heap_interval_domain.h" #ifdef DEBUG #include @@ -265,6 +269,38 @@ void template_generator_baset::filter_equality_domain() /*******************************************************************\ +Function: template_generator_baset::filter_heap_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void template_generator_baset::filter_heap_domain() +{ + domaint::var_specst new_var_specs(var_specs); + var_specs.clear(); + for(auto &var : new_var_specs) + { + if(var.var.id()==ID_symbol && var.var.type().id()==ID_pointer) + { + if(is_pointed(var.var) && + id2string(to_symbol_expr(var.var).get_identifier()).find(".")!= + std::string::npos) + continue; + // Filter out non-assigned OUT variables + if(var.kind!=domaint::OUT || + ssa_inlinert::get_original_identifier(to_symbol_expr(var.var))!= + to_symbol_expr(var.var).get_identifier()) + var_specs.push_back(var); + } + } +} + +/*******************************************************************\ + Function: template_generator_baset::add_var Inputs: @@ -289,8 +325,9 @@ void template_generator_baset::add_var( exprt post_var=post_renaming_map[var]; exprt aux_var=aux_renaming_map[var]; aux_expr=and_exprt( - implies_exprt(and_exprt(post_guard, not_exprt(init_guard)), - equal_exprt(aux_var, post_var)), + implies_exprt( + and_exprt(post_guard, not_exprt(init_guard)), + equal_exprt(aux_var, post_var)), implies_exprt(init_guard, equal_exprt(aux_var, init_renaming_map[var]))); post_guard=or_exprt(post_guard, init_guard); } @@ -593,11 +630,14 @@ bool template_generator_baset::instantiate_custom_templates( if(contains_new_var) add_post_vars=true; - static_cast(domain_ptr)->add_template_row( - expr, pre_guard, - contains_new_var ? and_exprt(pre_guard, post_guard) : post_guard, - aux_expr, - contains_new_var ? domaint::OUT : domaint::LOOP); + static_cast(domain_ptr) + ->add_template_row( + expr, + pre_guard, + contains_new_var ? + and_exprt(pre_guard, post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); } // pred abs domain else if(predabs) @@ -618,11 +658,14 @@ bool template_generator_baset::instantiate_custom_templates( if(contains_new_var) add_post_vars=true; - static_cast(domain_ptr)->add_template_row( - expr, pre_guard, - contains_new_var ? and_exprt(pre_guard, post_guard) : post_guard, - aux_expr, - contains_new_var ? domaint::OUT : domaint::LOOP); + static_cast(domain_ptr) + ->add_template_row( + expr, + pre_guard, + contains_new_var ? + and_exprt(pre_guard, post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); } else // neither pred abs, nor polyhedra { @@ -666,7 +709,7 @@ Function: template_generator_baset::instantiate_standard_domains \*******************************************************************/ void template_generator_baset::instantiate_standard_domains( - const local_SSAt &SSA) + const local_SSAt &SSA,bool recursive) { replace_mapt &renaming_map= std_invariants ? aux_renaming_map : post_renaming_map; @@ -678,44 +721,93 @@ void template_generator_baset::instantiate_standard_domains( domain_ptr= new equality_domaint(domain_number, renaming_map, var_specs, SSA.ns); } + else if(options.get_bool_option("heap")) + { + filter_heap_domain(); + domain_ptr=new heap_domaint(domain_number, renaming_map, var_specs, SSA.ns); + } else if(options.get_bool_option("intervals")) { domain_ptr= new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_interval_template( - var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_interval_template(var_specs, SSA.ns, + options.get_bool_option("context-sensitive") && + recursive); } else if(options.get_bool_option("zones")) { domain_ptr= new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_difference_template( - var_specs, SSA.ns); - static_cast(domain_ptr)->add_interval_template( - var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_difference_template(var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_interval_template(var_specs, SSA.ns); } else if(options.get_bool_option("octagons")) { domain_ptr= new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_sum_template( - var_specs, SSA.ns); - static_cast(domain_ptr)->add_difference_template( - var_specs, SSA.ns); - static_cast(domain_ptr)->add_interval_template( - var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_sum_template(var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_difference_template(var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_interval_template(var_specs, SSA.ns); } else if(options.get_bool_option("qzones")) { domain_ptr= new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_difference_template( - var_specs, SSA.ns); - static_cast(domain_ptr)->add_quadratic_template( - var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_difference_template(var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_quadratic_template(var_specs, SSA.ns); + } + else if(options.get_bool_option("heap-interval")) + { + filter_heap_interval_domain(); + domain_ptr= + new heap_interval_domaint(domain_number, renaming_map, var_specs, SSA.ns); + } +} + +void template_generator_baset::filter_heap_interval_domain() +{ + domaint::var_specst new_var_specs(var_specs); + var_specs.clear(); + for(domaint::var_specst::const_iterator v=new_var_specs.begin(); + v!=new_var_specs.end(); v++) + { + const domaint::vart &s=v->var; + + if(s.id()==ID_symbol && is_pointed(s) && + id2string(to_symbol_expr(s).get_identifier()).find(".")!= + std::string::npos) + continue; + + if(s.type().id()==ID_unsignedbv || + s.type().id()==ID_signedbv || + s.type().id()==ID_floatbv) + { + var_specs.push_back(*v); + continue; + } + + if(s.id()==ID_symbol && s.type().id()==ID_pointer) + { + // Filter out non-assigned OUT variables + if(v->kind!=domaint::OUT || + ssa_inlinert::get_original_identifier(to_symbol_expr(s))!= + to_symbol_expr(s).get_identifier()) + { + var_specs.push_back(*v); + continue; + } + } } } diff --git a/src/domains/template_generator_base.h b/src/domains/template_generator_base.h index 1c9428da2..6ee230a4a 100644 --- a/src/domains/template_generator_base.h +++ b/src/domains/template_generator_base.h @@ -73,6 +73,8 @@ class template_generator_baset:public messaget void filter_template_domain(); void filter_equality_domain(); + void filter_heap_domain(); + void filter_heap_interval_domain(); void add_var( const domaint::vart &var_to_add, @@ -102,7 +104,8 @@ class template_generator_baset:public messaget void get_pre_post_guards( const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, - exprt &pre_guard, exprt &post_guard); + exprt &pre_guard, + exprt &post_guard); void get_pre_var( const local_SSAt &SSA, local_SSAt::objectst::const_iterator o_it, @@ -122,7 +125,7 @@ class template_generator_baset:public messaget exprt &expr); virtual void handle_special_functions(const local_SSAt &SSA); - void instantiate_standard_domains(const local_SSAt &SSA); + void instantiate_standard_domains(const local_SSAt &SSA, bool recursive=false); bool instantiate_custom_templates(const local_SSAt &SSA); void rename_aux_post(symbol_exprt &expr) diff --git a/src/domains/template_generator_callingcontext.cpp b/src/domains/template_generator_callingcontext.cpp index 310ea4303..e4c51cbcb 100644 --- a/src/domains/template_generator_callingcontext.cpp +++ b/src/domains/template_generator_callingcontext.cpp @@ -93,13 +93,17 @@ void template_generator_callingcontextt::collect_variables_callingcontext( v_it!=cs_globals_in.end(); v_it++) { symbol_exprt dummy; - if(ssa_inlinert::find_corresponding_symbol(*v_it, globals_in, dummy)) + if(ssa_inlinert::find_corresponding_symbol(*v_it, globals_in, dummy) || + id2string(v_it->get_identifier()).find("dynamic_object$")!= + std::string::npos) + { add_var( *v_it, guard, guard, domaint::OUT, // the same for both forward and backward var_specs); + } } // TODO: actually, the context should contain both, @@ -113,6 +117,7 @@ void template_generator_callingcontextt::collect_variables_callingcontext( { std::set args; find_symbols(*a_it, args); + exprt arg=*a_it; add_vars(args, guard, guard, domaint::OUT, var_specs); } } diff --git a/src/domains/template_generator_ranking.cpp b/src/domains/template_generator_ranking.cpp index fab04e251..c7a27b0cc 100644 --- a/src/domains/template_generator_ranking.cpp +++ b/src/domains/template_generator_ranking.cpp @@ -133,11 +133,11 @@ void template_generator_rankingt::collect_variables_ranking( filter_ranking_domain(new_var_specs); #ifndef LEXICOGRAPHIC - static_cast(domain_ptr)->add_template( - new_var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_template(new_var_specs, SSA.ns); #else - static_cast(domain_ptr)->add_template( - new_var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_template(new_var_specs, SSA.ns); #endif var_specs.insert( diff --git a/src/domains/template_generator_summary.cpp b/src/domains/template_generator_summary.cpp index becceba5f..4b83c2d01 100644 --- a/src/domains/template_generator_summary.cpp +++ b/src/domains/template_generator_summary.cpp @@ -11,6 +11,7 @@ Author: Peter Schrammel #include "template_generator_summary.h" #include "equality_domain.h" #include "tpolyhedra_domain.h" +#include "domain.h" #include #include @@ -123,7 +124,9 @@ domaint::var_sett template_generator_summaryt::inout_vars() for(domaint::var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::IN || v->kind==domaint::OUT) + if(v->kind==domaint::IN || + v->kind==domaint::OUT || + v->kind==domaint::OUTHEAP) vars.insert(v->var); } return vars; diff --git a/src/domains/tpolyhedra_domain.cpp b/src/domains/tpolyhedra_domain.cpp index 470931c05..f8516167f 100644 --- a/src/domains/tpolyhedra_domain.cpp +++ b/src/domains/tpolyhedra_domain.cpp @@ -52,6 +52,31 @@ void tpolyhedra_domaint::initialize(valuet &value) } } +void tpolyhedra_domaint::initialize_in_templates(valuet &value, + std::map context_bounds) +{ + templ_valuet &v=static_cast(value); + v.resize(templ.size()); + for(std::size_t row=0; row context_bounds= + std::map()); virtual void join(valuet &value1, const valuet &value2); @@ -73,7 +76,8 @@ class tpolyhedra_domaint:public domaint exprt to_symb_post_constraints(const std::set &symb_rows); exprt get_row_symb_value_constraint( const rowt &row, - const row_valuet &row_value, bool geq=false); + const row_valuet &row_value, + bool geq=false); exprt get_row_symb_pre_constraint( const rowt &row, const row_valuet &row_value); @@ -97,13 +101,18 @@ class tpolyhedra_domaint:public domaint // printing virtual void output_value( - std::ostream &out, const valuet &value, const namespacet &ns) const; + std::ostream &out, + const valuet &value, + const namespacet &ns) const; virtual void output_domain( - std::ostream &out, const namespacet &ns) const; + std::ostream &out, + const namespacet &ns) const; // projection virtual void project_on_vars( - valuet &value, const var_sett &vars, exprt &result); + valuet &value, + const var_sett &vars, + exprt &result); unsigned template_size(); @@ -116,13 +125,17 @@ class tpolyhedra_domaint:public domaint kindt kind); void add_interval_template( - const var_specst &var_specs, const namespacet &ns); + const var_specst &var_specs, + const namespacet &ns, bool rec_cntx_sensitive=false); void add_difference_template( - const var_specst &var_specs, const namespacet &ns); + const var_specst &var_specs, + const namespacet &ns); void add_sum_template( - const var_specst &var_specs, const namespacet &ns); + const var_specst &var_specs, + const namespacet &ns); void add_quadratic_template( - const var_specst &var_specs, const namespacet &ns); + const var_specst &var_specs, + const namespacet &ns); symbol_exprt get_row_symb_value(const rowt &row); diff --git a/src/domains/util.cpp b/src/domains/util.cpp index ec12ad55a..4833531e6 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -132,8 +132,9 @@ void extend_expr_types(exprt &expr) expr.op1().type().id()==ID_signedbv)) { typet new_type=signedbv_typet(size0+size1+1); - expr=mult_exprt(typecast_exprt(expr.op0(), new_type), - typecast_exprt(expr.op1(), new_type)); + expr=mult_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); return; } else if(expr.op0().type().id()==ID_floatbv && diff --git a/src/domains/util.h b/src/domains/util.h index de0c316ef..790473db4 100644 --- a/src/domains/util.h +++ b/src/domains/util.h @@ -21,9 +21,14 @@ constant_exprt simplify_const(const exprt &expr); ieee_floatt simplify_const_float(const exprt &expr); mp_integer simplify_const_int(const exprt &expr); void pretty_print_termination_argument( - std::ostream &out, const namespacet &ns, const exprt &expr); + std::ostream &out, + const namespacet &ns, + const exprt &expr); void merge_and( - exprt & result, const exprt &expr1, const exprt &expr2, const namespacet &ns); + exprt & result, + const exprt &expr1, + const exprt &expr2, + const namespacet &ns); constant_exprt make_zero(const typet &type); constant_exprt make_one(const typet &type); constant_exprt make_minusone(const typet &type); diff --git a/src/solver/Makefile b/src/solver/Makefile index bd0fe97b7..b8ebeb8b4 100644 --- a/src/solver/Makefile +++ b/src/solver/Makefile @@ -1,7 +1,7 @@ SRC = summarizer_base.cpp summarizer_bw.cpp \ summarizer_bw_term.cpp summarizer_fw_contexts.cpp \ summarizer_fw.cpp summarizer_fw_term.cpp \ - summary.cpp summary_db.cpp + summary.cpp summary_db.cpp summarizer_rec_fw.cpp include ../config.inc include $(CBMC)/src/config.inc diff --git a/src/solver/a.dep b/src/solver/a.dep new file mode 100644 index 000000000..47fb9bcd2 --- /dev/null +++ b/src/solver/a.dep @@ -0,0 +1,301 @@ +summarizer_rec_fw.o: summarizer_rec_fw.cpp ../domains/ssa_analyzer.h \ + /home/sarbojit/2ls-master/cbmc/src/util/replace_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h \ + /home/sarbojit/2ls-master/cbmc/src/util/expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/type.h \ + /home/sarbojit/2ls-master/cbmc/src/util/source_location.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep.h \ + /home/sarbojit/2ls-master/cbmc/src/util/dstring.h \ + /home/sarbojit/2ls-master/cbmc/src/util/string_container.h \ + /home/sarbojit/2ls-master/cbmc/src/util/string_hash.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep_ids.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_types.h \ + /home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h \ + /home/sarbojit/2ls-master/cbmc/src/big-int/bigint.hh ../solver/summary.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_expr.h ../ssa/local_ssa.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_code.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program_template.h \ + /home/sarbojit/2ls-master/cbmc/src/util/namespace.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol_table.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol.h \ + /home/sarbojit/2ls-master/cbmc/src/langapi/language_util.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions_template.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_types.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol.h \ + ../domains/list_iterator.h ../domains/incremental_solver.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_pointers.h \ + /home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv.h \ + /home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h \ + /home/sarbojit/2ls-master/cbmc/src/util/expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/byte_operators.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_utils.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h \ + /home/sarbojit/2ls-master/cbmc/src/util/message.h \ + /home/sarbojit/2ls-master/cbmc/src/util/source_location.h \ + /home/sarbojit/2ls-master/cbmc/src/util/threeval.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_assignment.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_width.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_map.h \ + /home/sarbojit/2ls-master/cbmc/src/util/type.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_type.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/arrays.h \ + /home/sarbojit/2ls-master/cbmc/src/util/union_find.h \ + /home/sarbojit/2ls-master/cbmc/src/util/numbering.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/equality.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_conv.h \ + /home/sarbojit/2ls-master/cbmc/src/util/decision_procedure.h \ + /home/sarbojit/2ls-master/cbmc/src/util/message.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/functions.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/flattening/pointer_logic.h \ + /home/sarbojit/2ls-master/cbmc/src/util/numbering.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/refinement/bv_refinement.h \ + /home/sarbojit/2ls-master/cbmc/src/langapi/language_ui.h \ + /home/sarbojit/2ls-master/cbmc/src/util/language_file.h \ + /home/sarbojit/2ls-master/cbmc/src/util/ui_message.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck_glucose.h \ + /home/sarbojit/2ls-master/cbmc/src/solvers/sat/cnf.h ../domains/domain.h \ + /home/sarbojit/2ls-master/cbmc/src/util/i2string.h ../domains/util.h \ + /home/sarbojit/2ls-master/cbmc/src/util/arith_tools.h \ + /home/sarbojit/2ls-master/cbmc/src/util/ieee_float.h \ + /home/sarbojit/2ls-master/cbmc/src/util/format_spec.h \ + ../ssa/ssa_domain.h /home/sarbojit/2ls-master/cbmc/src/analyses/ai.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h \ + ../ssa/assignments.h ../ssa/ssa_object.h ../ssa/ssa_pointed_objects.h \ + ../ssa/ssa_heap_domain.h \ + /home/sarbojit/2ls-master/cbmc/src/analyses/static_analysis.h \ + ../ssa/ssa_value_set.h ../ssa/guard_map.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h \ + ../ssa/may_alias_analysis.h ../domains/strategy_solver_base.h \ + ../domains/incremental_solver.h ../domains/template_generator_base.h \ + /home/sarbojit/2ls-master/cbmc/src/util/options.h ../ssa/ssa_unwinder.h \ + ../ssa/unwindable_local_ssa.h ../ssa/local_ssa.h ../ssa/ssa_db.h \ + ../ssa/unwindable_local_ssa.h ../domains/template_gen_rec_summary.h \ + ../domains/template_generator_summary.h summarizer_rec_fw.h \ + /home/sarbojit/2ls-master/cbmc/src/util/time_stopping.h \ + ../ssa/ssa_inliner.h ../solver/summary_db.h ../solver/summary.h \ + /home/sarbojit/2ls-master/cbmc/src/util/json.h ../ssa/ssa_db.h \ + summarizer_fw.h summarizer_base.h + +../domains/ssa_analyzer.h: + +/home/sarbojit/2ls-master/cbmc/src/util/replace_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h: + +/home/sarbojit/2ls-master/cbmc/src/util/expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/type.h: + +/home/sarbojit/2ls-master/cbmc/src/util/source_location.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep.h: + +/home/sarbojit/2ls-master/cbmc/src/util/dstring.h: + +/home/sarbojit/2ls-master/cbmc/src/util/string_container.h: + +/home/sarbojit/2ls-master/cbmc/src/util/string_hash.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep_ids.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_types.h: + +/home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h: + +/home/sarbojit/2ls-master/cbmc/src/big-int/bigint.hh: + +../solver/summary.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_expr.h: + +../ssa/local_ssa.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_code.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program_template.h: + +/home/sarbojit/2ls-master/cbmc/src/util/namespace.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol_table.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol.h: + +/home/sarbojit/2ls-master/cbmc/src/langapi/language_util.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions_template.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_types.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol.h: + +../domains/list_iterator.h: + +../domains/incremental_solver.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_pointers.h: + +/home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv.h: + +/home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h: + +/home/sarbojit/2ls-master/cbmc/src/util/expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/byte_operators.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/bv_utils.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h: + +/home/sarbojit/2ls-master/cbmc/src/util/message.h: + +/home/sarbojit/2ls-master/cbmc/src/util/source_location.h: + +/home/sarbojit/2ls-master/cbmc/src/util/threeval.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_assignment.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_width.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_map.h: + +/home/sarbojit/2ls-master/cbmc/src/util/type.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/boolbv_type.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/arrays.h: + +/home/sarbojit/2ls-master/cbmc/src/util/union_find.h: + +/home/sarbojit/2ls-master/cbmc/src/util/numbering.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/equality.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop_conv.h: + +/home/sarbojit/2ls-master/cbmc/src/util/decision_procedure.h: + +/home/sarbojit/2ls-master/cbmc/src/util/message.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/literal_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/prop/prop.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/functions.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/flattening/pointer_logic.h: + +/home/sarbojit/2ls-master/cbmc/src/util/numbering.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/refinement/bv_refinement.h: + +/home/sarbojit/2ls-master/cbmc/src/langapi/language_ui.h: + +/home/sarbojit/2ls-master/cbmc/src/util/language_file.h: + +/home/sarbojit/2ls-master/cbmc/src/util/ui_message.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/satcheck_glucose.h: + +/home/sarbojit/2ls-master/cbmc/src/solvers/sat/cnf.h: + +../domains/domain.h: + +/home/sarbojit/2ls-master/cbmc/src/util/i2string.h: + +../domains/util.h: + +/home/sarbojit/2ls-master/cbmc/src/util/arith_tools.h: + +/home/sarbojit/2ls-master/cbmc/src/util/ieee_float.h: + +/home/sarbojit/2ls-master/cbmc/src/util/format_spec.h: + +../ssa/ssa_domain.h: + +/home/sarbojit/2ls-master/cbmc/src/analyses/ai.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h: + +../ssa/assignments.h: + +../ssa/ssa_object.h: + +../ssa/ssa_pointed_objects.h: + +../ssa/ssa_heap_domain.h: + +/home/sarbojit/2ls-master/cbmc/src/analyses/static_analysis.h: + +../ssa/ssa_value_set.h: + +../ssa/guard_map.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h: + +../ssa/may_alias_analysis.h: + +../domains/strategy_solver_base.h: + +../domains/incremental_solver.h: + +../domains/template_generator_base.h: + +/home/sarbojit/2ls-master/cbmc/src/util/options.h: + +../ssa/ssa_unwinder.h: + +../ssa/unwindable_local_ssa.h: + +../ssa/local_ssa.h: + +../ssa/ssa_db.h: + +../ssa/unwindable_local_ssa.h: + +../domains/template_gen_rec_summary.h: + +../domains/template_generator_summary.h: + +summarizer_rec_fw.h: + +/home/sarbojit/2ls-master/cbmc/src/util/time_stopping.h: + +../ssa/ssa_inliner.h: + +../solver/summary_db.h: + +../solver/summary.h: + +/home/sarbojit/2ls-master/cbmc/src/util/json.h: + +../ssa/ssa_db.h: + +summarizer_fw.h: + +summarizer_base.h: diff --git a/src/solver/summarizer_base.cpp b/src/solver/summarizer_base.cpp index 9a951bda9..553b0bd3f 100644 --- a/src/solver/summarizer_base.cpp +++ b/src/solver/summarizer_base.cpp @@ -194,7 +194,7 @@ exprt summarizer_baset::compute_calling_context( solver.new_context(); solver << SSA.get_enabling_exprs(); - solver << ssa_inliner.get_summaries(SSA); + solver << ssa_inliner.get_summaries_to_loc(SSA, n_it->location); ssa_analyzert analyzer; analyzer.set_message_handler(get_message_handler()); diff --git a/src/solver/summarizer_bw.h b/src/solver/summarizer_bw.h index 540f8fedd..638a70733 100644 --- a/src/solver/summarizer_bw.h +++ b/src/solver/summarizer_bw.h @@ -23,14 +23,13 @@ Author: Peter Schrammel class summarizer_bwt:public summarizer_baset { public: - explicit summarizer_bwt( - optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner): - summarizer_baset( - _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + summarizer_bwt( + optionst &options, + summary_dbt &summary_db, + ssa_dbt &ssa_db, + ssa_unwindert &ssa_unwinder, + ssa_inlinert &ssa_inliner): + summarizer_baset(options, summary_db, ssa_db, ssa_unwinder, ssa_inliner) { } diff --git a/src/solver/summarizer_fw.cpp b/src/solver/summarizer_fw.cpp index 1048a2b0f..7d5cdc90c 100644 --- a/src/solver/summarizer_fw.cpp +++ b/src/solver/summarizer_fw.cpp @@ -15,14 +15,9 @@ Author: Peter Schrammel #include #include "summarizer_fw.h" -#include "summary_db.h" #include #include -#include - -#include -#include // #define SHOW_WHOLE_RESULT @@ -71,6 +66,7 @@ void summarizer_fwt::compute_summary_rec( summary.params=SSA.params; summary.globals_in=SSA.globals_in; summary.globals_out=SSA.globals_out; + summary.set_value_domains(SSA); summary.fw_precondition=precondition; if(!options.get_bool_option("havoc")) @@ -171,6 +167,16 @@ void summarizer_fwt::do_summary( debug() << "whole result: " << from_expr(SSA.ns, "", whole_result) << eom; #endif + if(options.get_bool_option("heap")) + { + analyzer.update_heap_out(summary.globals_out); + const exprt advancer_bindings=analyzer.input_heap_bindings(); + if(!advancer_bindings.is_true()) + { + summary.aux_precondition=advancer_bindings; + } + } + if(context_sensitive && !summary.fw_precondition.is_true()) { summary.fw_transformer= @@ -208,7 +214,8 @@ void summarizer_fwt::inline_summaries( f_it!=n_it->function_calls.end(); f_it++) { assert(f_it->function().id()==ID_symbol); // no function pointers - if(!check_call_reachable( + if(to_symbol_expr(f_it->function()).get_identifier()==function_name || + !check_call_reachable( function_name, SSA, n_it, f_it, precondition, true)) { continue; diff --git a/src/solver/summarizer_fw.h b/src/solver/summarizer_fw.h index f21255c55..a57f4636f 100644 --- a/src/solver/summarizer_fw.h +++ b/src/solver/summarizer_fw.h @@ -23,14 +23,13 @@ Author: Peter Schrammel class summarizer_fwt:public summarizer_baset { public: - explicit summarizer_fwt( - optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner): - summarizer_baset( - _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + summarizer_fwt( + optionst &options, + summary_dbt &summary_db, + ssa_dbt &ssa_db, + ssa_unwindert &ssa_unwinder, + ssa_inlinert &ssa_inliner): + summarizer_baset(options, summary_db, ssa_db, ssa_unwinder, ssa_inliner) { } @@ -40,7 +39,7 @@ class summarizer_fwt:public summarizer_baset const exprt &precondition, bool context_sensitive); - void inline_summaries( + virtual void inline_summaries( const function_namet &function_name, local_SSAt &SSA, const exprt &precondition, diff --git a/src/solver/summarizer_rec_fw.cpp b/src/solver/summarizer_rec_fw.cpp new file mode 100755 index 000000000..200727ed3 --- /dev/null +++ b/src/solver/summarizer_rec_fw.cpp @@ -0,0 +1,202 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +/* + * File: summarizer_rec_fwt.cpp + * Author: sarbojit + * + * Created on 13 March, 2018, 4:53 PM + */ + +#include +#include +#include +#include "summarizer_rec_fw.h" + +void summarizer_rec_fwt::compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name);// TODO: make const + + bool recursive=false; + for(local_SSAt::nodet node:SSA.nodes) + { + for(function_application_exprt f_call:node.function_calls) + if(function_name==to_symbol_expr(f_call.function()).get_identifier()) + recursive=true; + } + if(recursive)//if this_function_is_recursive + { + //inline other non-recursive functions in the same SCC of the call graph + //this makes it self recursive + } + + // recursively compute summaries for non-recursive function calls + inline_summaries(function_name, SSA, precondition, context_sensitive); + + status() << "Analyzing function " << function_name << eom; + #if 0 + { + std::ostringstream out; + out << "Function body for " << function_name + << " to be analyzed: " << std::endl; + for(const auto &node : SSA.nodes) + { + if(!node.empty()) + node.output(out, SSA.ns); + } + out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) + << "\n"; + debug() << out.str() << eom; + } + #endif + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + //if(recursive) summary.fw_precondition=true; + summary.fw_precondition=precondition; + + + if(!options.get_bool_option("havoc")) + { + do_summary(function_name, SSA, summary, true_exprt(), context_sensitive,recursive); + } + + #if 0 + if(!options.get_bool_option("competition-mode")) + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary.output(out, SSA.ns); + status() << out.str() << eom; + } + #endif + + // store summary in db + //summary_db.put(function_name, summary); + + /*if(!options.get_bool_option("competition-mode")) + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + status() << out.str() << eom; + }*/ +} + +void summarizer_rec_fwt::do_summary( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary, + exprt cond, + bool context_sensitive,bool recursive) +{ + status() << "Computing summary" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // analyze + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + + tmpl_rename_mapt templ_maps; + + template_gen_rec_summaryt template_generator( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator( + function_name,solver.next_domain_number(), SSA, templ_maps, true,recursive); + + exprt::operandst conds; + conds.reserve(5); + conds.push_back(cond); + if(!(recursive && context_sensitive)) conds.push_back(summary.fw_precondition); + conds.push_back(ssa_inliner.get_summaries(SSA)); + +#ifdef REUSE_INVARIANTS + if(summary_db.exists(function_name)) // reuse existing invariants + { + const exprt &old_inv=summary_db.get(function_name).fw_invariant; + exprt inv=ssa_unwinder.get(function_name).rename_invariant(old_inv); + conds.push_back(inv); + +#if 0 + std::ostringstream out; + out << "(original inv)" << from_expr(SSA.ns, "", old_inv) << "\n"; + debug() << out.str() << eom; + out << "(renamed inv)" << from_expr(SSA.ns, "", inv) << "\n"; + debug() << out.str() << eom; +#endif + } +#endif + + cond=conjunction(conds); + + std::map context_bounds; + if(recursive && context_sensitive) + { + context_bounds = get_context_bounds(summary.fw_precondition); + + /*for(std::pair p : context_bounds) + { + debug()< summarizer_rec_fwt::get_context_bounds( + exprt con) +{ + std::map cntxt_bound; + for(exprt op:con.operands()) + { + assert(op.id()==ID_le); + cntxt_bound.insert(std::pair + (op.op0(),to_constant_expr(op.op1()))); + } + return cntxt_bound; +} \ No newline at end of file diff --git a/src/solver/summarizer_rec_fw.h b/src/solver/summarizer_rec_fw.h new file mode 100755 index 000000000..0501eff5d --- /dev/null +++ b/src/solver/summarizer_rec_fw.h @@ -0,0 +1,50 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +/* + * File: summarizer_rec_fwt.h + * Author: sarbojit + * + * Created on 13 March, 2018, 4:52 PM + */ + +#ifndef SUMMARIZER_REC_FWT_H +#define SUMMARIZER_REC_FWT_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_fw.h" + +class summarizer_rec_fwt:public summarizer_fwt { +public: + explicit summarizer_rec_fwt(optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_fwt( + _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } +protected: + virtual void compute_summary_rec(const function_namet &, + const exprt &, bool); + + virtual void do_summary(const function_namet &, + local_SSAt &SSA, summaryt &summary, exprt cond, + bool context_sensitive, bool recursive); + std::map get_context_bounds(exprt); +}; + +#endif /* SUMMARIZER_REC_FWT_H */ + diff --git a/src/solver/summary.cpp b/src/solver/summary.cpp index 3a74ba628..ffa76f7ae 100644 --- a/src/solver/summary.cpp +++ b/src/solver/summary.cpp @@ -173,6 +173,26 @@ void summaryt::join(const summaryt &new_summary) /*******************************************************************\ +Function: summaryt::set_value_domains + + Inputs: + + Outputs: + + Purpose: Get value domain for last location from SSA. + +\*******************************************************************/ + +void summaryt::set_value_domains(const local_SSAt &SSA) +{ + const local_SSAt::locationt &entry_loc=SSA.nodes.begin()->location; + const local_SSAt::locationt &exit_loc=(--SSA.nodes.end())->location; + value_domain_in=SSA.ssa_value_ai[entry_loc]; + value_domain_out=SSA.ssa_value_ai[exit_loc]; +} + +/*******************************************************************\ + Function: threeval2string Inputs: diff --git a/src/solver/summary.h b/src/solver/summary.h index 840a42230..f4cd32205 100644 --- a/src/solver/summary.h +++ b/src/solver/summary.h @@ -13,6 +13,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include typedef enum {YES, NO, UNKNOWN} threevalt; @@ -32,13 +33,14 @@ class summaryt bw_postcondition(nil_exprt()), bw_transformer(nil_exprt()), bw_invariant(nil_exprt()), + aux_precondition(nil_exprt()), termination_argument(nil_exprt()), terminates(UNKNOWN), mark_recompute(false) {} var_listt params; var_sett globals_in, globals_out; - + ssa_value_domaint value_domain_in, value_domain_out; predicatet fw_precondition; // accumulated calling contexts (over-approx) // predicatet fw_postcondition; // we are not projecting that out currently predicatet fw_transformer; // forward summary (over-approx) @@ -48,6 +50,8 @@ class summaryt predicatet bw_transformer; // backward summary (over- or under-approx) predicatet bw_invariant; // backward invariant (over- or under-approx) + predicatet aux_precondition; + predicatet termination_argument; threevalt terminates; @@ -58,6 +62,8 @@ class summaryt void join(const summaryt &new_summary); + void set_value_domains(const local_SSAt &SSA); + protected: void combine_or(exprt &olde, const exprt &newe); void combine_and(exprt &olde, const exprt &newe); diff --git a/src/ssa/Makefile b/src/ssa/Makefile index 0c4c0263f..81460b00c 100644 --- a/src/ssa/Makefile +++ b/src/ssa/Makefile @@ -1,9 +1,11 @@ -SRC = local_ssa.cpp \ - ssa_domain.cpp translate_union_member.cpp malloc_ssa.cpp \ +SRC = local_ssa.cpp ssa_var_collector.cpp \ + ssa_domain.cpp translate_union_member.cpp \ + malloc_ssa.cpp ssa_pointed_objects.cpp ssa_heap_domain.cpp \ guard_map.cpp ssa_object.cpp assignments.cpp ssa_dereference.cpp \ ssa_value_set.cpp address_canonizer.cpp simplify_ssa.cpp \ ssa_build_goto_trace.cpp ssa_inliner.cpp ssa_unwinder.cpp \ - unwindable_local_ssa.cpp ssa_db.cpp + unwindable_local_ssa.cpp ssa_db.cpp \ + ssa_pointed_objects.cpp ssa_heap_domain.cpp may_alias_analysis.cpp include ../config.inc include $(CBMC)/src/config.inc diff --git a/src/ssa/a.dep b/src/ssa/a.dep new file mode 100644 index 000000000..424de03ab --- /dev/null +++ b/src/ssa/a.dep @@ -0,0 +1,109 @@ +may_alias_analysis.o: may_alias_analysis.cpp may_alias_analysis.h \ + /home/sarbojit/2ls-master/cbmc/src/analyses/ai.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol_table.h \ + /home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol.h \ + /home/sarbojit/2ls-master/cbmc/src/util/expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/type.h \ + /home/sarbojit/2ls-master/cbmc/src/util/source_location.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep.h \ + /home/sarbojit/2ls-master/cbmc/src/util/dstring.h \ + /home/sarbojit/2ls-master/cbmc/src/util/string_container.h \ + /home/sarbojit/2ls-master/cbmc/src/util/string_hash.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep_ids.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_code.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program_template.h \ + /home/sarbojit/2ls-master/cbmc/src/util/namespace.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_expr.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_types.h \ + /home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h \ + /home/sarbojit/2ls-master/cbmc/src/big-int/bigint.hh \ + /home/sarbojit/2ls-master/cbmc/src/langapi/language_util.h \ + /home/sarbojit/2ls-master/cbmc/src/util/irep.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions_template.h \ + /home/sarbojit/2ls-master/cbmc/src/util/std_types.h \ + /home/sarbojit/2ls-master/cbmc/src/util/symbol.h \ + /home/sarbojit/2ls-master/cbmc/src/util/union_find.h \ + /home/sarbojit/2ls-master/cbmc/src/util/numbering.h ssa_value_set.h \ + ssa_object.h ssa_pointed_objects.h \ + /home/sarbojit/2ls-master/cbmc/src/util/expr.h \ + /home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h \ + ssa_heap_domain.h \ + /home/sarbojit/2ls-master/cbmc/src/analyses/static_analysis.h + +may_alias_analysis.h: + +/home/sarbojit/2ls-master/cbmc/src/analyses/ai.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_model.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol_table.h: + +/home/sarbojit/2ls-master/cbmc/src/util/hash_cont.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol.h: + +/home/sarbojit/2ls-master/cbmc/src/util/expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/type.h: + +/home/sarbojit/2ls-master/cbmc/src/util/source_location.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep.h: + +/home/sarbojit/2ls-master/cbmc/src/util/dstring.h: + +/home/sarbojit/2ls-master/cbmc/src/util/string_container.h: + +/home/sarbojit/2ls-master/cbmc/src/util/string_hash.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep_ids.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_code.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_program_template.h: + +/home/sarbojit/2ls-master/cbmc/src/util/namespace.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_expr.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_types.h: + +/home/sarbojit/2ls-master/cbmc/src/util/mp_arith.h: + +/home/sarbojit/2ls-master/cbmc/src/big-int/bigint.hh: + +/home/sarbojit/2ls-master/cbmc/src/langapi/language_util.h: + +/home/sarbojit/2ls-master/cbmc/src/util/irep.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions_template.h: + +/home/sarbojit/2ls-master/cbmc/src/util/std_types.h: + +/home/sarbojit/2ls-master/cbmc/src/util/symbol.h: + +/home/sarbojit/2ls-master/cbmc/src/util/union_find.h: + +/home/sarbojit/2ls-master/cbmc/src/util/numbering.h: + +ssa_value_set.h: + +ssa_object.h: + +ssa_pointed_objects.h: + +/home/sarbojit/2ls-master/cbmc/src/util/expr.h: + +/home/sarbojit/2ls-master/cbmc/src/goto-programs/goto_functions.h: + +ssa_heap_domain.h: + +/home/sarbojit/2ls-master/cbmc/src/analyses/static_analysis.h: diff --git a/src/ssa/address_canonizer.cpp b/src/ssa/address_canonizer.cpp index 2ffab3ff1..d3581611f 100644 --- a/src/ssa/address_canonizer.cpp +++ b/src/ssa/address_canonizer.cpp @@ -12,6 +12,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "address_canonizer.h" +#include "ssa_pointed_objects.h" /*******************************************************************\ @@ -75,6 +76,15 @@ exprt address_canonizer( return sum; } + else if(object.id()==ID_symbol && is_iterator(object)) + { + // address of iterator is dereferenced to a corresponding symbol - + // will be bound to real address during analysis + symbol_exprt iterator_addr( + id2string(to_symbol_expr(object).get_identifier())+"'addr", + address.type()); + return iterator_addr; + } else return address; } diff --git a/src/ssa/assignments.cpp b/src/ssa/assignments.cpp index 2a5bc1c6b..556d0d037 100644 --- a/src/ssa/assignments.cpp +++ b/src/ssa/assignments.cpp @@ -7,9 +7,11 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ #include +#include #include "assignments.h" #include "ssa_dereference.h" +#include "local_ssa.h" /*******************************************************************\ @@ -38,6 +40,14 @@ void assignmentst::build_assignment_map( const code_assignt &code_assign=to_code_assign(it->code); exprt lhs_deref=dereference(code_assign.lhs(), ssa_value_ai[it], "", ns); assign(lhs_deref, it, ns); + exprt lhs_symbolic_deref=symbolic_dereference(code_assign.lhs(), ns); + assign(lhs_symbolic_deref, it, ns); + + assign_symbolic_rhs(code_assign.rhs(), it, ns); + } + else if(it->is_assert()) + { + assign_symbolic_rhs(it->guard, it, ns); } else if(it->is_decl()) { @@ -49,19 +59,61 @@ void assignmentst::build_assignment_map( const code_function_callt &code_function_call= to_code_function_call(it->code); - // functions may alter state almost arbitrarily: - // * any global-scoped variables - // * any dirty locals + // Get information from ssa_heap_analysis + auto n_it=it; + ++n_it; + const irep_idt fname=to_symbol_expr( + code_function_call.function()).get_identifier(); + std::list new_objects; + std::set modified_objects; - for(objectst::const_iterator - o_it=ssa_objects.dirty_locals.begin(); - o_it!=ssa_objects.dirty_locals.end(); o_it++) - assign(*o_it, it, ns); + if(ssa_heap_analysis.has_location(n_it)) + { + new_objects=ssa_heap_analysis[n_it].new_caller_objects(fname, it); + modified_objects=ssa_heap_analysis[n_it].modified_objects(fname); + } + + // Assign new objects + for(auto &o : new_objects) + { + assign(o, it, ns); + } for(objectst::const_iterator - o_it=ssa_objects.globals.begin(); + o_it=ssa_objects.globals.begin(); o_it!=ssa_objects.globals.end(); o_it++) - assign(*o_it, it, ns); + { + if(id2string(o_it->get_identifier())==id2string(fname)+"#return_value") + assign(*o_it, it, ns); + } + + // Assign all modified objects + for(const exprt &modified : modified_objects) + { + const exprt arg= + ssa_heap_analysis[n_it].function_map.at(fname). + corresponding_expr(modified, code_function_call.arguments(), 0); + + if(arg!=modified) + { + const exprt arg_deref=dereference(arg, ssa_value_ai[it], "", ns); + assign(arg_deref, it, ns); + + std::set symbols; + find_symbols(arg_deref, symbols); + for(const symbol_exprt &symbol : symbols) + { + if(symbol.type()==arg_deref.type()) + { + auto &aliases=ssa_value_ai[n_it](symbol, ns).value_set; + for(auto &alias : aliases) + { + assign(alias.get_expr(), it, ns); + } + } + } + } + } // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) @@ -121,7 +173,8 @@ void assignmentst::assign( // object? ssa_objectt ssa_object(lhs, ns); - if(ssa_object) + if(ssa_object && + !ssa_object.is_unknown_obj()) // unknown objects are just placeholders { assign(ssa_object, loc, ns); } @@ -184,6 +237,38 @@ void assignmentst::assign( /*******************************************************************\ +Function: assignmentst::build_assertion + + Inputs: + + Outputs: + + Purpose: Adds to assignments dereferences from assertion + +\*******************************************************************/ + +void assignmentst::assign_symbolic_rhs( + const exprt &expr, + const locationt &loc, + const namespacet &ns) +{ + exprt rhs_symbolic_deref=symbolic_dereference(expr, ns); + ssa_objectt rhs_object(rhs_symbolic_deref, ns); + + if(has_symbolic_deref(rhs_symbolic_deref) && rhs_object) + { + rhs_symbolic_deref.set("#is_rhs_assign", true); + assign(rhs_symbolic_deref, loc, ns); + } + else if(has_symbolic_deref(rhs_symbolic_deref)) + { + forall_operands(it, expr) + assign_symbolic_rhs(*it, loc, ns); + } +} + +/*******************************************************************\ + Function: assignmentst::output Inputs: diff --git a/src/ssa/assignments.h b/src/ssa/assignments.h index 28b36d608..f316e158f 100644 --- a/src/ssa/assignments.h +++ b/src/ssa/assignments.h @@ -21,6 +21,7 @@ class assignmentst const ssa_objectst &ssa_objects; const ssa_value_ait &ssa_value_ai; + const ssa_heap_analysist &ssa_heap_analysis; typedef ssa_objectst::objectst objectst; @@ -42,13 +43,15 @@ class assignmentst return it->second; } - explicit assignmentst( + assignmentst( const goto_programt &_goto_program, const namespacet &_ns, const ssa_objectst &_ssa_objects, - const ssa_value_ait &_ssa_value_ai): + const ssa_value_ait &_ssa_value_ai, + const ssa_heap_analysist &_ssa_heap_analysis): ssa_objects(_ssa_objects), - ssa_value_ai(_ssa_value_ai) + ssa_value_ai(_ssa_value_ai), + ssa_heap_analysis(_ssa_heap_analysis) { build_assignment_map(_goto_program, _ns); } @@ -62,11 +65,18 @@ class assignmentst void build_assignment_map(const goto_programt &, const namespacet &); void assign( - const exprt &lhs, locationt, + const exprt &lhs, + locationt, const namespacet &ns); void assign( - const ssa_objectt &lhs, locationt, + const ssa_objectt &lhs, + locationt, + const namespacet &ns); + + void assign_symbolic_rhs( + const exprt &expr, + const locationt &loc, const namespacet &ns); }; diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index f86fbb092..65cb28415 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -22,7 +22,6 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "local_ssa.h" -#include "malloc_ssa.h" #include "ssa_dereference.h" #include "address_canonizer.h" @@ -58,6 +57,7 @@ void local_SSAt::build_SSA() build_guard(i_it); build_assertions(i_it); build_function_call(i_it); + build_unknown_objs(i_it); } // collect custom templates in loop heads @@ -136,23 +136,34 @@ void local_SSAt::get_globals( << from_expr(ns, "", read_lhs(it->get_expr(), loc)) << std::endl; #endif - if(!with_returns && - id2string(it->get_identifier()).find( - "#return_value")!=std::string::npos) + if(!with_returns && !is_pointed(it->get_expr()) && + id2string(it->get_identifier()).find("#return_value")!= + std::string::npos) continue; // filter out return values of other functions if(with_returns && returns_for_function!="" && - id2string(it->get_identifier()).find( - "#return_value")!=std::string::npos && - id2string(it->get_identifier()).find( - id2string(returns_for_function)+"#return_value")==std::string::npos) + id2string(it->get_identifier()).find("#return_value")== + id2string(it->get_identifier()).size()- + std::string("#return_value").size() && + id2string(it->get_identifier()).find( + id2string(returns_for_function)+"#return_value")==std::string::npos) continue; + const exprt &root_obj=it->get_root_object(); + if(is_ptr_object(root_obj)) + { + const symbolt *symbol; + irep_idt ptr_obj_id=root_obj.get(ID_ptr_object); + if(ns.lookup(ptr_obj_id, symbol)) + continue; + } + if(rhs_value) { const exprt &expr=read_rhs(it->get_expr(), loc); globals.insert(to_symbol_expr(expr)); + std::cout<get_expr())<<"\n"; } else { @@ -190,9 +201,10 @@ void local_SSAt::collect_custom_templates() if(nn_it->templates.empty()) continue; - n_it->loophead->templates.insert(n_it->loophead->templates.end(), - nn_it->templates.begin(), - nn_it->templates.end()); + n_it->loophead->templates.insert( + n_it->loophead->templates.end(), + nn_it->templates.begin(), + nn_it->templates.end()); nn_it->templates.clear(); } } @@ -451,7 +463,28 @@ void local_SSAt::build_transfer(locationt loc) exprt deref_lhs=dereference(code_assign.lhs(), loc); exprt deref_rhs=dereference(code_assign.rhs(), loc); - assign_rec(deref_lhs, deref_rhs, true_exprt(), loc); + if(deref_lhs.get_bool("#heap_access") || deref_rhs.get_bool("#heap_access")) + { + exprt symbolic_deref_lhs=symbolic_dereference(code_assign.lhs(), ns); + const exprt rhs=concretise_symbolic_deref_rhs(code_assign.rhs(), ns, loc); + + if(deref_lhs.get_bool("#heap_access") && + has_symbolic_deref(symbolic_deref_lhs)) + { + assign_rec(symbolic_deref_lhs, rhs, true_exprt(), loc); + assign_rec( + deref_lhs, + name(ssa_objectt(symbolic_deref_lhs, ns), OUT, loc), + true_exprt(), + loc); + } + else + { + assign_rec(deref_lhs, rhs, true_exprt(), loc); + } + } + else + assign_rec(deref_lhs, deref_rhs, true_exprt(), loc); } } @@ -528,22 +561,29 @@ void local_SSAt::build_function_call(locationt loc) return; } - f=to_function_application_expr(read_rhs(f, loc)); assert(f.function().id()==ID_symbol); // no function pointers + + f=to_function_application_expr(read_rhs(f, loc)); + irep_idt fname=to_symbol_expr(f.function()).get_identifier(); // add equalities for arguments unsigned i=0; - for(exprt::operandst::iterator it=f.arguments().begin(); - it!=f.arguments().end(); ++it, ++i) + for(exprt &a : f.arguments()) { - symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - "#arg"+i2string(i), it->type()); - n_it->equalities.push_back(equal_exprt(*it, arg)); - *it=arg; + if(a.is_constant() || + (a.id()==ID_typecast && to_typecast_expr(a).op().is_constant())) + { + const std::string arg_name= + id2string(fname)+"#arg"+i2string(i)+"#"+ + i2string(loc->location_number); + symbol_exprt arg(arg_name, a.type()); + n_it->equalities.push_back(equal_exprt(a, arg)); + a=arg; + } + ++i; } - n_it->function_calls.push_back( - to_function_application_expr(f)); + n_it->function_calls.push_back(to_function_application_expr(f)); } } @@ -671,7 +711,11 @@ void local_SSAt::build_assertions(locationt loc) { if(loc->is_assert()) { - exprt c=read_rhs(loc->guard, loc); + const exprt deref_rhs=dereference(loc->guard, loc); + collect_iterators_rhs(deref_rhs, loc); + + const exprt rhs=concretise_symbolic_deref_rhs(loc->guard, ns, loc); + exprt c=read_rhs(rhs, loc); exprt g=guard_symbol(loc); (--nodes.end())->assertions.push_back(implies_exprt(g, c)); } @@ -696,8 +740,10 @@ void local_SSAt::assertions_to_constraints() n_it!=nodes.end(); n_it++) { - n_it->constraints.insert(n_it->constraints.end(), - n_it->assertions.begin(), n_it->assertions.end()); + n_it->constraints.insert( + n_it->constraints.end(), + n_it->assertions.begin(), + n_it->assertions.end()); } } @@ -1021,9 +1067,10 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const else if(expr.id()==ID_index) { const index_exprt &index_expr=to_index_expr(expr); - return index_exprt(read_rhs(index_expr.array(), loc), - read_rhs(index_expr.index(), loc), - expr.type()); + return index_exprt( + read_rhs(index_expr.array(), loc), + read_rhs(index_expr.index(), loc), + expr.type()); } ssa_objectt object(expr, ns); @@ -1038,6 +1085,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const return tmp; } +#if 0 // Argument is a struct-typed ssa object? // May need to split up into members. const typet &type=ns.follow(expr.type()); @@ -1063,6 +1111,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const return result; } +#endif // is this an object we track? if(ssa_objects.objects.find(object)!= @@ -1146,7 +1195,10 @@ symbol_exprt local_SSAt::name( unsigned cnt=loc->location_number; irep_idt new_id=id2string(id)+"#"+ - (kind==PHI?"phi":kind==LOOP_BACK?"lb":kind==LOOP_SELECT?"ls":"")+ + (kind==PHI?"phi": + kind==LOOP_BACK?"lb": + kind==LOOP_SELECT?"ls": + kind==OBJECT_SELECT?"os":"")+ i2string(cnt)+ (kind==LOOP_SELECT?std::string(""):suffix); @@ -1159,6 +1211,8 @@ symbol_exprt local_SSAt::name( if(object.get_expr().source_location().is_not_nil()) new_symbol_expr.add_source_location()=object.get_expr().source_location(); + copy_pointed_info(new_symbol_expr, object.get_expr()); + return new_symbol_expr; } @@ -1208,6 +1262,8 @@ symbol_exprt local_SSAt::name_input(const ssa_objectt &object) const if(object.get_expr().source_location().is_not_nil()) new_symbol_expr.add_source_location()=object.get_expr().source_location(); + copy_pointed_info(new_symbol_expr, object.get_expr()); + return new_symbol_expr; } @@ -1287,6 +1343,9 @@ void local_SSAt::assign_rec( if(assigned.find(lhs_object)!=assigned.end()) { + collect_iterators_lhs(lhs_object, loc); + collect_iterators_rhs(rhs, loc); + exprt ssa_rhs=read_rhs(rhs, loc); const symbol_exprt ssa_symbol=name(lhs_object, OUT, loc); @@ -1344,7 +1403,46 @@ void local_SSAt::assign_rec( else if(lhs.id()==ID_if) { const if_exprt &if_expr=to_if_expr(lhs); - assign_rec(if_expr.true_case(), rhs, and_exprt(guard, if_expr.cond()), loc); + + exprt::operandst other_cond_conj; + if(if_expr.true_case().get_bool("#heap_access") && + if_expr.cond().id()==ID_equal) + { + const exprt heap_object=if_expr.true_case(); + const ssa_objectt ptr_object(to_equal_expr(if_expr.cond()).lhs(), ns); + if(ptr_object) + { + const irep_idt ptr_id=ptr_object.get_identifier(); + const exprt cond=read_rhs(if_expr.cond(), loc); + + for(const dyn_obj_assignt &do_assign : dyn_obj_assigns[heap_object]) + { + if(!alias_analysis[loc].aliases.same_set( + ptr_id, do_assign.pointer_id)) + { + other_cond_conj.push_back(do_assign.cond); + } + } + + dyn_obj_assigns[heap_object].emplace_back(ptr_id, cond); + } + } + + exprt cond=if_expr.cond(); + if(!other_cond_conj.empty()) + { + const exprt other_cond=or_exprt( + not_exprt(conjunction(other_cond_conj)), + name(guard_symbol(), OBJECT_SELECT, loc)); + cond=and_exprt(cond, other_cond); + } + exprt new_rhs=if_exprt(cond, rhs, if_expr.true_case()); + assign_rec( + if_expr.true_case(), + new_rhs, + and_exprt(guard, if_expr.cond()), + loc); + assign_rec( if_expr.false_case(), rhs, @@ -1364,7 +1462,7 @@ void local_SSAt::assign_rec( assign_rec(new_lhs, new_rhs, guard, loc); } else - throw "UNKNOWN LHS: "+lhs.id_string(); + throw "UNKNOWN LHS: "+lhs.id_string(); // NOLINT(*) } /*******************************************************************\ @@ -1736,3 +1834,249 @@ bool local_SSAt::has_function_calls() const return found; } +/*******************************************************************\ + +Function: local_SSAt::build_unknown_objs + + Inputs: + + Outputs: + + Purpose: If a location is malloc call, create "unknown object" for + return type. This is later used as a placeholder for invalid + of unknown dereference of an object of that type. + +\*******************************************************************/ + +void local_SSAt::build_unknown_objs(locationt loc) +{ + if(loc->is_assign()) + { + const code_assignt &code_assign=to_code_assign(loc->code); + const exprt &rhs=code_assign.rhs(); + if(rhs.get_bool("#malloc_result")) + { + const exprt &addr_of_do= + rhs.id()==ID_typecast ? to_typecast_expr(rhs).op() : rhs; + const exprt &dyn_obj=to_address_of_expr(addr_of_do).object(); + const typet &dyn_type=ns.follow(dyn_obj.type()); + + std::string dyn_type_name=dyn_type.id_string(); + if(dyn_type.id()==ID_struct) + dyn_type_name+="_"+id2string(to_struct_type(dyn_type).get_tag()); + irep_idt identifier="ssa::"+dyn_type_name+"_obj$unknown"; + + symbol_exprt unknown_obj(identifier, dyn_obj.type()); + unknown_objs.insert(unknown_obj); + } + } +} + +/*******************************************************************\ + +Function: local_SSAt::unknown_obj_eq + + Inputs: + + Outputs: + + Purpose: Create equality obj.component = &obj, which creates self-loop + on "unknown" objects. + +\*******************************************************************/ + +exprt local_SSAt::unknown_obj_eq( + const symbol_exprt &obj, + const struct_typet::componentt &component) const +{ + const irep_idt identifier= + id2string(obj.get_identifier())+"."+id2string(component.get_name()); + const symbol_exprt member(identifier, component.type()); + return equal_exprt(member, address_of_exprt(obj)); +} + +/*******************************************************************\ + +Function: local_SSAt::collect_iterators_rhs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::collect_iterators_rhs(const exprt &expr, locationt loc) +{ + if(expr.id()==ID_member) + { + const member_exprt &member=to_member_expr(expr); + if(member.compound().get_bool(ID_iterator) && + member.compound().id()==ID_symbol) + { + new_iterator_access(to_member_expr(expr), loc, list_iteratort::IN_LOC); + } + } + else + { + forall_operands(it, expr) + collect_iterators_rhs(*it, loc); + } +} + +/*******************************************************************\ + +Function: local_SSAt::collect_iterators_lhs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::collect_iterators_lhs( + const ssa_objectt &object, + local_SSAt::locationt loc) +{ + if(is_iterator(object.get_root_object()) && + object.get_root_object().id()==ID_symbol) + { + assert(object.get_expr().id()==ID_member); + new_iterator_access( + to_member_expr(object.get_expr()), + loc, + loc->location_number); + } +} + +/*******************************************************************\ + +Function: local_SSAt::new_iterator_access + + Inputs: + + Outputs: + + Purpose: Create new iterator access + +\*******************************************************************/ + +void local_SSAt::new_iterator_access( + const member_exprt &expr, + local_SSAt::locationt loc, + unsigned inst_loc_number) +{ + assert(is_iterator(expr.compound())); + + const irep_idt pointer_id=expr.compound().get(ID_it_pointer); + const symbolt &pointer_symbol=ns.lookup(pointer_id); + exprt pointer_rhs=read_rhs(pointer_symbol.symbol_expr(), loc); + assert(pointer_rhs.id()==ID_symbol); + + unsigned init_value_level=expr.compound().get_unsigned_int( + ID_it_init_value_level); + const exprt init_pointer=get_pointer(expr.compound(), init_value_level-1); + + list_iteratort iterator( + to_symbol_expr(pointer_rhs), + init_pointer, + get_iterator_fields(expr.compound())); + + auto it=iterators.insert(iterator); + it.first->add_access(expr, inst_loc_number); +} + +/*******************************************************************\ + +Function: local_SSAt::all_symbolic_deref_defined + + Inputs: + + Outputs: + + Purpose: Create new iterator access + +\*******************************************************************/ +bool local_SSAt::all_symbolic_deref_defined( + const exprt &expr, + const namespacet &ns, + locationt loc) const +{ + bool result=true; + ssa_objectt ssa_object(expr, ns); + if(ssa_object && has_symbolic_deref(ssa_object.get_expr())) + { + const ssa_domaint &ssa_domain=ssa_analysis[loc]; + auto def_it=ssa_domain.def_map.find(ssa_object.get_identifier()); + if(def_it==ssa_domain.def_map.end() || def_it->second.def.is_input()) + result=false; + } + else forall_operands(it, expr) + result=result && all_symbolic_deref_defined(*it, ns, loc); + return result; +} + + +/********************************************************************\ + +Function: local_SSAt::concretise_rhs + + Inputs: + + Outputs: + + Purpose: Concretise symbolic rhs and return resulting expr + +\*******************************************************************/ + +exprt local_SSAt::concretise_symbolic_deref_rhs( + const exprt &rhs, + const namespacet &ns, + const locationt loc) +{ + const exprt deref_rhs=dereference(rhs, loc); + const exprt symbolic_deref_rhs=symbolic_dereference(rhs, ns); + ssa_objectt rhs_object(symbolic_deref_rhs, ns); + + if(deref_rhs.get_bool("#heap_access") && rhs_object) + { + const exprt pointer=get_pointer( + rhs_object.get_root_object(), + pointed_level(rhs_object.get_root_object())-1); + const auto pointer_id=ssa_objectt(pointer, ns).get_identifier(); + const auto pointer_def=ssa_analysis[loc].def_map.find( + pointer_id)->second.def; + const auto symbolic_id=ssa_objectt(symbolic_deref_rhs, ns).get_identifier(); + const auto symbolic_def=ssa_analysis[loc].def_map.find( + symbolic_id)->second.def; + + if(!symbolic_def.is_assignment() + || (pointer_def.is_assignment() + && pointer_def.loc->location_number> + symbolic_def.loc->location_number)) + { + assign_rec(symbolic_deref_rhs, deref_rhs, true_exprt(), loc); + return name(ssa_objectt(symbolic_deref_rhs, ns), OUT, loc); + } + else + { + return symbolic_deref_rhs; + } + } + else + { + exprt rhs_copy=rhs; + Forall_operands(it, rhs_copy) + { + *it=concretise_symbolic_deref_rhs(*it, ns, loc); + } + return rhs_copy; + } + + return + all_symbolic_deref_defined(symbolic_deref_rhs, ns, loc)? + symbolic_deref_rhs:deref_rhs; +} diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 0f8fcc477..0a8c4b17d 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -13,10 +13,14 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "../domains/incremental_solver.h" +#include +#include + #include "ssa_domain.h" #include "guard_map.h" #include "ssa_object.h" +#include "ssa_heap_domain.h" +#include "may_alias_analysis.h" #define TEMPLATE_PREFIX "__CPROVER_template" #define TEMPLATE_DECL TEMPLATE_PREFIX @@ -32,11 +36,15 @@ class local_SSAt inline local_SSAt( const goto_functiont &_goto_function, const namespacet &_ns, + const ssa_heap_analysist &_heap_analysis, const std::string &_suffix=""): ns(_ns), goto_function(_goto_function), - ssa_objects(_goto_function, ns), - ssa_value_ai(_goto_function, ns), - assignments(_goto_function.body, ns, ssa_objects, ssa_value_ai), + heap_analysis(_heap_analysis), + ssa_objects(_goto_function, ns, _heap_analysis), + ssa_value_ai(_goto_function, ns, _heap_analysis), + assignments( + _goto_function.body, ns, ssa_objects, ssa_value_ai, heap_analysis), + alias_analysis(_goto_function, ns), guard_map(_goto_function.body), ssa_analysis(assignments), suffix(_suffix) @@ -49,7 +57,7 @@ class local_SSAt } void output(std::ostream &) const; - void output_verbose(std::ostream &) const; + virtual void output_verbose(std::ostream &) const; // the SSA node for a location class nodet @@ -57,14 +65,13 @@ class local_SSAt public: inline nodet( locationt _location, - std::list::iterator _loophead) - : - enabling_expr(true_exprt()), - marked(false), - location(_location), - loophead(_loophead) - { - } + std::list::iterator _loophead): + enabling_expr(true_exprt()), + marked(false), + location(_location), + loophead(_loophead) + { + } typedef std::vector equalitiest; equalitiest equalities; @@ -108,12 +115,14 @@ class local_SSAt void mark_nodes() { for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked=true; + n_it!=nodes.end(); n_it++) + n_it->marked=true; } void unmark_nodes() { - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked=false; + for(nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) + n_it->marked=false; } // for incremental unwinding @@ -126,6 +135,25 @@ class local_SSAt var_listt params; var_sett globals_in, globals_out; + std::set iterators; + + // unknown heap objects + var_sett unknown_objs; + + // Maps members of dynamic object to a set of pointers used to access those + // objects when assigning them + class dyn_obj_assignt + { + public: + const irep_idt pointer_id; + const exprt cond; + + dyn_obj_assignt(const irep_idt &pointer_id, const exprt &cond): + pointer_id(pointer_id), cond(cond) {} + }; + typedef std::list dyn_obj_assignst; + std::map dyn_obj_assigns; + bool has_function_calls() const; const namespacet &ns; @@ -141,9 +169,11 @@ class local_SSAt exprt edge_guard(locationt from, locationt to) const; // auxiliary functions - enum kindt { PHI, OUT, LOOP_BACK, LOOP_SELECT }; + enum kindt { PHI, OUT, LOOP_BACK, LOOP_SELECT, OBJECT_SELECT }; virtual symbol_exprt name( - const ssa_objectt &, kindt kind, locationt loc) const; + const ssa_objectt &, + kindt kind, + locationt loc) const; symbol_exprt name(const ssa_objectt &, const ssa_domaint::deft &) const; symbol_exprt name_input(const ssa_objectt &) const; virtual exprt nondet_symbol( @@ -160,7 +190,21 @@ class local_SSAt symbol_exprt read_rhs(const ssa_objectt &, locationt loc) const; exprt read_node_in(const ssa_objectt &, locationt loc) const; void assign_rec( - const exprt &lhs, const exprt &rhs, const exprt &guard, locationt loc); + const exprt &lhs, + const exprt &rhs, + const exprt &guard, + locationt loc); + + void collect_iterators_rhs(const exprt &expr, locationt loc); + void collect_iterators_lhs(const ssa_objectt &object, locationt loc); + void new_iterator_access( + const member_exprt &expr, + locationt loc, + unsigned inst_loc_number); + + exprt unknown_obj_eq( + const symbol_exprt &obj, + const struct_typet::componentt &component) const; void get_entry_exit_vars(); @@ -169,11 +213,25 @@ class local_SSAt exprt dereference(const exprt &expr, locationt loc) const; + bool all_symbolic_deref_defined( + const exprt &expr, + const namespacet &ns, + locationt loc) const; + + exprt concretise_symbolic_deref_rhs( + const exprt &rhs, + const namespacet &ns, + const locationt loc); + + const ssa_heap_analysist &heap_analysis; + ssa_objectst ssa_objects; typedef ssa_objectst::objectst objectst; ssa_value_ait ssa_value_ai; assignmentst assignments; + may_alias_analysist alias_analysis; + // protected: guard_mapt guard_map; @@ -190,7 +248,8 @@ class local_SSAt nodest::iterator find_node(locationt loc); nodest::const_iterator find_node(locationt loc) const; void find_nodes( - locationt loc, std::list &_nodes) const; + locationt loc, + std::list &_nodes) const; inline locationt get_location(unsigned location_number) const { @@ -212,6 +271,7 @@ class local_SSAt void build_guard(locationt loc); void build_function_call(locationt loc); void build_assertions(locationt loc); + void build_unknown_objs(locationt loc); // custom templates void collect_custom_templates(); diff --git a/src/ssa/malloc_ssa.cpp b/src/ssa/malloc_ssa.cpp index fd5246b4e..8c4a1003f 100644 --- a/src/ssa/malloc_ssa.cpp +++ b/src/ssa/malloc_ssa.cpp @@ -15,6 +15,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include #include "malloc_ssa.h" @@ -163,6 +164,8 @@ exprt malloc_ssa( if(result.type()!=code.type()) result=typecast_exprt(result, code.type()); + result.set("#malloc_result", true); + return result; } @@ -179,12 +182,12 @@ Function: replace_malloc_rec \*******************************************************************/ -static void replace_malloc_rec( +static bool replace_malloc_rec( exprt &expr, const std::string &suffix, symbol_tablet &symbol_table, const exprt &malloc_size, - unsigned &counter) + unsigned loc_number) { if(expr.id()==ID_side_effect && to_side_effect_expr(expr).get_statement()==ID_malloc) @@ -192,12 +195,22 @@ static void replace_malloc_rec( assert(!malloc_size.is_nil()); expr.op0()=malloc_size; - expr=malloc_ssa(to_side_effect_expr(expr), - "$"+i2string(counter++)+suffix, symbol_table); + expr=malloc_ssa( + to_side_effect_expr(expr), + "$"+i2string(loc_number)+suffix, + symbol_table); + return true; } else + { + bool result=false; Forall_operands(it, expr) - replace_malloc_rec(*it, suffix, symbol_table, malloc_size, counter); + { + if(replace_malloc_rec(*it, suffix, symbol_table, malloc_size, loc_number)) + result=true; + } + return result; + } } /*******************************************************************\ @@ -212,16 +225,33 @@ Function: replace_malloc \*******************************************************************/ -void replace_malloc( +bool replace_malloc( goto_modelt &goto_model, const std::string &suffix) { - unsigned counter=0; + bool result=false; Forall_goto_functions(f_it, goto_model.goto_functions) { + goto_programt::const_targett loop_end=f_it->second.body.instructions.end(); exprt malloc_size=nil_exprt(); Forall_goto_program_instructions(i_it, f_it->second.body) { + if(loop_end==f_it->second.body.instructions.end()) + { + for(const auto &incoming : i_it->incoming_edges) + { + if(incoming->is_backwards_goto() && + incoming!=i_it) + { + loop_end=incoming; + } + } + } + else if(i_it==loop_end) + { + loop_end=f_it->second.body.instructions.end(); + } + if(i_it->is_assign()) { code_assignt &code_assign=to_code_assign(i_it->code); @@ -235,12 +265,31 @@ void replace_malloc( id2string(to_symbol_expr(code_assign.lhs()).get_identifier()); if(lhs_id=="malloc::malloc_size" || lhs_id=="__builtin_alloca::alloca_size") - malloc_size=code_assign.rhs(); + { + namespacet ns(goto_model.symbol_table); + goto_functionst::goto_functiont function_copy=f_it->second; + constant_propagator_ait const_propagator(function_copy, ns); + forall_goto_program_instructions(copy_i_it, function_copy.body) + { + if(copy_i_it->location_number==i_it->location_number) + { + assert(copy_i_it->is_assign()); + malloc_size=to_code_assign(copy_i_it->code).rhs(); + } + } + } + } + if(replace_malloc_rec( + code_assign.rhs(), + suffix, + goto_model.symbol_table, + malloc_size, + i_it->location_number)) + { + result=(loop_end!=f_it->second.body.instructions.end()); } - replace_malloc_rec(code_assign.rhs(), suffix, - goto_model.symbol_table, malloc_size, counter); } } } + return result; } - diff --git a/src/ssa/malloc_ssa.h b/src/ssa/malloc_ssa.h index 5ef4e5ed4..bd6c2ec5c 100644 --- a/src/ssa/malloc_ssa.h +++ b/src/ssa/malloc_ssa.h @@ -17,7 +17,7 @@ exprt malloc_ssa( const std::string &suffix, symbol_tablet &); -void replace_malloc( +bool replace_malloc( goto_modelt &goto_model, const std::string &suffix); diff --git a/src/ssa/may_alias_analysis.cpp b/src/ssa/may_alias_analysis.cpp new file mode 100644 index 000000000..4b09b9ec8 --- /dev/null +++ b/src/ssa/may_alias_analysis.cpp @@ -0,0 +1,182 @@ +/*******************************************************************\ + +Module: May-alias analysis for a single function + +Author: Viktor Malik, imalik@fit.vutbr.cz + +\*******************************************************************/ + +#include "may_alias_analysis.h" + +/*******************************************************************\ + +Function: may_alias_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void may_alias_domaint::transform( + ai_domain_baset::locationt from, + ai_domain_baset::locationt to, + ai_baset &ai, + const namespacet &ns) +{ + if(from->is_assign()) + { + const code_assignt &code_assign=to_code_assign(from->code); + + const exprt lhs_deref=dereference(code_assign.lhs(), ns); + const exprt rhs_deref=dereference(code_assign.rhs(), ns); + + std::set aliases; + get_rhs_aliases(rhs_deref, aliases); + assign_lhs_aliases(lhs_deref, aliases); + } +} + +/*******************************************************************\ + +Function: may_alias_domaint::merge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +bool may_alias_domaint::merge( + const may_alias_domaint &other, + ai_domain_baset::locationt from, + ai_domain_baset::locationt to) +{ + bool changed=false; + + // do union + for(aliasest::const_iterator it=other.aliases.begin(); + it!=other.aliases.end(); it++) + { + irep_idt other_root=other.aliases.find(it); + + if(!aliases.same_set(*it, other_root)) + { + aliases.make_union(*it, other_root); + changed=true; + } + } + + return changed; +} + +/*******************************************************************\ + +Function: may_alias_domaint::assign_lhs_aliases + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void may_alias_domaint::assign_lhs_aliases( + const exprt &lhs, + const std::set &alias_set) +{ + if(lhs.type().id()==ID_pointer) + { + if(lhs.id()==ID_symbol) + { + irep_idt identifier=to_symbol_expr(lhs).get_identifier(); + + aliases.isolate(identifier); + + for(const irep_idt &alias : alias_set) + { + aliases.make_union(identifier, alias); + } + } + } +} + +/*******************************************************************\ + +Function: may_alias_domaint::get_rhs_aliases + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void may_alias_domaint::get_rhs_aliases( + const exprt &rhs, + std::set &alias_set) +{ + if(rhs.id()==ID_symbol && + id2string(to_symbol_expr(rhs).get_identifier()).find("__CPROVER_")== + std::string::npos) + { + irep_idt identifier=to_symbol_expr(rhs).get_identifier(); + alias_set.insert(identifier); + + for(aliasest::const_iterator it=aliases.begin(); + it!=aliases.end(); + it++) + if(aliases.same_set(*it, identifier)) + alias_set.insert(*it); + } + else if(rhs.id()==ID_if) + { + get_rhs_aliases(to_if_expr(rhs).true_case(), alias_set); + get_rhs_aliases(to_if_expr(rhs).false_case(), alias_set); + } + else if(rhs.id()==ID_typecast) + { + get_rhs_aliases(to_typecast_expr(rhs).op(), alias_set); + } +} + +/*******************************************************************\ + +Function: may_alias_domaint::dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +const exprt may_alias_domaint::dereference( + const exprt &expr, + const namespacet &ns) +{ + exprt deref=symbolic_dereference(expr, ns); + members_to_symbols(deref, ns); + return deref; +} + +/*******************************************************************\ + +Function: may_alias_domaint::members_to_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void may_alias_domaint::members_to_symbols(exprt &expr, const namespacet &ns) +{ + ssa_objectt object(expr, ns); + if(object) + expr=object.symbol_expr(); + Forall_operands(it, expr)members_to_symbols(*it, ns); +} diff --git a/src/ssa/may_alias_analysis.h b/src/ssa/may_alias_analysis.h new file mode 100644 index 000000000..011763736 --- /dev/null +++ b/src/ssa/may_alias_analysis.h @@ -0,0 +1,52 @@ +/*******************************************************************\ + +Module: May-alias analysis for a single function + +Author: Viktor Malik, imalik@fit.vutbr.cz + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_MAY_ALIAS_ANALYSIS_H +#define CPROVER_2LS_SSA_MAY_ALIAS_ANALYSIS_H + +#include +#include +#include "ssa_value_set.h" + +class may_alias_domaint:public ai_domain_baset +{ +public: + void transform( + locationt from, + locationt to, + ai_baset &ai, + const namespacet &ns) override; + + bool merge(const may_alias_domaint &other, locationt from, locationt to); + + typedef union_find aliasest; + aliasest aliases; + +protected: + void assign_lhs_aliases( + const exprt &lhs, + const std::set &rhs_alias_set); + void get_rhs_aliases(const exprt &rhs, std::set &alias_set); + + static const exprt dereference(const exprt &expr, const namespacet &ns); + static void members_to_symbols(exprt &expr, const namespacet &ns); +}; + +class may_alias_analysist:public ait +{ +public: + may_alias_analysist( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns) + { + operator()(goto_function, ns); + } +}; + + +#endif // CPROVER_2LS_SSA_MAY_ALIAS_ANALYSIS_H diff --git a/src/ssa/ssa_build_goto_trace.cpp b/src/ssa/ssa_build_goto_trace.cpp index 22891beff..3767e726c 100644 --- a/src/ssa/ssa_build_goto_trace.cpp +++ b/src/ssa/ssa_build_goto_trace.cpp @@ -14,8 +14,6 @@ Author: Daniel Kroening, Peter Schrammel #include "ssa_build_goto_trace.h" -#define TERM_CEX 1 - /*******************************************************************\ Function: ssa_build_goto_tracet::finalize_lhs @@ -79,8 +77,7 @@ bool ssa_build_goto_tracet::can_convert_ssa_expr(const exprt &expr) if(expr.id()==ID_member) { const member_exprt &member=to_member_expr(expr); - can_convert_ssa_expr(member.struct_op()); - return true; + return can_convert_ssa_expr(member.struct_op()); } else if(expr.id()==ID_index) { @@ -218,8 +215,9 @@ bool ssa_build_goto_tracet::record_step( #if 0 std::cout << "ASSIGN " << from_expr(unwindable_local_SSA.ns, "", code_assign) - << ": " << from_expr(unwindable_local_SSA.ns, "", rhs_ssa) - << "==" << from_expr(unwindable_local_SSA.ns, "", rhs_simplified) + << ": " << from_expr(unwindable_local_SSA.ns, "", lhs_simplified) + << " := " + << from_expr(unwindable_local_SSA.ns, "", rhs_simplified) << std::endl; #endif step.type=goto_trace_stept::ASSIGNMENT; @@ -309,9 +307,6 @@ void ssa_build_goto_tracet::operator()( unwindable_local_SSA.current_unwindings.clear(); unsigned last_level=0; unsigned step_nr=1; -#if TERM_CEX - bool stop_next=false; -#endif while(current_pc!=unwindable_local_SSA.goto_function.body.instructions.end()) { @@ -336,7 +331,8 @@ void ssa_build_goto_tracet::operator()( std::cout << "loop-head : " << current_pc->location_number << std::endl; std::cout << "unwindings: " << unwindable_local_SSA.odometer_to_string( - unwindable_local_SSA.current_unwindings, 100) + unwindable_local_SSA.current_unwindings, + 100) << std::endl; #endif } @@ -357,25 +353,20 @@ void ssa_build_goto_tracet::operator()( // get successor if(current_pc->is_goto() && taken) { -#if TERM_CEX - if(termination && stop_next) - { - break; - } -#endif if(current_pc->is_backwards_goto()) { + if(unwindable_local_SSA.current_unwindings.back()==0) + break; + // we de-(!)-crement the unwinding counter unwindable_local_SSA.decrement_unwindings(0); #if 0 std::cout << "loop-end : " << current_pc->location_number << std::endl; std::cout << "unwindings: " << unwindable_local_SSA.odometer_to_string( - unwindable_local_SSA.current_unwindings, 100) + unwindable_local_SSA.current_unwindings, + 100) << std::endl; -#endif -#if TERM_CEX - stop_next=true; #endif } current_pc=current_pc->get_target(); diff --git a/src/ssa/ssa_db.h b/src/ssa/ssa_db.h index c7c92ed91..1fa7d4543 100644 --- a/src/ssa/ssa_db.h +++ b/src/ssa/ssa_db.h @@ -64,9 +64,11 @@ class ssa_dbt inline void create( const function_namet &function_name, const goto_functionst::goto_functiont &goto_function, - const namespacet &ns) + const namespacet &ns, + const ssa_heap_analysist &heap_analysis) { - store[function_name]=new unwindable_local_SSAt(goto_function, ns); + store[function_name]= + new unwindable_local_SSAt(goto_function, ns, heap_analysis); } protected: diff --git a/src/ssa/ssa_dereference.cpp b/src/ssa/ssa_dereference.cpp index 0425e7d54..1e3d57d7f 100644 --- a/src/ssa/ssa_dereference.cpp +++ b/src/ssa/ssa_dereference.cpp @@ -319,26 +319,52 @@ exprt dereference_rec( { if(src.id()==ID_dereference) { - const exprt &pointer=to_dereference_expr(src).pointer(); - exprt pointer_deref= - dereference(pointer, ssa_value_domain, nondet_prefix, ns); - - // We use the identifier produced by - // local_SSAt::replace_side_effects_rec - exprt result=symbol_exprt(nondet_prefix, src.type()); - - // query the value sets - const ssa_value_domaint::valuest values= - ssa_value_domain(pointer, ns); - - for(ssa_value_domaint::valuest::value_sett::const_iterator - it=values.value_set.begin(); - it!=values.value_set.end(); - it++) + const exprt &pointer=dereference_rec( + to_dereference_expr(src).pointer(), + ssa_value_domain, + nondet_prefix, + ns); + + const typet &pointed_type=ns.follow(pointer.type().subtype()); + + const ssa_value_domaint::valuest values=ssa_value_domain(pointer, ns); + + exprt result; + if(values.value_set.empty()) { - exprt guard=ssa_alias_guard(src, it->get_expr(), ns); - exprt value=ssa_alias_value(src, it->get_expr(), ns); - result=if_exprt(guard, value, result); + result=pointed_object(pointer, ns); + } + else + { + auto it=values.value_set.begin(); + + if(values.null || values.unknown || + (values.value_set.size()>1 && it->type().get_bool("#dynamic"))) + { + std::string dyn_type_name=pointed_type.id_string(); + if(pointed_type.id()==ID_struct) + dyn_type_name+="_"+id2string(to_struct_type(pointed_type).get_tag()); + irep_idt identifier="ssa::"+dyn_type_name+"_obj$unknown"; + + result=symbol_exprt(identifier, src.type()); + result.set("#unknown_obj", true); + } + else + { + result=ssa_alias_value(src, (it++)->get_expr(), ns); + result.set("#heap_access", result.type().get_bool("#dynamic")); + } + + for(; it!=values.value_set.end(); ++it) + { + exprt guard=ssa_alias_guard(src, it->get_expr(), ns); + exprt value=ssa_alias_value(src, it->get_expr(), ns); + result=if_exprt(guard, value, result); + result.set( + "#heap_access", + result.get_bool("#heap_access") || + value.type().get_bool("#dynamic")); + } } return result; @@ -348,6 +374,7 @@ exprt dereference_rec( member_exprt tmp=to_member_expr(src); tmp.struct_op()= dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); + tmp.set("#heap_access", tmp.struct_op().get_bool("#heap_access")); #ifdef DEBUG std::cout << "dereference_rec tmp: " << from_expr(ns, "", tmp) << '\n'; @@ -363,6 +390,7 @@ exprt dereference_rec( address_of_exprt tmp=to_address_of_expr(src); tmp.object()= dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); + tmp.set("#heap_access", tmp.object().get_bool("#heap_access")); if(tmp.object().is_nil()) return nil_exprt(); @@ -373,7 +401,11 @@ exprt dereference_rec( { exprt tmp=src; Forall_operands(it, tmp) + { *it=dereference_rec(*it, ssa_value_domain, nondet_prefix, ns); + if(it->get_bool("#heap_access")) + tmp.set("#heap_access", true); + } return tmp; } } diff --git a/src/ssa/ssa_domain.cpp b/src/ssa/ssa_domain.cpp index a9848e8af..3e2d2bb65 100644 --- a/src/ssa/ssa_domain.cpp +++ b/src/ssa/ssa_domain.cpp @@ -88,6 +88,32 @@ void ssa_domaint::transform( o_it!=assigns.end(); o_it++) { + if(o_it->get_expr().get_bool("#is_rhs_assign") && + is_pointed(o_it->get_root_object())) + { + // the second part excluded cases + // when a result of malloc is at the right-handed side + const auto object_ai_it= + static_cast(ai)[from].def_map.find(o_it->get_identifier()); + if(object_ai_it!=static_cast(ai)[from].def_map.end() && + object_ai_it->second.def.is_assignment()) + { + const exprt pointer= + get_pointer( + o_it->get_root_object(), + pointed_level(o_it->get_root_object())-1); + const auto def_pointer= + static_cast(ai)[from] + .def_map.find( + ssa_objectt(pointer, ns).get_identifier())->second.def; + if(!def_pointer.is_assignment() || + def_pointer.loc->location_number< + object_ai_it->second.def.loc->location_number) + { + continue; + } + } + } irep_idt identifier=o_it->get_identifier(); def_entryt &def_entry=def_map[identifier]; diff --git a/src/ssa/ssa_heap_domain.cpp b/src/ssa/ssa_heap_domain.cpp new file mode 100644 index 000000000..7a641297d --- /dev/null +++ b/src/ssa/ssa_heap_domain.cpp @@ -0,0 +1,682 @@ +/*******************************************************************\ + +Module: Dynamic objects analysis + +Author: Viktor Malik + +\*******************************************************************/ + +#include +#include "ssa_heap_domain.h" + +/*******************************************************************\ + +Function: ssa_heap_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_domaint::transform( + const namespacet &ns, + domain_baset::locationt from, + domain_baset::locationt to) +{ + if(from->is_assign()) + { + const code_assignt &assign=to_code_assign(from->code); + assign_lhs_rec(assign.lhs(), assign.rhs(), ns); + } + else if(from->is_function_call() && from->function==to->function) + { + const code_function_callt &fun_call=to_code_function_call(from->code); + assert(fun_call.function().id()==ID_symbol); + const irep_idt &fun_id=to_symbol_expr(fun_call.function()).get_identifier(); + + if(function_map.find(fun_id)!=function_map.end()) + { + unsigned counter=0; + for(auto &obj : function_map.at(fun_id).new_objects) + { + symbol_exprt new_obj=obj.first; + rename_to_caller(new_obj, from, counter); + + const symbolt *symbol; + if(!ns.lookup(new_obj.get_identifier(), symbol)) + new_obj=symbol->symbol_expr(); + + if(function_map[function].new_objects.find(new_obj)== + function_map[function].new_objects.end()) + { + function_map[function].new_objects.insert( + std::make_pair( + new_obj, + std::set())); + } + + for(auto &expr : obj.second) + { + const exprt pointer=function_map.at(fun_id).corresponding_expr( + expr, fun_call.arguments(), 0); + + objectst old_objects=value_map[pointer]; + value_map[pointer]={new_obj}; + + if(is_function_output(pointer, function, ns, false)) + { + function_map[function].new_objects.at(new_obj).insert(pointer); + } + + for(auto &o : old_objects) + { + if(o.id()==ID_symbol && o.type().get_bool("#dynamic") && + new_obj!=o) + function_map[function].new_objects.at(to_symbol_expr(o)).erase( + pointer); + } + } + } + + for(auto &obj : function_map.at(fun_id).modified_objects) + { + const exprt caller_obj=function_map.at(fun_id).corresponding_expr( + obj, fun_call.arguments(), 0); + if(is_function_output(caller_obj, function, ns, false)) + function_map[function].modified_objects.insert(caller_obj); + } + } + } +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::merge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_heap_domaint::merge( + const ssa_heap_domaint &other, + domain_baset::locationt to) +{ + bool result=false; + + if(to->function=="" || to->function==other.function) + { + function=other.function; + + // Merge value maps - union + for(auto &other_value : other.value_map) + { + if(value_map.find(other_value.first)==value_map.end()) + { + value_map[other_value.first]=other_value.second; + result=true; + } + else + { + unsigned long old_size=value_map[other_value.first].size(); + value_map[other_value.first].insert( + other_value.second.begin(), + other_value.second.end()); + result=old_size!=value_map[other_value.first].size(); + } + } + } + else + { + function=to->function; + } + + for(auto &f : other.function_map) + { + auto &objects=function_map[f.first].new_objects; + const auto &other_objects=f.second.new_objects; + + if(f.first==function && function==other.function) + { + for(auto &other_object : other_objects) + { + if(objects.find(other_object.first)==objects.end()) + { + objects[other_object.first]=other_object.second; + result=true; + } + else if(!other_object.second.empty()) + { + unsigned long old_size=objects[other_object.first].size(); + std::set intersection; + std::set_intersection( + objects[other_object.first].begin(), + objects[other_object.first].end(), + other_object.second.begin(), + other_object.second.end(), + std::inserter( + intersection, + intersection.begin())); + if(!intersection.empty()) + objects[other_object.first]=intersection; + else + objects.erase(other_object.first); + + if(old_size!=objects[other_object.first].size()) + result=true; + } + } + } + else + { + for(auto &o : other_objects) + { + std::size_t old_size=objects[o.first].size(); + objects[o.first]=o.second; + if(old_size!=objects[o.first].size()) + result=true; + } + } + + function_map[f.first].params=f.second.params; + + std::size_t old_size=function_map[f.first].modified_objects.size(); + function_map[f.first].modified_objects.insert( + f.second.modified_objects.begin(), + f.second.modified_objects.end()); + if(old_size!=function_map[f.first].modified_objects.size()) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::assign_rhs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_domaint::assign_rhs( + const exprt &rhs, + const irep_idt &function, + objectst &objects, + const namespacet &ns) +{ + if(rhs.get_bool("#malloc_result")) + { + exprt malloc_result=rhs; + if(malloc_result.id()==ID_typecast) + malloc_result=to_typecast_expr(malloc_result).op(); + assert(malloc_result.id()==ID_address_of); + + const symbol_exprt new_object=to_symbol_expr( + to_address_of_expr(malloc_result).object()); + + function_infot &function_info=function_map[function]; + if(function_info.new_objects.find(new_object)== + function_info.new_objects.end()) + { + function_info.new_objects.insert( + std::make_pair( + new_object, + std::set())); + } + + objects={new_object}; + } + else if(rhs.id()==ID_typecast) + { + assign_rhs(to_typecast_expr(rhs).op(), function, objects, ns); + } + else + { + auto values=value_map.find(rhs); + if(values!=value_map.end()) + { + objects=values->second; + } + } +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::is_function_output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_heap_domaint::is_function_output( + const exprt &expr, + const irep_idt &function, + const namespacet &ns, + bool in_deref) +{ + if(expr.id()==ID_dereference) + { + return is_function_output( + to_dereference_expr(expr).pointer(), + function, + ns, + true); + } + else if(expr.id()==ID_member) + { + return is_function_output( + to_member_expr(expr).compound(), + function, + ns, + in_deref); + } + else if(expr.id()==ID_symbol) + { + const symbol_exprt &symbol_expr=to_symbol_expr(expr); + if(id2string(symbol_expr.get_identifier()).find("#return_value")!= + std::string::npos) + { + return symbol_expr.get_identifier()==id2string(function)+"#return_value"; + } + + const symbolt *symbol; + if(!ns.lookup(symbol_expr.get_identifier(), symbol) && + ((in_deref && symbol->is_parameter) || !symbol->is_procedure_local())) + return true; + } + return false; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::set ssa_heap_domaint::value(const exprt &expr) const +{ + std::set result; + if(value_map.find(expr)!=value_map.end()) + { + for(auto &value : value_map.at(expr)) + { + if(value.id()==ID_symbol) + result.insert(to_symbol_expr(value)); + } + } + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::new_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::list ssa_heap_domaint::new_objects() const +{ + return new_objects(function); +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::new_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::list ssa_heap_domaint::new_objects( + const irep_idt &fname) const +{ + std::list result; + if(function_map.find(fname)!=function_map.end()) + { + for(auto &obj : function_map.at(fname).new_objects) + result.push_back(obj.first); + } + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::rename_to_caller + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void ssa_heap_domaint::rename_to_caller( + symbol_exprt &object, + domain_baset::locationt loc, + unsigned &index) const +{ + object.set_identifier( + "ssa::dynamic_object$"+std::to_string(loc->location_number)+"$"+ + std::to_string(index++)); +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::new_caller_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::list ssa_heap_domaint::new_caller_objects( + const irep_idt &fname, + domain_baset::locationt loc) const +{ + std::list result=new_objects(fname); + unsigned counter=0; + for(symbol_exprt &o : result) + { + rename_to_caller(o, loc, counter); + } + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::modified_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::set ssa_heap_domaint::modified_objects( + const irep_idt &fname) const +{ + std::set result; + if(function_map.find(fname)!=function_map.end()) + { + result=function_map.at(fname).modified_objects; + } + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::assign_lhs_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_domaint::assign_lhs_rec( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns) +{ + objectst rhs_objects; + assign_rhs(rhs, function, rhs_objects, ns); + + if(!rhs_objects.empty()) + value_map[lhs]=rhs_objects; + else + value_map.erase(lhs); + + if(is_function_output(lhs, function, ns, false)) + { + auto &objects=function_map[function].new_objects; + for(const exprt &o : rhs_objects) + { + if(o.id()==ID_symbol && o.type().get_bool("#dynamic")) + { + const symbol_exprt new_o=to_symbol_expr(o); + if(objects.find(new_o)!=objects.end()) + { + objects[new_o].insert(lhs); + } + } + } + } + + update_modified(lhs, ns); +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::update_modified + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_domaint::update_modified(const exprt &expr, const namespacet &ns) +{ + if(expr.id()==ID_member) + { + update_modified(to_member_expr(expr).compound(), ns); + } + else if(expr.id()==ID_dereference) + { + for(const exprt &v : value_map[to_dereference_expr(expr).pointer()]) + { + if(is_function_output(v, function, ns, false)) + function_map[function].modified_objects.insert(v); + } + } + else if(is_function_output(expr, function, ns, false)) + function_map[function].modified_objects.insert(expr); +} + +/*******************************************************************\ + +Function: ssa_heap_analysist::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_analysist::initialize(const goto_functionst &goto_functions) +{ + static_analysis_baset::initialize(goto_functions); + + if(goto_functions.function_map.at("main").body_available()) + { + locationt e=goto_functions.function_map.at( + "main").body.instructions.begin(); + ssa_heap_domaint &entry=operator[](e); + + entry.function=e->function; + + forall_goto_functions(f_it, goto_functions) + { + if(f_it->second.body_available()) + { + locationt f_e=f_it->second.body.instructions.begin(); + ssa_heap_domaint &f_entry=operator[](f_e); + for(auto ¶m : f_it->second.type.parameters()) + { + entry.function_map[f_it->first].params.push_back( + param.get_identifier()); + const symbol_exprt param_expr(param.get_identifier(), param.type()); + init_ptr_param(param_expr, f_entry); + } + } + } + } +} + +/*******************************************************************\ + +Function: ssa_heap_analysist::init_ptr_param + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_analysist::init_ptr_param( + const exprt &expr, + ssa_heap_domaint &f_entry) +{ + const typet &type=ns.follow(expr.type()); + if(type.id()==ID_pointer) + { + const dereference_exprt dereference(expr, type.subtype()); + f_entry.value_map[expr].insert(dereference); + init_ptr_param(dereference, f_entry); + } + else if(type.id()==ID_struct) + { + assert(expr.id()==ID_dereference); + for(auto &component : to_struct_type(type).components()) + { + if(component.type().id()==ID_pointer && + ns.follow(component.type().subtype())==type) + { + const member_exprt member(expr, component.get_name(), component.type()); + f_entry.value_map[member].insert(expr); + } + } + } +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::function_infot::corresponding_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const exprt ssa_heap_domaint::function_infot::corresponding_expr( + const exprt &expr, + const code_function_callt::argumentst &arguments, + unsigned deref_level) const +{ + if(expr.id()==ID_symbol) + { + const irep_idt expr_id=to_symbol_expr(expr).get_identifier(); + exprt result=expr; + for(std::size_t i=0; i + +class ssa_heap_domaint:public domain_baset +{ +public: + virtual void transform( + const namespacet &ns, + locationt from, + locationt to) override; + bool merge(const ssa_heap_domaint &, locationt); + + irep_idt function; + + typedef std::set objectst; + std::map value_map; + + const std::set value(const exprt &expr) const; + + class function_infot + { + public: + std::map > new_objects; + std::set modified_objects; + std::vector params; + + const exprt corresponding_expr( + const exprt &expr, + const code_function_callt::argumentst &arguments, + unsigned deref_level) const; + + protected: + const exprt apply_deref(const exprt &expr, unsigned level) const; + }; + + std::map function_map; + + const std::list new_objects() const; + const std::list new_objects(const irep_idt &fname) const; + const std::list + new_caller_objects(const irep_idt &fname, locationt loc) const; + + const std::set modified_objects(const irep_idt &fname) const; +protected: + void assign_lhs_rec(const exprt &lhs, const exprt &rhs, const namespacet &ns); + + void assign_rhs( + const exprt &rhs, + const irep_idt &function, + objectst &objects, + const namespacet &ns); + + bool is_function_output( + const exprt &expr, + const irep_idt &function, + const namespacet &ns, + bool in_deref); + + void rename_to_caller( + symbol_exprt &object, + locationt loc, + unsigned &index) const; + + void update_modified(const exprt &expr, const namespacet &ns); +}; + +class ssa_heap_analysist:public static_analysist +{ +public: + explicit ssa_heap_analysist(const namespacet &_ns): + static_analysist(_ns) {} + + virtual void initialize(const goto_functionst &goto_functions) override; + +protected: + void init_ptr_param(const exprt &expr, ssa_heap_domaint &f_entry); +}; + + +#endif // CPROVER_2LS_SSA_SSA_HEAP_DOMAIN_H diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 2f68901a0..bce39e7e8 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -6,6 +6,8 @@ Author: Peter Schrammel \*******************************************************************/ +#include + #include #include @@ -34,6 +36,8 @@ void ssa_inlinert::get_summary( { counter++; + covered_cs_heap_out.clear(); + // getting globals at call site local_SSAt::var_sett cs_globals_in, cs_globals_out; goto_programt::const_targett loc=n_it->location; @@ -62,8 +66,18 @@ void ssa_inlinert::get_summary( std::cout << std::endl; #endif + bindings.push_back(get_replace_new_objects(SSA, *f_it, loc, summary)); + // equalities for arguments - bindings.push_back(get_replace_params(summary.params, *f_it)); + bindings.push_back( + get_replace_params( + summary.params, + *f_it, + cs_globals_in, + cs_globals_out, + SSA, + summary, + loc)); // equalities for globals_in if(forward) @@ -91,15 +105,21 @@ void ssa_inlinert::get_summary( if(forward) bindings.push_back( get_replace_globals_out( - summary.globals_out, cs_globals_in, - cs_globals_out)); + cs_globals_out, + summary, + *f_it, + SSA, + loc)); else bindings.push_back( get_replace_globals_out( - summary.globals_in, cs_globals_out, - cs_globals_in)); + cs_globals_in, + summary, + *f_it, + SSA, + loc)); } /*******************************************************************\ @@ -123,6 +143,27 @@ exprt ssa_inlinert::get_summaries(const local_SSAt &SSA) /*******************************************************************\ +Function: ssa_inlinert::get_summaries_to_loc + + Inputs: + + Outputs: + + Purpose: get summary for all function calls up to a given location + +\*******************************************************************/ + +exprt ssa_inlinert::get_summaries_to_loc( + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + exprt::operandst summaries, bindings; + get_summaries(SSA, true, summaries, bindings, loc); + return and_exprt(conjunction(bindings), conjunction(summaries)); +} + +/*******************************************************************\ + Function: ssa_inlinert::get_summaries Inputs: @@ -137,11 +178,15 @@ void ssa_inlinert::get_summaries( const local_SSAt &SSA, bool forward, exprt::operandst &summaries, - exprt::operandst &bindings) + exprt::operandst &bindings, + local_SSAt::locationt loc) { for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) { + if(loc!=local_SSAt::locationt() && + n_it->location->location_number>=loc->location_number) + return; for(local_SSAt::nodet::function_callst::const_iterator f_it= n_it->function_calls.begin(); f_it!=n_it->function_calls.end(); f_it++) @@ -151,8 +196,8 @@ void ssa_inlinert::get_summaries( if(summary_db.exists(fname)) { - get_summary(SSA, n_it, f_it, summary_db.get(fname), - forward, summaries, bindings); + get_summary( + SSA, n_it, f_it, summary_db.get(fname), forward, summaries, bindings); } } } @@ -325,15 +370,17 @@ void ssa_inlinert::replace( { rename(precondition); node->constraints.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); + implies_exprt( + SSA.guard_symbol(node->location), + precondition)); } else { rename(precondition); node->assertions.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); + implies_exprt( + SSA.guard_symbol(node->location), + precondition)); } exprt transformer; if(forward) @@ -420,20 +467,24 @@ exprt ssa_inlinert::get_replace_globals_in( for(summaryt::var_sett::const_iterator it=globals_in.begin(); it!=globals_in.end(); it++) { - symbol_exprt lhs=*it; // copy - rename(lhs); - symbol_exprt rhs; - if(find_corresponding_symbol(*it, globals, rhs)) + // bind only real globals - filter out heap objects + if(!is_pointed(*it)) { - debug() << "binding: " << lhs.get_identifier() << "==" - << rhs.get_identifier() << eom; - c.push_back(equal_exprt(lhs, rhs)); - } + symbol_exprt lhs=*it; // copy + rename(lhs); + symbol_exprt rhs; + if(find_corresponding_symbol(*it, globals, rhs)) + { + debug() << "binding: " << lhs.get_identifier() << " == " + << rhs.get_identifier() << eom; + c.push_back(equal_exprt(lhs, rhs)); + } #if 0 else warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif + } } return conjunction(c); } @@ -489,7 +540,12 @@ Function: ssa_inlinert::get_replace_params exprt ssa_inlinert::get_replace_params( const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr) + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const local_SSAt &SSA, + const summaryt &summary, + const local_SSAt::locationt &loc) { // equalities for arguments exprt::operandst c; @@ -505,9 +561,196 @@ exprt ssa_inlinert::get_replace_params( break; } - exprt lhs=*p_it; // copy + exprt param=*p_it; // copy + exprt arg=*it; // copy + + exprt lhs=param; rename(lhs); - c.push_back(equal_exprt(lhs, *it)); + // bind parameter <-> argument + c.push_back(equal_exprt(lhs, arg)); + + typet arg_type=SSA.ns.follow(arg.type()); + + // Bind objects pointed by parameter/argument pair + std::list args_in={arg}; + std::list args_out={arg}; + std::list params_in={param}; + std::list params_out={param}; + + while(arg_type.id()==ID_pointer) + { + std::list args_deref_in= + apply_dereference(args_in, SSA.ssa_value_ai[loc], SSA.ns); + std::list params_deref_in= + apply_dereference(params_in, summary.value_domain_in, SSA.ns); + + local_SSAt::locationt next_loc=loc; + ++next_loc; + std::list args_deref_out= + apply_dereference(args_out, SSA.ssa_value_ai[next_loc], SSA.ns); + std::list params_deref_out= + apply_dereference(params_out, summary.value_domain_out, SSA.ns); + + const typet arg_symbol_type=arg_type.subtype(); + arg_type=SSA.ns.follow(arg_symbol_type); + + if(contains_iterator(params_deref_out)) + { // If the caller contains iterators, bindings are different since + // objects from caller will appear in the callee summary + assert(!args_deref_in.empty() && !args_deref_out.empty()); + + arg_type=SSA.ns.follow(args_deref_in.begin()->type()); + assert(arg_type.id()==ID_struct); + + for(const exprt &a : args_deref_in) + { + std::list aliases= + apply_dereference({a}, SSA.ssa_value_ai[next_loc], SSA.ns); + aliases.push_front(a); + + for(auto &alias : aliases) + { + const exprt lhs_expr= + param_out_transformer(alias, arg_type, summary.globals_out); + const exprt rhs_expr= + arg_out_transformer( + alias, + arg_symbol_type, + params_deref_out.begin()->type(), + SSA, + loc); + // Bind argument address + c.push_back(equal_exprt(lhs_expr, rhs_expr)); + + for(auto &component : to_struct_type(arg_type).components()) + { + const exprt lhs_comp_expr= + param_in_member_transformer(alias, component); + const exprt rhs_comp_expr= + arg_in_member_transformer(alias, component, SSA, loc); + // Bind argument members at the input + c.push_back(equal_exprt(lhs_comp_expr, rhs_comp_expr)); + } + } + } + + for(const exprt &a : args_deref_out) + { + std::list aliases= + apply_dereference({a}, SSA.ssa_value_ai[next_loc], SSA.ns); + aliases.push_front(a); + for(auto &alias : aliases) + { + const typet &alias_type=SSA.ns.follow(alias.type()); + assert(alias_type.id()==ID_struct); + for(auto &component : to_struct_type(alias_type).components()) + { + // Bind argument members at the output (args_deref_out might + // contain different objects than args_deref_in since function + // call may create new objects). + symbol_exprt arg_member( + id2string(to_symbol_expr(alias).get_identifier())+"."+ + id2string(component.get_name()), component.type()); + + symbol_exprt member_lhs_out; + if(find_corresponding_symbol( + arg_member, summary.globals_out, member_lhs_out)) + { + rename(member_lhs_out); + const exprt arg_out= + arg_out_member_transformer(alias, component, SSA, loc); + c.push_back(equal_exprt(member_lhs_out, arg_out)); + } + } + } + } + } + else + { + // Bind objects pointed by argument and parameter when iterator is + // not present + if(params_deref_in.size()==1) + { + const exprt &p_in=params_deref_in.front(); + + exprt::operandst d; + for(const exprt &a_in : args_deref_in) + { + exprt::operandst binding; + const exprt lhs_expr=param_in_transformer(p_in); + const exprt rhs_expr=arg_in_transformer(a_in, SSA, loc); + binding.push_back(equal_exprt(lhs_expr, rhs_expr)); + + if(arg_type.id()==ID_struct) + { + for(auto &component : to_struct_type(arg_type).components()) + { + const exprt lhs_comp_expr= + param_in_member_transformer(p_in, component); + const exprt rhs_comp_expr= + arg_in_member_transformer(a_in, component, SSA, loc); + binding.push_back(equal_exprt(lhs_comp_expr, rhs_comp_expr)); + } + } + d.push_back(conjunction(binding)); + } + if(!d.empty()) + c.push_back(disjunction(d)); + + d.clear(); + for(const exprt &p_out : params_deref_out) + { + for(const exprt &a_out : args_deref_out) + { + if(!cs_heap_covered(a_out)) + { + exprt::operandst binding; + + const exprt lhs_expr= + param_out_transformer(p_out, arg_type, summary.globals_out); + const exprt rhs_expr= + arg_out_transformer( + a_out, + arg_symbol_type, + p_out.type(), + SSA, + loc); + binding.push_back(equal_exprt(lhs_expr, rhs_expr)); + + if(arg_type.id()==ID_struct) + { + for(auto &component : to_struct_type(arg_type).components()) + { + const exprt lhs_comp_expr= + param_out_member_transformer( + p_out, + component, + summary.globals_out); + const exprt rhs_comp_expr= + arg_out_member_transformer(a_out, component, SSA, loc); + binding.push_back( + equal_exprt( + lhs_comp_expr, + rhs_comp_expr)); + } + } + d.push_back(conjunction(binding)); + } + } + } + if(!d.empty()) + c.push_back(disjunction(d)); + } + } + + args_in=args_deref_in; + args_out=args_deref_out; + params_in=params_deref_in; + params_out=params_deref_out; + + if(args_in.empty() && args_out.empty()) + break; + } } return conjunction(c); } @@ -560,24 +803,128 @@ Function: ssa_inlinert::get_replace_globals_out \*******************************************************************/ exprt ssa_inlinert::get_replace_globals_out( - const local_SSAt::var_sett &globals_out, const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out) + const local_SSAt::var_sett &cs_globals_out, + const summaryt &summary, + const function_application_exprt &funapp_expr, + const local_SSAt &SSA, + local_SSAt::locationt loc) { // equalities for globals_out exprt::operandst c; for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); it!=cs_globals_out.end(); it++) { - symbol_exprt rhs=*it; // copy symbol_exprt lhs; - if(find_corresponding_symbol(*it, globals_out, lhs)) - rename(lhs); + const exprt rhs=*it; + + + if(is_pointed(*it) || + id2string(it->get_identifier()).find("dynamic_object$")!= + std::string::npos) + { + if(!cs_heap_covered(*it) && + !find_corresponding_symbol(*it, summary.globals_out, lhs)) + { + assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); + c.push_back(equal_exprt(lhs, rhs)); + } + } else - assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); - c.push_back(equal_exprt(lhs, rhs)); + { + if(find_corresponding_symbol(*it, summary.globals_out, lhs)) + { + // Bind function return value + rename(lhs); + c.push_back(equal_exprt(lhs, rhs)); + + typet type=SSA.ns.follow(rhs.type()); + + std::list callee_global={*it}; + std::list caller_global={*it}; + + // Bind all objects pointed by return value + while(type.id()==ID_pointer) + { + local_SSAt::locationt next_loc=loc; + ++next_loc; + std::list caller_deref= + apply_dereference( + caller_global, + SSA.ssa_value_ai[next_loc], + SSA.ns); + std::list callee_deref= + apply_dereference(callee_global, summary.value_domain_out, SSA.ns); + + if(!callee_deref.empty()) + { + const typet symbol_type=type.subtype(); + type=SSA.ns.follow(symbol_type); + + exprt::operandst d; + for(const exprt &callee : callee_deref) + { + for(const exprt &caller : caller_deref) + { + if(!cs_heap_covered(caller)) + { + exprt::operandst binding; + const exprt lhs_expr= + param_out_transformer(callee, type, summary.globals_out); + const exprt rhs_expr= + arg_out_transformer( + caller, + symbol_type, + callee.type(), + SSA, + loc); + binding.push_back(equal_exprt(lhs_expr, rhs_expr)); + + if(type.id()==ID_struct) + { + for(auto &component : to_struct_type(type).components()) + { + const exprt lhs_comp_expr= + param_out_member_transformer( + callee, + component, + summary.globals_out); + const exprt rhs_comp_expr= + arg_out_member_transformer(caller, component, SSA, loc); + binding.push_back( + equal_exprt( + lhs_comp_expr, + rhs_comp_expr)); + } + } + + d.push_back(conjunction(binding)); + } + } + } + + if(!d.empty()) + c.push_back(disjunction(d)); + } + + callee_global=callee_deref; + caller_global=caller_deref; + + if(caller_global.empty()) + break; + } + } + else + { + if(find_corresponding_symbol(*it, summary.globals_out, lhs)) + rename(lhs); + else + assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); + c.push_back(equal_exprt(lhs, rhs)); + } + } } - return conjunction (c); + return conjunction(c); } /*******************************************************************\ @@ -651,6 +998,23 @@ void ssa_inlinert::rename(exprt &expr) irep_idt id=id2string(sexpr.get_identifier())+"@"+i2string(counter); sexpr.set_identifier(id); } + else if(expr.id()==ID_address_of) + { + irep_idt id; + const exprt &obj=to_address_of_expr(expr).object(); + if(obj.id()==ID_symbol) + { + const std::string &obj_id=id2string(to_symbol_expr(obj).get_identifier()); + if(is_pointed(obj)) + id=get_pointer_id(obj); + else + id=id2string(obj_id)+"'addr"; + + id=id2string(id)+"@"+i2string(counter); + } + symbol_exprt addr_symbol(id, expr.type()); + expr=addr_symbol; + } Forall_operands(op, expr) rename(*op); } @@ -772,6 +1136,11 @@ void ssa_inlinert::rename_to_callee( replace_map[*it]=*p_it; } + replace_expr(replace_map, expr); + + // arguments might contain globals, thus, we have to replace them separately + replace_map.clear(); + for(summaryt::var_sett::const_iterator it=cs_globals_in.begin(); it!=cs_globals_in.end(); it++) { @@ -784,9 +1153,11 @@ void ssa_inlinert::rename_to_callee( warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif - replace_map[*it]= - symbol_exprt( - id2string(it->get_identifier())+"@"+i2string(++counter), it->type()); + // rename objects not present in globals in to non-suffix version + symbol_exprt to_replace(get_original_identifier(*it), it->type()); + replace_map[*it]=to_replace; + // to propagate #dynamic flag on type + replace_map[to_replace]=to_replace; } } @@ -904,13 +1275,14 @@ irep_idt ssa_inlinert::get_original_identifier(const symbol_exprt &s) char c=id.at(i); if(pos==std::string::npos) { - if(c=='#' || c=='@' || c=='%' || c=='!' || c=='$') + if(c=='#' || c=='@' || c=='%' || c=='!') pos=i; } else { - if(!(c=='#' || c=='@' || c=='%' || c=='!' || c=='$') && + if(!(c=='#' || c=='@' || c=='%' || c=='!') && !(c=='p' || c=='h' || c=='i') && + !(c=='l' || c=='b') && !('0'<=c && c<='9')) pos=std::string::npos; } @@ -920,3 +1292,377 @@ irep_idt ssa_inlinert::get_original_identifier(const symbol_exprt &s) return id; } +/*******************************************************************\ + +Function: ssa_inlinert::apply_dereference + + Inputs: Set of pointers and value analysis + + Outputs: Set of all objects that can be pointed by one of pointers + from the input set. + + Purpose: + +\*******************************************************************/ + +std::list ssa_inlinert::apply_dereference( + const std::list &exprs, + const ssa_value_domaint &value_domain, + const namespacet &ns) +{ + std::list result; + for(const exprt &expr : exprs) + { + if(expr.id()==ID_symbol || expr.id()==ID_address_of) + { + exprt to_query=expr; // copy + if(expr.id()==ID_symbol) + { + to_symbol_expr(to_query).set_identifier( + get_original_identifier(to_symbol_expr(expr))); + } + ssa_value_domaint::valuest values=value_domain(to_query, ns); + for(const ssa_objectt &v : values.value_set) + { + assert(v.get_expr().id()==ID_symbol); + result.push_back(v.get_expr()); + } + } + else if(expr.id()==ID_typecast) + { + std::list tmp=apply_dereference( + {to_typecast_expr(expr).op()}, value_domain, ns); + for(auto &e : tmp) + result.push_back(e); + } + } + return result; +} + +/*******************************************************************\ + +Function: ssa_inlinert::contains_iterator + + Inputs: List of expressions + + Outputs: True if the list contains an advancer + + Purpose: + +\*******************************************************************/ + +bool ssa_inlinert::contains_iterator(const std::list ¶ms) +{ + auto it=std::find_if( + params.begin(), + params.end(), + [](const exprt &p) { return is_iterator(p); }); + return (it!=params.end()); +} + +/*******************************************************************\ + +Function: ssa_inlinert::param_in_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::param_in_transformer(const exprt ¶m) +{ + assert(param.id()==ID_symbol); + symbol_exprt param_in=to_symbol_expr(param); + rename(param_in); + return param_in; +} + +/*******************************************************************\ + +Function: ssa_inlinert::arg_in_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::arg_in_transformer( + const exprt &arg, + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + return SSA.read_rhs(arg, loc); +} + +/*******************************************************************\ + +Function: ssa_inlinert::param_in_member_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::param_in_member_transformer( + const exprt ¶m, + const struct_union_typet::componentt &component) +{ + assert(param.id()==ID_symbol); + symbol_exprt param_member( + id2string(to_symbol_expr(param).get_identifier())+"."+ + id2string(component.get_name()), component.type()); + rename(param_member); + return param_member; +} + +/*******************************************************************\ + +Function: ssa_inlinert::arg_in_member_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::arg_in_member_transformer( + const exprt &arg, + const struct_union_typet::componentt &component, + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + symbol_exprt arg_member( + id2string(to_symbol_expr(arg).get_identifier())+"."+ + id2string(component.get_name()), + component.type()); + return SSA.read_rhs(arg_member, loc); +} + +/*******************************************************************\ + +Function: ssa_inlinert::param_out_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::param_out_transformer( + const exprt ¶m, + const typet &type, + const local_SSAt::var_sett &globals_out) +{ + assert(param.id()==ID_symbol); + + if(type.id()==ID_struct) + { + address_of_exprt param_addr=address_of_exprt(param); + rename(param_addr); + return param_addr; + } + else + { + symbol_exprt param_out=to_symbol_expr(param); + if(find_corresponding_symbol(to_symbol_expr(param), globals_out, param_out)) + rename(param_out); + return param_out; + } +} + +/*******************************************************************\ + +Function: ssa_inlinert::arg_out_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::arg_out_transformer( + const exprt &arg, + const typet &arg_symbol_type, + const typet ¶m_type, + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + const typet &arg_type=SSA.ns.follow(arg_symbol_type); + if(arg_type.id()==ID_struct) + { + assert(arg.id()==ID_symbol); + symbol_exprt arg_symbol=to_symbol_expr(arg); + address_of_exprt arg_addr=address_of_exprt(arg_symbol); + + const symbolt *symbol; + if(!SSA.ns.lookup(arg_symbol.get_identifier(), symbol)) + { + arg_addr=address_of_exprt(symbol->symbol_expr()); + } + + covered_cs_heap_out.insert(arg_symbol); + return arg_addr; + } + else + { + const symbol_exprt &arg_out= + SSA.name(ssa_objectt(arg, SSA.ns), local_SSAt::OUT, loc); + covered_cs_heap_out.insert(arg_out); + return arg_out; + } +} + +/*******************************************************************\ + +Function: ssa_inlinert::param_out_member_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::param_out_member_transformer( + const exprt ¶m, + const struct_union_typet::componentt &component, + const local_SSAt::var_sett &globals_out) +{ + assert(param.id()==ID_symbol); + + symbol_exprt param_member( + id2string(to_symbol_expr(param).get_identifier())+"."+ + id2string(component.get_name()), component.type()); + symbol_exprt param_out; + assert(find_corresponding_symbol(param_member, globals_out, param_out)); + rename(param_out); + return param_out; +} + +/*******************************************************************\ + +Function: ssa_inlinert::artg_out_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::arg_out_member_transformer( + const exprt &arg, + const struct_union_typet::componentt &component, + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + symbol_exprt arg_member( + id2string(to_symbol_expr(arg).get_identifier())+"."+ + id2string(component.get_name()), component.type()); + const symbol_exprt &arg_member_out= + SSA.name(ssa_objectt(arg_member, SSA.ns), local_SSAt::OUT, loc); + covered_cs_heap_out.insert(arg_member_out); + return arg_member_out; +} + +/*******************************************************************\ + +Function: ssa_inlinert::cs_heap_covered + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_inlinert::cs_heap_covered(const exprt &expr) +{ + return expr.id()==ID_symbol && + covered_cs_heap_out.find(to_symbol_expr(expr))!= + covered_cs_heap_out.end(); +} + +/*******************************************************************\ + +Function: ssa_inlinert::get_replace_new_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::get_replace_new_objects( + const local_SSAt &SSA, + const function_application_exprt funapp_expr, + local_SSAt::locationt loc, + const summaryt &summary) +{ + exprt::operandst binding; + + const irep_idt &fname=to_symbol_expr(funapp_expr.function()).get_identifier(); + + auto next_loc=loc; + ++next_loc; + if(SSA.heap_analysis.has_location(next_loc)) + { + const ssa_heap_domaint &heap_domain=SSA.heap_analysis[next_loc]; + + const std::list callee_objects= + heap_domain.new_objects(fname); + const std::list caller_objects= + heap_domain.new_caller_objects(fname, loc); + + auto callee_it=callee_objects.begin(); + for(auto caller_it=caller_objects.begin(); caller_it!=caller_objects.end(); + ++caller_it, ++callee_it) + { + const typet symbol_type=caller_it->type(); + const typet type=SSA.ns.follow(symbol_type); + + const exprt lhs_expr= + param_out_transformer(*callee_it, type, summary.globals_out); + const exprt rhs_expr= + arg_out_transformer(*caller_it, symbol_type, type, SSA, loc); + binding.push_back(equal_exprt(lhs_expr, rhs_expr)); + + if(type.id()==ID_struct) + { + for(auto &component : to_struct_type(type).components()) + { + const exprt lhs_comp_expr= + param_out_member_transformer( + *callee_it, + component, + summary.globals_out); + const exprt rhs_comp_expr= + arg_out_member_transformer(*caller_it, component, SSA, loc); + binding.push_back(equal_exprt(lhs_comp_expr, rhs_comp_expr)); + } + } + } + } + + return conjunction(binding); +} diff --git a/src/ssa/ssa_inliner.h b/src/ssa/ssa_inliner.h index 7cf8d0730..57634bf7a 100644 --- a/src/ssa/ssa_inliner.h +++ b/src/ssa/ssa_inliner.h @@ -38,17 +38,17 @@ class ssa_inlinert:public messaget const local_SSAt &SSA, bool forward, exprt::operandst &summaries, - exprt::operandst &bindings); + exprt::operandst &bindings, + local_SSAt::locationt loc=local_SSAt::locationt()); exprt get_summaries(const local_SSAt &SSA); + exprt get_summaries_to_loc(const local_SSAt &SSA, local_SSAt::locationt loc); void replace( local_SSAt &SSA, local_SSAt::nodest::iterator node, local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - // incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - // outgoing globals at call site + const local_SSAt::var_sett &cs_globals_in, // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, // outgoing globals at call site const summaryt &summary, bool forward, bool preconditions_as_assertions); @@ -62,10 +62,8 @@ class ssa_inlinert:public messaget local_SSAt::nodest &nodes, local_SSAt::nodest::iterator node, local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - // incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - // outgoing globals at call site + const local_SSAt::var_sett &cs_globals_in, // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, // outgoing globals at call site const local_SSAt &function); void replace( @@ -105,6 +103,11 @@ class ssa_inlinert:public messaget static irep_idt get_original_identifier(const symbol_exprt &s); + static std::list apply_dereference( + const std::list &exprs, + const ssa_value_domaint &value_domain, + const namespacet &ns); + protected: unsigned counter; summary_dbt &summary_db; @@ -113,6 +116,8 @@ class ssa_inlinert:public messaget local_SSAt::nodet::equalitiest new_equs; std::set rm_function_calls; + std::set covered_cs_heap_out; + void replace_globals_in( const local_SSAt::var_sett &globals_in, const local_SSAt::var_sett &globals); @@ -129,14 +134,69 @@ class ssa_inlinert:public messaget const local_SSAt::var_sett &globals); exprt get_replace_params( const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr); + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const local_SSAt &SSA, + const summaryt &summary, + const local_SSAt::locationt &loc); exprt get_replace_globals_out( - const local_SSAt::var_sett &globals_out, const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out); + const local_SSAt::var_sett &cs_globals_out, + const summaryt &summary, + const function_application_exprt &funapp_expr, + const local_SSAt &SSA, + local_SSAt::locationt loc); + exprt get_replace_new_objects( + const local_SSAt &SSA, + const function_application_exprt funapp_expr, + local_SSAt::locationt loc, + const summaryt &summary); void rename(exprt &expr); void rename(local_SSAt::nodet &node); + + bool cs_heap_covered(const exprt &expr); + + // Transformation functions for lists of input/output arguments/pointers + // (or their members) for binding purposes + + exprt param_in_transformer(const exprt ¶m); + exprt arg_in_transformer( + const exprt &arg, + const local_SSAt &SSA, + local_SSAt::locationt loc); + exprt param_in_member_transformer( + const exprt ¶m, + const struct_union_typet::componentt &component); + exprt arg_in_member_transformer( + const exprt &arg, + const struct_union_typet::componentt &component, + const local_SSAt &SSA, + local_SSAt::locationt loc); + + exprt param_out_transformer( + const exprt ¶m, + const typet &type, + const local_SSAt::var_sett &globals_out); + + exprt arg_out_transformer( + const exprt &arg, + const typet &arg_symbol_type, + const typet ¶m_type, + const local_SSAt &SSA, + local_SSAt::locationt loc); + exprt param_out_member_transformer( + const exprt ¶m, + const struct_union_typet::componentt &component, + const local_SSAt::var_sett &globals_out); + exprt arg_out_member_transformer( + const exprt &arg, + const struct_union_typet::componentt &component, + const local_SSAt &SSA, + local_SSAt::locationt loc); + + static bool contains_iterator(const std::list ¶ms); }; #endif diff --git a/src/ssa/ssa_object.cpp b/src/ssa/ssa_object.cpp index d3aa56993..eae7869e3 100644 --- a/src/ssa/ssa_object.cpp +++ b/src/ssa/ssa_object.cpp @@ -37,6 +37,18 @@ bool is_ptr_object(const exprt &src) src.get(ID_ptr_object)!=irep_idt(); } +/*******************************************************************\ + +Function: collect_objects_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void collect_objects_rec( const exprt &src, const namespacet &ns, @@ -45,6 +57,55 @@ void collect_objects_rec( /*******************************************************************\ +Function: collect_ptr_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void collect_ptr_objects( + const exprt &expr, + const namespacet &ns, + std::set &objects, + std::set &literals, + bool dynamic) +{ + if(expr.id()==ID_symbol) + { + const symbol_exprt &src=to_symbol_expr(expr); + const typet &type=ns.follow(src.type()); + if(type.id()==ID_pointer) + { + symbol_exprt ptr_object=pointed_object(src, ns); + + const symbolt *symbol; + if(dynamic || + (!ns.lookup(src.get_identifier(), symbol) && + !symbol->is_procedure_local())) + ptr_object.type().set("#dynamic", true); + + if(is_ptr_object(src)) + ptr_object.set(ID_ptr_object, src.get(ID_ptr_object)); + else + ptr_object.set(ID_ptr_object, src.get_identifier()); + + collect_objects_rec(ptr_object, ns, objects, literals); + collect_ptr_objects(ptr_object, ns, objects, literals, dynamic); + } + } + else + { + forall_operands(it, expr) + collect_ptr_objects(*it, ns, objects, literals, dynamic); + } +} + +/*******************************************************************\ + Function: collect_objects_address_of_rec Inputs: @@ -141,6 +202,10 @@ void collect_objects_rec( { if(type.id()==ID_struct) { + std::string id=id2string(ssa_object.get_identifier()); + if(src.type().get_bool("#dynamic") || is_pointed(src)) + objects.insert(ssa_object); + // need to split up const struct_typet &struct_type=to_struct_type(type); @@ -152,6 +217,8 @@ void collect_objects_rec( it++) { member_exprt new_src(src, it->get_name(), it->type()); + copy_pointed_info(new_src, src); + collect_objects_rec(new_src, ns, objects, literals); // recursive call } } @@ -162,6 +229,17 @@ void collect_objects_rec( #endif objects.insert(ssa_object); + + const exprt &root_object=ssa_object.get_root_object(); + if(ssa_object.type().get_bool("#dynamic") || !is_pointed(root_object)) + { + collect_ptr_objects( + ssa_object.symbol_expr(), + ns, + objects, + literals, + false); + } } } else @@ -195,6 +273,7 @@ void ssa_objectst::collect_objects( { symbol_exprt symbol=ns.lookup(*it).symbol_expr(); collect_objects_rec(symbol, ns, objects, literals); + collect_ptr_objects(symbol, ns, objects, literals, false); } // Rummage through body. @@ -203,51 +282,18 @@ void ssa_objectst::collect_objects( collect_objects_rec(it->guard, ns, objects, literals); collect_objects_rec(it->code, ns, objects, literals); } -} - -/*******************************************************************\ - -Function: ssa_objectst::add_ptr_objects - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ -void ssa_objectst::add_ptr_objects( - const namespacet &ns) -{ - objectst tmp; - - for(objectst::const_iterator o_it=objects.begin(); - o_it!=objects.end(); - o_it++) + // Add new objects created within the function + goto_programt::const_targett exit=--(src.body.instructions.end()); + if(heap_analysis.has_location(exit)) { - exprt root_object=o_it->get_root_object(); - if(root_object.id()==ID_symbol) + const std::list &new_objects= + heap_analysis[exit].new_objects(); + for(const symbol_exprt &o : new_objects) { - if(o_it->type().id()==ID_pointer) - { - const symbolt &symbol=ns.lookup(root_object); - if(symbol.is_parameter) - tmp.insert(*o_it); - } + collect_objects_rec(o, ns, objects, literals); } } - - for(objectst::const_iterator o_it=tmp.begin(); - o_it!=tmp.end(); - o_it++) - { - typet type=o_it->type().subtype(); - irep_idt identifier=id2string(o_it->get_identifier())+"'obj"; - symbol_exprt ptr_object(identifier, type); - ptr_object.set(ID_ptr_object, o_it->get_identifier()); - collect_objects_rec(ptr_object, ns, objects, literals); - } } /*******************************************************************\ @@ -280,8 +326,9 @@ void ssa_objectst::categorize_objects( if(root_object.id()==ID_symbol) { - if(is_ptr_object(root_object)) + if(is_ptr_object(root_object) || root_object.type().get_bool("#dynamic")) { + globals.insert(*o_it); } else { @@ -379,10 +426,6 @@ ssa_objectt::identifiert ssa_objectt::object_id_rec( { return identifiert(); } - else if(src.id()==ID_ptr_object) - { - return identifiert(id2string(src.get(ID_identifier))+"'obj"); - } else return identifiert(); } diff --git a/src/ssa/ssa_object.h b/src/ssa/ssa_object.h index 37f4b8c8e..0d2cb18e6 100644 --- a/src/ssa/ssa_object.h +++ b/src/ssa/ssa_object.h @@ -9,7 +9,9 @@ Author: Daniel Kroening, kroening@kroening.com #ifndef CPROVER_2LS_SSA_SSA_OBJECT_H #define CPROVER_2LS_SSA_SSA_OBJECT_H +#include "ssa_pointed_objects.h" #include +#include "ssa_heap_domain.h" class ssa_objectt { @@ -84,6 +86,32 @@ class ssa_objectt return get_root_object_rec(expr); } + bool is_unknown_obj() + { + std::string id_str=id2string(identifier); + return id_str.find("$unknown")!=std::string::npos; + } + + void set_flag(const irep_idt flag, bool value) + { + expr.set(flag, value); + } + + void set_iterator( + const irep_idt &pointer_id, + const std::vector &fields) + { + assert(expr.id()==ID_symbol && expr.get_bool(ID_pointed)); + expr.set(ID_iterator, true); + expr.set(ID_it_pointer, pointer_id); + set_iterator_fields(expr, fields); + expr.set(ID_it_init_value, to_symbol_expr(expr).get_identifier()); + expr.set(ID_it_init_value_level, expr.get(ID_pointed_level)); + const irep_idt new_id=id2string(pointer_id)+id2string("'it"); + to_symbol_expr(expr).set_identifier(new_id); + identifier=identifiert(new_id); + } + protected: exprt expr; identifiert identifier; @@ -103,12 +131,15 @@ class ssa_objectst typedef std::set literalst; literalst literals; + const ssa_heap_analysist &heap_analysis; + ssa_objectst( const goto_functionst::goto_functiont &goto_function, - const namespacet &ns) + const namespacet &ns, + const ssa_heap_analysist &_heap_analysis): + heap_analysis(_heap_analysis) { collect_objects(goto_function, ns); - add_ptr_objects(ns); categorize_objects(goto_function, ns); } @@ -120,9 +151,6 @@ class ssa_objectst void categorize_objects( const goto_functionst::goto_functiont &, const namespacet &); - - void add_ptr_objects( - const namespacet &); }; bool is_ptr_object(const exprt &); diff --git a/src/ssa/ssa_pointed_objects.cpp b/src/ssa/ssa_pointed_objects.cpp new file mode 100644 index 000000000..bfcdaddee --- /dev/null +++ b/src/ssa/ssa_pointed_objects.cpp @@ -0,0 +1,548 @@ +/*******************************************************************\ + +Module: Library of functions for working with pointed objects + +Author: Viktor Malik + +\*******************************************************************/ + +#include "ssa_pointed_objects.h" +#include "ssa_object.h" + +/*******************************************************************\ + +Function: level_str + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const irep_idt level_str(const unsigned level, const irep_idt &suffix) +{ + return "#lvl_"+std::to_string(level)+"_"+id2string(suffix); +} + +/*******************************************************************\ + +Function: it_field_str + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const irep_idt it_field_str(const unsigned level) +{ + return id2string(ID_it_field)+"_"+std::to_string(level); +} + +/*******************************************************************\ + +Function: copy_iterator + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void copy_iterator(exprt &dest, const exprt &src) +{ + if(src.get_bool(ID_iterator)) + { + dest.set(ID_iterator, true); + dest.set(ID_it_pointer, src.get(ID_it_pointer)); + dest.set(ID_it_init_value, src.get(ID_it_init_value)); + dest.set(ID_it_init_value_level, src.get(ID_it_init_value_level)); + + unsigned field_cnt=src.get_unsigned_int(ID_it_field_cnt); + dest.set(ID_it_field_cnt, field_cnt); + for(unsigned i=0; i fields) +{ + dest.set(ID_it_field_cnt, (unsigned) fields.size()); + for(unsigned i=0; i get_iterator_fields(const exprt &expr) +{ + assert(is_iterator(expr)); + unsigned cnt=expr.get_unsigned_int(ID_it_field_cnt); + std::vector result; + for(unsigned i=0; i pointer_fields( + const exprt &expr, + const unsigned from_level) +{ + std::vector result; + unsigned max_level=pointed_level(expr); + assert(from_level +#include +#include + +#define ID_pointed "#pointed" +#define ID_pointed_level "#level" +#define ID_pointer_id "id" +#define ID_pointer_subtype "subtype" +#define ID_pointer_sym "sym" +#define ID_pointer_compound "compound" +#define ID_pointer_field "field" +#define ID_iterator "#iterator" +#define ID_it_pointer "#it_pointer" +#define ID_it_field "#it_field" +#define ID_it_field_cnt "#it_field_cnt" +#define ID_it_init_value "#it_init_value" +#define ID_it_init_value_level "#it_init_value_level" + +symbol_exprt pointed_object(const exprt &expr, const namespacet &ns); + +bool is_pointed(const exprt &expr); +bool is_iterator(const exprt &expr); + +unsigned pointed_level(const exprt &expr); +unsigned it_value_level(const exprt &expr); + +const irep_idt pointer_root_id(const exprt &expr); +const irep_idt pointer_level_field(const exprt &expr, const unsigned level); +const std::vector pointer_fields( + const exprt &expr, + const unsigned from_level); + +const exprt get_pointer(const exprt &expr, unsigned level); +const exprt get_pointer_root(const exprt &expr, unsigned level); +const irep_idt get_pointer_id(const exprt &expr); + +void copy_pointed_info(exprt &dest, const exprt &src, const unsigned max_level); +void copy_pointed_info(exprt &dest, const exprt &src); +void copy_iterator(exprt &dest, const exprt &src); + +const exprt symbolic_dereference(const exprt &expr, const namespacet &ns); +bool has_symbolic_deref(const exprt &expr); + +void set_iterator_fields(exprt &dest, const std::vector fields); +const std::vector get_iterator_fields(const exprt &expr); + +const irep_idt iterator_to_initial_id( + const exprt &expr, + const irep_idt &expr_id); + +#endif // CPROVER_2LS_SSA_SSA_POINTED_OBJECTS_H diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index aecf2166c..fa372d597 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -7,6 +7,7 @@ Author: Peter Schrammel, Saurabh Joshi \*******************************************************************/ // #define DEBUG +#define COMPETITION #include @@ -280,8 +281,9 @@ void ssa_local_unwindert::unwind(unsigned k) return; current_enabling_expr= - symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), - bool_typet()); + symbol_exprt( + "unwind::"+id2string(fname)+"::enable"+i2string(k), + bool_typet()); SSA.enabling_exprs.push_back(current_enabling_expr); // TODO: just for exploratory integration, must go away @@ -471,7 +473,9 @@ void ssa_local_unwindert::add_assertions(loopt &loop, bool is_last) if(!is_last) // only add assumptions if we are not in %0 iteration { if(is_kinduction) + { node.constraints.push_back(*a_it); + } else if(is_bmc) { // only add in base case @@ -559,8 +563,9 @@ void ssa_local_unwindert::add_loop_connector(loopt &loop) SSA.increment_unwindings(0); } else if(e_it->lhs().id()==ID_symbol && - !has_prefix(id2string(to_symbol_expr(e_it->lhs()).get_identifier()), - "ssa::$guard")) + !has_prefix( + id2string(to_symbol_expr(e_it->lhs()).get_identifier()), + "ssa::$guard")) { // this is needed for while loops node.equalities.push_back(*e_it); SSA.decrement_unwindings(0); @@ -569,8 +574,9 @@ void ssa_local_unwindert::add_loop_connector(loopt &loop) } } // continuation guard and condition - exprt g_rhs=and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); + exprt g_rhs=and_exprt( + SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); SSA.decrement_unwindings(0); exprt g_lhs=SSA.guard_symbol(loop.body_nodes.begin()->location); SSA.increment_unwindings(0); @@ -591,8 +597,10 @@ Function: ssa_local_unwindert::add_exit_merges void ssa_local_unwindert::add_exit_merges(loopt &loop, unsigned k) { - SSA.nodes.push_back(local_SSAt::nodet(loop.body_nodes.begin()->location, - SSA.nodes.end())); // add new node + SSA.nodes.push_back( + local_SSAt::nodet( + loop.body_nodes.begin()->location, + SSA.nodes.end())); // add new node local_SSAt::nodet &node=SSA.nodes.back(); node.enabling_expr=current_enabling_expr; @@ -658,7 +666,11 @@ void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) it!=loop.assertion_hoisting_map.end(); ++it) { if(!is_last // only add assumptions if we are not in %0 iteration - && is_kinduction && !it->second.assertions.empty()) + && is_kinduction && !it->second.assertions.empty() +#ifdef COMPETITION + && !(it->first->guard.id()==ID_not && + it->first->guard.op0().id()==ID_overflow_shl)) +#endif { exprt e=disjunction(it->second.exit_conditions); SSA.rename(e, loop.body_nodes.begin()->location); @@ -716,8 +728,9 @@ Function: ssa_local_unwindert::get_continuation_condition exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const { SSA.current_location=loop.body_nodes.back().location; // TODO: must go away - return and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); + return and_exprt( + SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); } /*******************************************************************\ @@ -895,9 +908,10 @@ void ssa_unwindert::init(bool is_kinduction, bool is_bmc) ssa_dbt::functionst& funcs=ssa_db.functions(); for(auto &f : funcs) { - unwinder_map.insert(unwinder_pairt( - f.first, - ssa_local_unwindert(f.first, (*(f.second)), is_kinduction, is_bmc))); + unwinder_map.insert( + unwinder_pairt( + f.first, + ssa_local_unwindert(f.first, (*(f.second)), is_kinduction, is_bmc))); } } diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index 62b7a20d7..840d97b24 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -50,12 +50,16 @@ class ssa_local_unwindert // TODO: these two should be possible with unwindable_local_ssa facilities exprt rename_invariant(const exprt& inv_in) const; void unwinder_rename( - symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const; #endif // TODO: this must go away, should use SSA.rename instead void unwinder_rename( - symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const; class loopt // loop tree { @@ -110,7 +114,8 @@ class ssa_local_unwindert exprt get_continuation_condition(const loopt& loop) const; void loop_continuation_conditions( - const loopt& loop, exprt::operandst &loop_cont) const; + const loopt& loop, + exprt::operandst &loop_cont) const; void add_loop_body(loopt &loop); void add_assertions(loopt &loop, bool is_last); @@ -118,7 +123,10 @@ class ssa_local_unwindert void add_loop_connector(loopt &loop); void add_exit_merges(loopt &loop, unsigned k); equal_exprt build_exit_merge( - exprt e, const exprt &exits, unsigned k, locationt loc); + exprt e, + const exprt &exits, + unsigned k, + locationt loc); void add_hoisted_assertions(loopt &loop, bool is_last); }; diff --git a/src/ssa/ssa_value_set.cpp b/src/ssa/ssa_value_set.cpp index aa362160e..d8e6bffb0 100644 --- a/src/ssa/ssa_value_set.cpp +++ b/src/ssa/ssa_value_set.cpp @@ -7,15 +7,19 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ // #define DEBUG +#define COMPETITION #ifdef DEBUG #include #endif +#include + #include #include "ssa_value_set.h" #include "ssa_dereference.h" +#include "ssa_pointed_objects.h" /*******************************************************************\ @@ -57,6 +61,15 @@ void ssa_value_domaint::transform( { const code_function_callt &code_function_call= to_code_function_call(from->code); + const irep_idt &fname=to_symbol_expr( + code_function_call.function()).get_identifier(); + + const ssa_value_ait &value_analysis=static_cast(ai); + + const ssa_heap_domaint *heap_domain=NULL; + if(value_analysis.heap_analysis.has_location(to)) + heap_domain=&value_analysis.heap_analysis[to]; + // functions may alter state almost arbitrarily: // * any global-scoped variables @@ -74,12 +87,107 @@ void ssa_value_domaint::transform( assign(*o_it, it, ns); #endif + std::list objects; + + for(auto &argument : code_function_call.arguments()) + { + exprt arg=argument; + exprt arg_expr=argument; + while(arg.type().id()==ID_pointer) + { + if(arg.id()==ID_symbol) + { + symbol_exprt pointed_obj=pointed_object(arg, ns); + pointed_obj.type().set("#dynamic", true); + + std::set new_objects; + if(heap_domain) + new_objects=heap_domain->value(arg_expr); + if(new_objects.empty()) + { + new_objects.insert(pointed_obj); + } + + auto it=new_objects.begin(); + assign_lhs_rec(arg, address_of_exprt(*it), ns); + objects.push_back(*it); + + for(++it; it!=new_objects.end(); ++it) + { + assign_lhs_rec(arg, address_of_exprt(*it), ns, true); + objects.push_back(*it); + } + + arg_expr=dereference_exprt(arg_expr, arg.type().subtype()); + arg=pointed_obj; + } + else if(arg.id()==ID_address_of) + { + arg=arg_expr=to_address_of_expr(arg).object(); + } + else if(arg.id()==ID_typecast) + { + assert(arg_expr.id()==ID_typecast); + arg=arg_expr=to_typecast_expr(arg).op(); + } + else + break; + } + } + // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) { exprt lhs_deref=dereference(code_function_call.lhs(), *this, "", ns); assign_lhs_rec(lhs_deref, nil_exprt(), ns); } + + // the assignment of return value might be in next instruction + if(to->is_assign() && to_code_assign(to->code).rhs().id()==ID_symbol) + { + const symbol_exprt &return_value=to_symbol_expr( + to_code_assign(to->code).rhs()); + if(return_value.type().id()==ID_pointer && + return_value.get_identifier()==id2string(fname)+"#return_value") + { + std::set new_objects; + if(heap_domain) + new_objects=heap_domain->value(return_value); + if(new_objects.empty()) + { + symbol_exprt pointed_obj=pointed_object(return_value, ns); + pointed_obj.type().set("#dynamic", true); + new_objects.insert(pointed_obj); + } + + auto it=new_objects.begin(); + assign_lhs_rec(return_value, address_of_exprt(*it), ns); + objects.push_back(*it); + + for(++it; it!=new_objects.end(); ++it) + { + assign_lhs_rec(return_value, address_of_exprt(*it), ns, true); + objects.push_back(*it); + } + + if(heap_domain) + { + for(auto &new_o : heap_domain->new_caller_objects(fname, from)) + { + objects.push_back(new_o); + } + } + } + } + + for(const symbol_exprt &o1 : objects) + { + for(const symbol_exprt &o2 : objects) + { + if(o1!=o2 && o1.type()==o2.type()) + value_map[ssa_objectt(o1, ns)].value_set.insert(ssa_objectt(o2, ns)); + } + } } else if(from->is_dead()) { @@ -124,13 +232,20 @@ void ssa_value_domaint::assign_lhs_rec( const struct_typet &struct_type=to_struct_type(lhs_type); const struct_typet::componentst &components=struct_type.components(); + auto rhs_it= + rhs.id()==ID_struct ? rhs.operands().begin() : rhs.operands().end(); + for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); it++) { member_exprt new_lhs(lhs, it->get_name(), it->type()); - member_exprt new_rhs(rhs, it->get_name(), it->type()); + exprt new_rhs; + if(rhs_it!=rhs.operands().end()) + new_rhs=*(rhs_it++); + else + new_rhs=member_exprt(rhs, it->get_name(), it->type()); assign_lhs_rec(new_lhs, new_rhs, ns, add); // recursive call } @@ -142,6 +257,12 @@ void ssa_value_domaint::assign_lhs_rec( if(ssa_object) { + // TODO: this is required for interprocedural analysis, + // but interferes with intraprocedural analysis +#if 0 + assign_pointed_rhs_rec(rhs, ns); +#endif + valuest tmp_values; assign_rhs_rec(tmp_values, rhs, ns, false, 0); @@ -246,6 +367,11 @@ void ssa_value_domaint::assign_rhs_rec( } else if(rhs.id()==ID_plus) { +#ifdef COMPETITION + bool pointer=false; + bool arithmetics=false; +#endif + forall_operands(it, rhs) { if(it->type().id()==ID_pointer) @@ -255,8 +381,22 @@ void ssa_value_domaint::assign_rhs_rec( pointer_offset=1; unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, *it, ns, true, a); + +#ifdef COMPETITION + pointer=true; +#endif + } + else if(it->type().id()==ID_unsignedbv || it->type().id()==ID_signedbv) + { +#ifdef COMPETITION + arithmetics=true; +#endif } } + +#ifdef COMPETITION + assert(!(pointer && arithmetics)); +#endif } else if(rhs.id()==ID_minus) { @@ -268,6 +408,12 @@ void ssa_value_domaint::assign_rhs_rec( pointer_offset=1; unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, rhs.op0(), ns, true, a); + +#ifdef COMPETITION + assert( + !(rhs.op1().type().id()==ID_unsignedbv || + rhs.op1().type().id()==ID_signedbv)); +#endif } } else if(rhs.id()==ID_dereference) @@ -430,7 +576,10 @@ Function: ssa_value_domaint::valuest::merge \*******************************************************************/ -bool ssa_value_domaint::valuest::merge(const valuest &src) +bool ssa_value_domaint::valuest::merge( + const valuest &src, + bool is_loop_back, + const irep_idt &object_id) { bool result=false; @@ -457,9 +606,79 @@ bool ssa_value_domaint::valuest::merge(const valuest &src) } // value set - unsigned old_size=value_set.size(); - value_set.insert(src.value_set.begin(), src.value_set.end()); - if(old_size!=value_set.size()) + unsigned long old_size=value_set.size(); + for(const ssa_objectt &v : src.value_set) + { + if(is_loop_back) + { + if(is_pointed(v.get_expr())) + { + unsigned level=pointed_level(v.get_expr())-1; + exprt expr=v.get_expr(); + + auto it=value_set.end(); + + while(level>0) + { + const irep_idt ptr_root_id=pointer_root_id(expr); + it=std::find_if( + value_set.begin(), + value_set.end(), + [&ptr_root_id](const ssa_objectt &o) + { return o.get_identifier()==ptr_root_id; }); + if(it!=value_set.end()) + break; + else + { + expr=get_pointer_root(expr, level--); + } + } + + if(it!=value_set.end()) + { + if(!it->get_expr().get_bool(ID_iterator)) + { + assert(it->get_expr().get_bool(ID_pointed)); + ssa_objectt object_copy(*it); + object_copy.set_iterator( + object_id, + pointer_fields( + v.get_expr(), + level)); + value_set.erase(it); + value_set.insert(object_copy); + result=true; + } + continue; + } + } + if(is_iterator(v.get_expr())) + continue; + } + else + { + if(v.get_expr().get_bool(ID_iterator)) + { + const irep_idt &corresponding_id=iterator_to_initial_id( + v.get_expr(), v.get_identifier()); + + auto it=std::find_if( + value_set.begin(), + value_set.end(), + [&corresponding_id](const ssa_objectt &o) + { return o.get_expr().get_bool(ID_pointed) && + (o.get_identifier()==corresponding_id); }); + if(it!=value_set.end()) + { + if(v!=*it) + result=true; + value_set.erase(it); + } + } + } + value_set.insert(v); + } + if(value_set.size()!=old_size) result=true; // alignment @@ -496,7 +715,9 @@ bool ssa_value_domaint::merge( { if(v_it==value_map.end() || it->firstfirst) { - value_map.insert(v_it, *it); + if(!from->is_backwards_goto() || + !is_iterator(it->first.get_root_object())) + value_map.insert(v_it, *it); result=true; it++; continue; @@ -509,7 +730,8 @@ bool ssa_value_domaint::merge( assert(v_it->first==it->first); - if(v_it->second.merge(it->second)) + if(v_it->second.merge(it->second, from->is_backwards_goto(), + it->first.get_identifier())) result=true; v_it++; @@ -518,3 +740,141 @@ bool ssa_value_domaint::merge( return result; } + +/*******************************************************************\ + +Function: ssa_value_domaint::assign_pointed_rhs_rec + + Inputs: + + Outputs: + + Purpose: Dynamically add p'obj to value set of p + +\*******************************************************************/ + +void ssa_value_domaint::assign_pointed_rhs_rec( + const exprt &rhs, + const namespacet &ns) +{ + ssa_objectt ssa_object(rhs, ns); + + if(ssa_object && ssa_object.type().id()==ID_pointer) + { + if(ssa_object.get_root_object().get_bool("#unknown_obj")) + return; + + value_mapt::const_iterator m_it=value_map.find(ssa_object); + + if(m_it==value_map.end()) + { + const symbol_exprt pointed=pointed_object(rhs, ns); + ssa_objectt pointed_obj(pointed, ns); + value_map[ssa_object].value_set.insert(pointed_obj); + } + } + else + { + forall_operands(it, rhs) + assign_pointed_rhs_rec(*it, ns); + } +} + +/*******************************************************************\ + +Function: ssa_value_ait::initialize + + Inputs: GOTO function + + Outputs: + + Purpose: Initialize value sets for pointer parameters and pointer-typed + fields of objects pointed by parameters. + +\*******************************************************************/ + +void ssa_value_ait::initialize( + const goto_functionst::goto_functiont &goto_function) +{ + ait::initialize(goto_function); + + // Initialize value sets for pointer parameters + + if(!goto_function.type.parameters().empty()) + { + locationt e=goto_function.body.instructions.begin(); + ssa_value_domaint &entry=operator[](e); + + for(auto ¶m : goto_function.type.parameters()) + { + const symbol_exprt param_expr(param.get_identifier(), param.type()); + assign_ptr_param(param_expr, entry); + } + } +} + +/*******************************************************************\ + +Function: ssa_value_ait::assign_ptr_param_rec + + Inputs: Expression to be initialized and entry record of value set analysis + + Outputs: + + Purpose: Initialize value set for the given expression and recursively for all + structure members and all pointed objects. + Pointer-typed variable p initially points to abstract object p'obj. + Pointer-typed field of structure initially points to advancer. + +\*******************************************************************/ + +void ssa_value_ait::assign_ptr_param( + const exprt &expr, + ssa_value_domaint &entry) +{ + const typet &type=ns.follow(expr.type()); + if(type.id()==ID_pointer) + { + if(expr.id()==ID_symbol) + { + // pointer variable + symbol_exprt pointed_expr=pointed_object(expr, ns); + assign(expr, pointed_expr, entry); + assign_ptr_param(pointed_expr, entry); + } + } + else if(type.id()==ID_struct) + { + // split structure into fields + for(auto &component : to_struct_type(type).components()) + { + const member_exprt member(expr, component.get_name(), component.type()); + assign_ptr_param(member, entry); + } + } +} + +/*******************************************************************\ + +Function: ssa_value_ait::assign + + Inputs: Pointer variable src, pointed object dest and analysis entry. + + Outputs: + + Purpose: Insert object to value set of another object in the given entry. + +\*******************************************************************/ + +void ssa_value_ait::assign( + const exprt &src, + const exprt &dest, + ssa_value_domaint &entry) +{ + ssa_objectt src_obj(src, ns); + ssa_objectt dest_obj(dest, ns); + if(src_obj && dest_obj) + { + entry.value_map[src_obj].value_set.insert(dest_obj); + } +} diff --git a/src/ssa/ssa_value_set.h b/src/ssa/ssa_value_set.h index 4e1f8afc1..e2e79c791 100644 --- a/src/ssa/ssa_value_set.h +++ b/src/ssa/ssa_value_set.h @@ -12,13 +12,16 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "ssa_object.h" +#include "ssa_heap_domain.h" class ssa_value_domaint:public ai_domain_baset { public: virtual void transform(locationt, locationt, ai_baset &, const namespacet &); virtual void output( - std::ostream &, const ai_baset &, const namespacet &) const; + std::ostream &, + const ai_baset &, + const namespacet &) const; bool merge(const ssa_value_domaint &, locationt, locationt); struct valuest @@ -40,7 +43,10 @@ class ssa_value_domaint:public ai_domain_baset void output(std::ostream &, const namespacet &) const; - bool merge(const valuest &src); + bool merge( + const valuest &src, + bool is_loop_back=false, + const irep_idt &object_id=irep_idt()); inline void clear() { @@ -66,22 +72,27 @@ class ssa_value_domaint:public ai_domain_baset protected: void assign_lhs_rec( - const exprt &lhs, const exprt &rhs, + const exprt &lhs, + const exprt &rhs, const namespacet &, bool add=false); void assign_rhs_rec( - valuest &lhs, const exprt &rhs, + valuest &lhs, + const exprt &rhs, const namespacet &, bool offset, unsigned alignment) const; void assign_rhs_rec_address_of( - valuest &lhs, const exprt &rhs, + valuest &lhs, + const exprt &rhs, const namespacet &, bool offset, unsigned alignment) const; + void assign_pointed_rhs_rec(const exprt &rhs, const namespacet &ns); + static unsigned merge_alignment(unsigned a, unsigned b) { // could use lcm here @@ -100,12 +111,26 @@ class ssa_value_ait:public ait public: ssa_value_ait( const goto_functionst::goto_functiont &goto_function, - const namespacet &ns) + const namespacet &ns_, + const ssa_heap_analysist &_heap_analysis): + ns(ns_), + heap_analysis(_heap_analysis) { - operator()(goto_function, ns); + operator()(goto_function, ns_); } protected: + virtual void initialize( + const goto_functionst::goto_functiont &goto_function) override; + + void assign_ptr_param(const exprt &expr, ssa_value_domaint &entry); + + void assign(const exprt &src, const exprt &dest, ssa_value_domaint &entry); + + const namespacet &ns; + + const ssa_heap_analysist &heap_analysis; + friend class ssa_value_domaint; }; diff --git a/src/ssa/ssa_var_collector.cpp b/src/ssa/ssa_var_collector.cpp new file mode 100644 index 000000000..7bf67d28a --- /dev/null +++ b/src/ssa/ssa_var_collector.cpp @@ -0,0 +1,245 @@ +/*******************************************************************\ + +Module: Template Generator for Summaries, Invariants and Preconditions + +Author: Peter Schrammel, Stefan Marticek + +\*******************************************************************/ + +#include "ssa_var_collector.h" + +/*******************************************************************\ + +Function: template_generator_baset::add_var + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_var_collectort::add_var( + const domaint::vart &var, + const domaint::guardt &pre_guard, + domaint::guardt post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) +{ + exprt aux_expr=true_exprt(); + if(std_invariants && pre_guard.id()==ID_and) + { + exprt init_guard=and_exprt(pre_guard.op0(), not_exprt(pre_guard.op1())); + exprt post_var=post_renaming_map[var]; + exprt aux_var=aux_renaming_map[var]; + exprt aux_equals_post=equal_exprt(aux_var, post_var); + exprt aux_equals_init=equal_exprt(aux_var, init_renaming_map[var]); + aux_expr= + and_exprt( + implies_exprt( + and_exprt(post_guard, not_exprt(init_guard)), + aux_equals_post), + implies_exprt( + init_guard, + aux_equals_init)); + post_guard=or_exprt(post_guard, init_guard); + } + if(var.type().id()!=ID_array) + { + var_specs.push_back(domaint::var_spect()); + domaint::var_spect &var_spec=var_specs.back(); + var_spec.pre_guard=pre_guard; + var_spec.post_guard=post_guard; + var_spec.aux_expr=aux_expr; + var_spec.kind=kind; + var_spec.var=var; + } + + // arrays + if(var.type().id()==ID_array && options.get_bool_option("arrays")) + { + const array_typet &array_type=to_array_type(var.type()); + mp_integer size; + to_integer(array_type.size(), size); + for(mp_integer i=0; ilocation->location_number << std::endl; + assert(n_it->loophead!=SSA.nodes.end()); + std::cout << "pre-location: " + << n_it->loophead->location->location_number << std::endl; +#endif + exprt lhguard=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard), *n_it, true); + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), *n_it, true); + pre_guard=and_exprt(lhguard, lsguard); + + exprt pguard=SSA.guard_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard), *n_it, false); + exprt pcond=SSA.cond_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond), *n_it, false); + post_guard=and_exprt(pguard, pcond); +} + +/*******************************************************************\ + +Function: template_generator_baset::get_pre_var + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_var_collectort::get_pre_var( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var) +{ + pre_var=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var, *n_it, true); + + symbol_exprt post_var=SSA.read_rhs(*o_it, n_it->location); + ssa_local_unwinder.unwinder_rename(post_var, *n_it, false); + post_renaming_map[pre_var]=post_var; + + rename_aux_post(post_var); + aux_renaming_map[pre_var]=post_var; +} + +/*******************************************************************\ + +Function: template_generator_baset::get_init_expr + + Inputs: + + Outputs: + + Purpose: supposes that loop head PHIs are of the form + xphi=gls?xlb:x0 + +\*******************************************************************/ + +void ssa_var_collectort::get_init_expr( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr) +{ + symbol_exprt phi_var= + SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var, *n_it->loophead, true); + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + n_it->loophead->equalities.begin(); + e_it!=n_it->loophead->equalities.end(); e_it++) + { + if(e_it->rhs().id()==ID_if && + to_symbol_expr(e_it->lhs()).get_identifier()==phi_var.get_identifier()) + { + const if_exprt &if_expr=to_if_expr(e_it->rhs()); + init_expr=if_expr.false_case(); + // should already be renamed for inner loops + break; + } + } + + symbol_exprt pre_var=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var, *n_it, true); + init_renaming_map[pre_var]=init_expr; +} + +/*******************************************************************\ + +Function: ssa_var_collectort::collect_variables_loop + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_var_collectort::collect_variables_loop( + const local_SSAt &SSA, + bool forward) +{ + // used for renaming map + var_listt pre_state_vars, post_state_vars; + + // add loop variables + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + exprt pre_guard, post_guard; + get_pre_post_guards(SSA, n_it, pre_guard, post_guard); + + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[n_it->loophead->location].phi_nodes; + + // Record the objects modified by the loop to get + // 'primed' (post-state) and 'unprimed' (pre-state) variables. + for(local_SSAt::objectst::const_iterator + o_it=SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); + o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it= + phi_nodes.find(o_it->get_identifier()); + + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop + + symbol_exprt pre_var; + get_pre_var(SSA, o_it, n_it, pre_var); + exprt init_expr; + get_init_expr(SSA, o_it, n_it, init_expr); + add_var(pre_var, pre_guard, post_guard, domaint::LOOP, var_specs); + +#ifdef DEBUG + std::cout << "Adding " << from_expr(ns, "", in) << " " + << from_expr(ns, "", out) << std::endl; +#endif + } + } + } +} diff --git a/src/ssa/ssa_var_collector.h b/src/ssa/ssa_var_collector.h new file mode 100644 index 000000000..1c69981aa --- /dev/null +++ b/src/ssa/ssa_var_collector.h @@ -0,0 +1,79 @@ +/*******************************************************************\ + +Module: SSA var collector class + +Author: Peter Schrammel, Stefan Marticek + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_VAR_COLLECTOR_H +#define CPROVER_2LS_SSA_SSA_VAR_COLLECTOR_H + +#include + +#include "local_ssa.h" +#include "ssa_unwinder.h" + +#include + +class ssa_var_collectort +{ +public: + typedef strategy_solver_baset::var_listt var_listt; + + explicit ssa_var_collectort( + optionst &_options, + ssa_local_unwindert &_ssa_local_unwinder): + options(_options), + ssa_local_unwinder(_ssa_local_unwinder) + { + std_invariants=options.get_bool_option("std-invariants"); + } + + domaint::var_specst var_specs; + replace_mapt post_renaming_map; + replace_mapt init_renaming_map; + replace_mapt aux_renaming_map; + + optionst options; // copy: we may override options + + void add_var( + const domaint::vart &var_to_add, + const domaint::guardt &pre_guard, + domaint::guardt post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + + void get_pre_post_guards( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &pre_guard, + exprt &post_guard); + + void get_pre_var( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var); + + void get_init_expr( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr); + + void rename_aux_post(symbol_exprt &expr) + { + expr.set_identifier(id2string(expr.get_identifier())+"'"); + } + + virtual void collect_variables_loop( + const local_SSAt &SSA, + bool forward); + +protected: + bool std_invariants; // include value at loop entry + const ssa_local_unwindert &ssa_local_unwinder; +}; + +#endif // CPROVER_2LS_SSA_SSA_VAR_COLLECTOR_H diff --git a/src/ssa/unwindable_local_ssa.cpp b/src/ssa/unwindable_local_ssa.cpp index 8b49f8e76..e53155c7f 100644 --- a/src/ssa/unwindable_local_ssa.cpp +++ b/src/ssa/unwindable_local_ssa.cpp @@ -282,7 +282,7 @@ void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) if(expr.id()==ID_symbol) { symbol_exprt &s=to_symbol_expr(expr); - locationt def_loc; + locationt def_loc=goto_function.body.instructions.end(); // we could reuse name(), // but then we would have to search in the ssa_objects // ENHANCEMENT: maybe better to attach base name, ssa name, @@ -294,13 +294,15 @@ void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) s.set_identifier(id2string(id)+unwind_suffix); #if 0 - std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; - std::cout << "DEF_LEVEL: " << def_level << std::endl; - std::cout << "O.size: " << current_unwindings.size() << std::endl; - std::cout << "current: " << current_unwinding << std::endl; std::cout << "RENAME_SYMBOL: " << id << " --> " << s.get_identifier() << std::endl; + std::cout << "DEF_LOC: " + << (def_loc!=goto_function.body.instructions.end() + ? def_loc->location_number : -1) << std::endl; + std::cout << "DEF_LEVEL: " << def_level << std::endl; + std::cout << "O.size: " << current_unwindings.size() << std::endl; + std::cout << "current: " << current_unwinding << std::endl << std::endl; #endif } if(expr.id()==ID_nondet_symbol) @@ -459,3 +461,45 @@ void unwindable_local_SSAt::compute_loop_hierarchy() } while(i_it!=goto_function.body.instructions.begin()); } + +/*******************************************************************\ + +Function: unwindable_local_SSAt::output_verbose + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void unwindable_local_SSAt::output_verbose(std::ostream &out) const +{ + for(const auto &node : nodes) + { + if(node.empty()) + continue; + out << "*** " << node.location->location_number + << " " << node.location->source_location << "\n"; + node.output(out, ns); + for(const auto &e : node.equalities) + { + std::set symbols; + find_symbols(e, symbols); + for(const auto &s : symbols) + { + if(s.type().get_bool("#dynamic")) + out << s.get_identifier() << "\n"; + } + } + if(node.loophead!=nodes.end()) + out << "loop back to location " + << node.loophead->location->location_number << "\n"; + if(!node.enabling_expr.is_true()) + out << "enabled if " + << from_expr(ns, "", node.enabling_expr) << "\n"; + out << "\n"; + } + out << "(enable) " << from_expr(ns, "", get_enabling_exprs()) << "\n\n"; +} diff --git a/src/ssa/unwindable_local_ssa.h b/src/ssa/unwindable_local_ssa.h index 462e89169..11f518225 100644 --- a/src/ssa/unwindable_local_ssa.h +++ b/src/ssa/unwindable_local_ssa.h @@ -12,6 +12,7 @@ Author: Peter Schrammel, Saurabh Joshi #include #include "local_ssa.h" +#include "ssa_heap_domain.h" class unwindable_local_SSAt:public local_SSAt { @@ -19,8 +20,9 @@ class unwindable_local_SSAt:public local_SSAt unwindable_local_SSAt( const goto_functiont &_goto_function, const namespacet &_ns, + const ssa_heap_analysist &heap_analysis, const std::string &_suffix=""): - local_SSAt(_goto_function, _ns, _suffix), + local_SSAt(_goto_function, _ns, heap_analysis, _suffix), current_unwinding(-1) { compute_loop_hierarchy(); @@ -36,7 +38,10 @@ class unwindable_local_SSAt:public local_SSAt return name(obj, kind, loc, loc); } symbol_exprt name( - const ssa_objectt &, kindt, locationt def_loc, locationt current_loc) const; + const ssa_objectt &, + kindt, + locationt def_loc, + locationt current_loc) const; virtual exprt nondet_symbol( std::string prefix, const typet &type, @@ -76,6 +81,8 @@ class unwindable_local_SSAt:public local_SSAt locationt &loc, odometert &odometer) const; + void output_verbose(std::ostream &) const override; + protected: irep_idt get_ssa_name(const symbol_exprt &, locationt &loc) const;