diff --git a/CODING_STANDARD b/CODING_STANDARD new file mode 100644 index 00000000000..ac5ae963186 --- /dev/null +++ b/CODING_STANDARD @@ -0,0 +1,17 @@ +Here a few minimalistic coding rules for the cprover source tree: + +a) 2 spaces indent, no tabs +b) no "using namespace std;" +c) Avoid new/delete, use containers instead. +d) Avoid unnecessary #includes, especially in header files +e) No lines wider than 80 chars +f) Put matching { } into the same column +g) If a method is bigger than a page, break it into parts +h) Avoid destructive updates if possible. The irept has + constant time copy. + +Architecture-specific code: + +a) Avoid if possible. +b) Use __LINUX__, __MACH__, and _WIN32 to distinguish the architectures. +c) Don't include architecture-specific header files without #ifdef ... diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..8f75a90fb7b --- /dev/null +++ b/LICENSE @@ -0,0 +1,40 @@ +(C) 2001-2011, Daniel Kroening, Edmund Clarke, +Computer Science Department, Oxford University +Computer Systems Institute, ETH Zurich +Computer Science Department, Carnegie Mellon University + +All rights reserved. Redistribution and use in source and binary forms, with +or without modification, are permitted provided that the following +conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Daniel Kroening, + Edmund Clarke, + Computer Science Department, Oxford University + Computer Systems Institute, ETH Zurich + Computer Science Department, Carnegie Mellon University + + 4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS `AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000000..c399cb1a28c --- /dev/null +++ b/src/Makefile @@ -0,0 +1,163 @@ +DIRS = ansi-c big-int cbmc cprover csp ebmc hoare \ + infrules intrep solvers pvs separate smvlang util verilog \ + vhdl langapi netlist cpp pascal symex termination \ + satqe satmc satabs explain specc xmllang promela \ + goto-programs bplang vcegar hw-cbmc vsynth \ + boppo pointer-analysis cogent goto-instrument \ + goto-symex ipp prover trans bv_refinement mcp goto-cc \ + smtlang php cemc interpolation scoot floatbv cover \ + scratch ai k-induction + +all: cprover.dir cbmc.dir ebmc.dir cogent.dir \ + satabs.dir hw-cbmc.dir promela.dir \ + boppo.dir goto-cc.dir goto-instrument.dir cover.dir + +include config.inc +include common + +############################################################################### + +util.dir: big-int.dir + +languages: util.dir langapi.dir \ + ansi-c.dir intrep.dir smvlang.dir \ + xmllang.dir promela.dir bplang.dir + +goto-instrument.dir: goto-programs.dir languages pointer-analysis.dir + +cogent.dir: ansi-c.dir util.dir solvers.dir + +cprover.dir: languages + +cbmc.dir: languages solvers.dir goto-symex.dir \ + pointer-analysis.dir goto-programs.dir goto-symex.dir + +cover.dir: cbmc.dir + +ivt.dir: languages solvers.dir goto-symex.dir \ + pointer-analysis.dir goto-programs.dir goto-symex.dir + +cemc.dir: cbmc.dir + +scoot.dir: satabs.dir languages + +goto-cc.dir: languages pointer-analysis.dir goto-programs.dir + +hw-cbmc.dir: cbmc.dir trans.dir + +boppo.dir: langapi.dir bplang.dir promela.dir util.dir \ + solvers.dir languages + +explain.dir: cbmc.dir + +satabs.dir: languages goto-symex.dir goto-programs.dir \ + pointer-analysis.dir solvers.dir + +vcegar.dir: languages satqe.dir solvers.dir ebmc.dir + +vsynth.dir: langapi.dir verilog.dir util.dir vhdl.dir netlist.dir + +ebmc.dir: solvers.dir languages trans.dir + +satmc.dir: solvers.dir smvlang.dir util.dir verilog.dir languages \ + intrep.dir vhdl.dir netlist.dir trans.dir satqe.dir +k-induction.dir: cbmc.dir ai.dir + +scratch.dir: k-induction.dir + +ai.dir: cbmc.dir + +ifdef MODULE_INTERPOLATION +ebmc.dir: interpolation.dir +interpolation.dir: solvers.dir langapi.dir util.dir +endif + +ifdef MODULE_PROVER +satabs.dir: prover.dir +ebmc.dir: prover.dir +endif + +ifdef MODULE_IPP +satabs.dir: ipp.dir +endif + +ifdef MODULE_BV_REFINEMENT +cbmc.dir: bv_refinement.dir +endif + +ifdef MODULE_SATQE +satabs.dir: satqe.dir +all: satmc.dir +endif + +ifdef MODULE_SMTLANG +languages: smtlang.dir +endif + +ifdef MODULE_CPP +languages: cpp.dir +endif + +ifdef MODULE_PHP +languages: php.dir +endif + +ifdef MODULE_CSP +languages: csp.dir +endif + +ifdef MODULE_PVS +languages: pvs.dir +endif + +ifdef MODULE_VERILOG +languages: verilog.dir +endif + +ifdef MODULE_VHDL +languages: vhdl.dir +endif + +ifdef MODULE_NETLIST +languages: netlist.dir +endif + +ifdef MODULE_SPECC +languages: specc.dir +endif + +ifdef MODULE_PASCAL +languages: pascal.dir +endif + +ifdef MODULE_HWCBMC +satabs: hw-cbmc.dir +all: hw-cbmc.dir +endif + +ifdef MODULE_CEMC +all: cemc.dir +endif + +ifdef MODULE_FLOATBV +solvers.dir: floatbv.dir +endif + +$(patsubst %, %.dir, $(DIRS)): + ## Entering $(basename $@) + $(MAKE) $(MAKEARGS) -C $(basename $@) + +clean: $(patsubst %, %_clean, $(DIRS)) + rm -f *.o + +dep: $(patsubst %, %_dep, $(DIRS)) + +$(patsubst %, %_clean, $(DIRS)): + if [ -e $(patsubst %_clean, %, $@)/. ] ; then \ + $(MAKE) $(MAKEARGS) -C $(patsubst %_clean, %, $@) clean ; \ + fi + +$(patsubst %, %_dep, $(DIRS)): + if [ -e $(patsubst %_dep, %, $@)/. ] ; then \ + $(MAKE) $(MAKEARGS) -C $(patsubst %_dep, %, $@) dep ; \ + fi diff --git a/src/ansi-c/Makefile b/src/ansi-c/Makefile new file mode 100644 index 00000000000..88241ded8da --- /dev/null +++ b/src/ansi-c/Makefile @@ -0,0 +1,63 @@ +SRC = c_typecast.cpp y.tab.cpp lex.yy.cpp ansi_c_parser.cpp \ + expr2c.cpp ansi_c_language.cpp c_sizeof.cpp c_main.cpp \ + c_types.cpp c_final.cpp trans_unit.cpp ansi_c_typecheck.cpp \ + c_link.cpp c_preprocess.cpp c_link_type_eq.cpp \ + c_typecheck_base.cpp c_typecheck_initializer.cpp \ + c_typecheck_typecast.cpp c_typecheck_code.cpp \ + c_typecheck_expr.cpp c_typecheck_type.cpp string_constant.cpp \ + c_qualifiers.cpp c_typecheck_argc_argv.cpp ansi_c_parse_tree.cpp \ + preprocessor_line.cpp ansi_c_convert.cpp ansi_c_convert_type.cpp \ + type2name.cpp cprover_library.cpp \ + printf_formatter.cpp internal_additions.cpp \ + ansi_c_declaration.cpp designator.cpp concatenate_strings.cpp \ + literals/parse_float.cpp literals/unescape_string.cpp \ + literals/convert_float_literal.cpp \ + literals/convert_character_literal.cpp \ + literals/convert_integer_literal.cpp \ + literals/convert_string_literal.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) + +INCLUDES= -I .. -I ../util + +include ../config.inc +include ../common + +all: ansi-c$(LIBEXT) library/converter$(EXEEXT) + +############################################################################### + +y.tab.cpp: parser.y + $(YACC) $(YFLAGS) $$flags -pyyansi_c -d parser.y -o y.tab.cpp + +y.tab.h: y.tab.cpp + if [ -e y.tab.hpp ] ; then mv y.tab.hpp y.tab.h ; else \ + mv y.tab.cpp.h y.tab.h ; fi + +lex.yy.cpp: scanner.l + $(LEX) -Pyyansi_c -olex.yy.cpp scanner.l + +# extra dependencies +y.tab$(OBJEXT): y.tab.cpp y.tab.h +lex.yy$(OBJEXT): y.tab.cpp lex.yy.cpp y.tab.h +cprover_library$(OBJEXT): cprover_library.inc + +############################################################################### + +library/converter$(EXEEXT): + $(CXX) $(LINKFLAGS) -o $@ library/converter.cpp + +cprover_library.inc: library/converter library/*.c + cat library/*.c | library/converter > $@ + +cprover_library.cpp: cprover_library.inc + +############################################################################### + +ansi-c$(LIBEXT): $(OBJ) + $(LINKLIB) + +clean: + rm -f $(OBJ) ansi-c$(LIBEXT) + rm -f y.tab.h y.tab.cpp lex.yy.cpp y.tab.cpp.output y.output + rm -f library/converter$(EXEEXT) cprover_library.inc diff --git a/src/ansi-c/README b/src/ansi-c/README new file mode 100644 index 00000000000..72cb6b9a675 --- /dev/null +++ b/src/ansi-c/README @@ -0,0 +1,10 @@ +CodeWarrior C Compilers Reference 3.2: + +http://cache.freescale.com/files/soft_dev_tools/doc/ref_manual/CCOMPILERRM.pdf + +http://cache.freescale.com/files/soft_dev_tools/doc/ref_manual/ASMX86RM.pdf + +ARM 4.1 Compiler Reference: + +http://infocenter.arm.com/help/topic/com.arm.doc.dui0491c/DUI0491C_arm_compiler_reference.pdf + diff --git a/src/ansi-c/ansi_c_convert.cpp b/src/ansi-c/ansi_c_convert.cpp new file mode 100644 index 00000000000..68075b08bb0 --- /dev/null +++ b/src/ansi-c/ansi_c_convert.cpp @@ -0,0 +1,586 @@ +/*******************************************************************\ + +Module: ANSI-C Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "ansi_c_convert.h" +#include "ansi_c_convert_type.h" +#include "ansi_c_declaration.h" + +/*******************************************************************\ + +Function: ansi_c_convertt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convertt::convert(ansi_c_parse_treet &ansi_c_parse_tree) +{ + for(ansi_c_parse_treet::itemst::iterator + it=ansi_c_parse_tree.items.begin(); + it!=ansi_c_parse_tree.items.end(); + it++) + { + assert(it->id()==ID_declaration); + convert_declaration(*it); + } +} + +/*******************************************************************\ + +Function: ansi_c_convertt::convert_declaration + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convertt::convert_declaration(ansi_c_declarationt &declaration) +{ + c_storage_spect c_storage_spec; + + convert_type(declaration.type(), c_storage_spec); + + declaration.set_is_inline(c_storage_spec.is_inline); + declaration.set_is_static(c_storage_spec.is_static); + declaration.set_is_extern(c_storage_spec.is_extern); + declaration.set_is_thread_local(c_storage_spec.is_thread_local); + declaration.set_is_register(c_storage_spec.is_register); + + // do not overwrite is_typedef -- it's done by the parser + // typedefs are macros + if(declaration.get_is_typedef()) + declaration.set_is_macro(true); + + // add language prefix + declaration.set_name(language_prefix+id2string(declaration.get_name())); + + if(declaration.value().is_not_nil()) + { + if(declaration.type().id()==ID_code) + convert_code(to_code(declaration.value())); + else + convert_expr(declaration.value()); + } +} + +/*******************************************************************\ + +Function: ansi_c_convertt::convert_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convertt::convert_expr(exprt &expr) +{ + if(expr.id()==ID_sideeffect) + { + const irep_idt &statement=expr.get(ID_statement); + + if(statement==ID_statement_expression) + { + assert(expr.operands().size()==1); + convert_code(to_code(expr.op0())); + return; + // done + } + } + + Forall_operands(it, expr) + convert_expr(*it); + + if(expr.id()==ID_symbol) + { + expr.set(ID_identifier, final_id(expr.get(ID_identifier))); + expr.remove(ID_C_id_class); + expr.remove(ID_C_base_name); + } + else if(expr.id()==ID_sizeof) + { + if(expr.operands().size()==0) + { + typet &type=static_cast(expr.add(ID_type_arg)); + convert_type(type); + } + } + else if(expr.id()==ID_designated_initializer) + { + exprt &designator=static_cast(expr.add(ID_designator)); + convert_expr(designator); + } + else if(expr.id()==ID_builtin_alignof) + { + if(expr.operands().size()==0) + { + typet &type=static_cast(expr.add(ID_type_arg)); + convert_type(type); + } + } + else if(expr.id()==ID_gcc_builtin_va_arg) + { + convert_type(expr.type()); + } + else if(expr.id()==ID_gcc_builtin_types_compatible_p) + { + typet &type=(typet &)expr; + assert(type.subtypes().size()==2); + convert_type(type.subtypes()[0]); + convert_type(type.subtypes()[1]); + } + else if(expr.id()==ID_builtin_offsetof) + { + typet &type=static_cast(expr.add(ID_type_arg)); + convert_type(type); + exprt &designator=static_cast(expr.add(ID_designator)); + convert_expr(designator); + } + else if(expr.id()==ID_typecast) + { + convert_type(expr.type()); + } +} + +/*******************************************************************\ + +Function: ansi_c_convertt::convert_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convertt::convert_code(codet &code) +{ + const irep_idt &statement=code.get_statement(); + + if(statement==ID_expression) + { + assert(code.operands().size()==1); + convert_expr(code.op0()); + } + else if(statement==ID_decl_type) + { + // type only + assert(code.operands().size()==0); + convert_type(static_cast(code.add(ID_type_arg))); + } + else if(statement==ID_decl) + { + // 1 or 2 operands + if(code.operands().size()==1) + convert_expr(code.op0()); + else if(code.operands().size()==2) + { + convert_expr(code.op0()); + convert_expr(code.op1()); + } + else + assert(false); + } + else if(statement==ID_label) + { + assert(code.operands().size()==1); + convert_code(to_code(code.op0())); + + if(code.find(ID_case).is_not_nil()) + convert_expr(static_cast(code.add(ID_case))); + } + else if(statement==ID_block || + statement==ID_decl_block) + { + Forall_operands(it, code) + convert_code(to_code(*it)); + } + else if(statement==ID_ifthenelse) + { + assert(code.operands().size()==2 || + code.operands().size()==3); + + convert_expr(code.op0()); + convert_code(to_code(code.op1())); + + if(code.operands().size()==3) + convert_code(to_code(code.op2())); + } + else if(statement==ID_while || + statement==ID_dowhile) + { + assert(code.operands().size()==2); + + convert_expr(code.op0()); + convert_code(to_code(code.op1())); + } + else if(statement==ID_for) + { + assert(code.operands().size()==4); + + if(code.op0().is_not_nil()) + convert_code(to_code(code.op0())); + + if(code.op1().is_not_nil()) + convert_expr(code.op1()); + + if(code.op2().is_not_nil()) + convert_expr(code.op2()); + + convert_code(to_code(code.op3())); + } + else if(statement==ID_switch) + { + assert(code.operands().size()==2); + + convert_expr(code.op0()); + convert_code(to_code(code.op1())); + } + else if(statement==ID_break) + { + } + else if(statement==ID_goto) + { + } + else if(statement=="computed-goto") + { + assert(code.operands().size()==1); + + convert_expr(code.op0()); + } + else if(statement==ID_continue) + { + } + else if(statement==ID_return) + { + if(code.operands().size()==1) + convert_expr(code.op0()); + } + else if(statement==ID_decl) + { + assert(code.operands().size()==1 || + code.operands().size()==2); + + convert_type(code.op0().type()); + + if(code.operands().size()==2) + convert_expr(code.op1()); + } + else if(statement==ID_skip) + { + } + else if(statement==ID_asm || statement==ID_msc) + { + } + else if(statement==ID_gcc_local_label) + { + } + else + { + err_location(code); + str << "unexpected statement during conversion: " << statement; + throw 0; + } +} + +/*******************************************************************\ + +Function: ansi_c_convertt::convert_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convertt::convert_type(typet &type) +{ + c_storage_spect c_storage_spec; + convert_type(type, c_storage_spec); +} + +/*******************************************************************\ + +Function: ansi_c_convertt::convert_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convertt::convert_type( + typet &type, + c_storage_spect &c_storage_spec) +{ + { + ansi_c_convert_typet ansi_c_convert_type(get_message_handler()); + + ansi_c_convert_type.read(type); + ansi_c_convert_type.write(type); + c_storage_spec=ansi_c_convert_type.c_storage_spec; + } + + if(type.id()==ID_pointer) + { + c_storage_spect sub_storage_spec; + + convert_type(type.subtype(), sub_storage_spec); + c_storage_spec|=sub_storage_spec; + } + else if(type.id()==ID_c_bitfield) + { + convert_type(type.subtype()); + convert_expr(static_cast(type.add(ID_size))); + } + else if(type.id()==ID_symbol) + { + irep_idt identifier=final_id(type.get(ID_identifier)); + type.set(ID_identifier, identifier); + type.remove(ID_C_id_class); + type.remove(ID_C_base_name); + } + else if(type.id()==ID_code) + { + c_storage_spect sub_storage_spec; + + convert_type(type.subtype(), sub_storage_spec); + c_storage_spec|=sub_storage_spec; + + code_typet &code_type=to_code_type(type); + + // change subtype to return_type + code_type.return_type().swap(type.subtype()); + type.remove(ID_subtype); + + // take care of argument types + code_typet::argumentst &arguments=code_type.arguments(); + + // see if we have an ellipsis + if(!arguments.empty() && + arguments.back().id()==ID_ellipsis) + { + code_type.make_ellipsis(); + arguments.pop_back(); + } + + for(code_typet::argumentst::iterator + it=arguments.begin(); + it!=arguments.end(); + it++) + { + if(it->id()==ID_declaration) + { + code_typet::argumentt argument; + + ansi_c_declarationt &declaration= + to_ansi_c_declaration(*it); + + convert_type(declaration.type()); + + irep_idt base_name=declaration.get_base_name(); + + argument.type().swap(declaration.type()); + argument.set_base_name(base_name); + argument.location()=declaration.location(); + + argument.set_identifier( + language_prefix+id2string(declaration.get_name())); + + it->swap(argument); + } + else if(it->id()==ID_ellipsis) + throw "ellipsis only allowed as last argument"; + else + throw "unexpected argument: "+it->id_string(); + } + } + else if(type.id()==ID_array) + { + array_typet &array_type=to_array_type(type); + + c_storage_spect sub_storage_spec; + + convert_type(array_type.subtype(), sub_storage_spec); + c_storage_spec|=sub_storage_spec; + + convert_expr(array_type.size()); + } + else if(type.id()==ID_bv) + { + exprt &size=static_cast(type.add(ID_size)); + convert_expr(size); + } + else if(type.id()==ID_incomplete_array) + { + c_storage_spect sub_storage_spec; + + convert_type(type.subtype(), sub_storage_spec); + c_storage_spec|=sub_storage_spec; + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + irept::subt &components=type.add(ID_components).get_sub(); + + Forall_irep(it, components) + { + // the arguments are now declarations + ansi_c_declarationt &component= + to_ansi_c_declaration(static_cast(*it)); + + exprt new_component(ID_component); + + new_component.location()=component.location(); + new_component.set(ID_name, component.get_base_name()); + new_component.set(ID_pretty_name, component.get_base_name()); + new_component.type().swap(component.type()); + + convert_type(new_component.type()); + + component.swap(new_component); + } + } + else if(type.id()==ID_typeof) + { + if(type.find(ID_operands).is_nil()) + { + convert_type(static_cast(type.add(ID_type_arg))); + } + else + { + exprt &expr=(exprt &)type; + assert(expr.operands().size()==1); + convert_expr(expr.op0()); + } + } + else if(type.id()==ID_c_enum || + type.id()==ID_incomplete_c_enum) + { + // add width + type.set(ID_width, config.ansi_c.int_width); + } + else if(type.id()==ID_expression) + { + convert_expr((exprt &)(type.subtype())); + } + else if(type.id()==ID_vector) + { + vector_typet &vector_type=to_vector_type(type); + convert_expr(vector_type.size()); + + c_storage_spect sub_storage_spec; + + convert_type(vector_type.subtype(), sub_storage_spec); + c_storage_spec|=sub_storage_spec; + } +} + +/*******************************************************************\ + +Function: ansi_c_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_convert( + ansi_c_parse_treet &ansi_c_parse_tree, + const std::string &module, + message_handlert &message_handler) +{ + ansi_c_convertt ansi_c_convert(module, message_handler); + + try + { + ansi_c_convert.convert(ansi_c_parse_tree); + } + + catch(int e) + { + ansi_c_convert.error(); + return true; + } + + catch(const char *e) + { + ansi_c_convert.error(e); + return true; + } + + catch(const std::string &e) + { + ansi_c_convert.error(e); + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: ansi_c_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_convert( + exprt &expr, + const std::string &module, + message_handlert &message_handler) +{ + ansi_c_convertt ansi_c_convert(module, message_handler); + + try + { + ansi_c_convert.convert_expr(expr); + } + + catch(int e) + { + ansi_c_convert.error(); + } + + catch(const char *e) + { + ansi_c_convert.error(e); + } + + catch(const std::string &e) + { + ansi_c_convert.error(e); + } + + return ansi_c_convert.get_error_found(); +} diff --git a/src/ansi-c/ansi_c_convert.h b/src/ansi-c/ansi_c_convert.h new file mode 100644 index 00000000000..bf36c147ce6 --- /dev/null +++ b/src/ansi-c/ansi_c_convert.h @@ -0,0 +1,68 @@ +/*******************************************************************\ + +Module: ANSI-C Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_CONVERT_H +#define CPROVER_ANSI_C_CONVERT_H + +#include +#include +#include +#include + +#include "c_storage_spec.h" +#include "ansi_c_parse_tree.h" + +bool ansi_c_convert( + ansi_c_parse_treet &ansi_c_parse_tree, + const std::string &module, + message_handlert &message_handler); + +bool ansi_c_convert( + exprt &expr, + const std::string &module, + message_handlert &message_handler); + +class ansi_c_convertt:public message_streamt +{ +public: + ansi_c_convertt( + const std::string &_module, + message_handlert &_message_handler): + message_streamt(_message_handler), + language_prefix("c::"), + module(_module) + { + } + + virtual void convert(ansi_c_parse_treet &ansi_c_parse_tree); + + // expressions + virtual void convert_expr(exprt &expr); + + // types + virtual void convert_type(typet &type); + +protected: + virtual void convert_type( + typet &type, + c_storage_spect &c_storage_spec); + + virtual void convert_code(codet &code); + + virtual void convert_declaration(ansi_c_declarationt &declaration); + + std::string language_prefix; + const std::string &module; + + irep_idt final_id(const irep_idt &id) const + { + return language_prefix+id2string(id); + } +}; + +#endif diff --git a/src/ansi-c/ansi_c_convert_type.cpp b/src/ansi-c/ansi_c_convert_type.cpp new file mode 100644 index 00000000000..b4ef27b84f9 --- /dev/null +++ b/src/ansi-c/ansi_c_convert_type.cpp @@ -0,0 +1,367 @@ +/*******************************************************************\ + +Module: SpecC Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include +#include +#include + +#include "ansi_c_convert_type.h" + +/*******************************************************************\ + +Function: ansi_c_convert_typet::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convert_typet::read(const typet &type) +{ + clear(); + location=type.location(); + read_rec(type); +} + +/*******************************************************************\ + +Function: ansi_c_convert_typet::read_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convert_typet::read_rec(const typet &type) +{ + if(type.id()==ID_merged_type) + { + forall_subtypes(it, type) + read_rec(*it); + } + else if(type.id()==ID_signed) + signed_cnt++; + else if(type.id()==ID_unsigned) + unsigned_cnt++; + else if(type.id()==ID_ptr32) + c_qualifiers.is_ptr32=true; + else if(type.id()==ID_ptr64) + c_qualifiers.is_ptr64=true; + else if(type.id()==ID_volatile) + c_qualifiers.is_volatile=true; + else if(type.id()==ID_asm) + { + // ignore for now + } + else if(type.id()==ID_const) + c_qualifiers.is_constant=true; + else if(type.id()==ID_restricted) + c_qualifiers.is_restricted=true; + else if(type.id()==ID_char) + char_cnt++; + else if(type.id()==ID_int) + int_cnt++; + else if(type.id()==ID_int8) + int8_cnt++; + else if(type.id()==ID_int16) + int16_cnt++; + else if(type.id()==ID_int32) + int32_cnt++; + else if(type.id()==ID_int64) + int64_cnt++; + else if(type.id()==ID_bv) + { + bv_cnt++; + const exprt &size_expr= + static_cast(type.find(ID_size)); + + mp_integer size_int; + if(to_integer(size_expr, size_int)) + { + err_location(location); + error("bit vector width has to be constant"); + std::cout << type.pretty() << std::endl; + throw 0; + } + + if(size_int<1 || size_int>1024) + { + err_location(location); + error("bit vector width invalid"); + throw 0; + } + + bv_width=integer2long(size_int); + } + else if(type.id()==ID_short) + short_cnt++; + else if(type.id()==ID_long) + long_cnt++; + else if(type.id()==ID_double) + double_cnt++; + else if(type.id()==ID_float) + float_cnt++; + else if(type.id()==ID_bool) + bool_cnt++; + else if(type.id()==ID_complex) + complex_cnt++; + else if(type.id()==ID_static) + c_storage_spec.is_static=true; + else if(type.id()==ID_thread_local) + c_storage_spec.is_thread_local=true; + else if(type.id()==ID_inline) + c_storage_spec.is_inline=true; + else if(type.id()==ID_extern) + c_storage_spec.is_extern=true; + else if(type.id()==ID_typedef) + c_storage_spec.is_typedef=true; + else if(type.id()==ID_register) + c_storage_spec.is_register=true; + else if(type.id()==ID_auto) + { + // ignore + } + else if(type.id()==ID_packed) + packed=true; + else if(type.id()==ID_aligned) + aligned=true; + else if(type.id()==ID_transparent_union) + transparent_union=true; + else if(type.id()==ID_vector) + vector_size=to_vector_type(type).size(); + else if(type.id()==ID_void) + { + // we store 'void' as 'empty' + typet tmp=type; + tmp.id(ID_empty); + other.push_back(tmp); + } + else + other.push_back(type); +} + +/*******************************************************************\ + +Function: ansi_c_convert_typet::write + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_convert_typet::write(typet &type) +{ + type.clear(); + + // first, do "other" + + if(!other.empty()) + { + if(double_cnt || float_cnt || signed_cnt || + unsigned_cnt || int_cnt || bool_cnt || + short_cnt || char_cnt || complex_cnt || long_cnt || + int8_cnt || int16_cnt || int32_cnt || int64_cnt || + bv_cnt) + { + err_location(location); + error("illegal type modifier for defined type"); + throw 0; + } + + if(other.size()!=1) + { + err_location(location); + error("illegal combination of defined types"); + throw 0; + } + + type.swap(other.front()); + } + else if(double_cnt || float_cnt || complex_cnt) + { + if(signed_cnt || unsigned_cnt || int_cnt || bool_cnt || + int8_cnt || int16_cnt || int32_cnt || int64_cnt || + bv_cnt || + short_cnt || char_cnt) + { + err_location(location); + error("cannot combine integer type with float or complex"); + throw 0; + } + + if(double_cnt && float_cnt) + { + err_location(location); + error("conflicting type modifiers"); + throw 0; + } + + if(long_cnt==0) + { + if(double_cnt!=0) + type=double_type(); + else + type=float_type(); + } + else if(long_cnt==1 || long_cnt==2) + { + if(double_cnt!=0) + type=long_double_type(); + else + { + err_location(location); + error("conflicting type modifiers"); + throw 0; + } + } + else + { + err_location(location); + error("illegal type modifier for float or complex"); + throw 0; + } + } + else if(bool_cnt) + { + if(signed_cnt || unsigned_cnt || int_cnt || short_cnt || + int8_cnt || int16_cnt || int32_cnt || int64_cnt || + bv_cnt || + char_cnt || long_cnt) + { + err_location(location); + error("illegal type modifier for boolean type"); + throw 0; + } + + type.id(ID_bool); + } + else + { + // it is integer -- signed or unsigned? + + if(signed_cnt && unsigned_cnt) + { + err_location(location); + error("conflicting type modifiers"); + throw 0; + } + else if(unsigned_cnt) + type.id(ID_unsignedbv); + else if(signed_cnt) + type.id(ID_signedbv); + else + { + if(char_cnt) + type.id(config.ansi_c.char_is_unsigned?ID_unsignedbv:ID_signedbv); + else + type.id(ID_signedbv); + } + + // get width + + unsigned width; + + if(int8_cnt || int16_cnt || int32_cnt || int64_cnt || bv_cnt) + { + if(long_cnt || char_cnt || short_cnt) + { + err_location(location); + error("conflicting type modifiers"); + throw 0; + } + + if(int8_cnt) + width=1*8; + else if(int16_cnt) + width=2*8; + else if(int32_cnt) + width=4*8; + else if(int64_cnt) + width=8*8; + else if(bv_cnt) + width=bv_width; + else + assert(false); + } + else if(short_cnt) + { + if(long_cnt || char_cnt) + { + err_location(location); + error("conflicting type modifiers"); + throw 0; + } + + width=config.ansi_c.short_int_width; + } + else if(char_cnt) + { + if(long_cnt) + { + err_location(location); + error("illegal type modifier for char type"); + throw 0; + } + + width=config.ansi_c.char_width; + } + else if(long_cnt==0) + { + width=config.ansi_c.int_width; + } + else if(long_cnt==1) + { + width=config.ansi_c.long_int_width; + } + else if(long_cnt==2) + { + width=config.ansi_c.long_long_int_width; + } + else + { + err_location(location); + error("illegal type modifier for integer type"); + throw 0; + } + + type.set(ID_width, width); + } + + if(vector_size.is_not_nil()) + { + vector_typet new_type; + new_type.size()=vector_size; + new_type.location()=vector_size.location(); + new_type.subtype().swap(type); + type=new_type; + } + + c_qualifiers.write(type); + + if(transparent_union) + type.set(ID_transparent_union, true); + + if(packed && type.id()==ID_struct) + type.set(ID_packed, true); + + if(aligned) + type.set(ID_aligned, true); +} diff --git a/src/ansi-c/ansi_c_convert_type.h b/src/ansi-c/ansi_c_convert_type.h new file mode 100644 index 00000000000..de5de0fca97 --- /dev/null +++ b/src/ansi-c/ansi_c_convert_type.h @@ -0,0 +1,73 @@ +/*******************************************************************\ + +Module: ANSI-C Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_CONVERT_TYPE_H +#define CPROVER_ANSI_C_CONVERT_TYPE_H + +#include + +#include +#include +#include + +class ansi_c_convert_typet:public message_streamt +{ +public: + unsigned unsigned_cnt, signed_cnt, char_cnt, + int_cnt, short_cnt, long_cnt, + double_cnt, float_cnt, bool_cnt, + complex_cnt; + + // extensions + unsigned int8_cnt, int16_cnt, int32_cnt, int64_cnt, + ptr32_cnt, ptr64_cnt, + bv_cnt, bv_width; + + bool transparent_union, packed, aligned; + exprt vector_size; + + // storage spec + c_storage_spect c_storage_spec; + + // qualifiers + c_qualifierst c_qualifiers; + + void read(const typet &type); + void write(typet &type); + + locationt location; + + std::list other; + + ansi_c_convert_typet(message_handlert &_message_handler): + message_streamt(_message_handler) + { + } + + void clear() + { + unsigned_cnt=signed_cnt=char_cnt=int_cnt=short_cnt= + long_cnt=double_cnt=float_cnt=bool_cnt=complex_cnt= + int8_cnt=int16_cnt=int32_cnt=int64_cnt= + ptr32_cnt=ptr64_cnt= + bv_cnt=0; + vector_size.make_nil(); + bv_width=0; + + packed=aligned=transparent_union=false; + + other.clear(); + c_storage_spec.clear(); + c_qualifiers.clear(); + } + +protected: + void read_rec(const typet &type); +}; + +#endif diff --git a/src/ansi-c/ansi_c_declaration.cpp b/src/ansi-c/ansi_c_declaration.cpp new file mode 100644 index 00000000000..96d4e86512b --- /dev/null +++ b/src/ansi-c/ansi_c_declaration.cpp @@ -0,0 +1,74 @@ +/*******************************************************************\ + +Module: ANSI-C Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "ansi_c_declaration.h" + +/*******************************************************************\ + +Function: ansi_c_declarationt::ansi_c_declarationt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +ansi_c_declarationt::ansi_c_declarationt():exprt(ID_declaration) +{ +} + +/*******************************************************************\ + +Function: ansi_c_declarationt::to_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_declarationt::to_symbol(symbolt &symbol) const +{ + symbol.clear(); + symbol.location=location(); + symbol.value=value(); + symbol.type=type(); + symbol.name=get_name(); + symbol.base_name=get_base_name(); + symbol.is_type=get_is_type(); + symbol.is_extern=get_is_extern(); + symbol.is_macro=get_is_macro(); + symbol.is_actual=get_is_argument(); + + bool is_code=symbol.type.id()==ID_code; + + symbol.static_lifetime= + !symbol.is_macro && + !symbol.is_type && + (get_is_global() || get_is_static()) && + !is_code; + + symbol.thread_local= + (!get_is_global() && !is_code && + !get_is_static() && !symbol.is_type && + !symbol.is_macro) || + (get_is_global() && !is_code && get_is_thread_local()); + + symbol.file_local= + get_is_static() || symbol.is_macro || + (!get_is_global() && !get_is_extern() && !is_code); + + if(get_is_inline()) + symbol.type.set(ID_C_inlined, true); +} diff --git a/src/ansi-c/ansi_c_declaration.h b/src/ansi-c/ansi_c_declaration.h new file mode 100644 index 00000000000..62930c31bb5 --- /dev/null +++ b/src/ansi-c/ansi_c_declaration.h @@ -0,0 +1,166 @@ +/*******************************************************************\ + +Module: ANSI-CC Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_DECLARATION_H +#define CPROVER_ANSI_C_DECLARATION_H + +#include + +#include + +class ansi_c_declarationt:public exprt +{ +public: + ansi_c_declarationt(); + + exprt &value() + { + return static_cast(add(ID_value)); + } + + const exprt &value() const + { + return static_cast(find(ID_value)); + } + + void set_name(const irep_idt &name) + { + return set(ID_name, name); + } + + irep_idt get_name() const + { + return get(ID_name); + } + + irep_idt get_base_name() const + { + return get(ID_base_name); + } + + void set_base_name(const irep_idt &base_name) + { + return set(ID_base_name, base_name); + } + + bool get_is_type() const + { + return get_bool(ID_is_type); + } + + void set_is_type(bool is_type) + { + set(ID_is_type, is_type); + } + + bool get_is_typedef() const + { + return get_bool(ID_is_typedef); + } + + void set_is_typedef(bool is_typedef) + { + set(ID_is_typedef, is_typedef); + } + + bool get_is_macro() const + { + return get_bool(ID_is_macro); + } + + void set_is_macro(bool is_macro) + { + set(ID_is_macro, is_macro); + } + + bool get_is_static() const + { + return get_bool(ID_is_static); + } + + void set_is_static(bool is_static) + { + set(ID_is_static, is_static); + } + + bool get_is_argument() const + { + return get_bool(ID_is_argument); + } + + void set_is_argument(bool is_argument) + { + set(ID_is_argument, is_argument); + } + + bool get_is_global() const + { + return get_bool(ID_is_global); + } + + void set_is_global(bool is_global) + { + set(ID_is_global, is_global); + } + + bool get_is_register() const + { + return get_bool(ID_is_register); + } + + void set_is_register(bool is_register) + { + set(ID_is_register, is_register); + } + + bool get_is_thread_local() const + { + return get_bool(ID_is_thread_local); + } + + void set_is_thread_local(bool is_thread_local) + { + set(ID_is_thread_local, is_thread_local); + } + + bool get_is_inline() const + { + return get_bool(ID_is_inline); + } + + void set_is_inline(bool is_inline) + { + set(ID_is_inline, is_inline); + } + + bool get_is_extern() const + { + return get_bool(ID_is_extern); + } + + void set_is_extern(bool is_extern) + { + set(ID_is_extern, is_extern); + } + + void to_symbol(symbolt &symbol) const; +}; + +extern inline ansi_c_declarationt &to_ansi_c_declaration(exprt &expr) +{ + assert(expr.id()==ID_declaration); + return static_cast(expr); +} + +extern inline const ansi_c_declarationt &to_ansi_c_declaration(const exprt &expr) +{ + assert(expr.id()==ID_declaration); + return static_cast(expr); +} + +#endif diff --git a/src/ansi-c/ansi_c_expr.cpp b/src/ansi-c/ansi_c_expr.cpp new file mode 100644 index 00000000000..82d686d168f --- /dev/null +++ b/src/ansi-c/ansi_c_expr.cpp @@ -0,0 +1,69 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "ansi_c_expr.h" +#include "c_types.h" + +/*******************************************************************\ + +Function: string_constantt::set_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +string_constantt::string_constantt(): + exprt(ID_string_constant) +{ + set_value(irep_idt()); + type()=typet(ID_array); + type().subtype()=char_type(); +} + +/*******************************************************************\ + +Function: string_constantt::set_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_constantt::set_value(const irep_idt &value) +{ + exprt size_expr=from_integer(value.size()+1, int_type()); + type().add(ID_size).swap(size_expr); + set(ID_value, value); +} + +/*******************************************************************\ + +Function: string_constantt::set_long + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_constantt::set_long(bool is_long) +{ + set(ID_long, is_long); + type().subtype()=is_long?wchar_t_type():char_type(); +} diff --git a/src/ansi-c/ansi_c_expr.h b/src/ansi-c/ansi_c_expr.h new file mode 100644 index 00000000000..f1aacbb9092 --- /dev/null +++ b/src/ansi-c/ansi_c_expr.h @@ -0,0 +1,49 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_EXPR_H +#define CPROVER_ANSI_C_EXPR_H + +#include + +class string_constantt:public exprt +{ +public: + string_constantt(); + + friend inline const string_constantt &to_string_constant(const exprt &expr) + { + assert(expr.id()==ID_string_constant); + return static_cast(expr); + } + + friend inline string_constantt &to_string_constant(exprt &expr) + { + assert(expr.id()==ID_string_constant); + return static_cast(expr); + } + + void set_value(const irep_idt &value); + + inline const irep_idt &get_value() const + { + return get(ID_value); + } + + void set_long(bool _long); + + inline bool get_long() const + { + return get_bool(ID_long); + } +}; + +const string_constantt &to_string_constant(const exprt &expr); +string_constantt &to_string_constant(exprt &expr); + +#endif diff --git a/src/ansi-c/ansi_c_language.cpp b/src/ansi-c/ansi_c_language.cpp new file mode 100644 index 00000000000..dcbdb5ae520 --- /dev/null +++ b/src/ansi-c/ansi_c_language.cpp @@ -0,0 +1,381 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +#include "ansi_c_language.h" +#include "ansi_c_convert.h" +#include "ansi_c_typecheck.h" +#include "ansi_c_parser.h" +#include "expr2c.h" +#include "c_final.h" +#include "trans_unit.h" +#include "c_link.h" +#include "c_preprocess.h" +#include "c_main.h" +#include "internal_additions.h" + +/*******************************************************************\ + +Function: ansi_c_languaget::extensions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::set ansi_c_languaget::extensions() const +{ + std::set s; + s.insert("c"); + s.insert("i"); + return s; +} + +/*******************************************************************\ + +Function: ansi_c_languaget::modules_provided + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_languaget::modules_provided(std::set &modules) +{ + modules.insert(translation_unit(parse_path)); +} + +/*******************************************************************\ + +Function: ansi_c_languaget::preprocess + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool ansi_c_languaget::preprocess( + std::istream &instream, + const std::string &path, + std::ostream &outstream, + message_handlert &message_handler) +{ + // stdin? + if(path=="") + return c_preprocess(instream, outstream, message_handler); + + return c_preprocess(path, outstream, message_handler); +} + +/*******************************************************************\ + +Function: ansi_c_languaget::parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_languaget::parse( + std::istream &instream, + const std::string &path, + message_handlert &message_handler) +{ + // store the path + + parse_path=path; + + // preprocessing + + std::ostringstream o_preprocessed; + + if(preprocess(instream, path, o_preprocessed, message_handler)) + return true; + + std::istringstream i_preprocessed(o_preprocessed.str()); + + // parsing + + std::string code; + ansi_c_internal_additions(code); + std::istringstream codestr(code); + + ansi_c_parser.clear(); + ansi_c_parser.filename=""; + ansi_c_parser.in=&codestr; + ansi_c_parser.set_message_handler(message_handler); + ansi_c_parser.grammar=ansi_c_parsert::LANGUAGE; + + switch(config.ansi_c.mode) + { + case configt::ansi_ct::MODE_CODEWARRIOR: + ansi_c_parser.mode=ansi_c_parsert::CW; + break; + + case configt::ansi_ct::MODE_VISUAL_STUDIO: + ansi_c_parser.mode=ansi_c_parsert::MSC; + break; + + case configt::ansi_ct::MODE_ANSI: + ansi_c_parser.mode=ansi_c_parsert::ANSI; + break; + + case configt::ansi_ct::MODE_GCC: + ansi_c_parser.mode=ansi_c_parsert::GCC; + break; + + case configt::ansi_ct::MODE_ARM: + ansi_c_parser.mode=ansi_c_parsert::ARM; + break; + + default: + assert(false); + } + + ansi_c_scanner_init(); + + bool result=ansi_c_parser.parse(); + + if(!result) + { + ansi_c_parser.line_no=0; + ansi_c_parser.filename=path; + ansi_c_parser.in=&i_preprocessed; + ansi_c_scanner_init(); + result=ansi_c_parser.parse(); + } + + // save result + parse_tree.swap(ansi_c_parser.parse_tree); + + // save some memory + ansi_c_parser.clear(); + + return result; +} + +/*******************************************************************\ + +Function: ansi_c_languaget::typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_languaget::typecheck( + contextt &context, + const std::string &module, + message_handlert &message_handler) +{ + if(ansi_c_convert(parse_tree, module, message_handler)) + return true; + + contextt new_context; + + if(ansi_c_typecheck(parse_tree, new_context, module, message_handler)) + return true; + + if(c_link(context, new_context, message_handler)) + return true; + + return false; +} + +/*******************************************************************\ + +Function: ansi_c_languaget::final + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_languaget::final( + contextt &context, + message_handlert &message_handler) +{ + if(c_final(context, message_handler)) return true; + if(c_main(context, "c::", "c::main", message_handler)) return true; + + return false; +} + +/*******************************************************************\ + +Function: ansi_c_languaget::show_parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_languaget::show_parse(std::ostream &out) +{ + parse_tree.output(out); +} + +/*******************************************************************\ + +Function: new_ansi_c_language + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +languaget *new_ansi_c_language() +{ + return new ansi_c_languaget; +} + +/*******************************************************************\ + +Function: ansi_c_languaget::from_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_languaget::from_expr( + const exprt &expr, + std::string &code, + const namespacet &ns) +{ + code=expr2c(expr, ns); + return false; +} + +/*******************************************************************\ + +Function: ansi_c_languaget::from_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_languaget::from_type( + const typet &type, + std::string &code, + const namespacet &ns) +{ + code=type2c(type, ns); + return false; +} + +/*******************************************************************\ + +Function: ansi_c_languaget::to_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_languaget::to_expr( + const std::string &code, + const std::string &module, + exprt &expr, + message_handlert &message_handler, + const namespacet &ns) +{ + expr.make_nil(); + + // no preprocessing yet... + + std::istringstream i_preprocessed(code); + + // parsing + + ansi_c_parser.clear(); + ansi_c_parser.filename=""; + ansi_c_parser.in=&i_preprocessed; + ansi_c_parser.set_message_handler(message_handler); + ansi_c_parser.grammar=ansi_c_parsert::EXPRESSION; + ansi_c_parser.mode=ansi_c_parsert::GCC; + ansi_c_scanner_init(); + + bool result=ansi_c_parser.parse(); + + if(ansi_c_parser.parse_tree.items.empty()) + result=true; + else + { + expr=ansi_c_parser.parse_tree.items.front().value(); + + result=ansi_c_convert(expr, "", message_handler); + + // typecheck it + if(!result) + result=ansi_c_typecheck(expr, message_handler, ns); + } + + // save some memory + ansi_c_parser.clear(); + + return result; +} + +/*******************************************************************\ + +Function: ansi_c_languaget::~ansi_c_languaget + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +ansi_c_languaget::~ansi_c_languaget() +{ +} diff --git a/src/ansi-c/ansi_c_language.h b/src/ansi-c/ansi_c_language.h new file mode 100644 index 00000000000..66fce0c4d30 --- /dev/null +++ b/src/ansi-c/ansi_c_language.h @@ -0,0 +1,77 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_LANGUAGE_H +#define CPROVER_ANSI_C_LANGUAGE_H + +#include + +#include "ansi_c_parse_tree.h" + +class ansi_c_languaget:public languaget +{ +public: + virtual bool preprocess( + std::istream &instream, + const std::string &path, + std::ostream &outstream, + message_handlert &message_handler); + + virtual bool parse( + std::istream &instream, + const std::string &path, + message_handlert &message_handler); + + virtual bool typecheck( + contextt &context, + const std::string &module, + message_handlert &message_handler); + + virtual bool final( + contextt &context, + message_handlert &message_handler); + + virtual void show_parse(std::ostream &out); + + virtual ~ansi_c_languaget(); + ansi_c_languaget() { } + + virtual bool from_expr( + const exprt &expr, + std::string &code, + const namespacet &ns); + + virtual bool from_type( + const typet &type, + std::string &code, + const namespacet &ns); + + virtual bool to_expr( + const std::string &code, + const std::string &module, + exprt &expr, + message_handlert &message_handler, + const namespacet &ns); + + virtual languaget *new_language() + { return new ansi_c_languaget; } + + virtual std::string id() const { return "C"; } + virtual std::string description() const { return "ANSI-C 99"; } + virtual std::set extensions() const; + + virtual void modules_provided(std::set &modules); + +protected: + ansi_c_parse_treet parse_tree; + std::string parse_path; +}; + +languaget *new_ansi_c_language(); + +#endif diff --git a/src/ansi-c/ansi_c_parse_tree.cpp b/src/ansi-c/ansi_c_parse_tree.cpp new file mode 100644 index 00000000000..f4a829dafef --- /dev/null +++ b/src/ansi-c/ansi_c_parse_tree.cpp @@ -0,0 +1,68 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "ansi_c_parse_tree.h" + +/*******************************************************************\ + +Function: ansi_c_parse_treet::swap + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_parse_treet::swap(ansi_c_parse_treet &ansi_c_parse_tree) +{ + ansi_c_parse_tree.items.swap(items); +} + +/*******************************************************************\ + +Function: ansi_c_parse_treet::clear + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_parse_treet::clear() +{ + items.clear(); +} + +/*******************************************************************\ + +Function: ansi_c_parse_treet::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_parse_treet::output(std::ostream &out) const +{ + for(itemst::const_iterator + it=items.begin(); + it!=items.end(); + it++) + { + symbolt tmp; + it->to_symbol(tmp); + out << tmp; + } +} diff --git a/src/ansi-c/ansi_c_parse_tree.h b/src/ansi-c/ansi_c_parse_tree.h new file mode 100644 index 00000000000..0ca726c0c4e --- /dev/null +++ b/src/ansi-c/ansi_c_parse_tree.h @@ -0,0 +1,26 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_PARSE_TREE_H +#define CPROVER_ANSI_C_PARSE_TREE_H + +#include "ansi_c_declaration.h" + +class ansi_c_parse_treet +{ +public: + // the declarations + typedef std::list itemst; + itemst items; + + void swap(ansi_c_parse_treet &other); + void clear(); + void output(std::ostream &out) const; +}; + +#endif diff --git a/src/ansi-c/ansi_c_parser.cpp b/src/ansi-c/ansi_c_parser.cpp new file mode 100644 index 00000000000..a9c2ef21a5a --- /dev/null +++ b/src/ansi-c/ansi_c_parser.cpp @@ -0,0 +1,250 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "ansi_c_parser.h" + +ansi_c_parsert ansi_c_parser; + +/*******************************************************************\ + +Function: ansi_c_parsert::scopet::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_parsert::scopet::print(std::ostream &out) const +{ + out << "Prefix: " << prefix << std::endl; + + for(scopet::name_mapt::const_iterator n_it=name_map.begin(); + n_it!=name_map.end(); + n_it++) + { + out << " ID: " << n_it->first + << " CLASS: " << n_it->second.id_class + << std::endl; + } +} + +/*******************************************************************\ + +Function: ansi_c_parsert::lookup + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +ansi_c_id_classt ansi_c_parsert::lookup(std::string &name, bool tag) const +{ + const std::string scope_name=tag?"tag-"+name:name; + + for(scopest::const_reverse_iterator it=scopes.rbegin(); + it!=scopes.rend(); it++) + { + scopet::name_mapt::const_iterator n_it=it->name_map.find(scope_name); + if(n_it!=it->name_map.end()) + { + name=it->prefix+scope_name; + return n_it->second.id_class; + } + } + + return ANSI_C_UNKNOWN; +} + +/*******************************************************************\ + +Function: yyansi_cerror + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +extern char *yyansi_ctext; + +int yyansi_cerror(const std::string &error) +{ + ansi_c_parser.parse_error(error, yyansi_ctext); + return 0; +} + +/*******************************************************************\ + +Function: ansi_c_parsert::convert_declarator + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_parsert::convert_declarator( + irept &declarator, + const typet &type, + irept &identifier) +{ + typet *p=(typet *)&declarator; + + // walk down subtype until we hit symbol or "abstract" + while(true) + { + typet &t=*p; + + if(t.id()==ID_symbol) + { + identifier=t; + t=type; + break; + } + else if(t.id()==irep_idt() || + t.is_nil()) + { + std::cout << "D: " << declarator.pretty() << std::endl; + assert(0); + } + else if(t.id()==ID_abstract) + { + identifier.make_nil(); + t=type; + break; + } + else if(t.id()==ID_merged_type) + { + assert(!t.subtypes().empty()); + p=&(t.subtypes().back()); + } + else + p=&t.subtype(); + } +} + +/*******************************************************************\ + +Function: ansi_c_parsert::new_declaration + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_parsert::new_declaration( + const irept &type, + irept &declarator, + exprt &dest, + bool is_tag, + bool put_into_scope) +{ + assert(declarator.is_not_nil()); + + exprt identifier; + + convert_declarator(declarator, static_cast(type), identifier); + typet final_type=static_cast(declarator); + + std::string base_name=identifier.get_string(ID_C_base_name); + + bool is_global=current_scope().prefix=="" || + (mode==MSC && is_tag); + + ansi_c_id_classt id_class=get_class(final_type); + + const std::string scope_name= + is_tag?"tag-"+base_name:base_name; + + if(is_tag) + final_type.set(ID_tag, base_name); + + std::string name; + + if(base_name!="") + { + if(mode==MSC && is_tag) + name=scope_name; + else + name=current_scope().prefix+scope_name; + + if(put_into_scope) + { + scopet &scope = (mode==MSC && is_tag)?root_scope():current_scope(); + + // see if already in scope + scopet::name_mapt::const_iterator n_it= + scope.name_map.find(scope_name); + + if(n_it==scope.name_map.end()) + { + // add to scope + scope.name_map[scope_name].id_class=id_class; + } + } + } + + // create dest + ansi_c_declarationt declaration; + + declaration.type().swap(final_type); + declaration.set_base_name(base_name); + declaration.set_name(name); + declaration.location()=identifier.location(); + declaration.value().make_nil(); + declaration.set_is_type(is_tag || id_class==ANSI_C_TYPEDEF); + declaration.set_is_typedef(id_class==ANSI_C_TYPEDEF); + declaration.set_is_global(is_global); + + dest.swap(declaration); +} + +/*******************************************************************\ + +Function: ansi_c_parsert::get_class + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +ansi_c_id_classt ansi_c_parsert::get_class(const typet &type) +{ + if(type.id()==ID_typedef) + return ANSI_C_TYPEDEF; + else if(type.id()==ID_struct || + type.id()==ID_union || + type.id()==ID_c_enum) + return ANSI_C_TAG; + else if(type.id()==ID_merged_type) + { + forall_subtypes(it, type) + if(get_class(*it)==ANSI_C_TYPEDEF) + return ANSI_C_TYPEDEF; + } + else if(type.has_subtype()) + return get_class(type.subtype()); + + return ANSI_C_SYMBOL; +} diff --git a/src/ansi-c/ansi_c_parser.h b/src/ansi-c/ansi_c_parser.h new file mode 100644 index 00000000000..b9cdce25ad6 --- /dev/null +++ b/src/ansi-c/ansi_c_parser.h @@ -0,0 +1,170 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SPECC_PARSER_H +#define CPROVER_SPECC_PARSER_H + +#include + +#include +#include +#include +#include +#include + +#include "ansi_c_parse_tree.h" + +typedef enum { ANSI_C_UNKNOWN, ANSI_C_SYMBOL, ANSI_C_TYPEDEF, + ANSI_C_TAG } ansi_c_id_classt; + +int yyansi_cparse(); + +class ansi_c_parsert:public parsert +{ +public: + ansi_c_parse_treet parse_tree; + + ansi_c_parsert() + { + } + + virtual bool parse() + { + return yyansi_cparse(); + } + + virtual void clear() + { + parsert::clear(); + parse_tree.clear(); + + // scanner + string_literal.clear(); + tag_following=false; + asm_block_following=false; + parenthesis_counter=0; + + // setup global scope + scopes.clear(); + + // this is the global scope + scopes.push_back(scopet()); + } + + // internal state scanner + std::string string_literal; + bool tag_following; + bool asm_block_following; + unsigned parenthesis_counter; + + enum { LANGUAGE, EXPRESSION } grammar; + + enum { ANSI, GCC, MSC, ICC, CW, ARM } mode; + // ANSI is strict ANSI-C + // GCC is, well, gcc + // MSC is Microsoft Visual Studio + // ICC is Intel's C compiler + // CW is CodeWarrior (with GCC extensions enabled) + // ARM is ARM's RealView + + class identifiert + { + public: + ansi_c_id_classt id_class; + std::string base_name; + }; + + class scopet + { + public: + typedef hash_map_cont name_mapt; + name_mapt name_map; + + std::string prefix; + + unsigned compound_counter, anon_counter; + + scopet():compound_counter(0), anon_counter(0) { } + + void swap(scopet &scope) + { + name_map.swap(scope.name_map); + prefix.swap(scope.prefix); + std::swap(compound_counter, scope.compound_counter); + } + + void print(std::ostream &out) const; + }; + + typedef std::list scopest; + scopest scopes; + + scopet &root_scope() + { + return scopes.front(); + } + + const scopet &root_scope() const + { + return scopes.front(); + } + + void pop_scope() + { + scopes.pop_back(); + } + + scopet ¤t_scope() + { + assert(!scopes.empty()); + return scopes.back(); + } + + static void convert_declarator( + irept &declarator, + const typet &type, + irept &identifier); + + void new_declaration( + const irept &type, + irept &declarator, + exprt &declaration, + bool is_tag=false, + bool put_into_scope=true); + + void copy_item(const ansi_c_declarationt &declaration) + { + assert(declaration.id()==ID_declaration); + parse_tree.items.push_back(declaration); + } + + void new_scope(const std::string &prefix) + { + const scopet ¤t=current_scope(); + scopes.push_back(scopet()); + scopes.back().prefix=current.prefix+prefix; + } + + ansi_c_id_classt lookup(std::string &name, bool tag) const; + + static ansi_c_id_classt get_class(const typet &type); + + std::string get_anon_name() + { + std::string n="#anon"; + n+=i2string(current_scope().anon_counter++); + return n; + } +}; + +extern ansi_c_parsert ansi_c_parser; + +int yyansi_cerror(const std::string &error); +void ansi_c_scanner_init(); + +#endif diff --git a/src/ansi-c/ansi_c_typecheck.cpp b/src/ansi-c/ansi_c_typecheck.cpp new file mode 100644 index 00000000000..b73db36f2ae --- /dev/null +++ b/src/ansi-c/ansi_c_typecheck.cpp @@ -0,0 +1,112 @@ +/*******************************************************************\ + +Module: ANSI-C Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "ansi_c_typecheck.h" + +/*******************************************************************\ + +Function: ansi_c_typecheckt::typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_typecheckt::typecheck() +{ + for(ansi_c_parse_treet::itemst::iterator + it=parse_tree.items.begin(); + it!=parse_tree.items.end(); + it++) + { + if(it->id()==ID_declaration) + { + symbolt symbol; + to_ansi_c_declaration(*it).to_symbol(symbol); + typecheck_symbol(symbol); + } + else if(it->id()==ID_initializer) + { + } + else + assert(false); + } +} + +/*******************************************************************\ + +Function: ansi_c_typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_typecheck( + ansi_c_parse_treet &ansi_c_parse_tree, + contextt &context, + const std::string &module, + message_handlert &message_handler) +{ + ansi_c_typecheckt ansi_c_typecheck( + ansi_c_parse_tree, context, module, message_handler); + return ansi_c_typecheck.typecheck_main(); +} + +/*******************************************************************\ + +Function: ansi_c_typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ansi_c_typecheck( + exprt &expr, + message_handlert &message_handler, + const namespacet &ns) +{ + contextt context; + ansi_c_parse_treet ansi_c_parse_tree; + + ansi_c_typecheckt ansi_c_typecheck( + ansi_c_parse_tree, context, + ns.get_context(), "", message_handler); + + try + { + ansi_c_typecheck.typecheck_expr(expr); + } + + catch(int e) + { + ansi_c_typecheck.error(); + } + + catch(const char *e) + { + ansi_c_typecheck.error(e); + } + + catch(const std::string &e) + { + ansi_c_typecheck.error(e); + } + + return ansi_c_typecheck.get_error_found(); +} diff --git a/src/ansi-c/ansi_c_typecheck.h b/src/ansi-c/ansi_c_typecheck.h new file mode 100644 index 00000000000..78b6bf02a74 --- /dev/null +++ b/src/ansi-c/ansi_c_typecheck.h @@ -0,0 +1,60 @@ +/*******************************************************************\ + +Module: ANSI-C Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_TYPECHECK_H +#define CPROVER_ANSI_C_TYPECHECK_H + +#include + +#include "ansi_c_parse_tree.h" + +bool ansi_c_typecheck( + ansi_c_parse_treet &parse_tree, + contextt &context, + const std::string &module, + message_handlert &message_handler); + +bool ansi_c_typecheck( + exprt &expr, + message_handlert &message_handler, + const namespacet &ns); + +class ansi_c_typecheckt:public c_typecheck_baset +{ +public: + ansi_c_typecheckt( + ansi_c_parse_treet &_parse_tree, + contextt &_context, + const std::string &_module, + message_handlert &_message_handler): + c_typecheck_baset(_context, _module, _message_handler), + parse_tree(_parse_tree) + { + } + + ansi_c_typecheckt( + ansi_c_parse_treet &_parse_tree, + contextt &_context1, + const contextt &_context2, + const std::string &_module, + message_handlert &_message_handler): + c_typecheck_baset(_context1, _context2, + _module, _message_handler), + parse_tree(_parse_tree) + { + } + + virtual ~ansi_c_typecheckt() { } + + virtual void typecheck(); + +protected: + ansi_c_parse_treet &parse_tree; +}; + +#endif diff --git a/src/ansi-c/arm_builtin_headers.h b/src/ansi-c/arm_builtin_headers.h new file mode 100644 index 00000000000..289c7ad038b --- /dev/null +++ b/src/ansi-c/arm_builtin_headers.h @@ -0,0 +1,44 @@ +#define ARM_BUILTIN_HEADERS \ + "void __breakpoint(int val);\n" \ + "void __cdp(unsigned int coproc, unsigned int ops, unsigned int regs);\n" \ + "void __clrex(void);\n" \ + "unsigned char __clz(unsigned int val);\n" \ + "unsigned int __current_pc(void);\n" \ + "unsigned int __current_sp(void);\n" \ + "int __disable_fiq(void);\n" \ + "int __disable_irq(void);\n" \ + "void __enable_fiq(void);\n" \ + "void __enable_irq(void);\n" \ + "double __fabs(double val);\n" \ + "float __fabs(float val);\n" \ + "void __force_stores(void);\n" \ + "unsigned int __ldrex(volatile void *ptr);\n" \ + "unsigned long long __ldrexd(volatile void *ptr);\n" \ + "unsigned int __ldrt(const volatile void *ptr);\n" \ + "void __memory_changed(void);\n" \ + "void __nop(void);\n" \ + "void __pld();\n" \ + "void __pldw();\n" \ + "void __pli();\n" \ + "void __promise(expr);\n" \ + "int __qadd(int val1, int val2);\n" \ + "int __qdbl(int val);\n" \ + "int __qsub(int val1, int val2);\n" \ + "unsigned int __rbit(unsigned int val);" \ + "unsigned int __rev(unsigned int val);" \ + "unsigned int __return_address(void);" \ + "unsigned int __ror(unsigned int val, unsigned int shift);\n" \ + "void __schedule_barrier(void);\n" \ + "int __semihost(int val, const void *ptr);\n" \ + "void __sev(void);\n" \ + "void __sev(void);\n" \ + "float __sqrtf(float);\n" \ + "int __ssat(int val, unsigned int sat);\n" \ + "int __strex(unsigned int val, volatile void *ptr);\n" \ + "int __strexd(unsigned long long val, volatile void *ptr);\n" \ + "void __strt(unsigned int val, volatile void *ptr);\n" \ + "unsigned int __swp(unsigned int val, volatile void *ptr);\n" \ + "int __usat(unsigned int val, unsigned int sat);\n" \ + "void __wfe(void);\n" \ + "void __wfi(void);\n" \ + "void __yield(void);\n" diff --git a/src/ansi-c/c_final.cpp b/src/ansi-c/c_final.cpp new file mode 100644 index 00000000000..101fdb0fb9d --- /dev/null +++ b/src/ansi-c/c_final.cpp @@ -0,0 +1,168 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include + +#include "c_types.h" +#include "c_final.h" +#include "cprover_library.h" + +/*******************************************************************\ + +Function: c_final + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +#if 0 +void c_finalize_expression( + const contextt &context, + exprt &expr, + message_handlert &message_handler) +{ + if(expr.id()==ID_symbol) + { + if(expr.type().id()==ID_incomplete_array) + { + symbolst::const_iterator it= + context.symbols.find(expr.get(ID_identifier)); + + if(it==context.symbols.end()) + { + message_streamt message_stream(message_handler); + message_stream.str + << "c_finalize_expression: failed to find symbol `" + << expr.get("identifier") << "'"; + message_stream.error(); + throw 0; + } + + const symbolt &symbol=it->second; + + if(symbol.type.id()==ID_array) + expr.type()=symbol.type; + else if(symbol.type.id()==ID_incomplete_array) + { + message_streamt message_stream(message_handler); + message_stream.err_location(symbol.location); + message_stream.str + << "symbol `" << symbol.display_name() + << "' has incomplete type"; + message_stream.error(); + throw 0; + } + else + { + message_streamt message_stream(message_handler); + message_stream.err_location(symbol.location); + message_stream.str + << "symbol `" << symbol.display_name() + << "' has an unexpected type"; + message_stream.error(); + throw 0; + } + } + } + + if(expr.has_operands()) + Forall_operands(it, expr) + c_finalize_expression(context, *it, message_handler); +} +#endif + +/*******************************************************************\ + +Function: add_external_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool add_external_objects( + contextt &context, + message_handlert &message_handler) +{ + namespacet ns(context); + + replace_mapt replace_map; + + Forall_symbols(it, context.symbols) + { + symbolt &symbol=it->second; + const typet &final_type=ns.follow(symbol.type); + + if(symbol.mode==ID_C && + symbol.is_extern && !symbol.is_type) + { + if(final_type.id()==ID_incomplete_array) + { + // all arrays get a symbol-type + // (see c_typecheck_baset::typecheck_new_symbol) + assert(symbol.type.id()==ID_symbol); + + #if 0 + message_streamt message_stream(message_handler); + message_stream.err_location(symbol.location); + message_stream.str + << "symbol `" << symbol.display_name() + << "' is declared extern but never defined. " + << "The object will be modeled as an array of infinite size."; + message_stream.warning(); + #endif + + array_typet new_type; + new_type.subtype()=final_type.subtype(); + new_type.size()=exprt(ID_infinity, index_type()); + + const irep_idt &ident=symbol.type.get(ID_identifier); + contextt::symbolst::iterator fit=context.symbols.find(ident); + + if(fit==context.symbols.end()) + throw std::string("symbol not found: `")+ident.as_string()+"'"; + + // set the new type + fit->second.type=new_type; + } + } + } + + return false; +} + +/*******************************************************************\ + +Function: c_final + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_final(contextt &context, message_handlert &message_handler) +{ + add_cprover_library(context, message_handler); + add_external_objects(context, message_handler); + + return false; +} diff --git a/src/ansi-c/c_final.h b/src/ansi-c/c_final.h new file mode 100644 index 00000000000..e7a7eda67d0 --- /dev/null +++ b/src/ansi-c/c_final.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_C_FINAL_H +#define CPROVER_C_FINAL_H + +#include + +#include +#include + +bool c_final(contextt &context, message_handlert &message_handler); + +#endif diff --git a/src/ansi-c/c_link.cpp b/src/ansi-c/c_link.cpp new file mode 100644 index 00000000000..36b2c4886a2 --- /dev/null +++ b/src/ansi-c/c_link.cpp @@ -0,0 +1,448 @@ +/*******************************************************************\ + +Module: ANSI-C Linking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "c_link_type_eq.h" +#include "expr2c.h" + +#include "c_link.h" +#include "c_link_class.h" + +/*******************************************************************\ + +Function: c_linkt::to_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string c_linkt::to_string(const exprt &expr) +{ + return expr2c(expr, ns); +} + +/*******************************************************************\ + +Function: c_linkt::to_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string c_linkt::to_string(const typet &type) +{ + return type2c(type, ns); +} + +/*******************************************************************\ + +Function: c_linkt::duplicate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_linkt::duplicate( + symbolt &old_symbol, + symbolt &new_symbol) +{ + if(new_symbol.is_type!=old_symbol.is_type) + { + str << "symbol category conflict on symbol `" + << old_symbol.name << "'"; + throw 0; + } + + if(new_symbol.is_type) + duplicate_type(old_symbol, new_symbol); + else + duplicate_non_type(old_symbol, new_symbol); +} + +/*******************************************************************\ + +Function: c_linkt::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +irep_idt c_linkt::rename(const irep_idt &old_identifier) +{ + irep_idt new_identifier; + + do + { + new_identifier= + id2string(old_identifier)+"#link"+i2string(renaming_counter++); + } + while(main_context.symbols.find(new_identifier)!= + main_context.symbols.end()); + + return new_identifier; +} + +/*******************************************************************\ + +Function: c_linkt::duplicate_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_linkt::duplicate_type( + symbolt &old_symbol, + symbolt &new_symbol) +{ + // check if it is really the same + // -- use base_type_eq, not c_link_type_eq + if(base_type_eq(old_symbol.type, new_symbol.type, ns)) + return; + + // they are different + if(old_symbol.type.id()==ID_incomplete_struct && + new_symbol.type.id()==ID_struct) + { + old_symbol.type=new_symbol.type; // store new type + } + else if(old_symbol.type.id()==ID_struct && + new_symbol.type.id()==ID_incomplete_struct) + { + // ignore + } + else if(ns.follow(old_symbol.type).id()==ID_incomplete_array && + ns.follow(new_symbol.type).id()==ID_array) + { + old_symbol.type=new_symbol.type; // store new type + } + else if(ns.follow(old_symbol.type).id()==ID_array && + ns.follow(new_symbol.type).id()==ID_incomplete_array) + { + // ignore + } + else + { + // rename! + irep_idt old_identifier=new_symbol.name; + irep_idt new_identifier=rename(old_identifier); + + replace_symbol.insert(old_identifier, symbol_typet(new_identifier)); + + new_symbol.name=new_identifier; + + // need to replace again + replace_symbol.replace(new_symbol.type); + + // move over! + bool result=main_context.move(new_symbol); + assert(!result); + } +} + +/*******************************************************************\ + +Function: c_linkt::duplicate_non_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_linkt::duplicate_non_type( + symbolt &old_symbol, + symbolt &new_symbol) +{ + // first check if file-local + if(new_symbol.file_local) + { + // we just always rename these + irep_idt old_identifier=new_symbol.name; + replace_symbolt::expr_mapt::const_iterator r_it= + replace_symbol.expr_map.find(old_identifier); + assert(r_it!=replace_symbol.expr_map.end()); + assert(r_it->second.id()==ID_symbol); + + new_symbol.name=r_it->second.get(ID_identifier); + + // move over! + bool result=main_context.move(new_symbol); + assert(!result); + + return; + } + + // see if it is a function or a variable + + bool is_code_old_symbol=old_symbol.type.id()==ID_code; + bool is_code_new_symbol=new_symbol.type.id()==ID_code; + + if(is_code_old_symbol!=is_code_new_symbol) + { + err_location(new_symbol.location); + str << "error: conflicting definition for symbol \"" + << old_symbol.display_name() + << "\"" << std::endl; + str << "old definition: " << to_string(old_symbol.type) + << std::endl; + str << "Module: " << old_symbol.module << std::endl; + str << "new definition: " << to_string(new_symbol.type) + << std::endl; + str << "Module: " << new_symbol.module; + throw 0; + } + + if(is_code_old_symbol) + { + // Both are functions. + // We don't compare the types, they will be too different; + // we just care about the code + + if(!new_symbol.value.is_nil()) + { + if(old_symbol.value.is_nil()) + { + // the one with body wins! + old_symbol.value=new_symbol.value; + old_symbol.type=new_symbol.type; // for argument identifiers + } + else if(to_code_type(old_symbol.type).get_inlined()) + { + // ok + } + else if(base_type_eq(old_symbol.type, new_symbol.type, ns)) + { + // keep the one in old_symbol -- libraries come last! + str << "warning: function `" << old_symbol.name << "' in module `" << + new_symbol.module << "' is shadowed by a definition in module `" << + old_symbol.module << "'"; + warning(); + } + else + { + err_location(new_symbol.value); + str << "error: duplicate definition of function `" + << old_symbol.name + << "'" << std::endl; + str << "In module `" << old_symbol.module + << "' and module `" << new_symbol.module << "'"; + throw 0; + } + } + } + else + { + // both are variables + + if(!base_type_eq(old_symbol.type, new_symbol.type, ns)) + { + if(ns.follow(old_symbol.type).id()==ID_incomplete_array && + ns.follow(new_symbol.type).id()==ID_array) + { + // store new type + old_symbol.type=new_symbol.type; + } + else if(ns.follow(old_symbol.type).id()==ID_array && + ns.follow(new_symbol.type).id()==ID_incomplete_array) + { + // ignore + } + else if(old_symbol.type.id()==ID_incomplete_struct && + new_symbol.type.id()==ID_struct) + { + // store new type + old_symbol.type=new_symbol.type; + } + else if(old_symbol.type.id()==ID_struct && + new_symbol.type.id()==ID_incomplete_struct) + { + // ignore + } + else if(ns.follow(old_symbol.type).id()==ID_pointer && + ns.follow(new_symbol.type).id()==ID_incomplete_array) + { + // ignore + } + else + { + err_location(new_symbol.location); + str << "error: conflicting definition for variable `" + << old_symbol.name + << "'" << std::endl; + str << "old definition: " << to_string(old_symbol.type) + << std::endl; + str << "Module: " << old_symbol.module << std::endl; + str << "new definition: " << to_string(new_symbol.type) + << std::endl; + str << "Module: " << new_symbol.module; + throw 0; + } + } + + // care about initializers + + if(!new_symbol.value.is_nil() && + !new_symbol.value.get_bool(ID_C_zero_initializer)) + { + if(old_symbol.value.is_nil() || + old_symbol.value.get_bool(ID_C_zero_initializer)) + { + // new_symbol wins + old_symbol.value=new_symbol.value; + } + else if(!base_type_eq(old_symbol.value, new_symbol.value, ns)) + { + err_location(new_symbol.value); + str << "error: conflicting initializers for variable `" + << old_symbol.name + << "'" << std::endl; + str << "old value: " << to_string(old_symbol.value) + << std::endl; + str << "Module: " << old_symbol.module << std::endl; + str << "new value: " << to_string(new_symbol.value) + << std::endl; + str << "Module: " << new_symbol.module; + throw 0; + } + } + } +} + +/*******************************************************************\ + +Function: c_linkt::typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_linkt::typecheck() +{ + // we first take care of file-local non-type symbols + forall_symbols(it, src_context.symbols) + if(it->second.file_local && !it->second.is_type && + main_context.symbols.find(it->first)!=main_context.symbols.end()) + { + // collision! + irep_idt new_identifier=rename(it->first); + replace_symbol.insert( + it->first, symbol_exprt(new_identifier, it->second.type)); + } + + // we inspect all the symbols in src_context + + forall_symbols(it, src_context.symbols) + inspect_symbol(it->first); +} + +/*******************************************************************\ + +Function: c_linkt::inspect_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_linkt::inspect_symbol(const irep_idt &identifier) +{ + // are we doing it already? + if(!processing.insert(identifier).second) + return; + + // look it up, it must be there + symbolt &new_symbol=src_context.lookup(identifier); + + // first find out what symbols this uses + find_symbols_sett symbols; + find_type_and_expr_symbols(new_symbol.type, symbols); + + // make sure we inspect those first! + for(find_symbols_sett::const_iterator + s_it=symbols.begin(); + s_it!=symbols.end(); + s_it++) + inspect_symbol(*s_it); + + // first order of business is to apply renaming + replace_symbol.replace(new_symbol.value); + replace_symbol.replace(new_symbol.type); + + // ok, now check if we are to expect a collision + const contextt::symbolst::iterator main_s_it= + main_context.symbols.find(identifier); + + if(main_s_it!=main_context.symbols.end()) + duplicate(main_s_it->second, new_symbol); // handle the collision + else + { + // add into dest context -- should never fail, + // as there is no collision + + bool result=main_context.move(new_symbol); + assert(!result); + } +} + +/*******************************************************************\ + +Function: convert_c + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_link( + contextt &dest_context, + contextt &new_context, + message_handlert &message_handler) +{ + c_linkt c_link( + dest_context, new_context, message_handler); + + return c_link.typecheck_main(); +} diff --git a/src/ansi-c/c_link.h b/src/ansi-c/c_link.h new file mode 100644 index 00000000000..128791674cf --- /dev/null +++ b/src/ansi-c/c_link.h @@ -0,0 +1,24 @@ +/*******************************************************************\ + +Module: ANSI-C Linking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_C_LINK_H +#define CPROVER_C_LINK_H + +#include +#include + +// this merges the context "new_context" into "dest_context", +// applying appropriate renamings to symbols in "new_context" +// when necessary + +bool c_link( + contextt &dest_context, + contextt &new_context, + message_handlert &message_handler); + +#endif diff --git a/src/ansi-c/c_link_class.h b/src/ansi-c/c_link_class.h new file mode 100644 index 00000000000..bffd9e8f471 --- /dev/null +++ b/src/ansi-c/c_link_class.h @@ -0,0 +1,67 @@ +/*******************************************************************\ + +Module: ANSI-C Linking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_C_LINK_CLASS_H +#define CPROVER_ANSI_C_C_LINK_CLASS_H + +#include +#include +#include +#include + +class c_linkt:public typecheckt +{ +public: + c_linkt( + contextt &_main_context, + contextt &_src_context, + message_handlert &_message_handler): + typecheckt(_message_handler), + main_context(_main_context), + src_context(_src_context), + ns(_main_context, _src_context), // order matters + renaming_counter(0) + { + } + + virtual void typecheck(); + + replace_symbolt replace_symbol; + +protected: + void duplicate( + symbolt &old_symbol, + symbolt &new_symbol); + + void duplicate_type( + symbolt &old_symbol, + symbolt &new_symbol); + + void duplicate_non_type( + symbolt &old_symbol, + symbolt &new_symbol); + + void inspect_symbol(const irep_idt &identifier); + + irep_idt rename(const irep_idt &old_identifier); + + // overload to use language specific syntax + virtual std::string to_string(const exprt &expr); + virtual std::string to_string(const typet &type); + + contextt &main_context; + contextt &src_context; + namespacet ns; + + unsigned renaming_counter; + + typedef hash_set_cont processingt; + processingt processing; +}; + +#endif diff --git a/src/ansi-c/c_link_type_eq.cpp b/src/ansi-c/c_link_type_eq.cpp new file mode 100644 index 00000000000..a17020c15d3 --- /dev/null +++ b/src/ansi-c/c_link_type_eq.cpp @@ -0,0 +1,78 @@ +/*******************************************************************\ + +Module: Link Type Comparison + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +/*******************************************************************\ + + Class: c_link_type_eqt + + Purpose: + +\*******************************************************************/ + +class c_link_type_eqt:public base_type_eqt +{ +public: + c_link_type_eqt(const namespacet &_ns):base_type_eqt(_ns) + { + } + +protected: + bool base_type_eq_rec(const typet &type1, const typet &type2); +}; + +/*******************************************************************\ + +Function: c_link_type_eqt::c_link_type_eq_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_link_type_eqt::base_type_eq_rec( + const typet &type1, + const typet &type2) +{ + // we are more generous than base_type_eq + + if(type1.id()==ID_struct && + type2.id()==ID_incomplete_struct) + return true; + + if(type1.id()==ID_incomplete_struct && + type2.id()==ID_struct) + return true; + + return base_type_eqt::base_type_eq_rec(type1, type2); +} + +/*******************************************************************\ + +Function: c_link_type_eq + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_link_type_eq( + const typet &type1, + const typet &type2, + const namespacet &ns) +{ + c_link_type_eqt c_link_type_eq(ns); + return c_link_type_eq.base_type_eq(type1, type2); +} diff --git a/src/ansi-c/c_link_type_eq.h b/src/ansi-c/c_link_type_eq.h new file mode 100644 index 00000000000..c6aee7667fc --- /dev/null +++ b/src/ansi-c/c_link_type_eq.h @@ -0,0 +1,17 @@ +/*******************************************************************\ + +Module: ANSI-C Linking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_C_LINK_TYPE_EQ_H +#define CPROVER_C_LINK_TYPE_EQ_H + +bool c_link_type_eq( + const typet &type1, + const typet &type2, + const namespacet &ns); + +#endif diff --git a/src/ansi-c/c_main.cpp b/src/ansi-c/c_main.cpp new file mode 100644 index 00000000000..3d9a825d562 --- /dev/null +++ b/src/ansi-c/c_main.cpp @@ -0,0 +1,434 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c_types.h" +#include "c_main.h" + +/*******************************************************************\ + +Function: build_function_environment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt::operandst build_function_environment( + const code_typet::argumentst &arguments) +{ + exprt::operandst result; + result.resize(arguments.size()); + + for(unsigned i=0; isecond; + + init_symbol.value=code_blockt(); + init_symbol.value.location()=location; + + code_blockt &dest=to_code_block(to_code(init_symbol.value)); + + // do assignments based on "value" + + forall_symbols(it, context.symbols) + { + const irep_idt &identifier=it->first; + + if(it->second.static_lifetime) + { + const exprt &value=it->second.value; + + // special values + if(identifier==CPROVER_PREFIX "constant_infinity_uint" || + identifier=="c::__func__" || + identifier=="c::__FUNCTION__" || + identifier=="c::__PRETTY_FUNCTION__") + continue; + + // just for linking + if(has_prefix(id2string(identifier), CPROVER_PREFIX "architecture_")) + continue; + + if(value.is_not_nil()) + { + assert(value.type().id()!=ID_code); + + exprt symbol(ID_symbol, it->second.type); + symbol.set(ID_identifier, it->second.name); + + code_assignt code(symbol, it->second.value); + code.location()=it->second.location; + + dest.move_to_operands(code); + } + } + } + + // call designated "initialization" functions + + forall_symbols(it, context.symbols) + { + if(it->second.type.get_bool("initialization") && + it->second.type.id()==ID_code) + { + code_function_callt function_call; + function_call.function()=symbol_expr(it->second); + function_call.location()=location; + dest.move_to_operands(function_call); + } + } +} + +/*******************************************************************\ + +Function: c_main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_main( + contextt &context, + const std::string &default_prefix, + const std::string &standard_main, + message_handlert &message_handler) +{ + // check if main is already there + if(context.symbols.find(ID_main)!=context.symbols.end()) + return false; // silently ignore + + irep_idt main_symbol; + + // find main symbol + if(config.main!="") + { + std::list matches; + + forall_symbol_base_map(it, context.symbol_base_map, config.main) + { + // look it up + contextt::symbolst::const_iterator s_it=context.symbols.find(it->second); + + if(s_it==context.symbols.end()) continue; + + if(s_it->second.type.id()==ID_code) + matches.push_back(it->second); + } + + if(matches.empty()) + { + messaget message(message_handler); + message.error("main symbol `"+config.main+"' not found"); + return true; // give up + } + + if(matches.size()>=2) + { + messaget message(message_handler); + message.error("main symbol `"+config.main+"' is ambiguous"); + return true; + } + + main_symbol=matches.front(); + } + else + main_symbol=standard_main; + + // look it up + contextt::symbolst::const_iterator s_it=context.symbols.find(main_symbol); + + if(s_it==context.symbols.end()) + return false; // give up, no main + + const symbolt &symbol=s_it->second; + + // check if it has a body + if(symbol.value.is_nil()) + return false; // give up + + static_lifetime_init(context, symbol.location); + + code_blockt init_code; + + // build call to initialization function + + { + contextt::symbolst::iterator init_it= + context.symbols.find("c::__CPROVER_initialize"); + + assert(init_it!=context.symbols.end()); + + code_function_callt call_init; + call_init.lhs().make_nil(); + call_init.location()=symbol.location; + call_init.function()=symbol_expr(init_it->second); + + init_code.move_to_operands(call_init); + } + + // build call to main function + + code_function_callt call_main; + call_main.location()=symbol.location; + call_main.function()=symbol_expr(symbol); + + const code_typet::argumentst &arguments= + to_code_type(symbol.type).arguments(); + + if(symbol.name==standard_main) + { + if(arguments.size()==0) + { + // ok + } + else if(arguments.size()==2 || arguments.size()==3) + { + namespacet ns(context); + + const symbolt &argc_symbol=ns.lookup("c::argc'"); + const symbolt &argv_symbol=ns.lookup("c::argv'"); + + { + // assume argc is at least one + exprt one=from_integer(1, argc_symbol.type); + + exprt ge(ID_ge, typet(ID_bool)); + ge.copy_to_operands(symbol_expr(argc_symbol), one); + + codet assumption; + assumption.set_statement(ID_assume); + assumption.move_to_operands(ge); + init_code.move_to_operands(assumption); + } + + { + // assume argc is at most MAX/8-1 + mp_integer upper_bound= + power(2, config.ansi_c.int_width-4); + + exprt bound_expr=from_integer(upper_bound, argc_symbol.type); + + exprt le(ID_le, typet(ID_bool)); + le.copy_to_operands(symbol_expr(argc_symbol), bound_expr); + + codet assumption; + assumption.set_statement(ID_assume); + assumption.move_to_operands(le); + init_code.move_to_operands(assumption); + } + + if(arguments.size()==3) + { + const symbolt &envp_size_symbol=ns.lookup("c::envp_size'"); + // assume envp_size is at most MAX-1 + mp_integer max; + + if(envp_size_symbol.type.id()==ID_signedbv) + max=power(2, atoi(envp_size_symbol.type.get(ID_width).c_str())-1)-1; + else if(envp_size_symbol.type.id()==ID_unsignedbv) + max=power(2, atoi(envp_size_symbol.type.get(ID_width).c_str()))-1; + else + assert(false); + + exprt max_minus_one=from_integer(max-1, envp_size_symbol.type); + + exprt le(ID_le, typet(ID_bool)); + le.copy_to_operands(symbol_expr(envp_size_symbol), max_minus_one); + + codet assumption; + assumption.set_statement(ID_assume); + assumption.move_to_operands(le); + init_code.move_to_operands(assumption); + } + + { + /* zero_string doesn't work yet */ + + /* + exprt zero_string(ID_zero_string, array_typet()); + zero_string.type().subtype()=char_type(); + zero_string.type().set(ID_size, "infinity"); + exprt index(ID_index, char_type()); + index.copy_to_operands(zero_string, gen_zero(uint_type())); + exprt address_of("address_of", pointer_typet()); + address_of.type().subtype()=char_type(); + address_of.copy_to_operands(index); + + if(argv_symbol.type.subtype()!=address_of.type()) + address_of.make_typecast(argv_symbol.type.subtype()); + + // assign argv[*] to the address of a string-object + exprt array_of("array_of", argv_symbol.type); + array_of.copy_to_operands(address_of); + + init_code.copy_to_operands( + code_assignt(symbol_expr(argv_symbol), array_of)); + */ + } + + { + // assign argv[argc] to NULL + exprt null(ID_constant, argv_symbol.type.subtype()); + null.set(ID_value, ID_NULL); + + exprt index_expr(ID_index, argv_symbol.type.subtype()); + index_expr.copy_to_operands( + symbol_expr(argv_symbol), + symbol_expr(argc_symbol)); + + // disable bounds check on that one + index_expr.set("bounds_check", false); + + init_code.copy_to_operands(code_assignt(index_expr, null)); + } + + if(arguments.size()==3) + { + const symbolt &envp_symbol=ns.lookup("c::envp'"); + const symbolt &envp_size_symbol=ns.lookup("c::envp_size'"); + + // assume envp[envp_size] is NULL + exprt null(ID_constant, envp_symbol.type.subtype()); + null.set(ID_value, ID_NULL); + + exprt index_expr(ID_index, envp_symbol.type.subtype()); + index_expr.copy_to_operands( + symbol_expr(envp_symbol), + symbol_expr(envp_size_symbol)); + + // disable bounds check on that one + index_expr.set("bounds_check", false); + + exprt is_null(ID_equal, typet(ID_bool)); + is_null.copy_to_operands(index_expr, null); + + codet assumption2; + assumption2.set_statement(ID_assume); + assumption2.move_to_operands(is_null); + init_code.move_to_operands(assumption2); + } + + { + exprt::operandst &operands=call_main.arguments(); + + if(arguments.size()==3) + operands.resize(3); + else + operands.resize(2); + + exprt &op0=operands[0]; + exprt &op1=operands[1]; + + op0=symbol_expr(argc_symbol); + + { + const exprt &arg1=arguments[1]; + + exprt index_expr(ID_index, arg1.type().subtype()); + index_expr.copy_to_operands(symbol_expr(argv_symbol), gen_zero(index_type())); + + // disable bounds check on that one + index_expr.set("bounds_check", false); + + op1=exprt(ID_address_of, arg1.type()); + op1.move_to_operands(index_expr); + } + + // do we need envp? + if(arguments.size()==3) + { + const symbolt &envp_symbol=ns.lookup("c::envp'"); + exprt &op2=operands[2]; + + const exprt &arg2=arguments[2]; + + exprt index_expr(ID_index, arg2.type().subtype()); + index_expr.copy_to_operands( + symbol_expr(envp_symbol), gen_zero(index_type())); + + op2=exprt(ID_address_of, arg2.type()); + op2.move_to_operands(index_expr); + } + } + } + else + assert(false); + } + else + { + // produce nondet arguments + call_main.arguments()=build_function_environment(arguments); + } + + init_code.move_to_operands(call_main); + + // add "main" + symbolt new_symbol; + + code_typet main_type; + main_type.return_type()=empty_typet(); + + new_symbol.name=ID_main; + new_symbol.type.swap(main_type); + new_symbol.value.swap(init_code); + + if(context.move(new_symbol)) + { + messaget message; + message.set_message_handler(message_handler); + message.error("failed to move main symbol"); + return true; + } + + return false; +} diff --git a/src/ansi-c/c_main.h b/src/ansi-c/c_main.h new file mode 100644 index 00000000000..9cabd01a1a8 --- /dev/null +++ b/src/ansi-c/c_main.h @@ -0,0 +1,26 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_C_MAIN_H +#define CPROVER_C_MAIN_H + +#include +#include +#include + +bool c_main( + contextt &context, + const std::string &default_prefix, + const std::string &standard_main, + message_handlert &message_handler); + +void static_lifetime_init( + contextt &context, + const locationt &location); + +#endif diff --git a/src/ansi-c/c_preprocess.cpp b/src/ansi-c/c_preprocess.cpp new file mode 100644 index 00000000000..c89189895ee --- /dev/null +++ b/src/ansi-c/c_preprocess.cpp @@ -0,0 +1,812 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#ifdef __LINUX__ +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#include + +#include +#include +#include +#include +#include + +#include "c_preprocess.h" + +#define GCC_DEFINES_16 \ + " -D__INT_MAX__=32767"\ + " -D__CHAR_BIT__=8"\ + " -D__WCHAR_MAX__=32767"\ + " -D__SCHAR_MAX__=127"\ + " -D__SHRT_MAX__=32767"\ + " -D__LONG_LONG_MAX__=2147483647L"\ + " -D__LONG_MAX__=2147483647" \ + " -D__SIZE_TYPE__=\"unsigned int\""\ + " -D__PTRDIFF_TYPE__=int"\ + " -D__WCHAR_TYPE__=int"\ + " -D__WINT_TYPE__=int"\ + " -D__INTMAX_TYPE__=\"long long int\""\ + " -D__UINTMAX_TYPE__=\"long long unsigned int\"" + +#define GCC_DEFINES_32 \ + " -D__INT_MAX__=2147483647"\ + " -D__CHAR_BIT__=8"\ + " -D__WCHAR_MAX__=2147483647"\ + " -D__SCHAR_MAX__=127"\ + " -D__SHRT_MAX__=32767"\ + " -D__LONG_LONG_MAX__=9223372036854775807LL"\ + " -D__LONG_MAX__=2147483647L" \ + " -D__SIZE_TYPE__=\"long unsigned int\""\ + " -D__PTRDIFF_TYPE__=int"\ + " -D__WCHAR_TYPE__=int"\ + " -D__WINT_TYPE__=int"\ + " -D__INTMAX_TYPE__=\"long long int\""\ + " -D__UINTMAX_TYPE__=\"long long unsigned int\"" + +#define GCC_DEFINES_LP64 \ + " -D__INT_MAX__=2147483647"\ + " -D__CHAR_BIT__=8"\ + " -D__WCHAR_MAX__=2147483647"\ + " -D__SCHAR_MAX__=127"\ + " -D__SHRT_MAX__=32767"\ + " -D__LONG_LONG_MAX__=9223372036854775807LL"\ + " -D__LONG_MAX__=9223372036854775807L"\ + " -D__SIZE_TYPE__=\"long unsigned int\""\ + " -D__PTRDIFF_TYPE__=int"\ + " -D__WCHAR_TYPE__=int"\ + " -D__WINT_TYPE__=int"\ + " -D__INTMAX_TYPE__=\"long long int\""\ + " -D__UINTMAX_TYPE__=\"long long unsigned int\"" + +/*******************************************************************\ + +Function: c_preprocess + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool c_preprocess( + std::istream &instream, + std::ostream &outstream, + message_handlert &message_handler) +{ + std::string file=get_temporary_file("tmp.stdin", ".c"); + FILE *tmp=fopen(file.c_str(), "wt"); + + char ch; + while(instream.read(&ch, 1)!=NULL) + fputc(ch, tmp); + + fclose(tmp); + + bool result=c_preprocess(file, outstream, message_handler); + + unlink(file.c_str()); + + return result; +} + +/*******************************************************************\ + +Function: is_dot_i_file + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +static bool is_dot_i_file(const std::string &path) +{ + const char *ext=strrchr(path.c_str(), '.'); + return ext!=NULL && std::string(ext)==".i"; +} + +/*******************************************************************\ + +Function: c_preprocess + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool c_preprocess_codewarrior(const std::string &, std::ostream &, message_handlert &); +bool c_preprocess_arm(const std::string &, std::ostream &, message_handlert &); +bool c_preprocess_gcc(const std::string &, std::ostream &, message_handlert &); +bool c_preprocess_none(const std::string &, std::ostream &, message_handlert &); +bool c_preprocess_visual_studio(const std::string &, std::ostream &, message_handlert &); + +bool c_preprocess( + const std::string &path, + std::ostream &outstream, + message_handlert &message_handler) +{ + switch(config.ansi_c.mode) + { + case configt::ansi_ct::MODE_CODEWARRIOR: + return c_preprocess_codewarrior(path, outstream, message_handler); + + case configt::ansi_ct::MODE_GCC: + return c_preprocess_gcc(path, outstream, message_handler); + + case configt::ansi_ct::MODE_VISUAL_STUDIO: + return c_preprocess_visual_studio(path, outstream, message_handler); + + case configt::ansi_ct::MODE_ARM: + return c_preprocess_arm(path, outstream, message_handler); + + default: + assert(false); + } + + // not reached + return true; +} + +/*******************************************************************\ + +Function: c_preprocess_visual_studio + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool c_preprocess_visual_studio( + const std::string &file, + std::ostream &outstream, + message_handlert &message_handler) +{ + // check extension + if(is_dot_i_file(file)) + return c_preprocess_none(file, outstream, message_handler); + + #ifndef _WIN32 + // we fall back to gcc + return c_preprocess_gcc(file, outstream, message_handler); + #endif + + message_streamt message_stream(message_handler); + + // use VC98 CL + + std::string stderr_file=get_temporary_file("tmp.stderr", ""); + std::string command_file_name=get_temporary_file("tmp.cl-cmd", ""); + + { + std::ofstream command_file(command_file_name.c_str()); + + command_file << "/nologo" << std::endl; + command_file << "/E" << std::endl; + command_file << "/D__CPROVER__" << std::endl; + command_file << "/D__WORDSIZE="+i2string(config.ansi_c.pointer_width) << std::endl; + + if(config.ansi_c.pointer_width==64) + { + command_file << "/D__PTRDIFF_TYPE__=long long int" << std::endl; + // yes, both _WIN32 and _WIN64 get defined + command_file << "/D_WIN64" << std::endl; + } + else + command_file << "/D__PTRDIFF_TYPE__=int" << std::endl; + + // Standard Defines, ANSI9899 6.10.8 + command_file << "/D__STDC_VERSION__=199901L" << std::endl; + command_file << "/D__STDC_IEC_559__=1" << std::endl; + command_file << "/D__STDC_IEC_559_COMPLEX__=1" << std::endl; + command_file << "/D__STDC_ISO_10646__=1" << std::endl; + + for(std::list::const_iterator + it=config.ansi_c.defines.begin(); + it!=config.ansi_c.defines.end(); + it++) + command_file << "/D" << *it << std::endl; + + for(std::list::const_iterator + it=config.ansi_c.include_paths.begin(); + it!=config.ansi_c.include_paths.end(); + it++) + command_file << "/I" << *it << std::endl; + + command_file << file << std::endl; + } + + std::string tmpi=get_temporary_file("tmp.cl", ""); + + std::string command="CL @"+command_file_name; + command+=" > \""+tmpi+"\""; + command+=" 2> \""+stderr_file+"\""; + + // _popen isn't very reliable on WIN32 + // that's why we use system() + int result=system(command.c_str()); + + FILE *stream=fopen(tmpi.c_str(), "r"); + + if(stream==NULL) + { + unlink(tmpi.c_str()); + unlink(stderr_file.c_str()); + message_stream.error("Preprocessing failed (fopen failed)"); + return true; + } + + { + char ch; + while((ch=fgetc(stream))!=EOF) + outstream << ch; + } + + fclose(stream); + unlink(tmpi.c_str()); + unlink(command_file_name.c_str()); + + // errors/warnings + { + std::ifstream stderr_stream(stderr_file.c_str()); + char ch; + while((stderr_stream.read(&ch, 1))!=NULL) + message_stream.str << ch; + } + + unlink(stderr_file.c_str()); + + if(result!=0) + { + message_stream.error_parse(1); + message_stream.error("Preprocessing failed"); + return true; + } + else + message_stream.error_parse(2); + + return false; +} + +/*******************************************************************\ + +Function: c_preprocess_codewarrior + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool c_preprocess_codewarrior( + const std::string &file, + std::ostream &outstream, + message_handlert &message_handler) +{ + message_streamt message_stream(message_handler); + + // CodeWarrior prepends some header to the file, + // marked with '#' signs. + // We skip over it. + // + // CodeWarrior has an ugly way of marking lines, e.g.: + // + // /* #line 1 "__ppc_eabi_init.cpp" /* stack depth 0 */ + // + // We remove the initial '/* ' prefix + + std::ifstream in(file.c_str()); + + std::string line; + + while(in) + { + str_getline(in, line); + + if(line.size()>=1 && + line[0]=='#' && (line[1]=='#' || line[1]==' ' || line[1]=='\t')) + { + // skip the line! + } + else if(line.size()>=3 && + line[0]=='/' && line[1]=='*' && line[2]==' ') + { + outstream << line.c_str()+3 << std::endl; // strip the '/* ' + } + else + outstream << line << std::endl; + } + + return false; +} + +/*******************************************************************\ + +Function: c_preprocess_gcc + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool c_preprocess_gcc( + const std::string &file, + std::ostream &outstream, + message_handlert &message_handler) +{ + // check extension + if(is_dot_i_file(file)) + return c_preprocess_none(file, outstream, message_handler); + + // preprocessing + message_streamt message_stream(message_handler); + + std::string stderr_file=get_temporary_file("tmp.stderr", ""); + + std::string command; + + command="gcc -E -undef -D__CPROVER__"; + + if(config.ansi_c.os!=configt::ansi_ct::OS_WIN) + { + command+=" -D__null=0"; + command+=" -D__WORDSIZE="+i2string(config.ansi_c.pointer_width); + + // Tell the system library which standards we support. + // these are not gcc-default! + //command+=" -D_POSIX_SOURCE=1 -D_POSIX_C_SOURCE=200112L"; + //command+=" -D__STRICT_ANSI__=1"; + + command+=" -D__DBL_MIN_EXP__=\"(-1021)\""; + command+=" -D__FLT_MIN__=1.17549435e-38F"; + command+=" -D__DEC64_DEN__=0.000000000000001E-383DD"; + command+=" -D__CHAR_BIT__=8"; + command+=" -D__WCHAR_MAX__=2147483647"; + command+=" -D__DBL_DENORM_MIN__=4.9406564584124654e-324"; + command+=" -D__FLT_EVAL_METHOD__=0"; + command+=" -D__DBL_MIN_10_EXP__=\"(-307)\""; + command+=" -D__FINITE_MATH_ONLY__=0"; + command+=" -D__DEC64_MAX_EXP__=384"; + command+=" -D__SHRT_MAX__=32767"; + command+=" -D__LDBL_MAX__=1.18973149535723176502e+4932L"; + command+=" -D__DEC32_EPSILON__=1E-6DF"; + command+=" -D__SCHAR_MAX__=127"; + command+=" -D__USER_LABEL_PREFIX__=_"; + command+=" -D__DEC64_MIN_EXP__=\"(-383)\""; + command+=" -D__DBL_DIG__=15"; + command+=" -D__FLT_EPSILON__=1.19209290e-7F"; + command+=" -D__LDBL_MIN__=3.36210314311209350626e-4932L"; + command+=" -D__DEC32_MAX__=9.999999E96DF"; + command+=" -D__DECIMAL_DIG__=21"; + command+=" -D__LDBL_HAS_QUIET_NAN__=1"; + command+=" -D__DYNAMIC__=1"; + command+=" -D__GNUC__=4"; + command+=" -D__FLT_HAS_DENORM__=1"; + command+=" -D__DBL_MAX__=1.7976931348623157e+308"; + command+=" -D__DBL_HAS_INFINITY__=1"; + command+=" -D__DEC32_MIN_EXP__=\"(-95)\""; + command+=" -D__LDBL_HAS_DENORM__=1"; + command+=" -D__DEC32_MIN__=1E-95DF"; + command+=" -D__DBL_MAX_EXP__=1024"; + command+=" -D__DEC128_EPSILON__=1E-33DL"; + command+=" -D__SSE2_MATH__=1"; + command+=" -D__LONG_LONG_MAX__=9223372036854775807LL"; + command+=" -D__GXX_ABI_VERSION=1002"; + command+=" -D__FLT_MIN_EXP__=\"(-125)\""; + command+=" -D__DBL_MIN__=2.2250738585072014e-308"; + command+=" -D__DBL_HAS_QUIET_NAN__=1"; + command+=" -D__DEC128_MIN__=1E-6143DL"; + command+=" -D__REGISTER_PREFIX__="; + command+=" -D__DBL_HAS_DENORM__=1"; + command+=" -D__DEC_EVAL_METHOD__=2"; + command+=" -D__DEC128_MAX__=9.999999999999999999999999999999999E6144DL"; + command+=" -D__FLT_MANT_DIG__=24"; + command+=" -D__DEC64_EPSILON__=1E-15DD"; + command+=" -D__DEC128_MIN_EXP__=\"(-6143)\""; + command+=" -D__DEC32_DEN__=0.000001E-95DF"; + command+=" -D__FLT_RADIX__=2"; + command+=" -D__LDBL_EPSILON__=1.08420217248550443401e-19L"; + command+=" -D__k8=1"; + command+=" -D__LDBL_DIG__=18"; + command+=" -D__FLT_HAS_QUIET_NAN__=1"; + command+=" -D__FLT_MAX_10_EXP__=38"; + command+=" -D__FLT_HAS_INFINITY__=1"; + command+=" -D__DEC64_MAX__=9.999999999999999E384DD"; + command+=" -D__DEC64_MANT_DIG__=16"; + command+=" -D__DEC32_MAX_EXP__=96"; + command+=" -D__DEC128_DEN__=0.000000000000000000000000000000001E-6143DL"; + command+=" -D__LDBL_MANT_DIG__=64"; + command+=" -D__CONSTANT_CFSTRINGS__=1"; + command+=" -D__DEC32_MANT_DIG__=7"; + command+=" -D__k8__=1"; + command+=" -D__pic__=2"; + command+=" -D__FLT_DIG__=6"; + command+=" -D__INT_MAX__=2147483647"; + command+=" -D__FLT_MAX_EXP__=128"; + //command+=" -D__BLOCKS__=1"; + command+=" -D__DBL_MANT_DIG__=53"; + command+=" -D__DEC64_MIN__=1E-383DD"; + command+=" -D__LDBL_MIN_EXP__=\"(-16381)\""; + command+=" -D__LDBL_MAX_EXP__=16384"; + command+=" -D__LDBL_MAX_10_EXP__=4932"; + command+=" -D__DBL_EPSILON__=2.2204460492503131e-16"; + command+=" -D__GNUC_PATCHLEVEL__=1"; + command+=" -D__LDBL_HAS_INFINITY__=1"; + command+=" -D__INTMAX_MAX__=9223372036854775807L"; + command+=" -D__FLT_DENORM_MIN__=1.40129846e-45F"; + command+=" -D__PIC__=2"; + command+=" -D__FLT_MAX__=3.40282347e+38F"; + command+=" -D__FLT_MIN_10_EXP__=\"(-37)\""; + command+=" -D__DEC128_MAX_EXP__=6144"; + command+=" -D__GNUC_MINOR__=2"; + command+=" -D__DBL_MAX_10_EXP__=308"; + command+=" -D__LDBL_DENORM_MIN__=3.64519953188247460253e-4951L"; + command+=" -D__DEC128_MANT_DIG__=34"; + command+=" -D__LDBL_MIN_10_EXP__=\"(-4931)\""; + + if(config.ansi_c.int_width==16) + command+=GCC_DEFINES_16; + else if(config.ansi_c.int_width==32) + command+=GCC_DEFINES_32; + else if(config.ansi_c.int_width==64) + command+=GCC_DEFINES_LP64; + } + + switch(config.ansi_c.os) + { + case configt::ansi_ct::OS_LINUX: + if(config.ansi_c.arch==configt::ansi_ct::ARCH_I386) + command+=" -Di386 -D__i386 -D__i386__"; + else if(config.ansi_c.arch==configt::ansi_ct::ARCH_X86_64) + command+=" -D__LP64__ -D__x86_64 -D__x86_64__ -D_LP64"; + command+=" -Dlinux -D__linux -D__linux__ -D__gnu_linux__"; + command+=" -Dunix -D__unix -D__unix__"; + command+=" -D__USE_UNIX98"; + break; + + case configt::ansi_ct::OS_MACOS: + if(config.ansi_c.arch==configt::ansi_ct::ARCH_I386) + command+=" -Di386 -D__i386 -D__i386__ -D__LITTLE_ENDIAN__"; + else if(config.ansi_c.arch==configt::ansi_ct::ARCH_PPC) + command+=" -D__BIG_ENDIAN__"; + command+=" -D__APPLE__ -D__MACH__"; + // needs to be __APPLE_CPP__ for C++ + command+=" -D__APPLE_CC__"; + break; + + case configt::ansi_ct::OS_WIN: + command+=" -D _MSC_VER=1400"; + command+=" -D _WIN32"; + command+=" -D _M_IX86=Blend"; + + if(config.ansi_c.arch==configt::ansi_ct::ARCH_X86_64) + command+=" -D _WIN64"; // yes, both _WIN32 and _WIN64 get defined + + if(config.ansi_c.char_is_unsigned) + command+=" -D _CHAR_UNSIGNED"; + break; + + case configt::ansi_ct::NO_OS: + command+=" -nostdinc"; // make sure we don't mess with the system library + break; + + default: + assert(false); + } + + // Standard Defines, ANSI9899 6.10.8 + command += " -D __STDC_VERSION__=199901L"; + command += " -D __STDC_IEC_559__=1"; + command += " -D __STDC_IEC_559_COMPLEX__=1"; + command += " -D __STDC_ISO_10646__=1"; + + for(std::list::const_iterator + it=config.ansi_c.defines.begin(); + it!=config.ansi_c.defines.end(); + it++) + command+=" -D'"+*it+"'"; + + for(std::list::const_iterator + it=config.ansi_c.include_paths.begin(); + it!=config.ansi_c.include_paths.end(); + it++) + command+=" -I'"+*it+"'"; + + for(std::list::const_iterator + it=config.ansi_c.include_files.begin(); + it!=config.ansi_c.include_files.end(); + it++) + command+=" -include '"+*it+"'"; + + for(std::list::const_iterator + it=config.ansi_c.preprocessor_options.begin(); + it!=config.ansi_c.preprocessor_options.end(); + it++) + command+=" "+*it; + + int result; + + #ifdef _WIN32 + std::string tmpi=get_temporary_file("tmp.cl", ""); + command+=" \""+file+"\""; + command+=" > \""+tmpi+"\""; + command+=" 2> \""+stderr_file+"\""; + + // _popen isn't very reliable on WIN32 + // that's why we use system() and a temporary file + result=system(command.c_str()); + + FILE *stream=fopen(tmpi.c_str(), "r"); + + if(stream!=NULL) + { + char ch; + while((ch=fgetc(stream))!=EOF) + outstream << ch; + + fclose(stream); + unlink(tmpi.c_str()); + } + else + { + unlink(tmpi.c_str()); + unlink(stderr_file.c_str()); + message_stream.error("Preprocessing failed (fopen failed)"); + return true; + } + #else + command+=" \""+file+"\""; + command+=" 2> \""+stderr_file+"\""; + + FILE *stream=popen(command.c_str(), "r"); + + if(stream!=NULL) + { + char ch; + while((ch=fgetc(stream))!=EOF) + outstream << ch; + + result=pclose(stream); + } + else + { + unlink(stderr_file.c_str()); + message_stream.error("Preprocessing failed (popen failed)"); + return true; + } + #endif + + // errors/warnings + { + std::ifstream stderr_stream(stderr_file.c_str()); + char ch; + while((stderr_stream.read(&ch, 1))!=NULL) + message_stream.str << ch; + } + + unlink(stderr_file.c_str()); + + if(result!=0) + { + message_stream.error_parse(1); + message_stream.error("Preprocessing failed"); + return true; + } + else + message_stream.error_parse(2); + + return false; +} + +/*******************************************************************\ + +Function: c_preprocess_arm + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool c_preprocess_arm( + const std::string &file, + std::ostream &outstream, + message_handlert &message_handler) +{ + // check extension + if(is_dot_i_file(file)) + return c_preprocess_none(file, outstream, message_handler); + + // preprocessing using armcc + message_streamt message_stream(message_handler); + + std::string stderr_file=get_temporary_file("tmp.stderr", ""); + + std::string command; + + command="armcc -E -D__CPROVER__"; + +// command+=" -D__sizeof_int="+i2string(config.ansi_c.int_width/8); +// command+=" -D__sizeof_long="+i2string(config.ansi_c.long_int_width/8); +// command+=" -D__sizeof_ptr="+i2string(config.ansi_c.pointer_width/8); + //command+=" -D__EDG_VERSION__=308"; + //command+=" -D__EDG__"; +// command+=" -D__CC_ARM=1"; + //command+=" -D__ARMCC_VERSION=410000"; +// command+=" -D__arm__"; + +// if(config.ansi_c.endianess==configt::ansi_ct::IS_BIG_ENDIAN) +// command+=" -D__BIG_ENDIAN"; + +// if(config.ansi_c.char_is_unsigned) +// command+=" -D__CHAR_UNSIGNED__"; + + if(config.ansi_c.os!=configt::ansi_ct::OS_WIN) + { + command+=" -D__null=0"; + command+=" -D__WORDSIZE="+i2string(config.ansi_c.pointer_width); + + if(config.ansi_c.int_width==16) + command+=GCC_DEFINES_16; + else if(config.ansi_c.int_width==32) + command+=GCC_DEFINES_32; + else if(config.ansi_c.int_width==64) + command+=GCC_DEFINES_LP64; + } + + // Standard Defines, ANSI9899 6.10.8 + command+=" -D__STDC__"; + //command+=" -D__STDC_VERSION__=199901L"; + + for(std::list::const_iterator + it=config.ansi_c.defines.begin(); + it!=config.ansi_c.defines.end(); + it++) + command+=" \"-D"+*it+"\""; + + for(std::list::const_iterator + it=config.ansi_c.include_paths.begin(); + it!=config.ansi_c.include_paths.end(); + it++) + command+=" \"-I"+*it+"\""; + + int result; + + #ifdef _WIN32 + std::string tmpi=get_temporary_file("tmp.cl", ""); + command+=" \""+file+"\""; + command+=" > \""+tmpi+"\""; + command+=" 2> \""+stderr_file+"\""; + + //std::cout << "C: "<< command << std::endl; + + // _popen isn't very reliable on WIN32 + // that's why we use system() and a temporary file + result=system(command.c_str()); + + FILE *stream=fopen(tmpi.c_str(), "r"); + + if(stream!=NULL) + { + char ch; + while((ch=fgetc(stream))!=EOF) + outstream << ch; + + fclose(stream); + unlink(tmpi.c_str()); + } + else + { + unlink(tmpi.c_str()); + unlink(stderr_file.c_str()); + message_stream.error("Preprocessing failed (fopen failed)"); + return true; + } + #else + command+=" \""+file+"\""; + command+=" 2> \""+stderr_file+"\""; + + FILE *stream=popen(command.c_str(), "r"); + + if(stream!=NULL) + { + char ch; + while((ch=fgetc(stream))!=EOF) + outstream << ch; + + result=pclose(stream); + } + else + { + unlink(stderr_file.c_str()); + message_stream.error("Preprocessing failed (popen failed)"); + return true; + } + #endif + + // errors/warnings + { + std::ifstream stderr_stream(stderr_file.c_str()); + char ch; + while((stderr_stream.read(&ch, 1))!=NULL) + message_stream.str << ch; + } + + unlink(stderr_file.c_str()); + + if(result!=0) + { + message_stream.error_parse(1); + message_stream.error("Preprocessing failed"); + return true; + } + else + message_stream.error_parse(2); + + return false; +} + +/*******************************************************************\ + +Function: c_preprocess_none + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool c_preprocess_none( + const std::string &file, + std::ostream &outstream, + message_handlert &message_handler) +{ + std::ifstream infile(file.c_str()); + + if(!infile) + { + message_streamt message_stream(message_handler); + message_stream.error("failed to open `"+file+"'"); + return true; + } + + char ch; + + while(infile.read(&ch, 1)) + outstream << ch; + + return false; +} diff --git a/src/ansi-c/c_preprocess.h b/src/ansi-c/c_preprocess.h new file mode 100644 index 00000000000..4abbb27bb45 --- /dev/null +++ b/src/ansi-c/c_preprocess.h @@ -0,0 +1,26 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_C_PREPROCESS_H +#define CPROVER_C_PREPROCESS_H + +#include +#include +#include + +bool c_preprocess( + const std::string &path, + std::ostream &outstream, + message_handlert &message_handler); + +bool c_preprocess( + std::istream &instream, + std::ostream &outstream, + message_handlert &message_handler); + +#endif diff --git a/src/ansi-c/c_qualifiers.cpp b/src/ansi-c/c_qualifiers.cpp new file mode 100644 index 00000000000..65c9e277247 --- /dev/null +++ b/src/ansi-c/c_qualifiers.cpp @@ -0,0 +1,154 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "c_qualifiers.h" + +/*******************************************************************\ + +Function: c_qualifierst::as_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string c_qualifierst::as_string() const +{ + std::string qualifiers; + + if(is_constant) + qualifiers+="const "; + + if(is_volatile) + qualifiers+="volatile "; + + if(is_restricted) + qualifiers+="restricted "; + + if(is_ptr32) + qualifiers+="__ptr32 "; + + if(is_ptr64) + qualifiers+="__ptr64 "; + + return qualifiers; +} + +/*******************************************************************\ + +Function: c_qualifierst::read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_qualifierst::read(const typet &src) +{ + if(src.get_bool(ID_C_constant)) + is_constant=true; + + if(src.get_bool(ID_C_volatile)) + is_volatile=true; + + if(src.get_bool(ID_C_restricted)) + is_restricted=true; + + if(src.get_bool(ID_C_ptr32)) + is_ptr32=true; + + if(src.get_bool(ID_C_ptr64)) + is_ptr64=true; +} + +/*******************************************************************\ + +Function: c_qualifierst::write + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_qualifierst::write(typet &dest) const +{ + if(is_constant) + dest.set(ID_C_constant, true); + else + dest.remove(ID_C_constant); + + if(is_volatile) + dest.set(ID_C_volatile, true); + else + dest.remove(ID_C_volatile); + + if(is_restricted) + dest.set(ID_C_restricted, true); + else + dest.remove(ID_C_restricted); + + if(is_ptr32) + dest.set(ID_C_ptr32, true); + else + dest.remove(ID_C_ptr32); + + if(is_ptr64) + dest.set(ID_C_ptr64, true); + else + dest.remove(ID_C_ptr64); +} + +/*******************************************************************\ + +Function: c_qualifierst::clear + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_qualifierst::clear(typet &dest) +{ + dest.remove(ID_C_constant); + dest.remove(ID_C_volatile); + dest.remove(ID_C_restricted); + dest.remove(ID_C_ptr32); + dest.remove(ID_C_ptr64); +} + +/*******************************************************************\ + +Function: operator << + + Inputs: + + Outputs: + + Purpose: pretty-print the qualifiers + +\*******************************************************************/ + +std::ostream &operator << ( + std::ostream &out, + const c_qualifierst &c_qualifiers) +{ + return out << c_qualifiers.as_string(); +} + diff --git a/src/ansi-c/c_qualifiers.h b/src/ansi-c/c_qualifiers.h new file mode 100644 index 00000000000..483dcbfb7d4 --- /dev/null +++ b/src/ansi-c/c_qualifiers.h @@ -0,0 +1,103 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_C_QUALIFIERS_H +#define CPROVER_ANSI_C_C_QUALIFIERS_H + +#include + +#include + +class c_qualifierst +{ +public: + c_qualifierst() + { + clear(); + } + + explicit c_qualifierst(const typet &src) + { + clear(); + read(src); + } + + void clear() + { + is_constant=false; + is_volatile=false; + is_restricted=false; + is_ptr32=is_ptr64=false; + } + + // standard ones + bool is_constant, is_volatile, is_restricted; + + // MS Visual Studio extension + bool is_ptr32, is_ptr64; + + std::string as_string() const; + void read(const typet &src); + void write(typet &src) const; + + static void clear(typet &dest); + + bool is_subset_of(const c_qualifierst &q) const + { + return (!is_constant || q.is_constant) && + (!is_volatile || q.is_volatile) && + (!is_restricted || q.is_restricted) && + (!is_ptr32 || q.is_ptr32) && + (!is_ptr64 || q.is_ptr64); + } + + friend bool operator == ( + const c_qualifierst &a, + const c_qualifierst &b) + { + return a.is_constant==b.is_constant && + a.is_volatile==b.is_volatile && + a.is_restricted==b.is_restricted && + a.is_ptr32==b.is_ptr32 && + a.is_ptr64==b.is_ptr64; + } + + friend bool operator != ( + const c_qualifierst &a, + const c_qualifierst &b) + { + return !(a==b); + } + + c_qualifierst &operator += ( + const c_qualifierst &b) + { + is_constant|=b.is_constant; + is_volatile|=b.is_volatile; + is_restricted|=b.is_restricted; + is_ptr32|=b.is_ptr32; + is_ptr64|=b.is_ptr64; + return *this; + } + + friend unsigned count(const c_qualifierst &q) + { + return q.is_constant+q.is_volatile+q.is_restricted+ + q.is_ptr32+q.is_ptr64; + } + + bool is_empty() const + { + return !is_constant && !is_volatile && !is_restricted && + !is_ptr32 && !is_ptr64; + } +}; + +std::ostream &operator << (std::ostream &, const c_qualifierst &); + +#endif diff --git a/src/ansi-c/c_sizeof.cpp b/src/ansi-c/c_sizeof.cpp new file mode 100644 index 00000000000..b2879ea8377 --- /dev/null +++ b/src/ansi-c/c_sizeof.cpp @@ -0,0 +1,200 @@ +/*******************************************************************\ + +Module: Conversion of sizeof Expressions + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include + +#include "c_sizeof.h" +#include "c_typecast.h" +#include "c_types.h" + +/*******************************************************************\ + +Function: c_sizeoft::sizeof_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt c_sizeoft::sizeof_rec(const typet &type) +{ + exprt dest; + + if(type.id()==ID_signedbv || + type.id()==ID_unsignedbv || + type.id()==ID_floatbv || + type.id()==ID_fixedbv) + { + unsigned bits=atoi(type.get(ID_width).c_str()); + unsigned bytes=bits/8; + if((bits%8)!=0) bytes++; + dest=from_integer(bytes, size_type()); + } + else if(type.id()==ID_c_enum || + type.id()==ID_incomplete_c_enum) + { + dest=from_integer(config.ansi_c.int_width/8, size_type()); + } + else if(type.id()==ID_pointer) + { + // references fall into this category as well! + + // the following is an MS extension + if(type.get_bool(ID_C_ptr32)) + return from_integer(4, size_type()); + + unsigned bits=config.ansi_c.pointer_width; + unsigned bytes=bits/8; + if((bits%8)!=0) bytes++; + dest=from_integer(bytes, size_type()); + } + else if(type.id()==ID_bool) + { + dest=from_integer(1, size_type()); + } + else if(type.id()==ID_array) + { + const exprt &size_expr= + to_array_type(type).size(); + + exprt tmp_dest=sizeof_rec(type.subtype()); + + if(tmp_dest.is_nil()) + return tmp_dest; + + mp_integer a, b; + + if(!to_integer(tmp_dest, a) && + !to_integer(size_expr, b)) + { + dest=from_integer(a*b, size_type()); + } + else + { + dest.id(ID_mult); + dest.type()=size_type(); + dest.copy_to_operands(size_expr); + dest.move_to_operands(tmp_dest); + c_implicit_typecast(dest.op0(), dest.type(), ns); + c_implicit_typecast(dest.op1(), dest.type(), ns); + } + } + else if(type.id()==ID_incomplete_array) + { + // treated like an empty array + dest=from_integer(0, size_type()); + } + else if(type.id()==ID_struct) + { + const struct_typet::componentst &components= + to_struct_type(type).components(); + + dest=from_integer(0, size_type()); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it->get_bool(ID_is_type)) + continue; + + const typet &sub_type=it->type(); + + if(sub_type.id()==ID_code) + { + } + else + { + exprt tmp=sizeof_rec(sub_type); + + if(tmp.is_nil()) + return tmp; + + exprt sum=plus_exprt(dest, tmp); + dest=sum; + } + } + } + else if(type.id()==ID_union) + { + const irept::subt &components= + type.find(ID_components).get_sub(); + + mp_integer max_size=0; + + forall_irep(it, components) + { + if(it->get_bool(ID_is_type)) + continue; + + const typet &sub_type=static_cast(it->find(ID_type)); + + if(sub_type.id()==ID_code) + { + } + else + { + exprt tmp=sizeof_rec(sub_type); + + if(tmp.is_nil()) + return tmp; + + simplify(tmp, ns); + + mp_integer tmp_int; + + if(to_integer(tmp, tmp_int)) + return static_cast(get_nil_irep()); + + if(tmp_int>max_size) max_size=tmp_int; + } + } + + dest=from_integer(max_size, size_type()); + } + else if(type.id()==ID_symbol) + { + return sizeof_rec(ns.follow(type)); + } + else if(type.id()==ID_empty) + { + // gcc says that sizeof(void)==1, ISO C doesn't + dest=from_integer(1, size_type()); + } + else + dest.make_nil(); + + return dest; +} + +/*******************************************************************\ + +Function: c_sizeof + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt c_sizeof(const typet &src, const namespacet &ns) +{ + c_sizeoft c_sizeof_inst(ns); + exprt tmp=c_sizeof_inst(src); + simplify(tmp, ns); + return tmp; +} diff --git a/src/ansi-c/c_sizeof.h b/src/ansi-c/c_sizeof.h new file mode 100644 index 00000000000..ef14b476429 --- /dev/null +++ b/src/ansi-c/c_sizeof.h @@ -0,0 +1,45 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "expr.h" + +class c_sizeoft +{ +public: + c_sizeoft(const namespacet &_ns):ns(_ns) + { + } + + virtual ~c_sizeoft() + { + } + + exprt operator()(const typet &type) + { + return c_sizeof(type); + } + +protected: + const namespacet &ns; + + virtual exprt sizeof_rec(const typet &type); + + exprt c_sizeof(const typet &type) + { + exprt result(sizeof_rec(type)); + + if(result.is_nil()) return result; + + result.set("#c_sizeof_type", type); + return result; + } +}; + +exprt c_sizeof(const typet &src, const namespacet &ns); diff --git a/src/ansi-c/c_storage_spec.h b/src/ansi-c/c_storage_spec.h new file mode 100644 index 00000000000..518c210b9bb --- /dev/null +++ b/src/ansi-c/c_storage_spec.h @@ -0,0 +1,71 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_C_STORAGE_SPEC_H +#define CPROVER_ANSI_C_C_STORAGE_SPEC_H + +#include + +class c_storage_spect +{ +public: + c_storage_spect() + { + clear(); + } + + void clear() + { + is_typedef=false; + is_extern=false; + is_thread_local=false; + is_static=false; + is_register=false; + is_inline=false; + } + + bool is_typedef, is_extern, is_static, is_register, + is_inline, is_thread_local; + + std::string as_string() const; + + friend bool operator == ( + const c_storage_spect &a, + const c_storage_spect &b) + { + return a.is_typedef==b.is_typedef && + a.is_extern==b.is_extern && + a.is_static==b.is_static && + a.is_register==b.is_register && + a.is_thread_local==b.is_thread_local && + a.is_inline==b.is_inline; + } + + friend bool operator != ( + const c_storage_spect &a, + const c_storage_spect &b) + { + return !(a==b); + } + + friend c_storage_spect &operator |= ( + c_storage_spect &a, + const c_storage_spect &b) + { + a.is_typedef |=b.is_typedef; + a.is_extern |=b.is_extern; + a.is_static |=b.is_static; + a.is_register |=b.is_register; + a.is_inline |=b.is_inline; + a.is_thread_local |=b.is_thread_local; + + return a; + } +}; + +#endif diff --git a/src/ansi-c/c_typecast.cpp b/src/ansi-c/c_typecast.cpp new file mode 100644 index 00000000000..d029aaa3bf0 --- /dev/null +++ b/src/ansi-c/c_typecast.cpp @@ -0,0 +1,677 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "c_typecast.h" +#include "c_types.h" +#include "c_qualifiers.h" + +/*******************************************************************\ + +Function: c_implicit_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_implicit_typecast( + exprt &expr, + const typet &dest_type, + const namespacet &ns) +{ + c_typecastt c_typecast(ns); + c_typecast.implicit_typecast(expr, dest_type); + return !c_typecast.errors.empty(); +} + +/*******************************************************************\ + +Function: check_c_implicit_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool check_c_implicit_typecast( + const typet &src_type, + const typet &dest_type, + const namespacet &ns) +{ + c_typecastt c_typecast(ns); + exprt tmp; + tmp.type()=src_type; + c_typecast.implicit_typecast(tmp, dest_type); + return !c_typecast.errors.empty(); +} + +/*******************************************************************\ + +Function: c_implicit_typecast_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_implicit_typecast_arithmetic( + exprt &expr1, exprt &expr2, + const namespacet &ns) +{ + c_typecastt c_typecast(ns); + c_typecast.implicit_typecast_arithmetic(expr1, expr2); + return !c_typecast.errors.empty(); +} + +/*******************************************************************\ + +Function: is_void_pointer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool is_void_pointer(const typet &type) +{ + if(type.id()==ID_pointer) + { + if(type.subtype().id()==ID_empty) + return true; + + return is_void_pointer(type.subtype()); + } + else + return false; +} + +/*******************************************************************\ + +Function: check_c_implicit_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool check_c_implicit_typecast( + const typet &src_type, + const typet &dest_type) +{ + // check qualifiers + + if(src_type.id()==ID_pointer && dest_type.id()==ID_pointer && + src_type.subtype().get_bool(ID_C_constant) && + !dest_type.subtype().get_bool(ID_C_constant)) + return true; + + if(src_type==dest_type) return false; + + const irep_idt &src_type_id=src_type.id(); + + if(src_type_id==ID_natural) + { + if(dest_type.id()==ID_bool) return false; + if(dest_type.id()==ID_integer) return false; + if(dest_type.id()==ID_real) return false; + if(dest_type.id()==ID_complex) return false; + if(dest_type.id()==ID_unsignedbv) return false; + if(dest_type.id()==ID_signedbv) return false; + if(dest_type.id()==ID_floatbv) return false; + } + else if(src_type_id==ID_integer) + { + if(dest_type.id()==ID_bool) return false; + if(dest_type.id()==ID_real) return false; + if(dest_type.id()==ID_complex) return false; + if(dest_type.id()==ID_unsignedbv) return false; + if(dest_type.id()==ID_signedbv) return false; + if(dest_type.id()==ID_floatbv) return false; + if(dest_type.id()==ID_fixedbv) return false; + if(dest_type.id()==ID_pointer) return false; + } + else if(src_type_id==ID_real) + { + if(dest_type.id()==ID_bool) return false; + if(dest_type.id()==ID_complex) return false; + if(dest_type.id()==ID_floatbv) return false; + if(dest_type.id()==ID_fixedbv) return false; + } + else if(src_type_id==ID_rational) + { + if(dest_type.id()==ID_bool) return false; + if(dest_type.id()==ID_complex) return false; + if(dest_type.id()==ID_floatbv) return false; + if(dest_type.id()==ID_fixedbv) return false; + } + else if(src_type_id==ID_bool) + { + if(dest_type.id()==ID_integer) return false; + if(dest_type.id()==ID_real) return false; + if(dest_type.id()==ID_unsignedbv) return false; + if(dest_type.id()==ID_signedbv) return false; + if(dest_type.id()==ID_pointer) return false; + if(dest_type.id()==ID_floatbv) return false; + if(dest_type.id()==ID_fixedbv) return false; + if(dest_type.id()==ID_c_enum) return false; + } + else if(src_type_id==ID_unsignedbv || + src_type_id==ID_signedbv || + src_type_id==ID_c_enum || + src_type_id==ID_incomplete_c_enum) + { + if(dest_type.id()==ID_unsignedbv) return false; + if(dest_type.id()==ID_bool) return false; + if(dest_type.id()==ID_integer) return false; + if(dest_type.id()==ID_real) return false; + if(dest_type.id()==ID_rational) return false; + if(dest_type.id()==ID_signedbv) return false; + if(dest_type.id()==ID_floatbv) return false; + if(dest_type.id()==ID_fixedbv) return false; + if(dest_type.id()==ID_pointer) return false; + if(dest_type.id()==ID_c_enum) return false; + if(dest_type.id()==ID_incomplete_c_enum) return false; + } + else if(src_type_id==ID_floatbv || + src_type_id==ID_fixedbv) + { + if(dest_type.id()==ID_bool) return false; + if(dest_type.id()==ID_integer) return false; + if(dest_type.id()==ID_real) return false; + if(dest_type.id()==ID_rational) return false; + if(dest_type.id()==ID_signedbv) return false; + if(dest_type.id()==ID_unsignedbv) return false; + if(dest_type.id()==ID_floatbv) return false; + if(dest_type.id()==ID_fixedbv) return false; + } + else if(src_type_id==ID_array || + src_type_id==ID_incomplete_array || + src_type_id==ID_pointer) + { + if(dest_type.id()==ID_pointer) + { + const irept &dest_subtype=dest_type.subtype(); + const irept &src_subtype =src_type.subtype(); + + if(src_subtype==dest_subtype) + return false; + else if(is_void_pointer(src_type) || // from void to anything + is_void_pointer(dest_type)) // to void from anything + return false; + } + + if((dest_type.id()==ID_array || + dest_type.id()==ID_incomplete_array) && + (src_type.subtype()==dest_type.subtype())) return false; + + if(dest_type.id()==ID_bool) return false; + if(dest_type.id()==ID_unsignedbv) return false; + if(dest_type.id()==ID_signedbv) return false; + } + + return true; +} + +/*******************************************************************\ + +Function: c_typecastt::follow_with_qualifiers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet c_typecastt::follow_with_qualifiers(const typet &src_type) +{ + if(src_type.id()!=ID_symbol) return src_type; + + typet dest_type=src_type; + + // collect qualifiers + c_qualifierst qualifiers; + + // hack for something that isn't a proper type qualifier + bool transparent_union=false; + + while(dest_type.id()==ID_symbol) + { + qualifiers+=c_qualifierst(dest_type); + + if(dest_type.get_bool(ID_transparent_union)) + transparent_union=true; + + const symbolt &followed_type_symbol= + ns.lookup(dest_type.get(ID_identifier)); + + dest_type=followed_type_symbol.type; + } + + qualifiers.write(dest_type); + + if(transparent_union) + dest_type.set(ID_transparent_union, true); + + return dest_type; +} + +/*******************************************************************\ + +Function: c_typecastt::get_c_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +c_typecastt::c_typet c_typecastt::get_c_type( + const typet &type) +{ + unsigned width=atoi(type.get(ID_width).c_str()); + + if(type.id()==ID_signedbv) + { + if(width<=config.ansi_c.char_width) + return CHAR; + else if(width<=config.ansi_c.int_width) + return INT; + else if(width<=config.ansi_c.long_int_width) + return LONG; + else if(width<=config.ansi_c.long_long_int_width) + return LONGLONG; + } + else if(type.id()==ID_unsignedbv) + { + if(width<=config.ansi_c.char_width) + return UCHAR; + else if(width<=config.ansi_c.int_width) + return UINT; + else if(width<=config.ansi_c.long_int_width) + return ULONG; + else if(width<=config.ansi_c.long_long_int_width) + return ULONGLONG; + } + else if(type.id()==ID_bool) + return BOOL; + else if(type.id()==ID_floatbv || + type.id()==ID_fixedbv) + { + if(width<=config.ansi_c.single_width) + return SINGLE; + else if(width<=config.ansi_c.double_width) + return DOUBLE; + else if(width<=config.ansi_c.long_double_width) + return LONGDOUBLE; + } + else if(type.id()==ID_pointer) + { + if(type.subtype().id()==ID_empty) + return VOIDPTR; + else + return PTR; + } + else if(type.id()==ID_array || + type.id()==ID_incomplete_array) + { + return PTR; + } + else if(type.id()==ID_c_enum || + type.id()==ID_incomplete_c_enum) + { + return INT; + } + else if(type.id()==ID_symbol) + return get_c_type(ns.follow(type)); + else if(type.id()==ID_rational) + return RATIONAL; + else if(type.id()==ID_real) + return REAL; + + return OTHER; +} + +/*******************************************************************\ + +Function: c_typecastt::implicit_typecast_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecastt::implicit_typecast_arithmetic( + exprt &expr, + c_typet c_type) +{ + typet new_type; + + const typet &expr_type=ns.follow(expr.type()); + + switch(c_type) + { + case PTR: + if(expr_type.id()==ID_array || + expr_type.id()==ID_incomplete_array) + { + new_type.id(ID_pointer); + new_type.subtype()=expr_type.subtype(); + break; + } + return; + + case BOOL: new_type=bool_typet(); break; + case CHAR: assert(false); // should always be promoted + case UCHAR: assert(false); // should always be promoted + case INT: new_type=int_type(); break; + case UINT: new_type=uint_type(); break; + case LONG: new_type=long_int_type(); break; + case ULONG: new_type=long_uint_type(); break; + case LONGLONG: new_type=long_long_int_type(); break; + case ULONGLONG: new_type=long_long_uint_type(); break; + case SINGLE: new_type=float_type(); break; + case DOUBLE: new_type=double_type(); break; + case LONGDOUBLE: new_type=long_double_type(); break; + case RATIONAL: new_type=rational_typet(); break; + case REAL: new_type=real_typet(); break; + case INTEGER: new_type=integer_typet(); break; + default: return; + } + + if(new_type!=expr_type) + { + if(new_type.id()==ID_pointer && + (expr_type.id()==ID_array || + expr_type.id()==ID_incomplete_array)) + { + exprt index_expr(ID_index, expr_type.subtype()); + index_expr.reserve_operands(2); + index_expr.move_to_operands(expr); + index_expr.copy_to_operands(gen_zero(index_type())); + expr=exprt(ID_address_of, new_type); + expr.move_to_operands(index_expr); + } + else + do_typecast(expr, new_type); + } +} + +/*******************************************************************\ + +Function: c_typecastt::implicit_typecast_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecastt::implicit_typecast_arithmetic(exprt &expr) +{ + c_typet c_type=get_c_type(expr.type()); + c_type=std::max(c_type, INT); // minimum promotion + implicit_typecast_arithmetic(expr, c_type); +} + +/*******************************************************************\ + +Function: c_typecastt::implicit_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecastt::implicit_typecast( + exprt &expr, + const typet &type) +{ + typet src_type=follow_with_qualifiers(expr.type()), + dest_type=follow_with_qualifiers(type); + + implicit_typecast_followed(expr, src_type, dest_type); +} + +/*******************************************************************\ + +Function: c_typecastt::implicit_typecast_followed + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecastt::implicit_typecast_followed( + exprt &expr, + const typet &src_type, + const typet &dest_type) +{ + // do transparent union + if(dest_type.id()==ID_union && + dest_type.get_bool(ID_transparent_union) && + src_type.id()!=ID_union) + { + // check union members + const union_typet &union_type=to_union_type(dest_type); + + for(union_typet::componentst::const_iterator + it=union_type.components().begin(); + it!=union_type.components().end(); + it++) + { + if(!check_c_implicit_typecast(src_type, it->type())) + { + // build union constructor + exprt union_expr(ID_union, union_type); + union_expr.move_to_operands(expr); + union_expr.set(ID_component_name, it->get_name()); + expr=union_expr; + return; // ok + } + } + } + + if(dest_type.id()==ID_pointer) + { + // special case: 0 == NULL + + if(expr.is_zero() && ( + src_type.id()==ID_unsignedbv || + src_type.id()==ID_signedbv || + src_type.id()==ID_natural || + src_type.id()==ID_integer)) + { + expr=exprt(ID_constant, dest_type); + expr.set(ID_value, ID_NULL); + return; // ok + } + + if(src_type.id()==ID_pointer || + src_type.id()==ID_array || + src_type.id()==ID_incomplete_array) + { + // we are quite generous about pointers + + const typet &src_sub=ns.follow(src_type.subtype()); + const typet &dest_sub=ns.follow(dest_type.subtype()); + + if(is_void_pointer(src_type) || + is_void_pointer(dest_type)) + { + // from/to void is always good + } + else if(src_sub.id()==ID_code && + dest_sub.id()==ID_code) + { + // very generous: + // between any two function pointers it's ok + } + else if(base_type_eq(src_type.subtype(), dest_type.subtype(), ns)) + { + // ok + } + else if(is_number(src_sub) && is_number(dest_sub)) + { + // also generous: between any to scalar types it's ok + } + else + warnings.push_back("incompatible pointer types"); + + // check qualifiers + + if(src_type.subtype().get_bool(ID_C_constant) && + !dest_type.subtype().get_bool(ID_C_constant)) + warnings.push_back("disregarding const"); + + if(src_type.subtype().get_bool(ID_C_volatile) && + !dest_type.subtype().get_bool(ID_C_volatile)) + warnings.push_back("disregarding volatile"); + + if(src_type==dest_type) + { + expr.type()=src_type; // because of qualifiers + } + else + do_typecast(expr, dest_type); + + return; // ok + } + } + + if(check_c_implicit_typecast(src_type, dest_type)) + errors.push_back("implicit conversion not permitted"); + else if(src_type!=dest_type) + do_typecast(expr, dest_type); +} + +/*******************************************************************\ + +Function: c_typecastt::implicit_typecast_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecastt::implicit_typecast_arithmetic( + exprt &expr1, + exprt &expr2) +{ + const typet &type1=ns.follow(expr1.type()); + const typet &type2=ns.follow(expr2.type()); + + c_typet c_type1=get_c_type(type1), + c_type2=get_c_type(type2); + + c_typet max_type=std::max(c_type1, c_type2); + max_type=std::max(max_type, INT); // minimum promotion + + implicit_typecast_arithmetic(expr1, max_type); + implicit_typecast_arithmetic(expr2, max_type); + + if(max_type==PTR) + { + if(c_type1==VOIDPTR) + do_typecast(expr1, expr2.type()); + + if(c_type2==VOIDPTR) + do_typecast(expr2, expr1.type()); + } +} + +/*******************************************************************\ + +Function: c_typecastt::do_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecastt::do_typecast(exprt &dest, const typet &type) +{ + // special case: array -> pointer is actually + // something like address_of + + const typet &dest_type=ns.follow(dest.type()); + + if(dest_type.id()==ID_array || + dest_type.id()==ID_incomplete_array) + { + index_exprt index; + index.array()=dest; + index.index()=gen_zero(index_type()); + index.type()=dest_type.subtype(); + dest=gen_address_of(index); + if(ns.follow(dest.type())!=ns.follow(type)) + dest.make_typecast(type); + return; + } + + if(dest_type!=type) + { + dest.make_typecast(type); + + if(dest.op0().is_constant()) + { + // preserve #c_sizeof_type -- don't make it a reference! + const irept c_sizeof_type= + dest.op0().find("#c_sizeof_type"); + + simplify_exprt simplify_expr(ns); + simplify_expr.simplify_typecast(dest); + + if(c_sizeof_type.is_not_nil()) + dest.add("#c_sizeof_type")=c_sizeof_type; + } + } +} diff --git a/src/ansi-c/c_typecast.h b/src/ansi-c/c_typecast.h new file mode 100644 index 00000000000..276a04ddb3c --- /dev/null +++ b/src/ansi-c/c_typecast.h @@ -0,0 +1,92 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_C_TYPECAST_H +#define CPROVER_C_TYPECAST_H + +#include +#include + +// try a type cast from expr.type() to type +// +// false: typecast successfull, expr modified +// true: typecast failed + +bool check_c_implicit_typecast( + const typet &src_type, + const typet &dest_type); + +bool check_c_implicit_typecast( + const typet &src_type, + const typet &dest_type, + const namespacet &ns); + +bool c_implicit_typecast( + exprt &expr, + const typet &dest_type, + const namespacet &ns); + +bool c_implicit_typecast_arithmetic( + exprt &expr1, exprt &expr2, + const namespacet &ns); + +class c_typecastt +{ +public: + c_typecastt(const namespacet &_ns):ns(_ns) + { + } + + virtual ~c_typecastt() + { + } + + virtual void implicit_typecast( + exprt &expr, + const typet &type); + + virtual void implicit_typecast_arithmetic( + exprt &expr); + + virtual void implicit_typecast_arithmetic( + exprt &expr1, + exprt &expr2); + + std::list errors; + std::list warnings; + +protected: + const namespacet &ns; + + // these are in promotion order + + enum c_typet { BOOL, CHAR, UCHAR, INT, UINT, LONG, ULONG, + LONGLONG, ULONGLONG, + INTEGER, + SINGLE, DOUBLE, LONGDOUBLE, + RATIONAL, REAL, + VOIDPTR, PTR, OTHER }; + + c_typet get_c_type(const typet &type); + + void implicit_typecast_arithmetic( + exprt &expr, + c_typet c_type); + + typet follow_with_qualifiers(const typet &src); + + // after follow_with_qualifiers + virtual void implicit_typecast_followed( + exprt &expr, + const typet &src_type, + const typet &dest_type); + + void do_typecast(exprt &dest, const typet &type); +}; + +#endif diff --git a/src/ansi-c/c_typecheck_argc_argv.cpp b/src/ansi-c/c_typecheck_argc_argv.cpp new file mode 100644 index 00000000000..b62ed3da2c8 --- /dev/null +++ b/src/ansi-c/c_typecheck_argc_argv.cpp @@ -0,0 +1,133 @@ +/*******************************************************************\ + +Module: ANSI-C Conversion / Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "c_typecheck_base.h" + +/*******************************************************************\ + +Function: c_typecheck_baset::add_argc_argv + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::add_argc_argv(const symbolt &main_symbol) +{ + const irept &arguments= + main_symbol.type.find(ID_arguments); + + if(arguments.get_sub().size()==0) + return; + + if(arguments.get_sub().size()!=2 && + arguments.get_sub().size()!=3) + { + err_location(main_symbol.location); + throw "main expected to have no or two or three arguments"; + } + + symbolt *argc_new_symbol; + + const exprt &op0=static_cast(arguments.get_sub()[0]); + const exprt &op1=static_cast(arguments.get_sub()[1]); + + { + symbolt argc_symbol; + + argc_symbol.base_name="argc"; + argc_symbol.name="c::argc'"; + argc_symbol.type=op0.type(); + argc_symbol.static_lifetime=true; + argc_symbol.lvalue=true; + + if(argc_symbol.type.id()!=ID_signedbv && + argc_symbol.type.id()!=ID_unsignedbv) + { + err_location(main_symbol.location); + str << "argc argument expected to be integer type, but got `" + << to_string(argc_symbol.type) << "'"; + throw 0; + } + + move_symbol(argc_symbol, argc_new_symbol); + } + + { + if(op1.type().id()!=ID_pointer || + op1.type().subtype().id()!=ID_pointer) + { + err_location(main_symbol.location); + str << "argv argument expected to be pointer-to-pointer type, " + "but got `" + << to_string(op1.type()) << "'"; + throw 0; + } + + // we make the type of this thing an array of pointers + typet argv_type=array_typet(); + argv_type.subtype()=op1.type().subtype(); + + // need to add one to the size -- the array is terminated + // with NULL + exprt one_expr=from_integer(1, argc_new_symbol->type); + + exprt size_expr(ID_plus, argc_new_symbol->type); + size_expr.copy_to_operands(symbol_expr(*argc_new_symbol), one_expr); + argv_type.add(ID_size).swap(size_expr); + + symbolt argv_symbol; + + argv_symbol.base_name="argv"; + argv_symbol.name="c::argv'"; + argv_symbol.type=argv_type; + argv_symbol.static_lifetime=true; + argv_symbol.lvalue=true; + + symbolt *argv_new_symbol; + move_symbol(argv_symbol, argv_new_symbol); + } + + if(arguments.get_sub().size()==3) + { + symbolt envp_symbol; + envp_symbol.base_name="envp"; + envp_symbol.name="c::envp'"; + envp_symbol.type=(static_cast(arguments.get_sub()[2])).type(); + envp_symbol.static_lifetime=true; + + symbolt envp_size_symbol, *envp_new_size_symbol; + envp_size_symbol.base_name="envp_size"; + envp_size_symbol.name="c::envp_size'"; + envp_size_symbol.type=op0.type(); // same type as argc! + envp_size_symbol.static_lifetime=true; + move_symbol(envp_size_symbol, envp_new_size_symbol); + + if(envp_symbol.type.id()!=ID_pointer) + { + err_location(main_symbol.location); + str << "envp argument expected to be pointer type, but got `" + << to_string(envp_symbol.type) << "'"; + throw 0; + } + + exprt size_expr = symbol_expr(*envp_new_size_symbol); + + envp_symbol.type.id(ID_array); + envp_symbol.type.add(ID_size).swap(size_expr); + + symbolt *envp_new_symbol; + move_symbol(envp_symbol, envp_new_symbol); + } +} diff --git a/src/ansi-c/c_typecheck_base.cpp b/src/ansi-c/c_typecheck_base.cpp new file mode 100644 index 00000000000..4f54092770d --- /dev/null +++ b/src/ansi-c/c_typecheck_base.cpp @@ -0,0 +1,642 @@ +/*******************************************************************\ + +Module: ANSI-C Conversion / Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#include "c_typecheck_base.h" +#include "expr2c.h" +#include "type2name.h" +#include "std_types.h" + +/*******************************************************************\ + +Function: c_typecheck_baset::to_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string c_typecheck_baset::to_string(const exprt &expr) +{ + return expr2c(expr, *this); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::to_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string c_typecheck_baset::to_string(const typet &type) +{ + return type2c(type, *this); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::replace_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::replace_symbol(irept &symbol) +{ + id_replace_mapt::const_iterator it= + id_replace_map.find(symbol.get(ID_identifier)); + + if(it!=id_replace_map.end()) + symbol.set(ID_identifier, it->second); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::move_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::move_symbol(symbolt &symbol, symbolt *&new_symbol) +{ + symbol.mode=mode; + symbol.module=module; + + if(context.move(symbol, new_symbol)) + { + err_location(symbol.location); + throw "failed to move symbol `"+id2string(symbol.name)+ + "' into context"; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_symbol(symbolt &symbol) +{ + // first of all, we typecheck the type + typecheck_type(symbol.type); + + bool is_function=symbol.type.id()==ID_code; + + const typet &final_type=follow(symbol.type); + + // set a few flags + symbol.lvalue=!symbol.is_type && !symbol.is_macro; + + std::string prefix="c::"; + std::string root_name=prefix+id2string(symbol.base_name); + std::string new_name=id2string(symbol.name); + + // do anon-tags first + if(symbol.is_type && + has_prefix(id2string(symbol.name), prefix+"tag-#anon")) + { + // we rename them to make collisions unproblematic + std::string typestr = type2name(symbol.type); + new_name = prefix+"tag-#anon#" + typestr; + + id_replace_map[symbol.name]=new_name; + + contextt::symbolst::const_iterator it=context.symbols.find(new_name); + if(it!=context.symbols.end()) + return; // bail out, we have an appropriate symbol already. + + irep_idt newtag=std::string("#anon#")+typestr; + symbol.type.set(ID_tag, newtag); + } + else if(symbol.file_local) + { + // file-local stuff -- we rename to prevent collisions with + // non-file local symbols with the same name + // collisions are resolved during linking + assert(has_prefix(new_name, prefix)); + new_name=prefix+"$file::"+std::string(new_name, prefix.size(), std::string::npos); + } + else if(symbol.is_extern && !is_function) + { + // variables mared as "extern" go into the global namespace + // and have static lifetime + new_name=root_name; + symbol.static_lifetime=true; + } + else if(is_function && !symbol.is_actual) + { + // functions always go into the global namespace + // (code doesn't have lifetime), + // unless it's a function argument + new_name=root_name; + symbol.static_lifetime=false; + } + else if(!is_function && symbol.value.id()==ID_code) + { + err_location(symbol.value); + throw "only functions can have a function body"; + } + + if(symbol.name!=new_name) + { + id_replace_map[symbol.name]=new_name; + symbol.name=new_name; + } + + // and now that we have the proper name + // we clean the type of any side-effects + // (needs to be done before next symbol) + clean_type(symbol, symbol.type); + + // set the pretty name + if(symbol.is_type && + (final_type.id()==ID_struct || + final_type.id()==ID_incomplete_struct)) + { + symbol.pretty_name="struct "+id2string(symbol.base_name); + } + else if(symbol.is_type && + (final_type.id()==ID_union || + final_type.id()==ID_incomplete_union)) + { + symbol.pretty_name="union "+id2string(symbol.base_name); + } + else if(symbol.is_type && + (final_type.id()==ID_c_enum || + final_type.id()==ID_incomplete_c_enum)) + { + symbol.pretty_name="enum "+id2string(symbol.base_name); + } + else + { + // just strip the c:: + symbol.pretty_name= + std::string(new_name, prefix.size(), std::string::npos); + + // strip the $file:: + if(has_prefix(id2string(symbol.pretty_name), "$file::")) + symbol.pretty_name=std::string(id2string(symbol.pretty_name), 7, std::string::npos); + } + + // see if we have it already + contextt::symbolst::iterator old_it=context.symbols.find(symbol.name); + + if(old_it==context.symbols.end()) + { + // just put into context + symbolt *new_symbol; + move_symbol(symbol, new_symbol); + + typecheck_new_symbol(*new_symbol); + } + else + { + if(old_it->second.is_type!=symbol.is_type) + { + err_location(symbol.location); + str << "redeclaration of `" << symbol.display_name() + << "' as a different kind of symbol"; + throw 0; + } + + if(symbol.is_type) + typecheck_redefinition_type(old_it->second, symbol); + else + typecheck_redefinition_non_type(old_it->second, symbol); + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_new_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_new_symbol(symbolt &symbol) +{ + if(symbol.is_actual) + adjust_function_argument(symbol.type); + + // check initializer, if needed + + if(symbol.type.id()==ID_code) + { + if(symbol.value.is_not_nil()) + typecheck_function_body(symbol); + else + { + // we don't need the identifiers + code_typet &code_type=to_code_type(symbol.type); + for(code_typet::argumentst::iterator + it=code_type.arguments().begin(); + it!=code_type.arguments().end(); + it++) + it->set_identifier(irep_idt()); + } + } + else + { + if(symbol.type.id()==ID_incomplete_array || + symbol.type.id()==ID_array) + { + // Insert a new type symbol for the array. + // We do this because we want a convenient way + // of making the type complete later on + + symbolt new_symbol; + new_symbol.name=id2string(symbol.name)+"$type"; + new_symbol.base_name=id2string(symbol.base_name)+"$type"; + new_symbol.location=symbol.location; + new_symbol.mode=symbol.mode; + new_symbol.module=symbol.module; + new_symbol.type=symbol.type; + new_symbol.is_type=true; + new_symbol.is_macro=false; + + symbol.type=symbol_typet(new_symbol.name); + + symbolt *new_sp; + context.move(new_symbol, new_sp); + } + + // check the initializer + do_initializer(symbol); + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_redefinition_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_redefinition_type( + symbolt &old_symbol, + symbolt &new_symbol) +{ + const typet &final_old=follow(old_symbol.type); + const typet &final_new=follow(new_symbol.type); + + // see if we had s.th. incomplete before + if(final_old.id()==ID_incomplete_struct || + final_old.id()==ID_incomplete_union || + final_old.id()==ID_incomplete_c_enum) + { + // new one complete? + if("incomplete_"+final_new.id_string()==final_old.id_string()) + { + // overwrite location + old_symbol.location=new_symbol.location; + + // move body + old_symbol.type.swap(new_symbol.type); + } + else if(new_symbol.type.id()==old_symbol.type.id()) + return; + else + { + err_location(new_symbol.location); + str << "error: conflicting definition of type symbol `" + << new_symbol.display_name() + << "'"; + throw 0; + } + } + else + { + // see if new one is just a tag + if(final_new.id()==ID_incomplete_struct || + final_new.id()==ID_incomplete_union || + final_new.id()==ID_incomplete_c_enum) + { + if("incomplete_"+final_old.id_string()==final_new.id_string()) + { + // just ignore silently + } + else + { + // arg! new tag type + err_location(new_symbol.location); + str << "error: conflicting definition of tag symbol `" + << new_symbol.display_name() + << "'"; + throw 0; + } + } + else if(config.ansi_c.os==configt::ansi_ct::OS_WIN && + final_new.id()==ID_c_enum && final_old.id()==ID_c_enum) + { + // under Windows, ignore this silently; + // MSC doesn't think this is a problem, but GCC complains. + } + else if(config.ansi_c.os==configt::ansi_ct::OS_WIN && + final_new.id()==ID_pointer && final_old.id()==ID_pointer && + follow(final_new.subtype()).id()==ID_c_enum && + follow(final_old.subtype()).id()==ID_c_enum) + { + // under Windows, ignore this silently; + // MSC doesn't think this is a problem, but GCC complains. + } + else + { + // see if it changed + if(follow(new_symbol.type)!=follow(old_symbol.type)) + { + err_location(new_symbol.location); + str << "error: type symbol `" << new_symbol.display_name() + << "' defined twice:" << std::endl; + str << "Original: " << to_string(old_symbol.type) << std::endl; + str << " New: " << to_string(new_symbol.type); + throw 0; + } + } + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_redefinition_non_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_redefinition_non_type( + symbolt &old_symbol, + symbolt &new_symbol) +{ + // do initializer, this may change the type + if(follow(new_symbol.type).id()!=ID_code) + do_initializer(new_symbol); + + const typet &final_old=follow(old_symbol.type); + const typet &final_new=follow(new_symbol.type); + + // K&R stuff? + if(old_symbol.type.id()==ID_KnR) + { + // check the type + if(final_new.id()==ID_code) + { + err_location(new_symbol.location); + throw "function type not allowed for K&R function argument"; + } + + // fix up old symbol -- we now got the type + old_symbol.type=new_symbol.type; + return; + } + + if(final_new.id()==ID_code) + { + bool inlined= + (new_symbol.type.get_bool(ID_C_inlined) || + old_symbol.type.get_bool(ID_C_inlined)); + + if(final_old.id()!=ID_code) + { + err_location(new_symbol.location); + str << "error: function symbol `" << new_symbol.display_name() + << "' defined twice with different types:" << std::endl; + str << "Original: " << to_string(old_symbol.type) << std::endl; + str << " New: " << to_string(new_symbol.type); + throw 0; + } + + code_typet &old_ct=to_code_type(old_symbol.type); + code_typet &new_ct=to_code_type(new_symbol.type); + + if(old_ct.has_ellipsis() && !new_ct.has_ellipsis()) + old_ct=new_ct; + else if(!old_ct.has_ellipsis() && new_ct.has_ellipsis()) + new_ct=old_ct; + + if(inlined) + { + old_symbol.type.set(ID_C_inlined, true); + new_symbol.type.set(ID_C_inlined, true); + } + + // do body + + if(new_symbol.value.is_not_nil()) + { + if(old_symbol.value.is_not_nil()) + { + err_location(new_symbol.location); + str << "function `" << new_symbol.display_name() + << "' defined twice"; + error(); + throw 0; + } + + typecheck_function_body(new_symbol); + + // overwrite location + old_symbol.location=new_symbol.location; + + // move body + old_symbol.value.swap(new_symbol.value); + + // overwrite type (because of argument names) + old_symbol.type=new_symbol.type; + } + + return; + } + + if(final_old!=final_new) + { + if(final_old.id()==ID_array && + final_new.id()==ID_incomplete_array && + final_old.subtype()==final_new.subtype()) + { + // this is ok, just use old type + new_symbol.type=old_symbol.type; + } + else if(final_old.id()==ID_incomplete_array && + final_new.id()==ID_array && + final_old.subtype()==final_new.subtype()) + { + // this is also ok + if(old_symbol.type.id()==ID_symbol) + { + // fix the symbol, not just the type + const irep_idt identifier= + old_symbol.type.get(ID_identifier); + + contextt::symbolst::iterator s_it=context.symbols.find(identifier); + + if(s_it==context.symbols.end()) + { + err_location(old_symbol.location); + str << "typecheck_redefinition_non_type: " + "failed to find symbol `" << identifier << "'"; + throw 0; + } + + symbolt &symbol=s_it->second; + + symbol.type=final_new; + } + else + old_symbol.type=new_symbol.type; + } + else if((final_old.id()==ID_incomplete_c_enum || + final_old.id()==ID_c_enum) && + (final_new.id()==ID_incomplete_c_enum || + final_new.id()==ID_c_enum)) + { + // this is ok for now + } + else + { + err_location(new_symbol.location); + str << "error: symbol `" << new_symbol.display_name() + << "' defined twice with different types:" << std::endl; + str << "Original: " << to_string(old_symbol.type) << std::endl; + str << " New: " << to_string(new_symbol.type); + throw 0; + } + } + else // finals are equal + { + } + + // do value + if(new_symbol.value.is_not_nil()) + { + // see if we already have one + if(old_symbol.value.is_not_nil()) + { + if(new_symbol.value.get_bool(ID_C_zero_initializer)) + { + // do nothing + } + else if(old_symbol.value.get_bool(ID_C_zero_initializer)) + { + old_symbol.value=new_symbol.value; + old_symbol.type=new_symbol.type; + } + else + { + if(new_symbol.is_macro && + (final_new.id()==ID_incomplete_c_enum || + final_new.id()==ID_c_enum) && + old_symbol.value.is_constant() && + new_symbol.value.is_constant() && + old_symbol.value.get(ID_value)==new_symbol.value.get(ID_value)) + { + // ignore + } + else + { + err_location(new_symbol.value); + str << "symbol `" << new_symbol.display_name() + << "' already has an initial value"; + warning(); + } + } + } + else + { + old_symbol.value=new_symbol.value; + old_symbol.type=new_symbol.type; + } + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_function_body + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_function_body(symbolt &symbol) +{ + code_typet &code_type=to_code_type(symbol.type); + + // adjust the function identifiers + for(code_typet::argumentst::iterator + a_it=code_type.arguments().begin(); + a_it!=code_type.arguments().end(); + a_it++) + { + irep_idt identifier=a_it->get_identifier(); + if(identifier!=irep_idt()) + { + id_replace_mapt::const_iterator + m_it=id_replace_map.find(identifier); + + if(m_it!=id_replace_map.end()) + a_it->set_identifier(m_it->second); + } + } + + assert(symbol.value.is_not_nil()); + + // fix type + symbol.value.type()=code_type; + + // set return type + return_type=code_type.return_type(); + + typecheck_code(to_code(symbol.value)); + + if(symbol.name=="c::main") + add_argc_argv(symbol); +} diff --git a/src/ansi-c/c_typecheck_base.h b/src/ansi-c/c_typecheck_base.h new file mode 100644 index 00000000000..c2a600b686b --- /dev/null +++ b/src/ansi-c/c_typecheck_base.h @@ -0,0 +1,269 @@ +/*******************************************************************\ + +Module: ANSI-C Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_C_TYPECHECK_BASE_H +#define CPROVER_C_TYPECHECK_BASE_H + +#include +#include +#include +#include +#include +#include + +#include "designator.h" + +class c_typecheck_baset: + public typecheckt, + public namespacet +{ +public: + c_typecheck_baset( + contextt &_context, + const std::string &_module, + message_handlert &_message_handler): + typecheckt(_message_handler), + namespacet(_context), + context(_context), + module(_module), + mode("C") + { + } + + c_typecheck_baset( + contextt &_context1, + const contextt &_context2, + const std::string &_module, + message_handlert &_message_handler): + typecheckt(_message_handler), + namespacet(_context1, _context2), + context(_context1), + module(_module), + mode("C") + { + } + + virtual ~c_typecheck_baset() { } + + virtual void typecheck()=0; + virtual void typecheck_expr(exprt &expr); + +protected: + contextt &context; + const irep_idt module; + const irep_idt mode; + unsigned tmp_counter; + + typedef hash_map_cont id_replace_mapt; + id_replace_mapt id_replace_map; + + // apply id_replace_map + void replace_symbol(irept &symbol); + + // overload to use language specific syntax + virtual std::string to_string(const exprt &expr); + virtual std::string to_string(const typet &type); + + // + // service functions + // + + // initializers + struct init_statet + { + protected: + const exprt array; + unsigned pos; + + public: + explicit init_statet(const exprt &_array):array(_array), pos(0) + { + } + + unsigned remaining() const + { + return array.operands().size()-pos; + } + + bool has_next() const + { + return pos() const + { + return &(array.operands()[pos]); + } + }; + + virtual exprt zero_initializer( + const typet &type, + const locationt &location); + + virtual void do_initializer( + exprt &initializer, + const typet &type, + bool force_constant); + + virtual exprt do_initializer_rec( + const exprt &value, + const typet &type, + bool force_constant); + + virtual exprt do_initializer_list( + const exprt &value, + const typet &type, + bool force_constant); + + virtual void do_designated_initializer( + exprt &result, + designatort &designator, + const exprt &value, + bool force_constant); + + designatort make_designator(const typet &type, const exprt &src); + void designator_enter(const typet &type, designatort &designator); // go down + void increment_designator(designatort &designator); + + // typecasts + + virtual void implicit_typecast(exprt &expr, const typet &type); + virtual void implicit_typecast_arithmetic(exprt &expr); + virtual void implicit_typecast_arithmetic(exprt &expr1, exprt &expr2); + + virtual void implicit_typecast_bool(exprt &expr) + { + implicit_typecast(expr, bool_typet()); + } + + // code + virtual void start_typecheck_code(); + virtual void typecheck_code(codet &code); + + virtual void typecheck_assign(codet &expr); + virtual void typecheck_asm(codet &code); + virtual void typecheck_block(codet &code); + virtual void typecheck_break(codet &code); + virtual void typecheck_continue(codet &code); + virtual void typecheck_decl(codet &code); + virtual void typecheck_decl_type(codet &code); + virtual void typecheck_decl_block(codet &code); + virtual void typecheck_expression(codet &code); + virtual void typecheck_for(codet &code); + virtual void typecheck_goto(codet &code); + virtual void typecheck_computed_goto(codet &code); + virtual void typecheck_ifthenelse(code_ifthenelset &code); + virtual void typecheck_label(code_labelt &code); + virtual void typecheck_return(codet &code); + virtual void typecheck_switch(codet &code); + virtual void typecheck_while(codet &code); + virtual void typecheck_start_thread(codet &code); + + bool break_is_allowed; + bool continue_is_allowed; + bool case_is_allowed; + typet switch_op_type; + typet return_type; + + // expressions + virtual void typecheck_expr_builtin_va_arg(exprt &expr); + virtual void typecheck_expr_builtin_offsetof(exprt &expr); + virtual void typecheck_expr_main(exprt &expr); + virtual void typecheck_expr_operands(exprt &expr); + virtual void typecheck_expr_comma(exprt &expr); + virtual void typecheck_expr_constant(exprt &expr); + virtual void typecheck_expr_side_effect(side_effect_exprt &expr); + virtual void typecheck_expr_unary_arithmetic(exprt &expr); + virtual void typecheck_expr_unary_boolean(exprt &expr); + virtual void typecheck_expr_binary_arithmetic(exprt &expr); + virtual void typecheck_expr_pointer_arithmetic(exprt &expr); + virtual void typecheck_arithmetic_pointer(const exprt &expr); + virtual void typecheck_expr_binary_boolean(exprt &expr); + virtual void typecheck_expr_trinary(if_exprt &expr); + virtual void typecheck_expr_address_of(exprt &expr); + virtual void typecheck_expr_dereference(exprt &expr); + virtual void typecheck_expr_member(exprt &expr); + bool has_component_rec(const typet &type, const irep_idt &component_name); + virtual void typecheck_expr_ptrmember(exprt &expr); + virtual void typecheck_expr_rel(exprt &expr); + virtual void adjust_float_rel(exprt &expr); + virtual void typecheck_expr_index(exprt &expr); + virtual void typecheck_expr_typecast(exprt &expr); + virtual void typecheck_expr_symbol(exprt &expr); + virtual void typecheck_expr_sizeof(exprt &expr); + virtual void typecheck_expr_alignof(exprt &expr); + virtual void typecheck_expr_function_identifier(exprt &expr); + virtual void typecheck_side_effect_gcc_conditional_expression(side_effect_exprt &expr); + virtual void typecheck_side_effect_function_call(side_effect_expr_function_callt &expr); + virtual void typecheck_side_effect_assignment(exprt &expr); + virtual void typecheck_side_effect_statement_expression(side_effect_exprt &expr); + virtual void typecheck_function_call_arguments(side_effect_expr_function_callt &expr); + virtual void do_special_functions(side_effect_expr_function_callt &expr); + + virtual void make_constant(exprt &expr); + virtual void make_constant_index(exprt &expr); + virtual void make_constant_rec(exprt &expr); + + // types + virtual void typecheck_type(typet &type); + virtual void typecheck_compound_type(struct_union_typet &type); + virtual void typecheck_code_type(code_typet &type); + virtual void typecheck_symbol_type(typet &type); + virtual void typecheck_c_bitfield_type(typet &type); + virtual void typecheck_typeof_type(typet &type); + virtual void typecheck_array_type(array_typet &type); + virtual void typecheck_vector_type(vector_typet &type); + virtual void adjust_function_argument(typet &type) const; + virtual void add_padding(struct_typet &type); + virtual unsigned alignment(const typet &type) const; + virtual bool is_complete_type(const typet &type) const; + + // this cleans expressions in array types + virtual void clean_type( + const symbolt &base_symbol, + typet &type); + + typedef hash_set_cont already_cleanedt; + already_cleanedt already_cleaned; + + // this is for storing side-effects found in types + typedef hash_map_cont clean_codet; + clean_codet clean_code; + + void make_index_type(exprt &expr); + + // environment + void add_argc_argv(const symbolt &main_symbol); + + // context management + void move_symbol(symbolt &symbol, symbolt *&new_symbol); + void move_symbol(symbolt &symbol) + { symbolt *new_symbol; move_symbol(symbol, new_symbol); } + + // top level stuff + void typecheck_symbol(symbolt &symbol); + void typecheck_new_symbol(symbolt &symbol); + void typecheck_redefinition_type(symbolt &old_symbol, symbolt &new_symbol); + void typecheck_redefinition_non_type(symbolt &old_symbol, symbolt &new_symbol); + void typecheck_function_body(symbolt &symbol); + + virtual void do_initializer(symbolt &symbol); +}; + +#endif diff --git a/src/ansi-c/c_typecheck_code.cpp b/src/ansi-c/c_typecheck_code.cpp new file mode 100644 index 00000000000..95facf5db8b --- /dev/null +++ b/src/ansi-c/c_typecheck_code.cpp @@ -0,0 +1,816 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "c_typecheck_base.h" + +/*******************************************************************\ + +Function: c_typecheck_baset::init + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::start_typecheck_code() +{ + case_is_allowed=break_is_allowed=continue_is_allowed=false; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_code(codet &code) +{ + if(code.id()!=ID_code) + throw "expected code, got "+code.pretty(); + + code.type()=code_typet(); + + const irep_idt &statement=code.get_statement(); + + if(statement==ID_expression) + typecheck_expression(code); + else if(statement==ID_label) + typecheck_label(to_code_label(code)); + else if(statement==ID_block) + typecheck_block(code); + else if(statement==ID_ifthenelse) + typecheck_ifthenelse(to_code_ifthenelse(code)); + else if(statement==ID_while || + statement==ID_dowhile) + typecheck_while(code); + else if(statement==ID_for) + typecheck_for(code); + else if(statement==ID_switch) + typecheck_switch(code); + else if(statement==ID_break) + typecheck_break(code); + else if(statement==ID_goto) + typecheck_goto(code); + else if(statement=="computed-goto") + typecheck_computed_goto(code); + else if(statement==ID_continue) + typecheck_continue(code); + else if(statement==ID_return) + typecheck_return(code); + else if(statement==ID_decl) + typecheck_decl(code); + else if(statement==ID_decl_type) + typecheck_decl_type(code); + else if(statement==ID_decl_block) + typecheck_decl_block(code); + else if(statement==ID_assign) + typecheck_assign(code); + else if(statement==ID_skip) + { + } + else if(statement==ID_asm) + typecheck_asm(code); + else if(statement=="start_thread") + typecheck_start_thread(code); + else if(statement==ID_gcc_local_label) + { + } + else + { + err_location(code); + str << "unexpected statement: " << statement; + throw 0; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_asm + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_asm(codet &code) +{ +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_assign(codet &code) +{ + if(code.operands().size()!=2) + throw "assignment statement expected to have two operands"; + + typecheck_expr(code.op0()); + typecheck_expr(code.op1()); + + implicit_typecast(code.op1(), code.op0().type()); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_decl_block + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_decl_block(codet &code) +{ + Forall_operands(it, code) + typecheck_code(to_code(*it)); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_block + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_block(codet &code) +{ + Forall_operands(it, code) + typecheck_code(to_code(*it)); + + // do decl-blocks + + exprt new_ops; + new_ops.operands().reserve(code.operands().size()); + + Forall_operands(it1, code) + { + if(it1->is_nil()) continue; + + codet &code_op=to_code(*it1); + + if(code_op.get_statement()==ID_decl_block) + { + Forall_operands(it2, code_op) + if(it2->is_not_nil()) + new_ops.move_to_operands(*it2); + } + else if(code_op.get_statement()==ID_label) + { + // these may be nested + codet *code_ptr=&code_op; + + while(code_ptr->get_statement()==ID_label) + { + assert(code_ptr->operands().size()==1); + code_ptr=&to_code(code_ptr->op0()); + } + + codet &label_op=*code_ptr; + + // move declaration out of label + if(label_op.get_statement()==ID_decl_block) + { + codet tmp; + tmp.swap(label_op); + label_op=codet(ID_skip); + + new_ops.move_to_operands(code_op); + + Forall_operands(it2, tmp) + if(it2->is_not_nil()) + new_ops.move_to_operands(*it2); + } + else + new_ops.move_to_operands(code_op); + } + else + new_ops.move_to_operands(code_op); + } + + code.operands().swap(new_ops.operands()); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_break + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_break(codet &code) +{ + if(!break_is_allowed) + { + err_location(code); + throw "break not allowed here"; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_continue + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_continue(codet &code) +{ + if(!continue_is_allowed) + { + err_location(code); + throw "continue not allowed here"; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_decl + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_decl_type(codet &code) +{ + assert(code.operands().size()==0); + // type only! +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_decl + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_decl(codet &code) +{ + // this may have 1 or 2 operands + + if(code.operands().size()!=1 && + code.operands().size()!=2) + { + err_location(code); + throw "decl expected to have 0-2 arguments"; + } + + // op0 must be symbol + if(code.op0().id()!=ID_symbol) + { + err_location(code); + throw "decl expected to have symbol as first operand"; + } + + // replace if needed + replace_symbol(code.op0()); + + // look it up + const irep_idt &identifier=code.op0().get(ID_identifier); + + contextt::symbolst::iterator s_it=context.symbols.find(identifier); + + if(s_it==context.symbols.end()) + { + err_location(code); + throw "failed to find decl symbol in context"; + } + + symbolt &symbol=s_it->second; + + // see if it's a typedef + // or a function + // or static + if(symbol.is_type || + symbol.type.id()==ID_code || + symbol.static_lifetime) + { + locationt location=code.location(); + code=code_skipt(); + code.location()=location; + return; + } + + code.location()=symbol.location; + + // check the initializer, if any + if(code.operands().size()==2) + { + typecheck_expr(code.op1()); + do_initializer(code.op1(), symbol.type, false); + + if(follow(symbol.type).id()==ID_incomplete_array) + symbol.type=code.op1().type(); + } + + // set type now (might be changed by initializer) + code.op0().type()=symbol.type; + + // this must not be an incomplete type + if(!is_complete_type(code.op0().type())) + { + err_location(code); + throw "incomplete type not permitted here"; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::is_complete_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_typecheck_baset::is_complete_type(const typet &type) const +{ + if(type.id()==ID_incomplete_struct || + type.id()==ID_incomplete_union || + type.id()==ID_incomplete_array) + return false; + else if(type.id()==ID_array) + return is_complete_type(type.subtype()); + else if(type.id()==ID_struct || type.id()==ID_union) + { + const struct_union_typet::componentst &components= + to_struct_union_type(type).components(); + for(struct_union_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + if(!is_complete_type(it->type())) + return false; + } + else if(type.id()==ID_vector) + return is_complete_type(type.subtype()); + else if(type.id()==ID_symbol) + return is_complete_type(follow(type)); + + return true; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expression + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expression(codet &code) +{ + if(code.operands().size()!=1) + throw "expression statement expected to have one operand"; + + exprt &op=code.op0(); + typecheck_expr(op); + + if(op.id()==ID_sideeffect) + { + const irep_idt &statement=op.get(ID_statement); + + if(statement==ID_assign) + { + assert(op.operands().size()==2); + + // pull assignment statements up + exprt::operandst operands; + operands.swap(op.operands()); + code.set_statement(ID_assign); + code.operands().swap(operands); + + if(code.op1().id()==ID_sideeffect && + code.op1().get(ID_statement)==ID_function_call) + { + assert(code.op1().operands().size()==2); + + code_function_callt function_call; + function_call.location().swap(code.op1().location()); + function_call.lhs()=code.op0(); + function_call.function()=code.op1().op0(); + function_call.arguments()=code.op1().op1().operands(); + code.swap(function_call); + } + } + else if(statement==ID_function_call) + { + assert(op.operands().size()==2); + + // pull function calls up + code_function_callt function_call; + function_call.location()=code.location(); + function_call.function()=op.op0(); + function_call.arguments()=op.op1().operands(); + code.swap(function_call); + } + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_for + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_for(codet &code) +{ + if(code.operands().size()!=4) + throw "for expected to have four operands"; + + // the "for" statement has an implicit block around it, + // since code.op0() may contain declarations + // + // we therefore transform + // + // for(a;b;c) d; + // + // to + // + // { a; for(;b;c) d; } + + if(code.op0().is_nil()) + { + if(code.op1().is_nil()) + code.op1().make_true(); + else + { + typecheck_expr(code.op1()); + implicit_typecast_bool(code.op1()); + } + + if(code.op2().is_not_nil()) + typecheck_expr(code.op2()); + + if(code.op3().is_not_nil()) + { + // save & set flags + bool old_break_is_allowed=break_is_allowed; + bool old_continue_is_allowed=continue_is_allowed; + + break_is_allowed=continue_is_allowed=true; + + // recursive call + typecheck_code(to_code(code.op3())); + + // restore flags + break_is_allowed=old_break_is_allowed; + continue_is_allowed=old_continue_is_allowed; + } + } + else + { + code_blockt code_block; + code_block.location()=code.location(); + + code_block.reserve_operands(2); + code_block.move_to_operands(code.op0()); + code.op0().make_nil(); + code_block.move_to_operands(code); + code.swap(code_block); + typecheck_code(code); // recursive call + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_label + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_label(code_labelt &code) +{ + if(code.operands().size()!=1) + { + err_location(code); + throw "label expected to have one operand"; + } + + typecheck_code(to_code(code.op0())); + + if(code.is_default()) + { + if(!case_is_allowed) + { + err_location(code); + throw "did not expect default label here"; + } + } + + if(code.find(ID_case).is_not_nil()) + { + if(!case_is_allowed) + { + err_location(code); + throw "did not expect `case' here"; + } + + exprt &case_expr=static_cast(code.add(ID_case)); + + Forall_operands(it, case_expr) + { + typecheck_expr(*it); + implicit_typecast(*it, switch_op_type); + } + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_goto + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_goto(codet &code) +{ +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_computed_goto + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_computed_goto(codet &code) +{ + if(code.operands().size()!=1) + { + err_location(code); + throw "computed-goto expected to have one operand"; + } + + exprt &dest=code.op0(); + + if(dest.id()!=ID_dereference) + { + err_location(dest); + throw "computed-goto expected to have dereferencing operand"; + } + + assert(dest.operands().size()==1); + + typecheck_expr(dest.op0()); + dest.type()=empty_typet(); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_ifthenelse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_ifthenelse(code_ifthenelset &code) +{ + if(code.operands().size()!=2 && + code.operands().size()!=3) + throw "ifthenelse expected to have two or three operands"; + + exprt &cond=code.cond(); + + typecheck_expr(cond); + + #if 0 + if(cond.id()==ID_sideeffect && + cond.get(ID_statement)==ID_assign) + { + err_location(cond); + warning("warning: assignment in if condition"); + } + #endif + + implicit_typecast_bool(cond); + + typecheck_code(to_code(code.then_case())); + + if(code.operands().size()==3 && + !code.else_case().is_nil()) + typecheck_code(to_code(code.else_case())); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_start_thread + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_start_thread(codet &code) +{ + if(code.operands().size()!=1) + throw "start_thread expected to have one operand"; + + typecheck_code(to_code(code.op0())); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_return + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_return(codet &code) +{ + if(code.operands().size()==0) + { + if(return_type.id()!=ID_empty) + { + err_location(code); + throw "function expected to return a value"; + } + } + else if(code.operands().size()==1) + { + typecheck_expr(code.op0()); + + if(return_type.id()==ID_empty) + { + if(code.op0().type().id()!=ID_empty) + { + err_location(code); + throw "function not expected to return a value"; + } + } + else + implicit_typecast(code.op0(), return_type); + } + else + { + err_location(code); + throw "return expected to have 0 or 1 operands"; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_switch + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_switch(codet &code) +{ + if(code.operands().size()!=2) + throw "switch expects two operands"; + + typecheck_expr(code.op0()); + + // this needs to be promoted + implicit_typecast_arithmetic(code.op0()); + + // save & set flags + + bool old_case_is_allowed(case_is_allowed); + bool old_break_is_allowed(break_is_allowed); + typet old_switch_op_type(switch_op_type); + + switch_op_type=code.op0().type(); + break_is_allowed=case_is_allowed=true; + + typecheck_code(to_code(code.op1())); + + // restore flags + case_is_allowed=old_case_is_allowed; + break_is_allowed=old_break_is_allowed; + switch_op_type=old_switch_op_type; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_while + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_while(codet &code) +{ + if(code.operands().size()!=2) + throw "while expected to have two operands"; + + typecheck_expr(code.op0()); + implicit_typecast_bool(code.op0()); + + // save & set flags + bool old_break_is_allowed(break_is_allowed); + bool old_continue_is_allowed(continue_is_allowed); + + break_is_allowed=continue_is_allowed=true; + + typecheck_code(to_code(code.op1())); + + // restore flags + break_is_allowed=old_break_is_allowed; + continue_is_allowed=old_continue_is_allowed; +} diff --git a/src/ansi-c/c_typecheck_expr.cpp b/src/ansi-c/c_typecheck_expr.cpp new file mode 100644 index 00000000000..c46373d0f6b --- /dev/null +++ b/src/ansi-c/c_typecheck_expr.cpp @@ -0,0 +1,2687 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c_types.h" +#include "c_typecast.h" +#include "c_typecheck_base.h" +#include "c_sizeof.h" +#include "string_constant.h" + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr(exprt &expr) +{ + if(expr.id()=="already_typechecked") + { + assert(expr.operands().size()==1); + exprt tmp; + tmp.swap(expr.op0()); + expr.swap(tmp); + return; + } + + // fist do sub-nodes + typecheck_expr_operands(expr); + + // now do case-split + typecheck_expr_main(expr); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_main(exprt &expr) +{ + if(expr.id()==ID_sideeffect) + typecheck_expr_side_effect(to_side_effect_expr(expr)); + else if(expr.id()==ID_constant) + typecheck_expr_constant(expr); + else if(expr.id()==ID_infinity) + { + // ignore + } + else if(expr.id()==ID_symbol) + typecheck_expr_symbol(expr); + else if(expr.id()==ID_unary_plus || + expr.id()==ID_unary_minus || + expr.id()==ID_bitnot) + typecheck_expr_unary_arithmetic(expr); + else if(expr.id()==ID_not) + typecheck_expr_unary_boolean(expr); + else if(expr.id()==ID_and || expr.id()==ID_or) + typecheck_expr_binary_boolean(expr); + else if(expr.id()==ID_address_of) + typecheck_expr_address_of(expr); + else if(expr.id()==ID_dereference) + typecheck_expr_dereference(expr); + else if(expr.id()==ID_member) + typecheck_expr_member(expr); + else if(expr.id()==ID_ptrmember) + typecheck_expr_ptrmember(expr); + else if(expr.id()==ID_equal || + expr.id()==ID_notequal || + expr.id()==ID_lt || + expr.id()==ID_le || + expr.id()==ID_gt || + expr.id()==ID_ge) + typecheck_expr_rel(expr); + else if(expr.id()==ID_index) + typecheck_expr_index(expr); + else if(expr.id()==ID_typecast) + typecheck_expr_typecast(expr); + else if(expr.id()==ID_sizeof) + typecheck_expr_sizeof(expr); + else if(expr.id()==ID_builtin_alignof) + typecheck_expr_alignof(expr); + else if(expr.id()==ID_plus || expr.id()==ID_minus || + expr.id()==ID_mult || expr.id()==ID_div || + expr.id()==ID_mod || + expr.id()==ID_shl || expr.id()==ID_shr || + expr.id()==ID_bitand || expr.id()==ID_bitxor || expr.id()==ID_bitor) + typecheck_expr_binary_arithmetic(expr); + else if(expr.id()==ID_comma) + typecheck_expr_comma(expr); + else if(expr.id()==ID_if) + typecheck_expr_trinary(to_if_expr(expr)); + else if(expr.id()==ID_code) + { + err_location(expr); + str << "typecheck_expr_main got code: " << expr.pretty(); + throw 0; + } + else if(expr.id()==ID_gcc_builtin_va_arg) + typecheck_expr_builtin_va_arg(expr); + else if(expr.id()==ID_gcc_builtin_types_compatible_p) + { + expr.type()=bool_typet(); + typet::subtypest &subtypes=((typet &)(expr)).subtypes(); + assert(subtypes.size()==2); + typecheck_type(subtypes[0]); + typecheck_type(subtypes[1]); + locationt location=expr.location(); + expr.make_bool(follow(subtypes[0])==follow(subtypes[1])); + expr.location()=location; + } + else if(expr.id()==ID_builtin_offsetof) + typecheck_expr_builtin_offsetof(expr); + else if(expr.id()==ID_string_constant) + { + // already fine -- mark as lvalue + expr.set(ID_C_lvalue, true); + } + else if(expr.id()==ID_arguments) + { + // already fine + } + else if(expr.id()==ID_designated_initializer) + { + exprt &designator=static_cast(expr.add(ID_designator)); + + Forall_operands(it, designator) + { + if(it->id()==ID_index) + { + assert(it->operands().size()==1); + typecheck_expr(it->op0()); // still needs typechecking + } + } + } + else if(expr.id()==ID_initializer_list) + { + // already fine, just set some type + expr.type()=empty_typet(); + } + else if(expr.id()==ID_forall || expr.id()==ID_exists) + { + assert(expr.operands().size()==2); + typecheck_expr_main(expr.op1()); + expr.type()=bool_typet(); + } + else if(expr.id()==ID_label) + { + expr.type()=empty_typet(); + } + else if(expr.id()==ID_array) + { + // these pop up as string constants, and are already typed + } + else + { + err_location(expr); + str << "unexpected expression: " << expr.pretty(); + throw 0; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_comma + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_comma(exprt &expr) +{ + if(expr.operands().size()!=2) + { + err_location(expr); + str << "comma operator expects two operands"; + throw 0; + } + + expr.type()=expr.op1().type(); + + // make this an l-value if the last operand is one + if(expr.op1().get_bool(ID_C_lvalue)) + expr.set(ID_C_lvalue, true); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_builtin_va_arg + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_builtin_va_arg(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + str << "builtin_va_arg expects one operand"; + throw 0; + } + + typecheck_type(expr.type()); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_builtin_offsetof + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_builtin_offsetof(exprt &expr) +{ + // these need not be constant, due to array indices! + + if(expr.operands().size()!=0) + { + err_location(expr); + throw "builtin_offsetof expects no operands"; + } + + typet &type=static_cast(expr.add(ID_type_arg)); + typecheck_type(type); + + exprt &member=static_cast(expr.add(ID_designator)); + + exprt result=gen_zero(size_type()); + + forall_operands(m_it, member) + { + if(type.id()==ID_symbol) + type=follow(type); + + if(m_it->id()==ID_member) + { + if(type.id()!=ID_union && type.id()!=ID_struct) + { + err_location(expr); + throw "offsetof of member expects struct/union type, " + "but got `"+to_string(type)+"'"; + } + + bool found=false; + irep_idt component_name=m_it->get(ID_component_name); + + while(!found) + { + assert(type.id()==ID_union || type.id()==ID_struct); + + const struct_union_typet &struct_union_type= + to_struct_union_type(type); + + if(!has_component_rec(struct_union_type, component_name)) + { + err_location(expr); + throw "offset-of of member failed to find component `"+ + id2string(component_name)+"' in `"+to_string(type)+"'"; + } + + const struct_union_typet::componentst &components= + struct_union_type.components(); + + for(struct_union_typet::componentst::const_iterator + c_it=components.begin(); + c_it!=components.end(); + c_it++) + { + if(c_it->get_name()==component_name) + { + typet tmp=follow(c_it->type()); + type=tmp; + found=true; + break; + } + else if(c_it->get_anonymous()) + { + if(has_component_rec(c_it->type(), component_name)) + { + typet tmp=follow(c_it->type()); + type=tmp; + assert(type.id()==ID_union || type.id()==ID_struct); + break; // we run into another iteration of the outer loop + } + } + + if(type.id()==ID_struct) + { + exprt sub_size=c_sizeof(c_it->type(), *this); + + if(sub_size.is_nil()) + { + err_location(expr); + str << "offsetof failed to determine size of `" + << to_string(c_it->type()) << "'"; + throw 0; + } + + if(sub_size.type()!=size_type()) + sub_size.make_typecast(size_type()); + + result=plus_exprt(result, sub_size); + } + } + } + } + else if(m_it->id()==ID_index) + { + assert(m_it->operands().size()==1); + + if(type.id()!=ID_array && + type.id()!=ID_incomplete_array) + { + err_location(expr); + throw "offsetof of index expects array type"; + } + + exprt index=m_it->op0(); + exprt sub_size=c_sizeof(type.subtype(), *this); + if(index.type()!=size_type()) index.make_typecast(size_type()); + result=plus_exprt(result, mult_exprt(sub_size, index)); + + typet tmp=type.subtype(); + type=tmp; + } + } + + simplify(result, *this); + result.location()=expr.location(); + + expr.swap(result); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_operands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_operands(exprt &expr) +{ + if(expr.id()==ID_sideeffect && + expr.get(ID_statement)==ID_function_call) + { + // don't do function operand + assert(expr.operands().size()==2); + + typecheck_expr(expr.op1()); // arguments + } + else if(expr.id()==ID_sideeffect && + expr.get(ID_statement)==ID_statement_expression) + { + typecheck_code(to_code(expr.op0())); + } + else + { + Forall_operands(it, expr) + typecheck_expr(*it); + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_symbol(exprt &expr) +{ + // adjust identifier, if needed + replace_symbol(expr); + + const irep_idt &identifier=to_symbol_expr(expr).get_identifier(); + + // look it up + const symbolt *symbol_ptr; + if(lookup(identifier, symbol_ptr)) + { + err_location(expr); + str << "failed to find symbol `" << identifier << "'"; + throw 0; + } + + const symbolt &symbol=*symbol_ptr; + + if(symbol.is_type) + { + err_location(expr); + str << "did not expect a type symbol here, but got `" + << symbol.display_name() << "'"; + throw 0; + } + + // save location + locationt location=expr.location(); + + if(symbol.is_macro) + { + expr=symbol.value; + + // put it back + expr.location()=location; + } + else if(has_prefix(id2string(identifier), CPROVER_PREFIX "constant_infinity")) + { + expr=exprt(ID_infinity, symbol.type); + + // put it back + expr.location()=location; + } + else if(identifier=="c::__func__" || + identifier=="c::__FUNCTION__" || + identifier=="c::__PRETTY_FUNCTION__") + { + // __func__ is an ANSI-C standard compliant hack to get the function name + // __FUNCTION__ and __PRETTY_FUNCTION__ are GCC-specific + string_constantt s; + s.set_value(location.get_function()); + s.location()=location; + expr.swap(s); + } + else + { + expr=symbol_expr(symbol); + + // put it back + expr.location()=location; + + if(symbol.lvalue) + expr.set(ID_C_lvalue, true); + + if(expr.type().id()==ID_code) // function designator + { // special case: this is sugar for &f + exprt tmp(ID_address_of, pointer_typet()); + tmp.set("#implicit", true); + tmp.type().subtype()=expr.type(); + tmp.location()=expr.location(); + tmp.move_to_operands(expr); + expr.swap(tmp); + } + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_side_effect_statement_expression + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_side_effect_statement_expression( + side_effect_exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + str << "statement expression expects one operand"; + throw 0; + } + + codet &code=to_code(expr.op0()); + + assert(code.get(ID_statement)==ID_block); + + // the type is the type of the last statement in the + // block, but do worry about labels! + + codet *last=&code; + + while(true) + { + const irep_idt &statement=last->get_statement(); + + if(statement==ID_block) + { + if(last->operands().size()==0) + { + expr.type()=typet(ID_empty); + return; + } + + last=&to_code(last->operands().back()); + } + else if(statement==ID_label) + { + assert(last->operands().size()==1); + last=&(to_code(last->op0())); + } + else + break; + } + + irep_idt last_statement=last->get_statement(); + + if(last_statement==ID_expression) + { + assert(last->operands().size()==1); + expr.type()=last->op0().type(); + } + else if(last_statement==ID_function_call) + { + // make the last statement an expression + + code_function_callt &fc=to_code_function_call(*last); + + side_effect_expr_function_callt sideeffect; + + sideeffect.function()=fc.function(); + sideeffect.arguments()=fc.arguments(); + sideeffect.location()=fc.location(); + + sideeffect.type()= + static_cast(fc.function().type().find(ID_return_type)); + + expr.type()=sideeffect.type(); + + if(fc.lhs().is_nil()) + { + codet code_expr(ID_expression); + code_expr.location() = fc.location(); + code_expr.move_to_operands(sideeffect); + last->swap(code_expr); + } + else + { + codet code_expr(ID_expression); + code_expr.location() = fc.location(); + + exprt assign(ID_sideeffect); + assign.set(ID_statement, ID_assign); + assign.location()=fc.location(); + assign.move_to_operands(fc.lhs(), sideeffect); + assign.type()=assign.op1().type(); + + code_expr.move_to_operands(assign); + last->swap(code_expr); + } + } + else + expr.type()=typet(ID_empty); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_sizeof + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_sizeof(exprt &expr) +{ + typet type; + + if(expr.operands().size()==0) + { + type.swap(static_cast(expr.add(ID_type_arg))); + typecheck_type(type); + } + else if(expr.operands().size()==1) + { + type.swap(expr.op0().type()); + } + else + { + err_location(expr); + str << "sizeof operator expects zero or one operand, " + "but got " << expr.operands().size(); + throw 0; + } + + exprt new_expr=c_sizeof(type, *this); + + if(new_expr.is_nil()) + { + err_location(expr); + str << "type has no size: " + << to_string(type); + throw 0; + } + + new_expr.swap(expr); + + expr.add("#c_sizeof_type")=type; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_alignof + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_alignof(exprt &expr) +{ + typet argument_type; + + if(expr.operands().size()==1) + argument_type=expr.op0().type(); + else + { + typet &op_type=static_cast(expr.add(ID_type_arg)); + typecheck_type(op_type); + argument_type=op_type; + } + + // we only care about the type + unsigned a=alignment(argument_type); + + exprt tmp=from_integer(a, size_type()); + tmp.location()=expr.location(); + + expr.swap(tmp); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_typecast(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + error("typecast operator expects one operand"); + throw 0; + } + + exprt &op=expr.op0(); + + typecheck_type(expr.type()); + + const typet expr_type=follow(expr.type()); + + if(expr_type.id()==ID_struct) + { + // typecast to struct: this is a GCC extension + if(op.id()!=ID_initializer_list) + { + err_location(expr); + str << "type cast to struct requires initializer_list argument"; + throw 0; + } + + // just do a normal initialization + do_initializer(op, expr_type, false); + + // just remove typecast + exprt tmp=op; + expr=tmp; + + return; + } + else if(expr_type.id()==ID_union) + { + // this is a GCC extension. It's either a 'temporary union', + // where the argument is expected to be a 'initializer_list', + // or a cast from one of the member types + + if(op.id()==ID_initializer_list) + { + // just do a normal initialization + do_initializer(op, expr_type, false); + + // just remove typecast + exprt tmp=op; + expr=tmp; + + return; + } + + // we need to find a member with the right type + const union_typet &union_type=to_union_type(expr_type); + const union_typet::componentst &components=union_type.components(); + + for(union_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(base_type_eq(it->type(), op.type(), *this)) + { + // found! build union constructor + union_exprt union_expr(union_type); + union_expr.location()=expr.location(); + union_expr.op()=op; + union_expr.set_component_name(it->get_name()); + expr=union_expr; + return; + } + } + + err_location(expr); + str << "type cast to union either requires initializer list " + "argument or an expression with a member type"; + throw 0; + } + else if(expr_type.id()==ID_array || + expr_type.id()==ID_incomplete_array) + { + // this is a GCC extension called 'array constructor' + // the argument is expected to be an 'initializer_list' + if(op.id()!=ID_initializer_list) + { + err_location(expr); + str << "type cast to array requires initializer_list " + "argument"; + throw 0; + } + + // just do a normal initialization + do_initializer(op, expr_type, false); + + // just remove typecast + exprt tmp=op; + expr=tmp; + + return; + } + + // a cast to void is always fine + if(expr_type.id()==ID_empty) + return; + + if(!is_number(expr_type) && + expr_type.id()!=ID_bool && + expr_type.id()!=ID_pointer && + expr_type.id()!=ID_array && + expr_type.id()!=ID_c_enum && + expr_type.id()!=ID_incomplete_c_enum) + { + err_location(expr); + str << "type cast to `" + << to_string(expr.type()) << "' not permitted"; + throw 0; + } + + const typet op_type=follow(op.type()); + + if(is_number(op_type) || + op_type.id()==ID_c_enum || + op_type.id()==ID_incomplete_c_enum || + op_type.id()==ID_bool || + op_type.id()==ID_pointer) + { + } + else if(op_type.id()==ID_array || + op_type.id()==ID_incomplete_array) + { + index_exprt index; + index.array()=op; + index.index()=gen_zero(index_type()); + index.type()=op_type.subtype(); + op=gen_address_of(index); + } + else if(op_type.id()==ID_empty) + { + if(expr_type.id()!=ID_empty) + { + err_location(expr); + str << "type cast from void only permitted to void, but got `" + << to_string(expr.type()) << "'"; + throw 0; + } + } + else + { + err_location(expr); + str << "type cast from `" + << to_string(op_type) << "' not permitted"; + throw 0; + } + + // special case: NULL + + if(expr_type.id()==ID_pointer && + op.is_zero()) + { + // zero typecasted to a pointer is NULL + expr.id(ID_constant); + expr.set(ID_value, ID_NULL); + expr.remove(ID_operands); + return; + } + + // the new thing is an lvalue if the previous one is + // an lvalue, and it's just a pointer type cast + // this isn't really standard conformant! + if(expr.op0().get_bool(ID_C_lvalue)) + { + if(expr_type.id()==ID_pointer) + expr.set(ID_C_lvalue, true); + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::make_index_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::make_index_type(exprt &expr) +{ + implicit_typecast(expr, index_type()); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_index(exprt &expr) +{ + if(expr.operands().size()!=2) + { + err_location(expr); + str << "operator `" << expr.id() + << "' expects two operands"; + throw 0; + } + + exprt &array_expr=expr.op0(); + exprt &index_expr=expr.op1(); + + // we might have to swap them + + { + const typet &array_full_type=follow(array_expr.type()); + const typet &index_full_type=follow(index_expr.type()); + + if(array_full_type.id()!=ID_array && + array_full_type.id()!=ID_incomplete_array && + array_full_type.id()!=ID_pointer && + (index_full_type.id()==ID_array || + index_full_type.id()==ID_incomplete_array || + index_full_type.id()==ID_pointer)) + std::swap(array_expr, index_expr); + } + + make_index_type(index_expr); + + const typet &final_array_type=follow(array_expr.type()); + + if(final_array_type.id()==ID_array || + final_array_type.id()==ID_incomplete_array) + { + if(array_expr.get_bool(ID_C_lvalue)) + expr.set(ID_C_lvalue, true); + } + else if(final_array_type.id()==ID_pointer) + { + // p[i] is syntactic sugar for *(p+i) + + typecheck_arithmetic_pointer(expr.op0()); + exprt addition(ID_plus, array_expr.type()); + addition.operands().swap(expr.operands()); + expr.move_to_operands(addition); + expr.id(ID_dereference); + expr.set(ID_C_lvalue, true); + } + else + { + err_location(expr); + str << "operator [] must take array or pointer but got `" + << to_string(array_expr.type()) << "'"; + throw 0; + } + + expr.type()=final_array_type.subtype(); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::adjust_float_rel + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::adjust_float_rel(exprt &expr) +{ + // equality and disequality on float is not mathematical equality! + assert(expr.operands().size()==2); + + if(follow(expr.op0().type()).id()==ID_floatbv) + { + if(expr.id()==ID_equal) + expr.id(ID_ieee_float_equal); + else if(expr.id()==ID_notequal) + expr.id(ID_ieee_float_notequal); + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_rel + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_rel(exprt &expr) +{ + expr.type()=typet(ID_bool); + + if(expr.operands().size()!=2) + { + err_location(expr); + str << "operator `" << expr.id() + << "' expects two operands"; + throw 0; + } + + exprt &op0=expr.op0(); + exprt &op1=expr.op1(); + + const typet o_type0=op0.type(); + const typet o_type1=op1.type(); + + if(expr.id()==ID_equal || expr.id()==ID_notequal) + { + if(follow(o_type0)==follow(o_type1)) + { + const typet &final_type=follow(o_type0); + if(final_type.id()!=ID_array && + final_type.id()!=ID_incomplete_array && + final_type.id()!=ID_incomplete_struct) + { + adjust_float_rel(expr); + return; // no promotion necessary + } + } + } + + implicit_typecast_arithmetic(op0, op1); + + const typet &type0=op0.type(); + const typet &type1=op1.type(); + + if(type0==type1) + { + if(is_number(type0)) + { + adjust_float_rel(expr); + return; + } + + if(type0.id()==ID_pointer) + { + if(expr.id()==ID_equal || expr.id()==ID_notequal) + return; + + if(expr.id()==ID_le || expr.id()==ID_lt || + expr.id()==ID_ge || expr.id()==ID_gt) + return; + } + + if(type0.id()==ID_string_constant) + { + if(expr.id()==ID_equal || expr.id()==ID_notequal) + return; + } + } + else + { + // pointer and zero + if(type0.id()==ID_pointer && op1.is_zero()) + { + op1=constant_exprt(type0); + op1.set(ID_value, ID_NULL); + return; + } + + if(type1.id()==ID_pointer && op0.is_zero()) + { + op0=constant_exprt(type1); + op0.set(ID_value, ID_NULL); + return; + } + + // pointer and integer + if(type0.id()==ID_pointer && is_number(type1)) + { + op1.make_typecast(type0); + return; + } + + if(type1.id()==ID_pointer && is_number(type0)) + { + op0.make_typecast(type1); + return; + } + + if(type0.id()==ID_pointer && type1.id()==ID_pointer) + { + op1.make_typecast(type0); + return; + } + } + + err_location(expr); + str << "operator `" << expr.id() + << "' not defined for types `" + << to_string(o_type0) << "' and `" + << to_string(o_type1) << "'"; + throw 0; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_ptrmember + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_ptrmember(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + error("ptrmember operator expects one operand"); + throw 0; + } + + const typet &final_op0_type=follow(expr.op0().type()); + + if(final_op0_type.id()!=ID_pointer && + final_op0_type.id()!=ID_array) + { + err_location(expr); + str << "ptrmember operator requires pointer type " + "on left hand side, but got `" + << to_string(expr.op0().type()) << "'"; + throw 0; + } + + // turn x->y into (*x).y + + exprt deref(ID_dereference); + deref.move_to_operands(expr.op0()); + deref.location()=expr.location(); + + typecheck_expr_dereference(deref); + + expr.op0().swap(deref); + + expr.id(ID_member); + typecheck_expr_member(expr); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool c_typecheck_baset::has_component_rec( + const typet &type, + const irep_idt &component_name) +{ + if(type.id()==ID_symbol) + return has_component_rec(follow(type), component_name); + else if(type.id()==ID_struct || type.id()==ID_union) + { + const struct_union_typet &struct_union_type= + to_struct_union_type(type); + + const struct_union_typet::componentst &components= + struct_union_type.components(); + + for(struct_union_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it->get_name()==component_name) + return true; + else if(it->get_anonymous()) + { + if(has_component_rec(it->type(), component_name)) + return true; + } + } + + return false; + } + else + return false; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_member(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + error("member operator expects one operand"); + throw 0; + } + + exprt &op0=expr.op0(); + typet type=op0.type(); + + follow_symbol(type); + + if(type.id()==ID_incomplete_struct) + { + err_location(expr); + str << "member operator got incomplete structure type " + "on left hand side"; + throw 0; + } + + if(type.id()!=ID_struct && + type.id()!=ID_union) + { + err_location(expr); + str << "member operator requires structure type " + "on left hand side but got `" + << to_string(type) << "'"; + throw 0; + } + + const struct_union_typet &struct_union_type= + to_struct_union_type(type); + + const irep_idt &component_name= + expr.get(ID_component_name); + + const struct_union_typet::componentst &components= + struct_union_type.components(); + + struct_union_typet::componentt component; + + component.make_nil(); + + // first try to find directly + + for(struct_union_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it->get_name()==component_name) + { + component=*it; + break; + } + } + + // if that fails, search the anonymous members + + if(component.is_nil()) + { + for(struct_union_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + // look into anonymous members, possibly recursively + if(it->get_anonymous() && + has_component_rec(it->type(), component_name)) + { + // re-write, this is a bit wasteful + member_exprt tmp; + tmp.set_component_name(it->get_name()); + tmp.op0()=expr.op0(); + typecheck_expr_member(tmp); + expr.op0().swap(tmp); + typecheck_expr_member(expr); + return; + } + } + + // give up + err_location(expr); + str << "member `" << component_name + << "' not found in `" + << to_string(type) << "'"; + throw 0; + } + + const irep_idt &access=component.get_access(); + + if(access==ID_private) + { + err_location(expr); + str << "member `" << component_name + << "' is " << access; + throw 0; + } + + expr.type()=component.type(); + + if(op0.get_bool(ID_C_lvalue)) + expr.set(ID_C_lvalue, true); + + if(op0.get_bool(ID_C_constant)) + expr.set(ID_C_constant, true); + + // copy method identifier + const irep_idt &identifier=component.get(ID_C_identifier); + + if(identifier!=irep_idt()) + expr.set(ID_C_identifier, identifier); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_trinary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_trinary(if_exprt &expr) +{ + exprt::operandst &operands=expr.operands(); + + assert(operands.size()==3); + + // copy (save) original types + const typet o_type0=operands[0].type(); + const typet o_type1=operands[1].type(); + const typet o_type2=operands[2].type(); + + implicit_typecast_bool(operands[0]); + implicit_typecast_arithmetic(operands[1], operands[2]); + + if(operands[1].type().id()==ID_pointer && + operands[2].type().id()!=ID_pointer) + implicit_typecast(operands[2], operands[1].type()); + else if(operands[2].type().id()==ID_pointer && + operands[1].type().id()!=ID_pointer) + implicit_typecast(operands[1], operands[2].type()); + + if(operands[1].type().id()==ID_pointer && + operands[2].type().id()==ID_pointer && + operands[1].type()!=operands[2].type()) + { + // make it void * + expr.type()=typet(ID_pointer); + expr.type().subtype()=typet(ID_empty); + implicit_typecast(operands[1], expr.type()); + implicit_typecast(operands[2], expr.type()); + } + + if(follow(operands[1].type())==follow(operands[2].type())) + { + expr.type()=operands[1].type(); + return; + } + + if(operands[1].type().id()==ID_empty || + operands[2].type().id()==ID_empty) + { + expr.type()=empty_typet(); + return; + } + + err_location(expr); + str << "operator ?: not defined for types `" + << to_string(o_type1) << "' and `" + << to_string(o_type2) << "'"; + throw 0; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_side_effect_gcc_conditional_expresssion + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_side_effect_gcc_conditional_expression( + side_effect_exprt &expr) +{ + // x ? : y is almost the same as x ? x : y, + // but not quite, as x is evaluated only once + + exprt::operandst &operands=expr.operands(); + + if(operands.size()!=2) + { + err_location(expr); + error("gcc conditional_expr expects two operands"); + throw 0; + } + + // use typechecking code for "if" + + if_exprt if_expr; + if_expr.cond()=operands[0]; + if_expr.true_case()=operands[0]; + if_expr.false_case()=operands[1]; + if_expr.location()=expr.location(); + + typecheck_expr_trinary(if_expr); + + // copy the result + expr.op0()=if_expr.op1(); + expr.op1()=if_expr.op2(); + expr.type()=if_expr.type(); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_address_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_address_of(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + error("unary operator & expects one operand"); + throw 0; + } + + exprt &op=expr.op0(); + + // special case: address of label + if(op.id()==ID_label) + { + expr.type()=pointer_typet(empty_typet()); + return; + } + + // special case: address of function designator + // ANSI-C 99 section 6.3.2.1 paragraph 4 + + if(op.id()==ID_address_of && + op.get_bool(ID_C_implicit) && + op.operands().size()==1 && + op.op0().id()==ID_symbol && + op.op0().type().id()==ID_code) + { + // make the implicit address_of an explicit address_of + exprt tmp; + tmp.swap(op); + tmp.set(ID_C_implicit, false); + expr.swap(tmp); + return; + } + + expr.type()=pointer_typet(); + + if(op.id()==ID_struct || + op.id()==ID_union || + op.id()==ID_array || + op.id()==ID_string_constant) + { + // these are really objects + } + else if(op.get_bool(ID_C_lvalue)) + { + // ok + } + else if(op.type().id()==ID_code) + { + // ok + } + else + { + err_location(expr); + str << "address_of error: `" << to_string(op) + << "' not an lvalue"; + throw 0; + } + + expr.type().subtype()=op.type(); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_dereference(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + str << "unary operator * expects one operand"; + throw 0; + } + + exprt &op=expr.op0(); + + const typet op_type=follow(op.type()); + + if(op_type.id()==ID_array || + op_type.id()==ID_incomplete_array) + { + // *a is the same as a[0] + expr.id(ID_index); + expr.type()=op_type.subtype(); + expr.copy_to_operands(gen_zero(index_type())); + assert(expr.operands().size()==2); + } + else if(op_type.id()==ID_pointer) + { + if(op_type.subtype().id()==ID_empty) + { + err_location(expr); + warning("operand of unary * is a void pointer"); + } + + expr.type()=op_type.subtype(); + } + else + { + err_location(expr); + str << "operand of unary * `" << to_string(op) + << "' is not a pointer, but got `" + << to_string(op_type) << "'"; + throw 0; + } + + expr.set(ID_C_lvalue, true); + + // if you dereference a pointer pointing to + // a function, you get a pointer again + // allowing ******...*p + + typecheck_expr_function_identifier(expr); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_function_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_function_identifier(exprt &expr) +{ + if(expr.type().id()==ID_code) + { + exprt tmp(ID_address_of, pointer_typet()); + tmp.set(ID_C_implicit, true); + tmp.type().subtype()=expr.type(); + tmp.location()=expr.location(); + tmp.move_to_operands(expr); + expr.swap(tmp); + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_side_effect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_side_effect(side_effect_exprt &expr) +{ + const irep_idt &statement=expr.get_statement(); + + if(statement==ID_preincrement || + statement==ID_predecrement || + statement==ID_postincrement || + statement==ID_postdecrement) + { + if(expr.operands().size()!=1) + { + err_location(expr); + str << statement << "operator expects one operand"; + } + + const exprt &op0=expr.op0(); + const typet &type0=op0.type(); + const typet &final_type0=follow(type0); + + if(!op0.get_bool(ID_C_lvalue)) + { + err_location(op0); + str << "prefix operator error: `" << to_string(op0) + << "' not an lvalue"; + throw 0; + } + + if(type0.get_bool(ID_C_constant)) + { + err_location(op0); + str << "error: `" << to_string(op0) + << "' is constant"; + throw 0; + } + + if(is_number(final_type0) || + final_type0.id()==ID_bool || + final_type0.id()==ID_c_enum || + final_type0.id()==ID_incomplete_c_enum) + { + expr.type()=type0; + } + else if(final_type0.id()==ID_pointer) + { + expr.type()=type0; + typecheck_arithmetic_pointer(op0); + } + else + { + err_location(expr); + str << "operator `" << statement + << "' not defined for type `" + << to_string(type0) << "'"; + throw 0; + } + } + else if(has_prefix(id2string(statement), "assign")) + typecheck_side_effect_assignment(expr); + else if(statement==ID_function_call) + typecheck_side_effect_function_call(to_side_effect_expr_function_call(expr)); + else if(statement==ID_statement_expression) + typecheck_side_effect_statement_expression(expr); + else if(statement==ID_gcc_conditional_expression) + typecheck_side_effect_gcc_conditional_expression(expr); + else + { + err_location(expr); + str << "unknown side effect: " << statement; + throw 0; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_side_effect_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_side_effect_function_call( + side_effect_expr_function_callt &expr) +{ + if(expr.operands().size()!=2) + { + err_location(expr); + throw "function_call side effect expects two operands"; + } + + exprt &f_op=expr.function(); + + // f_op is not yet typechecked, in contrast to the other arguments + // this is a big special case + + if(f_op.id()==ID_symbol) + { + replace_symbol(f_op); + + if(context.symbols.find(f_op.get(ID_identifier))==context.symbols.end()) + { + // maybe this is an undeclared function + // let's just add it + const irep_idt &identifier=f_op.get(ID_identifier); + + symbolt new_symbol; + + new_symbol.name=identifier; + new_symbol.base_name=std::string(id2string(identifier), 3, std::string::npos); + new_symbol.location=expr.location(); + new_symbol.type=code_typet(); + new_symbol.type.set("#incomplete", true); + new_symbol.type.add(ID_return_type)=int_type(); + // TODO: should add arguments + + symbolt *symbol_ptr; + move_symbol(new_symbol, symbol_ptr); + + err_location(f_op); + str << "function `" << identifier << "' is not declared"; + warning(); + } + } + + // typecheck it now + typecheck_expr(f_op); + + const typet f_op_type=follow(f_op.type()); + + if(f_op_type.id()!=ID_pointer) + { + err_location(f_op); + str << "expected function/function pointer as argument but got `" + << to_string(f_op_type) << "'"; + throw 0; + } + + // do implicit dereference + if(f_op.id()==ID_address_of && + f_op.get_bool(ID_C_implicit) && + f_op.operands().size()==1) + { + exprt tmp; + tmp.swap(f_op.op0()); + f_op.swap(tmp); + } + else + { + exprt tmp(ID_dereference, f_op_type.subtype()); + tmp.set(ID_C_implicit, true); + tmp.location()=f_op.location(); + tmp.move_to_operands(f_op); + f_op.swap(tmp); + } + + if(f_op.type().id()!=ID_code) + { + err_location(f_op); + throw "expected code as argument"; + } + + const code_typet &code_type=to_code_type(f_op.type()); + + expr.type()=code_type.return_type(); + + typecheck_function_call_arguments(expr); + + do_special_functions(expr); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::do_special_functions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::do_special_functions( + side_effect_expr_function_callt &expr) +{ + const exprt &f_op=expr.function(); + + // some built-in functions + if(f_op.id()==ID_symbol) + { + const irep_idt &identifier=to_symbol_expr(f_op).get_identifier(); + + if(identifier==CPROVER_PREFIX "same_object") + { + if(expr.arguments().size()!=2) + { + err_location(f_op); + throw "same_object expects two operands"; + } + + exprt same_object_expr("same-object", bool_typet()); + same_object_expr.operands()=expr.arguments(); + expr.swap(same_object_expr); + } + else if(identifier==CPROVER_PREFIX "buffer_size") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "buffer_size expects one operand"; + } + + exprt buffer_size_expr("buffer_size", uint_type()); + buffer_size_expr.operands()=expr.arguments(); + expr.swap(buffer_size_expr); + } + else if(identifier==CPROVER_PREFIX "is_zero_string") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "is_zero_string expects one operand"; + } + + exprt is_zero_string_expr("is_zero_string", bool_typet()); + is_zero_string_expr.operands()=expr.arguments(); + is_zero_string_expr.set(ID_C_lvalue, true); // make it an lvalue + expr.swap(is_zero_string_expr); + } + else if(identifier==CPROVER_PREFIX "zero_string_length") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "zero_string_length expects one operand"; + } + + exprt zero_string_length_expr("zero_string_length", uint_type()); + zero_string_length_expr.operands()=expr.arguments(); + zero_string_length_expr.set(ID_C_lvalue, true); // make it an lvalue + expr.swap(zero_string_length_expr); + } + else if(identifier==CPROVER_PREFIX "DYNAMIC_OBJECT") + { + if(expr.arguments().size()!=1) + throw "dynamic_object expects one argument"; + + exprt dynamic_object_expr=exprt(ID_dynamic_object, expr.type()); + dynamic_object_expr.operands()=expr.arguments(); + expr.swap(dynamic_object_expr); + } + else if(identifier==CPROVER_PREFIX "POINTER_OFFSET") + { + if(expr.arguments().size()!=1) + throw "pointer_offset expects one argument"; + + exprt pointer_offset_expr=exprt(ID_pointer_offset, expr.type()); + pointer_offset_expr.operands()=expr.arguments(); + expr.swap(pointer_offset_expr); + } + else if(identifier==CPROVER_PREFIX "POINTER_OBJECT") + { + if(expr.arguments().size()!=1) + throw "pointer_object expects one argument"; + + exprt pointer_object_expr=exprt(ID_pointer_object, expr.type()); + pointer_object_expr.operands()=expr.arguments(); + expr.swap(pointer_object_expr); + } + else if(identifier==CPROVER_PREFIX "isnan") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "isnan expects one operand"; + } + + exprt isnan_expr(ID_isnan, bool_typet()); + isnan_expr.operands()=expr.arguments(); + expr.swap(isnan_expr); + } + else if(identifier==CPROVER_PREFIX "isfinite") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "isfinite expects one operand"; + } + + exprt isfinite_expr(ID_isfinite, bool_typet()); + isfinite_expr.operands()=expr.arguments(); + expr.swap(isfinite_expr); + } + else if(identifier==CPROVER_PREFIX "abs" || + identifier==CPROVER_PREFIX "labs" || + identifier==CPROVER_PREFIX "fabs" || + identifier==CPROVER_PREFIX "fabsf" || + identifier==CPROVER_PREFIX "fabsl") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "abs-functions expect one operand"; + } + + exprt abs_expr(ID_abs, expr.type()); + abs_expr.operands()=expr.arguments(); + expr.swap(abs_expr); + } + else if(identifier==CPROVER_PREFIX "malloc") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "malloc expects one operand"; + } + + exprt malloc_expr=side_effect_exprt(ID_malloc); + malloc_expr.type()=expr.type(); + malloc_expr.location()=expr.location(); + malloc_expr.operands()=expr.arguments(); + expr.swap(malloc_expr); + } + else if(identifier==CPROVER_PREFIX "isinf") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "isinf expects one operand"; + } + + exprt isinf_expr(ID_isinf, bool_typet()); + isinf_expr.operands()=expr.arguments(); + expr.swap(isinf_expr); + } + else if(identifier==CPROVER_PREFIX "isnormal") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "isnormal expects one operand"; + } + + exprt isnormal_expr(ID_isnormal, bool_typet()); + isnormal_expr.operands()=expr.arguments(); + expr.swap(isnormal_expr); + } + else if(identifier==CPROVER_PREFIX "sign") + { + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "sign expects one operand"; + } + + exprt sign_expr(ID_sign, bool_typet()); + sign_expr.operands()=expr.arguments(); + sign_expr.location()=expr.location(); + expr.swap(sign_expr); + } + else if(identifier==CPROVER_PREFIX "equal") + { + if(expr.arguments().size()!=2) + { + err_location(f_op); + throw "equal expects two operands"; + } + + equality_exprt equality_expr; + equality_expr.operands()=expr.arguments(); + equality_expr.location()=expr.location(); + + if(!base_type_eq(equality_expr.lhs().type(), + equality_expr.rhs().type(), *this)) + { + err_location(f_op); + throw "equal expects two operands of same type"; + } + + expr.swap(equality_expr); + } + else if(identifier=="c::__builtin_expect") + { + // this is a gcc extension to provide branch prediction + if(expr.arguments().size()!=2) + { + err_location(f_op); + throw "__builtin_expect expects two arguments"; + } + + exprt tmp=expr.arguments()[0]; + expr.swap(tmp); + } + else if(identifier=="c::__builtin_choose_expr") + { + // this is a gcc extension similar to ?: + if(expr.arguments().size()!=3) + { + err_location(f_op); + throw "__builtin_choose_expr expects three arguments"; + } + + make_constant(expr.arguments()[0]); + + if(expr.arguments()[0].is_true()) + { + exprt tmp=expr.arguments()[1]; + expr.swap(tmp); + } + else + { + exprt tmp=expr.arguments()[2]; + expr.swap(tmp); + } + } + else if(identifier=="c::__builtin_constant_p") + { + // this is a gcc extension to tell whether the argument + // is known to be a compile-time constant + if(expr.arguments().size()!=1) + { + err_location(f_op); + throw "__builtin_constant_p expects one argument"; + } + + exprt tmp1=expr.arguments().front(); + simplify(tmp1, *this); + + exprt tmp2=from_integer(tmp1.is_constant(), expr.type()); + expr.swap(tmp2); + } + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_function_call_arguments + + Inputs: type-checked arguments, type-checked function + + Outputs: type-adjusted function arguments + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_function_call_arguments( + side_effect_expr_function_callt &expr) +{ + exprt &f_op=expr.function(); + const code_typet &code_type=to_code_type(f_op.type()); + exprt::operandst &arguments=expr.arguments(); + const code_typet::argumentst &argument_types= + code_type.arguments(); + + // no. of arguments test + + if(code_type.get_bool("#incomplete")) + { + // can't check + } + else if(code_type.has_ellipsis()) + { + if(argument_types.size()>arguments.size()) + { + err_location(expr); + throw "not enough function arguments"; + } + } + else if(argument_types.size()!=arguments.size()) + { + err_location(expr); + str << "wrong number of function arguments: " + << "expected " << argument_types.size() + << ", but got " << arguments.size(); + throw 0; + } + + for(unsigned i=0; i(argument_type.find(ID_type)); + + if(op_type.id()==ID_bool && + op.id()==ID_sideeffect && + op.get(ID_statement)==ID_assign && + op.type().id()!=ID_bool) + { + err_location(expr); + warning("assignment where Boolean argument is expected"); + } + + implicit_typecast(op, op_type); + } + else + { + // don't know type, just do standard conversion + + const typet &type=follow(op.type()); + if(type.id()==ID_array || type.id()==ID_incomplete_array) + { + pointer_typet dest_type; + dest_type.subtype()=empty_typet(); + dest_type.subtype().set(ID_C_constant, ID_1); + implicit_typecast(op, dest_type); + } + } + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_constant(exprt &expr) +{ + // nothing to do +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_unary_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_unary_arithmetic(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + str << "operator `" << expr.id() + << "' expects one operand"; + throw 0; + } + + exprt &operand=expr.op0(); + + const typet &o_type=follow(operand.type()); + + if(o_type.id()==ID_vector) + { + if(is_number(follow(o_type.subtype()))) + { + // Vector arithmetic. + expr.type()=operand.type(); + return; + } + } + + implicit_typecast_arithmetic(operand); + + if(is_number(operand.type())) + { + expr.type()=operand.type(); + return; + } + + err_location(expr); + str << "operator `" << expr.id() + << "' not defined for type `" + << to_string(operand.type()) << "'"; + throw 0; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_unary_boolean + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_unary_boolean(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + str << "operator `" << expr.id() + << "' expects one operand"; + throw 0; + } + + exprt &operand=expr.op0(); + + implicit_typecast_bool(operand); + expr.type()=typet(ID_bool); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_binary_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_binary_arithmetic(exprt &expr) +{ + if(expr.operands().size()!=2) + { + err_location(expr); + str << "operator `" << expr.id() + << "' expects two operands"; + throw 0; + } + + exprt &op0=expr.op0(); + exprt &op1=expr.op1(); + + const typet o_type0=follow(op0.type()); + const typet o_type1=follow(op1.type()); + + if(o_type0.id()==ID_vector && + o_type1.id()==ID_vector) + { + if(follow(o_type0.subtype())==follow(o_type1.subtype()) && + is_number(follow(o_type0.subtype()))) + { + // Vector arithmetic + // Fairly strict typing rules, no promotion + expr.type()=op0.type(); + return; + } + } + + if(expr.id()==ID_shl || expr.id()==ID_shr) + { + // must do the promotions _separately_! + implicit_typecast_arithmetic(op0); + implicit_typecast_arithmetic(op1); + + if(is_number(op0.type()) && + is_number(op1.type())) + { + expr.type()=op0.type(); + + if(expr.id()==ID_shr) // shifting operation depends on types + { + const typet &op0_type=follow(op0.type()); + + if(op0_type.id()==ID_unsignedbv) + { + expr.id(ID_lshr); + return; + } + else if(op0_type.id()==ID_signedbv) + { + expr.id(ID_ashr); + return; + } + } + + return; + } + } + else + { + implicit_typecast_arithmetic(op0, op1); + + const typet &type0=follow(op0.type()); + const typet &type1=follow(op1.type()); + + if(expr.id()==ID_plus || expr.id()==ID_minus || + expr.id()==ID_mult || expr.id()==ID_div) + { + if(type0.id()==ID_pointer || type1.id()==ID_pointer) + { + typecheck_expr_pointer_arithmetic(expr); + return; + } + else if(type0==type1) + { + if(is_number(type0)) + { + expr.type()=type0; + return; + } + } + } + else if(expr.id()==ID_mod) + { + if(type0==type1) + { + if(type0.id()==ID_signedbv || type0.id()==ID_unsignedbv) + { + expr.type()=type0; + return; + } + } + } + else if(expr.id()==ID_bitand || expr.id()==ID_bitxor || expr.id()==ID_bitor) + { + if(type0==type1) + { + if(is_number(type0)) + { + expr.type()=type0; + return; + } + } + } + } + + err_location(expr); + str << "operator `" << expr.id() + << "' not defined for types `" + << to_string(o_type0) << "' and `" + << to_string(o_type1) << "'"; + throw 0; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_arithmetic_pointer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_arithmetic_pointer(const exprt &expr) +{ + const typet &type=expr.type(); + assert(type.id()==ID_pointer); + + typet subtype=type.subtype(); + + if(subtype.id()==ID_symbol) + subtype=follow(subtype); + + if(subtype.id()==ID_incomplete_struct) + { + err_location(expr); + throw "pointer arithmetic with unknown object size"; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_pointer_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_pointer_arithmetic(exprt &expr) +{ + assert(expr.operands().size()==2); + exprt &op0=expr.op0(); + exprt &op1=expr.op1(); + + const typet &type0=op0.type(); + const typet &type1=op1.type(); + + if(expr.id()==ID_minus || + (expr.id()==ID_sideeffect && expr.get(ID_statement)==ID_assign_minus)) + { + if(type0.id()==ID_pointer && + type1.id()==ID_pointer) + { + expr.type()=pointer_diff_type(); + typecheck_arithmetic_pointer(op0); + typecheck_arithmetic_pointer(op1); + return; + } + + if(type0.id()==ID_pointer) + { + typecheck_arithmetic_pointer(op0); + make_index_type(op1); + expr.type()=type0; + return; + } + } + else if(expr.id()==ID_plus || + (expr.id()==ID_sideeffect && expr.get(ID_statement)==ID_assign_plus)) + { + exprt *p_op, *int_op; + + if(type0.id()==ID_pointer) + { + p_op=&op0; + int_op=&op1; + } + else if(type1.id()==ID_pointer) + { + p_op=&op1; + int_op=&op0; + } + else + assert(false); + + typecheck_arithmetic_pointer(*p_op); + make_index_type(*int_op); + expr.type()=p_op->type(); + return; + } + + err_location(expr); + str << "operator `" << expr.id() + << "' not defined for types `" + << to_string(type0) << "' and `" + << to_string(type1) << "'"; + throw 0; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_expr_binary_boolean + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_expr_binary_boolean(exprt &expr) +{ + if(expr.operands().size()!=2) + { + err_location(expr); + str << "operator `" << expr.id() + << "' expects two operands"; + throw 0; + } + + implicit_typecast_bool(expr.op0()); + implicit_typecast_bool(expr.op1()); + + expr.type()=typet(ID_bool); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_side_effect_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_side_effect_assignment(exprt &expr) +{ + if(expr.operands().size()!=2) + { + err_location(expr); + str << "operator `" << expr.get(ID_statement) + << "' expects two operands"; + throw 0; + } + + const irep_idt &statement=expr.get(ID_statement); + + exprt &op0=expr.op0(); + exprt &op1=expr.op1(); + + // se if we have a typecast on the LHS + if(op0.id()==ID_typecast) + { + assert(op0.operands().size()==1); + + // set #lvalue and #constant + op0.set(ID_C_lvalue, op0.op0().get_bool(ID_C_lvalue)); + op0.set(ID_C_constant, op0.op0().get_bool(ID_C_constant)); + } + + const typet o_type0=op0.type(); + const typet o_type1=op1.type(); + + const typet &type0=op0.type(); + const typet &final_type0=follow(type0); + + expr.type()=type0; + + if(!op0.get_bool(ID_C_lvalue)) + { + err_location(expr); + str << "assignment error: `" << to_string(op0) + << "' not an lvalue"; + throw 0; + } + + if(o_type0.get_bool(ID_C_constant)) + { + err_location(expr); + str << "error: `" << to_string(op0) + << "' is constant"; + throw 0; + } + + if(statement==ID_assign) + { + implicit_typecast(op1, o_type0); + return; + } + else if(statement==ID_assign_shl || + statement==ID_assign_shr) + { + implicit_typecast_arithmetic(op1); + + if(is_number(op1.type())) + { + expr.type()=type0; + + if(statement==ID_assign_shl) + { + return; + } + else + { + if(final_type0.id()==ID_unsignedbv) + { + expr.set(ID_statement, ID_assign_lshr); + return; + } + else if(final_type0.id()==ID_signedbv || + final_type0.id()==ID_c_enum) + { + expr.set(ID_statement, ID_assign_ashr); + return; + } + } + } + } + else + { + if(final_type0.id()==ID_pointer && + (statement==ID_assign_minus || statement==ID_assign_plus)) + { + typecheck_expr_pointer_arithmetic(expr); + return; + } + else if(final_type0.id()==ID_bool || + final_type0.id()==ID_c_enum || + final_type0.id()==ID_incomplete_c_enum) + { + implicit_typecast_arithmetic(op1); + if(is_number(op1.type())) + return; + } + else if(final_type0.id()==ID_vector && + final_type0==follow(op1.type())) + { + return; + } + else + { + implicit_typecast(op1, op0.type()); + if(is_number(op0.type())) + { + expr.type()=type0; + return; + } + } + } + + err_location(expr); + str << "assignment `" << statement + << "' not defined for types `" + << to_string(o_type0) << "' and `" + << to_string(o_type1) << "'"; + + throw 0; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::make_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::make_constant(exprt &expr) +{ + make_constant_rec(expr); + simplify(expr, *this); + + if(!expr.is_constant() && + expr.id()!=ID_infinity) + { + err_location(expr.find_location()); + str << "expected constant expression, but got `" + << to_string(expr) << "'"; + throw 0; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::make_constant_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::make_constant_index(exprt &expr) +{ + make_constant(expr); + make_index_type(expr); + simplify(expr, *this); + + if(!expr.is_constant() && + expr.id()!=ID_infinity) + { + err_location(expr.find_location()); + throw "conversion to integer failed"; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::make_constant_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::make_constant_rec(exprt &expr) +{ +} diff --git a/src/ansi-c/c_typecheck_initializer.cpp b/src/ansi-c/c_typecheck_initializer.cpp new file mode 100644 index 00000000000..9c46cffcd40 --- /dev/null +++ b/src/ansi-c/c_typecheck_initializer.cpp @@ -0,0 +1,913 @@ +/*******************************************************************\ + +Module: ANSI-C Conversion / Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c_types.h" +#include "c_typecheck_base.h" +#include "string_constant.h" + +/*******************************************************************\ + +Function: c_typecheck_baset::zero_initializer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt c_typecheck_baset::zero_initializer( + const typet &type, + const locationt &location) +{ + const irep_idt &type_id=type.id(); + + if(type_id==ID_bool) + { + exprt result=false_exprt(); + result.location()=location; + return result; + } + else if(type_id==ID_unsignedbv || + type_id==ID_signedbv || + type_id==ID_floatbv || + type_id==ID_fixedbv || + type_id==ID_pointer) + { + exprt result=gen_zero(type); + result.location()=location; + return result; + } + else if(type_id==ID_code) + { + err_location(location); + throw "cannot zero-initialize code-type"; + } + else if(type_id==ID_c_enum || + type_id==ID_incomplete_c_enum) + { + constant_exprt value(type); + value.set_value(ID_0); + value.location()=location; + return value; + } + else if(type_id==ID_array) + { + const array_typet &array_type=to_array_type(type); + exprt tmpval=zero_initializer(array_type.subtype(), location); + + mp_integer array_size; + + if(array_type.size().id()==ID_infinity) + { + exprt value(ID_array_of, type); + value.copy_to_operands(tmpval); + value.location()=location; + return value; + } + else if(to_integer(array_type.size(), array_size)) + { + err_location(location); + str << "failed to zero-initialize array of non-fixed size `" + << to_string(array_type.size()) << "'"; + throw 0; + } + + if(array_size<0) + { + err_location(location); + throw "failed to zero-initialize array of with negative size"; + } + + exprt value(ID_array, type); + value.operands().resize(integer2long(array_size), tmpval); + value.location()=location; + + return value; + } + else if(type_id==ID_incomplete_array) + { + // we initialize this with an empty array + + exprt value(ID_array, type); + value.type().id(ID_array); + value.type().set(ID_size, gen_zero(size_type())); + value.location()=location; + + return value; + } + else if(type_id==ID_struct) + { + const struct_typet::componentst &components= + to_struct_type(type).components(); + + exprt value(ID_struct, type); + + value.operands().reserve(components.size()); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + value.copy_to_operands(zero_initializer(it->type(), location)); + + value.location()=location; + + return value; + } + else if(type_id==ID_union) + { + const union_typet::componentst &components= + to_union_type(type).components(); + + exprt value(ID_union, type); + + if(components.empty()) + return value; // stupid empty union + + value.set(ID_component_name, components.front().get(ID_name)); + value.copy_to_operands( + zero_initializer(components.front().type(), location)); + value.location()=location; + + return value; + } + else if(type_id==ID_symbol) + return zero_initializer(follow(type), location); + else + { + err_location(location); + str << "Failed to zero-initialize `" << to_string(type) + << "'"; + throw 0; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::do_initializer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::do_initializer( + exprt &initializer, + const typet &type, + bool force_constant) +{ + exprt result=do_initializer_rec(initializer, type, force_constant); + + if(type.id()==ID_incomplete_array) + assert(result.type().id()==ID_array); + + initializer=result; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::do_initializer_rec + + Inputs: + + Outputs: + + Purpose: initialize something of type `type' with given + value `value' + +\*******************************************************************/ + +exprt c_typecheck_baset::do_initializer_rec( + const exprt &value, + const typet &type, + bool force_constant) +{ + const typet &full_type=follow(type); + + if(full_type.id()==ID_incomplete_struct) + { + err_location(value); + str << "type `" + << to_string(full_type) << "' is still incomplete -- cannot initialize"; + throw 0; + } + + if(value.id()==ID_initializer_list) + return do_initializer_list(value, full_type, force_constant); + + if(value.id()==ID_array && + value.get_bool(ID_C_string_constant) && + (full_type.id()==ID_array || + full_type.id()==ID_incomplete_array) && + (full_type.subtype().id()==ID_signedbv || + full_type.subtype().id()==ID_unsignedbv) && + full_type.subtype().get(ID_width)==value.type().subtype().get(ID_width)) + { + exprt tmp=value; + + // adjust char type + tmp.type().subtype()=full_type.subtype(); + + Forall_operands(it, tmp) + it->type()=full_type.subtype(); + + if(full_type.id()==ID_array) + { + // check size + mp_integer array_size; + if(to_integer(to_array_type(full_type).size(), array_size)) + { + err_location(value); + throw "array size needs to be constant"; + } + + if(array_size<0) + { + err_location(value); + throw "array size must not be negative"; + } + + if(mp_integer(tmp.operands().size())>array_size) + { + // cut off long strings. gcc does a warning for this + tmp.operands().resize(integer2long(array_size)); + tmp.type()=full_type; + } + else if(mp_integer(tmp.operands().size())array_size) + { + // cut off long strings. gcc does a warning for this + tmp2.operands().resize(integer2long(array_size)); + tmp2.type()=full_type; + } + else if(mp_integer(tmp2.operands().size())(value.find(ID_designator))); + + assert(!designator.empty()); + + return do_designated_initializer( + result, designator, value.op0(), force_constant); + } + + exprt *dest=&result; + + // first phase: follow given designator + + for(unsigned i=0; i=dest->operands().size()) + { + if(type.id()==ID_incomplete_array) + { + exprt zero=zero_initializer(type.subtype(), value.location()); + dest->operands().resize(integer2long(index)+1, zero); + } + else + { + err_location(value); + str << "index designator " << index + << " out of bounds (" << dest->operands().size() << ")"; + throw 0; + } + } + + dest=&(dest->operands()[integer2long(index)]); + } + else if(type.id()==ID_union) + { + // union initialization is quite special + const union_typet &union_type=to_union_type(type); + const union_typet::componentt &component=union_type.components()[index]; + + // build a union expression from the argument + exprt union_expr(ID_union, type); + union_expr.operands().resize(1); + union_expr.op0()=zero_initializer(component.type(), value.location()); + union_expr.location()=value.location(); + union_expr.set(ID_component_name, component.get_name()); + + *dest=union_expr; + dest=&(dest->op0()); + } + else + assert(false); + } + + // second phase: assign value + // for this, we may need to go down, adding to the designator + + while(true) + { + // see what type we have to initialize + + typet type=follow(designator.back().subtype); + assert(type.id()!=ID_symbol); + + // do we initialize a scalar? + if(type.id()!=ID_struct && + type.id()!=ID_union && + type.id()!=ID_array && + type.id()!=ID_incomplete_array) + { + // The initializer for a scalar shall be a single expression, + // * optionally enclosed in braces. * + + if(value.id()==ID_initializer_list && + value.operands().size()==1) + *dest=do_initializer_rec(value.op0(), type, force_constant); + else + *dest=do_initializer_rec(value, type, force_constant); + + assert(type==follow(dest->type())); + + return; // done + } + + // see what initializer we are given + if(value.id()==ID_initializer_list) + { + *dest=do_initializer_rec(value, type, force_constant); + return; // done + } + else if(value.id()==ID_string_constant) + { + // we stop for initializers that are string-constants, + // which are like arrays + if(type.id()==ID_array || type.id()==ID_incomplete_array) + { + *dest=do_initializer_rec(value, type, force_constant); + return; // done + } + } + else if(follow(value.type())==type) + { + // a struct/union can be initialized directly with + // an expression of the right type. This doesn't + // work with arrays, unfortunately. + if(type.id()==ID_struct || type.id()==ID_union) + { + *dest=value; + return; // done + } + } + + // we are initializing a compound type, and enter it! + designator_enter(type, designator); + + assert(!dest->operands().empty()); + dest=&(dest->op0()); + + // we run into another loop iteration + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::increment_designator + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::increment_designator(designatort &designator) +{ + assert(!designator.empty()); + + while(true) + { + designatort::entryt &entry=designator[designator.size()-1]; + + entry.index++; + + if(entry.type.id()==ID_incomplete_array) + return; // we will keep going forever + + if(entry.type.id()==ID_struct && + entry.indexget_name()==component_name) + { + // done! + entry.index=number; + entry.size=components.size(); + entry.subtype=follow(components[entry.index].type()); + entry.type=tmp_type; + } + else if(c_it->get_anonymous() && + has_component_rec(c_it->type(), component_name)) + { + entry.index=number; + entry.size=components.size(); + entry.subtype=follow(c_it->type()); + entry.type=tmp_type; + tmp_type=to_struct_union_type(entry.subtype); + designator.push_entry(entry); + found=repeat=true; + break; + } + } + } + while(repeat); + + if(!found) + { + err_location(d_op); + str << "failed to find struct component `" << component_name + << "' in initialization of `" << to_string(struct_union_type) << "'"; + throw 0; + } + } + } + else + { + err_location(d_op); + str << "designated initializers cannot initialize `" + << to_string(type) << "'"; + throw 0; + } + + type=entry.subtype; + designator.push_entry(entry); + } + + assert(!designator.empty()); + + return designator; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::do_initializer_list + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt c_typecheck_baset::do_initializer_list( + const exprt &value, + const typet &type, + bool force_constant) +{ + assert(value.id()==ID_initializer_list); + + if(type.id()==ID_symbol) + return do_initializer_list( + value, follow(type), force_constant); + + exprt result; + if(type.id()==ID_struct || + type.id()==ID_array || + type.id()==ID_union) + { + // start with zero everywhere + result=zero_initializer(type, value.location()); + } + else if(type.id()==ID_incomplete_array) + { + // start with empty array + result=exprt(ID_array, type); + result.location()=value.location(); + } + else + { + // The initializer for a scalar shall be a single expression, + // * optionally enclosed in braces. * + + if(value.operands().size()==1) + return do_initializer_rec(value.op0(), type, force_constant); + + err_location(value); + str << "cannot initialize `" << to_string(type) << "' with " + "an initializer list"; + throw 0; + } + + designatort current_designator; + + designator_enter(type, current_designator); + + forall_operands(it, value) + { + do_designated_initializer( + result, current_designator, *it, force_constant); + + // increase designator -- might go up + increment_designator(current_designator); + } + + if(type.id()==ID_incomplete_array) + { + // make complete + unsigned size=result.operands().size(); + result.type().id(ID_array); + result.type().set(ID_size, from_integer(size, index_type())); + } + + return result; +} diff --git a/src/ansi-c/c_typecheck_type.cpp b/src/ansi-c/c_typecheck_type.cpp new file mode 100644 index 00000000000..36733d0f6b4 --- /dev/null +++ b/src/ansi-c/c_typecheck_type.cpp @@ -0,0 +1,782 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "c_typecheck_base.h" +#include "c_types.h" +#include "c_sizeof.h" +#include "c_qualifiers.h" + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_type(typet &type) +{ + if(type.id()==ID_code) + typecheck_code_type(to_code_type(type)); + else if(type.id()==ID_array) + typecheck_array_type(to_array_type(type)); + else if(type.id()==ID_incomplete_array) + typecheck_type(type.subtype()); + else if(type.id()==ID_pointer) + typecheck_type(type.subtype()); + else if(type.id()==ID_struct || + type.id()==ID_union) + typecheck_compound_type(to_struct_union_type(type)); + else if(type.id()==ID_c_enum) + { + } + else if(type.id()==ID_c_bitfield) + typecheck_c_bitfield_type(type); + else if(type.id()==ID_typeof) + typecheck_typeof_type(type); + else if(type.id()==ID_symbol) + typecheck_symbol_type(type); + else if(type.id()==ID_vector) + typecheck_vector_type(to_vector_type(type)); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_code_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_code_type(code_typet &type) +{ + code_typet::argumentst &arguments=type.arguments(); + + // if we don't have any arguments, we assume it's (...) + if(arguments.empty()) + { + type.make_ellipsis(); + } + else if(arguments.size()==1 && + arguments[0].type().id()==ID_empty) + { + // if we just have one argument of type void, remove it + arguments.clear(); + } + else + { + for(unsigned i=0; itype()); + + unsigned anon_member_counter=0; + + // scan for anonymous members, and name them + for(struct_union_typet::componentst::iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it->get_name()!=irep_idt()) continue; + + it->set_name("$anon"+i2string(anon_member_counter++)); + it->set_anonymous(true); + } + + // scan for duplicate members + + { + hash_set_cont members; + + for(struct_union_typet::componentst::iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(!members.insert(it->get_name()).second) + { + // we do nothing (as gcc won't complain) + } + } + } + + // we may add some minimal padding inside structs (not unions) + // unless there is an attribute that says that the struct is + // 'packed' + + if(type.id()==ID_struct && + !type.get_bool(ID_packed)) + add_padding(to_struct_type(type)); + + // we allow a zero-length (GCC) or incomplete (C99) array + // as _last_ member! + + for(struct_union_typet::componentst::iterator + it=components.begin(); + it!=components.end(); + it++) + { + typet &type=it->type(); + + if((type.id()==ID_array && + to_array_type(type).size().is_zero()) || + type.id()==ID_incomplete_array) + { + // needs to be last member + if(it!=--components.end()) + { + err_location(*it); + throw "flexible struct member must be last member"; + } + + // if it's incomplete, make it zero + if(type.id()==ID_incomplete_array) + { + type.id(ID_array); + type.set(ID_size, gen_zero(index_type())); + } + } + } +} + +/*******************************************************************\ + +Function: alignment_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static const typet &alignment_type(const typet &type) +{ + if(type.id()==ID_array) + return alignment_type(type.subtype()); + else if(type.id()==ID_struct) + { + const struct_typet::componentst &components= + to_struct_type(type).components(); + + if(!components.empty()) + return alignment_type(components.front().type()); + } + + return type; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::alignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned c_typecheck_baset::alignment(const typet &type) const +{ + if(type.id()==ID_array || + type.id()==ID_incomplete_array) + return alignment(type.subtype()); + else if(type.id()==ID_struct || type.id()==ID_union) + { + const struct_union_typet::componentst &components= + to_struct_union_type(type).components(); + + unsigned result=1; + + // get the max + // (should really be the smallest common denominator) + for(struct_union_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + result=std::max(result, alignment(it->type())); + + return result; + } + else if(type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_fixedbv || + type.id()==ID_floatbv) + { + unsigned width=type.get_int(ID_width); + return width%8?width/8+1:width/8; + } + else if(type.id()==ID_pointer) + { + unsigned width=config.ansi_c.pointer_width; + return width%8?width/8+1:width/8; + } + else if(type.id()==ID_symbol) + return alignment(follow(type)); + + return 1; +} + +/*******************************************************************\ + +Function: c_typecheck_baset::add_padding + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::add_padding(struct_typet &type) +{ + struct_typet::componentst &components=type.components(); + + mp_integer offset=0; + unsigned padding_counter=0; + + for(struct_typet::componentst::iterator + it=components.begin(); + it!=components.end(); + it++) + { + const typet &it_type=it->type(); + unsigned a=alignment(it_type); + + // check minimum alignment + if(a(type.add(ID_size)); + + typecheck_expr(size); + make_constant_index(size); + + mp_integer i; + if(to_integer(size, i)) + { + err_location(type); + throw "failed to convert bit field width"; + } + + if(i<0) + { + err_location(type); + throw "bit field size is negative"; + } + + const typet &base_type=follow(type.subtype()); + + if(base_type.id()==ID_bool) + { + if(i>1) + { + err_location(type); + throw "bit field size too large"; + } + + type.id(ID_unsignedbv); + type.set(ID_width, integer2long(i)); + } + else if(base_type.id()==ID_signedbv || + base_type.id()==ID_unsignedbv || + base_type.id()==ID_c_enum) + { + unsigned width=atoi(base_type.get(ID_width).c_str()); + + if(i>width) + { + err_location(type); + throw "bit field size too large"; + } + + width=integer2long(i); + + typet tmp(base_type); + type.swap(tmp); + type.set(ID_width, width); + } + else + { + err_location(type); + str << "bit field with non-integer type: " + << to_string(base_type); + throw 0; + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_typeof_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_typeof_type(typet &type) +{ + // retain the qualifiers as is + c_qualifierst c_qualifiers; + c_qualifiers.read(type); + + if(type.find(ID_operands).is_nil()) + { + typet t=static_cast(type.find(ID_type_arg)); + typecheck_type(t); + type.swap(t); + } + else + { + exprt expr=((const exprt &)type).op0(); + typecheck_expr(expr); + + // undo an implicit address-of + if(expr.id()==ID_address_of && + expr.get_bool(ID_C_implicit)) + { + assert(expr.operands().size()==1); + exprt tmp; + tmp.swap(expr.op0()); + expr.swap(tmp); + } + + type.swap(expr.type()); + } + + c_qualifiers.write(type); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::typecheck_symbol_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::typecheck_symbol_type(typet &type) +{ + // adjust identifier, if needed + replace_symbol(type); + + const irep_idt &identifier=type.get(ID_identifier); + + contextt::symbolst::const_iterator s_it=context.symbols.find(identifier); + + if(s_it==context.symbols.end()) + { + err_location(type); + str << "type symbol `" << identifier << "' not found"; + throw 0; + } + + const symbolt &symbol=s_it->second; + + if(!symbol.is_type) + { + err_location(type); + throw "expected type symbol"; + } + + if(symbol.is_macro) + { + c_qualifierst c_qualifiers; + c_qualifiers.read(type); + type=symbol.type; // overwrite + c_qualifiers.write(type); + } + + // an extension + if(symbol.base_name=="__CPROVER_rational") + { + type=rational_typet(); + } + else if(symbol.base_name=="__CPROVER_integer") + { + type=integer_typet(); + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::adjust_function_argument + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::adjust_function_argument(typet &type) const +{ + if(type.id()==ID_array || + type.id()==ID_incomplete_array) + { + type.id(ID_pointer); + type.remove(ID_size); + type.remove(ID_C_constant); + } + else if(type.id()==ID_code) + { + // see ISO/IEC 9899:1999 page 199 clause 8 + pointer_typet tmp; + tmp.subtype()=type; + type.swap(tmp); + } + else if(type.id()==ID_KnR) + { + // any KnR args without type yet? + type=int_type(); // the default is integer! + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::clean_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::clean_type( + const symbolt &base_symbol, + typet &type) +{ + if(type.id()==ID_symbol) + { + // we need to follow for structs and such, but only once + irep_idt identifier=to_symbol_type(type).get_identifier(); + if(already_cleaned.insert(identifier).second) + { + contextt::symbolst::iterator s_it=context.symbols.find(identifier); + assert(s_it!=context.symbols.end()); + clean_type(base_symbol, s_it->second.type); + } + } + else if(type.id()==ID_array) + { + array_typet &array_type=to_array_type(type); + + clean_type(base_symbol, array_type.subtype()); + + // the size need not be a constant! + // this was simplified already by typecheck_array_type + + exprt &size=array_type.size(); + + if(!size.is_constant() && + size.id()!=ID_infinity) + { + // Need to pull out! We insert new symbol. + unsigned count=0; + irep_idt temp_identifier; + std::string suffix; + do + { + suffix="#array_size"+i2string(count); + temp_identifier=id2string(base_symbol.name)+suffix; + count++; + } + while(context.symbols.find(temp_identifier)!=context.symbols.end()); + + // add the symbol to context + symbolt new_symbol; + new_symbol.name=temp_identifier; + new_symbol.pretty_name=id2string(base_symbol.pretty_name)+suffix; + new_symbol.base_name=id2string(base_symbol.base_name)+suffix; + new_symbol.type=size.type(); + new_symbol.file_local=true; + new_symbol.is_type=false; + new_symbol.value.make_nil(); + context.add(new_symbol); + + // produce the code that initializes the symbol + symbol_exprt symbol_expr; + symbol_expr.set_identifier(temp_identifier); + symbol_expr.type()=size.type(); + code_assignt assignment; + assignment.lhs()=symbol_expr; + assignment.rhs()=size; + assignment.location()=array_type.size().location(); + + // store the code + + // fix type + size=symbol_expr; + } + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + struct_union_typet::componentst &components= + to_struct_union_type(type).components(); + + for(struct_union_typet::componentst::iterator + it=components.begin(); + it!=components.end(); + it++) + clean_type(base_symbol, it->type()); + } + else if(type.id()==ID_code) + { + // done, can't contain arrays + } + else if(type.id()==ID_pointer) + { + clean_type(base_symbol, type.subtype()); + } + else if(type.id()==ID_vector) + { + // should be clean + } +} + diff --git a/src/ansi-c/c_typecheck_typecast.cpp b/src/ansi-c/c_typecheck_typecast.cpp new file mode 100644 index 00000000000..ff911964d78 --- /dev/null +++ b/src/ansi-c/c_typecheck_typecast.cpp @@ -0,0 +1,105 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "c_typecast.h" +#include "c_typecheck_base.h" +#include "c_types.h" + +/*******************************************************************\ + +Function: c_typecheck_baset::implicit_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::implicit_typecast( + exprt &expr, + const typet &type) +{ + c_typecastt c_typecast(*this); + + typet original_expr_type(expr.type()); + + c_typecast.implicit_typecast(expr, type); + + for(std::list::const_iterator + it=c_typecast.errors.begin(); + it!=c_typecast.errors.end(); + it++) + { + err_location(expr); + str << "in expression `" << to_string(expr) << "':\n"; + str << "conversion from `" + << to_string(original_expr_type) << "' to `" + << to_string(type) << "': " + << *it; + error(); + } + + if(!c_typecast.errors.empty()) + throw 0; // give up + + for(std::list::const_iterator + it=c_typecast.warnings.begin(); + it!=c_typecast.warnings.end(); + it++) + { + err_location(expr); + str << "warning: conversion from `" + << to_string(original_expr_type) + << "' to `" + << to_string(type) + << "': " << *it; + warning(); + } +} + +/*******************************************************************\ + +Function: c_typecheck_baset::implicit_typecast_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::implicit_typecast_arithmetic( + exprt &expr1, + exprt &expr2) +{ + c_typecastt c_typecast(*this); + c_typecast.implicit_typecast_arithmetic(expr1, expr2); +} + +/*******************************************************************\ + +Function: c_typecheck_baset::implicit_typecast_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void c_typecheck_baset::implicit_typecast_arithmetic(exprt &expr) +{ + c_typecastt c_typecast(*this); + c_typecast.implicit_typecast_arithmetic(expr); +} diff --git a/src/ansi-c/c_types.cpp b/src/ansi-c/c_types.cpp new file mode 100644 index 00000000000..63478d9b156 --- /dev/null +++ b/src/ansi-c/c_types.cpp @@ -0,0 +1,343 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "c_types.h" +#include "config.h" + +/*******************************************************************\ + +Function: build_float_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet build_float_type(unsigned width) +{ + if(config.ansi_c.use_fixed_for_float) + { + fixedbv_typet result; + result.set_width(width); + result.set_integer_bits(width/2); + return result; + } + else + { + floatbv_typet result=floatbv_typet(); + result.set_width(width); + + switch(width) + { + case 32: result.set_f(23); break; + case 64: result.set_f(52); break; + default: assert(false); + } + + return result; + } +} + +/*******************************************************************\ + +Function: index_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet index_type() +{ + // same as signed size type + return signedbv_typet(config.ansi_c.pointer_width); +} + +/*******************************************************************\ + +Function: enum_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet enum_type() +{ + return signedbv_typet(config.ansi_c.int_width); +} + +/*******************************************************************\ + +Function: int_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet int_type() +{ + return signedbv_typet(config.ansi_c.int_width); +} + +/*******************************************************************\ + +Function: uint_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet uint_type() +{ + return unsignedbv_typet(config.ansi_c.int_width); +} + +/*******************************************************************\ + +Function: size_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet size_type() +{ + return unsignedbv_typet(config.ansi_c.pointer_width); +} + +/*******************************************************************\ + +Function: signed_size_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet signed_size_type() +{ + return signedbv_typet(config.ansi_c.pointer_width); +} + +/*******************************************************************\ + +Function: long_int_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet long_int_type() +{ + return signedbv_typet(config.ansi_c.long_int_width); +} + +/*******************************************************************\ + +Function: long_long_int_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet long_long_int_type() +{ + return signedbv_typet(config.ansi_c.long_long_int_width); +} + +/*******************************************************************\ + +Function: long_uint_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet long_uint_type() +{ + return unsignedbv_typet(config.ansi_c.long_int_width); +} + +/*******************************************************************\ + +Function: long_long_uint_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet long_long_uint_type() +{ + return unsignedbv_typet(config.ansi_c.long_long_int_width); +} + +/*******************************************************************\ + +Function: char_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet char_type() +{ + if(config.ansi_c.char_is_unsigned) + return unsignedbv_typet(config.ansi_c.char_width); + else + return signedbv_typet(config.ansi_c.char_width); +} + +/*******************************************************************\ + +Function: uchar_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet uchar_type() +{ + return unsignedbv_typet(config.ansi_c.char_width); +} + +/*******************************************************************\ + +Function: wchar_t_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet wchar_t_type() +{ + return signedbv_typet(config.ansi_c.wchar_t_width); +} + +/*******************************************************************\ + +Function: float_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet float_type() +{ + return build_float_type(config.ansi_c.single_width); +} + +/*******************************************************************\ + +Function: double_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet double_type() +{ + return build_float_type(config.ansi_c.double_width); +} + +/*******************************************************************\ + +Function: long_double_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet long_double_type() +{ + return build_float_type(config.ansi_c.long_double_width); +} + +/*******************************************************************\ + +Function: pointer_diff_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet pointer_diff_type() +{ + return signedbv_typet(config.ansi_c.pointer_width); +} + diff --git a/src/ansi-c/c_types.h b/src/ansi-c/c_types.h new file mode 100644 index 00000000000..9cf214e6e4f --- /dev/null +++ b/src/ansi-c/c_types.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_C_TYPES_H +#define CPROVER_C_TYPES_H + +#include + +typet index_type(); +typet enum_type(); +typet int_type(); +typet uint_type(); +typet long_int_type(); +typet long_long_int_type(); +typet long_uint_type(); +typet long_long_uint_type(); +typet char_type(); +typet uchar_type(); +typet wchar_t_type(); +typet float_type(); +typet double_type(); +typet long_double_type(); +typet size_type(); +typet signed_size_type(); +typet pointer_diff_type(); + +#endif diff --git a/src/ansi-c/concatenate_strings.cpp b/src/ansi-c/concatenate_strings.cpp new file mode 100644 index 00000000000..4dd5e10db8b --- /dev/null +++ b/src/ansi-c/concatenate_strings.cpp @@ -0,0 +1,135 @@ +/*******************************************************************\ + +Module: C/C++ Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#include "string_constant.h" +#include "concatenate_strings.h" + +/*******************************************************************\ + +Function: concatenate_array_strings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void concatenate_array_strings( + array_exprt &dest, + const array_exprt &other) +{ + assert(dest.type().id()==ID_array); + assert(other.type().id()==ID_array); + assert(dest.type().subtype()==other.type().subtype()); + + exprt::operandst &dest_op=dest.operands(); + const exprt::operandst &other_op=other.operands(); + + // dest must be zero-terminated + assert(dest_op.size()!=0); + + dest_op.reserve(dest_op.size()+other_op.size()-1); + + dest_op.pop_back(); + + for(exprt::operandst::const_iterator + it=other_op.begin(); it!=other_op.end(); it++) + dest_op.push_back(*it); + + // adjust size + exprt &size=to_array_type(dest.type()).size(); + size=from_integer(dest_op.size(), size.type()); +} + +/*******************************************************************\ + +Function: concatenate_strings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void concatenate_strings(exprt &dest, const exprt &other) +{ + // both are standard strings? + + if(dest.id()==ID_string_constant && + other.id()==ID_string_constant) + { + dest.set(ID_value, + dest.get_string(ID_value)+other.get_string(ID_value)); + } + else if(dest.id()==ID_array && other.id()==ID_array) + { + // both are wide + concatenate_array_strings( + to_array_expr(dest), to_array_expr(other)); + } + else if(dest.id()==ID_array && other.id()==ID_string_constant) + { + // dest is wide, other isn't, we first need to convert + array_exprt other_tmp=to_string_constant(other).to_array_expr(); + + exprt::operandst &other_op=other_tmp.operands(); + + for(exprt::operandst::iterator + it=other_op.begin(); it!=other_op.end(); it++) + { + // need to convert + mp_integer value; + if(to_integer(*it, value)) + assert(false); + *it=from_integer(value, dest.type().subtype()); + } + + other_tmp.type()=dest.type(); + + concatenate_array_strings( + to_array_expr(dest), other_tmp); + } + else if(dest.id()==ID_string_constant && other.id()==ID_array) + { + // other is wide, dest isn't, we first need to convert + + array_exprt dest_tmp=to_string_constant(dest).to_array_expr(); + + exprt::operandst &dest_op=dest_tmp.operands(); + + for(exprt::operandst::iterator + it=dest_op.begin(); it!=dest_op.end(); it++) + { + // need to convert + mp_integer value; + if(to_integer(*it, value)) + assert(false); + *it=from_integer(value, other.type().subtype()); + } + + dest_tmp.type()=other.type(); + + concatenate_array_strings( + dest_tmp, to_array_expr(other)); + + dest=dest_tmp; + } + else + { + // shouldn't happen + assert(false); + } +} diff --git a/src/ansi-c/concatenate_strings.h b/src/ansi-c/concatenate_strings.h new file mode 100644 index 00000000000..f335efa73ec --- /dev/null +++ b/src/ansi-c/concatenate_strings.h @@ -0,0 +1,16 @@ +/*******************************************************************\ + +Module: C/C++ Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_CONCATENATE_STRINGS_H +#define CPROVER_ANSI_C_CONCATENATE_STRINGS_H + +#include + +void concatenate_strings(exprt &dest, const exprt &other); + +#endif diff --git a/src/ansi-c/cprover_library.cpp b/src/ansi-c/cprover_library.cpp new file mode 100644 index 00000000000..1d4abf7efcc --- /dev/null +++ b/src/ansi-c/cprover_library.cpp @@ -0,0 +1,85 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include "cprover_library.h" +#include "ansi_c_language.h" +#include "c_link.h" + +/*******************************************************************\ + +Function: add_cprover_library + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +struct cprover_library_entryt +{ + const char *function; + const char *model; +} cprover_library[]= +#include "cprover_library.inc" + +void add_cprover_library( + contextt &context, + message_handlert &message_handler) +{ + if(config.ansi_c.lib==configt::ansi_ct::LIB_NONE) + return; + + std::ostringstream library_text; + + library_text << + "#line 1 \"\"\n" + "#undef inline\n"; + + if(config.ansi_c.string_abstraction) + library_text << "#define __CPROVER_STRING_ABSTRACTION\n"; + + unsigned count=0; + + for(cprover_library_entryt *e=cprover_library; + e->function!=NULL; + e++) + { + irep_idt id=e->function; + + contextt::symbolst::const_iterator old= + context.symbols.find(id); + + if(old!=context.symbols.end() && + old->second.value.is_nil()) + { + count++; + library_text << e->model << std::endl; + } + } + + if(count>0) + { + std::istringstream in(library_text.str()); + ansi_c_languaget ansi_c_language; + ansi_c_language.parse(in, "", message_handler); + + contextt new_context; + ansi_c_language.typecheck( + new_context, "", message_handler); + + c_link(context, new_context, message_handler); + } +} + diff --git a/src/ansi-c/cprover_library.h b/src/ansi-c/cprover_library.h new file mode 100644 index 00000000000..e9db3b48c45 --- /dev/null +++ b/src/ansi-c/cprover_library.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_CPROVER_LIBRARY_H +#define CPROVER_ANSI_C_CPROVER_LIBRARY_H + +#include +#include + +void add_cprover_library( + contextt &context, + message_handlert &message_handler); + +#endif diff --git a/src/ansi-c/designator.cpp b/src/ansi-c/designator.cpp new file mode 100644 index 00000000000..41e30403f42 --- /dev/null +++ b/src/ansi-c/designator.cpp @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: ANSI-C Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "designator.h" + +/*******************************************************************\ + +Function: operator << + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::ostream &operator << ( + std::ostream &out, + const designatort &designator) +{ + for(unsigned i=0; i + +#include +#include + +class designatort +{ +public: + struct entryt + { + unsigned index; + unsigned size; + typet type, subtype; + + entryt():index(0), size(0) + { + } + }; + + bool empty() const { return index_list.empty(); } + unsigned size() const { return index_list.size(); } + const entryt &operator[](unsigned i) const { return index_list[i]; } + entryt &operator[](unsigned i) { return index_list[i]; } + const entryt &back() const { return index_list.back(); }; + const entryt &front() const { return index_list.front(); }; + + designatort() { } + + void push_entry(const entryt &entry) + { + index_list.push_back(entry); + } + + void pop_entry() + { + index_list.pop_back(); + } + +protected: + // a list of indices into arrays or structs + typedef std::vector index_listt; + index_listt index_list; +}; + +std::ostream &operator << (std::ostream &, const designatort &); + +#endif diff --git a/src/ansi-c/expr2c.cpp b/src/ansi-c/expr2c.cpp new file mode 100644 index 00000000000..057eb00c50e --- /dev/null +++ b/src/ansi-c/expr2c.cpp @@ -0,0 +1,3759 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "expr2c.h" +#include "c_types.h" +#include "expr2c_class.h" + +/*******************************************************************\ + +Function: expr2ct::id_shorthand + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::id_shorthand(const exprt &expr) const +{ + const irep_idt &identifier=expr.get(ID_identifier); + const symbolt *symbol; + + if(!ns.lookup(identifier, symbol)) + return id2string(symbol->base_name); + + std::string sh=id2string(identifier); + + std::string::size_type pos=sh.rfind("::"); + if(pos!=std::string::npos) + sh.erase(0, pos+2); + + return sh; +} + +/*******************************************************************\ + +Function: expr2ct::get_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void expr2ct::get_symbols(const exprt &expr) +{ + if(expr.id()==ID_symbol) + symbols.insert(expr); + + forall_operands(it, expr) + get_symbols(*it); +} + +/*******************************************************************\ + +Function: expr2ct::get_shorthands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void expr2ct::get_shorthands(const exprt &expr) +{ + get_symbols(expr); + + for(std::set::const_iterator it= + symbols.begin(); + it!=symbols.end(); + it++) + { + std::string sh=id_shorthand(*it); + + std::pair::iterator, bool> result= + shorthands.insert( + std::pair(sh, *it)); + + if(!result.second) + if(result.first->second!=*it) + { + ns_collision.insert(it->get(ID_identifier)); + ns_collision.insert(result.first->second.get(ID_identifier)); + } + } +} + +/*******************************************************************\ + +Function: expr2ct::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert(const typet &src) +{ + return convert_rec(src, c_qualifierst()); +} + +/*******************************************************************\ + +Function: expr2ct::convert_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_rec( + const typet &src, + const c_qualifierst &qualifiers) +{ + c_qualifierst new_qualifiers(qualifiers); + new_qualifiers.read(src); + + std::string q=new_qualifiers.as_string(); + + if(src.id()==ID_bool) + { + return q+"_Bool"; + } + else if(src.id()==ID_natural || + src.id()==ID_integer || + src.id()==ID_rational) + { + return q+src.id_string(); + } + else if(src.id()==ID_empty) + { + return q+"void"; + } + else if(src.id()==ID_signedbv || + src.id()==ID_unsignedbv) + { + mp_integer width=string2integer(src.get_string(ID_width)); + + bool is_signed=src.id()==ID_signedbv; + std::string sign_str=is_signed?"signed ":"unsigned "; + + if(width==config.ansi_c.int_width) + { + if(is_signed) sign_str=""; + return q+sign_str+"int"; + } + else if(width==config.ansi_c.long_int_width) + { + if(is_signed) sign_str=""; + return q+sign_str+"long int"; + } + else if(width==config.ansi_c.char_width) + { + // always include sign + return q+sign_str+"char"; + } + else if(width==config.ansi_c.short_int_width) + { + if(is_signed) sign_str=""; + return q+sign_str+"short int"; + } + else if(width==config.ansi_c.long_long_int_width) + { + if(is_signed) sign_str=""; + return q+sign_str+"long long int"; + } + else + { + return q+sign_str+"__CPROVER_bitvector["+integer2string(width)+"]"; + } + } + else if(src.id()==ID_floatbv || + src.id()==ID_fixedbv) + { + mp_integer width=string2integer(src.get_string(ID_width)); + + if(width==config.ansi_c.single_width) + return q+"float"; + else if(width==config.ansi_c.double_width) + return q+"double"; + } + else if(src.id()==ID_struct || + src.id()==ID_incomplete_struct) + { + std::string dest=q+"struct"; + + const std::string &tag=src.get_string(ID_tag); + if(tag!="") dest+=" "+tag; + + /* + const irept &components=type.find(ID_components); + + forall_irep(it, components.get_sub()) + { + typet &subtype=(typet &)it->find(ID_type); + base_type(subtype, ns); + } + */ + + return dest; + } + else if(src.id()==ID_union || + src.id()==ID_incomplete_union) + { + std::string dest=q+"union"; + + const std::string &tag=src.get_string(ID_tag); + if(tag!="") dest+=" "+tag; + + /* + const irept &components=type.find(ID_components); + + forall_irep(it, components.get_sub()) + { + typet &subtype=(typet &)it->find(ID_type); + base_type(subtype, ns); + } + */ + + return dest; + } + else if(src.id()==ID_c_enum || + src.id()==ID_incomplete_c_enum) + { + std::string result=q+"enum"; + if(src.get(ID_name)!="") result+=" "+src.get_string(ID_tag); + return result; + } + else if(src.id()==ID_pointer) + { + if(src.subtype().id()==ID_code) + { + const typet &return_type=(typet &)src.subtype().find(ID_return_type); + + std::string dest=q+convert(return_type); + + // function "name" + dest+=" (*)"; + + // arguments + dest+="("; + const irept &arguments=src.subtype().find(ID_arguments); + + forall_irep(it, arguments.get_sub()) + { + const typet &argument_type=((exprt &)*it).type(); + + if(it!=arguments.get_sub().begin()) + dest+=", "; + + dest+=convert(argument_type); + } + + dest+=")"; + + return dest; + } + else + { + std::string tmp=convert(src.subtype()); + + if(q=="") + return tmp+" *"; + else + return q+" ("+tmp+" *)"; + } + } + else if(src.id()==ID_array) + { + std::string size_string=convert(to_array_type(src).size()); + return convert(src.subtype())+" ["+size_string+"]"; + } + else if(src.id()==ID_incomplete_array) + { + return convert(src.subtype())+" []"; + } + else if(src.id()==ID_symbol) + { + return convert_rec(ns.follow(src), new_qualifiers); + } + else if(src.id()==ID_code) + { + const code_typet &code_type=to_code_type(src); + + const typet &return_type=code_type.return_type(); + + std::string dest=convert(return_type)+" "; + + dest+="("; + const code_typet::argumentst &arguments=code_type.arguments(); + + for(code_typet::argumentst::const_iterator + it=arguments.begin(); + it!=arguments.end(); + it++) + { + if(it!=arguments.begin()) + dest+=", "; + + dest+=convert(it->type()); + } + + dest+=")"; + return dest; + } + else if(src.id()==ID_vector) + { + const vector_typet &vector_type=to_vector_type(src); + std::string dest="VECTOR("; + dest+=convert(vector_type.size()); + dest+=", "; + dest+=convert(vector_type.subtype()); + dest+=")"; + return dest; + } + + { + lispexprt lisp; + irep2lisp(src, lisp); + std::string dest="irep(\""; + MetaString(dest, lisp.expr2string()); + dest+="\")"; + + return dest; + } +} + +/*******************************************************************\ + +Function: expr2ct::convert_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_typecast( + const exprt &src, + unsigned &precedence) +{ + precedence=14; + + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + // some special cases + + const typet &type=ns.follow(src.type()); + + if(type.id()==ID_pointer && + ns.follow(type.subtype()).id()==ID_empty && // to (void *)? + src.op0().is_zero()) + return "NULL"; + + std::string dest="("+convert(type)+")"; + + std::string tmp=convert(src.op0(), precedence); + + if(src.op0().id()==ID_member || + src.op0().id()==ID_constant || + src.op0().id()==ID_symbol) // better fix precedence + dest+=tmp; + else + dest+='('+tmp+')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_implicit_address_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_implicit_address_of( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + return convert(src.op0(), precedence); +} + +/*******************************************************************\ + +Function: expr2ct::convert_trinary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_trinary( + const exprt &src, + const std::string &symbol1, + const std::string &symbol2, + unsigned precedence) +{ + if(src.operands().size()!=3) + return convert_norep(src, precedence); + + const exprt::operandst &operands=src.operands(); + const exprt &op0=operands.front(); + const exprt &op1=*(++operands.begin()); + const exprt &op2=operands.back(); + + unsigned p0, p1, p2; + + std::string s_op0=convert(op0, p0); + std::string s_op1=convert(op1, p1); + std::string s_op2=convert(op2, p2); + + std::string dest; + + if(precedence>=p0) dest+='('; + dest+=s_op0; + if(precedence>=p0) dest+=')'; + + dest+=' '; + dest+=symbol1; + dest+=' '; + + if(precedence>=p1) dest+='('; + dest+=s_op1; + if(precedence>=p1) dest+=')'; + + dest+=' '; + dest+=symbol2; + dest+=' '; + + if(precedence>=p2) dest+='('; + dest+=s_op2; + if(precedence>=p2) dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_quantifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_quantifier( + const exprt &src, + const std::string &symbol, + unsigned precedence) +{ + if(src.operands().size()!=3) + return convert_norep(src, precedence); + + unsigned p0, p2; + + std::string op0=convert(src.op0(), p0); + std::string op2=convert(src.op2(), p2); + + std::string dest=symbol+" "; + + if(precedence>p0) dest+='('; + dest+=op0; + if(precedence>p0) dest+=')'; + + const exprt &instantiations=src.op1(); + if(instantiations.is_not_nil()) + { + dest+=" ("; + forall_operands(it, instantiations) + { + unsigned p; + std::string inst=convert(*it, p); + if(it!=instantiations.operands().begin()) dest+=", "; + dest+=inst; + } + dest+=")"; + } + + dest+=':'; + dest+=' '; + + if(precedence>p2) dest+='('; + dest+=op2; + if(precedence>p2) dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_with + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_with( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()<3) + return convert_norep(src, precedence); + + unsigned p0; + std::string op0=convert(src.op0(), p0); + + std::string dest; + + if(precedence>p0) dest+='('; + dest+=op0; + if(precedence>p0) dest+=')'; + + dest+=" WITH ["; + + for(unsigned i=1; ip || (precedence==p && full_parentheses)) dest+='('; + dest+=op; + if(precedence>p || (precedence==p && full_parentheses)) dest+=')'; + } + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_unary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_unary( + const exprt &src, + const std::string &symbol, + unsigned precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + unsigned p; + std::string op=convert(src.op0(), p); + + std::string dest=symbol; + if(precedence>=p) dest+='('; + dest+=op; + if(precedence>=p) dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_pointer_object_has_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_pointer_object_has_type( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + unsigned p0; + std::string op0=convert(src.op0(), p0); + + std::string dest="POINTER_OBJECT_HAS_TYPE"; + dest+='('; + dest+=op0; + dest+=", "; + dest+=convert(static_cast(src.find("object_type"))); + dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_malloc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_malloc( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + unsigned p0; + std::string op0=convert(src.op0(), p0); + + std::string dest="MALLOC"; + dest+='('; + dest+=convert((const typet &)src.find("#type")); + dest+=", "; + dest+=op0; + dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_nondet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_nondet( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()!=0) + return convert_norep(src, precedence); + + return "NONDET("+convert(src.type())+")"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_statement_expression + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_statement_expression( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + return "{"+convert_code(to_code(src.op0()), 2)+"}"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_prob_coin + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_prob_coin( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()==1) + return "COIN("+convert(src.op0())+")"; + else + return convert_norep(src, precedence); +} + +/*******************************************************************\ + +Function: expr2ct::convert_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_literal( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()==1) + return "L("+src.get_string(ID_literal)+")"; + else + return convert_norep(src, precedence); +} + +/*******************************************************************\ + +Function: expr2ct::convert_prob_uniform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_prob_uniform( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()==1) + return "PROB_UNIFORM("+convert(src.type())+","+convert(src.op0())+")"; + else + return convert_norep(src, precedence); +} + +/*******************************************************************\ + +Function: expr2ct::convert_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_function( + const exprt &src, + const std::string &name, + unsigned precedence) +{ + std::string dest=name; + dest+='('; + + forall_operands(it, src) + { + unsigned p; + std::string op=convert(*it, p); + + if(it!=src.operands().begin()) dest+=", "; + + dest+=op; + } + + dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_array_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_array_of( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + return "ARRAY_OF("+convert(src.op0())+')'; +} + +/*******************************************************************\ + +Function: expr2ct::convert_byte_extract + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_byte_extract( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=2) + return convert_norep(src, precedence); + + unsigned p0; + std::string op0=convert(src.op0(), p0); + + unsigned p1; + std::string op1=convert(src.op1(), p1); + + std::string dest=src.id_string(); + dest+='('; + dest+=op0; + dest+=", "; + dest+=op1; + dest+=", "; + dest+=convert(src.type()); + dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_byte_update + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_byte_update( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=3) + return convert_norep(src, precedence); + + unsigned p0; + std::string op0=convert(src.op0(), p0); + + unsigned p1; + std::string op1=convert(src.op1(), p1); + + unsigned p2; + std::string op2=convert(src.op2(), p2); + + std::string dest=src.id_string(); + dest+='('; + dest+=op0; + dest+=", "; + dest+=op1; + dest+=", "; + dest+=op2; + dest+=", "; + dest+=convert(src.op2().type()); + dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_unary_post + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_unary_post( + const exprt &src, + const std::string &symbol, + unsigned precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + unsigned p; + std::string op=convert(src.op0(), p); + + std::string dest; + if(precedence>p) dest+='('; + dest+=op; + if(precedence>p) dest+=')'; + dest+=symbol; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_index( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=2) + return convert_norep(src, precedence); + + unsigned p; + std::string op=convert(src.op0(), p); + + std::string dest; + if(precedence>p) dest+='('; + dest+=op; + if(precedence>p) dest+=')'; + + dest+='['; + dest+=convert(src.op1()); + dest+=']'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_pointer_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_pointer_arithmetic( + const exprt &src, unsigned &precedence) +{ + if(src.operands().size()!=2) + return convert_norep(src, precedence); + + std::string dest="POINTER_ARITHMETIC("; + + unsigned p; + std::string op; + + op=convert(src.op0().type()); + dest+=op; + + dest+=", "; + + op=convert(src.op0(), p); + if(precedence>p) dest+='('; + dest+=op; + if(precedence>p) dest+=')'; + + dest+=", "; + + op=convert(src.op1(), p); + if(precedence>p) dest+='('; + dest+=op; + if(precedence>p) dest+=')'; + + dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_pointer_difference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_pointer_difference( + const exprt &src, unsigned &precedence) +{ + if(src.operands().size()!=2) + return convert_norep(src, precedence); + + std::string dest="POINTER_DIFFERENCE("; + + unsigned p; + std::string op; + + op=convert(src.op0().type()); + dest+=op; + + dest+=", "; + + op=convert(src.op0(), p); + if(precedence>p) dest+='('; + dest+=op; + if(precedence>p) dest+=')'; + + dest+=", "; + + op=convert(src.op1(), p); + if(precedence>p) dest+='('; + dest+=op; + if(precedence>p) dest+=')'; + + dest+=')'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_member( + const member_exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + unsigned p; + std::string dest; + + if(src.op0().id()==ID_dereference && + src.operands().size()==1) + { + std::string op=convert(src.op0().op0(), p); + + if(precedence>p) dest+='('; + dest+=op; + if(precedence>p) dest+=')'; + + dest+="->"; + } + else + { + std::string op=convert(src.op0(), p); + + if(precedence>p) dest+='('; + dest+=op; + if(precedence>p) dest+=')'; + + dest+='.'; + } + + const typet &full_type=ns.follow(src.op0().type()); + + if(full_type.id()!=ID_struct && + full_type.id()!=ID_union) + return convert_norep(src, precedence); + + const struct_union_typet &struct_union_type= + to_struct_union_type(full_type); + + irep_idt component_name=src.get_component_name(); + + if(component_name!="") + { + const exprt comp_expr= + struct_union_type.get_component(component_name); + + if(comp_expr.is_nil()) + return convert_norep(src, precedence); + + dest+=comp_expr.get_string(ID_pretty_name); + + return dest; + } + + unsigned n=src.get_component_number(); + + if(n>=struct_union_type.components().size()) + return convert_norep(src, precedence); + + const exprt comp_expr= + struct_union_type.components()[n]; + + dest+=comp_expr.get_string(ID_pretty_name); + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_array_member_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_array_member_value( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + return "[]="+convert(src.op0()); +} + +/*******************************************************************\ + +Function: expr2ct::convert_struct_member_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_struct_member_value( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=1) + return convert_norep(src, precedence); + + return "."+src.get_string(ID_name)+"="+convert(src.op0()); +} + +/*******************************************************************\ + +Function: expr2ct::convert_norep + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_norep( + const exprt &src, + unsigned &precedence) +{ + lispexprt lisp; + irep2lisp(src, lisp); + std::string dest="irep(\""; + MetaString(dest, lisp.expr2string()); + dest+="\")"; + precedence=15; + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_symbol( + const exprt &src, + unsigned &precedence) +{ + const irep_idt &id=src.get(ID_identifier); + std::string dest; + + if(ns_collision.find(id)==ns_collision.end()) + dest=id_shorthand(src); + else + dest=id2string(id); + + if(src.id()==ID_next_symbol) + dest="NEXT("+dest+")"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_nondet_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_nondet_symbol( + const exprt &src, + unsigned &precedence) +{ + const std::string &id=src.get_string(ID_identifier); + return "nondet_symbol("+id+")"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_predicate_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_predicate_symbol( + const exprt &src, + unsigned &precedence) +{ + const std::string &id=src.get_string(ID_identifier); + return "ps("+id+")"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_predicate_next_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_predicate_next_symbol( + const exprt &src, + unsigned &precedence) +{ + const std::string &id=src.get_string(ID_identifier); + return "pns("+id+")"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_quantified_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_quantified_symbol( + const exprt &src, + unsigned &precedence) +{ + const std::string &id=src.get_string(ID_identifier); + return id; +} + +/*******************************************************************\ + +Function: expr2ct::convert_nondet_bool + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_nondet_bool( + const exprt &src, + unsigned &precedence) +{ + return "nondet_bool()"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_object_descriptor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_object_descriptor( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()!=2) + return convert_norep(src, precedence); + + std::string result="<"; + + result+=convert(src.op0()); + result+=", "; + result+=convert(src.op1()); + result+=", "; + result+=convert(src.type()); + + result+=">"; + + return result; +} + +/*******************************************************************\ + +Function: expr2ct::convert_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_constant( + const exprt &src, + unsigned &precedence) +{ + const typet &type=ns.follow(src.type()); + const irep_idt value=src.get(ID_value); + std::string dest; + + if(src.id()==ID_string_constant) + { + dest='"'; + MetaString(dest, id2string(value)); + dest+='"'; + } + else if(type.id()==ID_integer || + type.id()==ID_natural || + type.id()==ID_rational) + dest=id2string(value); + else if(type.id()==ID_c_enum || + type.id()==ID_incomplete_c_enum) + { + mp_integer int_value=string2integer(id2string(value)); + mp_integer i=0; + const irept &body=type.find(ID_body); + + forall_irep(it, body.get_sub()) + { + if(i==int_value) + { + dest=it->get_string(ID_name); + return dest; + } + + const exprt &v= + static_cast(it->find(ID_value)); + + if(v.is_not_nil()) + if(to_integer(v, i)) + break; + + ++i; + } + + // failed... + dest="enum("+id2string(value)+")"; + + return dest; + } + else if(type.id()==ID_rational) + return convert_norep(src, precedence); + else if(type.id()==ID_bv) + dest=id2string(value); + else if(type.id()==ID_bool) + { + if(src.is_true()) + dest="TRUE"; + else + dest="FALSE"; + } + else if(type.id()==ID_unsignedbv || + type.id()==ID_signedbv) + { + mp_integer int_value=binary2integer(id2string(value), type.id()==ID_signedbv); + dest=integer2string(int_value); + } + else if(type.id()==ID_floatbv) + { + dest=ieee_floatt(src).to_ansi_c_string(); + + if(dest!="" && isdigit(dest[dest.size()-1])) + { + if(src.type()==float_type()) + dest+="f"; + else if(src.type()==double_type()) + dest+="l"; + } + } + else if(type.id()==ID_fixedbv) + { + dest=fixedbvt(src).to_ansi_c_string(); + + if(dest!="" && isdigit(dest[dest.size()-1])) + { + if(src.type()==float_type()) + dest+="f"; + else if(src.type()==double_type()) + dest+="l"; + } + } + else if(type.id()==ID_array || + type.id()==ID_incomplete_array) + { + dest="{ "; + + forall_operands(it, src) + { + std::string tmp=convert(*it); + + if((it+1)!=src.operands().end()) + { + tmp+=", "; + if(tmp.size()>40) tmp+="\n "; + } + + dest+=tmp; + } + + dest+=" }"; + } + else if(type.id()==ID_pointer) + { + if(value==ID_NULL) + dest="NULL"; + else if(value=="INVALID" || + has_prefix(id2string(value), "INVALID-") || + value=="NULL+offset") + dest=id2string(value); + else + return convert_norep(src, precedence); + } + else + return convert_norep(src, precedence); + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_struct + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_struct( + const exprt &src, + unsigned &precedence) +{ + const typet full_type=ns.follow(src.type()); + + if(full_type.id()!=ID_struct) + return convert_norep(src, precedence); + + std::string dest="{ "; + + const irept::subt &components= + full_type.find(ID_components).get_sub(); + + assert(components.size()==src.operands().size()); + + exprt::operandst::const_iterator o_it=src.operands().begin(); + + bool first=true; + bool newline=false; + unsigned last_size=0; + + forall_irep(c_it, components) + { + if(o_it->type().id()==ID_code) + continue; + + if(first) + first=false; + else + { + dest+=","; + + if(newline) + dest+="\n "; + else + dest+=" "; + } + + std::string tmp=convert(*o_it); + + if(last_size+40get_string(ID_name); + dest+="="; + dest+=tmp; + + o_it++; + } + + dest+=" }"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_vector + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_vector( + const exprt &src, + unsigned &precedence) +{ + const typet full_type=ns.follow(src.type()); + + if(full_type.id()!=ID_vector) + return convert_norep(src, precedence); + + std::string dest="{ "; + + bool first=true; + bool newline=false; + unsigned last_size=0; + + forall_operands(it, src) + { + if(first) + first=false; + else + { + dest+=","; + + if(newline) + dest+="\n "; + else + dest+=" "; + } + + std::string tmp=convert(*it); + + if(last_size+40is_constant()) + all_constant=false; + + if(src.get_bool(ID_C_string_constant) && + all_constant && + (subtype==char_type() || subtype==wchar_t_type())) + { + bool wide=subtype==wchar_t_type(); + + if(wide) + dest+="L"; + + dest+="\""; + + dest.reserve(dest.size()+1+src.operands().size()); + + forall_operands(it, src) + { + assert(it->is_constant()); + mp_integer i; + to_integer(*it, i); + int ch=integer2long(i); + + switch(ch) + { + case '\n': dest+="\\n"; break; /* NL (0x0a) */ + case '\t': dest+="\\t"; break; /* HT (0x09) */ + case '\v': dest+="\\v"; break; /* VT (0x0b) */ + case '\b': dest+="\\b"; break; /* BS (0x08) */ + case '\r': dest+="\\r"; break; /* CR (0x0d) */ + case '\f': dest+="\\f"; break; /* FF (0x0c) */ + case '\a': dest+="\\a"; break; /* BEL (0x07) */ + case '\\': dest+="\\"; break; + case '"': dest+="\\\""; break; + + default: + if(isalnum(ch)) + dest+=(char)ch; + else + { + // TODO, hexify + } + } + } + + dest+="\""; + + return dest; + } + + dest="{ "; + + forall_operands(it, src) + { + std::string tmp; + + if(it->is_not_nil()) + tmp=convert(*it); + + if((it+1)!=src.operands().end()) + { + tmp+=", "; + if(tmp.size()>40) tmp+="\n "; + } + + dest+=tmp; + } + + dest+=" }"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_array_list + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_array_list( + const exprt &src, + unsigned &precedence) +{ + std::string dest="{ "; + + if((src.operands().size()%2)!=0) + return convert_norep(src, precedence); + + forall_operands(it, src) + { + std::string tmp1=convert(*it); + + it++; + + std::string tmp2=convert(*it); + + std::string tmp="["+tmp1+"]="+tmp2; + + if((it+1)!=src.operands().end()) + { + tmp+=", "; + if(tmp.size()>40) tmp+="\n "; + } + + dest+=tmp; + } + + dest+=" }"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_initializer_list + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_initializer_list( + const exprt &src, + unsigned &precedence) +{ + std::string dest="{ "; + + forall_operands(it, src) + { + std::string tmp=convert(*it); + + if((it+1)!=src.operands().end()) + { + tmp+=", "; + if(tmp.size()>40) tmp+="\n "; + } + + dest+=tmp; + } + + dest+=" }"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_designated_initializer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_designated_initializer( + const exprt &src, + unsigned &precedence) +{ + if(src.operands().size()!=1) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + std::string dest="."; + // TODO it->find(ID_member) + dest+="="; + dest+=convert(src.op0()); + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_function_application + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_function_application( + const function_application_exprt &src, + unsigned &precedence) +{ + std::string dest; + + { + unsigned p; + std::string function_str=convert(src.function(), p); + dest+=function_str; + } + + dest+="("; + + unsigned i=0; + + forall_expr(it, src.arguments()) + { + unsigned p; + std::string arg_str=convert(*it, p); + + if(i>0) dest+=", "; + // TODO: ggf. Klammern je nach p + dest+=arg_str; + + i++; + } + + dest+=")"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_side_effect_expr_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_side_effect_expr_function_call( + const side_effect_expr_function_callt &src, + unsigned &precedence) +{ + std::string dest; + + { + unsigned p; + std::string function_str=convert(src.function(), p); + dest+=function_str; + } + + dest+="("; + + unsigned i=0; + + forall_expr(it, src.arguments()) + { + unsigned p; + std::string arg_str=convert(*it, p); + + if(i>0) dest+=", "; + // TODO: ggf. Klammern je nach p + dest+=arg_str; + + i++; + } + + dest+=")"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_overflow + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_overflow( + const exprt &src, + unsigned &precedence) +{ + precedence=16; + + std::string dest="overflow(\""; + dest+=src.id().c_str()+9; + dest+="\""; + + forall_operands(it, src) + { + unsigned p; + std::string arg_str=convert(*it, p); + + dest+=", "; + // TODO: ggf. Klammern je nach p + dest+=arg_str; + } + + dest+=")"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::indent_str + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::indent_str(unsigned indent) +{ + std::string dest; + for(unsigned j=0; j=2); + std::string dest=indent_str(indent-2); + dest+="{\n"; + + forall_operands(it, src) + { + if(it->get(ID_statement)==ID_block) + dest+=convert_code_block(to_code(*it), indent+2); + else + dest+=convert_code(to_code(*it), indent); + } + + dest+=indent_str(indent-2); + dest+="}\n"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_expression + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_expression( + const codet &src, + unsigned indent) +{ + std::string dest=indent_str(indent); + + std::string expr_str; + if(src.operands().size()==1) + expr_str=convert(src.op0()); + else + { + unsigned precedence; + expr_str=convert_norep(src, precedence); + } + + dest+=expr_str+";"; + + dest+="\n"; + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code( + const codet &src, + unsigned indent) +{ + const irep_idt &statement=src.get(ID_statement); + + if(statement==ID_expression) + return convert_code_expression(src, indent); + + if(statement==ID_block) + return convert_code_block(src, indent); + + if(statement==ID_switch) + return convert_code_switch(src, indent); + + if(statement==ID_for) + return convert_code_for(src, indent); + + if(statement==ID_while) + return convert_code_while(src, indent); + + if(statement==ID_asm) + return convert_code_asm(src, indent); + + if(statement==ID_skip) + return indent_str(indent)+";\n"; + + if(statement==ID_dowhile) + return convert_code_dowhile(src, indent); + + if(statement==ID_ifthenelse) + return convert_code_ifthenelse(src, indent); + + if(statement==ID_return) + return convert_code_return(src, indent); + + if(statement==ID_goto) + return convert_code_goto(src, indent); + + if(statement==ID_printf) + return convert_code_printf(src, indent); + + if(statement==ID_input) + return convert_code_input(src, indent); + + if(statement==ID_assume) + return convert_code_assume(src, indent); + + if(statement==ID_assert) + return convert_code_assert(src, indent); + + if(statement==ID_break) + return convert_code_break(src, indent); + + if(statement==ID_continue) + return convert_code_continue(src, indent); + + if(statement==ID_decl) + return convert_code_decl(src, indent); + + if(statement==ID_assign) + return convert_code_assign(src, indent); + + if(statement==ID_init) + return convert_code_init(src, indent); + + if(statement=="lock") + return convert_code_lock(src, indent); + + if(statement=="unlock") + return convert_code_unlock(src, indent); + + if(statement==ID_function_call) + return convert_code_function_call(to_code_function_call(src), indent); + + if(statement==ID_label) + return convert_code_label(src, indent); + + if(statement==ID_free) + return convert_code_free(src, indent); + + if(statement==ID_array_set) + return convert_code_array_set(src, indent); + + if(statement==ID_array_copy) + return convert_code_array_copy(src, indent); + + unsigned precedence; + return convert_norep(src, precedence); +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_assign( + const codet &src, + unsigned indent) +{ + std::string tmp=convert_binary(src, "=", 2, true); + + std::string dest=indent_str(indent)+tmp+";\n"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_free + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_free( + const codet &src, + unsigned indent) +{ + if(src.operands().size()!=1) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + return indent_str(indent)+"FREE("+convert(src.op0())+");"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_init + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_init( + const codet &src, + unsigned indent) +{ + std::string tmp=convert_binary(src, "=", 2, true); + + return indent_str(indent)+"INIT "+tmp+";"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_lock + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_lock( + const codet &src, + unsigned indent) +{ + if(src.operands().size()!=1) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + return indent_str(indent)+"LOCK("+convert(src.op0())+");"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_unlock + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_unlock( + const codet &src, + unsigned indent) +{ + if(src.operands().size()!=1) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + return indent_str(indent)+"UNLOCK("+convert(src.op0())+");"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_function_call( + const code_function_callt &src, + unsigned indent) +{ + if(src.operands().size()!=3) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + std::string dest; + + if(src.lhs().is_not_nil()) + { + unsigned p; + std::string lhs_str=convert(src.lhs(), p); + + // TODO: ggf. Klammern je nach p + dest+=lhs_str; + dest+="="; + } + + { + unsigned p; + std::string function_str=convert(src.function(), p); + dest+=function_str; + } + + dest+="("; + + unsigned i=0; + + const exprt::operandst &arguments=src.arguments(); + + forall_expr(it, arguments) + { + unsigned p; + std::string arg_str=convert(*it, p); + + if(i>0) dest+=", "; + // TODO: ggf. Klammern je nach p + dest+=arg_str; + + i++; + } + + dest+=")"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_printf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_printf( + const codet &src, + unsigned indent) +{ + std::string dest=indent_str(indent)+"PRINTF("; + + forall_operands(it, src) + { + unsigned p; + std::string arg_str=convert(*it, p); + + if(it!=src.operands().begin()) dest+=", "; + // TODO: ggf. Klammern je nach p + dest+=arg_str; + } + + dest+=");"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_input + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_input( + const codet &src, + unsigned indent) +{ + std::string dest=indent_str(indent)+"INPUT("; + + forall_operands(it, src) + { + unsigned p; + std::string arg_str=convert(*it, p); + + if(it!=src.operands().begin()) dest+=", "; + // TODO: ggf. Klammern je nach p + dest+=arg_str; + } + + dest+=");"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_array_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_array_set( + const codet &src, + unsigned indent) +{ + std::string dest=indent_str(indent)+"ARRAY_SET("; + + forall_operands(it, src) + { + unsigned p; + std::string arg_str=convert(*it, p); + + if(it!=src.operands().begin()) dest+=", "; + // TODO: ggf. Klammern je nach p + dest+=arg_str; + } + + dest+=");"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_array_copy + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_array_copy( + const codet &src, + unsigned indent) +{ + std::string dest=indent_str(indent)+"ARRAY_COPY("; + + forall_operands(it, src) + { + unsigned p; + std::string arg_str=convert(*it, p); + + if(it!=src.operands().begin()) dest+=", "; + // TODO: ggf. Klammern je nach p + dest+=arg_str; + } + + dest+=");"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_assert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_assert( + const codet &src, + unsigned indent) +{ + if(src.operands().size()!=1) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + return indent_str(indent)+"assert("+convert(src.op0())+");"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_assume + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_assume( + const codet &src, + unsigned indent) +{ + if(src.operands().size()!=1) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + return indent_str(indent)+"assume("+convert(src.op0())+");"; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code_label + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code_label( + const codet &src, + unsigned indent) +{ + bool first=true; + std::string labels_string; + + { + const irept::named_subt &labels=src.find(ID_labels).get_named_sub(); + + forall_named_irep(it, labels) + { + if(first) { labels_string+="\n"; first=false; } + labels_string+=indent_str(indent); + labels_string+=name2string(it->first); + labels_string+=":\n"; + } + + const exprt &case_expr=(exprt &)src.find(ID_case); + + forall_operands(it, case_expr) + { + if(first) { labels_string+="\n"; first=false; } + labels_string+=indent_str(indent); + labels_string+="case "; + labels_string+=convert(*it); + labels_string+=":\n"; + } + + if(src.get_bool(ID_default)) + { + if(first) { labels_string+="\n"; first=false; } + labels_string+=indent_str(indent); + labels_string+="default:\n"; + } + } + + if(src.operands().size()!=1) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + std::string tmp=convert_code(to_code(src.op0()), indent); + + return labels_string+tmp; +} + +/*******************************************************************\ + +Function: expr2ct::convert_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_code(const codet &src) +{ + return convert_code(src, 2); +} + +/*******************************************************************\ + +Function: expr2ct::convert_Hoare + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_Hoare(const exprt &src) +{ + unsigned precedence; + + if(src.operands().size()!=2) + return convert_norep(src, precedence); + + const exprt &assumption=src.op0(); + const exprt &assertion=src.op1(); + const codet &code= + static_cast(src.find(ID_code)); + + std::string dest="\n"; + dest+="{"; + + if(!assumption.is_nil()) + { + std::string assumption_str=convert(assumption); + dest+=" assume("; + dest+=assumption_str; + dest+=");\n"; + } + else + dest+="\n"; + + { + std::string code_str=convert_code(code); + dest+=code_str; + } + + if(!assertion.is_nil()) + { + std::string assertion_str=convert(assertion); + dest+=" assert("; + dest+=assertion_str; + dest+=");\n"; + } + + dest+="}"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert_extractbit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert_extractbit( + const exprt &src, + unsigned precedence) +{ + if(src.operands().size()!=2) + return convert_norep(src, precedence); + + std::string dest=convert(src.op0(), precedence); + dest+='['; + dest+=convert(src.op1(), precedence); + dest+=']'; + + return dest; +} + +/*******************************************************************\ + +Function: expr2ct::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert( + const exprt &src, + unsigned &precedence) +{ + precedence=16; + + if(src.id()==ID_plus) + return convert_binary(src, "+", precedence=12, false); + + else if(src.id()==ID_minus) + { + if(src.operands().size()==1) + return convert_norep(src, precedence); + else + return convert_binary(src, "-", precedence=12, true); + } + + else if(src.id()==ID_unary_minus) + { + if(src.operands().size()!=1) + return convert_norep(src, precedence); + else + return convert_unary(src, "-", precedence=15); + } + + else if(src.id()==ID_unary_plus) + { + if(src.operands().size()!=1) + return convert_norep(src, precedence); + else + return convert_unary(src, "+", precedence=15); + } + + else if(src.id()=="invalid-pointer") + { + return convert_function(src, "INVALID-POINTER", precedence=15); + } + + else if(src.id()=="pointer_arithmetic") + { + return convert_pointer_arithmetic(src, precedence=15); + } + + else if(src.id()=="pointer_difference") + { + return convert_pointer_difference(src, precedence=15); + } + + else if(src.id()=="NULL-object") + { + return "NULL-object"; + } + + else if(src.id()==ID_integer_address || + src.id()==ID_stack_object || + src.id()==ID_static_object) + { + return id2string(src.id()); + } + + else if(src.id()==ID_infinity) + { + return convert_function(src, "INFINITY", precedence=15); + } + + else if(src.id()=="builtin-function") + { + return src.get_string(ID_identifier); + } + + else if(src.id()==ID_pointer_object) + { + return convert_function(src, "POINTER_OBJECT", precedence=15); + } + + else if(src.id()=="object_value") + { + return convert_function(src, "OBJECT_VALUE", precedence=15); + } + + else if(src.id()=="pointer_object_has_type") + { + return convert_pointer_object_has_type(src, precedence=15); + } + + else if(src.id()==ID_array_of) + { + return convert_array_of(src, precedence=15); + } + + else if(src.id()==ID_pointer_offset) + { + return convert_function(src, "POINTER_OFFSET", precedence=15); + } + + else if(src.id()=="pointer_base") + { + return convert_function(src, "POINTER_BASE", precedence=15); + } + + else if(src.id()=="pointer_cons") + { + return convert_function(src, "POINTER_CONS", precedence=15); + } + + else if(src.id()==ID_same_object) + { + return convert_function(src, "SAME-OBJECT", precedence=15); + } + + else if(src.id()==ID_dynamic_object) + { + return convert_function(src, "DYNAMIC_OBJECT", precedence=15); + } + + else if(src.id()=="is_zero_string") + { + return convert_function(src, "IS_ZERO_STRING", precedence=15); + } + + else if(src.id()=="zero_string") + { + return convert_function(src, "ZERO_STRING", precedence=15); + } + + else if(src.id()=="zero_string_length") + { + return convert_function(src, "ZERO_STRING_LENGTH", precedence=15); + } + + else if(src.id()=="buffer_size") + { + return convert_function(src, "BUFFER_SIZE", precedence=15); + } + + else if(src.id()==ID_pointer_offset) + { + return convert_function(src, "POINTER_OFFSET", precedence=15); + } + + else if(src.id()==ID_isnan) + { + return convert_function(src, "isnan", precedence=15); + } + + else if(src.id()==ID_isfinite) + { + return convert_function(src, "isfinite", precedence=15); + } + + else if(src.id()==ID_isinf) + { + return convert_function(src, "isinf", precedence=15); + } + + else if(src.id()==ID_isnormal) + { + return convert_function(src, "isnormal", precedence=15); + } + + else if(src.id()==ID_builtin_offsetof) + { + return convert_function(src, "builtin_offsetof", precedence=15); + } + + else if(src.id()==ID_builtin_alignof) + { + return convert_function(src, "builtin_alignof", precedence=15); + } + + else if(has_prefix(src.id_string(), "byte_extract")) + { + return convert_byte_extract(src, precedence=15); + } + + else if(has_prefix(src.id_string(), "byte_update")) + { + return convert_byte_update(src, precedence=15); + } + + else if(src.id()==ID_address_of) + { + if(src.operands().size()!=1) + return convert_norep(src, precedence); + else if(src.op0().id()==ID_label) + return "&&"+src.op0().get_string(ID_identifier); + else + return convert_unary(src, "&", precedence=15); + } + + else if(src.id()==ID_dereference) + { + if(src.operands().size()!=1) + return convert_norep(src, precedence); + else + return convert_unary(src, "*", precedence=15); + } + + else if(src.id()==ID_index) + return convert_index(src, precedence=16); + + else if(src.id()==ID_member) + return convert_member(to_member_expr(src), precedence=16); + + else if(src.id()=="array-member-value") + return convert_array_member_value(src, precedence=16); + + else if(src.id()=="struct-member-value") + return convert_struct_member_value(src, precedence=16); + + else if(src.id()==ID_function_application) + return convert_function_application(to_function_application_expr(src), precedence); + + else if(src.id()==ID_sideeffect) + { + const irep_idt &statement=src.get(ID_statement); + if(statement==ID_preincrement) + return convert_unary(src, "++", precedence=15); + else if(statement==ID_predecrement) + return convert_unary(src, "--", precedence=15); + else if(statement==ID_postincrement) + return convert_unary_post(src, "++", precedence=16); + else if(statement==ID_postdecrement) + return convert_unary_post(src, "--", precedence=16); + else if(statement==ID_assign_plus) + return convert_binary(src, "+=", precedence=2, true); + else if(statement==ID_assign_minus) + return convert_binary(src, "-=", precedence=2, true); + else if(statement==ID_assign_mult) + return convert_binary(src, "*=", precedence=2, true); + else if(statement==ID_assign_div) + return convert_binary(src, "/=", precedence=2, true); + else if(statement==ID_assign_mod) + return convert_binary(src, "%=", precedence=2, true); + else if(statement==ID_assign_shl) + return convert_binary(src, "<<=", precedence=2, true); + else if(statement==ID_assign_shr) + return convert_binary(src, ">>=", precedence=2, true); + else if(statement==ID_assign_bitand) + return convert_binary(src, "&=", precedence=2, true); + else if(statement==ID_assign_bitxor) + return convert_binary(src, "^=", precedence=2, true); + else if(statement==ID_assign_bitor) + return convert_binary(src, "|=", precedence=2, true); + else if(statement==ID_assign) + return convert_binary(src, "=", precedence=2, true); + else if(statement==ID_function_call) + return convert_side_effect_expr_function_call(to_side_effect_expr_function_call(src), precedence); + else if(statement==ID_malloc) + return convert_malloc(src, precedence=15); + else if(statement==ID_printf) + return convert_function(src, "PRINTF", precedence=15); + else if(statement==ID_nondet) + return convert_nondet(src, precedence=15); + else if(statement=="prob_coin") + return convert_prob_coin(src, precedence=15); + else if(statement=="prob_unif") + return convert_prob_uniform(src, precedence=15); + else if(statement==ID_literal) + return convert_literal(src, precedence=15); + else if(statement==ID_statement_expression) + return convert_statement_expression(src, precedence=15); + else + return convert_norep(src, precedence); + } + + else if(src.id()==ID_not) + return convert_unary(src, "!", precedence=15); + + else if(src.id()==ID_bitnot) + return convert_unary(src, "~", precedence=15); + + else if(src.id()==ID_mult) + return convert_binary(src, "*", precedence=13, false); + + else if(src.id()==ID_div) + return convert_binary(src, "/", precedence=13, true); + + else if(src.id()==ID_mod) + return convert_binary(src, "%", precedence=13, true); + + else if(src.id()==ID_shl) + return convert_binary(src, "<<", precedence=11, true); + + else if(src.id()==ID_ashr || src.id()==ID_lshr) + return convert_binary(src, ">>", precedence=11, true); + + else if(src.id()==ID_lt || src.id()==ID_gt || + src.id()==ID_le || src.id()==ID_ge) + return convert_binary(src, src.id_string(), precedence=10, true); + + else if(src.id()==ID_notequal) + return convert_binary(src, "!=", precedence=9, true); + + else if(src.id()==ID_equal) + return convert_binary(src, "==", precedence=9, true); + + else if(src.id()==ID_ieee_float_equal) + return convert_function(src, "IEEE_FLOAT_EQUAL", precedence=15); + + else if(src.id()==ID_width) + return convert_function(src, "WIDTH", precedence=15); + + else if(src.id()==ID_ieee_float_notequal) + return convert_function(src, "IEEE_FLOAT_NOTEQUAL", precedence=15); + + else if(src.id()==ID_byte_update_little_endian) + return convert_function(src, "BYTE_UPDATE_LITTLE_ENDIAN", precedence=15); + + else if(src.id()==ID_byte_update_big_endian) + return convert_function(src, "BYTE_UPDATE_BIG_ENDIAN", precedence=15); + + else if(src.id()==ID_abs) + return convert_function(src, "abs", precedence=15); + + else if(src.id()==ID_bitand) + return convert_binary(src, "&", precedence=8, false); + + else if(src.id()==ID_bitxor) + return convert_binary(src, "^", precedence=7, false); + + else if(src.id()==ID_bitor) + return convert_binary(src, "|", precedence=6, false); + + else if(src.id()==ID_and) + return convert_binary(src, "&&", precedence=5, false); + + else if(src.id()==ID_or) + return convert_binary(src, "||", precedence=4, false); + + else if(src.id()==ID_implies) + return convert_binary(src, "=>", precedence=3, true); + + else if(src.id()==ID_if) + return convert_trinary(src, "?", ":", precedence=3); + + else if(src.id()==ID_forall) + return convert_quantifier(src, "FORALL", precedence=2); + + else if(src.id()==ID_exists) + return convert_quantifier(src, "EXISTS", precedence=2); + + else if(src.id()=="lambda") + return convert_quantifier(src, "LAMBDA", precedence=2); + + else if(src.id()==ID_with) + return convert_with(src, precedence=15); + + else if(src.id()==ID_symbol) + return convert_symbol(src, precedence); + + else if(src.id()==ID_next_symbol) + return convert_symbol(src, precedence); + + else if(src.id()==ID_nondet_symbol) + return convert_nondet_symbol(src, precedence); + + else if(src.id()=="predicate_symbol") + return convert_predicate_symbol(src, precedence); + + else if(src.id()=="predicate_next_symbol") + return convert_predicate_next_symbol(src, precedence); + + else if(src.id()=="quantified_symbol") + return convert_quantified_symbol(src, precedence); + + else if(src.id()=="nondet_bool") + return convert_nondet_bool(src, precedence); + + else if(src.id()=="object_descriptor") + return convert_object_descriptor(src, precedence); + + else if(src.id()=="Hoare") + return convert_Hoare(src); + + else if(src.id()==ID_code) + return convert_code(to_code(src)); + + else if(src.id()==ID_constant) + return convert_constant(src, precedence); + + else if(src.id()==ID_string_constant) + return convert_constant(src, precedence); + + else if(src.id()==ID_struct) + return convert_struct(src, precedence); + + else if(src.id()==ID_vector) + return convert_vector(src, precedence); + + else if(src.id()==ID_union) + return convert_union(src, precedence); + + else if(src.id()==ID_array) + return convert_array(src, precedence); + + else if(src.id()=="array-list") + return convert_array_list(src, precedence); + + else if(src.id()==ID_typecast) + return convert_typecast(src, precedence); + + else if(src.id()=="implicit_address_of") + return convert_implicit_address_of(src, precedence); + + else if(src.id()=="implicit_dereference") + return convert_function(src, "IMPLICIT_DEREFERENCE", precedence=15); + + else if(src.id()==ID_comma) + return convert_binary(src, ", ", precedence=1, false); + + else if(src.id()==ID_cond) + return convert_cond(src, precedence); + + else if(std::string(src.id_string(), 0, 9)=="overflow-") + return convert_overflow(src, precedence); + + else if(src.id()==ID_unknown) + return "*"; + + else if(src.id()=="invalid") + return "#"; + + else if(src.id()==ID_extractbit) + return convert_extractbit(src, precedence); + + else if(src.id()==ID_initializer_list) + return convert_initializer_list(src, precedence=15); + + else if(src.id()==ID_designated_initializer) + return convert_designated_initializer(src, precedence=15); + + // no C language expression for internal representation + return convert_norep(src, precedence); +} + +/*******************************************************************\ + +Function: expr2ct::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2ct::convert(const exprt &src) +{ + unsigned precedence; + return convert(src, precedence); +} + +/*******************************************************************\ + +Function: expr2c + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2c(const exprt &expr, const namespacet &ns) +{ + std::string code; + expr2ct expr2c(ns); + expr2c.get_shorthands(expr); + return expr2c.convert(expr); +} + +/*******************************************************************\ + +Function: type2c + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string type2c(const typet &type, const namespacet &ns) +{ + expr2ct expr2c(ns); + //expr2c.get_shorthands(expr); + return expr2c.convert(type); +} diff --git a/src/ansi-c/expr2c.h b/src/ansi-c/expr2c.h new file mode 100644 index 00000000000..c4e22b60f5e --- /dev/null +++ b/src/ansi-c/expr2c.h @@ -0,0 +1,18 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_EXPR2C_H +#define CPROVER_EXPR2C_H + +#include +#include + +std::string expr2c(const exprt &expr, const namespacet &ns); +std::string type2c(const typet &type, const namespacet &ns); + +#endif diff --git a/src/ansi-c/expr2c_class.h b/src/ansi-c/expr2c_class.h new file mode 100644 index 00000000000..56d433fb615 --- /dev/null +++ b/src/ansi-c/expr2c_class.h @@ -0,0 +1,185 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_EXPR2C_CLASS_H +#define CPROVER_EXPR2C_CLASS_H + +#include +#include + +#include +#include +#include +#include + +#include "c_qualifiers.h" + +class expr2ct +{ +public: + expr2ct(const namespacet &_ns):ns(_ns) { } + virtual ~expr2ct() { } + + virtual std::string convert(const typet &src); + virtual std::string convert(const exprt &src); + + void get_shorthands(const exprt &expr); + +protected: + const namespacet &ns; + + virtual std::string convert_rec( + const typet &src, + const c_qualifierst &qualifiers); + + static std::string indent_str(unsigned indent); + + std::set symbols; + std::map shorthands; + std::set ns_collision; + + void get_symbols(const exprt &expr); + std::string id_shorthand(const exprt &expr) const; + + std::string convert_typecast( + const exprt &src, unsigned &precedence); + + std::string convert_pointer_arithmetic(const exprt &src, + unsigned &precedence); + + std::string convert_pointer_difference(const exprt &src, + unsigned &precedence); + + std::string convert_implicit_address_of( + const exprt &src, unsigned &precedence); + + std::string convert_binary( + const exprt &src, const std::string &symbol, + unsigned precedence, bool full_parentheses); + + std::string convert_cond( + const exprt &src, unsigned precedence); + + std::string convert_struct_member_value( + const exprt &src, unsigned precedence); + + std::string convert_array_member_value( + const exprt &src, unsigned precedence); + + std::string convert_member( + const member_exprt &src, unsigned precedence); + + std::string convert_pointer_object_has_type( + const exprt &src, unsigned precedence); + + std::string convert_array_of(const exprt &src, unsigned precedence); + + std::string convert_trinary( + const exprt &src, const std::string &symbol1, + const std::string &symbol2, unsigned precedence); + + std::string convert_overflow( + const exprt &src, unsigned &precedence); + + std::string convert_quantifier( + const exprt &src, const std::string &symbol, + unsigned precedence); + + std::string convert_with( + const exprt &src, unsigned precedence); + + std::string convert_index( + const exprt &src, unsigned precedence); + + std::string convert_byte_extract( + const exprt &src, + unsigned precedence); + + std::string convert_byte_update( + const exprt &src, + unsigned precedence); + + std::string convert_extractbit( + const exprt &src, + unsigned precedence); + + std::string convert_unary( + const exprt &src, const std::string &symbol, + unsigned precedence); + + std::string convert_unary_post( + const exprt &src, const std::string &symbol, + unsigned precedence); + + std::string convert_function( + const exprt &src, const std::string &symbol, + unsigned precedence); + + std::string convert_Hoare(const exprt &src); + + std::string convert_code(const codet &src); + virtual std::string convert_code(const codet &src, unsigned indent); + std::string convert_code_label(const codet &src, unsigned indent); + std::string convert_code_asm(const codet &src, unsigned indent); + std::string convert_code_assign(const codet &src, unsigned indent); + std::string convert_code_free(const codet &src, unsigned indent); + std::string convert_code_init(const codet &src, unsigned indent); + std::string convert_code_ifthenelse(const codet &src, unsigned indent); + std::string convert_code_for(const codet &src, unsigned indent); + std::string convert_code_while(const codet &src, unsigned indent); + std::string convert_code_dowhile(const codet &src, unsigned indent); + std::string convert_code_block(const codet &src, unsigned indent); + std::string convert_code_expression(const codet &src, unsigned indent); + std::string convert_code_return(const codet &src, unsigned indent); + std::string convert_code_goto(const codet &src, unsigned indent); + std::string convert_code_assume(const codet &src, unsigned indent); + std::string convert_code_assert(const codet &src, unsigned indent); + std::string convert_code_break(const codet &src, unsigned indent); + std::string convert_code_switch(const codet &src, unsigned indent); + std::string convert_code_continue(const codet &src, unsigned indent); + std::string convert_code_decl(const codet &src, unsigned indent); + std::string convert_code_function_call(const code_function_callt &src, unsigned indent); + std::string convert_code_lock(const codet &src, unsigned indent); + std::string convert_code_unlock(const codet &src, unsigned indent); + std::string convert_code_printf(const codet &src, unsigned indent); + std::string convert_code_input(const codet &src, unsigned indent); + std::string convert_code_array_set(const codet &src, unsigned indent); + std::string convert_code_array_copy(const codet &src, unsigned indent); + + virtual std::string convert(const exprt &src, unsigned &precedence); + + std::string convert_function_application(const function_application_exprt &src, unsigned &precedence); + std::string convert_side_effect_expr_function_call(const side_effect_expr_function_callt &src, unsigned &precedence); + std::string convert_malloc(const exprt &src, unsigned &precedence); + std::string convert_nondet(const exprt &src, unsigned &precedence); + std::string convert_prob_coin(const exprt &src, unsigned &precedence); + std::string convert_prob_uniform(const exprt &src, unsigned &precedence); + std::string convert_statement_expression(const exprt &src, unsigned &precendence); + + virtual std::string convert_symbol(const exprt &src, unsigned &precedence); + std::string convert_predicate_symbol(const exprt &src, unsigned &precedence); + std::string convert_predicate_next_symbol(const exprt &src, unsigned &precedence); + std::string convert_nondet_symbol(const exprt &src, unsigned &precedence); + std::string convert_quantified_symbol(const exprt &src, unsigned &precedence); + std::string convert_nondet_bool(const exprt &src, unsigned &precedence); + std::string convert_object_descriptor(const exprt &src, unsigned &precedence); + std::string convert_literal(const exprt &src, unsigned &precedence); + virtual std::string convert_constant(const exprt &src, unsigned &precedence); + + std::string convert_norep(const exprt &src, unsigned &precedence); + + virtual std::string convert_struct(const exprt &src, unsigned &precedence); + std::string convert_union(const exprt &src, unsigned &precedence); + std::string convert_vector(const exprt &src, unsigned &precedence); + std::string convert_array(const exprt &src, unsigned &precedence); + std::string convert_array_list(const exprt &src, unsigned &precedence); + std::string convert_initializer_list(const exprt &src, unsigned &precedence); + std::string convert_designated_initializer(const exprt &src, unsigned &precedence); +}; + +#endif diff --git a/src/ansi-c/gcc_builtin_headers.h b/src/ansi-c/gcc_builtin_headers.h new file mode 100644 index 00000000000..2c8debbe311 --- /dev/null +++ b/src/ansi-c/gcc_builtin_headers.h @@ -0,0 +1,93 @@ +#define GCC_BUILTIN_HEADERS \ + "typedef void* __builtin_va_list;\n" \ + "void __builtin_va_start(void *ap, const void *x);\n" \ + "void __builtin_va_end(void *ap);\n" \ + "void __builtin_va_copy(__builtin_va_list dest, __builtin_va_list src);\n" \ + "int __builtin_constant_p(int);\n" \ + "\n" \ + "int __builtin_abs(int);\n" \ + "long int __builtin_labs(long);\n" \ + "double __builtin_cos(double);\n" \ + "double __builtin_cosf(double);\n" \ + "double __builtin_fabs(double);\n" \ + "double __builtin_fabsf(double);\n" \ + "int __builtin_memcmp(const void *s1, const void *s2, unsigned n);\n" \ + "void *__builtin_memcpy(void *dest, const void *src, unsigned n);\n" \ + "void *__builtin___memcpy_chk(void *dest, const void *src, unsigned n, unsigned size);\n" \ + "char *__builtin___memmove_chk(void *dest, const void *src, unsigned n, unsigned size);\n" \ + "double __builtin_sin(double);\n" \ + "float __builtin_sinf(float);\n" \ + "double __builtin_sqrt(double);\n" \ + "float __builtin_sqrtf(float);\n" \ + "int __builtin_strcmp(const char *s1, const char *s2);\n" \ + "unsigned __builtin_strlen(const char *s);\n" \ + "int __builtin_strncmp(const char *s1, const char *s2, unsigned n);\n" \ + "void __builtin_abort();\n" \ + "void __builtin_prefetch(void *, ...);\n" \ + "int __builtin_printf(const char *fmt, ...);\n" \ + "int __builtin_fprintf(void *stream, const char *fmt, ...);\n" \ + "int __builtin_fscanf(void *stream, const char *fmt, ...);\n" \ + "int __builtin_scanf(const char *str, const char *fmt, ...);\n" \ + "int __builtin_fputs(const char *s, void *stream);\n" \ + "long __builtin_expect(long, long);\n" \ + "void *__builtin_memset(void *s, int c, unsigned n);\n" \ + "void *__builtin___memset_chk(void *s, int c, unsigned n, unsigned size);\n" \ + "char *__builtin_strcat(char *dest, const char *src);\n" \ + "char *__builtin___strcat_chk(char *dest, const char *src, unsigned size);\n" \ + "char *__builtin_strcpy(char *dest, const char *src);\n" \ + "char *__builtin___strcpy_chk(char *dest, const char *src, unsigned size);\n" \ + "char *__builtin_strncpy(char *dest, const char *src, unsigned n);\n" \ + "char *__builtin___strncpy_chk(char *dest, const char *src, unsigned n, unsigned size);\n" \ + "void __builtin_exit(int status);\n" \ + "char *__builtin_strchr(const char *s, int c);\n" \ + "unsigned __builtin_strspn(const char *s, const char *accept);\n" \ + "unsigned __builtin_strcspn(const char *s, const char *reject);\n" \ + "char *__builtin_strstr(const char *a, const char *b);\n" \ + "char *__builtin_strpbrk(const char *s, const char *accept);\n" \ + "char *__builtin_strrchr(const char *s, int c);\n" \ + "char *__builtin_strncat(char *dest, const char *src, unsigned n);\n" \ + "char *__builtin___strncat_chk(char *dest, const char *src, unsigned n, unsigned size);\n" \ + "char *__builtin___stpcpy_chk(char *dest, const char *src, unsigned size);\n" \ + "void *__builtin_alloca(unsigned s);\n" \ + "int __builtin_ffs(int i);\n" \ + "char *__builtin_index(const char *s, int c);\n" \ + "char *__builtin_rindex(const char *s, int c);\n" \ + "int __builtin_bcmp(const void *s1, const void *s2, unsigned n);\n" \ + "int __builtin_bzero(void *s, unsigned n);\n" \ + "long double __builtin_sinl(long double x);\n" \ + "long double __builtin_cosl(long double x);\n" \ + "long double __builtin_sqrtl(long double x);\n" \ + "long double __builtin_fabsl(long double x);\n" \ + "int __builtin_popcount(unsigned int x);\n" \ + "int __builtin_popcountll(unsigned long long int x);\n" \ + "float __builtin_huge_valf();\n" \ + "double __builtin_huge_val();\n" \ + "float __builtin_inff();\n" \ + "double __builtin_inf();\n" \ + "float __builtin_nanf(const char *);\n" \ + "double __builtin_nan(const char *);\n" \ + "float __builtin_nansf(const char *);\n" \ + "double __builtin_nans(const char *);\n" \ + "long double __builtin_infl();\n" \ + "unsigned __builtin_object_size();\n" \ + "void *__builtin_return_address(unsigned level);\n" \ + "void *__builtin_extract_return_addr(void *);\n" \ + "int __builtin_choose_expr(_Bool, ...);\n" \ + "int __sync_fetch_and_add(volatile void *, int, ...);\n" \ + "int __sync_fetch_and_sub(volatile void *, int, ...);\n" \ + "int __sync_fetch_and_or(volatile void *, int, ...);\n" \ + "int __sync_fetch_and_and(volatile void *, int, ...);\n" \ + "int __sync_fetch_and_xor(volatile void *, int, ...);\n" \ + "int __sync_fetch_and_nand(volatile void *, int, ...);\n" \ + "int __sync_add_and_fetch(volatile void *, int, ...);\n" \ + "int __sync_sub_and_fetch(volatile void *, int, ...);\n" \ + "int __sync_or_and_fetch(volatile void *, int, ...);\n" \ + "int __sync_and_and_fetch(volatile void *, int, ...);\n" \ + "int __sync_xor_and_fetch(volatile void *, int, ...);\n" \ + "int __sync_nand_and_fetch(volatile void *, int, ...);\n" \ + "_Bool __sync_bool_compare_and_swap(volatile void *, int oldval, int newval, ...);\n" \ + "int __sync_val_compare_and_swap(volatile void *, int oldval, int newval, ...);\n" \ + "void __sync_synchronize();\n" \ + "int __sync_lock_test_and_set(volatile void *, ...);\n" \ + "void __sync_lock_release(volatile void *, ...);\n" \ + "\n\n" diff --git a/src/ansi-c/internal_additions.cpp b/src/ansi-c/internal_additions.cpp new file mode 100644 index 00000000000..156dfef2b07 --- /dev/null +++ b/src/ansi-c/internal_additions.cpp @@ -0,0 +1,168 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "gcc_builtin_headers.h" +#include "arm_builtin_headers.h" +#include "internal_additions.h" + +/*******************************************************************\ + +Function: architecture_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static std::string architecture_string(int value, const char *s) +{ + return std::string("const int __CPROVER_architecture_")+ + std::string(s)+ + "="+i2string(value)+";\n"; +} + +/*******************************************************************\ + +Function: ansi_c_internal_additions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_internal_additions(std::string &code) +{ + code+= + "typedef __typeof__(sizeof(int)) __CPROVER_size_t;\n" + "void __CPROVER_assume(_Bool assumption);\n" + "void assert(_Bool assertion);\n" + "void __CPROVER_assert(_Bool assertion, const char *description);\n" + "_Bool __CPROVER_equal();\n" + "_Bool __CPROVER_same_object(const void *, const void *);\n" + "_Bool __CPROVER_is_zero_string(const void *);\n" + "__CPROVER_size_t __CPROVER_zero_string_length(const void *);\n" + "__CPROVER_size_t __CPROVER_buffer_size(const void *);\n" + + "const unsigned __CPROVER_constant_infinity_uint;\n" + "typedef void __CPROVER_integer;\n" + "typedef void __CPROVER_rational;\n" + "void __CPROVER_initialize(void);\n" + "void __CPROVER_input(const char *id, ...);\n" + "void __CPROVER_output(const char *id, ...);\n" + "void __CPROVER_cover(_Bool condition);\n" + "void __CPROVER_atomic_begin();\n" + "void __CPROVER_atomic_end();\n" + + // traces + "void CBMC_trace(int lvl, const char *event, ...);\n" + + // pointers + "unsigned __CPROVER_POINTER_OBJECT(const void *p);\n" + "signed __CPROVER_POINTER_OFFSET(const void *p);\n" + "_Bool __CPROVER_DYNAMIC_OBJECT(const void *p);\n" + "extern unsigned char __CPROVER_memory[__CPROVER_constant_infinity_uint];\n" + + // malloc + "void *__CPROVER_malloc(__CPROVER_size_t size);\n" + "const void *__CPROVER_deallocated=0;\n" + "const void *__CPROVER_malloc_object=0;\n" + "__CPROVER_size_t __CPROVER_malloc_size;\n" + + // obsolete, will go away + "_Bool __CPROVER_alloc[__CPROVER_constant_infinity_uint];\n" + "__CPROVER_size_t __CPROVER_alloc_size[__CPROVER_constant_infinity_uint];\n" + + // this is ANSI-C + "extern __CPROVER_thread_local const char __func__[__CPROVER_constant_infinity_uint];\n" + + // this is GCC + "extern __CPROVER_thread_local const char __FUNCTION__[__CPROVER_constant_infinity_uint];\n" + "extern __CPROVER_thread_local const char __PRETTY_FUNCTION__[__CPROVER_constant_infinity_uint];\n" + + // float stuff + "_Bool __CPROVER_isnan(double f);\n" + "_Bool __CPROVER_isfinite(double f);\n" + "_Bool __CPROVER_isinf(double f);\n" + "_Bool __CPROVER_isnormal(double f);\n" + "_Bool __CPROVER_sign(double f);\n" + "extern int __CPROVER_rounding_mode;\n" + + // absolute value + "int __CPROVER_abs(int x);\n" + "long int __CPROVER_labs(long int x);\n" + "double __CPROVER_fabs(double x);\n" + "long double __CPROVER_fabsl(long double x);\n" + "float __CPROVER_fabsf(float x);\n" + + // arrays + "_Bool __CPROVER_array_equal(const void array1[], const void array2[]);\n" + "void __CPROVER_array_copy(const void dest[], const void src[]);\n" + "void __CPROVER_array_set(const void dest[], ...);\n" + + // k-induction + "void __CPROVER_k_induction_hint(unsigned min, unsigned max, " + "unsigned step, unsigned loop_free);\n" + + // manual specification of predicates + "void __CPROVER_predicate(_Bool predicate);\n" + + // GCC junk stuff + GCC_BUILTIN_HEADERS + + "\n"; + + if(config.ansi_c.os==configt::ansi_ct::OS_WIN) + code+="int __noop();\n"; // this is Visual C/C++ + + // ARM stuff + if(config.ansi_c.mode==configt::ansi_ct::MODE_ARM) + code+=ARM_BUILTIN_HEADERS; + + // Architecture strings + ansi_c_architecture_strings(code); +} + +/*******************************************************************\ + +Function: architecture_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ansi_c_architecture_strings(std::string &code) +{ + code+=architecture_string(config.ansi_c.int_width, "int_width"); + code+=architecture_string(config.ansi_c.long_int_width, "long_int_width"); + code+=architecture_string(config.ansi_c.char_width, "char_width"); + code+=architecture_string(config.ansi_c.short_int_width, "short_int_width"); + code+=architecture_string(config.ansi_c.long_long_int_width, "long_long_int_width"); + code+=architecture_string(config.ansi_c.pointer_width, "pointer_width"); + code+=architecture_string(config.ansi_c.char_is_unsigned, "char_is_unsigned"); + code+=architecture_string(config.ansi_c.int_width, "word_size"); // old + code+=architecture_string(config.ansi_c.use_fixed_for_float, "fixed_for_float"); + code+=architecture_string(config.ansi_c.alignment, "alignment"); + code+=architecture_string(config.ansi_c.single_width, "single_width"); + code+=architecture_string(config.ansi_c.double_width, "double_width"); + code+=architecture_string(config.ansi_c.long_double_width, "long_double_width"); + code+=architecture_string(config.ansi_c.wchar_t_width, "wchar_t_width"); + code+=architecture_string(config.ansi_c.endianess, "endianess"); +} diff --git a/src/ansi-c/internal_additions.h b/src/ansi-c/internal_additions.h new file mode 100644 index 00000000000..4fc765509bd --- /dev/null +++ b/src/ansi-c/internal_additions.h @@ -0,0 +1,12 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +void ansi_c_internal_additions(std::string &code); +void ansi_c_architecture_strings(std::string &code); diff --git a/src/ansi-c/library/converter.cpp b/src/ansi-c/library/converter.cpp new file mode 100644 index 00000000000..c2420859cc4 --- /dev/null +++ b/src/ansi-c/library/converter.cpp @@ -0,0 +1,56 @@ +#include +#include + +bool has_prefix(const std::string &s, const std::string &prefix) +{ + return std::string(s, 0, prefix.size())==prefix; +} + +int main() +{ + std::string line; + bool first=true; + + std::cout << "{\n"; + + while(getline(std::cin, line)) + { + if(has_prefix(line, "/* FUNCTION: ")) + { + if(first) first=false; else std::cout << "},\n"; + + std::string function=std::string(line, 13, std::string::npos); + std::size_t pos=function.find(' '); + if(pos!=std::string::npos) function=std::string(function, 0, pos); + + std::cout << "{ \"c::" << function << "\",\n"; + std::cout << " \"#line 1 \\\"-" << function << "\\\"\\n\"\n"; + } + else if(!first) + { + std::cout << " \""; + + for(unsigned i=0; i='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); } + +/* FUNCTION: isalpha */ + +inline int isalpha(int c) +{ return (c>='a' && c<='z') || (c>='A' && c<='Z'); } + +/* FUNCTION: isblank */ + +inline int isblank(int c) +{ return c==' ' || c=='\t'; } + +/* FUNCTION: iscntrl */ + +inline int iscntrl(int c) +{ return (c>=0 && c<='\037') || c=='\177'; } + +/* FUNCTION: isdigit */ + +inline int isdigit(int c) +{ return c>='0' && c<='9'; } + +/* FUNCTION: isgraph */ + +inline int isgraph(int c) +{ return c>='!' && c<='~'; } + +/* FUNCTION: islower */ + +inline int islower(int c) +{ return c>='a' && c<='z'; } + +/* FUNCTION: isprint */ + +inline int isprint(int c) +{ return c>=' ' && c<='~'; } + +/* FUNCTION: ispunct */ + +inline int ispunct(int c) +{ return c=='!' || + c=='"' || + c=='#' || + c=='$' || + c=='%' || + c=='&' || + c=='\'' || + c=='(' || + c==')' || + c=='*' || + c=='+' || + c==',' || + c=='-' || + c=='.' || + c=='/' || + c==':' || + c==';' || + c=='<' || + c=='=' || + c=='>' || + c=='?' || + c=='@' || + c=='[' || + c=='\\' || + c==']' || + c=='^' || + c=='_' || + c=='`' || + c=='{' || + c=='|' || + c=='}' || + c=='~'; } + +/* FUNCTION: isspace */ + +inline int isspace(int c) +{ return c=='\t' || + c=='\n' || + c=='\v' || + c=='\f' || + c=='\r' || + c==' '; } + +/* FUNCTION: isupper */ + +inline int isupper(int c) +{ return c>='A' && c<='Z'; } + +/* FUNCTION: isxdigit */ + +inline int isxdigit(int c) +{ return (c>='A' && c<='F') || (c>='a' && c<='f') || (c>='0' && c<='9'); } + +/* FUNCTION: tolower */ + +inline int tolower(int c) +{ return (c>='A' && c<='Z')?c+('a'-'A'):c; } + +/* FUNCTION: toupper */ + +inline int toupper(int c) +{ return (c>='a' && c<='z')?c-('a'-'A'):c; } + diff --git a/src/ansi-c/library/err.c b/src/ansi-c/library/err.c new file mode 100644 index 00000000000..818aae6c2c9 --- /dev/null +++ b/src/ansi-c/library/err.c @@ -0,0 +1,47 @@ +/* FUNCTION: err */ + +#ifndef __CPROVER_ERR_H_INCLUDED +#include +#define __CPROVER_ERR_H_INCLUDED +#endif + +void err(int eval, const char *fmt, ...) +{ + // should check arguments +} + +/* FUNCTION: err */ + +#ifndef __CPROVER_ERR_H_INCLUDED +#include +#define __CPROVER_ERR_H_INCLUDED +#endif + +void errx(int eval, const char *fmt, ...) +{ + // should check arguments +} + +/* FUNCTION: warn */ + +#ifndef __CPROVER_ERR_H_INCLUDED +#include +#define __CPROVER_ERR_H_INCLUDED +#endif + +void warn(const char *fmt, ...) +{ + // should check arguments +} + +/* FUNCTION: warnx */ + +#ifndef __CPROVER_ERR_H_INCLUDED +#include +#define __CPROVER_ERR_H_INCLUDED +#endif + +void warnx(const char *fmt, ...) +{ + // should check arguments +} diff --git a/src/ansi-c/library/fcntl.c b/src/ansi-c/library/fcntl.c new file mode 100644 index 00000000000..bea044fced7 --- /dev/null +++ b/src/ansi-c/library/fcntl.c @@ -0,0 +1,13 @@ +/* FUNCTION: fcntl */ + +#ifndef __CPROVER_FCNTL_H_INCLUDED +#include +#define __CPROVER_FCNTL_H_INCLUDED +#endif + +int fcntl(int fd, int cmd, ...) +{ +__CPROVER_hide: + int return_value; + return return_value; +} diff --git a/src/ansi-c/library/fenv.c b/src/ansi-c/library/fenv.c new file mode 100644 index 00000000000..faee0a6c205 --- /dev/null +++ b/src/ansi-c/library/fenv.c @@ -0,0 +1,16 @@ +/* FUNCTION: fegetround */ + +inline int fegetround(void) +{ +__CPROVER_hide: + return __CPROVER_rounding_mode; +} + +/* FUNCTION: fesetround */ + +inline int fesetround(int rounding_mode) +{ +__CPROVER_hide: + __CPROVER_rounding_mode=rounding_mode; + return 0; // we never fail +} diff --git a/src/ansi-c/library/getopt.c b/src/ansi-c/library/getopt.c new file mode 100644 index 00000000000..c7fdc55287f --- /dev/null +++ b/src/ansi-c/library/getopt.c @@ -0,0 +1,16 @@ +/* FUNCTION: getopt */ + +extern char *optarg; + +inline int getopt(int argc, char * const argv[], + const char *optstring) +{ + __CPROVER_HIDE:; + unsigned result_index; + __CPROVER_assume(result_index + +in_addr_t inet_addr(const char *cp) +{ + __CPROVER_HIDE:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(cp), "inet_addr zero-termination of argument"); + #endif + + in_addr_t result; + return result; +} + +/* FUNCTION: inet_aton */ + +int inet_aton(const char *cp, struct in_addr *pin) +{ + __CPROVER_HIDE:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(cp), "inet_aton zero-termination of name argument"); + #endif + + int result; + return result; +} + +/* FUNCTION: inet_network */ + +in_addr_t inet_network(const char *cp) +{ + __CPROVER_HIDE:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(cp), "inet_network zero-termination of name argument"); + #endif + + in_addr_t result; + return result; +} + diff --git a/src/ansi-c/library/math.c b/src/ansi-c/library/math.c new file mode 100644 index 00000000000..ec486070994 --- /dev/null +++ b/src/ansi-c/library/math.c @@ -0,0 +1,84 @@ +/* FUNCTION: fabs */ + +inline double fabs(double d) { return __CPROVER_fabs(d); } + +/* FUNCTION: fabsl */ + +inline long double fabsl(long double d) { return __CPROVER_fabsl(d); } + +/* FUNCTION: fabsf */ + +inline float fabsf(float f) { return __CPROVER_fabsf(f); } + +/* FUNCTION: isfinite */ + +int isfinite(double d) { return __CPROVER_isfinite(d); } + +/* FUNCTION: isinf */ + +inline int isinf(double d) { return __CPROVER_isinf(d); } + +/* FUNCTION: isnan */ + +inline int isnan(double d) { return __CPROVER_isnan(d); } + +/* FUNCTION: isnormal */ + +int isnormal(double d) { return __CPROVER_isnormal(d); } + +/* FUNCTION: signbit */ + +inline int signbit(double d) { return __CPROVER_sign(d); } + +/* FUNCTION: __signbitf */ + +inline float __signbitf(float f) { return __CPROVER_sign(f); } + +/* FUNCTION: __signbit */ + +inline double __signbit(double d) { return __CPROVER_sign(d); } + +/* FUNCTION: __fpclassifyd */ + +#ifndef __CPROVER_MATH_H_INCLUDED +#include +#define __CPROVER_MATH_H_INCLUDED +#endif + +inline int __fpclassifyd(double d) { + if(__CPROVER_isnan(d)) return FP_NAN; + if(__CPROVER_isinf(d)) return FP_INFINITE; + if(d==0) return FP_ZERO; + if(__CPROVER_isnormal(d)) return FP_NORMAL; + return FP_SUBNORMAL; +} + +/* FUNCTION: __fpclassifyf */ + +#ifndef __CPROVER_MATH_H_INCLUDED +#include +#define __CPROVER_MATH_H_INCLUDED +#endif + +inline int __fpclassifyf(float f) { + if(__CPROVER_isnan(f)) return FP_NAN; + if(__CPROVER_isinf(f)) return FP_INFINITE; + if(f==0) return FP_ZERO; + if(__CPROVER_isnormal(f)) return FP_NORMAL; + return FP_SUBNORMAL; +} + +/* FUNCTION: __fpclassify */ + +#ifndef __CPROVER_MATH_H_INCLUDED +#include +#define __CPROVER_MATH_H_INCLUDED +#endif + +inline int __fpclassify(long double d) { + if(__CPROVER_isnan(d)) return FP_NAN; + if(__CPROVER_isinf(d)) return FP_INFINITE; + if(d==0) return FP_ZERO; + if(__CPROVER_isnormal(d)) return FP_NORMAL; + return FP_SUBNORMAL; +} diff --git a/src/ansi-c/library/netdb.c b/src/ansi-c/library/netdb.c new file mode 100644 index 00000000000..ea022830d9f --- /dev/null +++ b/src/ansi-c/library/netdb.c @@ -0,0 +1,52 @@ +/* FUNCTION: gethostbyname */ + +#include + +struct hostent *gethostbyname(const char *name) +{ + __CPROVER_HIDE:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(name), "gethostbyname zero-termination of name argument"); + #endif + + _Bool error; + if(error) return 0; + + // quite restrictive, as will alias between calls + static struct hostent result; + + // we whould be filling in the fields of this + return &result; +} + +/* FUNCTION: gethostbyaddr */ + +struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type) +{ + __CPROVER_HIDE:; + + _Bool error; + if(error) return 0; + + // quite restrictive, as will alias between calls + static struct hostent result; + + // we whould be filling in the fields of this + return &result; +} + +/* FUNCTION: gethostent */ + +struct hostent *gethostent(void) +{ + __CPROVER_HIDE:; + + _Bool error; + if(error) return 0; + + // quite restrictive, as will alias between calls + static struct hostent result; + + // we whould be filling in the fields of this + return &result; +} diff --git a/src/ansi-c/library/new.c b/src/ansi-c/library/new.c new file mode 100644 index 00000000000..5223c5ebfd5 --- /dev/null +++ b/src/ansi-c/library/new.c @@ -0,0 +1,54 @@ +/* FUNCTION: __new */ + +inline void *__new(__typeof__(sizeof(int)) malloc_size) +{ + __CPROVER_HIDE:; + void *res; + res=__CPROVER_malloc(malloc_size); + + // make sure it's not recorded as deallocated + __CPROVER_deallocated=(res==__CPROVER_deallocated)?0:__CPROVER_deallocated; + + // record the object size for non-determistic bounds checking + _Bool record_malloc; + if(record_malloc) + { + __CPROVER_malloc_object=res; + __CPROVER_malloc_size=malloc_size; + } + + return res; +} + +/* FUNCTION: __placement_new */ + +inline void *__placement_new(__typeof__(sizeof(int)) malloc_size, void *p) +{ + __CPROVER_HIDE:; + return p; +} + +/* FUNCTION: __delete */ + +inline void __delete(void *ptr) +{ + __CPROVER_HIDE:; + // If ptr is NULL, no operation is performed. + if(ptr!=NULL) + { + // is it dynamic? + __CPROVER_assert(__CPROVER_DYNAMIC_OBJECT(ptr), + "delete argument is dynamic object"); + __CPROVER_assert(__CPROVER_POINTER_OFFSET(ptr)==0, + "delete argument has offset zero"); + + // catch double delete + if(__CPROVER_deallocated==ptr) + __CPROVER_assert(0, "double delete"); + + // non-deterministically record as deallocated + _Bool record; + if(record) __CPROVER_deallocated=ptr; + } +} + diff --git a/src/ansi-c/library/noop.c b/src/ansi-c/library/noop.c new file mode 100644 index 00000000000..fae5d0bd65d --- /dev/null +++ b/src/ansi-c/library/noop.c @@ -0,0 +1,6 @@ +/* FUNCTION: __noop */ + +int __noop() +{ + // does nothing +} diff --git a/src/ansi-c/library/pthread_lib.c b/src/ansi-c/library/pthread_lib.c new file mode 100644 index 00000000000..bcbc3c67eb9 --- /dev/null +++ b/src/ansi-c/library/pthread_lib.c @@ -0,0 +1,184 @@ +/* FUNCTION: pthread_mutex_init */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_mutex_init( + pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) +{ __CPROVER_HIDE: *((char *)mutex)=0; return 0; } + +/* FUNCTION: pthread_mutex_lock */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_mutex_lock(pthread_mutex_t *mutex) +{ __CPROVER_HIDE: + __CPROVER_atomic_begin(); + __CPROVER_assume(!*((char *)mutex)); + *((char *)mutex)=1; + __CPROVER_atomic_end(); + return 0; // we never fail +} + +/* FUNCTION: pthread_mutex_trylock */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + __CPROVER_HIDE: + __CPROVER_atomic_begin(); + if(*((char *)mutex)) { __CPROVER_atomic_end(); return 1; } + *((char *)mutex)=1; + __CPROVER_atomic_end(); + return 0; +} + +/* FUNCTION: pthread_mutex_unlock */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ __CPROVER_HIDE: + __CPROVER_assert(*((char *)mutex), + "must hold lock upon unlock"); + *((char *)mutex)=0; + return 0; // we never fail +} + +/* FUNCTION: pthread_mutex_destroy */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ } + +/* FUNCTION: pthread_exit */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline void pthread_exit(void *value_ptr) +{ __CPROVER_hide:; __CPROVER_assume(0); } + +/* FUNCTION: pthread_rwlock_destroy */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_rwlock_destroy(pthread_rwlock_t *lock) +{ } + +/* FUNCTION: pthread_rwlock_init */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_rwlock_init(pthread_rwlock_t *lock, + const pthread_rwlockattr_t *attr) +{ __CPROVER_HIDE: (*(char *)lock)=0; } + +/* FUNCTION: pthread_rwlock_rdlock */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_rwlock_rdlock(pthread_rwlock_t *lock) +{ /* TODO */ } + +/* FUNCTION: pthread_rwlock_tryrdlock */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_rwlock_tryrdlock(pthread_rwlock_t *lock) +{ /* TODO */ } + +/* FUNCTION: pthread_rwlock_trywrlock */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_rwlock_trywrlock(pthread_rwlock_t *lock) +{ __CPROVER_HIDE: + __CPROVER_atomic_begin(); + if(*(char *)lock) { __CPROVER_atomic_end(); return 1; } + (*(char *)lock)=1; + __CPROVER_atomic_end(); + return 0; +} + +/* FUNCTION: pthread_rwlock_unlock */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_rwlock_unlock(pthread_rwlock_t *lock) +{ __CPROVER_HIDE: (*(char *)lock)=0; } + +/* FUNCTION: pthread_rwlock_wrlock */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_rwlock_wrlock(pthread_rwlock_t *lock) +{ __CPROVER_HIDE: + __CPROVER_atomic_begin(); + __CPROVER_assume(!(*(char *)lock)); + (*(char *)lock)=1; + __CPROVER_atomic_end(); + return 0; // we never fail +} + +/* FUNCTION: pthread_create */ + +#ifndef __CPROVER_PTHREAD_H_INCLUDED +#include +#define __CPROVER_PTHREAD_H_INCLUDED +#endif + +inline int pthread_create( + pthread_t *thread, + pthread_attr_t *attr, + void * (*start_routine)(void *), + void *arg) +{ + __CPROVER_HIDE:; + pthread_t thread_id; + + if(!thread) *thread=thread_id; + __CPROVER_ASYNC_1: start_routine(arg); + + return 0; +} + diff --git a/src/ansi-c/library/signal.c b/src/ansi-c/library/signal.c new file mode 100644 index 00000000000..22032e4d70e --- /dev/null +++ b/src/ansi-c/library/signal.c @@ -0,0 +1,15 @@ +/* FUNCTION: kill */ + +#ifndef __CPROVER_SYS_TYPES_H_INCLUDED +#include +#define __CPROVER_SYS_TYPES_H_INCLUDED +#endif + +#ifndef __CPROVER_SIGNAL_H_INCLUDED +#include +#define __CPROVER_SIGNAL_H_INCLUDED +#endif + +int kill(pid_t pid, int sig) +{ +} diff --git a/src/ansi-c/library/stdio.c b/src/ansi-c/library/stdio.c new file mode 100644 index 00000000000..97b3c8f2f57 --- /dev/null +++ b/src/ansi-c/library/stdio.c @@ -0,0 +1,417 @@ + +/* FUNCTION: putchar */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int putchar(int c) +{ + _Bool error; + __CPROVER_HIDE: printf("%c", c); + return (error?-1:c); +} + +/* FUNCTION: puts */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int puts(const char *s) +{ + _Bool error; + int ret; + __CPROVER_HIDE: printf("%s\n", s); + if(error) ret=-1; else __CPROVER_assume(ret>=0); + return ret; +} + +/* FUNCTION: fopen */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +#ifndef __CPROVER_STDLIB_H_INCLUDED +#include +#define __CPROVER_STDLIB_H_INCLUDED +#endif + +inline FILE *fopen(const char *filename, const char *m) +{ + __CPROVER_HIDE:; + FILE *f=malloc(sizeof(FILE)); + + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(f), "fopen zero-termination of 1st argument"); + __CPROVER_assert(__CPROVER_is_zero_string(m), "fopen zero-termination of 2nd argument"); + #endif + + return f; +} + +/* FUNCTION: fclose */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int fclose(FILE *stream) +{ + __CPROVER_HIDE:; + int return_value; + free(stream); + return return_value; +} + +/* FUNCTION: fdopen */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +#ifndef __CPROVER_STDLIB_H_INCLUDED +#include +#define __CPROVER_STDLIB_H_INCLUDED +#endif + +inline FILE *fdopen(int handle, const char *m) +{ + __CPROVER_HIDE:; + FILE *f=malloc(sizeof(FILE)); + + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(m), + "fdopen zero-termination of 2nd argument"); + #endif + + return f; +} + +/* FUNCTION: fgets */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline char *fgets(char *str, int size, FILE *stream) +{ + __CPROVER_HIDE:; + _Bool error; + + #ifdef __CPROVER_STRING_ABSTRACTION + int resulting_size; + __CPROVER_assert(__CPROVER_buffer_size(str)>=size, "buffer-overflow in fgets"); + if(size>0) + { + __CPROVER_assume(resulting_size +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline size_t fread( + void *ptr, + size_t size, + size_t nitems, + FILE *stream) +{ + __CPROVER_HIDE:; + size_t nread; + size_t bytes=nread*size; + size_t i; + __CPROVER_assume(nread<=nitems); + + for(i=0; i +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int feof(FILE *stream) +{ + // just return nondet + int return_value; + *stream; + return return_value; +} + +/* FUNCTION: ferror */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int ferror(FILE *stream) +{ + // just return nondet + int return_value; + *stream; + return return_value; +} + +/* FUNCTION: fileno */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int fileno(FILE *stream) +{ + // just return nondet + int return_value; + *stream; + return return_value; +} + +/* FUNCTION: fputs */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +int fputs(const char *s, FILE *stream) +{ + // just return nondet + int return_value; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(s), "fputs zero-termination of 1st argument"); + #endif + *stream; + return return_value; +} + +/* FUNCTION: fflush */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +int fflush(FILE *stream) +{ + // just return nondet + int return_value; + *stream; + return return_value; +} + +/* FUNCTION: fpurge */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +int fpurge(FILE *stream) +{ + // just return nondet + int return_value; + *stream; + return return_value; +} + +/* FUNCTION: read */ + +#ifndef __CPROVER_UNISTD_H_INCLUDED +#include +#define __CPROVER_UNISTD_H_INCLUDED +#endif + +inline ssize_t read(int fildes, void *buf, size_t nbyte) +{ + __CPROVER_HIDE:; + ssize_t nread; + size_t i; + __CPROVER_assume(nread<=nbyte); + + for(i=0; i +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int fgetc(FILE *stream) +{ + __CPROVER_HIDE:; + int return_value; + *stream; + // it's a byte or EOF (-1) + __CPROVER_assume(return_value>=-1 && return_value<=255); + return return_value; +} + +/* FUNCTION: getc */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int getc(FILE *stream) +{ + __CPROVER_HIDE:; + int return_value; + *stream; + // it's a byte or EOF + __CPROVER_assume(return_value>=-1 && return_value<=255); + return return_value; +} + +/* FUNCTION: getchar */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int getchar() +{ + __CPROVER_HIDE:; + int return_value; + // it's a byte or EOF + __CPROVER_assume(return_value>=-1 && return_value<=255); + return return_value; +} + +/* FUNCTION: getw */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int getw(FILE *stream) +{ + __CPROVER_HIDE:; + int return_value; + *stream; + // it's any int, no restriction + return return_value; +} + +/* FUNCTION: fseek */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +inline int fseek(FILE *stream, long offset, int whence) +{ + __CPROVER_HIDE:; + int return_value; + *stream; + return return_value; +} + +/* FUNCTION: ftell */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +long ftell(FILE *stream) +{ + __CPROVER_HIDE:; + int return_value; + *stream; + return return_value; +} + +/* FUNCTION: rewind */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +void rewind(FILE *stream) +{ + __CPROVER_HIDE: + *stream; +} + +/* FUNCTION: fwrite */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +size_t fwrite( + const void *ptr, + size_t size, + size_t nitems, + FILE *stream) +{ + __CPROVER_HIDE:; + size_t nwrite; + __CPROVER_assume(nwrite<=nitems); + return nwrite; +} + +/* FUNCTION: perror */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +#include +#define __CPROVER_STDIO_H_INCLUDED +#endif + +void perror(const char *s) +{ + __CPROVER_hide:; + if(s!=0) + { + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(s), "perror zero-termination"); + #endif + // should go to stderr + if(s[0]!=0) + printf("%s: ", s); + } + + // TODO: print errno error +} + diff --git a/src/ansi-c/library/stdlib.c b/src/ansi-c/library/stdlib.c new file mode 100644 index 00000000000..e432c07d5e9 --- /dev/null +++ b/src/ansi-c/library/stdlib.c @@ -0,0 +1,183 @@ +/* FUNCTION: abs */ + +#undef abs + +inline int abs(int i) { return __CPROVER_abs(i); } + +/* FUNCTION: labs */ + +#undef labs + +inline long int labs(long int i) { return __CPROVER_labs(i); } + +/* FUNCTION: exit */ + +#undef exit + +inline void exit(int status) +{ + __CPROVER_assume(0); +} + +/* FUNCTION: abort */ + +#undef abort + +inline void abort(void) +{ + __CPROVER_assume(0); +} + +/* FUNCTION: calloc */ + +#ifndef __CPROVER_STDLIB_H_INCLUDED +#include +#define __CPROVER_STDLIB_H_INCLUDED +#endif + +#undef calloc + +inline void *calloc(size_t nmemb, size_t size) +{ + __CPROVER_HIDE:; + size_t total_size=nmemb*size; + void *res; + res=malloc(total_size); + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_is_zero_string(res); + __CPROVER_zero_string_length(res)=0; + //for(int i=0; i +#define __CPROVER_STDLIB_H_INCLUDED +#endif + +#undef malloc + +inline void *malloc(size_t malloc_size) +{ + // realistically, malloc may return NULL, + // and __CPROVER_malloc doesn't, but no one cares + __CPROVER_HIDE:; + void *res; + res=__CPROVER_malloc(malloc_size); + + // make sure it's not recorded as deallocated + __CPROVER_deallocated=(res==__CPROVER_deallocated)?0:__CPROVER_deallocated; + + // record the object size for non-determistic bounds checking + _Bool record_malloc; + if(record_malloc) + { + __CPROVER_malloc_object=res; + __CPROVER_malloc_size=malloc_size; + } + + return res; +} + +/* FUNCTION: free */ + +#ifndef __CPROVER_STDLIB_H_INCLUDED +#include +#define __CPROVER_STDLIB_H_INCLUDED +#endif + +#undef free + +inline void free(void *ptr) +{ + __CPROVER_HIDE:; + // If ptr is NULL, no operation is performed. + if(ptr!=NULL) + { + // is it dynamic? + __CPROVER_assert(__CPROVER_DYNAMIC_OBJECT(ptr), + "free argument is dynamic object"); + __CPROVER_assert(__CPROVER_POINTER_OFFSET(ptr)==0, + "free argument has offset zero"); + + // catch double free + if(__CPROVER_deallocated==ptr) + __CPROVER_assert(0, "double free"); + + // non-deterministically record as deallocated + _Bool record; + if(record) __CPROVER_deallocated=ptr; + } +} + +/* FUNCTION: atoi */ + +#ifndef __CPROVER_STDLIB_H_INCLUDED +#include +#define __CPROVER_STDLIB_H_INCLUDED +#endif + +#undef atoi + +inline int atoi(const char *nptr) +{ + __CPROVER_HIDE:; + int res; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(nptr), + "zero-termination of argument of atoi"); + #endif + return res; +} + +/* FUNCTION: atol */ + +#ifndef __CPROVER_STDLIB_H_INCLUDED +#include +#define __CPROVER_STDLIB_H_INCLUDED +#endif + +#undef atol + +inline long atol(const char *nptr) +{ + __CPROVER_HIDE:; + long res; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(nptr), + "zero-termination of argument of atol"); + #endif + return res; +} + +/* FUNCTION: getenv */ + +#undef getenv + +inline char *getenv(const char *name) +{ + __CPROVER_HIDE:; + + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(name), + "zero-termination of argument of getenv"); + #endif + + _Bool found; + if(!found) return 0; + + char *buffer; + size_t buf_size; + + __CPROVER_assume(buf_size>=1); + buffer=(char *)__CPROVER_malloc(buf_size); + buffer[buf_size-1]=0; + return buffer; +} diff --git a/src/ansi-c/library/string.c b/src/ansi-c/library/string.c new file mode 100644 index 00000000000..5b29624479d --- /dev/null +++ b/src/ansi-c/library/string.c @@ -0,0 +1,424 @@ +/* FUNCTION: strcpy */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strcpy + +inline char *strcpy(char *dst, const char *src) +{ + __CPROVER_HIDE:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(src), "strcpy zero-termination of 2nd argument"); + __CPROVER_assert(__CPROVER_buffer_size(dst)>__CPROVER_zero_string_length(src), "strcpy buffer overflow"); + dst[__CPROVER_zero_string_length(src)]=0; + __CPROVER_is_zero_string(dst)=1; + __CPROVER_zero_string_length(dst)=__CPROVER_zero_string_length(src); + #else + size_t i=0; + char ch; + do + { + ch=src[i]; + dst[i]=ch; + i++; + } + while(ch!=(char)0); + #endif + return dst; +} + +/* FUNCTION: strncpy */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strncpy + +inline char *strncpy(char *dst, const char *src, size_t n) +{ + __CPROVER_HIDE:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(src), "strncpy zero-termination of 2nd argument"); + __CPROVER_assert(__CPROVER_buffer_size(dst)>=n, "strncpy buffer overflow"); + __CPROVER_is_zero_string(dst)=__CPROVER_zero_string_length(src) +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strcat + +inline char *strcat(char *dst, const char *src) +{ + __CPROVER_HIDE: + #ifdef __CPROVER_STRING_ABSTRACTION + size_t new_size; + __CPROVER_assert(__CPROVER_is_zero_string(dst), "strcat zero-termination of 1st argument"); + __CPROVER_assert(__CPROVER_is_zero_string(src), "strcat zero-termination of 2nd argument"); + new_size=__CPROVER_zero_string_length(dst)+__CPROVER_zero_string_length(src); + __CPROVER_assert(__CPROVER_buffer_size(dst)>new_size, + "strcat buffer overflow"); + size_t old_size=__CPROVER_zero_string_length(dst); + //" for(size_t i=0; i<__CPROVER_zero_string_length(src); i++) + //" dst[old_size+i]; + dst[new_size]=0; + __CPROVER_is_zero_string(dst)=1; + __CPROVER_zero_string_length(dst)=new_size; + #else + size_t i=0; + while(dst[i]!=0) i++; + + size_t j=0; + char ch; + do + { + char ch=src[j]; + dst[i]=ch; + i++; + j++; + } + while(ch!=(char)0); + #endif + return dst; +} + +/* FUNCTION: strncat */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strncat + +inline char *strncat(char *dst, const char *src, size_t n) +{ + __CPROVER_HIDE: + #ifdef __CPROVER_STRING_ABSTRACTION + size_t additional, new_size; + __CPROVER_assert(__CPROVER_is_zero_string(dst), "strncat zero-termination of 1st argument"); + __CPROVER_assert(__CPROVER_is_zero_string(src) || __CPROVER_buffer_size(src)>=n, "strncat zero-termination of 2nd argument"); + additional=(n<__CPROVER_zero_string_length(src))?n:__CPROVER_zero_string_length(src); + new_size=__CPROVER_is_zero_string(dst)+additional; + __CPROVER_assert(__CPROVER_buffer_size(dst)>new_size, + "strncat buffer overflow"); + size_t dest_len=__CPROVER_zero_string_length(dst); + size_t i; + for (i = 0 ; i < n && i<__CPROVER_zero_string_length(src) ; i++) + dst[dest_len + i] = src[i]; + dst[dest_len + i] = 0; + __CPROVER_is_zero_string(dst)=1; + __CPROVER_zero_string_length(dst)=new_size; + #else + #endif + return dst; +} + +/* FUNCTION: strcmp */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strcmp + +inline int strcmp(const char *s1, const char *s2) +{ + __CPROVER_HIDE:; + int retval; + if(s1!=0 && s1==s2) return 0; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(s1), "strcmp zero-termination of 1st argument"); + __CPROVER_assert(__CPROVER_is_zero_string(s2), "strcmp zero-termination of 2nd argument"); + if(__CPROVER_zero_string_length(s1) != __CPROVER_zero_string_length(s2)) __CPROVER_assume(retval!=0); + #else + size_t i; + unsigned char ch1, ch2; + while(1) + { + ch1=s1[i]; + ch2=s2[i]; + if(ch1==ch2) + { + if(ch1==(char)0) return 0; + } + else if(ch1 +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strncmp + +inline int strncmp(const char *s1, const char *s2, size_t n) +{ + __CPROVER_HIDE: + if(s1!=0 && s1==s2) return 0; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(s1) || __CPROVER_buffer_size(s1)>=n, "strncmp zero-termination of 1st argument"); + __CPROVER_assert(__CPROVER_is_zero_string(s2) || __CPROVER_buffer_size(s2)>=n, "strncmp zero-termination of 2nd argument"); + #else + #endif +} + +/* FUNCTION: strlen */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strlen + +inline size_t strlen(const char *s) +{ + __CPROVER_HIDE: + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(s), "strlen zero-termination"); + return __CPROVER_zero_string_length(s); + #else + size_t len=0; + while(s[len]!=0) len++; + return len; + #endif +} + +/* FUNCTION: strdup */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#ifndef __CPROVER_STDLIB_H_INCLUDED +#include +#define __CPROVER_STDLIB_H_INCLUDED +#endif + +#undef strdup + +inline char *strdup(const char *str) +{ + __CPROVER_HIDE:; + size_t bufsz; + bufsz=(strlen(str)+1)*sizeof(char); + char *cpy=malloc(bufsz); + if(cpy==((void *)0)) return 0; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assume(__CPROVER_buffer_size(cpy)==bufsz); + #endif + strcpy(cpy, str); + return cpy; +} + +/* FUNCTION: memcpy */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef memcpy + +inline void *memcpy(void *dst, const void *src, size_t n) +{ + __CPROVER_HIDE: + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_buffer_size(src)>=n, "memcpy buffer overflow"); + __CPROVER_assert(__CPROVER_buffer_size(dst)>=n, "memcpy buffer overflow"); + // for(size_t i=0; i __CPROVER_zero_string_length(src)) + { + __CPROVER_is_zero_string(dst)=1; + __CPROVER_zero_string_length(dst)=__CPROVER_zero_string_length(src); + } + else if(!(__CPROVER_is_zero_string(dst) && + n <= __CPROVER_zero_string_length(dst))) + __CPROVER_is_zero_string(dst)=0; + #else + for(size_t i=0; i +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef memset + +inline void *memset(void *s, int c, size_t n) +{ + __CPROVER_HIDE: + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_buffer_size(s)>=n, "memset buffer overflow"); + // for(size_t i=0; i __CPROVER_zero_string_length(s)) + { + __CPROVER_is_zero_string(s)=1; + } + else if(c==0) + { + __CPROVER_is_zero_string(s)=1; + __CPROVER_zero_string_length(s)=0; + } + else + __CPROVER_is_zero_string(s)=0; + #else + char *sp=s; + for(size_t i=0; i +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef memmove + +inline void *memmove(void *dest, const void *src, size_t n) +{ + __CPROVER_HIDE: + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_buffer_size(src)>=n, "memmove buffer overflow"); + // dst = src (with overlap allowed) + if(__CPROVER_is_zero_string(src) && + n > __CPROVER_zero_string_length(src)) + { + __CPROVER_is_zero_string(src)=1; + __CPROVER_zero_string_length(dest)=__CPROVER_zero_string_length(src); + } + else + __CPROVER_is_zero_string(dest)=0; + #else + if (dest-src >= n) + { + for(size_t i=0; i0 ; i--) dest[i-1]=src[i-1]; + } + #endif + return dest; +} + +/* FUNCTION: memcmp */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef memcmp + +inline int memcmp(const void *s1, const void *s2, size_t n) +{ + __CPROVER_HIDE:; + int res; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_buffer_size(s1)>=n, "memcmp buffer overflow of 1st argument"); + __CPROVER_assert(__CPROVER_buffer_size(s2)>=n, "memcmp buffer overflow of 2nd argument"); + #else + const unsigned char *sc1=s1, *sc2=s2; + for(; n!=0; n--) + { + res = (s1++) - (s2++); + if (res != 0) + return res; + } + #endif + return res; +} + +/* FUNCTION: strchr */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strchr + +inline char *strchr(const char *src, int c) +{ + __CPROVER_HIDE:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(src), "strchr zero-termination of string argument"); + _Bool found; + size_t i; + return found?src+i:0; + #else + for(size_t i=0; src[i]!=0; i++) + if(src[i]==(char)c) return ((char *)src)+i; + + return 0; + #endif +} + +/* FUNCTION: strrchr */ + +#ifndef __CPROVER_STRING_H_INCLUDED +#include +#define __CPROVER_STRING_H_INCLUDED +#endif + +#undef strchr + +inline char *strrchr(const char *src, int c) +{ + __CPROVER_HIDE:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(src), "strrchr zero-termination of string argument"); + _Bool found; + size_t i; + return found?((char *)src)+i:0; + #else + char *res=0; + for(size_t i=0; src[i]!=0; i++) + if(src[i]==(char)c) res=((char *)src)+i; + return res; + #endif +} + diff --git a/src/ansi-c/library/syslog.c b/src/ansi-c/library/syslog.c new file mode 100644 index 00000000000..f8bdc23ac7f --- /dev/null +++ b/src/ansi-c/library/syslog.c @@ -0,0 +1,33 @@ +/* FUNCTION: openlog */ + +#ifndef __CPROVER_SYSLOG_H_INCLUDED +#include +#define __CPROVER_SYSLOG_H_INCLUDED +#endif + +void openlog(const char *ident, int option, int facility) +{ +} + +/* FUNCTION: closelog */ + +#ifndef __CPROVER_SYSLOG_H_INCLUDED +#include +#define __CPROVER_SYSLOG_H_INCLUDED +#endif + +void closelog(void) +{ +} + +/* FUNCTION: syslog */ + +#ifndef __CPROVER_SYSLOG_H_INCLUDED +#include +#define __CPROVER_SYSLOG_H_INCLUDED +#endif + +void syslog(int priority, const char *format, ...) +{ + // should check arguments +} diff --git a/src/ansi-c/library/time.c b/src/ansi-c/library/time.c new file mode 100644 index 00000000000..75c7635ff9a --- /dev/null +++ b/src/ansi-c/library/time.c @@ -0,0 +1,116 @@ +/* FUNCTION: time */ + +#ifndef __CPROVER_TIME_H_INCLUDED +#include +#define __CPROVER_TIME_H_INCLUDED +#endif + +#undef time + +time_t time(time_t *tloc) +{ + time_t res; + if(!tloc) *tloc=res; + return res; +} + +/* FUNCTION: gmtime */ + +#ifndef __CPROVER_TIME_H_INCLUDED +#include +#define __CPROVER_TIME_H_INCLUDED +#endif + +#undef gmtime + +struct tm *gmtime(const time_t *clock) +{ + // not very general, may be too restrictive + // need to set the fields to something meaningful + *clock; + static struct tm return_value; + return &return_value; +} + +/* FUNCTION: gmtime_r */ + +#ifndef __CPROVER_TIME_H_INCLUDED +#include +#define __CPROVER_TIME_H_INCLUDED +#endif + +#undef gmtime + +struct tm *gmtime_r(const time_t *clock, struct tm *result) +{ + // need to set the fields to something meaningful + *clock; + return result; +} + +/* FUNCTION: localtime */ + +#ifndef __CPROVER_TIME_H_INCLUDED +#include +#define __CPROVER_TIME_H_INCLUDED +#endif + +#undef localtime + +struct tm *localtime(const time_t *clock) +{ + // not very general, may be too restrictive + // need to set the fields to something meaningful + *clock; + static struct tm return_value; + return &return_value; +} + +/* FUNCTION: localtime_r */ + +#ifndef __CPROVER_TIME_H_INCLUDED +#include +#define __CPROVER_TIME_H_INCLUDED +#endif + +#undef localtime + +struct tm *localtime_r(const time_t *clock, struct tm *result) +{ + // need to set the fields to something meaningful + *clock; + return result; +} + +/* FUNCTION: mktime */ + +#ifndef __CPROVER_TIME_H_INCLUDED +#include +#define __CPROVER_TIME_H_INCLUDED +#endif + +#undef mktime + +time_t mktime(struct tm *timeptr) +{ + *timeptr; + time_t result; + return result; +} + +/* FUNCTION: timegm */ + +#ifndef __CPROVER_TIME_H_INCLUDED +#include +#define __CPROVER_TIME_H_INCLUDED +#endif + +#undef timegm + +time_t timegm(struct tm *timeptr) +{ + *timeptr; + time_t result; + return result; +} + diff --git a/src/ansi-c/library/unistd.c b/src/ansi-c/library/unistd.c new file mode 100644 index 00000000000..024260d00ab --- /dev/null +++ b/src/ansi-c/library/unistd.c @@ -0,0 +1,24 @@ +/* FUNCTION: sleep */ + +unsigned nondet_uint(); + +unsigned int sleep(unsigned int seconds) +{ + // do nothing, but return nondet value + unsigned remaining_time=nondet_uint(); + + if(remaining_time>seconds) remaining_time=seconds; + + return remaining_time; +} + +/* FUNCTION: unlink */ + +void unlink(const char *s) +{ + __CPROVER_hide:; + #ifdef __CPROVER_STRING_ABSTRACTION + __CPROVER_assert(__CPROVER_is_zero_string(s), "unlink zero-termination"); + #endif +} + diff --git a/src/ansi-c/library/windows.c b/src/ansi-c/library/windows.c new file mode 100644 index 00000000000..fe91e3d301f --- /dev/null +++ b/src/ansi-c/library/windows.c @@ -0,0 +1,16 @@ +/* FUNCTION: QueryPerformanceFrequency */ + +#include + +BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency) +{ + __CPROVER_HIDE:; + __int64 result; + lpFrequency->QuadPart=result; + _Bool error; + if(error) return 0; + __CPROVER_assume(result!=0); + return 1; +} + + \ No newline at end of file diff --git a/src/ansi-c/literals/convert_character_literal.cpp b/src/ansi-c/literals/convert_character_literal.cpp new file mode 100644 index 00000000000..09c996bb200 --- /dev/null +++ b/src/ansi-c/literals/convert_character_literal.cpp @@ -0,0 +1,93 @@ +/*******************************************************************\ + +Module: C Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include + +#include "unescape_string.h" +#include "convert_character_literal.h" + +/*******************************************************************\ + +Function: convert_character_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt convert_character_literal( + const std::string &src, + bool force_integer_type) +{ + assert(src.size()>=2); + + exprt result; + + if(src[0]=='L') + { + assert(src[1]=='\''); + assert(src[src.size()-1]=='\''); + + std::vector value; + unescape_wide_string(std::string(src, 2, src.size()-3), value); + + if(value.size()==0) + throw "empty wide character literal"; + else if(value.size()==1) + { + typet type=force_integer_type?int_type():wchar_t_type(); + result=from_integer(value[0], type); + } + else + throw "wide literals with "+i2string(value.size())+ + " characters are not supported"; + } + else + { + assert(src[0]=='\''); + assert(src[src.size()-1]=='\''); + + std::string value; + unescape_string(std::string(src, 1, src.size()-2), value); + + if(value.size()==0) + throw "empty character literal"; + else if(value.size()==1) + { + typet type=force_integer_type?int_type():char_type(); + result=from_integer(value[0], type); + } + else if(value.size()>=2 && value.size()<=4) + { + mp_integer x=0; + + for(unsigned i=0; i +#include + +// Ugh. Characters have type 'int' in C, but type +// 'char' in C++. + +exprt convert_character_literal( + const std::string &src, + bool force_integer_type); + +#endif diff --git a/src/ansi-c/literals/convert_float_literal.cpp b/src/ansi-c/literals/convert_float_literal.cpp new file mode 100644 index 00000000000..3aefedeb2a3 --- /dev/null +++ b/src/ansi-c/literals/convert_float_literal.cpp @@ -0,0 +1,85 @@ +/*******************************************************************\ + +Module: C++ Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include "../c_types.h" +#include "parse_float.h" +#include "convert_float_literal.h" + +/*******************************************************************\ + +Function: convert_float_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt convert_float_literal(const std::string &src) +{ + mp_integer significand; + mp_integer exponent; + bool is_float, is_long; + + parse_float(src, significand, exponent, is_float, is_long); + + exprt result=exprt(ID_constant); + + result.set(ID_C_cformat, src); + + if(is_float) + result.type()=float_type(); + else if(is_long) + result.type()=long_double_type(); + else + result.type()=double_type(); + + if(config.ansi_c.use_fixed_for_float) + { + unsigned width=atoi(result.type().get(ID_width).c_str()); + unsigned fraction_bits; + const irep_idt &integer_bits=result.type().get(ID_integer_bits); + + if(integer_bits==irep_idt()) + fraction_bits=width/2; + else + fraction_bits=width-atoi(integer_bits.c_str()); + + mp_integer factor=mp_integer(1)< +#include + +exprt convert_float_literal(const std::string &src); + +#endif diff --git a/src/ansi-c/literals/convert_integer_literal.cpp b/src/ansi-c/literals/convert_integer_literal.cpp new file mode 100644 index 00000000000..6346781193a --- /dev/null +++ b/src/ansi-c/literals/convert_integer_literal.cpp @@ -0,0 +1,144 @@ +/*******************************************************************\ + +Module: C++ Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include + +#include "convert_integer_literal.h" + +/*******************************************************************\ + +Function: convert_integer_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt convert_integer_literal( + const std::string &src, + unsigned base) +{ + bool is_unsigned=false; + unsigned long_cnt=0; + unsigned width_suffix=0; + + for(unsigned i=0; i=2) + min_width=config.ansi_c.long_long_int_width; + + bool is_hex_or_oct=(base==8) || (base==16); + + #define FITS(width, signed) \ + ((signed?!is_unsigned:(is_unsigned || is_hex_or_oct)) && \ + (width>=min_width) && \ + (power(2, signed?width-1:width)>value_abs)) + + unsigned width; + bool is_signed=false; + + if(FITS(config.ansi_c.int_width, true)) // int + { + width=config.ansi_c.int_width; + is_signed=true; + } + else if(FITS(config.ansi_c.int_width, false)) // unsigned int + { + width=config.ansi_c.int_width; + is_signed=false; + } + else if(FITS(config.ansi_c.long_int_width, true)) // long int + { + width=config.ansi_c.long_int_width; + is_signed=true; + } + else if(FITS(config.ansi_c.long_int_width, false)) // unsigned long int + { + width=config.ansi_c.long_int_width; + is_signed=false; + } + else if(FITS(config.ansi_c.long_long_int_width, true)) // long long int + { + width=config.ansi_c.long_long_int_width; + is_signed=true; + } + else if(FITS(config.ansi_c.long_long_int_width, false)) // unsigned long long int + { + width=config.ansi_c.long_long_int_width; + is_signed=false; + } + else + { + // way too large + width=config.ansi_c.long_long_int_width; + } + + typet type=typet(is_signed?ID_signedbv:ID_unsignedbv); + + type.set(ID_width, width); + + exprt result=from_integer(value, type); + result.set(ID_C_cformat, src); + + return result; +} diff --git a/src/ansi-c/literals/convert_integer_literal.h b/src/ansi-c/literals/convert_integer_literal.h new file mode 100644 index 00000000000..18c583af0aa --- /dev/null +++ b/src/ansi-c/literals/convert_integer_literal.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: C++ Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CPP_CONVERT_INTEGER_LITERAL_H +#define CPROVER_CPP_CONVERT_INTEGER_LITERAL_H + +#include +#include + +exprt convert_integer_literal( + const std::string &src, + unsigned base); + +#endif diff --git a/src/ansi-c/literals/convert_string_literal.cpp b/src/ansi-c/literals/convert_string_literal.cpp new file mode 100644 index 00000000000..39ec3be7f49 --- /dev/null +++ b/src/ansi-c/literals/convert_string_literal.cpp @@ -0,0 +1,91 @@ +/*******************************************************************\ + +Module: C/C++ Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "../c_types.h" +#include "../ansi_c_expr.h" + +#include "unescape_string.h" +#include "convert_string_literal.h" + +/*******************************************************************\ + +Function: convert_string_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert_string_literal( + const std::string &src, + std::string &dest) +{ + dest=""; + +} + +/*******************************************************************\ + +Function: convert_string_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt convert_string_literal(const std::string &src) +{ + assert(src.size()>=2); + + if(src[0]=='L') + { + assert(src[1]=='"'); + assert(src[src.size()-1]=='"'); + + std::vector value; + unescape_wide_string(std::string(src, 2, src.size()-3), value); + + // add trailing zero + value.push_back(0); + + exprt result=exprt(ID_array); + result.set(ID_C_string_constant, true); + result.type()=typet(ID_array); + result.type().subtype()=wchar_t_type(); + result.type().set(ID_size, from_integer(value.size(), index_type())); + + result.operands().resize(value.size()); + for(unsigned i=0; i +#include + +exprt convert_string_literal(const std::string &src); + +#endif diff --git a/src/ansi-c/literals/parse_float.cpp b/src/ansi-c/literals/parse_float.cpp new file mode 100644 index 00000000000..c15e3cb8d0a --- /dev/null +++ b/src/ansi-c/literals/parse_float.cpp @@ -0,0 +1,103 @@ +/*******************************************************************\ + +Module: Conversion of Expressions + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "parse_float.h" + +/*******************************************************************\ + +Function: convert_ct::parse_float + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void parse_float( + const std::string &src, + mp_integer &significand, + mp_integer &exponent, + bool &is_float, bool &is_long) +{ + // {digits}{dot}{digits}{exponent}?{floatsuffix}? + // {digits}{dot}{exponent}?{floatsuffix}? + // {dot}{digits}{exponent}?{floatsuffix}? + // {digits}{exponent}{floatsuffix}? + + const char *p=src.c_str(); + + std::string str_whole_number, + str_fraction_part, + str_exponent; + + // get whole number part + while(*p!='.' && *p!=0 && *p!='e' && *p!='E' && + *p!='f' && *p!='F' && *p!='l' && *p!='L') + { + str_whole_number+=*p; + p++; + } + + // skip dot + if(*p=='.') + p++; + + // get fraction part + while(*p!=0 && *p!='e' && *p!='E' && + *p!='f' && *p!='F' && *p!='l' && *p!='L') + { + str_fraction_part+=*p; + p++; + } + + // skip E + if(*p=='e' || *p=='E') + p++; + + // skip + + if(*p=='+') + p++; + + // get exponent + while(*p!=0 && *p!='f' && *p!='F' && *p!='l' && *p!='L') + { + str_exponent+=*p; + p++; + } + + // get flags + is_float=is_long=false; + + while(*p!=0) + { + if(*p=='f' || *p=='F') + is_float=true; + else if(*p=='l' || *p=='L') + is_long=true; + + p++; + } + + std::string str_number=str_whole_number+ + str_fraction_part; + + if(str_number.empty()) + significand=0; + else + significand=string2integer(str_number); + + if(str_exponent.empty()) + exponent=0; + else + exponent=string2integer(str_exponent); + + // adjust exponent + exponent-=str_fraction_part.size(); +} diff --git a/src/ansi-c/literals/parse_float.h b/src/ansi-c/literals/parse_float.h new file mode 100644 index 00000000000..1e684da3e58 --- /dev/null +++ b/src/ansi-c/literals/parse_float.h @@ -0,0 +1,23 @@ +/*******************************************************************\ + +Module: ANSI-C Conversion / Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_PARSE_FLOAT_H +#define CPROVER_ANSI_C_PARSE_FLOAT_H + +#include + +#include + +void parse_float( + const std::string &src, + mp_integer &significand, + mp_integer &exponent, // base 10 + bool &is_float, + bool &is_long); + +#endif diff --git a/src/ansi-c/literals/unescape_string.cpp b/src/ansi-c/literals/unescape_string.cpp new file mode 100644 index 00000000000..77aa52f295d --- /dev/null +++ b/src/ansi-c/literals/unescape_string.cpp @@ -0,0 +1,184 @@ +/*******************************************************************\ + +Module: ANSI-C Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "unescape_string.h" + +/*******************************************************************\ + +Function: unescape_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void unescape_string( + const std::string &src, + std::string &dest) +{ + dest=""; + dest.reserve(src.size()); + + for(unsigned i=0; i &dest) +{ + dest.reserve(src.size()); + + for(unsigned i=0; i +#include + +void unescape_string( + const std::string &src, + std::string &dest); + +void unescape_wide_string( + const std::string &src, + std::vector &dest); + +#endif diff --git a/src/ansi-c/parser.y b/src/ansi-c/parser.y new file mode 100644 index 00000000000..3f6b4ae1f84 --- /dev/null +++ b/src/ansi-c/parser.y @@ -0,0 +1,2513 @@ +%{ + +/* + * This parser is based on: + * + * c5.y, a ANSI-C grammar written by James A. Roskind. + * "Portions Copyright (c) 1989, 1990 James A. Roskind". + * (http://www.idiom.com/free-compilers/, + * ftp://ftp.infoseek.com/ftp/pub/c++grammar/, + * ftp://ftp.sra.co.jp/.a/pub/cmd/c++grammar2.0.tar.gz) + */ + +#define PARSER ansi_c_parser + +#include "ansi_c_parser.h" +#include "concatenate_strings.h" + +int yyansi_clex(); +extern char *yyansi_ctext; + +#include "parser_static.inc" + +#include "y.tab.h" + +/*** token declaration **************************************************/ +%} + +/*** ANSI-C keywords ***/ + +%token TOK_AUTO "auto" +%token TOK_BOOL "bool" +%token TOK_COMPLEX "complex" +%token TOK_BREAK "break" +%token TOK_CASE "case" +%token TOK_CHAR "char" +%token TOK_CONST "const" +%token TOK_CONTINUE "continue" +%token TOK_DEFAULT "default" +%token TOK_DO "do" +%token TOK_DOUBLE "double" +%token TOK_ELSE "else" +%token TOK_ENUM "enum" +%token TOK_EXTERN "extern" +%token TOK_FLOAT "float" +%token TOK_FOR "for" +%token TOK_GOTO "goto" +%token TOK_IF "if" +%token TOK_INLINE "inline" +%token TOK_INT "int" +%token TOK_LONG "long" +%token TOK_REGISTER "register" +%token TOK_RETURN "return" +%token TOK_SHORT "short" +%token TOK_SIGNED "signed" +%token TOK_SIZEOF "sizeof" +%token TOK_STATIC "static" +%token TOK_STRUCT "struct" +%token TOK_SWITCH "switch" +%token TOK_TYPEDEF "typedef" +%token TOK_UNION "union" +%token TOK_UNSIGNED "unsigned" +%token TOK_VOID "void" +%token TOK_VOLATILE "volatile" +%token TOK_WHILE "while" + +/*** multi-character operators ***/ + +%token TOK_ARROW "->" +%token TOK_INCR "++" +%token TOK_DECR "--" +%token TOK_SHIFTLEFT "<<" +%token TOK_SHIFTRIGHT ">>" +%token TOK_LE "<=" +%token TOK_GE ">=" +%token TOK_EQ "==" +%token TOK_NE "!=" +%token TOK_ANDAND "&&" +%token TOK_OROR "||" +%token TOK_ELLIPSIS "..." + +/*** modifying assignment operators ***/ + +%token TOK_MULTASSIGN "*=" +%token TOK_DIVASSIGN "/=" +%token TOK_MODASSIGN "%=" +%token TOK_PLUSASSIGN "+=" +%token TOK_MINUSASSIGN "-=" +%token TOK_SLASSIGN "<<=" +%token TOK_SRASSIGN ">>=" +%token TOK_ANDASSIGN "&=" +%token TOK_EORASSIGN "^=" +%token TOK_ORASSIGN "|=" + +/*** scanner parsed tokens (these have a value!) ***/ + +%token TOK_IDENTIFIER +%token TOK_TYPEDEFNAME +%token TOK_INTEGER +%token TOK_FLOATING +%token TOK_CHARACTER +%token TOK_STRING +%token TOK_ASM_STRING + +/*** extensions ***/ + +%token TOK_INT8 "__int8" +%token TOK_INT16 "__int16" +%token TOK_INT32 "__int32" +%token TOK_INT64 "__int64" +%token TOK_PTR32 "__ptr32" +%token TOK_PTR64 "__ptr64" +%token TOK_TYPEOF "typeof" +%token TOK_GCC_ASM "__asm__" +%token TOK_GCC_ASM_PAREN "__asm__ (with parentheses)" +%token TOK_GCC_ATTRIBUTE_ALIGNED "__attribute__((aligned))" +%token TOK_GCC_ATTRIBUTE_TRANSPARENT_UNION "__attribute__((transparent_union))" +%token TOK_GCC_ATTRIBUTE_PACKED "__attribute__((packed))" +%token TOK_GCC_ATTRIBUTE_VECTOR_SIZE "__attribute__((vector_size))" +%token TOK_GCC_LABEL "__label__" +%token TOK_MSC_ASM "__asm" +%token TOK_BUILTIN_VA_ARG "__builtin_va_arg" +%token TOK_GCC_BUILTIN_TYPES_COMPATIBLE_P "__builtin_types_compatible_p" +%token TOK_OFFSETOF "__offsetof" +%token TOK_ALIGNOF "__alignof__" +%token TOK_MSC_TRY "try" +%token TOK_MSC_FINALLY "finally" +%token TOK_MSC_EXCEPT "except" +%token TOK_MSC_LEAVE "leave" +%token TOK_FORALL "forall" +%token TOK_EXISTS "exists" +%token TOK_THREAD_LOCAL "thread_local" +%token TOK_ARRAY_OF "array_of" +%token TOK_CPROVER_BITVECTOR "__CPROVER_bitvector" +%token TOK_REAL "__real__" +%token TOK_IMAG "__imag__" + +/*** special scanner reports ***/ + +%token TOK_SCANNER_ERROR /* used by scanner to report errors */ +%token TOK_SCANNER_EOF /* used by scanner to report end of import */ + +/*** grammar selection ***/ + +%token TOK_PARSE_LANGUAGE +%token TOK_PARSE_EXPRESSION +%token TOK_PARSE_TYPE + +/*** priority, associativity, etc. definitions **************************/ + +%start grammar + +%expect 1 /* the famous "dangling `else'" ambiguity */ + /* results in one shift/reduce conflict */ + /* that we don't want to be reported */ + +%{ +/************************************************************************/ +/*** rules **************************************************************/ +/************************************************************************/ +%} +%% + +/*** Grammar selection **************************************************/ + +grammar: + TOK_PARSE_LANGUAGE translation_unit + | TOK_PARSE_EXPRESSION comma_expression + { + ansi_c_declarationt ansi_c_declaration; + ansi_c_declaration.value()=stack($2); + PARSER.copy_item(ansi_c_declaration); + } + | TOK_PARSE_TYPE type_name + { + ansi_c_declarationt ansi_c_declaration; + ansi_c_declaration.type()= + static_cast(static_cast(stack($2))); + ansi_c_declaration.set_is_type(true); + PARSER.copy_item(ansi_c_declaration); + } + ; + +/*** Token with values **************************************************/ + +identifier: + TOK_IDENTIFIER + ; + +typedef_name: + TOK_TYPEDEFNAME + ; + +integer: + TOK_INTEGER + ; + +floating: + TOK_FLOATING + ; + +character: + TOK_CHARACTER + ; + +string: + TOK_STRING + ; + +/*** Constants **********************************************************/ + +/* note: the following has been changed from the ANSI-C grammar: */ +/* - constant includes string_literal_list (cleaner) */ + +constant: integer + | floating + | character + | string_literal_list + ; + +string_literal_list: + string + | string_literal_list string + { $$ = $1; + // do concatenation + concatenate_strings(stack($1), stack($2)); + } + ; + +/*** Expressions ********************************************************/ + +primary_expression: + identifier + | constant + | '(' comma_expression ')' + { $$ = $2; } + | statement_expression + | gcc_builtin_expressions + | offsetof + | alignof + | quantifier_expression + ; + +gcc_builtin_expressions: + TOK_BUILTIN_VA_ARG '(' assignment_expression ',' type_name ')' + { + $$=$1; + stack($$).id(ID_gcc_builtin_va_arg); + mto($$, $3); + stack($$).type().swap(stack($5)); + } + | TOK_GCC_BUILTIN_TYPES_COMPATIBLE_P '(' + type_name ',' type_name ')' + { + $$=$1; + stack($$).id(ID_gcc_builtin_types_compatible_p); + irept::subt &subtypes=stack($$).add(ID_subtypes).get_sub(); + subtypes.resize(2); + subtypes[0].swap(stack($3)); + subtypes[1].swap(stack($5)); + } + ; + +offsetof: + TOK_OFFSETOF '(' type_name ',' offsetof_member_designator ')' + { + $$=$1; + stack($$).id(ID_builtin_offsetof); + stack($$).add(ID_type_arg).swap(stack($3)); + stack($$).add(ID_designator).swap(stack($5)); + } + ; + +offsetof_member_designator: + member_name + { + init($$, ID_designated_initializer); + stack($$).operands().resize(1); + stack($$).op0().id(ID_member); + stack($$).op0().location()=stack($1).location(); + stack($$).op0().set(ID_component_name, stack($1).get(ID_C_base_name)); + } + | offsetof_member_designator '.' member_name + { + $$=$1; + set($2, ID_member); + stack($2).set(ID_component_name, stack($3).get(ID_C_base_name)); + mto($$, $2); + } + | offsetof_member_designator '[' comma_expression ']' + { + $$=$1; + set($2, ID_index); + mto($2, $3); + mto($$, $2); + } + ; + +alignof: TOK_ALIGNOF '(' unary_expression ')' + { $$=$1; + set($$, ID_builtin_alignof); + mto($$, $3); + } + | TOK_ALIGNOF '(' type_name ')' + { + $$=$1; + stack($$).id(ID_builtin_alignof); + stack($$).add(ID_type_arg).swap(stack($3)); + } + ; + +quantifier_expression: + TOK_FORALL compound_scope '{' type_name identifier ';' comma_expression '}' + { + //unsigned prefix=PARSER.current_scope().compound_counter; + init($$); + exprt s=stack($5); // save + PARSER.new_declaration(stack($4), stack($5), stack($$)); + stack($$).id(ID_forall); + stack($$).location()=stack($1).location(); + stack($$).move_to_operands(s); + mto($$, $7); + exprt blah=stack($$); + PARSER.pop_scope(); + } + | TOK_EXISTS compound_scope '{' type_name identifier ';' comma_expression '}' + { + $$=$1; + stack($$).id(ID_exists); + stack($$).location()=stack($1).location(); + mto($$, $4); + stack($$).operands()[0].type() = stack($3).type(); + mto($$, $6); + PARSER.pop_scope(); + } + ; + +statement_expression: '(' compound_statement ')' + { init($$, ID_sideeffect); + stack($$).set(ID_statement, ID_statement_expression); + stack($$).location()=stack($1).location(); + mto($$, $2); + } + ; + +postfix_expression: + primary_expression + | postfix_expression '[' comma_expression ']' + { binary($$, $1, $2, ID_index, $3); } + | postfix_expression '(' ')' + { $$=$2; + set($$, ID_sideeffect); + stack($$).operands().resize(2); + stack($$).op0().swap(stack($1)); + stack($$).op1().clear(); + stack($$).op1().id(ID_arguments); + stack($$).set(ID_statement, ID_function_call); + } + | postfix_expression '(' argument_expression_list ')' + { $$=$2; + locationt location=stack($2).location(); + init($$, ID_sideeffect); + stack($$).set(ID_statement, ID_function_call); + stack($$).operands().resize(2); + stack($$).op0().swap(stack($1)); + stack($$).op1().swap(stack($3)); + stack($$).op1().id(ID_arguments); + stack($$).location()=location; + } + | postfix_expression '.' member_name + { $$=$2; + set($$, ID_member); + mto($$, $1); + stack($$).set(ID_component_name, stack($3).get(ID_C_base_name)); + } + | postfix_expression TOK_ARROW member_name + { $$=$2; + set($$, ID_ptrmember); + mto($$, $1); + stack($$).set(ID_component_name, stack($3).get(ID_C_base_name)); + } + | postfix_expression TOK_INCR + { $$=$2; + locationt location=stack($2).location(); + init($$, ID_sideeffect); + mto($$, $1); + stack($$).set(ID_statement, ID_postincrement); + stack($$).location()=location; + } + | postfix_expression TOK_DECR + { $$=$2; + locationt location=stack($2).location(); + init($$, ID_sideeffect); + mto($$, $1); + stack($$).set(ID_statement, ID_postdecrement); + stack($$).location()=location; + } + ; + +member_name: + identifier + | typedef_name + ; + +argument_expression_list: + assignment_expression + { + init($$, ID_expression_list); + mto($$, $1); + } + | argument_expression_list ',' assignment_expression + { + $$=$1; + mto($$, $3); + } + ; + +unary_expression: + postfix_expression + | TOK_INCR unary_expression + { $$=$1; + set($$, ID_sideeffect); + stack($$).set(ID_statement, ID_preincrement); + mto($$, $2); + } + | TOK_DECR unary_expression + { $$=$1; + set($$, ID_sideeffect); + stack($$).set(ID_statement, ID_predecrement); + mto($$, $2); + } + | '&' cast_expression + { $$=$1; + set($$, ID_address_of); + mto($$, $2); + } + | TOK_ANDAND identifier_or_typedef_name + { $$=$1; + set($$, ID_address_of); + stack($$).operands().resize(1); + stack($$).op0()=stack($2); + stack($$).op0().id(ID_label); + stack($$).op0().set(ID_identifier, stack($2).get(ID_C_base_name)); + } + | '*' cast_expression + { $$=$1; + set($$, ID_dereference); + mto($$, $2); + } + | '+' cast_expression + { $$=$1; + set($$, ID_unary_plus); + mto($$, $2); + } + | '-' cast_expression + { $$=$1; + set($$, ID_unary_minus); + mto($$, $2); + } + | '~' cast_expression + { $$=$1; + set($$, ID_bitnot); + mto($$, $2); + } + | '!' cast_expression + { $$=$1; + set($$, ID_not); + mto($$, $2); + } + | TOK_SIZEOF unary_expression + { $$=$1; + set($$, ID_sizeof); + mto($$, $2); + } + | TOK_SIZEOF '(' type_name ')' + { $$=$1; + set($$, ID_sizeof); + stack($$).add(ID_type_arg).swap(stack($3)); + } + | TOK_REAL unary_expression + { $$=$1; + set($$, ID_complex_real); + mto($$, $2); + } + | TOK_IMAG unary_expression + { $$=$1; + set($$, ID_complex_imag); + mto($$, $2); + } + ; + +cast_expression: + unary_expression + | '(' type_name ')' cast_expression + { + $$=$1; + set($$, ID_typecast); + mto($$, $4); + stack($$).type().swap(stack($2)); + } + /* The following is a GCC extension + to allow a 'temporary union' or struct constructor + or array constructor */ + | '(' type_name ')' '{' initializer_list_opt '}' + { + exprt tmp(ID_initializer_list); + tmp.location()=stack($4).location(); + tmp.operands().swap(stack($5).operands()); + $$=$1; + set($$, ID_typecast); + stack($$).move_to_operands(tmp); + stack($$).type().swap(stack($2)); + } + | '(' type_name ')' '{' initializer_list ',' '}' + { + // same as above + exprt tmp(ID_initializer_list); + tmp.location()=stack($4).location(); + tmp.operands().swap(stack($5).operands()); + $$=$1; + set($$, ID_typecast); + stack($$).move_to_operands(tmp); + stack($$).type().swap(stack($2)); + } + ; + +multiplicative_expression: + cast_expression + | multiplicative_expression '*' cast_expression + { binary($$, $1, $2, ID_mult, $3); } + | multiplicative_expression '/' cast_expression + { binary($$, $1, $2, ID_div, $3); } + | multiplicative_expression '%' cast_expression + { binary($$, $1, $2, ID_mod, $3); } + ; + +additive_expression: + multiplicative_expression + | additive_expression '+' multiplicative_expression + { binary($$, $1, $2, ID_plus, $3); } + | additive_expression '-' multiplicative_expression + { binary($$, $1, $2, ID_minus, $3); } + ; + +shift_expression: + additive_expression + | shift_expression TOK_SHIFTLEFT additive_expression + { binary($$, $1, $2, ID_shl, $3); } + | shift_expression TOK_SHIFTRIGHT additive_expression + { binary($$, $1, $2, ID_shr, $3); } + ; + +relational_expression: + shift_expression + | relational_expression '<' shift_expression + { binary($$, $1, $2, ID_lt, $3); } + | relational_expression '>' shift_expression + { binary($$, $1, $2, ID_gt, $3); } + | relational_expression TOK_LE shift_expression + { binary($$, $1, $2, ID_le, $3); } + | relational_expression TOK_GE shift_expression + { binary($$, $1, $2, ID_ge, $3); } + ; + +equality_expression: + relational_expression + | equality_expression TOK_EQ relational_expression + { binary($$, $1, $2, ID_equal, $3); } + | equality_expression TOK_NE relational_expression + { binary($$, $1, $2, ID_notequal, $3); } + ; + +and_expression: + equality_expression + | and_expression '&' equality_expression + { binary($$, $1, $2, ID_bitand, $3); } + ; + +exclusive_or_expression: + and_expression + | exclusive_or_expression '^' and_expression + { binary($$, $1, $2, ID_bitxor, $3); } + ; + +inclusive_or_expression: + exclusive_or_expression + | inclusive_or_expression '|' exclusive_or_expression + { binary($$, $1, $2, ID_bitor, $3); } + ; + +logical_and_expression: + inclusive_or_expression + | logical_and_expression TOK_ANDAND inclusive_or_expression + { binary($$, $1, $2, ID_and, $3); } + ; + +logical_or_expression: + logical_and_expression + | logical_or_expression TOK_OROR logical_and_expression + { binary($$, $1, $2, ID_or, $3); } + ; + +conditional_expression: + logical_or_expression + | logical_or_expression '?' comma_expression ':' conditional_expression + { $$=$2; + stack($$).id(ID_if); + mto($$, $1); + mto($$, $3); + mto($$, $5); + } + | logical_or_expression '?' ':' conditional_expression + { $$=$2; + stack($$).id(ID_sideeffect); + stack($$).set(ID_statement, ID_gcc_conditional_expression); + mto($$, $1); + mto($$, $4); + } + ; + +assignment_expression: + conditional_expression + | cast_expression '=' assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign); } + | cast_expression TOK_MULTASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_mult); } + | cast_expression TOK_DIVASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_div); } + | cast_expression TOK_MODASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_mod); } + | cast_expression TOK_PLUSASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_plus); } + | cast_expression TOK_MINUSASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_minus); } + | cast_expression TOK_SLASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_shl); } + | cast_expression TOK_SRASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_shr); } + | cast_expression TOK_ANDASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_bitand); } + | cast_expression TOK_EORASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_bitxor); } + | cast_expression TOK_ORASSIGN assignment_expression + { binary($$, $1, $2, ID_sideeffect, $3); stack($$).set(ID_statement, ID_assign_bitor); } + ; + +comma_expression: + assignment_expression + | comma_expression ',' assignment_expression + { binary($$, $1, $2, ID_comma, $3); } + ; + +constant_expression: + assignment_expression + ; + +comma_expression_opt: + /* nothing */ + { init($$); stack($$).make_nil(); } + | comma_expression + ; + +/*** Declarations *******************************************************/ + +declaration: + declaration_specifier ';' + { + // type only! + codet decl(ID_decl_type); + decl.add(ID_type_arg).swap(stack($1)); + init($$); + stack($$).move_to_operands(decl); + } + | type_specifier ';' + { + // type only! + codet decl(ID_decl_type); + decl.add(ID_type_arg).swap(stack($1)); + init($$); + stack($$).move_to_operands(decl); + } + | declaring_list ';' + | default_declaring_list ';' + ; + +default_declaring_list: + declaration_qualifier_list identifier_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + PARSER.copy_item(to_ansi_c_declaration(stack($$))); + } + initializer_opt + { + init($$); + stack($$).add(ID_type)=stack($1); + decl_statement($$, $3, $4); + } + | type_qualifier_list identifier_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + PARSER.copy_item(to_ansi_c_declaration(stack($$))); + } + initializer_opt + { + init($$); + stack($$).add(ID_type)=stack($1); + decl_statement($$, $3, $4); + } + | default_declaring_list ',' identifier_declarator + { + init($$); + const irept &t=stack($1).find(ID_type); + PARSER.new_declaration(t, stack($3), stack($$)); + PARSER.copy_item(to_ansi_c_declaration(stack($$))); + } + initializer_opt + { + $$=$1; + decl_statement($$, $4, $5); + } + ; + +post_declarator_attributes: + TOK_GCC_ASM_PAREN volatile_opt '(' gcc_asm_commands ')' + { + } + ; + +post_declarator_attributes_opt: + /* nothing */ + | post_declarator_attributes + ; + +declaring_list: + declaration_specifier declarator + gcc_type_attribute_opt + post_declarator_attributes_opt + { + // the symbol has to be visible during initialization + merge_types($1, $3); + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + PARSER.copy_item(to_ansi_c_declaration(stack($$))); + } + initializer_opt + { + init($$); + stack($$).add(ID_type)=stack($1); + decl_statement($$, $5, $6); + } + | type_specifier declarator + gcc_type_attribute_opt + post_declarator_attributes_opt + { + // the symbol has to be visible during initialization + merge_types($1, $3); + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + PARSER.copy_item(to_ansi_c_declaration(stack($$))); + } + initializer_opt + { + init($$); + stack($$).add(ID_type)=stack($1); + decl_statement($$, $5, $6); + } + | declaring_list ',' declarator + gcc_type_attribute_opt + post_declarator_attributes_opt + { + init($$); + irept t=stack($1).find(ID_type); + merge_types(t, stack($4)); + PARSER.new_declaration(t, stack($3), stack($$)); + PARSER.copy_item(to_ansi_c_declaration(stack($$))); + } + initializer_opt + { + $$=$1; + decl_statement($$, $6, $7); + } + ; + +declaration_specifier: + basic_declaration_specifier + | sue_declaration_specifier + | typedef_declaration_specifier + | typeof_declaration_specifier + ; + +type_specifier: + basic_type_specifier + | sue_type_specifier + | typedef_type_specifier + | typeof_type_specifier + ; + +declaration_qualifier_list: + storage_class + | type_qualifier_list storage_class + { + $$=$1; + merge_types($$, $2); + } + | declaration_qualifier_list declaration_qualifier + { + $$=$1; + merge_types($$, $2); + } + ; + +type_qualifier_list: + type_qualifier + | type_qualifier_list type_qualifier + { + $$=$1; + merge_types($$, $2); + } + /* the following is to allow mixing of type attributes with + type qualifiers */ + | type_qualifier_list gcc_type_attribute + { + $$=$1; + merge_types($$, $2); + } + ; + +declaration_qualifier: + storage_class + | type_qualifier + ; + +type_qualifier: + TOK_CONST { $$=$1; set($$, ID_const); } + | TOK_VOLATILE { $$=$1; set($$, ID_volatile); } + | TOK_PTR32 { $$=$1; set($$, ID_ptr32); } + | TOK_PTR64 { $$=$1; set($$, ID_ptr64); } + ; + +basic_declaration_specifier: + declaration_qualifier_list basic_type_name gcc_type_attribute_opt + { + $$=$1; + merge_types($$, $2); + } + | basic_type_specifier storage_class gcc_type_attribute_opt + { + $$=$1; + merge_types($$, $2); + } + | basic_declaration_specifier declaration_qualifier gcc_type_attribute_opt + { + $$=$1; + merge_types($$, $2); + } + | basic_declaration_specifier basic_type_name gcc_type_attribute_opt + { + $$=$1; + merge_types($$, $2); + }; + +basic_type_specifier: + basic_type_name gcc_type_attribute_opt + { + $$=$1; + } + | type_qualifier_list basic_type_name gcc_type_attribute_opt + { + $$=$1; + merge_types($$, $2); + } + | basic_type_specifier type_qualifier + { + $$=$1; + merge_types($$, $2); + } + | basic_type_specifier basic_type_name gcc_type_attribute_opt + { + $$=$1; + merge_types($$, $2); + }; + +sue_declaration_specifier: + declaration_qualifier_list elaborated_type_name + { + $$=$1; + merge_types($$, $2); + } + | sue_type_specifier storage_class + { + $$=$1; + merge_types($$, $2); + } + | sue_declaration_specifier declaration_qualifier + { + $$=$1; + merge_types($$, $2); + } + ; + +sue_type_specifier: + elaborated_type_name + | type_qualifier_list elaborated_type_name + { + $$=$1; + merge_types($$, $2); + } + | sue_type_specifier type_qualifier + { + $$=$1; + merge_types($$, $2); + } + ; + +typedef_declaration_specifier: + typedef_type_specifier storage_class + { + $$=$1; + merge_types($$, $2); + } + | declaration_qualifier_list typedef_name + { + $$=$1; + merge_types($$, $2); + } + | typedef_declaration_specifier declaration_qualifier + { + $$=$1; + merge_types($$, $2); + } + ; + +typeof_declaration_specifier: + typeof_type_specifier storage_class + { + $$=$1; + merge_types($$, $2); + } + | declaration_qualifier_list typeof_specifier + { + $$=$1; + merge_types($$, $2); + } + | typeof_declaration_specifier declaration_qualifier + { + $$=$1; + merge_types($$, $2); + } + ; + +typedef_type_specifier: + typedef_name + | type_qualifier_list typedef_name + { + $$=$1; + merge_types($$, $2); + } + | typedef_type_specifier type_qualifier + { + $$=$1; + merge_types($$, $2); + } + ; + +typeof_specifier: + TOK_TYPEOF '(' comma_expression ')' + { $$ = $1; + stack($$).id(ID_typeof); + mto($$, $3); + } + | TOK_TYPEOF '(' type_name ')' + { $$ = $1; + stack($$).id(ID_typeof); + stack($$).set(ID_type_arg, stack($3)); + } + ; + +typeof_type_specifier: + typeof_specifier + | type_qualifier_list typeof_specifier + { + $$=$1; + merge_types($$, $2); + } + ; + +/* +gcc_attribute_expression_list: + assignment_expression + { + init($$, ID_expression_list); + mto($$, $1); + } + | gcc_attribute_expression_list ',' assignment_expression + { + $$=$1; + mto($$, $3); + } + ; +*/ + +/* +gcc_attribute_expression_list_opt: +*/ + /* empty */ +/* + { + init($$, ID_expression_list); + } + | gcc_attribute_expression_list + ; +*/ + +/* +gcc_attribute_parameters: + '(' gcc_attribute_expression_list_opt ')' + ; +*/ + +storage_class: + TOK_TYPEDEF { $$=$1; set($$, ID_typedef); } + | TOK_EXTERN { $$=$1; set($$, ID_extern); } + | TOK_STATIC { $$=$1; set($$, ID_static); } + | TOK_AUTO { $$=$1; set($$, ID_auto); } + | TOK_REGISTER { $$=$1; set($$, ID_register); } + | TOK_INLINE { $$=$1; set($$, ID_inline); } + | TOK_THREAD_LOCAL { $$=$1; set($$, ID_thread_local); } + | TOK_GCC_ASM { $$=$1; set($$, ID_asm); } + ; + +basic_type_name: + TOK_INT { $$=$1; set($$, ID_int); } + | TOK_INT8 { $$=$1; set($$, ID_int8); } + | TOK_INT16 { $$=$1; set($$, ID_int16); } + | TOK_INT32 { $$=$1; set($$, ID_int32); } + | TOK_INT64 { $$=$1; set($$, ID_int64); } + | TOK_CHAR { $$=$1; set($$, ID_char); } + | TOK_SHORT { $$=$1; set($$, ID_short); } + | TOK_LONG { $$=$1; set($$, ID_long); } + | TOK_FLOAT { $$=$1; set($$, ID_float); } + | TOK_DOUBLE { $$=$1; set($$, ID_double); } + | TOK_SIGNED { $$=$1; set($$, ID_signed); } + | TOK_UNSIGNED { $$=$1; set($$, ID_unsigned); } + | TOK_VOID { $$=$1; set($$, ID_void); } + | TOK_BOOL { $$=$1; set($$, ID_bool); } + | TOK_COMPLEX { $$=$1; set($$, ID_complex); } + | TOK_CPROVER_BITVECTOR '[' comma_expression ']' + { + $$=$1; + set($$, ID_bv); + stack($$).add(ID_size).swap(stack($3)); + } + ; + +elaborated_type_name: + aggregate_name + | enum_name + | array_of_construct + ; + +array_of_construct: + TOK_ARRAY_OF '<' type_name '>' + { $$=$1; ((typet &)stack($$)).subtype().swap(stack($2)); } + ; + +aggregate_name: + aggregate_key + gcc_type_attribute_opt + { + // an anon struct/union + exprt symbol(ID_symbol); + symbol.set(ID_C_base_name, PARSER.get_anon_name()); + + init($$); + PARSER.new_declaration(stack($1), symbol, stack($$), true); + } + '{' member_declaration_list_opt '}' + gcc_type_attribute_opt + { + typet &type=to_ansi_c_declaration(stack($3)).type(); + type.add(ID_components).get_sub().swap( + stack($5).add(ID_operands).get_sub()); + + // throw in the gcc attributes + merge_types(type, stack($2)); + merge_types(type, stack($7)); + + // grab symbol + init($$, ID_symbol); + stack($$).set(ID_identifier, to_ansi_c_declaration(stack($3)).get_name()); + stack($$).location()=to_ansi_c_declaration(stack($3)).location(); + PARSER.copy_item(to_ansi_c_declaration(stack($3))); + } + | aggregate_key + gcc_type_attribute_opt + identifier_or_typedef_name + { + // a struct/union with tag + init($$); + PARSER.new_declaration(stack($1), stack($3), stack($$), true); + + // announce the tag before the members + ansi_c_declarationt tmp=to_ansi_c_declaration(stack($$)); // copy! + tmp.type().id("incomplete_"+stack($1).id_string()); + assert(tmp.id()==ID_declaration); + PARSER.copy_item(tmp); + } + '{' member_declaration_list_opt '}' + gcc_type_attribute_opt + { + typet &type=stack($4).type(); + type.add(ID_components).get_sub().swap( + stack($6).add(ID_operands).get_sub()); + + // throw in the gcc attributes + merge_types(type, stack($2)); + merge_types(type, stack($8)); + + // grab symbol + init($$, ID_symbol); + stack($$).set(ID_identifier, stack($4).get(ID_name)); + stack($$).location()=stack($4).location(); + PARSER.copy_item(to_ansi_c_declaration(stack($4))); + } + | aggregate_key + gcc_type_attribute_opt + identifier_or_typedef_name + { + do_tag($1, $3); + $$=$3; + merge_types($$, $2); + } + ; + +aggregate_key: + TOK_STRUCT + { $$=$1; set($$, ID_struct); } + | TOK_UNION + { $$=$1; set($$, ID_union); } + ; + +gcc_type_attribute_opt: + /* empty */ + { + init($$); + } + | gcc_type_attribute + ; + +gcc_type_attribute: + TOK_GCC_ATTRIBUTE_PACKED + { $$=$1; set($$, ID_packed); } + | TOK_GCC_ATTRIBUTE_TRANSPARENT_UNION + { $$=$1; set($$, ID_transparent_union); } + | TOK_GCC_ATTRIBUTE_ALIGNED + { $$=$1; set($$, ID_aligned); } + | TOK_GCC_ATTRIBUTE_VECTOR_SIZE '(' comma_expression ')' + { $$=$1; set($$, ID_vector); stack($$).add(ID_size)=stack($3); } + ; + +member_declaration_list_opt: + /* Nothing */ + { + init($$, ID_declaration_list); + } + | member_declaration_list + ; + +member_declaration_list: + member_declaration + | member_declaration_list member_declaration + { + assert(stack($1).id()==ID_declaration_list); + assert(stack($2).id()==ID_declaration_list); + $$=$1; + Forall_operands(it, stack($2)) + stack($$).move_to_operands(*it); + stack($2).clear(); + } + ; + +member_declaration: + member_declaring_list ';' + | member_default_declaring_list ';' + | ';' /* empty declaration */ + { + init($$, ID_declaration_list); + } + ; + +member_default_declaring_list: + gcc_type_attribute_opt + type_qualifier_list member_identifier_declarator + { + init($$, ID_declaration_list); + + exprt declaration; + PARSER.new_declaration(stack($2), stack($3), declaration, false, false); + + stack($$).move_to_operands(declaration); + } + | member_default_declaring_list ',' member_identifier_declarator + { + exprt declaration; + PARSER.new_declaration(stack($1), stack($3), declaration, false, false); + + $$=$1; + stack($$).move_to_operands(declaration); + } + ; + +member_declaring_list: + gcc_type_attribute_opt + type_specifier member_declarator + { + init($$, ID_declaration_list); + + // save the type_specifier + stack($$).add("declaration_type")=stack($2); + + exprt declaration; + PARSER.new_declaration(stack($2), stack($3), declaration, false, false); + + stack($$).move_to_operands(declaration); + } + | member_declaring_list ',' member_declarator + { + exprt declaration; + + irept declaration_type(stack($1).find("declaration_type")); + PARSER.new_declaration(declaration_type, stack($3), declaration, false, false); + + $$=$1; + stack($$).move_to_operands(declaration); + } + ; + +member_declarator: + declarator bit_field_size_opt gcc_type_attribute_opt + { + $$=$1; + + if(stack($2).is_not_nil()) + make_subtype($$, $2); + } + | /* empty */ + { + init($$, ID_abstract); + } + | bit_field_size gcc_type_attribute_opt + { + $$=$1; + stack($$).add(ID_subtype)=irept(ID_abstract); + } + ; + +member_identifier_declarator: + identifier_declarator bit_field_size_opt gcc_type_attribute_opt + { + $$=$1; + make_subtype($$, $2); + } + | bit_field_size gcc_type_attribute_opt + { + $$=$1; + stack($$).add(ID_subtype)=irept(ID_abstract); + } + ; + +bit_field_size_opt: + /* nothing */ + { + init($$, ID_nil); + } + | bit_field_size + ; + +bit_field_size: /* Expression */ + ':' constant_expression + { + $$=$1; + set($$, ID_c_bitfield); + stack($$).set(ID_size, stack($2)); + stack($$).add(ID_subtype).id(ID_abstract); + } + ; + +enum_name: /* Type */ + enum_key + gcc_type_attribute_opt + { + // an anon enum, we want that to be visible before the + // members + exprt symbol(ID_symbol); + symbol.set(ID_C_base_name, PARSER.get_anon_name()); + + init($$); + PARSER.new_declaration(stack($1), symbol, stack($$), true); + PARSER.copy_item(to_ansi_c_declaration(stack($$))); + } + '{' enumerator_list '}' + gcc_type_attribute_opt + { + // grab symbol + init($$, ID_symbol); + stack($$).set(ID_identifier, stack($3).get(ID_name)); + stack($$).location()=stack($3).location(); + + do_enum_members((const typet &)stack($$), stack($5)); + } + | enum_key + gcc_type_attribute_opt + identifier_or_typedef_name + { + // we want the tag to be visible before the members + init($$); + PARSER.new_declaration(stack($1), stack($3), stack($$), true); + PARSER.copy_item(to_ansi_c_declaration(stack($$))); + } + '{' enumerator_list '}' + gcc_type_attribute_opt + { + // grab symbol + init($$, ID_symbol); + stack($$).set(ID_identifier, stack($4).get(ID_name)); + stack($$).location()=stack($4).location(); + + do_enum_members((const typet &)stack($$), stack($6)); + } + | enum_key + gcc_type_attribute_opt + identifier_or_typedef_name + { + do_tag($1, $3); + $$=$3; + } + ; + +enum_key: TOK_ENUM + { + $$=$1; + set($$, ID_c_enum); + } + ; + +enumerator_list: /* MemberList */ + enumerator_declaration + { + init($$); + mto($$, $1); + } + | enumerator_list ',' enumerator_declaration + { + $$=$1; + mto($$, $3); + } + | enumerator_list ',' + { + $$=$1; + } + ; + +enumerator_declaration: + identifier_or_typedef_name enumerator_value_opt + { + init($$); + irept type(ID_c_enum); + PARSER.new_declaration(type, stack($1), stack($$)); + stack($$).set(ID_is_macro, true); + stack($$).add(ID_value).swap(stack($2)); + } + ; + +enumerator_value_opt: /* Expression */ + /* nothing */ + { + init($$); + stack($$).make_nil(); + } + | '=' constant_expression + { + $$=$2; + } + ; + +parameter_type_list: /* ParameterList */ + parameter_list + | parameter_list ',' TOK_ELLIPSIS + { + typet tmp(ID_ellipsis); + $$=$1; + ((typet &)stack($$)).move_to_subtypes(tmp); + } + ; + +KnR_parameter_list: + KnR_parameter + { + init($$, ID_arguments); + mts($$, $1); + } + | KnR_parameter_list ',' KnR_parameter + { + $$=$1; + mts($$, $3); + } + ; + +KnR_parameter: identifier + { + init($$); + irept type(ID_KnR); + PARSER.new_declaration(type, stack($1), stack($$)); + } + ; + +parameter_list: + parameter_declaration + { + init($$, ID_arguments); + mts($$, $1); + } + | parameter_list ',' parameter_declaration + { + $$=$1; + mts($$, $3); + } + ; + +parameter_declaration: + declaration_specifier + { + init($$); + exprt declarator=exprt(ID_abstract); + PARSER.new_declaration(stack($1), declarator, stack($$)); + } + | declaration_specifier parameter_abstract_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | declaration_specifier identifier_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | declaration_specifier parameter_typedef_declarator + { + // the second tree is really the argument -- not part + // of the type! + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | declaration_qualifier_list + { + init($$); + exprt declarator=exprt(ID_abstract); + PARSER.new_declaration(stack($1), declarator, stack($$)); + } + | declaration_qualifier_list parameter_abstract_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | declaration_qualifier_list identifier_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | type_specifier + { + init($$); + exprt declarator=exprt(ID_abstract); + PARSER.new_declaration(stack($1), declarator, stack($$)); + } + | type_specifier parameter_abstract_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | type_specifier identifier_declarator + { + init($$); + stack($1), stack($2), stack($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | type_specifier parameter_typedef_declarator + { + // the second tree is really the argument -- not part + // of the type! + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | type_qualifier_list + { + init($$); + exprt declarator=exprt(ID_abstract); + PARSER.new_declaration(stack($1), declarator, stack($$)); + } + | type_qualifier_list parameter_abstract_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + | type_qualifier_list identifier_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + } + ; + +identifier_or_typedef_name: + identifier + | typedef_name + ; + +type_name: + type_specifier + | type_specifier abstract_declarator + { + $$=$1; + make_subtype($$, $2); + } + | type_qualifier_list + | type_qualifier_list abstract_declarator + { + $$=$1; + make_subtype($$, $2); + } + ; + +initializer_opt: + /* nothing */ + { + newstack($$); + stack($$).make_nil(); + } + | '=' initializer + { $$ = $2; } + ; + +/* note: the following has been changed from the ANSI-C grammar: */ +/* - an initializer is not an assignment_expression, */ +/* but a constant_expression */ +/* (which probably is the case anyway for 99.9% of C programs) */ + +initializer: + constant_expression /* was: assignment_expression */ + | designated_initializer + | '{' initializer_list_opt '}' + { + $$=$1; + set($$, ID_initializer_list); + stack($$).operands().swap(stack($2).operands()); + } + | '{' initializer_list ',' '}' + { + $$=$1; + set($$, ID_initializer_list); + stack($$).operands().swap(stack($2).operands()); + } + ; + +initializer_list: + initializer + { + $$=$1; + exprt tmp; + tmp.swap(stack($$)); + stack($$).clear(); + stack($$).move_to_operands(tmp); + } + | initializer_list ',' initializer + { + $$=$1; + mto($$, $3); + } + ; + +initializer_list_opt: + initializer_list + | /* nothing */ + { + init($$); + set($$, ID_initializer_list); + stack($$).operands().clear(); + } + ; + +/* GCC extension: designated initializer */ +designated_initializer: + designated_initializer_designator '=' initializer + { + $$=$2; + stack($$).id(ID_designated_initializer); + stack($$).add(ID_designator).swap(stack($1)); + mto($$, $3); + } + | member_name ':' initializer + { + // yet another GCC speciality + $$=$2; + stack($$).id(ID_designated_initializer); + exprt designator; + exprt member(ID_member); + member.set(ID_component_name, stack($1).get(ID_C_base_name)); + designator.move_to_operands(member); + stack($$).add(ID_designator).swap(designator); + mto($$, $3); + } + ; + +designated_initializer_designator: + '.' member_name + { + init($$); + stack($1).id(ID_member); + stack($1).set(ID_component_name, stack($2).get(ID_C_base_name)); + mto($$, $1); + } + | '[' comma_expression ']' + { + init($$); + stack($1).id(ID_index); + mto($1, $2); + mto($$, $1); + } + | '[' comma_expression TOK_ELLIPSIS comma_expression ']' + { + // TODO + init($$); + stack($1).id(ID_index); + mto($1, $2); + mto($$, $1); + } + | designated_initializer_designator '[' comma_expression ']' + { + $$=$1; + stack($2).id(ID_index); + mto($2, $3); + mto($$, $2); + } + | designated_initializer_designator '[' comma_expression TOK_ELLIPSIS comma_expression ']' + { + // TODO + $$=$1; + stack($2).id(ID_index); + mto($2, $3); + mto($$, $2); + } + | designated_initializer_designator '.' member_name + { + $$=$1; + stack($2).id(ID_member); + stack($2).set(ID_component_name, stack($3).get(ID_C_base_name)); + mto($$, $2); + } + ; + +/*** Statements *********************************************************/ + +statement: + labeled_statement + | compound_statement + | declaration_statement + | expression_statement + | selection_statement + | iteration_statement + | jump_statement + | gcc_asm_statement + | gcc_local_label_statement + | msc_asm_statement + | msc_seh_statement + ; + +declaration_statement: + declaration + { + init($$); + statement($$, ID_decl_block); + stack($$).operands().swap(stack($1).operands()); + } + ; + +labeled_statement: + identifier_or_typedef_name ':' statement + { + $$=$2; + statement($$, ID_label); + stack($$).set(ID_label, stack($1).get(ID_C_base_name)); + mto($$, $3); + } + | TOK_CASE constant_expression ':' statement + { + $$=$1; + statement($$, ID_label); + mto($$, $4); + static_cast(stack($$).add(ID_case)). + move_to_operands(stack($2)); + } + | TOK_CASE constant_expression TOK_ELLIPSIS constant_expression ':' statement + { + // this is a GCC extension + $$=$1; + statement($$, ID_label); + mto($$, $6); + static_cast(stack($$).add(ID_case)). + move_to_operands(stack($2)); + // TODO -- the other one + } + | TOK_DEFAULT ':' statement + { + $$=$1; + statement($$, ID_label); + mto($$, $3); + stack($$).set(ID_default, true); + } + ; + +/* note: the following has been changed from the ANSI-C grammar: */ +/* - rule compound_scope is used to prepare an inner scope for */ +/* each compound_statement (and to obtain the line infos) */ + +compound_statement: + compound_scope '{' '}' + { + $$=$2; + statement($$, ID_block); + stack($$).set(ID_C_end_location, stack($3).location()); + PARSER.pop_scope(); + } + | compound_scope '{' statement_list '}' + { + $$=$3; + stack($$).location()=stack($2).location(); + stack($$).set(ID_C_end_location, stack($4).location()); + PARSER.pop_scope(); + } + | compound_scope '{' TOK_ASM_STRING '}' + { + init($$); + stack($$).location()=stack($2).location(); + stack($$).set(ID_statement, ID_asm); + mto($$, $3); + PARSER.pop_scope(); + } + ; + +compound_scope: + /* nothing */ + { + unsigned prefix=++PARSER.current_scope().compound_counter; + PARSER.new_scope(i2string(prefix)+"::"); + } + ; + +statement_list: + statement + { + $$=$1; + to_code(stack($$)).make_block(); + } + | statement_list statement + { + mto($$, $2); + } + ; + +expression_statement: + comma_expression_opt ';' + { + $$=$2; + + if(stack($1).is_nil()) + statement($$, ID_skip); + else + { + statement($$, ID_expression); + mto($$, $1); + } + } + ; + +selection_statement: + TOK_IF '(' comma_expression ')' statement + { + $$=$1; + statement($$, ID_ifthenelse); + stack($$).operands().reserve(2); + mto($$, $3); + mto($$, $5); + } + | TOK_IF '(' comma_expression ')' statement TOK_ELSE statement + { + $$=$1; + statement($$, ID_ifthenelse); + stack($$).operands().reserve(3); + mto($$, $3); + mto($$, $5); + mto($$, $7); + } + | TOK_SWITCH '(' comma_expression ')' statement + { + $$=$1; + statement($$, ID_switch); + stack($$).operands().reserve(2); + mto($$, $3); + mto($$, $5); + } + ; + +declaration_or_expression_statement: + declaration_statement + | expression_statement + ; + +iteration_statement: + TOK_WHILE '(' comma_expression_opt ')' statement + { + $$=$1; + statement($$, ID_while); + stack($$).operands().reserve(2); + mto($$, $3); + mto($$, $5); + } + | TOK_DO statement TOK_WHILE '(' comma_expression ')' ';' + { + $$=$1; + statement($$, ID_dowhile); + stack($$).operands().reserve(2); + mto($$, $5); + mto($$, $2); + } + | TOK_FOR '(' declaration_or_expression_statement + comma_expression_opt ';' comma_expression_opt ')' statement + { + $$=$1; + statement($$, ID_for); + stack($$).operands().reserve(4); + mto($$, $3); + mto($$, $4); + mto($$, $6); + mto($$, $8); + } + ; + +jump_statement: + TOK_GOTO comma_expression ';' + { + $$=$1; + if(stack($2).id()==ID_symbol) + { + statement($$, ID_goto); + stack($$).set(ID_destination, stack($2).get(ID_C_base_name)); + } + else + { + // this is a gcc extension. + // the original grammar uses identifier_or_typedef_name + statement($$, "computed-goto"); + mto($$, $2); + } + } + | TOK_CONTINUE ';' + { $$=$1; statement($$, ID_continue); } + | TOK_BREAK ';' + { $$=$1; statement($$, ID_break); } + | TOK_RETURN ';' + { $$=$1; statement($$, ID_return); } + | TOK_RETURN comma_expression ';' + { $$=$1; statement($$, ID_return); mto($$, $2); } + ; + +gcc_local_label_statement: + TOK_GCC_LABEL identifier_or_typedef_name ';' + { + $$=$1; + statement($$, ID_gcc_local_label); + stack($$).set(ID_label, stack($2).get(ID_C_base_name)); + } + ; + +gcc_asm_statement: + TOK_GCC_ASM_PAREN volatile_opt '(' gcc_asm_commands ')' ';' + { $$=$1; + statement($$, ID_asm); + stack($$).set(ID_flavor, ID_gcc); + stack($$).operands().swap(stack($4).operands()); + } + | TOK_GCC_ASM_PAREN '{' TOK_ASM_STRING '}' + { + $$=$1; + statement($$, ID_asm); + stack($$).set(ID_flavor, ID_gcc); + mto($$, $3); + } + ; + +msc_asm_statement: + TOK_MSC_ASM '{' TOK_ASM_STRING '}' + { $$=$1; + statement($$, ID_asm); + stack($$).set(ID_flavor, ID_msc); + mto($$, $3); + } + | TOK_MSC_ASM TOK_ASM_STRING + { $$=$1; + statement($$, ID_asm); + stack($$).set(ID_flavor, ID_msc); + mto($$, $2); + } + ; + +msc_seh_statement: + TOK_MSC_TRY compound_statement + TOK_MSC_EXCEPT '(' comma_expression ')' compound_statement + { + $$=$2; /* EXCEPT-Block is ignored */ + to_code(stack($$)).make_block(); + code_labelt l; + l.set_label("try-exit"); + l.operands()[0] = code_skipt(); + stack($$).move_to_operands(l); + } + | TOK_MSC_TRY compound_statement + TOK_MSC_FINALLY compound_statement + { + $$=$2; + to_code(stack($$)).make_block(); + code_labelt l; + l.set_label("try-exit"); + l.operands()[0] = code_skipt(); + stack($$).move_to_operands(l); + mto($$, $4); /* FINALLY is added to the block */ + } + | TOK_MSC_LEAVE + { + $$=$1; + statement($$, ID_goto); + stack($$).set(ID_destination, "try-exit"); + } + ; + +volatile_opt: + /* nothing */ + | TOK_VOLATILE + ; + +/* asm ( assembler template + : output operands // optional + : input operands // optional + : list of clobbered registers // optional + ); +*/ + +gcc_asm_commands: + gcc_asm_assembler_template + { + init($$); + mto($$, $1); + } + | gcc_asm_assembler_template gcc_asm_outputs + { + init($$); + mto($$, $1); + } + | gcc_asm_assembler_template gcc_asm_outputs gcc_asm_inputs + { + init($$); + mto($$, $1); + } + | gcc_asm_assembler_template gcc_asm_outputs gcc_asm_inputs gcc_asm_clobbered_registers + { + init($$); + mto($$, $1); + } + ; + +gcc_asm_assembler_template: string_literal_list + ; + +gcc_asm_outputs: + ':' gcc_asm_output_list + | ':' + ; + +gcc_asm_output: + string_literal_list '(' comma_expression ')' + | '[' identifier_or_typedef_name ']' + string_literal_list '(' comma_expression ')' + ; + +gcc_asm_output_list: + gcc_asm_output + | gcc_asm_output_list ',' gcc_asm_output + ; + +gcc_asm_inputs: + ':' gcc_asm_input_list + | ':' + ; + +gcc_asm_input: + string_literal_list '(' comma_expression ')' + | '[' identifier_or_typedef_name ']' + string_literal_list '(' comma_expression ')' + ; + +gcc_asm_input_list: + gcc_asm_input + | gcc_asm_input_list ',' gcc_asm_input + +gcc_asm_clobbered_registers: + ':' gcc_asm_clobbered_registers_list + | ':' + ; + +gcc_asm_clobbered_register: + string_literal_list + ; + +gcc_asm_clobbered_registers_list: + gcc_asm_clobbered_register + | gcc_asm_clobbered_registers_list ',' gcc_asm_clobbered_register + ; + +/*** External Definitions ***********************************************/ + + +/* note: the following has been changed from the ANSI-C grammar: */ +/* - translation unit is allowed to be empty! */ + +translation_unit: + /* nothing */ + | external_definition_list + ; + +external_definition_list: + external_definition + | external_definition_list external_definition + ; + +external_definition: + function_definition + | declaration + | asm_definition + | ';' // empty declaration + ; + +asm_definition: + TOK_GCC_ASM_PAREN '(' string_literal_list ')' ';' + ; + +function_definition: + function_head + { + // the function symbol needs to be visible before any declarations + // in the body (the following compound_statement) + to_ansi_c_declaration(stack($1)).value().make_nil(); + PARSER.copy_item(to_ansi_c_declaration(stack($1))); + } + function_body + { + // we now present the body as initializer + to_ansi_c_declaration(stack($1)).value().swap(stack($3)); + PARSER.copy_item(to_ansi_c_declaration(stack($1))); + + // kill scope + PARSER.pop_scope(); + PARSER.function=irep_idt(); + } + ; + +function_body: + compound_statement + ; + +KnR_parameter_header_opt: + /* empty */ + { + init($$); + } + | KnR_parameter_header + ; + +KnR_parameter_header: + KnR_parameter_declaration + { + $$=$1; + } + | KnR_parameter_header KnR_parameter_declaration + { + $$=$1; + Forall_irep(it, stack($2).get_sub()) + stack($$).move_to_sub(*it); + } + ; + +KnR_parameter_declaration: + KnR_parameter_declaring_list ';' + ; + +KnR_parameter_declaring_list: + declaration_specifier declarator + { + init($$); + exprt tmp; + PARSER.new_declaration(stack($1), stack($2), tmp); + stack($$).move_to_sub(tmp); + } + | type_specifier declarator + { + init($$); + exprt tmp; + PARSER.new_declaration(stack($1), stack($2), tmp); + stack($$).move_to_sub(tmp); + } + | KnR_parameter_declaring_list ',' declarator + { + $$=$1; + const irept &t=stack($1).find(ID_type); + exprt tmp; + PARSER.new_declaration(t, stack($3), tmp); + stack($$).move_to_sub(tmp); + } + ; + +function_head: + identifier_declarator /* void */ + { + init($$); + irept type(ID_int); + PARSER.new_declaration(type, stack($1), stack($$)); + create_function_scope(stack($$)); + } + | declaration_specifier declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + create_function_scope(stack($$)); + } + | type_specifier declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + create_function_scope(stack($$)); + } + | declaration_qualifier_list identifier_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + create_function_scope(stack($$)); + } + | type_qualifier_list identifier_declarator + { + init($$); + PARSER.new_declaration(stack($1), stack($2), stack($$)); + create_function_scope(stack($$)); + } + ; + +declarator: + identifier_declarator + | typedef_declarator + ; + +typedef_declarator: + paren_typedef_declarator + | parameter_typedef_declarator + ; + +parameter_typedef_declarator: + typedef_name + | typedef_name postfixing_abstract_declarator + { + $$=$1; + make_subtype($$, $2); + } + | clean_typedef_declarator + ; + +clean_typedef_declarator: /* Declarator */ + clean_postfix_typedef_declarator + | '*' parameter_typedef_declarator + { + $$=$2; + do_pointer($1, $2); + } + | '*' type_qualifier_list parameter_typedef_declarator + { + merge_types($2, $3); + $$=$2; + do_pointer($1, $2); + } + ; + +clean_postfix_typedef_declarator: + '(' clean_typedef_declarator ')' + { $$ = $2; } + | '(' clean_typedef_declarator ')' postfixing_abstract_declarator + { + /* note: this is a pointer ($2) to a function ($4) */ + /* or an array ($4)! */ + $$=$2; + make_subtype($$, $4); + } + ; + +paren_typedef_declarator: + paren_postfix_typedef_declarator + | '*' '(' simple_paren_typedef_declarator ')' + { + $$=$3; + do_pointer($1, $3); + } + | '*' type_qualifier_list '(' simple_paren_typedef_declarator ')' + { + // not sure where the type qualifiers belong + merge_types($2, $4); + $$=$2; + do_pointer($1, $2); + } + | '*' paren_typedef_declarator + { + $$=$2; + do_pointer($1, $2); + } + | '*' type_qualifier_list paren_typedef_declarator + { + merge_types($2, $3); + $$=$2; + do_pointer($1, $2); + } + ; + +paren_postfix_typedef_declarator: /* Declarator */ + '(' paren_typedef_declarator ')' + { $$ = $2; } + | '(' simple_paren_typedef_declarator postfixing_abstract_declarator ')' + { /* note: this is a function ($3) with a typedef name ($2) */ + $$=$2; + make_subtype($$, $3); + } + | '(' paren_typedef_declarator ')' postfixing_abstract_declarator + { + /* note: this is a pointer ($2) to a function ($4) */ + /* or an array ($4)! */ + $$=$2; + make_subtype($$, $4); + } + ; + +simple_paren_typedef_declarator: + typedef_name + | '(' simple_paren_typedef_declarator ')' + { $$ = $3; } + ; + +identifier_declarator: + unary_identifier_declarator + | paren_identifier_declarator + ; + +unary_identifier_declarator: + postfix_identifier_declarator + | '*' identifier_declarator + { + $$=$2; + do_pointer($1, $2); + } + | '*' type_qualifier_list identifier_declarator + { + // the type_qualifier_list is for the pointer, + // and not the identifier_declarator + stack($1).id(ID_pointer); + stack($1).add(ID_subtype)=irept(ID_abstract); + merge_types($2, $1); // dest=$2 + make_subtype($3, $2); // dest=$3 + $$=$3; + } + ; + +postfix_identifier_declarator: + paren_identifier_declarator postfixing_abstract_declarator + { + /* note: this is a function or array ($2) with name ($1) */ + $$=$1; + make_subtype($$, $2); + } + | '(' unary_identifier_declarator ')' + { $$ = $2; } + | '(' unary_identifier_declarator ')' postfixing_abstract_declarator + { + /* note: this is a pointer ($2) to a function ($4) */ + /* or an array ($4)! */ + $$=$2; + make_subtype($$, $4); + } + ; + +paren_identifier_declarator: + identifier + | '(' paren_identifier_declarator ')' + { $$=$2; } + ; + +abstract_declarator: + unary_abstract_declarator + | postfix_abstract_declarator + | postfixing_abstract_declarator + ; + +parameter_abstract_declarator: + parameter_unary_abstract_declarator + | parameter_postfix_abstract_declarator + ; + +postfixing_abstract_declarator: /* AbstrDeclarator */ + array_abstract_declarator + | '(' ')' + { + $$=$1; + set($$, ID_code); + stack($$).add(ID_arguments); + stack($$).add(ID_subtype)=irept(ID_abstract); + } + | '(' + { + unsigned prefix=++PARSER.current_scope().compound_counter; + PARSER.new_scope(i2string(prefix)+"::"); + } + parameter_type_list + ')' + { + $$=$1; + set($$, ID_code); + stack($$).add(ID_subtype)=irept(ID_abstract); + stack($$).add(ID_arguments).get_sub(). + swap(stack($3).add(ID_subtypes).get_sub()); + PARSER.pop_scope(); + } + /* The following rule implements K&R headers! */ + | '(' + { + unsigned prefix=++PARSER.current_scope().compound_counter; + PARSER.new_scope(i2string(prefix)+"::"); + } + KnR_parameter_list + ')' + KnR_parameter_header_opt + { + $$=$1; + set($$, ID_code); + stack($$).add(ID_subtype)=irept(ID_abstract); + stack($$).add(ID_arguments).get_sub(). + swap(stack($3).add(ID_subtypes).get_sub()); + PARSER.pop_scope(); + adjust_KnR_arguments(stack($$).add(ID_arguments), stack($5)); + } + ; + +parameter_postfixing_abstract_declarator: + array_abstract_declarator + | '(' ')' + { + $$=$1; + set($$, ID_code); + stack($$).add(ID_arguments); + stack($$).add(ID_subtype)=irept(ID_abstract); + } + | '(' + { + unsigned prefix=++PARSER.current_scope().compound_counter; + PARSER.new_scope(i2string(prefix)+"::"); + } + parameter_type_list + ')' + { + $$=$1; + set($$, ID_code); + stack($$).add(ID_subtype)=irept(ID_abstract); + stack($$).add(ID_arguments).get_sub(). + swap(stack($3).add(ID_subtypes).get_sub()); + PARSER.pop_scope(); + } + ; + +array_abstract_declarator: + '[' ']' + { + $$=$1; + set($$, ID_incomplete_array); + stack($$).add(ID_subtype)=irept(ID_abstract); + } + | '[' '*' ']' + { + $$=$1; + set($$, ID_incomplete_array); + stack($$).add(ID_subtype)=irept(ID_abstract); + } + | '[' constant_expression ']' + { + $$=$1; + set($$, ID_array); + stack($$).add(ID_size).swap(stack($2)); + stack($$).add(ID_subtype)=irept(ID_abstract); + } + | array_abstract_declarator '[' constant_expression ']' + { + // we need to push this down + $$=$1; + set($2, ID_array); + stack($2).add(ID_size).swap(stack($3)); + stack($2).add(ID_subtype)=irept(ID_abstract); + make_subtype($1, $2); + } + ; + +unary_abstract_declarator: + '*' + { + $$=$1; + set($$, ID_pointer); + stack($$).add(ID_subtype)=irept(ID_abstract); + } + | '*' type_qualifier_list + { + // tye type_qualifier_list belongs to the pointer, + // not to the (missing) abstract declarator + $$=$2; + exprt declarator=exprt(ID_abstract); + merge_types(stack($2), declarator); + do_pointer($1, $2); + } + | '*' abstract_declarator + { + $$=$2; + do_pointer($1, $2); + } + | '*' type_qualifier_list abstract_declarator + { + // tye type_qualifier_list belongs to the pointer, + // not to the abstract declarator + $$=$2; + merge_types($2, $3); + do_pointer($1, $2); + } + ; + +parameter_unary_abstract_declarator: + '*' + { + $$=$1; + set($$, ID_pointer); + stack($$).add(ID_subtype)=irept(ID_abstract); + } + | '*' type_qualifier_list + { + // tye type_qualifier_list belongs to the pointer, + // not to the (missing) abstract declarator + $$=$2; + exprt declarator=exprt(ID_abstract); + merge_types(stack($2), declarator); + do_pointer($1, $2); + } + | '*' parameter_abstract_declarator + { + $$=$2; + do_pointer($1, $2); + } + | '*' type_qualifier_list parameter_abstract_declarator + { + // tye type_qualifier_list belongs to the pointer, + // not to the (missing) abstract declarator + $$=$2; + merge_types($2, $3); + do_pointer($1, $2); + } + ; + +postfix_abstract_declarator: + '(' unary_abstract_declarator ')' + { $$ = $2; } + | '(' postfix_abstract_declarator ')' + { $$ = $2; } + | '(' postfixing_abstract_declarator ')' + { $$ = $2; } + | '(' unary_abstract_declarator ')' postfixing_abstract_declarator + { + /* note: this is a pointer ($2) to a function ($4) */ + /* or an array ($4) of pointers with name ($2)! */ + $$=$2; + make_subtype($$, $4); + } + ; + +parameter_postfix_abstract_declarator: + '(' parameter_unary_abstract_declarator ')' + { $$ = $2; } + | '(' parameter_postfix_abstract_declarator ')' + { $$ = $2; } + | parameter_postfixing_abstract_declarator + | '(' parameter_unary_abstract_declarator ')' parameter_postfixing_abstract_declarator + { + /* note: this is a pointer ($2) to a function ($4) */ + /* or an array ($4) of pointers with name ($2)! */ + $$=$2; + make_subtype($$, $4); + } + ; + +%% diff --git a/src/ansi-c/parser_static.inc b/src/ansi-c/parser_static.inc new file mode 100644 index 00000000000..0fdd375b16c --- /dev/null +++ b/src/ansi-c/parser_static.inc @@ -0,0 +1,485 @@ +#include +#include +#include +#include +#include + +#include + +#define YYSTYPE unsigned +#define YYSTYPE_IS_TRIVIAL 1 + +#define mto(x, y) stack(x).move_to_operands(stack(y)) +#define mts(x, y) ((typet &)stack(x)).move_to_subtypes((typet &)stack(y)) +#define binary(x, y, l, id, z) { init(x, id); \ + stack(x).location()=stack(l).location(); \ + stack(x).reserve_operands(2); mto(x, y); mto(x, z); } + +/*******************************************************************\ + +Function: init + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void init(YYSTYPE &expr) +{ + newstack(expr); +} + +/*******************************************************************\ + +Function: init + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +inline static void init(YYSTYPE &expr, const irep_idt &id) +{ + newstack(expr); + stack(expr).id(id); +} + +/*******************************************************************\ + +Function: set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +inline static void set(YYSTYPE expr, const irep_idt &id) +{ + stack(expr).id(id); +} + +/*******************************************************************\ + +Function: statement + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void statement(YYSTYPE &expr, const irep_idt &id) +{ + set(expr, ID_code); + stack(expr).set(ID_statement, id); +} + +/*******************************************************************\ + +Function: decl_statement + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void decl_statement( + YYSTYPE &dest, + YYSTYPE &decl, + YYSTYPE &initializer) +{ + ansi_c_declarationt &ansi_c_declaration= + to_ansi_c_declaration(stack(decl)); + + codet decl_statement(ID_decl); + + symbol_exprt symbol; + symbol.set_identifier(ansi_c_declaration.get_name()); + + decl_statement.move_to_operands(symbol); + + if(stack(initializer).is_not_nil()) + { + // repeat declaration to set the initializer + ansi_c_declaration.value()=stack(initializer); + PARSER.copy_item(ansi_c_declaration); + + decl_statement.move_to_operands(stack(initializer)); + } + + stack(dest).move_to_operands(decl_statement); +} + +/*******************************************************************\ + +Function: merge_types + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void merge_types(irept &dest, irept &src) +{ + #if 0 + std::cout << "D: " << dest.pretty() << std::endl; + std::cout << "S: " << src.pretty() << std::endl; + #endif + + if(src.is_nil()) + return; + + if(dest.id()!=ID_merged_type) + { + locationt location=static_cast(dest).location(); + typet new_type(ID_merged_type); + new_type.move_to_subtypes((typet &)(dest)); + dest.swap(new_type); + static_cast(dest).location()=location; + } + + static_cast(dest).move_to_subtypes(static_cast(src)); +} + +/*******************************************************************\ + +Function: merge_types + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void merge_types(const YYSTYPE dest, const YYSTYPE src) +{ + merge_types(stack(dest), stack(src)); +} + +/*******************************************************************\ + +Function: make_subtype + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void make_subtype(typet &dest, typet &src) +{ + // inserts "src" into "dest" + // e.g., src is a pointer or array, + // dest is a symbol or type + + // find spot in 'dest' where to insert 'src' + + #if 0 + std::cout << "D: " << dest.pretty() << std::endl; + std::cout << "S: " << src.pretty() << std::endl; + #endif + + assert(src.id()==ID_array || + src.id()==ID_incomplete_array || + src.id()==ID_pointer || + src.id()==ID_code || + src.id()==ID_merged_type || + src.id()==ID_c_bitfield); + + typet *p=&dest; + + while(true) + { + // see if we need to walk down + typet *sub=p; + + if(p->id()==ID_merged_type) + { + // do last one + assert(!p->subtypes().empty()); + sub=&(p->subtypes().back()); + } + + if(sub->id()==ID_pointer || + sub->id()==ID_array || + sub->id()==ID_incomplete_array || + sub->id()==ID_code) + { + // walk down + p=&sub->subtype(); + } + else + { + if(p->id()==ID_abstract) + { + p->swap(src); + break; + } + else if(p->is_nil()) + assert(false); + else if(p->id()==irep_idt()) + assert(false); + else + { + // *p is now type or symbol + + // save symbol + typet symbol=*p; + p->swap(src); + + // find spot where to put symbol + while(true) + { + if(p->id()==ID_abstract) + { + p->swap(symbol); + break; + } + else if(p->id()==ID_merged_type) + { + assert(!p->subtypes().empty()); + p=&(p->subtypes().back()); + } + else if(p->id()==irep_idt()) + assert(false); + else if(p->is_nil()) + assert(false); + else + p=&p->subtype(); + } + break; + } + } + } +} + +/*******************************************************************\ + +Function: make_subtype + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void make_subtype(YYSTYPE dest, YYSTYPE src) +{ + make_subtype((typet &)stack(dest), (typet &)stack(src)); +} + +/*******************************************************************\ + +Function: do_pointer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void do_pointer(const YYSTYPE ptr, const YYSTYPE dest) +{ + set(ptr, ID_pointer); + stack(ptr).add(ID_subtype)=irept(ID_abstract); + make_subtype(dest, ptr); +} + +/*******************************************************************\ + +Function: do_enum_members + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void do_enum_members( + const typet &enum_type, + exprt &members) +{ + exprt value; + + // start with 0 + value=gen_zero(int_type()); + + Forall_operands(it, members) + { + ansi_c_declarationt &ansi_c_declaration= + to_ansi_c_declaration(*it); + + ansi_c_declaration.type()=enum_type; + + exprt &v=ansi_c_declaration.value(); + if(v.is_nil()) + v=value; + + exprt symbol_expr(ID_symbol); + symbol_expr.set(ID_identifier, ansi_c_declaration.get_name()); + + value=exprt(ID_plus); + value.copy_to_operands(symbol_expr, gen_one(int_type())); + + PARSER.copy_item(ansi_c_declaration); + } +} + +/*******************************************************************\ + +Function: do_tag + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void do_tag(YYSTYPE &key, YYSTYPE &tag) +{ + irep_idt id_class_str=stack(tag).get(ID_C_id_class); + + ansi_c_id_classt id_class= + (ansi_c_id_classt)atoi(id_class_str.c_str()); + + if(id_class==ANSI_C_TAG) // we have it already + return; + + // it's new + ansi_c_declarationt declaration; + PARSER.new_declaration(stack(key), stack(tag), declaration, true); + + // grab symbol + stack(tag).id(ID_symbol); + stack(tag).set(ID_identifier, declaration.get_name()); + stack(tag).location()=declaration.location(); + + declaration.type().id("incomplete_"+declaration.type().id_string()); + PARSER.copy_item(declaration); +} + +/*******************************************************************\ + +Function: create_function_scope + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void create_function_scope(exprt &expr) +{ + ansi_c_declarationt &declaration=to_ansi_c_declaration(expr); + + PARSER.function=declaration.get_base_name(); + + std::string prefix=PARSER.current_scope().prefix+ + id2string(declaration.get_base_name())+"::"; + PARSER.new_scope(prefix); + + if(declaration.type().id()==ID_code) + { + code_typet &code_type=to_code_type(declaration.type()); + + code_typet::argumentst &arguments=code_type.arguments(); + + unsigned anon_count=0; + + // do the parameter declarations + for(code_typet::argumentst::iterator + it=arguments.begin(); + it!=arguments.end(); + it++) + { + if(it->id()==ID_declaration) + { + ansi_c_declarationt &arg_decl=to_ansi_c_declaration(*it); + + if(arg_decl.type().id()==ID_incomplete_array) + arg_decl.type().id(ID_pointer); + + if(arg_decl.get_base_name()==irep_idt()) + arg_decl.set_base_name("#anon_arg"+i2string(anon_count++)); + + // make sure we know it's an argument + arg_decl.set_is_argument(true); + + // fix name + arg_decl.set_name( + PARSER.current_scope().prefix+id2string(arg_decl.get_base_name())); + + // copy declaration + PARSER.copy_item(arg_decl); + + // add to scope + PARSER.current_scope().name_map + [arg_decl.get_base_name()].id_class=ANSI_C_SYMBOL; + } + } + } +} + +/*******************************************************************\ + +Function: adjust_KnR_arguments + + Inputs: + + Outputs: + + Purpose: this patches the KnR argument types into the + function type + +\*******************************************************************/ + +static void adjust_KnR_arguments(irept &arguments, irept &declarations) +{ + Forall_irep(d_it, declarations.get_sub()) + { + assert(d_it->id()==ID_declaration); + irep_idt base_name=d_it->get(ID_base_name); + + // we just do a linear search over the arguments + // this could be improved with a hash map + Forall_irep(a_it, arguments.get_sub()) + { + if(a_it->get(ID_base_name)==base_name) + { + a_it->add(ID_type)=d_it->find(ID_type); + break; + } + } + } +} diff --git a/src/ansi-c/preprocessor_line.cpp b/src/ansi-c/preprocessor_line.cpp new file mode 100644 index 00000000000..5b483c6f4da --- /dev/null +++ b/src/ansi-c/preprocessor_line.cpp @@ -0,0 +1,84 @@ +/*******************************************************************\ + +Module: ANSI-C Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "literals/unescape_string.h" +#include "preprocessor_line.h" + +/*******************************************************************\ + +Function: preprocessor_line + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void preprocessor_line( + const char *text, + unsigned &line_no, + irep_idt &file_name) +{ + const char *ptr=text; + std::string line_number; + + // skip WS + while(*ptr==' ' || *ptr=='\t') ptr++; + + // skip # + if(*ptr!='#') return; + ptr++; + + // skip WS + while(*ptr==' ' || *ptr=='\t') ptr++; + + // skip "line" + if(*ptr=='l') + { + while(*ptr!=0 && *ptr!=' ' && *ptr!='\t') ptr++; + } + + // skip WS + while(*ptr==' ' || *ptr=='\t') ptr++; + + // get line number + while(isdigit(*ptr)) + { + line_number+=*ptr; + ptr++; + } + + // skip until " + while(*ptr!='\n' && *ptr!='"') ptr++; + + line_no=atoi(line_number.c_str()); + + // skip " + if(*ptr!='"') + return; + + ptr++; + + std::string file_name_tmp; + + // get file name + while(*ptr!='\n' && *ptr!='"') + { + file_name_tmp+=*ptr; + ptr++; + } + + std::string file_name_tmp2; + unescape_string(file_name_tmp, file_name_tmp2); + file_name=file_name_tmp2; +} diff --git a/src/ansi-c/preprocessor_line.h b/src/ansi-c/preprocessor_line.h new file mode 100644 index 00000000000..1d0bd0da550 --- /dev/null +++ b/src/ansi-c/preprocessor_line.h @@ -0,0 +1,21 @@ +/*******************************************************************\ + +Module: ANSI-C Language Conversion + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ANSI_C_LANGUAGE_PREPROCESSOR_LINE_H +#define CPROVER_ANSI_C_LANGUAGE_PREPROCESSOR_LINE_H + +#include + +#include + +void preprocessor_line( + const char *text, + unsigned &line_no, + irep_idt &file_name); + +#endif diff --git a/src/ansi-c/printf_formatter.cpp b/src/ansi-c/printf_formatter.cpp new file mode 100644 index 00000000000..7ddcec3918d --- /dev/null +++ b/src/ansi-c/printf_formatter.cpp @@ -0,0 +1,240 @@ +/*******************************************************************\ + +Module: printf Formatting + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include +#include + +#include "c_types.h" +#include "printf_formatter.h" + +/*******************************************************************\ + +Function: printf_formattert::make_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const exprt printf_formattert::make_type( + const exprt &src, const typet &dest) +{ + if(src.type()==dest) return src; + exprt tmp=src; + tmp.make_typecast(dest); + simplify(tmp, ns); + return tmp; +} + +/*******************************************************************\ + +Function: printf_formattert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void printf_formattert::operator()( + const std::string &_format, + const std::list &_operands) +{ + format=_format; + operands=_operands; +} + +/*******************************************************************\ + +Function: printf_formattert::print() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void printf_formattert::print(std::ostream &out) +{ + format_pos=0; + next_operand=operands.begin(); + + try + { + while(!eol()) process_char(out); + } + + catch(eol_exception) + { + } +} + +/*******************************************************************\ + +Function: printf_formattert::as_string() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string printf_formattert::as_string() +{ + std::ostringstream stream; + print(stream); + return stream.str(); +} + +/*******************************************************************\ + +Function: printf_formattert::process_format + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void printf_formattert::process_format(std::ostream &out) +{ + exprt tmp; + format_constantt format_constant; + + format_constant.precision=6; + format_constant.min_width=0; + format_constant.zero_padding=false; + + char ch=next(); + + if(ch=='0') // leading zeros + { + format_constant.zero_padding=true; + ch=next(); + } + + while(isdigit(ch)) // width + { + format_constant.min_width*=10; + format_constant.min_width+=ch-'0'; + ch=next(); + } + + if(ch=='.') // precision + { + format_constant.precision=0; + ch=next(); + + while(isdigit(ch)) + { + format_constant.precision*=10; + format_constant.precision+=ch-'0'; + ch=next(); + } + } + + switch(ch) + { + case '%': + out << ch; + break; + + case 'f': + case 'F': + if(next_operand==operands.end()) break; + out << format_constant( + make_type(*(next_operand++), double_type())); + break; + + case 'g': + case 'G': + if(format_constant.precision==0) format_constant.precision=1; + if(next_operand==operands.end()) break; + out << format_constant( + make_type(*(next_operand++), double_type())); + break; + + case 's': + { + if(next_operand==operands.end()) break; + // this is the address of a string + const exprt &op=*(next_operand++); + if(op.id()==ID_address_of && + op.operands().size()==1 && + op.op0().id()==ID_index && + op.op0().operands().size()==2 && + op.op0().op0().id()==ID_string_constant) + out << format_constant(op.op0().op0()); + } + break; + + case 'd': + if(next_operand==operands.end()) break; + out << format_constant( + make_type(*(next_operand++), int_type())); + break; + + case 'D': + if(next_operand==operands.end()) break; + out << format_constant( + make_type(*(next_operand++), long_int_type())); + break; + + case 'u': + if(next_operand==operands.end()) break; + out << format_constant( + make_type(*(next_operand++), uint_type())); + break; + + case 'U': + if(next_operand==operands.end()) break; + out << format_constant( + make_type(*(next_operand++), long_uint_type())); + break; + + default: + out << '%' << ch; + } +} + +/*******************************************************************\ + +Function: printf_formattert::process_char + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void printf_formattert::process_char(std::ostream &out) +{ + char ch=next(); + + if(ch=='%') + process_format(out); + else + out << ch; +} diff --git a/src/ansi-c/printf_formatter.h b/src/ansi-c/printf_formatter.h new file mode 100644 index 00000000000..356ccdcae72 --- /dev/null +++ b/src/ansi-c/printf_formatter.h @@ -0,0 +1,51 @@ +/*******************************************************************\ + +Module: printf Formatting + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PRINTF_FORMATTER +#define CPROVER_PRINTF_FORMATTER + +#include +#include + +class printf_formattert +{ +public: + void operator()( + const std::string &format, + const std::list &_operands); + + void print(std::ostream &out); + std::string as_string(); + + explicit printf_formattert(const namespacet &_ns):ns(_ns) + { + } + +protected: + const namespacet &ns; + std::string format; + std::list operands; + std::list::const_iterator next_operand; + unsigned format_pos; + inline bool eol() const { return format_pos>=format.size(); } + + class eol_exception { }; + + char next() + { + if(eol()) throw eol_exception(); + return format[format_pos++]; + } + + void process_char(std::ostream &out); + void process_format(std::ostream &out); + + const exprt make_type(const exprt &src, const typet &dest); +}; + +#endif diff --git a/src/ansi-c/scanner.l b/src/ansi-c/scanner.l new file mode 100644 index 00000000000..9012b4990ed --- /dev/null +++ b/src/ansi-c/scanner.l @@ -0,0 +1,885 @@ +%option nounput + +%{ + +/* + * This scanner is based on: + * + * cpp5.l, a C/C++ scanner written by James A. Roskind. + * "Portions Copyright (c) 1989, 1990 James A. Roskind". + * (http://www.idiom.com/free-compilers/, + * ftp://ftp.infoseek.com/ftp/pub/c++grammar/, + * ftp://ftp.sra.co.jp/.a/pub/cmd/c++grammar2.0.tar.gz) + */ + +#ifdef _WIN32 +#define YY_NO_UNISTD_H +static int isatty(int) { return 0; } +#endif + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define PARSER ansi_c_parser +#define YYSTYPE unsigned +#undef ECHO +#define ECHO + +#include "ansi_c_parser.h" +#include "y.tab.h" + +#define loc() \ + { newstack(yyansi_clval); PARSER.set_location(stack(yyansi_clval)); } + +int make_identifier() +{ + loc(); + + std::string base_name(yytext); + std::string identifier(base_name); + + ansi_c_id_classt result= + PARSER.lookup(identifier, PARSER.tag_following); + PARSER.tag_following=false; + + stack(yyansi_clval).id(ID_symbol); + stack(yyansi_clval).set(ID_C_base_name, base_name); + stack(yyansi_clval).set(ID_identifier, identifier); + stack(yyansi_clval).set(ID_C_id_class, result); + + if(result==ANSI_C_TYPEDEF) + return TOK_TYPEDEFNAME; + else + return TOK_IDENTIFIER; +} + +/*** macros for easier rule definition **********************************/ +%} + +delimiter [ \t\b\r] +newline [\n\f\v]|"\\\n" +whitespace {delimiter}+ +ws {delimiter}* +ucletter [A-Z] +lcletter [a-z] +letter ({ucletter}|{lcletter}) +digit [0-9] +bindigit [01] +octdigit [0-7] +hexdigit [0-9a-fA-F] +identifier (({letter}|"_")({letter}|{digit}|"_"|"$")*) +integer {digit}+ +binary {bindigit}+ +iw_suffix ("i8"|"i16"|"i32"|"i64"|"i128") +decinteger [1-9]{digit}* +decinteger_w {decinteger}{iw_suffix} +octinteger "0"{octdigit}* +octinteger_w {octinteger}{iw_suffix} +hexinteger "0"[xX]{hexdigit}+ +hexinteger_w {hexinteger}{iw_suffix} +decinteger_u {decinteger}[uU] +decinteger_uw {decinteger_u}{iw_suffix} +octinteger_u {octinteger}[uU] +octinteger_uw {octinteger_u}{iw_suffix} +hexinteger_u {hexinteger}[uU] +hexinteger_uw {hexinteger_u}{iw_suffix} +decinteger_l {decinteger}[lL] +octinteger_l {octinteger}[lL] +hexinteger_l {hexinteger}[lL] +decinteger_ul {decinteger}([uU][lL]|[lL][uU]) +octinteger_ul {octinteger}([uU][lL]|[lL][uU]) +hexinteger_ul {hexinteger}([uU][lL]|[lL][uU]) +decinteger_ll {decinteger}[lL][lL] +octinteger_ll {octinteger}[lL][lL] +hexinteger_ll {hexinteger}[lL][lL] +decinteger_ull {decinteger}([uU][lL][lL]|[lL][lL][uU]) +octinteger_ull {octinteger}([uU][lL][lL]|[lL][lL][uU]) +hexinteger_ull {hexinteger}([uU][lL][lL]|[lL][lL][uU]) +octchar "\\"{octdigit}{1,3} +hexchar "\\x"{hexdigit}+ +exponent [eE][+-]?{integer} +fraction {integer} +float1 {integer}"."{fraction}?({exponent})? +float2 "."{fraction}({exponent})? +float3 {integer}{exponent} +float {float1}|{float2}|{float3} +float_f {float}[fF]|{integer}[fF] +float_l {float}[lL] +bitvector {binary}[bB] +bitvector_u {binary}([uU][bB]|[bB][uU]) +cppstart {ws}"#"{ws} +cpplineno {cppstart}"line"*{ws}{integer}{ws}.*{newline} +cpppragma {cppstart}"pragma"{ws}.* +cppdirective {cppstart}.* + +simple_escape [abfnrtve'"?\\] +octal_escape [0-7]{1,3} +hex_escape "x"[0-9a-fA-F]+ + +escape_sequence [\\]({simple_escape}|{octal_escape}|{hex_escape}) +c_char [^'\\\n]|{escape_sequence} +s_char [^"\\\n]|{escape_sequence} + +%x GRAMMAR +%x COMMENT1 +%x COMMENT2 +%x ASM_BLOCK +%x MSC_ASM +%x MSC_DECLSPEC +%x MSC_PRAGMA +%x MSC_ANNOTATION +%x GCC_ATTRIBUTE1 +%x GCC_ATTRIBUTE2 +%x GCC_ATTRIBUTE3 +%x GCC_ATTRIBUTE4 +%x GCC_ATTRIBUTE5 +%x GCC_ASM +%x GCC_ASM_VOLATILE +%x GCC_ASM_BLOCK1 +%x GCC_ASM_BLOCK2 +%x GCC_ASM_PAREN + +%{ +void ansi_c_scanner_init() +{ + YY_FLUSH_BUFFER; + BEGIN(0); +} +%} + +%% + +.|\n { BEGIN(GRAMMAR); + yyless(0); /* start again with this character */ + //yy_current_buffer->yy_at_bol = 1; /* at beginning of line */ + switch(PARSER.grammar) + { + case ansi_c_parsert::EXPRESSION: return TOK_PARSE_EXPRESSION; + case ansi_c_parsert::LANGUAGE: return TOK_PARSE_LANGUAGE; + default: assert(0); + } + } + +"/*" { BEGIN(COMMENT1); } /* begin C comment state */ + +{ + "*/" { BEGIN(GRAMMAR); } /* end comment state, back GRAMMAR */ + "/*" { yyansi_cerror("Probably nested comments"); } + <> { yyansi_cerror("Unterminated comment"); return TOK_SCANNER_ERROR; } + [^*/\n]* { /* ignore every char except '*' and NL (performance!) */ } + . { } /* all single characters within comments are ignored */ + \n { } + } + +"//" { BEGIN(COMMENT2); } /* begin C++ comment state */ + +{ + \n { BEGIN(GRAMMAR); } /* end comment state, back GRAMMAR */ + .* { } /* all characters within comments are ignored */ + } + +"L"?[']{c_char}+['] { + newstack(yyansi_clval); + stack(yyansi_clval)=convert_character_literal(yytext, true); + PARSER.set_location(stack(yyansi_clval)); + return TOK_CHARACTER; + } + +"L"?["]{s_char}*["] { + newstack(yyansi_clval); + stack(yyansi_clval)=convert_string_literal(yytext); + PARSER.set_location(stack(yyansi_clval)); + return TOK_STRING; + } + +{newline} { } /* skipped */ +{whitespace} { } /* skipped */ + +{cpplineno} { + preprocessor_line(yytext, PARSER.line_no, PARSER.filename); + PARSER.line_no--; + } +{cpppragma} { + #if 0 + TmpString.form( + "'pragma' directive found:" GL_WARN_MSG_NEWLINE + "%s" GL_WARN_MSG_NEWLINE + "Directive ignored", + &PRS_text[0]); + PRS_PrintWarning(GL_WARN_INFORMATIVE, TmpString.chars()); + TmpString = ""; + #endif + } +{cppdirective} { + yyansi_cerror("Preprocessor directive found"); + return TOK_SCANNER_ERROR; + } + +%{ +/*** keywords ***/ +%} + +{ +"auto" { loc(); return TOK_AUTO; } +"_Bool" { loc(); return TOK_BOOL; } +"break" { loc(); return TOK_BREAK; } +"case" { loc(); return TOK_CASE; } +"char" { loc(); return TOK_CHAR; } +"_Complex" { loc(); return TOK_COMPLEX; } +"const" { loc(); return TOK_CONST; } +"continue" { loc(); return TOK_CONTINUE; } +"default" { loc(); return TOK_DEFAULT; } +"do" { loc(); return TOK_DO; } +"double" { loc(); return TOK_DOUBLE; } +"else" { loc(); return TOK_ELSE; } +"enum" { loc(); PARSER.tag_following=true; return TOK_ENUM; } +"extern" { loc(); return TOK_EXTERN; } +"float" { loc(); return TOK_FLOAT; } +"for" { loc(); return TOK_FOR; } +"goto" { loc(); return TOK_GOTO; } +"if" { loc(); return TOK_IF; } +"inline" { loc(); return TOK_INLINE; } +"int" { loc(); return TOK_INT; } +"long" { loc(); return TOK_LONG; } +"register" { loc(); return TOK_REGISTER; } +"restrict" { /* ignore */ } +"return" { loc(); return TOK_RETURN; } +"short" { loc(); return TOK_SHORT; } +"signed" { loc(); return TOK_SIGNED; } +"sizeof" { loc(); return TOK_SIZEOF; } +"static" { loc(); return TOK_STATIC; } +"struct" { loc(); PARSER.tag_following=true; return TOK_STRUCT; } +"switch" { loc(); return TOK_SWITCH; } +"typedef" { loc(); return TOK_TYPEDEF; } +"union" { loc(); PARSER.tag_following=true; return TOK_UNION; } +"unsigned" { loc(); return TOK_UNSIGNED; } +"void" { loc(); return TOK_VOID; } +"volatile" { loc(); return TOK_VOLATILE; } +"while" { loc(); return TOK_WHILE; } + +"__int8" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_INT8; } + else + return make_identifier(); + } +"__int16" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_INT16; } + else + return make_identifier(); + } +"__int32" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_INT32; } + else + return make_identifier(); + } +"__int64" { if(PARSER.mode==ansi_c_parsert::MSC || + PARSER.mode==ansi_c_parsert::ARM || + PARSER.mode==ansi_c_parsert::CW) + { loc(); return TOK_INT64; } + else + return make_identifier(); + } +"__ptr32" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_PTR32; } + else + return make_identifier(); + } +"__ptr64" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_PTR64; } + else + return make_identifier(); + } + +"__complex__" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_COMPLEX; } + else + return make_identifier(); + } + +"__real__" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_REAL; } + else + return make_identifier(); + } + +"__imag__" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_IMAG; } + else + return make_identifier(); + } + +%{ +/* note: "wchar_t" should be in the list above, but it is left out */ +/* because it is a 'typedef' in some standard header files */ +%} + +"__builtin_va_arg" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_BUILTIN_VA_ARG; } + else + return make_identifier(); + } + +"__builtin_offsetof" | +"__offsetof__" | +"offsetof" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_OFFSETOF; } + else + return make_identifier(); + } + +"__builtin_types_compatible_p" { + if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_GCC_BUILTIN_TYPES_COMPATIBLE_P; } + else + return make_identifier(); + } + +"__alignof__" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_ALIGNOF; } + else + return make_identifier(); + } + +"__ALIGNOF__" { if(PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_ALIGNOF; } + else + return make_identifier(); + } + +"__builtin_alignof" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_ALIGNOF; } + else + return make_identifier(); + } + +"__asm" { if(PARSER.mode==ansi_c_parsert::MSC) + { + loc(); + BEGIN(MSC_ASM); + return TOK_MSC_ASM; + } + else + BEGIN(GCC_ASM); + } + +"asm" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::CW) + BEGIN(GCC_ASM); + else + return make_identifier(); + } + +"__asm__" { BEGIN(GCC_ASM); } + +"[repeatable" { BEGIN(MSC_ANNOTATION); } +"[source_annotation_attribute" { BEGIN(MSC_ANNOTATION); } +"[returnvalue" { BEGIN(MSC_ANNOTATION); } +"[SA_Pre" { BEGIN(MSC_ANNOTATION); } +"[SA_Post" { BEGIN(MSC_ANNOTATION); } +"[SA_FormatString" { BEGIN(MSC_ANNOTATION); } + +"__declspec" { if(PARSER.mode==ansi_c_parsert::MSC || + PARSER.mode==ansi_c_parsert::CW || + PARSER.mode==ansi_c_parsert::ARM) + { + BEGIN(MSC_DECLSPEC); + PARSER.parenthesis_counter=0; + } + else + return make_identifier(); + } + +"__pragma" { if(PARSER.mode==ansi_c_parsert::MSC) + { + BEGIN(MSC_PRAGMA); + PARSER.parenthesis_counter=0; + } + else + return make_identifier(); + } + +"__attribute__" | +"__attribute" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::CW || + PARSER.mode==ansi_c_parsert::ARM) + BEGIN(GCC_ATTRIBUTE1); + else + return make_identifier(); + } + +"__aligned" { /* ignore */ } +"__aligned__" { /* ignore */ } + +"__extension__" { /* ignore */ } + +"__restrict" { /* ignore */ } + +"_cdecl" { /* ignore */ } +"__cdecl" { /* ignore */ } +"__cdecl__" { /* ignore */ } +"_stdcall" { /* ignore */ } +"__stdcall" { /* ignore */ } +"_fastcall" { /* ignore */ } +"__fastcall" { /* ignore */ } +"__w64" { /* ignore */ } + +"__const" { loc(); return TOK_CONST; } +"__const__" { loc(); return TOK_CONST; } + +"__signed" { loc(); return TOK_SIGNED; } +"__signed__" { loc(); return TOK_SIGNED; } + +"__volatile" { loc(); return TOK_VOLATILE; } +"__volatile__" { loc(); return TOK_VOLATILE; } + +"__pure" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + // ignore + } + else + return make_identifier(); + } + +"__align" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + BEGIN(MSC_DECLSPEC); + PARSER.parenthesis_counter=0; + } + else + return make_identifier(); + } + +"__smc" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + BEGIN(MSC_DECLSPEC); + PARSER.parenthesis_counter=0; + } + else + return make_identifier(); + } + +"__INTADDR__" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + // ignore + } + else + return make_identifier(); + } + +"__irq" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + // ignore + } + else + return make_identifier(); + } + +"__packed" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + // ignore + } + else + return make_identifier(); + } + +"__value_in_regs" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + // ignore + } + else + return make_identifier(); + } + +"__weak" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + // ignore + } + else + return make_identifier(); + } + +"__writeonly" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + // ignore + } + else + return make_identifier(); + } + +"__global_reg" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + BEGIN(MSC_DECLSPEC); + PARSER.parenthesis_counter=0; + } + else + return make_identifier(); + } + +"__svc" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + BEGIN(MSC_DECLSPEC); + PARSER.parenthesis_counter=0; + } + else + return make_identifier(); + } + +"__svc_indirect" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + BEGIN(MSC_DECLSPEC); + PARSER.parenthesis_counter=0; + } + else + return make_identifier(); + } + +"__svc_indirect_r7" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + BEGIN(MSC_DECLSPEC); + PARSER.parenthesis_counter=0; + } + else + return make_identifier(); + } + +"__softfp" { /* an ARM extension */ + if(PARSER.mode==ansi_c_parsert::ARM) + { + // ignore + } + else + return make_identifier(); + } + +"typeof" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ICC || + PARSER.mode==ansi_c_parsert::CW || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_TYPEOF; } + else + return make_identifier(); + } +"__typeof" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ICC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_TYPEOF; } + else + return make_identifier(); + } + +"__typeof__" { loc(); return TOK_TYPEOF; } + +"__forceinline" { if(PARSER.mode==ansi_c_parsert::MSC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_INLINE; } + else + return make_identifier(); + } +"__inline" { loc(); return TOK_INLINE; } +"__inline__" { loc(); return TOK_INLINE; } + +"__label__" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_GCC_LABEL; } + else + return make_identifier(); + } + +"__try" | +"try" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_MSC_TRY; } + else + return make_identifier(); + } + +"__finally" | +"finally" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_MSC_FINALLY; } + else + return make_identifier(); + } + +"__except" | +"except" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_MSC_EXCEPT; } + else + return make_identifier(); + } + +"__leave" | +"leave" { if(PARSER.mode==ansi_c_parsert::MSC) + { loc(); return TOK_MSC_LEAVE; } + else + return make_identifier(); + } + +"__CPROVER_forall" { loc(); return TOK_FORALL; } +"__CPROVER_exists" { loc(); return TOK_EXISTS; } +"__CPROVER_array_of" { loc(); return TOK_ARRAY_OF; } +"__CPROVER_thread_local" { loc(); return TOK_THREAD_LOCAL; } +"__CPROVER_bitvector" { loc(); return TOK_CPROVER_BITVECTOR; } + +"__thread" { if(PARSER.mode==ansi_c_parsert::GCC || + PARSER.mode==ansi_c_parsert::ARM) + { loc(); return TOK_THREAD_LOCAL; } + else + return make_identifier(); + } +} + + /* operands following */ + +{ +"->" { loc(); return TOK_ARROW; } +"++" { loc(); return TOK_INCR; } +"--" { loc(); return TOK_DECR; } +"<<" { loc(); return TOK_SHIFTLEFT; } +">>" { loc(); return TOK_SHIFTRIGHT; } +"<=" { loc(); return TOK_LE; } +">=" { loc(); return TOK_GE; } +"==" { loc(); return TOK_EQ; } +"!=" { loc(); return TOK_NE; } +"&&" { loc(); return TOK_ANDAND; } +"||" { loc(); return TOK_OROR; } +"..." { loc(); return TOK_ELLIPSIS; } + +"*=" { loc(); return TOK_MULTASSIGN; } +"/=" { loc(); return TOK_DIVASSIGN; } +"%=" { loc(); return TOK_MODASSIGN; } +"+=" { loc(); return TOK_PLUSASSIGN; } +"-=" { loc(); return TOK_MINUSASSIGN; } +"<<=" { loc(); return TOK_SLASSIGN; } +">>=" { loc(); return TOK_SRASSIGN; } +"&=" { loc(); return TOK_ANDASSIGN; } +"^=" { loc(); return TOK_EORASSIGN; } +"|=" { loc(); return TOK_ORASSIGN; } +} + +{ + +{identifier} { return make_identifier(); } + +{decinteger} | +{decinteger_l} | +{decinteger_ll} | +{decinteger_u} | +{decinteger_ul} | +{decinteger_ull} | +{decinteger_w} | +{decinteger_uw} { newstack(yyansi_clval); + stack(yyansi_clval)=convert_integer_literal(yytext, 10); + PARSER.set_location(stack(yyansi_clval)); + return TOK_INTEGER; + } + +{octinteger} | +{octinteger_l} | +{octinteger_ll} | +{octinteger_u} | +{octinteger_ul} | +{octinteger_ull} | +{octinteger_w} | +{octinteger_uw} { newstack(yyansi_clval); + stack(yyansi_clval)=convert_integer_literal(yytext, 8); + PARSER.set_location(stack(yyansi_clval)); + return TOK_INTEGER; + } + +{hexinteger} | +{hexinteger_l} | +{hexinteger_ll} | +{hexinteger_u} | +{hexinteger_ul} | +{hexinteger_ull} | +{hexinteger_w} | +{hexinteger_uw} { newstack(yyansi_clval); + stack(yyansi_clval)=convert_integer_literal(yytext, 16); + PARSER.set_location(stack(yyansi_clval)); + return TOK_INTEGER; + } + +{float} { newstack(yyansi_clval); + stack(yyansi_clval)=convert_float_literal(yytext); + PARSER.set_location(stack(yyansi_clval)); + return TOK_FLOATING; + } +{float_f} { newstack(yyansi_clval); + stack(yyansi_clval)=convert_float_literal(yytext); + PARSER.set_location(stack(yyansi_clval)); + return TOK_FLOATING; + } +{float_l} { newstack(yyansi_clval); + stack(yyansi_clval)=convert_float_literal(yytext); + PARSER.set_location(stack(yyansi_clval)); + return TOK_FLOATING; + } + +"{" { + PARSER.tag_following=false; + if(PARSER.asm_block_following) { BEGIN(ASM_BLOCK); } + loc(); + return yytext[0]; + } + +";" { PARSER.asm_block_following=false; + PARSER.tag_following=false; + loc(); + return yytext[0]; + } + +. { loc(); PARSER.tag_following=false; return yytext[0]; } +} + +"]" { BEGIN(GRAMMAR); } +. { /* ignore */ } + +{ws}"{" { BEGIN(ASM_BLOCK); loc(); return '{'; } +[^{^}^\n]* { loc(); + stack(yyansi_clval).set(ID_value, yytext); + stack(yyansi_clval).id(ID_string_constant); + BEGIN(GRAMMAR); + return TOK_ASM_STRING; + } + +[^}]* { loc(); + stack(yyansi_clval).set(ID_value, yytext); + stack(yyansi_clval).id(ID_string_constant); + return TOK_ASM_STRING; } +"}" { PARSER.asm_block_following=false; + BEGIN(GRAMMAR); loc(); return '}'; } + +")" { PARSER.parenthesis_counter--; + if(PARSER.parenthesis_counter==0) + BEGIN(GRAMMAR); } +"(" { PARSER.parenthesis_counter++; } +. { /* Throw away */ } + +")" { PARSER.parenthesis_counter--; + if(PARSER.parenthesis_counter==0) + BEGIN(GRAMMAR); } +"(" { PARSER.parenthesis_counter++; } +. { /* Throw away */ } + + /* The following ugly stuff avoids two-token lookahead in the parser; + e.g., asm void f() vs. asm ("xyz") or asm { ... } */ +{ +{ws} { /* ignore */ } +{newline} { /* ignore */ } +"{" { yyless(0); BEGIN(GRAMMAR); loc(); PARSER.asm_block_following=true; return TOK_GCC_ASM_PAREN; } +"(" { yyless(0); BEGIN(GRAMMAR); loc(); return TOK_GCC_ASM_PAREN; } +"volatile" { yyless(0); BEGIN(GRAMMAR); loc(); return TOK_GCC_ASM_PAREN; } +"__volatile__" { yyless(0); BEGIN(GRAMMAR); loc(); return TOK_GCC_ASM_PAREN; } +. { yyless(0); BEGIN(GRAMMAR); loc(); PARSER.asm_block_following=true; return TOK_GCC_ASM; } +} + +{ +"("{ws}"(" { BEGIN(GCC_ATTRIBUTE2); PARSER.parenthesis_counter=0; } +{ws} { /* ignore */ } +{newline} { /* ignore */ } +. { BEGIN(GRAMMAR); loc(); return yytext[0]; } +} + +{ // an attribute is following -- these may be keywords! +"vector_size" { BEGIN(GCC_ATTRIBUTE3); loc(); return TOK_GCC_ATTRIBUTE_VECTOR_SIZE; } +"__vector_size__" { BEGIN(GCC_ATTRIBUTE3); loc(); return TOK_GCC_ATTRIBUTE_VECTOR_SIZE; } +"packed" { BEGIN(GCC_ATTRIBUTE3); loc(); return TOK_GCC_ATTRIBUTE_PACKED; } +"__packed__" { BEGIN(GCC_ATTRIBUTE3); loc(); return TOK_GCC_ATTRIBUTE_PACKED; } +"__transparent_union__" { BEGIN(GCC_ATTRIBUTE3); loc(); return TOK_GCC_ATTRIBUTE_TRANSPARENT_UNION; } +"transparent_union" { BEGIN(GCC_ATTRIBUTE3); loc(); return TOK_GCC_ATTRIBUTE_TRANSPARENT_UNION; } +{ws} { /* ignore */ } +{newline} { /* ignore */ } +{identifier} { BEGIN(GCC_ATTRIBUTE4); } +")" { BEGIN(GCC_ATTRIBUTE5); } +. { /* ignore */ } +} + +{ // an attribute we do process +"(" { PARSER.parenthesis_counter++; loc(); return '('; } +")" { if(PARSER.parenthesis_counter==0) + BEGIN(GCC_ATTRIBUTE5); + else + { + PARSER.parenthesis_counter--; + loc(); + return ')'; + } + } +"," { if(PARSER.parenthesis_counter==0) + BEGIN(GCC_ATTRIBUTE2); + else + { + loc(); + return ','; + } + } +{decinteger} | +{decinteger_l} | +{decinteger_ll} | +{decinteger_u} | +{decinteger_ul} | +{decinteger_ull} | +{decinteger_w} | +{decinteger_uw} { newstack(yyansi_clval); + stack(yyansi_clval)=convert_integer_literal(yytext, 10); + PARSER.set_location(stack(yyansi_clval)); + return TOK_INTEGER; + } +{ws} { /* ignore */ } +{newline} { /* ignore */ } +. { loc(); return yytext[0]; } +} + +{ // an attribute we just ignore +"(" { PARSER.parenthesis_counter++; } +")" { if(PARSER.parenthesis_counter==0) + BEGIN(GCC_ATTRIBUTE5); + else + PARSER.parenthesis_counter--; + } +"," { if(PARSER.parenthesis_counter==0) + BEGIN(GCC_ATTRIBUTE2); } +. { /* Throw away */ } +} + +{ // end bit: the closing parenthesis +")" { BEGIN(GRAMMAR); } +{ws} { /* Throw away */ } +{newline} { /* Throw away */ } +. { BEGIN(GRAMMAR); loc(); return yytext[0]; } +} + +<> { yyterminate(); /* done! */ } + +%% + +int yywrap() { return 1; } diff --git a/src/ansi-c/string_constant.cpp b/src/ansi-c/string_constant.cpp new file mode 100644 index 00000000000..69c2e3a923f --- /dev/null +++ b/src/ansi-c/string_constant.cpp @@ -0,0 +1,106 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "string_constant.h" +#include "c_types.h" + +/*******************************************************************\ + +Function: string_constantt::set_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +string_constantt::string_constantt(): + exprt(ID_string_constant) +{ + set_value(irep_idt()); + type()=typet(ID_array); + type().subtype()=char_type(); +} + +/*******************************************************************\ + +Function: string_constantt::set_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_constantt::set_value(const irep_idt &value) +{ + exprt size_expr=from_integer(value.size()+1, int_type()); + type().add(ID_size).swap(size_expr); + set(ID_value, value); +} + +/*******************************************************************\ + +Function: string_constantt:to_array_expr + + Inputs: + + Outputs: + + Purpose: convert string into array constant + +\*******************************************************************/ + +array_exprt string_constantt::to_array_expr() const +{ + const std::string &str=get_string(ID_value); + unsigned string_size=str.size()+1; // zero + const typet &char_type=type().subtype(); + bool char_is_unsigned=char_type.id()==ID_unsignedbv; + + exprt size=from_integer(string_size, index_type()); + + array_exprt dest; + dest.type()=array_typet(); + dest.type().subtype()=char_type; + dest.type().set(ID_size, size); + + dest.operands().resize(string_size); + + exprt::operandst::iterator it=dest.operands().begin(); + for(unsigned i=0; i=32 && ch<=126) + { + char ch_str[2]; + ch_str[0]=ch; + ch_str[1]=0; + + op.set(ID_C_cformat, "'"+std::string(ch_str)+"'"); + } + } + + return dest; +} + diff --git a/src/ansi-c/string_constant.h b/src/ansi-c/string_constant.h new file mode 100644 index 00000000000..42a477814f4 --- /dev/null +++ b/src/ansi-c/string_constant.h @@ -0,0 +1,45 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_STRING_CONSTANT_H +#define CPROVER_STRING_CONSTANT_H + +#include +#include + +class string_constantt:public exprt +{ +public: + string_constantt(); + + friend inline const string_constantt &to_string_constant(const exprt &expr) + { + assert(expr.id()==ID_string_constant); + return static_cast(expr); + } + + friend inline string_constantt &to_string_constant(exprt &expr) + { + assert(expr.id()==ID_string_constant); + return static_cast(expr); + } + + void set_value(const irep_idt &value); + + inline const irep_idt &get_value() const + { + return get(ID_value); + } + + array_exprt to_array_expr() const; +}; + +const string_constantt &to_string_constant(const exprt &expr); +string_constantt &to_string_constant(exprt &expr); + +#endif diff --git a/src/ansi-c/test.c b/src/ansi-c/test.c new file mode 100644 index 00000000000..113ff060d58 --- /dev/null +++ b/src/ansi-c/test.c @@ -0,0 +1,6 @@ +asm void test(); + +void asm test() +{ + mov eax, eax +} diff --git a/src/ansi-c/trans_unit.cpp b/src/ansi-c/trans_unit.cpp new file mode 100644 index 00000000000..b57fbeed216 --- /dev/null +++ b/src/ansi-c/trans_unit.cpp @@ -0,0 +1,41 @@ +/*******************************************************************\ + +Module: Translation Unit + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "trans_unit.h" + +/*******************************************************************\ + +Function: translation_unit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string translation_unit(const std::string &path) +{ + // convert path into a suggestion for a translation unit name + + std::string unit=path; + + std::string::size_type pos; + + pos=unit.find_last_of("/\\"); + if(pos!=std::string::npos) + unit=std::string(unit, pos+1); + + pos=unit.find_last_of('.'); + if(pos!=std::string::npos) + unit=std::string(unit, 0, pos); + + return unit; +} + diff --git a/src/ansi-c/trans_unit.h b/src/ansi-c/trans_unit.h new file mode 100644 index 00000000000..ba1665d948c --- /dev/null +++ b/src/ansi-c/trans_unit.h @@ -0,0 +1,11 @@ +/*******************************************************************\ + +Module: Translation Unit + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +std::string translation_unit(const std::string &path); diff --git a/src/ansi-c/type2name.cpp b/src/ansi-c/type2name.cpp new file mode 100644 index 00000000000..0f7a48e4f7a --- /dev/null +++ b/src/ansi-c/type2name.cpp @@ -0,0 +1,141 @@ +/*******************************************************************\ + +Module: Type Naming for C + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include +#include + +#include "type2name.h" + +/*******************************************************************\ + +Function: type2name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string type2name(const typet &type) +{ + std::string result; + if(type.id()==irep_idt()) + throw "Empty type encountered."; + else if(type.id()==ID_empty) + result+="V"; + else if(type.id()==ID_signedbv) + result+="S" + type.get(ID_width).as_string(); + else if(type.id()==ID_unsignedbv) + result+="U" + type.get(ID_width).as_string(); + else if(type.id()==ID_bool) + result+="B"; + else if(type.id()==ID_integer) + result+="I"; + else if(type.id()==ID_real) + result+="R"; + else if(type.id()==ID_complex) + result+="C"; + else if(type.id()==ID_floatbv) + result+="F" + type.get(ID_width).as_string(); + else if(type.id()==ID_fixedbv) + result+="X" + type.get(ID_width).as_string(); + else if(type.id()==ID_natural) + result+="N"; + else if(type.id()==ID_pointer) + result+="*"; + else if(type.id()==ID_reference) + result+="&"; + else if(type.id()==ID_code) + { + const code_typet &t = to_code_type(type); + const code_typet::argumentst arguments = t.arguments(); + result+="P("; + for (code_typet::argumentst::const_iterator it = arguments.begin(); + it!=arguments.end(); + it++) + { + result+=type2name(it->type()); + result+="'" + it->get_identifier().as_string() + "'|"; + } + result.resize(result.size()-1); + result+=")"; + } + else if(type.id()==ID_array) + { + const array_typet &t = to_array_type(type); + result+="ARR" + t.size().get(ID_value).as_string(); + } + else if(type.id()==ID_incomplete_array) + { + result+="ARR?"; + } + else if(type.id()==ID_symbol) + { + result+="SYM#" + type.get(ID_identifier).as_string() + "#"; + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + if(type.id()==ID_struct) result +="ST"; + if(type.id()==ID_union) result +="UN"; + const struct_typet &t = to_struct_type(type); + const struct_typet::componentst &components = t.components(); + result+="["; + for(struct_typet::componentst::const_iterator it = components.begin(); + it!=components.end(); + it++) + { + result+=type2name(it->type()); + result+="'" + it->get(ID_name).as_string() + "'|"; + } + result.resize(result.size()-1); + result+="]"; + } + else if(type.id()==ID_incomplete_struct) + result +="ST?"; + else if(type.id()==ID_incomplete_union) + result +="UN?"; + else if(type.id()==ID_c_enum) + result +="EN" + type.get(ID_width).as_string(); + else if(type.id()==ID_incomplete_c_enum) + result +="EN?"; + else if(type.id()==ID_c_bitfield) + result+="BF"+type.get(ID_size).as_string(); + else if(type.id()==ID_vector) + result+="VEC"+type.get(ID_size).as_string(); + else + throw (std::string("Unknown type '") + + type.id().as_string() + + "' encountered."); + + if(type.has_subtype()) + { + result+="{"; + result+=type2name(type.subtype()); + result+="}"; + } + + if(type.has_subtypes()) + { + result+="$"; + forall_subtypes(it, type) + { + result+=type2name(*it); + result+="|"; + } + result.resize(result.size()-1); + result+="$"; + } + + return result; +} + diff --git a/src/ansi-c/type2name.h b/src/ansi-c/type2name.h new file mode 100644 index 00000000000..7742d031c92 --- /dev/null +++ b/src/ansi-c/type2name.h @@ -0,0 +1,12 @@ +/*******************************************************************\ + +Module: Type Naming for C + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include + +std::string type2name(const typet &type); diff --git a/src/big-int/README b/src/big-int/README new file mode 100644 index 00000000000..2fa251b46bd --- /dev/null +++ b/src/big-int/README @@ -0,0 +1 @@ +http://www.dirk-zoller.de/ diff --git a/src/big-int/allocainc.h b/src/big-int/allocainc.h new file mode 100644 index 00000000000..58b1acec762 --- /dev/null +++ b/src/big-int/allocainc.h @@ -0,0 +1,51 @@ +// $Id: allocainc.h,v 1.7 2005-12-25 14:44:24 kroening Exp $ + +// Whatever is necessary to use alloca(). + +#ifndef ALLOCAINC_H +#define ALLOCAINC_H + + +#if defined linux || defined __linux__ \ + || defined sun \ + || defined UWIN \ + || defined osf1 \ + || defined __MACH__ \ + || defined __CYGWIN__ + +#include + +#elif defined _MSC_VER \ + || defined __BORLANDC__ \ + || defined __MINGW32__ + +# include + +#elif defined __vax + +// In vax-alloca.mar. +extern "C" void *alloca (unsigned); + +#elif defined __VMS + +// DEC CXX on VMS alpha. +# include +# define alloca(N) __ALLOCA(N) + +#elif defined __xlC__ + +# pragma alloca +# include + +#elif defined __FCC__ + +# define alloca(X) __builtin_alloca(X) + +#elif defined __FreeBSD__ + +# include + +#endif + + +#endif//ndef ALLOCAINC_H diff --git a/src/big-int/bigint-func.cc b/src/big-int/bigint-func.cc new file mode 100644 index 00000000000..737e9bcbe5c --- /dev/null +++ b/src/big-int/bigint-func.cc @@ -0,0 +1,115 @@ +// $Id: bigint-func.cc,v 1.1.1.1 2002-06-13 22:00:30 kroening Exp $ + +// References: +// +// [1] Hüttenhofer/Lesch/Peyerimhoff, Mathematik in Anwendung mit C++ +// Heidelberg, Wiesbaden 1994 + +#include "bigint.hh" + + +BigInt +pow (BigInt const &x, unsigned y) +{ + BigInt a = x; + BigInt r = 1; + for (;;) + { + if (y & 1) + r *= a; + y >>= 1; + if (y == 0) + return r; + a *= a; + } +} + + +// Modular exponentiation, see [1] p. 74. + +BigInt +pow (BigInt const &x, BigInt const &y, BigInt const &m) +{ + BigInt a = x; + BigInt b = y; + BigInt r = 1; + for (;;) + { + a %= m; + if (b.is_odd()) + { + r *= a; + r %= m; + } + b /= 2; + if (b.is_zero()) + return r; + a *= a; + } +} + + +// Integer square root. see [1] p.23, slightly changed for better performance. + +BigInt +sqrt (BigInt const &n) +{ + BigInt x0 = n; + BigInt x1 = n; + x1 += 1; + x1 /= 2; + while (x1 < x0) + { + x0 = x1; + x1 += n / x0; + x1 /= 2; + } + return x0; +} + + +BigInt +gcd (const BigInt &X, const BigInt &Y) +{ + BigInt x (X); + BigInt y (Y); + for (;;) + { + if (y.is_zero()) + return x; + x %= y; + if (x.is_zero()) + return y; + y %= x; + } +} + + +// Modular inverse, see [1] p.35 +// Returns i in range 1..m-1 such that i*a = 1 mod m. +// a must be in range 1..m-1. + +BigInt +modinv (const BigInt &a, const BigInt &m) +{ + BigInt j (1), i (0); + BigInt b (m), c (a); + BigInt x, y; + while (!c.is_zero()) + { + BigInt::div (b, c, x, y); + b = c; + c = y; + y = j; + + // j = i - j * x; trading clarity for efficiency. + j *= x; + j -= i; + j.negate(); + + i = y; + } + if (i.is_negative()) + i += m; + return i; +} diff --git a/src/big-int/bigint-test.cc b/src/big-int/bigint-test.cc new file mode 100644 index 00000000000..ec13b56063c --- /dev/null +++ b/src/big-int/bigint-test.cc @@ -0,0 +1,292 @@ +// $Id: bigint-test.cc,v 1.1.1.1 2002-06-13 22:00:30 kroening Exp $ + +// My own BigInt class, test cases. + +#include "bigint.hh" +#include "allocainc.h" + +#include +#include +#include +#include + + +// ===================================================================== +// Printing and reading bignums. +// ===================================================================== + +static void +print (FILE *f, BigInt const &x, unsigned base = 10) +{ + unsigned len = x.digits (base) + 2; + char *s = x.as_string ((char *)alloca (len), len, base); + fputs (s, f); +} + +static bool +read (FILE *f, BigInt &x, unsigned base = 10) +{ + char buf[4096]; + return 1 == fscanf (f, " %4096[-+0-9a-zA-Z]", buf) + && x.scan (buf, base) == buf + strlen (buf); +} + +static void +print (BigInt const &x, unsigned base = 10) +{ + print (stdout, x, base); +} + +#if 0 +static bool +read (BigInt &x, unsigned base = 10) +{ + return read (stdin, x, base); +} +#endif + + +// ===================================================================== +// Simple tests. +// ===================================================================== +// Good when something basic is broken an must be debugged. + +static void +run_simple_tests () +{ + print (BigInt (unsigned (0xFFFFFFFF))); putchar ('\n'); + print (BigInt (unsigned (0xFFFFFFFF)), 2); putchar ('\n'); + print (BigInt ("123456789012345678901234567890")); putchar ('\n'); + + print (BigInt ("99999999999999999999999999999999", 10) / + BigInt ("999999999999999999999999", 10), 10); putchar ('\n'); + print (BigInt ("99999999999999999999999999999999", 10) % + BigInt ("999999999999999999999999", 10), 10); putchar ('\n'); + + BigInt t (100); + t -= 300; + print (t); + putchar ('\n'); + + BigInt r = BigInt (-124) + 124; + print (r); + if (BigInt (0) <= r) + printf ("\nworks ok here\n"); + + int j; + BigInt i (1); + for (j = 0; j < 1000; j++) + i += 100000000; + print (i); + putchar ('\n'); + + for (j = 0; j < 2000; j++) + i -= 100000000; + print (i); + putchar ('\n'); + + for (j = 0; j < 1000; j++) + i += 100000000; + print (i); + putchar ('\n'); +} + + +// ===================================================================== +// Test cases from the clisp test suite in number.tst. +// ===================================================================== + +// I took those test cases in number.tst from file +// +// clisp-1998-09-09/tests/number.tst +// +// in clispsrc.tar.gz. From the README file in that directory: +/* + +This directory contains a test suite for testing Common Lisp (CLtL1) +implementations. + +In its original version it was built by + + Horst Friedrich, ISST of FhG + Ingo Mohr, ISST of FhG + Ulrich Kriegel, ISST of FhG + Windfried Heicking, ISST of FhG + Rainer Rosenmueller, ISST of FhG + +at + + Institut für Software- und Systemtechnik der Fraunhofer-Gesellschaft + (Fraunhofer Institute for Software Engineering and Systems Engineering) + Kurstraße 33 + D-10117 Berlin + Germany + +for their Common Lisp implementation named XCL. + +What you see here is a version adapted to CLISP and AKCL by + + Bruno Haible +*/ + +// Actually I have no idea what principles directed the choice of test +// cases and what they are worth. Nevertheless it makes me feel better +// when BigInt comes to the same results as a Common Lisp should. Note +// that Lisp uses a floored divide operator which means that the +// quotient is rounded towards negative infinity. The remainder has to +// be adjusted accordingly. + +// Each test is operator op1 op2 result [result2]. Everything is white +// space delimited with line breaks meaning nothing special. Read +// operator and operands, compute, compare with expected result and +// complain if not. + +static void +print_err (char const *op, + BigInt const &a, BigInt const &b, + BigInt const &r, BigInt const &s) +{ + fprintf (stderr, "Error in %s:\n", op); + print (stderr, a); + fprintf (stderr, " %s ", op); + print (stderr, b); + fputs (" = ", stderr); + print (stderr, r); + fputs ("\nShould be: ", stderr); + print (stderr, s); + fputs ("\n", stderr); +} + +static void +run_clisp_tests (char const *fn) +{ + FILE *f = fopen (fn, "rt"); + if (f == 0) + { + fprintf (stderr, "Error opening %s: %s.\n", fn, strerror (errno)); + return; + } + for (;;) + { + char op[2]; + if (1 != fscanf (f, " %1[-+*/]", op)) + { + puts ("\nFine!"); + if (!feof (f)) + fprintf (stderr, "Error reading operator from %s.\n", fn); + fclose (f); + return; + } + BigInt a, b, r, er; + if (!read (f, a) || + !read (f, b) || + !read (f, er)) + { + fprintf (stderr, "Error reading number from %s.\n", fn); + fclose (f); + return; + } +#if 0 + print(a,10); putchar ('\n'); + print(b,10); putchar ('\n'); + print(er,10); putchar ('\n'); +#endif + switch (op[0]) + { + case '+': + r = a + b; + break; + case '-': + r = a - b; + break; + case '*': + r = a * b; + break; + case '/': + // These lines also have a remainder. + BigInt em; + if (!read (f, em)) + { + fprintf (stderr, "Error reading number from %s.\n", fn); + fclose (f); + return; + } + r = a / b; + BigInt m = a % b; + // The test-data from the Lisp testsuite are assuming + // floored divide. Fix the results accordingly. + if (!m.is_zero() && a.is_positive() != b.is_positive()) + { + r -= 1; + m += b; + } + if (r != er) + { + print_err ("/", a, b, r, er); + continue; + } + if (m != em) + { + print_err ("%", a, b, m, em); + continue; + } + // Also try the method returning both. + BigInt::div (a, b, r, m); + // Again, transform to floored divide. + if (!m.is_zero() && a.is_positive() != b.is_positive()) + { + r -= 1; + m += b; + } + if (r != er) + { + print_err ("div", a, b, r, er); + continue; + } + if (m != em) + { + print_err ("rem", a, b, m, em); + continue; + } + putchar ('.'); + fflush (stdout); + continue; + } + if (r != er) + { + print_err (op, a, b, r, er); + continue; + } + putchar ('.'); + fflush (stdout); + } +} + + +// ===================================================================== +// Integer roots. +// ===================================================================== + +static BigInt +sqrt (int n, int dig) +{ + BigInt N (n); + N *= pow (BigInt (100), dig); + return sqrt (N); +} + +static void +run_sqrt_test () +{ + print (sqrt (2, 1000)); + putchar ('\n'); +} + +int +main (int, char *[]) +{ + run_simple_tests (); + run_clisp_tests ("number.tst"); + run_sqrt_test (); + return 0; +} diff --git a/src/big-int/bigint.cc b/src/big-int/bigint.cc new file mode 100644 index 00000000000..c2cc276021e --- /dev/null +++ b/src/big-int/bigint.cc @@ -0,0 +1,1276 @@ +// $Id: bigint.cc,v 1.5 2010-12-28 16:14:18 kroening Exp $ + +// My own BigInt class, implementation. + +// The copyright at this source is owned Dirk Zoller. +// You may however use and modify this without restriction. + +#include "bigint.hh" +#include "allocainc.h" + +#include +#include +#include + +// How to report errors. +#include +#define error(x) fprintf (stderr, "%s\n", x) + + +// Shortcut access to BigInt scoped things. +typedef BigInt::llong_t llong_t; +typedef BigInt::ullong_t ullong_t; +typedef BigInt::onedig_t onedig_t; +typedef BigInt::twodig_t twodig_t; + +static const unsigned small = BigInt::small; +static const int single_bits = sizeof (onedig_t) * CHAR_BIT; +static const twodig_t base = twodig_t (1) << single_bits; +static const twodig_t single_max = base - 1; + + +inline unsigned +adjust_size (unsigned size) +{ + // Always allocate at least something greater than an ullong_t. + if (size <= small) + size = small + 1; + + // Assuming the heap works with a specific granularity G and needs + // space B for a few bookkeeping pointers: Prefer allocation sizes + // of N * G - B. (Just guesses. May tune that later.) + const unsigned G = 32; + const unsigned B = 8; + size *= sizeof (onedig_t); + size += B; + size += G - 1; + size /= G; + size *= G; + size -= B; + size /= sizeof (onedig_t); + return size; +} + + +// Compare unsigned digit strings, returns -1/0/+1. + +inline int +digit_cmp (onedig_t const *a, onedig_t const *b, unsigned n) +{ + for (int i = n; --i >= 0; ) + { + if (a[i] < b[i]) + return -1; + else if (a[i] > b[i]) + return 1; + } + return 0; +} + + +// Add unsigned digit strings, return carry. Assumes l1 >= l2! +static _fast onedig_t +digit_add (onedig_t const *d1, unsigned l1, + onedig_t const *d2, unsigned l2, + onedig_t *r) // May be same as d1 or d2. +{ + twodig_t c = 0; + unsigned i = 0; + while (i < l2) + { + c += twodig_t (d1[i]) + twodig_t (d2[i]); + r[i++] = onedig_t (c); + c >>= single_bits; + } + while (i < l1) + { + c += twodig_t (d1[i]); + r[i++] = onedig_t (c); + c >>= single_bits; + if (c == 0) + goto copy; + } + return onedig_t (c); + copy: + if (r != d1) + while (i < l1) + r[i] = d1[i], ++i; + return 0; +} + + +// Subtract unsigned digit strings, return carry. Assumes l1 >= l2! +static _fast void +digit_sub (onedig_t const *d1, unsigned l1, + onedig_t const *d2, unsigned l2, + onedig_t *r) // May be same as d1 or d2. +{ + twodig_t c = 1; + unsigned i = 0; + while (i < l2) + { + c += twodig_t (d1[i]) + single_max - twodig_t (d2[i]); + r[i++] = onedig_t (c); + c >>= single_bits; + } + while (i < l1) + { + if (c != 0) + goto copy; + c = twodig_t (d1[i]) + single_max; + r[i++] = onedig_t (c); + c >>= single_bits; + } + return; + copy: + if (r != d1) + while (i < l1) + r[i] = d1[i], ++i; +} + + +// Multiply unsigned digit string by single digit, replaces argument +// with product and returns overflowing digit. +static _fast onedig_t +digit_mul (onedig_t *b, unsigned l, onedig_t d) +{ + twodig_t p = 0; + for (int i = l; --i >= 0; ) + { + p += twodig_t (d) * twodig_t (*b); + *b++ = onedig_t (p); + p >>= single_bits; + } + return onedig_t (p); +} + + +// Multiply two digit strings. Writes result into a third digit string +// which must have the appropriate size and must not be same as one of +// the arguments. +static _fast void +digit_mul (onedig_t const *a, unsigned la, + onedig_t const *b, unsigned lb, + onedig_t *r) // Must not be same as a or b. +{ + memset (r, 0, (la + lb) * sizeof (onedig_t)); + for (unsigned i = 0; i < la; i++) + { + onedig_t d = a[i]; + twodig_t p = 0; + for (unsigned j = 0; j < lb; j++) + { + p += r[j] + twodig_t (d) * b[j]; + r[j] = onedig_t (p); + p >>= single_bits; + } + r[lb] = onedig_t (p); + ++r; + } +} + + +// Divide unsigned digit string by single digit, replaces argument +// with quotient and returns remainder. +static _fast onedig_t +digit_div (onedig_t *b, unsigned l, onedig_t d) +{ + twodig_t r = 0; + for (int i = l; --i >= 0; ) + { + r <<= single_bits; + r |= b[i]; + b[i] = onedig_t (r / d); + r %= d; + } + return onedig_t (r); +} + + +// Subroutines for division primitive. Mostly for clarity. + +// Guess quotient digit based on the three most significant dividend +// digits and the two most significant digits of the divisor. Since we +// have least significant digit first here, the indices used to access +// less significant digits are negative. +inline onedig_t +guess_q (onedig_t const *r, onedig_t const *y) +{ + onedig_t q = onedig_t (y[0] == r[0] + ? single_max + : (twodig_t (r[0]) << single_bits | r[-1]) / y[0]); + for (;;) + { + onedig_t t[3]; + twodig_t p; + p = twodig_t (q) * y[-1]; t[0] = onedig_t (p); p >>= single_bits; + p += twodig_t (q) * y[ 0]; t[1] = onedig_t (p); p >>= single_bits; + t[2] = onedig_t (p); + if (digit_cmp (t, r - 2, 3) <= 0) + return q; + --q; + } +} + +// Multiply divisor with quotient digit and subtract from dividend. +// Returns overflow. +static _fast onedig_t +multiply_and_subtract (onedig_t *r, onedig_t const *y, unsigned l, onedig_t q) +{ + twodig_t p = 0; + twodig_t h = 1; + for (unsigned i = 0; i < l; i++) + { + p += twodig_t (q) * y[i]; + h += r[i]; + h += single_max; + h -= onedig_t (p); + r[i] = onedig_t (h); + p >>= single_bits; + h >>= single_bits; + } + h += r[l]; + h += single_max; + h -= p; + r[l] = onedig_t (h); + return onedig_t (h >> single_bits); +} + +// Add back divisor digits to dividend, corresponds to a correction of +// the guessed quotient digit by -1. +static _fast void +add_back (onedig_t *r, onedig_t const *y, unsigned l) +{ + twodig_t h = 0; + for (unsigned i = 0; i < l; i++) + { + h += r[i]; + h += y[i]; + r[i] = onedig_t (h); + h >>= single_bits; + } + r[l] = 0; +} + +// Divide two digit strings. Divides r by y/yl. Stores quotient in +// q/ql and leaves the remainder in r. Size of r is yl+ql. +static _fast void +digit_div (onedig_t *r, const onedig_t *y, unsigned yl, onedig_t *q, unsigned ql) +{ + r += ql; + for (int i = ql; --r, --i >= 0; ) + { + onedig_t qh = guess_q (r + yl, y + yl - 1); + if (multiply_and_subtract (r, y, yl, qh) == 0) + { + --qh; + add_back (r, y, yl); + } + if (q != 0) + q[i] = qh; + } +} + + +// Newly allocate uninitialized space for specified number of digits. + +inline void +BigInt::allocate (unsigned digits) +{ + size = adjust_size (digits); + length = 0; + digit = new onedig_t[size]; +} + + +// Used in assignment: When smaller than specified digits, allocate +// anew. Don`t bother to keep the contents. + +inline void +BigInt::reallocate (unsigned digits) +{ + if (digits > size) + { + if (size) + delete[] digit; + size = adjust_size (digits); + digit = new onedig_t[size]; + } +} + + +// Increase size keeping the contents. + +inline void +BigInt::resize (unsigned digits) +{ + if (digits > size) + { + onedig_t *old_digit = digit; + unsigned old_size = size; + size = adjust_size (digits); + digit = new onedig_t[size]; + if (old_digit) + { + memcpy (digit, old_digit, length * sizeof (onedig_t)); + if (old_size) + delete[] old_digit; + } + } +} + + +// Adjust length (e.g. after subtraction). + +inline void +BigInt::adjust() +{ + for (; ; --length) + { + if (length == 0) + { + positive = true; + break; + } + if (digit[length - 1] != 0) + break; + } +} + + +// Store unsigned elementary integer type into string of onedig_t. + +inline void +digit_set (ullong_t ul, onedig_t d[small], unsigned &l) +{ + l = 0; + if (ul) + { + d[l++] = onedig_t (ul); + ul >>= single_bits; + if (small > 2) + while (ul) + { + d[l++] = onedig_t (ul); + ul >>= single_bits; + } + else + if (ul) + d[l++] = onedig_t (ul); + } +} + +void +BigInt::assign (ullong_t ul) +{ + positive = true; + digit_set (ul, digit, length); +} + +// Dito signed. +void +BigInt::assign (llong_t l) +{ + if (l >= 0) + { + digit_set (ullong_t (l), digit, length); + positive = true; + } + else + { + digit_set (ullong_t (-l), digit, length); + positive = false; + } +} + + +BigInt::~BigInt() +{ + if (size > 0) + { + memset (digit, 0, size * sizeof digit[0]); // Crypto-paranoia. + delete[] digit; + } +} + +BigInt::BigInt (onedig_t *dig, unsigned len, bool pos) + : size (0), + length (len), + digit (dig), + positive (pos) +{} + +BigInt::BigInt() + : size (adjust_size (small)), + length (0), + digit (new onedig_t[size]), + positive (true) +{} + +BigInt::BigInt (signed long int n) + : size (adjust_size (small)), + length (0), + digit (new onedig_t[size]) +{ + assign (llong_t (n)); +} + +BigInt::BigInt (unsigned long int n) + : size (adjust_size (small)), + length (0), + digit (new onedig_t[size]) +{ + assign (ullong_t (n)); +} + +BigInt::BigInt (int n) + : size (adjust_size (small)), + length (0), + digit (new onedig_t[size]) +{ + assign (llong_t (n)); +} + +BigInt::BigInt (unsigned u) + : size (adjust_size (small)), + length (0), + digit (new onedig_t[size]) +{ + assign (ullong_t (u)); +} + +BigInt::BigInt (llong_t l) + : size (adjust_size (small)), + length (0), + digit (new onedig_t[size]) +{ + assign (l); +} + +BigInt::BigInt (ullong_t ul) + : size (adjust_size (small)), + length (0), + digit (new onedig_t[size]) +{ + assign (ul); +} + +BigInt::BigInt (BigInt const &y) + : size (adjust_size (y.length)), + length (y.length), + digit (new onedig_t[size]), + positive (y.positive) +{ + memcpy (digit, y.digit, length * sizeof (onedig_t)); +} + +BigInt::BigInt (char const *s, onedig_t b) + : size (adjust_size (small)), + length (0), + digit (new onedig_t[size]), + positive (true) +{ + scan (s, b); +} + + +BigInt & +BigInt::operator= (llong_t l) +{ + reallocate (small); + assign (l); + return *this; +} + +BigInt & +BigInt::operator= (ullong_t ul) +{ + reallocate (small); + assign (ul); + return *this; +} + +BigInt & +BigInt::operator= (BigInt const &y) +{ + if (&y != this) + { + reallocate (y.length); + length = y.length; + positive = y.positive; + memcpy (digit, y.digit, length * sizeof (onedig_t)); + } + return *this; +} + +BigInt & +BigInt::operator= (char const *s) +{ + scan (s); + return *this; +} + + +char const * +BigInt::scan_on (char const *s, onedig_t b) +{ + for (char c = *s; c; c = *++s) + { + // Convert digit. Use 0..9A..Z for singles up to 36. Ignoring case. + c = toupper (c); + onedig_t dig; + if (c < '0') + return s; + else if (c <= '9') + dig = c - '0'; + else if (c < 'A') + return s; + else if (c <= 'Z') + dig = c - 'A' + 10; + else + return s; + if (dig >= b) + return s; + // Multiply this by single and add digit. + onedig_t r = digit_mul (digit, length, b); + if (r) + { + resize (length + 1); + digit[length++] = r; + } + if (length == 0) + // digit_add() would choke on empty first argument. + length = 1, digit[0] = dig; + else + { + r = digit_add (digit, length, &dig, 1, digit); + if (r) + { + resize (length + 1); + digit[length++] = r; + } + } + } + adjust(); + return s; +} + +char const * +BigInt::scan (char const *s, onedig_t b) +{ + if (*s == '-') + positive = false, ++s; + else if (*s == '+') + ++s; + return scan_on (s, b); +} + + +unsigned +BigInt::digits (onedig_t b) const +{ + int bits = -1; + while (b) + ++bits, b >>= 1; + return (single_bits * length + bits - 1) / bits; +} + +char * +BigInt::as_string (char *p, unsigned l, onedig_t b) const +{ + if (l < 2) + return 0; // Not enough room for number. + p[--l] = '\0'; + // Check for zero. Would otherwise print as empty string. + unsigned len = length; + while (len && digit[len-1] == 0) + --len; + if (len == 0) + { + p[--l] = '0'; + return p + l; + } + // Make a temporary copy of the digits. + onedig_t *dig = (onedig_t *)alloca (len * sizeof (onedig_t)); + memcpy (dig, digit, len * sizeof (onedig_t)); + // Divide down by single, generating digits from right to left. + do + { + if (l == 0) + return 0; + onedig_t r = digit_div (dig, len, b); + p[--l] = r < 10 ? r + '0' : 'A' + r - 10; + if (dig[len-1] == 0) + --len; + } + while (len); + // Maybe attach sign. + if (!positive){ + if (l == 0) + return 0; + else + p[--l] = '-'; + } + // Return pointer to start of number. + return p + l; +} + +bool +BigInt::dump (unsigned char *p, unsigned n) +{ + // Access most significant digit. + onedig_t *t = digit + length; + while (t > digit && t[-1] == 0) + --t; + if (t <= digit) + { + // Number is zero. + memset (p, 0, n); + return true; + } + // Determine number m of characters needed. + onedig_t d = *--t; + unsigned i = sizeof (onedig_t); + while (--i && (d >> i * CHAR_BIT) == 0); + unsigned m = ++i + (t - digit) * sizeof (onedig_t); + // Fill in leading zeroes. + if (m > n) + { + // Number doesn't fit into the provided space, overflow. + memset (p, 0, n); + return false; + } + memset (p, 0, n - m); + p += n - m; + // Copy out digits. + for (;;) + { + while (i--) + *p++ = d >> i * CHAR_BIT; + if (t <= digit) + break; + d = *--t; + i = sizeof (onedig_t); + } + return true; +} + +void +BigInt::load (unsigned char const *p, unsigned n) +{ + // Skip leading zeroes. + while (n > 0 && *p == 0) + ++p, --n; + // Provide enough digits for the assigned number. + reallocate ((n + sizeof (onedig_t) - 1) / sizeof (onedig_t)); + // Copy in digits. + length = 0; + unsigned char const *q = p + n; + onedig_t d = 0; + unsigned i = 0; + for (;;) + { + if (q <= p) + break; + d |= *--q << i++ * CHAR_BIT; + if (i < sizeof (onedig_t)) + continue; + digit[length++] = d; + d = 0; + i = 0; + } + if (d) + digit[length++] = d; +} + +bool +BigInt::is_long() const +{ + if (length < small) + return true; + if (length > small) + return false; + const onedig_t max = onedig_t (1) << (single_bits - 1); + if (digit[small - 1] < max) + return true; + if (positive || digit[small - 1] > max) + return false; + // There is exactly one good signed number n with abs (n) having the + // topmost bit set: The most negative number. + for (int l = length - 1; --l >= 0; ) + if (digit[l] != 0) + return false; + return true; +} + +ullong_t BigInt::to_ulong() const +{ + ullong_t ul = 0; + for (int i = length; --i >= 0; ) + { + ul <<= single_bits; + ul |= digit[i]; + } + return ul; +} + +llong_t BigInt::to_long() const +{ + ullong_t ul = to_ulong(); + return positive ? ul : -llong_t (ul); +} + + +// Auxiliary method for comparing magnitude. + +inline int +BigInt::ucompare (BigInt const &b) const +{ + if (length < b.length) + return -1; + else if (length > b.length) + return 1; + else + return digit_cmp (digit, b.digit, length); +} + +// Comparision primitives. + +int +BigInt::compare (ullong_t b) const +{ + if (!positive) + return -1; + onedig_t dig[small]; + unsigned len; + digit_set (b, dig, len); + if (length < len) + return -1; + if (length > len) + return 1; + else + return digit_cmp (digit, dig, len); +} + +int +BigInt::compare (llong_t b) const +{ + // DK original code: + // return b < 0 ? -compare (ullong_t (-b)) : compare (ullong_t (b)); + // fails if b is negative + + if(b>=0) + return compare (ullong_t (b)); + + if (positive) + return 1; + + onedig_t dig[small]; + unsigned len; + digit_set (-b, dig, len); + if (length < len) + return 1; + if (length > len) + return -1; + else + return -digit_cmp (digit, dig, len); +} + +int +BigInt::compare (BigInt const &b) const +{ + if (positive < b.positive) + return -1; + else if (positive > b.positive) + return 1; + int result = ucompare (b); + return positive ? result : -result; +} + + +// Auxiliary method for all adding and subtracting. + +void +BigInt::add (onedig_t const *dig, unsigned len, bool pos) +{ + // Make sure the result fits into this, even with carry. + resize ((length > len ? length : len) + 1); + + // Assign greater operand to d1/l1, for the add/sub primitives + // expect the greater operand first. + onedig_t const *d1; + onedig_t const *d2; + unsigned l1, l2; + bool gt = (length > len || + (length == len && digit_cmp (digit, dig, len) >= 0)); + if (gt) + { + d1 = digit; + l1 = length; + d2 = dig; + l2 = len; + } + else + { + d1 = dig; + l1 = len; + d2 = digit; + l2 = length; + } + + // Decide if the magnitudes are to be added or subtracted. + if (positive == pos) + { + // We're adding effectively. + onedig_t c = digit_add (d1, l1, d2, l2, digit); + length = l1; + if (c) + digit[length++] = c; + // Sign remains unchanged. + } + else + { + // We're subtracting effectively. + digit_sub (d1, l1, d2, l2, digit); + length = l1; + // The greater operand determines the sign of the result. + if (!gt) + positive = pos; + adjust(); + } +} + + +// Auxiliary method for multiplication. + +void +BigInt::mul (onedig_t const *dig, unsigned len, bool pos) +{ + if (len < 2) + { + // Handle small dig/len operand efficiently. + if (len == 0 || dig[0] == 0) + { + length = 0; + positive = true; + return; + } + if (dig[0] != 1) + { + onedig_t c = digit_mul (digit, length, dig[0]); + if (c != 0) + { + resize (length + 1); + digit[length++] = c; + } + } + } + else if (length < 2) + { + // Handle small this efficiently, replacing it with dig/len. + if (length == 0 || digit[0] == 0) + { + length = 0; + positive = true; + return; + } + onedig_t d = digit[0]; + reallocate (len + 1); + memcpy (digit, dig, len * sizeof (onedig_t)); + length = len; + if (d != 1) + { + onedig_t c = digit_mul (digit, length, d); + if (c != 0) + digit[length++] = c; + } + } + else + { + // Get a new string of digits for the result. + unsigned old_size = size; + size = adjust_size (length + len); + onedig_t *r = new onedig_t[size]; + + // The first parameter pair defines the outer loop which should + // be the shorter. + if (length < len) + digit_mul (digit, length, dig, len, r); + else + digit_mul (dig, len, digit, length, r); + + // Replace digit string of this with result. + if (old_size) + delete[] digit; + digit = r; + length += len; + adjust(); + } + if (!pos) + positive = !positive; +} + + +BigInt & +BigInt::operator+= (llong_t y) +{ + bool pos = y > 0; + ullong_t uy = pos ? y : -y; + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + add (yb, yl, pos); + return *this; +} + +BigInt & +BigInt::operator-= (llong_t y) +{ + bool pos = y > 0; + ullong_t uy = pos ? y : -y; + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + add (yb, yl, !pos); + return *this; +} + +BigInt & +BigInt::operator*= (llong_t y) +{ + bool pos = y > 0; + ullong_t uy = pos ? y : -y; + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + mul (yb, yl, pos); + return *this; +} + +BigInt & +BigInt::operator/= (llong_t y) +{ + bool pos = y > 0; + ullong_t uy = pos ? y : -y; + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + return *this /= BigInt (yb, yl, pos); +} + +BigInt & +BigInt::operator%= (llong_t y) +{ + bool pos = y > 0; + ullong_t uy = pos ? y : -y; + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + return *this %= BigInt (yb, yl, pos); +} + + +BigInt & +BigInt::operator+= (ullong_t uy) +{ + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + add (yb, yl, true); + return *this; +} + +BigInt & +BigInt::operator-= (ullong_t uy) +{ + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + add (yb, yl, false); + return *this; +} + +BigInt & +BigInt::operator*= (ullong_t uy) +{ + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + mul (yb, yl, true); + return *this; +} + +BigInt & +BigInt::operator/= (ullong_t uy) +{ + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + return *this /= BigInt (yb, yl, true); +} + +BigInt & +BigInt::operator%= (ullong_t uy) +{ + onedig_t yb[small]; + unsigned yl; + digit_set (uy, yb, yl); + return *this %= BigInt (yb, yl, true); +} + + +BigInt & +BigInt::operator+= (BigInt const &y) +{ + add (y.digit, y.length, y.positive); + return *this; +} + +BigInt & +BigInt::operator-= (BigInt const &y) +{ + add (y.digit, y.length, !y.positive); + return *this; +} + +BigInt & +BigInt::operator*= (BigInt const &y) +{ + mul (y.digit, y.length, y.positive); + return *this; +} + + +// Division method returning both quotient and remainder. + +void +BigInt::div (BigInt const &x, BigInt const &y, BigInt &q, BigInt &r) +{ + // Eliminate some trivial cases. + int cmp = x.ucompare (y); + if (cmp < 0) + { + // Dividend less than divisor: Quotient = 0, remainder = dividend. + r = x; + q.length = 0; + q.positive = true; + return; + } + if (cmp == 0) + { + // Both operands are equal: Quotient = 1, remainder = 0. + r.length = 0; + r.positive = true; + q.length = 1; + q.digit[0] = 1; + if (!y.positive) + q.negate(); + return; + } + if (y.length == 0) + { + zero: + error ("Division by zero."); + return; + } + if (x.is_ulong()) + { + // Can do it directly. + ullong_t n = x.to_ulong(); + ullong_t m = y.to_ulong(); + if (m == 0) + goto zero; + q.assign (n / m); + r.assign (n % m); + } + else if (y.length == 1) + { + // This digit_div() transforms the dividend into the quotient. + q = y; + r.digit[0] = digit_div (q.digit, q.length, y.digit[0]); + r.length = r.digit[0] ? 1 : 0; + } + else + { + // Copy operands and scale them such that the first divisor + // digit is initially no less than half base. This is essential + // for guess_q() to work. + unsigned al = x.length; + onedig_t *a = (onedig_t *)alloca ((al + 2) * sizeof (onedig_t)); + memcpy (a, x.digit, al * sizeof (onedig_t)); + + unsigned bl = y.length; + onedig_t *b = (onedig_t *)alloca (bl * sizeof (onedig_t)); + memcpy (b, y.digit, bl * sizeof (onedig_t)); + + onedig_t scale = onedig_t (base / (1 + b[bl - 1])); + if (scale != 1) + { + if ((a[al] = digit_mul (a, al, scale)) != 0) ++al; + digit_mul (b, bl, scale); + } + + // Still we must avoid the first dividend digit being greater + // than the first divisor digit. + if (a[al-1] >= b[bl-1]) + a[al++] = 0; + + // Prepare q for receiving the quotient. + q.length = al - bl; + q.resize (q.length); + + // Divide. + digit_div (a, b, bl, q.digit, q.length); + + // Unscale the remainder and copy it into r. + for (al = bl; al && a[al - 1] == 0; --al); + if (scale != 1) + digit_div (a, al, scale); + if (al && a[al - 1] == 0) --al; + r.length = al; + r.resize (r.length); + memcpy (r.digit, a, al * sizeof (onedig_t)); + } + q.adjust(); + q.positive = q.length == 0 || x.positive == y.positive; + r.positive = r.length == 0 || x.positive; +} + +BigInt & +BigInt::operator/= (BigInt const &y) +{ + // Eliminate some trivial cases. + int cmp = ucompare (y); + if (cmp < 0) + { + // Dividend less than divisor: Quotient = 0. + length = 0; + positive = true; + return *this; + } + if (cmp == 0) + { + // Both operands are equal: Quotient = 1. + length = 1; + digit[0] = 1; + goto set_sign; + } + if (y.length == 0) + { + zero: + error ("Division by zero."); + return *this; + } + if (is_ulong()) + { + // Can do it directly. + ullong_t n = to_ulong(); + ullong_t m = y.to_ulong(); + if (m == 0) + goto zero; + digit_set (n / m, digit, length); + } + else if (y.length == 1) + { + // This digit_div() transforms the dividend into the quotient. + digit_div (digit, length, y.digit[0]); + } + else + { + // Copy and scale as above in div(). + unsigned al = length; + onedig_t *a = (onedig_t *)alloca ((al + 2) * sizeof (onedig_t)); + memcpy (a, digit, al * sizeof (onedig_t)); + + unsigned bl = y.length; + onedig_t *b = (onedig_t *)alloca (bl * sizeof (onedig_t)); + memcpy (b, y.digit, bl * sizeof (onedig_t)); + + onedig_t scale = onedig_t (base / (1 + b[bl - 1])); + if (scale != 1) + { + if ((a[al] = digit_mul (a, al, scale)) != 0) ++al; + digit_mul (b, bl, scale); + } + if (a[al-1] >= b[bl-1]) + a[al++] = 0; + length = al - bl; + digit_div (a, b, bl, digit, length); + } + adjust(); + set_sign: + if (!y.positive) + negate(); + return *this; +} + +BigInt & +BigInt::operator%= (BigInt const &y) +{ + // Eliminate some trivial cases. + int cmp = ucompare (y); + if (cmp < 0) + { + // Dividend less than divisor: Remainder = dividend. + return *this; + } + if (cmp == 0) + { + // Both operands are equal: Remainder = 0. + length = 0; + positive = true; + return *this; + } + if (y.length == 0) + { + zero: + error ("Division by zero."); + return *this; + } + if (is_ulong()) + { + // Can do it directly. + ullong_t n = to_ulong(); + ullong_t m = y.to_ulong(); + if (m == 0) + goto zero; + digit_set (n % m, digit, length); + } + else if (y.length == 1) + { + // This digit_div() transforms the dividend into the quotient. + // Overwrite with remainder immediately afterwards. + digit[0] = digit_div (digit, length, y.digit[0]); + length = 1; + } + else + { + // Scale as above. But do not copy dividend. It is transformed + // into the remainder which is the result we want here. + resize (length + 2); + unsigned &al = length; + onedig_t *a = digit; + + unsigned bl = y.length; + onedig_t *b = (onedig_t *)alloca (bl * sizeof (onedig_t)); + memcpy (b, y.digit, bl * sizeof (onedig_t)); + + onedig_t scale = onedig_t (base / (1 + b[bl - 1])); + if (scale != 1) + { + if ((a[al] = digit_mul (a, al, scale)) != 0) ++al; + digit_mul (b, bl, scale); + } + if (a[al-1] >= b[bl-1]) + a[al++] = 0; + digit_div (a, b, bl, 0, al - bl); + length = bl; + adjust(); + if (scale != 1) + digit_div (digit, length, scale); + } + adjust(); + if (length == 0) + positive = true; + return *this; +} diff --git a/src/big-int/bigint.hh b/src/big-int/bigint.hh new file mode 100644 index 00000000000..f83ef77d202 --- /dev/null +++ b/src/big-int/bigint.hh @@ -0,0 +1,345 @@ +// $Id: bigint.hh,v 1.12 2009-01-24 15:14:46 kroening Exp $ + +// My own BigInt class, declaration. + +#ifndef BIGINT_HH +#define BIGINT_HH + +// This one is pretty simple but has a fair divide implementation. +// Though I'm not ambitious enough to do that FFT-like stuff. +// +// Desirable properties / design goals: +// +// - Highly configurable, should run on chips from 8 to 64 bits word size. +// - Is relatively small. +// - Is thread safe. +// - Does not significantly pollute the global namespace. +// +// Deliberate omissions: +// +// - Support of the C++ iostream classes which I find obsolete. +// (Of course there are conversions from/to strings.) +// - Bitwise operations. A bitset is a different thing than a BigInt. +// +// Porting and configuration: +// +// - Somehow resolve the problem when your compiler has no `bool' yet. +// - Choose appropriate elementary types to use for onedig_t, twodig_t, +// llong_t, and ullong_t. Some possible variants are #ifdef'd below. +// - Decide what to do on division by zero (in implementation file.) +// - In allocainc.h, do whatever necessary to provide the alloca() function. +// +// That should be all. I saw this running on: +// +// - Linux/egcs-1.1.2 ... gcc-2.95.2 +// - Linux/Comeau C++ 4.2.42 +// - Linux/Fujitsu C++ +// - FreeBSD 3.1/gcc-2.7.2 ... 4.1/gcc-2.95.2, NetBSD 1.4.2/egcs-1.1.1 +// - FreeBSD 4.x/TenDRA 4.2-BETA +// - AIX/xlC +// - Sun Solaris 2.5 ... 8/Sparc/SunPRO CC 4.2 ... Forte 6.0 +// - Windows NT/i386/MS Visual-C 4.2 ... 6.0 +// - Windows NT/i386/Borland-C++ 5.0, 5.5 (also with Intel reference compiler) +// - DEC-OSF1/DEC CXX 6.0 +// - DEC OpenVMS 6.2/VAX/DEC CXX 5.5 (without optimization) +// - DEC OpenVMS 6.2 and 7.1/Alpha/DEC CXX 5.6 +// +// Legal status: +// +// The copyright at this source is owned by Dirk Zoller, +// e-mail: duz@sol-3.de +// +// You may however use and modify this without restriction. + + +// Add your losing compiler to this list. +#if !defined bool \ + && (defined __SUNPRO_CC && (__SUNPRO_CC < 0x500 || \ + __SUNPRO_CC_COMPAT < 5) || \ + defined __xlC__ || \ + defined __DECCXX && __DECCXX_VER < 60000000 || \ + defined _MSC_VER && _MSC_VER < 1100) +#undef bool +#undef false +#undef true +#define bool int +#define false 0 +#define true 1 +#endif + +// Minor optimization for gcc on some intel platforms. +#if !defined _fast +# if defined __GNUC__ && defined __i386__ && defined NDEBUG +# define _fast __attribute__ ((__regparm__ (3),__stdcall__)) +# if defined _WIN32 +# define _fasta // Mingw-gcc crashes when alloca is used +# else // inside a function declared regparm +# define _fasta _fast // or stdcall (don't know which). +# endif +# else +# define _fast +# define _fasta +# endif +#endif + + +class BigInt +{ +public: + // Decide about and publish configuration details: + + // Choose digit type for best performance. Bigger is better as long + // as there are machine instructions for multiplying and dividing on + // twice the size of a digit, i.e. on twodig_t. +#if defined __GNUG__ || defined __alpha// || defined __TenDRA__ + // Or other true 64 bit CPU. + typedef unsigned onedig_t; + typedef unsigned long long twodig_t; +#elif defined _MSC_VER // || defined __BORLANDC__ (works but is slower) + // Like GCC's long long this __int64 actually performs better than + // unsigned. Though not as much as is the case with gcc. + typedef unsigned onedig_t; + typedef unsigned __int64 twodig_t; +#elif 1 + // Majority (currently) of 32 bit CPUs. + typedef unsigned short onedig_t; + typedef unsigned twodig_t; +#else + // Works on 8/16 bit CPUs just as well. + typedef unsigned char onedig_t; + typedef unsigned short twodig_t; +#endif + + // Choose largest integral type to use. Must be >= twodig_t. +#if defined __GNUG__ + typedef long long llong_t; + typedef unsigned long long ullong_t; +#elif defined _MSC_VER// || defined __BORLANDC__ + typedef __int64 llong_t; + typedef unsigned __int64 ullong_t; +#else + typedef long llong_t; + typedef unsigned long ullong_t; +#endif + + // Maximum number of onedig_t digits which could also be represented + // by an elementary type. + enum { small = sizeof (ullong_t) / sizeof (onedig_t) }; + +private: + unsigned size; // Length of digit vector. + unsigned length; // Used places in digit vector. + onedig_t *digit; // Least significant first. + bool positive; // Signed magnitude representation. + + // Create or resize this. + inline void allocate (unsigned digits); + inline void reallocate (unsigned digits); + inline void resize (unsigned digits); + + // Adjust length (e.g. after subtraction). + inline void adjust(); + + // Assign to this. + void assign (llong_t) _fast; + void assign (ullong_t) _fast; + + // Aux methods, only for internal use. + inline int ucompare (BigInt const &) const; + void add (onedig_t const *, unsigned, bool) _fast; + void mul (onedig_t const *, unsigned, bool) _fast; + + // Auxiliary constructor used for temporary or static BigInt. + // Sets size=0 which indicates that ~BigInt must not delete[]. + inline BigInt (onedig_t *, unsigned, bool) _fast; + +public: + ~BigInt() _fast; + + BigInt() _fast; + BigInt (int) _fast; + BigInt (unsigned) _fast; + + BigInt (long signed int x) _fast; + BigInt (long unsigned int x) _fast; + + BigInt (llong_t) _fast; + BigInt (ullong_t) _fast; + BigInt (BigInt const &) _fast; + BigInt (char const *, onedig_t = 10) _fast; + + BigInt &operator= (llong_t) _fast; + BigInt &operator= (ullong_t) _fast; + BigInt &operator= (BigInt const &) _fast; + BigInt &operator= (char const *) _fast; + + // Input conversion from text. + + char const *scan_on (char const *, onedig_t = 10) _fast; + char const *scan (char const *, onedig_t = 10) _fast; + + // Output conversion into text. + + // Return an upper bound for the number of digits the textual + // representation of this might have. + unsigned digits (onedig_t = 10) const _fast; + + // Convert into string, right adjusted in field of specified width. + // Returns pointer to start of number or NULL if field too small. + char *as_string (char *, unsigned, onedig_t = 10) const _fasta; + + // Convert to/from a binary representation. + + // Write and read in a compact byte-wise binary form. Effectively + // print in base 256 with the most significant digit first. Also + // read back from such a representation. Return success. + bool dump (unsigned char *, unsigned) _fast; + void load (unsigned char const *, unsigned) _fast; + + // Conversions to elementary types. + + bool is_long() const _fast; + bool is_ulong() const { return length <= small; } + + // disabled by DK: makes operators ambigous + //operator llong_t() const _fast; + //operator ullong_t() const _fast; + llong_t to_long() const _fast; + ullong_t to_ulong() const _fast; + +#ifndef bool + // Like int: non-zero is true. Equivalent to !is_zero(). + //operator bool() const { return length != 0; } +#endif + + // All comparisions are done with these primitives. + + int compare (llong_t) const _fast; + int compare (ullong_t) const _fast; + int compare (BigInt const &) const _fast; + + // Eliminate need for explicit casts when comparing. + + int compare (int n) const { return compare (llong_t (n)); } + int compare (unsigned n) const { return compare (ullong_t (n)); } + + // Tests. These are faster than comparing with 0 or of course %2. + + bool is_zero() const { return length == 0; } + bool is_positive() const { return positive; } + bool is_negative() const { return !positive; } + bool is_odd() const { return length != 0 && digit[0] & 1; } + bool is_even() const { return !is_odd(); } + + // Arithmetic. + + BigInt &negate() { if(!is_zero()) positive = !positive; return *this; } + BigInt operator-() const { return BigInt (*this).negate(); } + + BigInt &operator+= (llong_t) _fast; + BigInt &operator-= (llong_t) _fast; + BigInt &operator*= (llong_t) _fast; + BigInt &operator/= (llong_t) _fast; + BigInt &operator%= (llong_t) _fast; + + BigInt &operator+= (unsigned long x) { return (*this)+=(ullong_t)x; } + BigInt &operator-= (unsigned long x) { return (*this)-=(ullong_t)x; } + BigInt &operator*= (unsigned long x) { return (*this)*=(ullong_t)x; } + BigInt &operator/= (unsigned long x) { return (*this)/=(ullong_t)x; } + BigInt &operator%= (unsigned long x) { return (*this)%=(ullong_t)x; } + + BigInt &operator+= (ullong_t) _fast; + BigInt &operator-= (ullong_t) _fast; + BigInt &operator*= (ullong_t) _fast; + BigInt &operator/= (ullong_t) _fast; + BigInt &operator%= (ullong_t) _fast; + + BigInt &operator+= (BigInt const &) _fast; + BigInt &operator-= (BigInt const &) _fast; + BigInt &operator*= (BigInt const &) _fast; + BigInt &operator/= (BigInt const &) _fasta; + BigInt &operator%= (BigInt const &) _fasta; + + BigInt &operator++ () { return operator+=(1); } // preincrement + BigInt &operator-- () { return operator-=(1); } // predecrement + + static void div (BigInt const &, BigInt const &, + BigInt ", BigInt &rem) _fasta; + + // Avoid the need for explicit casts to [u]llong_t. + + // disabled by DK + //operator int() const { return int (operator llong_t()); } + //operator unsigned() const { return unsigned (operator ullong_t()); } + + BigInt &operator = (int n) { return operator = (llong_t (n)); } + BigInt &operator+= (int n) { return operator+= (llong_t (n)); } + BigInt &operator-= (int n) { return operator-= (llong_t (n)); } + BigInt &operator*= (int n) { return operator*= (llong_t (n)); } + BigInt &operator/= (int n) { return operator/= (llong_t (n)); } + BigInt &operator%= (int n) { return operator%= (llong_t (n)); } + + BigInt &operator = (unsigned n) { return operator = (ullong_t (n)); } + BigInt &operator+= (unsigned n) { return operator+= (ullong_t (n)); } + BigInt &operator-= (unsigned n) { return operator-= (ullong_t (n)); } + BigInt &operator*= (unsigned n) { return operator*= (ullong_t (n)); } + BigInt &operator/= (unsigned n) { return operator/= (ullong_t (n)); } + BigInt &operator%= (unsigned n) { return operator%= (ullong_t (n)); } + + // Binary arithmetic operators. These are entirely syntactic sugar. + // Though there's joy in repetition -- let the preprocessor enjoy. + +#define decl_binary(T) \ + BigInt operator+ (T b) const { return BigInt (*this) += b; } \ + BigInt operator- (T b) const { return BigInt (*this) -= b; } \ + BigInt operator* (T b) const { return BigInt (*this) *= b; } \ + BigInt operator/ (T b) const { return BigInt (*this) /= b; } \ + BigInt operator% (T b) const { return BigInt (*this) %= b; } + decl_binary (int); + decl_binary (unsigned); + decl_binary (llong_t); + decl_binary (ullong_t); + decl_binary (BigInt const &); +#undef decl_binary + + BigInt operator+ (unsigned long b) const { return BigInt (*this) += (ullong_t)b; } \ + BigInt operator- (unsigned long b) const { return BigInt (*this) -= (ullong_t)b; } \ + BigInt operator* (unsigned long b) const { return BigInt (*this) *= (ullong_t)b; } \ + BigInt operator/ (unsigned long b) const { return BigInt (*this) /= (ullong_t)b; } \ + BigInt operator% (unsigned long b) const { return BigInt (*this) %= (ullong_t)b; } + + // Binary comparision operators. + +#define decl_binary(T) \ + bool operator< (T b) const { return compare (b) < 0; } \ + bool operator> (T b) const { return compare (b) > 0; } \ + bool operator<= (T b) const { return compare (b) <= 0; } \ + bool operator>= (T b) const { return compare (b) >= 0; } \ + bool operator== (T b) const { return compare (b) == 0; } \ + bool operator!= (T b) const { return compare (b) != 0; } + decl_binary (int); + decl_binary (unsigned); + decl_binary (llong_t); + decl_binary (ullong_t); + decl_binary (BigInt const &); +#undef decl_binary + + bool operator< (unsigned long b) const { return compare ((ullong_t)b) < 0; } \ + bool operator> (unsigned long b) const { return compare ((ullong_t)b) > 0; } \ + bool operator<= (unsigned long b) const { return compare ((ullong_t)b) <= 0; } \ + bool operator>= (unsigned long b) const { return compare ((ullong_t)b) >= 0; } \ + bool operator== (unsigned long b) const { return compare ((ullong_t)b) == 0; } \ + bool operator!= (unsigned long b) const { return compare ((ullong_t)b) != 0; } +}; + + +// Functions on BigInt. Implementations in bigint-func.cc. + +BigInt pow (BigInt const &, unsigned) _fast; +BigInt pow (BigInt const &, BigInt const &, BigInt const &modulus) _fast; +BigInt sqrt (BigInt const &) _fast; +BigInt gcd (const BigInt &, const BigInt &) _fast; +BigInt modinv (const BigInt &, const BigInt &) _fast; + + +#endif//ndef BIGINT_HH diff --git a/src/big-int/makefile b/src/big-int/makefile new file mode 100644 index 00000000000..0000feb261c --- /dev/null +++ b/src/big-int/makefile @@ -0,0 +1,20 @@ +RM = rm -f + +include ../config.inc +include ../common + +OBJ = bigint-test$(OBJEXT) bigint-func$(OBJEXT) bigint$(OBJEXT) +PRG = test-bigint$(EXEEXT) + +all : $(PRG) + +clean : + $(RM) $(OBJ) $(PRG) + +.cc.o: + $(CXX) -c $(CXXFLAGS) -o $@ $< + +$(PRG): $(OBJ) + $(CXX) $(CXXFLAGS) -o $(PRG) $(OBJ) + +dep: diff --git a/src/big-int/nt.cmd b/src/big-int/nt.cmd new file mode 100644 index 00000000000..fa667c1aa18 --- /dev/null +++ b/src/big-int/nt.cmd @@ -0,0 +1,9 @@ +rem $Id: nt.cmd,v 1.1.1.1 2002-06-13 22:00:30 kroening Exp $ +rem +rem Compile bigint class and run test program. + +set CFLAGS=-Gr -GR- -GX- -Gi- -Gm- -G6 -W3 +set OPTIMIZE=-Ox + +cl %CFLAGS% %OPTIMIZE% -Fe"bigint-test" -TP bigint-test.cc bigint.cc bigint-func.cc +bigint-test.exe diff --git a/src/big-int/number.tst b/src/big-int/number.tst new file mode 100644 index 00000000000..073b807e2d3 --- /dev/null +++ b/src/big-int/number.tst @@ -0,0 +1,1136 @@ ++ 17009115185923538769 -12047631083067675031 +4961484102855863738 + ++ 12677011568664239747 3269056182420253574 +15946067751084493321 + ++ 9315504781982082433 13857624532376678678 +23173129314358761111 + ++ 15226508728194069537 11481952022080775416 +26708460750274844953 + ++ 7461641943684774743 12249026721402718630 +19710668665087493373 + ++ 1180469445886971055 -3208456171287181032 +-2027986725400209977 + ++ 18358552990465743315 221529797579218180385160273426219343697 +221529797579218180403518826416685087012 + ++ -14819874956616484359 30498815629431206969122152847973230849 +30498815629431206954302277891356746490 + ++ -11781881800334342169 112219460388643619332860331282276228017 +112219460388643619321078449481941885848 + ++ 3570694277032201957 284821691832196381859344006870088122712 +284821691832196381862914701147120324669 + ++ -17005463295060938595 69162171850264911722979835561124066203 +69162171850264911705974372266063127608 + ++ 15647113311796203488 150750467185419235519670165664526735459 +150750467185419235535317278976322938947 + ++ -14330150541101371097 -13054027994001826312503071338715966858478218093171762021549815587520723118772963817341751396703629529810372702877555022105594068768886421335353882155416908 +-13054027994001826312503071338715966858478218093171762021549815587520723118772963817341751396703629529810372702877555022105594068768886435665504423256788005 + ++ 7406427184711759740 -4059250217961011548005203450962458026528281798230141192186669580689721046971433745892994467792118611646113962840750314719233572760336084100766391093756252 +-4059250217961011548005203450962458026528281798230141192186669580689721046971433745892994467792118611646113962840750314719233572760336076694339206381996512 + ++ 8819522415901031498 7274905269237471130619913887005155660991437201841760414347836177003483932007334374478344594178179032728521106519295465031750530183363793325150672647162846 +7274905269237471130619913887005155660991437201841760414347836177003483932007334374478344594178179032728521106519295465031750530183363802144673088548194344 + ++ -7242932332215698200 -10558564312909325527488520195600871241245891651644550509993750377630234801225525279855157008009255586978047154906058790342845859331159009687703010657137320 +-10558564312909325527488520195600871241245891651644550509993750377630234801225525279855157008009255586978047154906058790342845859331159016930635342872835520 + ++ 9794320575955609492 13380937715397052566925484435342184213544885758759259410983243841206628594840271850190097746775475837233042430565529099681550277688470325394342993771343357 +13380937715397052566925484435342184213544885758759259410983243841206628594840271850190097746775475837233042430565529099681550277688470335188663569726952849 + ++ -18404048401680891243 6690884608978704096379677348142836785900717005050936986370615083929607190833180925295418079551348559691161519822750772440155040888224482801864925665484770 +6690884608978704096379677348142836785900717005050936986370615083929607190833180925295418079551348559691161519822750772440155040888224464397816523984593527 + ++ -10763220363947284865 -30985722824355332972176356513316569304601382411274079243859710673739383446566598659878378034375348869471278415635671865753349734809209959160389615096293457362383744562507969316522225741589739150453090393424063226271167062127000223628785686999799282795143706407082119829140399988180879618548495395684946331608899565543458192773899200054228140747414544792128323269250618482622488195333106891323515989863192944848391405358725993695671970811097285270641251816244586360288952156538400321933146150313939864593445583603568771077260174826348411367609521412133720180359748539721570562669201065857989876521301209899829037444385 +-30985722824355332972176356513316569304601382411274079243859710673739383446566598659878378034375348869471278415635671865753349734809209959160389615096293457362383744562507969316522225741589739150453090393424063226271167062127000223628785686999799282795143706407082119829140399988180879618548495395684946331608899565543458192773899200054228140747414544792128323269250618482622488195333106891323515989863192944848391405358725993695671970811097285270641251816244586360288952156538400321933146150313939864593445583603568771077260174826348411367609521412133720180359748539721570562669201065857989876521311973120192984729250 + ++ -12742462236537568498 8711131313747826394504271797986775572294949693272674156076339989631171694968899228610359983845552623710580616605402899155485071497929100432998183040757832449369366844015907530612334721882095163137705867337969942902346066961718232788529860214990099385213558935023241940238638069647809530490438245386869385682221280939688108487754251075630026707075310465788398213293782900699868609660892232563106662995330591906155134237356516622436517046191466823447743155250482328613449506396571170001248589926831956459700467126756876526930443317428628239358666456771112897986098390410773312792390699312960051747534683311506465130527 +8711131313747826394504271797986775572294949693272674156076339989631171694968899228610359983845552623710580616605402899155485071497929100432998183040757832449369366844015907530612334721882095163137705867337969942902346066961718232788529860214990099385213558935023241940238638069647809530490438245386869385682221280939688108487754251075630026707075310465788398213293782900699868609660892232563106662995330591906155134237356516622436517046191466823447743155250482328613449506396571170001248589926831956459700467126756876526930443317428628239358666456771112897986098390410773312792390699312960051747521940849269927562029 + ++ 9991390529516174614 7879872958436992955898278403297937595295396115022400543178444946646147916754852888072481665174663073269556311758611700754643170639645548596647557683044355930340624784190093631808382820554407595007761070026239341594197877214157118335743842022627898879376346092898666610367809537340994845045475091410516226225078052019727419030585524815982151736622865401299588936172760762386183577504972623377661437665668080131418564228642443266935225613702941906491478788336262289516199380144218708241406077806669686589734333554945412904560108150202389909124657090061223183441083590340175629756198442568877659538345749595968764873879 +7879872958436992955898278403297937595295396115022400543178444946646147916754852888072481665174663073269556311758611700754643170639645548596647557683044355930340624784190093631808382820554407595007761070026239341594197877214157118335743842022627898879376346092898666610367809537340994845045475091410516226225078052019727419030585524815982151736622865401299588936172760762386183577504972623377661437665668080131418564228642443266935225613702941906491478788336262289516199380144218708241406077806669686589734333554945412904560108150202389909124657090061223183441083590340175629756198442568877659538355740986498281048493 + ++ 831234034418847630 -744676478858160349467117341859049692149463503380690495147216354303526704924280287782902146026018180364963325847811379182950159627878800024734206345960410146056000392683000433501805629464626281031086102425271022388473812300724085127447081771317912465921636737545371909901577246384446144919253141375367648958387948463576516115079816552636772639965957498569187848459747361493535081532845254971492261148968198806736512864867151355002902241562014241077734122599581732704243705918200179789271894804233542502502119523149682814025979598424744685548054183678652651244898867735764030968089217841214778606507809487462642341164 +-744676478858160349467117341859049692149463503380690495147216354303526704924280287782902146026018180364963325847811379182950159627878800024734206345960410146056000392683000433501805629464626281031086102425271022388473812300724085127447081771317912465921636737545371909901577246384446144919253141375367648958387948463576516115079816552636772639965957498569187848459747361493535081532845254971492261148968198806736512864867151355002902241562014241077734122599581732704243705918200179789271894804233542502502119523149682814025979598424744685548054183678652651244898867735764030968089217841214778606506978253428223493534 + ++ -6996572501442843347 -16567158719848992553565776505785820491834685475229611199353714982570065913508303466008005931649515528390057456882757990896824841386431756898386429000065518724021230756426613661219891419166146764347562529640689229693578574350948436847247856000438153789455857903402883189892697143647998643667467614427922009931545254965075041050860609824086811877108940020349157317276288348430058535959434983921323332907180869396258655826781438419383792024592535415693101119109484610789291889841197827977530804650015884500878613240443324806805475203272442094530735476095374446946252236490708915034012846683015547314889561060687692538144 +-16567158719848992553565776505785820491834685475229611199353714982570065913508303466008005931649515528390057456882757990896824841386431756898386429000065518724021230756426613661219891419166146764347562529640689229693578574350948436847247856000438153789455857903402883189892697143647998643667467614427922009931545254965075041050860609824086811877108940020349157317276288348430058535959434983921323332907180869396258655826781438419383792024592535415693101119109484610789291889841197827977530804650015884500878613240443324806805475203272442094530735476095374446946252236490708915034012846683015547314896557633189135381491 + ++ -8920936222630165483 -18738991973681679876688842391791783563249057933653045519186959571392922172943405646958686202208790537612746921398028331540617848217445632123805070077600768524509025758950743971128222843292926773668584735575066246660802064630842300367821042873152766467703905048558085377302000898639290554395913805527529259855535801856020623830262396582180677933562523957295341539162448074423901242873918231922121053192425691524797238343327318801359521456598967984637483081312932069399045363737622797213185099130529375169698811801965974416555301085043300426947769193582129151016159057101028336667142913854943018973494705119572045938607 +-18738991973681679876688842391791783563249057933653045519186959571392922172943405646958686202208790537612746921398028331540617848217445632123805070077600768524509025758950743971128222843292926773668584735575066246660802064630842300367821042873152766467703905048558085377302000898639290554395913805527529259855535801856020623830262396582180677933562523957295341539162448074423901242873918231922121053192425691524797238343327318801359521456598967984637483081312932069399045363737622797213185099130529375169698811801965974416555301085043300426947769193582129151016159057101028336667142913854943018973503626055794676104090 + ++ -243510292488206214847646757340020705642 5940577100149745132 +-243510292488206214841706180239870960510 + ++ 35446324064743728955945058978206455057 -6248622708755929572 +35446324064743728949696436269450525485 + ++ -285342226760657637664173494795024413673 -11942737781617905307 +-285342226760657637676116232576642318980 + ++ 180790435817422032042321866247362452865 12401641959336396832 +180790435817422032054723508206698849697 + ++ -179994871947239535956826388240542999950 13573822506399140772 +-179994871947239535943252565734143859178 + ++ -308198027295905163635866438671452347268 -8790069282378476990 +-308198027295905163644656507953830824258 + ++ -139324757925833055762410227358605285566 -190622873846936719063564661032771271922 +-329947631772769774825974888391376557488 + ++ 332866352618304570046318203427223999347 147978646177673305481282943528696833018 +480844998795977875527601146955920832365 + ++ -39471620476300923970352914034802271156 28992893610776120142668950821916856486 +-10478726865524803827683963212885414670 + ++ 274120253734611965146455315763505869288 254675910805265090692978775702306142625 +528796164539877055839434091465812011913 + ++ -122086811464559635596206661886176775901 287312583034687582188356355813963609701 +165225771570127946592149693927786833800 + ++ 288576174771266329955482943556556984728 -57843540651903655425270706396868707777 +230732634119362674530212237159688276951 + ++ -47977736580820486006305788441965482221 984809271313988066640898939725532304075331399066274624928410251834520283291912387208948664716457549646483445981126881113426109906085249657168046936670489 +984809271313988066640898939725532304075331399066274624928410251834520283291912387208948664716457549646483445981126833135689529085599243351379604971188268 + ++ 21225484205143479814642328762121362291 11839789093732539327981861490012713257538550745921177905266671749716203131127256902110452504526721633943016923389974867770082516862899595554460170417713940 +11839789093732539327981861490012713257538550745921177905266671749716203131127256902110452504526721633943016923389974888995566722006379410196788932539076231 + ++ -193095363331703875886398909106293703000 4389392021031719669078675478621418677903292147307684123866099084349756491860737402449105804868232530632178577388168068485304437343508442251302846768269976 +4389392021031719669078675478621418677903292147307684123866099084349756491860737402449105804868232530632178577388167875389941105639632555852393740474566976 + ++ -14827657635864183514988182371035598180 -7256545787852407071411458891023580461638051949278710509801472046178301830006724297747051044450550248499056073213660185258676369175307019300952192657194576 +-7256545787852407071411458891023580461638051949278710509801472046178301830006724297747051044450550248499056073213660200086334005039490534289134563692792756 + ++ 54301423175725658626298504084995819705 -13385853291610595576947504757201441006088030688464261540642594993520424631577281077984278942244446266776534612440941312995898184903431893212829646845766101 +-13385853291610595576947504757201441006088030688464261540642594993520424631577281077984278942244446266776534612440941258694475009177773266914325561849946396 + ++ 195114404067053480147948948510253723990 -8373866462448797623435948949281383906369538962237624940506813188612614128993186653340202956656303504523161255703176374041758276069255591562198514767063594 +-8373866462448797623435948949281383906369538962237624940506813188612614128993186653340202956656303504523161255703176178927354209015775443613250004513339604 + ++ -308030589512186791277525017840002670741 -11922204352024596469278978325035646517433105521287613403902396944414655739824695945028308092245747333098422116078042326104667969967224788442970266049942774583538734406057081597034454910987815490244451193242377705191422489528853976486607580169986057592557285271953385769215318545520155212402919465580052078255078759756709086185424029620805084776442744700501748376290562843380642608395240491162047933014854466267084965223593172702334466729933986413870670083326499598274393380692146118979961818816348097032083332695128587696590646086980241100792624502607816103195636761141133903550454815591457829485684936036414823492160 +-11922204352024596469278978325035646517433105521287613403902396944414655739824695945028308092245747333098422116078042326104667969967224788442970266049942774583538734406057081597034454910987815490244451193242377705191422489528853976486607580169986057592557285271953385769215318545520155212402919465580052078255078759756709086185424029620805084776442744700501748376290562843380642608395240491162047933014854466267084965223593172702334466729933986413870670083326499598274393380692146118979961818816348097032083332695128587696590646086980241100792624502607816103195636761141133903550762846180970016276962461054254826162901 + ++ -172649878347923210775992373331623646864 22180935775581457002090790736532281654456312526625354262953960635330604551829750571440878712430708012807252279301365732385899228826740712544768476577874129759972563823209525283326887563301081200476495752033290851190327066070873711444930389093339915885090143783170994309089448293499799071372787520776773788274677288230540162485916160484352398851925328125588729604931589867889917097887951581817207079060016091919559509735997493084833476849835444339835031436580214492450731100723026312163752403946315983551266206214298679421644737804098691991631489261658890937663698502561036246447760919715595005106669653475931803053499 +22180935775581457002090790736532281654456312526625354262953960635330604551829750571440878712430708012807252279301365732385899228826740712544768476577874129759972563823209525283326887563301081200476495752033290851190327066070873711444930389093339915885090143783170994309089448293499799071372787520776773788274677288230540162485916160484352398851925328125588729604931589867889917097887951581817207079060016091919559509735997493084833476849835444339835031436580214492450731100723026312163752403946315983551266206214298679421644737804098691991631489261658890937663698502561036246447588269837247081895893661102600179406635 + ++ 17539006966816771902104329685391462527 15609797782337099611892065465036826453911053690739041627254619195700021040383385710184052653282070244915503750549545390475671883312314708978681904377133928647935359080875691628246716591529028104762422990155477702994042953196747769893182153631482194578269859879402160062955490194674372351117284129320011166238130774752386987036267064693133554447596069886693581191241594745541512444806003236372840085705813835001957163976961730871756250344335996073970142337882238844723800849054637237549515249957267772181010402413375667537558243971058326641257721901094391380667244006959028327507917720426571969997513984360849930719808 +15609797782337099611892065465036826453911053690739041627254619195700021040383385710184052653282070244915503750549545390475671883312314708978681904377133928647935359080875691628246716591529028104762422990155477702994042953196747769893182153631482194578269859879402160062955490194674372351117284129320011166238130774752386987036267064693133554447596069886693581191241594745541512444806003236372840085705813835001957163976961730871756250344335996073970142337882238844723800849054637237549515249957267772181010402413375667537558243971058326641257721901094391380667244006959028327507935259433538786769416088690535322182335 + ++ 244901855797156286376563377540855746602 -22138106346578776369849317622304392466030036563754663379976505966920461958652141160336156065177498990718609170201272980114106671808245437660234479124938853665375934080221740523696180221118540569603989748587853373569525751680828044059607889572522502629277877343410298879764820905044284757389006201848194571453112545228115550224254565141563427486518108434758694923122284117299374156393942906293546318323661938734959824887786185558612820887463537294120950912969343488704744978847504513710882720654330147775174336365363311173472002077960424794151168301281665765411704505095008907760396535767621855642720080219960822554492 +-22138106346578776369849317622304392466030036563754663379976505966920461958652141160336156065177498990718609170201272980114106671808245437660234479124938853665375934080221740523696180221118540569603989748587853373569525751680828044059607889572522502629277877343410298879764820905044284757389006201848194571453112545228115550224254565141563427486518108434758694923122284117299374156393942906293546318323661938734959824887786185558612820887463537294120950912969343488704744978847504513710882720654330147775174336365363311173472002077960424794151168301281665765411704505095008907760151633911824699356343516842419966807890 + ++ -119403662992279138748600939857239307122 26272999248235953724172008428088697264933069743507017434844709711501131900922919455931092196539942532993887162365511473221418376205773427597933886270411672062672089518774390132453916538404354895529975888201032175628249480896964400801763570333497287321002961557096975786141940970260074557095118887294558700145949117395512768347250531196100831164663613049206690894640391431616112104502483838173255614981302462548882276825096564828583591963617871547373532874400764134244496979962241959713525053686209002866840900623246072884125102845824992994967009109046451949348656842486048332953732384499190437432898387573320391878853 +26272999248235953724172008428088697264933069743507017434844709711501131900922919455931092196539942532993887162365511473221418376205773427597933886270411672062672089518774390132453916538404354895529975888201032175628249480896964400801763570333497287321002961557096975786141940970260074557095118887294558700145949117395512768347250531196100831164663613049206690894640391431616112104502483838173255614981302462548882276825096564828583591963617871547373532874400764134244496979962241959713525053686209002866840900623246072884125102845824992994967009109046451949348656842486048332953612980836198158294149786633463152571731 + ++ 313963939617834410089002930298454269912 23286645405607099799151331553995799851855144387826191186590140820016670502830395945076644578998873585162998873396623634135231418574284200209367505115739462344028303923666952261030907434438322884189133236837089851688275865098623902644385995630973049587854251981548128145516004461191094062488421288607625783540996659060285661398859383778209495884203323937672739376151794507745282074538961033778823733980759695886879886017489555795079194346438911010371103435094677167286870898482214310646392174423422237727456012197253183422715313378603607058548706460095379882633958651034759773864354021315490712575535559549015858088608 +23286645405607099799151331553995799851855144387826191186590140820016670502830395945076644578998873585162998873396623634135231418574284200209367505115739462344028303923666952261030907434438322884189133236837089851688275865098623902644385995630973049587854251981548128145516004461191094062488421288607625783540996659060285661398859383778209495884203323937672739376151794507745282074538961033778823733980759695886879886017489555795079194346438911010371103435094677167286870898482214310646392174423422237727456012197253183422715313378603607058548706460095379882633958651034759773864667985255108546985624562479314312358520 + ++ 2000877973959266893810594143560134441447453310844726478119781029700338468704683515329516333146806175216349912753585564808803731447160643580198590073658869 -17993015014355471903 +2000877973959266893810594143560134441447453310844726478119781029700338468704683515329516333146806175216349912753585564808803731447160625587183575718186966 + ++ 5492930533666246223206322654398877802091439062008700770880939594548305919677404080859141226095489505872709347538974725998600861651942609010590873980143878 15372278140141207703 +5492930533666246223206322654398877802091439062008700770880939594548305919677404080859141226095489505872709347538974725998600861651942624382869014121351581 + ++ -13405500833215428652808705089190188280715732437731292502890523313631564795139560159124390691283401484515088713758307366404145018349044148223082253439210893 -14793401891248640808 +-13405500833215428652808705089190188280715732437731292502890523313631564795139560159124390691283401484515088713758307366404145018349044163016484144687851701 + ++ 9945195259699924701593703207751086973468898794114625092150620088406276196469184233537941913755508476427888065765634203723512911676149274871082481174186606 8699133332160461067 +9945195259699924701593703207751086973468898794114625092150620088406276196469184233537941913755508476427888065765634203723512911676149283570215813334647673 + ++ -1785165974800693006461065312083337532938610906605533088558498259067461510781028452552786542598361030690629530721209490413999022804146471920873844686294838 -13079925952361275418 +-1785165974800693006461065312083337532938610906605533088558498259067461510781028452552786542598361030690629530721209490413999022804146485000799797047570256 + ++ -4861207515430071951958387366611380234482792653010151054346367776006873932152600469133110239669746470475230906073865131648496652783311445471793936775767736 -9381557743227419896 +-4861207515430071951958387366611380234482792653010151054346367776006873932152600469133110239669746470475230906073865131648496652783311454853351680003187632 + ++ -6638723469626495957966112633999375479181736600737250559572415894485618850919815869703127084789143821420728194272094956858541960962483734293877093635361160 277811698220276334443479876776376776138 +-6638723469626495957966112633999375479181736600737250559572415894485618850919815869703127084789143821420728194272094679046843740686149290814000317258585022 + ++ 1983880417172931934469534542170437296262471214582817006917470485544552211448284732460451903536334682269123998240709059499894818265755197559390728940140016 -118940994129137705779355371753506018694 +1983880417172931934469534542170437296262471214582817006917470485544552211448284732460451903536334682269123998240708940558900689128049418204018975434121322 + ++ -9354509264984586574958285335910611806441061705184818350015454221731287473282231343722010109181841005578131927454778025302197744540571159656556971614966757 120224841184491944160266976391113485817 +-9354509264984586574958285335910611806441061705184818350015454221731287473282231343722010109181841005578131927454777905077356560048626999389580580501480940 + ++ 4389359421234641412950681847970318834150108533025088077429496538447029921663033978550089607257809597829358374972237448178553189381274150213236222139873594 106674783386899772113212633712093787897 +4389359421234641412950681847970318834150108533025088077429496538447029921663033978550089607257809597829358374972237554853336576281046263425869934233661491 + ++ -9319417879153488839579936799737117639058244394679644240663244688680826325564084529474537634510092069422987165268448907193562300482925125162731530249763801 192969103435503875767216559494769734726 +-9319417879153488839579936799737117639058244394679644240663244688680826325564084529474537634510092069422987165268448714224458864979049357946172035480029075 + ++ 1394404616168163951844558734723678125985464491792846741433683801962971891047718103736551854371207400145441134823994228143957746922511631911996296931168332 -211230038021470285136061932161632203274 +1394404616168163951844558734723678125985464491792846741433683801962971891047718103736551854371207400145441134823994016913919725452226495850064135298965058 + ++ -2935941510094051560788359387128767361559188973149773593522440619832472030019457317998381634585179453958737810428870232715146002408187749944694186205812791 -1221176156661231926164756142840452419679061324806989304452215660535991083923207702827717652226257158321829748247784282139952864899457896871473184473608543 +-4157117666755283486953115529969219781238250297956762897974656280368463113942665020826099286811436612280567558676654514855098867307645646816167370679421334 + ++ -1338674579024795395027232680327531457830908239605718353094975139226848400289367913459076082700361212506196070727982446232782659114647371030398516119682505 -1298372177520411182435886041880377054374169787570856408996533471838082317927648953576721017727347029007573543972764860712708420553928791798580799809858729 +-2637046756545206577463118722207908512205078027176574762091508611064930718217016867035797100427708241513769614700747306945491079668576162828979315929541234 + ++ -2072456075229532951804023218627137969798924912365258263779029006567941400203608770518731715660383378937120213112973528605594220795605977413985543331908189 -9744489461776287963808523409593616918248399004543154581056479712028497082820841423941781438667661074968238703192056877665754560746003512076830245760254982 +-11816945537005820915612546628220754888047323916908412844835508718596438483024450194460513154328044453905358916305030406271348781541609489490815789092163171 + ++ -2570682164188734368809161664810917340861573482754788446510182252413437925852206735928397938304353826925422441004271229738766803460790995673395984247950088 656920705293329551826685120408221577679101260931105312141757138825917579070505267306626244216341686712802796891966598838285570807961966448181138356047523 +-1913761458895404816982476544402695763182472221823683134368425113587520346781701468621771694088012140212619644112304630900481232652829029225214845891902565 + ++ 7846359203342053693101523606887617345982401999003795257520576318451663998927274759872692123323796450295314377046602880394071105863527900699633560551732837 3683380639347829102597675045842249667669675715600522157867595962635108482512780509393310714588544837398923613138772339053021025559943198965234376657126821 +11529739842689882795699198652729867013652077714604317415388172281086772481440055269266002837912341287694237990185375219447092131423471099664867937208859658 + ++ -11692323148567132684205145901751681947225824260005631214936266006610207543813382900867093989444659986091234552140689684476541703112098935301322850961583953 -8534276689564199122569555420819240948691777228327984555753862457592427992599992931175844172478864477440165366128106812103785256271256853749622592560655914 +-20226599838131331806774701322570922895917601488333615770690128464202635536413375832042938161923524463531399918268796496580326959383355789050945443522239867 + ++ -10734754884168724884333968138739681643742524619139397687680049322697740991391014196697040576174049452737571835233123127815762146577096625434481167057340772 17059878151450238567815178684522345445687980385106446646013863901583786249398194029757376950491550197185231926262467028755342392379269039238766592672298850588065335172902157386017520689203005559576263548017475991638498600879259882041932152385436968424098224966518534467302264172016376096778201462205990822825056602379115848799619564610033123837036507127427054121975400703490855123544706355545059512146550901507159940126280812512339749605195422987937677650572797378799103456094203126081464905326203083057134061673694975250599375795827437561275156235513192978645909947341297774926450637694325145427434486258223666250272 +17059878151450238567815178684522345445687980385106446646013863901583786249398194029757376950491550197185231926262467028755342392379269039238766592672298850588065335172902157386017520689203005559576263548017475991638498600879259882041932152385436968424098224966518534467302264172016376096778201462205990822825056602379115848799619564610033123837036507127427054121975400703490855123544706355545059512146550901507159940126280812512339749605195422987937677650572797368064348571925478241747496766586521439314609442534297287570550053098086446170260959538472616804596457209769462541803322821932178568330809051777056608909500 + ++ 1982582032974021971225071139786536402936929744496433027195224299475980201425925452469321205602618940472354066218156609448199804973454183972974358405933935 -5591374624026484498020036332218412149978824230210339582240360391202660977358546150723165491729699122647688030937226316069237264083850854032732663284717882873051337566653841254365703461654061656817936193716386141166210237666314879751427421825450110467888973152907618520704486700443275358649289847595635931220181024199692771066498714511145489237541761266539978351840438236927937894376002981658065431416811632941197501676956304254109064936038146674412392128883565757325842468006824235119684861972224857533964558963441079998949499582965764591461900562931342373507763081479989957632695010603500633322408246084430203281475 +-5591374624026484498020036332218412149978824230210339582240360391202660977358546150723165491729699122647688030937226316069237264083850854032732663284717882873051337566653841254365703461654061656817936193716386141166210237666314879751427421825450110467888973152907618520704486700443275358649289847595635931220181024199692771066498714511145489237541761266539978351840438236927937894376002981658065431416811632941197501676956304254109064936038146674412392128883565755343260435032802263894613722185688454597034814467008052803725200106985563165536448093610136770888822609125923739476085562403695659868224273110071797347540 + ++ 11532228364136654310006206557545352284448588590560137249197311142901246089838098630841794341370689745410654263817911440601934362503092628725755210859171724 -25776236925500995542036591604259749301547568770017466769502569415611770276300787105037848049555500555975152877716727294374436703766730618054071617947449695177320842403963009384468257891933593584757723535299746543328292715942626303315235241470269740287031317322772461137186093930239744879822272349431389779234805703118929710210161489122272898252221025966631463842234537744822906696719691188223105175714602909117904182229960075276443648211003011686250829474364425483901920822837775032295913486152631638908227467242772081310515646217115760180349854601959031626524004201825198439309850266508687796415478396821644422350208 +-25776236925500995542036591604259749301547568770017466769502569415611770276300787105037848049555500555975152877716727294374436703766730618054071617947449695177320842403963009384468257891933593584757723535299746543328292715942626303315235241470269740287031317322772461137186093930239744879822272349431389779234805703118929710210161489122272898252221025966631463842234537744822906696719691188223105175714602909117904182229960075276443648211003011686250829474364425472369692458701120722289706928607279354459638876682634832113204503315869670342251223760164690255834258791170934621398409664574325293322849671066433563178484 + ++ -2603756427337798371354526130541868239006085657393372011847827118826669474695402075575479286172808099892726251004549675772420422527946534088483901153485670 -10844269742362409682236511127219508926736627172993604953084481596070757241623728297275447608738915355190715664012379562650777199088096670239050254578284071100042116609747208178716191571268815994455064584659920497876052406993834873124981417288518101426395560764186717660091472734401090302285129741058888303693710456902635092811413971399734306158050053239768185860958896447298052082493590498954512083131068867270078638929796561440903919430094619437872896595720463663570751134804664228918188923926951933302878771189484614604311920655871182974081898031051411394311700207305532216445616083858025977851570522763537300875989 +-10844269742362409682236511127219508926736627172993604953084481596070757241623728297275447608738915355190715664012379562650777199088096670239050254578284071100042116609747208178716191571268815994455064584659920497876052406993834873124981417288518101426395560764186717660091472734401090302285129741058888303693710456902635092811413971399734306158050053239768185860958896447298052082493590498954512083131068867270078638929796561440903919430094619437872896595720463666174507562142462600272715054468820172308964428582856626452139039482540657669483973606530697567119800100031783220995291856278448505798104611247438454361659 + ++ -5929887196386997518766568868806997104240129372360669348628384183712406620199102166145939206783172815805659513128544493795329100599632286529420772709366102 24544958491142793859949310604465694574872439331169358241746200808802938771527900616394258199996170862256988647191747967628756772368808644819831481350919782560499270148419601775750932556119448001824346026042068416905254113155445053931789404515589532235225580737103411251232560863878948880220469490014568323308965914171394449781093816607870593225534700167342589927524232815571862258490314644577819742372918446373756857848586825568514909823940075182825283229026250682015641747568282510036326125505522447591703308661608718100933027549520132308555240654655887041040427813131621391320267698106519650611462269033902177180035 +24544958491142793859949310604465694574872439331169358241746200808802938771527900616394258199996170862256988647191747967628756772368808644819831481350919782560499270148419601775750932556119448001824346026042068416905254113155445053931789404515589532235225580737103411251232560863878948880220469490014568323308965914171394449781093816607870593225534700167342589927524232815571862258490314644577819742372918446373756857848586825568514909823940075182825283229026250676085754551181284991269757256698525343351573936300939369472548843837113512109453074508716680257867612007472108262775773902777419050979175739613129467813933 + ++ -8848084327536592532063677611386811805244460767433749071435930786126721080365289638381557872263825830664387392539638767251180242665642373539064690745095464 -15917950175678012281826361248776190984758236997789474333609547749168308439513527143790323694526378056113636462939674273462177686456811495629631337058042159570336251822399402513133598701991665209363955263097315081570618652783181494594400709239428597117944511110842795526862595552977665064029517628515465251448116061875878430407784298951946811321795808932206846491091803276390661869369638950672478828532423383951689632136029256108992610781912267083149156104328033893238864631158195280554850035949666897861529711006187241710164902350100555999894332438423857208747342184052953230247487231455921360593096823760117493579248 +-15917950175678012281826361248776190984758236997789474333609547749168308439513527143790323694526378056113636462939674273462177686456811495629631337058042159570336251822399402513133598701991665209363955263097315081570618652783181494594400709239428597117944511110842795526862595552977665064029517628515465251448116061875878430407784298951946811321795808932206846491091803276390661869369638950672478828532423383951689632136029256108992610781912267083149156104328033902086948958694787812618527647336478703105990478439936313146095688476821636365183970819981729472573172848440345769886254482636164026235470362824808238674712 + ++ -16314775600714318471451792035636584056297958597339492996728118376578145765736873313518831390349547274517050864260054903974054712997529177834428786007341762649083404743713562157667828894017440065599882523458121037421757904691003094608420565550031561905074671735751685371533975894842331113347413787808917193134135744321547478500861021485075363990553639161661734684228250909589741380076008551020384304303171431833670236949934603973673998262066558668396388979463892768199916011368116729432353268535563246463324517035331079693172060671712718486388759443825620676228470068291448236914050793177812037679396721657020438979754 12553426083939460917 +-16314775600714318471451792035636584056297958597339492996728118376578145765736873313518831390349547274517050864260054903974054712997529177834428786007341762649083404743713562157667828894017440065599882523458121037421757904691003094608420565550031561905074671735751685371533975894842331113347413787808917193134135744321547478500861021485075363990553639161661734684228250909589741380076008551020384304303171431833670236949934603973673998262066558668396388979463892768199916011368116729432353268535563246463324517035331079693172060671712718486388759443825620676228470068291448236914050793177812037679384168230936499518837 + ++ 20637030084881771176788188367974505419050866216433677435050410899110162793040751338330447574748263391136356400036001988938659722098883893353523409458775455519257672423829361150611806294256710309281788819450225670112435352092313483086404714074567539245791066202051788986426960935796927738180831688497683293306590464598379493141645539253898709000874685535467854788184424886911457134522632486730390913239660179785071885982403741669161655812015114272497907946919026898579927936299607156006210124954460880383605958519412435713868501997649784658832599101777001703519408664662715322044086646014163774269660274683400619225321 11620128128044940816 +20637030084881771176788188367974505419050866216433677435050410899110162793040751338330447574748263391136356400036001988938659722098883893353523409458775455519257672423829361150611806294256710309281788819450225670112435352092313483086404714074567539245791066202051788986426960935796927738180831688497683293306590464598379493141645539253898709000874685535467854788184424886911457134522632486730390913239660179785071885982403741669161655812015114272497907946919026898579927936299607156006210124954460880383605958519412435713868501997649784658832599101777001703519408664662715322044086646014163774269671894811528664166137 + ++ -9838804688358141062268493389453191808060717708062736103828856866310283812230958467655270667206937622979717683919584610288962829724022506216738929136418489468786902364550847498615864720240589837282441807174290461916292258263929411081218952357662703079709351365960916688275651864441386750529258343003652300629003597744958152243494244227986280506395347894285277364095898602965258114321853474000520432831298793365139040664543928707100657375292032051256485942532600998813627925626928634068613637417702688610315924917761411247617905738119218110678854564441914784262998574445847209847985439514580300936248281049628734475702 2380166482232871816 +-9838804688358141062268493389453191808060717708062736103828856866310283812230958467655270667206937622979717683919584610288962829724022506216738929136418489468786902364550847498615864720240589837282441807174290461916292258263929411081218952357662703079709351365960916688275651864441386750529258343003652300629003597744958152243494244227986280506395347894285277364095898602965258114321853474000520432831298793365139040664543928707100657375292032051256485942532600998813627925626928634068613637417702688610315924917761411247617905738119218110678854564441914784262998574445847209847985439514580300936245900883146501603886 + ++ -30961575335426221869515496362216292453766907587859856766456625722888557357647164641922707199324601608700561081422636642523431947551124957385652791834855425829101761914145137205962610515642614866296480715893528289170482422505734612327038754622917335073993027434927547277037587173529054849390646376806910407207016292483185533697336599641898250465186168797820802225861771331652801064811222606773495565340386327294310913503461903243119204619412324538886439122443769008953829820425376589389335553937319588224864611583436327810214798652896733118881040503785110481197462772022447173744898802421806800203373153221004361953729 -10586442965055062759 +-30961575335426221869515496362216292453766907587859856766456625722888557357647164641922707199324601608700561081422636642523431947551124957385652791834855425829101761914145137205962610515642614866296480715893528289170482422505734612327038754622917335073993027434927547277037587173529054849390646376806910407207016292483185533697336599641898250465186168797820802225861771331652801064811222606773495565340386327294310913503461903243119204619412324538886439122443769008953829820425376589389335553937319588224864611583436327810214798652896733118881040503785110481197462772022447173744898802421806800203383739663969417016488 + ++ 8835746018617511846981408800319983340292665114153404569022025834059427359831684523399830234196625160662387716033871154398104436720494608541518837969397374272734698261557358249258503982414578618525420572597611597792132117034895074841909295420434392963714805547538976612884853497014341345150095544449860198192757839489063747595073430612069212219930749783824683135433987509303139260133564905961552149844964215891730262218278214035649706577154652729844092199333026620127958228847111442161350881527928460177763370427262298116900358910460957772350452949782281117704005514462730290063772968929608448642592954601418753021512 -12227722924075527556 +8835746018617511846981408800319983340292665114153404569022025834059427359831684523399830234196625160662387716033871154398104436720494608541518837969397374272734698261557358249258503982414578618525420572597611597792132117034895074841909295420434392963714805547538976612884853497014341345150095544449860198192757839489063747595073430612069212219930749783824683135433987509303139260133564905961552149844964215891730262218278214035649706577154652729844092199333026620127958228847111442161350881527928460177763370427262298116900358910460957772350452949782281117704005514462730290063772968929608448642580726878494677493956 + ++ -5455184800550144006991157215735481579353213544152145628297990102571936052187486515129266239245491863623978659179559754999567936067584384479787934704340911556625153536160778495579370425428019248950494107696016864499055854257192071541354806671987402367524770228296322497224645429524493838356022616251290117624472061673033274133156467148770562815676767117605001434288573911556053311048284534341905722947046607192815465807736361991479044698448267471087552952494477144251510778491315012457514838113324210534577956298926109164909779987221094000880908857594198276812276890284008572664102792405452379662935026125770444036994 -7349798942312432150 +-5455184800550144006991157215735481579353213544152145628297990102571936052187486515129266239245491863623978659179559754999567936067584384479787934704340911556625153536160778495579370425428019248950494107696016864499055854257192071541354806671987402367524770228296322497224645429524493838356022616251290117624472061673033274133156467148770562815676767117605001434288573911556053311048284534341905722947046607192815465807736361991479044698448267471087552952494477144251510778491315012457514838113324210534577956298926109164909779987221094000880908857594198276812276890284008572664102792405452379662942375924712756469144 + ++ 27233955893140063612427006607965940109569052437681267421929959186535416115028420267622879017163568256526042146282241931623674996867133390355390677118211537487769195270234259640386625552763891339073878417517169618832945750393661600092643257470064376916337734385887099957095417541169462231630821139075814859604097878094729685589777579267192538715202397220666651307185763054526407234767132218634060693076054116575833737797189157152326979078121760900891899319809724675232853322526718686306470372869701173824664984405178677187081936624687293494821338781534163633206006387449585716391843039459733925494003066841874935048611 -66646390577667468207341453008390168215 +27233955893140063612427006607965940109569052437681267421929959186535416115028420267622879017163568256526042146282241931623674996867133390355390677118211537487769195270234259640386625552763891339073878417517169618832945750393661600092643257470064376916337734385887099957095417541169462231630821139075814859604097878094729685589777579267192538715202397220666651307185763054526407234767132218634060693076054116575833737797189157152326979078121760900891899319809724675232853322526718686306470372869701173824664984405178677187081936624687293494821338781534163633206006387449585716391776393069156258025795725388866544880396 + ++ 15030400024888781078933103028897733817304421960545019199443871381537070197157227994520524631721701055962609956080413517776229513420814407790533237358129529547793422514837651333555776540939235592155512951229106778709351772195248438493792786143040421233061520515971787881798980515709417481015662862327435825812557205663033601853937647320838585333754027488605638576977560072206293290493215523194883494322543800546276353830683084405428005815296131527861252717516620765986589669237487765523936713749717927502645633123584240464131140829496052170285171610845098023517906586134613874506419828208611247177336492131262918439281 -164048419232636429449474429717211197442 +15030400024888781078933103028897733817304421960545019199443871381537070197157227994520524631721701055962609956080413517776229513420814407790533237358129529547793422514837651333555776540939235592155512951229106778709351772195248438493792786143040421233061520515971787881798980515709417481015662862327435825812557205663033601853937647320838585333754027488605638576977560072206293290493215523194883494322543800546276353830683084405428005815296131527861252717516620765986589669237487765523936713749717927502645633123584240464131140829496052170285171610845098023517906586134613874506255779789378610747887017701545707241839 + ++ -10227062646189307616073129048534031298512434237226774743330733206156788005874968173984804649812506029813402205606562016228122184161577517837608957023376079537037472977098465137152327215807765130656192272994478964341604278041664840636982572214751638093860605132350960802560601354006634296348422600320863531059118477125143903734159707623839282511184908969206873548650544269932394344952983661665472663102992782521888857016369837211403335306200813816060883478434441858442549261115972947741929087886423170398410216855322384956160289855500229952405068604320121652911887067414460828300146993858360430784079225137421074839819 117460076430162201914796277915447781936 +-10227062646189307616073129048534031298512434237226774743330733206156788005874968173984804649812506029813402205606562016228122184161577517837608957023376079537037472977098465137152327215807765130656192272994478964341604278041664840636982572214751638093860605132350960802560601354006634296348422600320863531059118477125143903734159707623839282511184908969206873548650544269932394344952983661665472663102992782521888857016369837211403335306200813816060883478434441858442549261115972947741929087886423170398410216855322384956160289855500229952405068604320121652911887067414460828300029533781930268582164428859505627057883 + ++ 27989453264793973121573869640708223239762902243991948581280654553806618470632044367386680716040316895884976837122054709584963028986161694425215067648887944710852278135008221491665079705797192389681328802747226171436158375378499411314855257919224316919346771317457123252623293612958336691335423245293660257386649100685560072354549579281852792682734916555498283053758141666658137856828164206947320523255487437004565021167276952652515632644458005291855624829941937578229983628962137595011570216766689546500517528191189928660433013004254032861383790553611840534023221000900694995707453499030166286828319347894538505334235 -59175168207571178843658955348404514921 +27989453264793973121573869640708223239762902243991948581280654553806618470632044367386680716040316895884976837122054709584963028986161694425215067648887944710852278135008221491665079705797192389681328802747226171436158375378499411314855257919224316919346771317457123252623293612958336691335423245293660257386649100685560072354549579281852792682734916555498283053758141666658137856828164206947320523255487437004565021167276952652515632644458005291855624829941937578229983628962137595011570216766689546500517528191189928660433013004254032861383790553611840534023221000900694995707394323861958715649475688939190100819314 + ++ 1178650930337394440162727078866515771626896502845852711186000991913866844090831426017480263676964607121490209778220339316756171449922437605552456088105443130477974682689512446683178356259305893852096425478878588001446154476458310269704392486398646169362313605456233489086567865316333034897433650974160168545492823208575634152241341906068149887959566983066154182855136114289266802474404127414747112706158621650063987662749553991791509795764642256261917497984177610694405881831052199417235241109412927893781778469398975117797578753730248539151297798807326284978255001046995523851829184120171969918537718488250577987049 -151873924489040812813761508259707631973 +1178650930337394440162727078866515771626896502845852711186000991913866844090831426017480263676964607121490209778220339316756171449922437605552456088105443130477974682689512446683178356259305893852096425478878588001446154476458310269704392486398646169362313605456233489086567865316333034897433650974160168545492823208575634152241341906068149887959566983066154182855136114289266802474404127414747112706158621650063987662749553991791509795764642256261917497984177610694405881831052199417235241109412927893781778469398975117797578753730248539151297798807326284978255001046995523851677310195682929105723956979990870355076 + ++ 28233332719950979786871881804755080223325040620170668729385709165879717973040387558150293205758215739710262749733170837042434162049732587908182282319848154049410849721309988807368466228286699721201975848741931128639324322061892706638973259354962358866000024260698793885547287093369940035337370984725857550291339492871017395328145015077506882578124550084937438336881072124376107623716831044079223921566902242543198986921476998895559488862309653154914291349588095330683589871173449191854284433182368052817373384461363574550061788800329400860372148193491004593903732351395815409821222597665222975816418433744748143385431 -43245950360315656184924888243641533635 +28233332719950979786871881804755080223325040620170668729385709165879717973040387558150293205758215739710262749733170837042434162049732587908182282319848154049410849721309988807368466228286699721201975848741931128639324322061892706638973259354962358866000024260698793885547287093369940035337370984725857550291339492871017395328145015077506882578124550084937438336881072124376107623716831044079223921566902242543198986921476998895559488862309653154914291349588095330683589871173449191854284433182368052817373384461363574550061788800329400860372148193491004593903732351395815409821179351714862660160233508856504501851796 + ++ 17311283930487575047109155431670372891723312431004343097275158353815289445461275098157423001160013464866170709729134076291306322952612660169010483426086431377525432637844274608988581691477819008626983761905899834444008235608280930166913911248710072733217113558125600345343437000427963292980921009445490627620344145866648036116660335905940809860199697939729919140888034303887423527841395304960072549430314367914315102150378504502158659627719016733307736583749830415574905929299482373462584995162798576853564481617711234957058703455021082855018642616999836886763535412642684228990890160568207941504887072856663966242787 1954009743321912552050341299974626734964446274711484506734354360114801426013796892421541915293157994203607853436799102383078659985249097057923578528366737 +17311283930487575047109155431670372891723312431004343097275158353815289445461275098157423001160013464866170709729134076291306322952612660169010483426086431377525432637844274608988581691477819008626983761905899834444008235608280930166913911248710072733217113558125600345343437000427963292980921009445490627620344145866648036116660335905940809860199697939729919140888034303887423527841395304960072549430314367914315102150378504502158659627719016733307736583749830417528915672621394925512926295137425311818010756329195741691413063569822508868815535038541752179921529616250537665789992543646867926753984130780242494609524 + ++ 1135960177108146621604027872788612991247811085764456406834564014092038611848908717507207251239454266163702244932570537009884467598603226302482406831131219148530146321028801515381981782506355042255201016953375149829517466449677312249611502599434850555618739830488706171667035140895674806873502543300909514568759918040129665855731078258004983486524477103833885001539135541445685573269814159175744401893663504523858005835387122082112362666991112899837534230326730196110477118156871579503345757821268248575583821695674912517830056856597644827244194658166928026249459511837772775196175188368236573504643083995409774002567 -5513982495816270388232134254127393284677692173792609278582774509636977743203029647121158805174638642867428501907786521939155900331399058909602425073976766 +1135960177108146621604027872788612991247811085764456406834564014092038611848908717507207251239454266163702244932570537009884467598603226302482406831131219148530146321028801515381981782506355042255201016953375149829517466449677312249611502599434850555618739830488706171667035140895674806873502543300909514568759918040129665855731078258004983486524477103833885001539135541445685573269814159175744401893663504523858005835387122082112362666991112899837534230326730190596494622340601191271211503693874963897891647903065633935055547219619901624214547537008122851610816644409270867409653249212336242105584174392984700025801 + ++ -30369736932762868789456108597366835061749107555998091727589163626331595118680326568212941898571309672187038272915036839449380083450246957904300051802617002374912724325419651633014408152565340519439718081357147324136023867003917288524338643759680061563616479323818330115572573568245719292922176485298767387601922362893307843067637295955606642841006993776777666041277965868780958830666697755738164183356399977211227424725670822944234275611849032230010745799964550976844117943559190671369193871330514473741920389633762695829790016565565261170688485790141638094160105909405353382982945608773290740598479367828342651860878 3451570547959142767282758882796967240086418127970526029661337442068316209707489088420708984628065070358319478649952710478991064476168799556496237099109563 +-30369736932762868789456108597366835061749107555998091727589163626331595118680326568212941898571309672187038272915036839449380083450246957904300051802617002374912724325419651633014408152565340519439718081357147324136023867003917288524338643759680061563616479323818330115572573568245719292922176485298767387601922362893307843067637295955606642841006993776777666041277965868780958830666697755738164183356399977211227424725670822944234275611849032230010745799964550973392547395600047904086434988533547233655502261663236666168452574497249051463199397369432653466095035551085874733030235129782226264429679811332105552751315 + ++ 24749014370880469345815230363662696846133977441600857690896762642529872426102613384561609594131771018575590861342023688138502403609639138062665279129058939911797019091643704220495944170754490238422880589600838613701783818105188827633578438439212856537589855796204839275633245851474930725845096235668385012500773524750522781174430369067441632028068262240870795850561389232369373523415592833273932285308223863420210049445377497367753786125779044716949754454461623397410528064697616617917065021866397277409044449982605591256067763430930720398889239414812509701319783809830072841056369381573100589260104551934136733317845 -9461623592584966196513107657889418526847060851423069480904645009418813160370721071067349946095573698635859409908288864150475056170059858850823883834932131 +24749014370880469345815230363662696846133977441600857690896762642529872426102613384561609594131771018575590861342023688138502403609639138062665279129058939911797019091643704220495944170754490238422880589600838613701783818105188827633578438439212856537589855796204839275633245851474930725845096235668385012500773524750522781174430369067441632028068262240870795850561389232369373523415592833273932285308223863420210049445377497367753786125779044716949754454461623387948904472112650421403957363976978750561983598559536110351422754012117560028168168347462563605746085173970662932767505231098044419200245701110252898385714 + ++ 19070246171469235561279483225919489206942407814032615339351735800304747459507922411906751965555240682457214768298108831815622470433175555196912899313888991765436434867025639919521068437191248198117664398275835972573354886915721765715992151871453808224011999677700078879590132676060988550961950472536029228350169237717222998397029428440792110955380302156159849645211726041489206565536560827557279129751110297078563108009278363910936720061216511798518178957070787710331228500533067546198458251241005176280410230146430275074766072259256583499095689284871987010372039977403712023630453400259082684930755893684499232318008 12330599952818018622104330691506128012101935028731995985677032980931398338453806827555760801312052792065671886621851470997557806941112316627790755867100463 +19070246171469235561279483225919489206942407814032615339351735800304747459507922411906751965555240682457214768298108831815622470433175555196912899313888991765436434867025639919521068437191248198117664398275835972573354886915721765715992151871453808224011999677700078879590132676060988550961950472536029228350169237717222998397029428440792110955380302156159849645211726041489206565536560827557279129751110297078563108009278363910936720061216511798518178957070787722661828453351086168302788942747133188382345258878426260751799053190654921952902516840632788322424832043075598645481924397816889626043072521475255099418471 + ++ -20895998178036569919774658790651496115060841511658297683195804524712012347695091074325978179977718571444320688167469052862702339462089668992243209990795362064005869602003990235714500149401994013174762139297327430396441552225926368085284222509085197484452650071390132794942944512235132641643003294762547138305644086106533258432786768644384855008506026923783604514268955071498269812887794817192371944269611642901807443894686178438687102834127061425955994253034824027771176714559050403098437684091684851207513969915720607528045624635094984539637789113651579846373399975502788877555747414523231999341294756679330384323996 764238600803843266244444637050072967342049538611688895792923539838804953492110953673720766879606601435939162680753428779068917662740403667549850724878795 +-20895998178036569919774658790651496115060841511658297683195804524712012347695091074325978179977718571444320688167469052862702339462089668992243209990795362064005869602003990235714500149401994013174762139297327430396441552225926368085284222509085197484452650071390132794942944512235132641643003294762547138305644086106533258432786768644384855008506026923783604514268955071498269812887794817192371944269611642901807443894686178438687102834127061425955994253034824027006938113755207136853993047041611883865464431304031711735122084796290031047526835439930812966766798539563626196802318635454314336600891089129479659445201 + ++ 6243894672855694190803081952962387322599009058758027960092936187687064819462191583137945440936085088260632250436567758576422207449236613172605950116622271404444221039084346501796818945639456207912207604248991842124079786471250102192718092353598850889806607728696519257402580732995770031331187089424192803722612735557735028710899438934171272639518928194764526910590046378401600819132587804143949995694950116915803127294011661411525934100144319021440919928013617766507409909846670172516021888661284467975865076091834094160862228180625536450124272957206172214541444266874056050295270719541605687740822711659847211976891 11877496607682442993105675644902145742318375725225741293060927105303783712520284640625374957608051032540491531573337817824773543104969422017506696018037874641947740606655370938613842356322585858034851150595788166740174872996252792014218946552442572806242471174234462119454014379628228878122072189387777413014452140618318641689597452676091677588204537830401725113931418426919671512011822864583481449136550835952005765386885680701637038206002172218712504732572449659704181315669255320876647592649071711438131711904976335957846353867776093588236311654631696625859173554395714740218099921290128795607292259527492722462071 +18121391280538137183908757597864533064917384783983769253153863292990848531982476223763320398544136120801123782009905576401195750554206035190112646134660146046391961645739717440410661301962042065947058754844780008864254659467502894206937038906041423696049078902930981376856595112623998909453259278811970216737064876176053670400496891610262950227723466025166252024521464805321272331144410668727431444831500952867808892680897342113162972306146491240153424660586067426211591225515925493392669481310356179413996787996810430118708582048401630038360584611837868840400617821269770790513370640831734483348114971187339934438962 + ++ -24023960171862805266003610953999097357395283354964456554686635290239019705581779621120391229617494503580661676939681517550103414632840981987397485411400553792707518662609532504246677658012933762605038799352109564432278094548068984563394926376371580465135388578139331334464060067790936072127680597181415407099723844313625277987147283697141407959289588588489162704824409673099509423520008795428217612706997355591985894255450783091681112776112997887084157623388943538145736618168104404283342039105202585543852590302154958791010622670839015475427693311663800177428904406869645066988663292128104453773413982185343111560886 -31939808827732134714870375774276102357277346245583282398423150631754622253109692213928642228787888509211781331649081002266227303203259124984426497846441848502574293640959494009564992092503141598640200823656998243767453860939156780549404892392521391484933772285520949470194562525777116137058001008184603332597820522016200623301007194309404025522056113671560767212894303567191067178003014955596425115379852712737129325098876542459702682095445350281859042779889411325882123213577906096942649941285655935053362468972482748617111598313960198743596285343178242282172686940700127068972627110105953098737923773182254460772630 +-55963768999594939980873986728275199714672629600547738953109785921993641958691471835049033458405383012792443008588762519816330717836100106971823983257842402295281812303569026513811669750516075361245239623009107808199731955487225765112799818768892971950069160863660280804658622593568052209185681605366018739697544366329825901288154478006545433481345702260049929917718713240290576601523023751024642728086850068329115219354327325551383794871558348168943200403278354864027859831746010501225991980390858520597215059274637707408122220984799214219023978654842042459601591347569772135961290402234057552511337755367597572333516 + ++ 14513652183174940741664411990199277445706189147726874603036586212536012746892966848269748909379750612027025331446918381470766609543142456872580466135425754204680927122749772612276850998180593344389487924747722210296498854143380696064338777945015153982467675141485724865534995199700908286263993697988986805404864429385840512740226775506122190698806967785494289035976495492863456705096841250592980439363856397663738211335801835896091823148249303370609165910779981271035234045185574995335952208702661648744928539539455138167482396767268362221492607154709559716065850417221174683768503217544145599044845325824451589309835 -12814535978730024053359592817368712576084646962861720729844389627130663192435154658607204342320327460695280260731620465435530495952836598646143907272825807563512741964987882356778796849529260646503692618525570185450780889283642116889481314560395290434301143877809550098309214046129802023655714098730144464028249594406616074059558969757405392170810220921023905546104487938441503430332099605473144930508420331873995741851604525954472341693863067199617721032815462094767522339305487934030130207039176659398466616780628644572276059410087128533031562978399689702766028716401176531098447698206272762966470643604141938670152 +1699116204444916688304819172830564869621542184865153873192196585405349554457812189662544567059423151331745070715297916035236113590305858226436558862599946641168185157761890255498054148651332697885795306222152024845717964859738579174857463384619863548166531263676174767225781153571106262608279599258842341376614834979224438680667805748716798527996746864470383489872007554421953274764741645119835508855436065789742469484197309941619481454386236170991444877964519176267711705880087061305822001663484989346461922758826493595206337357181233688461044176309870013299821700819998152670055519337872836078374682220309650639683 + ++ 11356479761814008572465147431830778885327227506593483181241437802252618729479905490826767363633131720717461693888023278837835457496021519184903984385091047829540007466025527592005114414671285638168997562037691602144751434208304408870143450743278437854754504713023422097017723330207792526222436928747286558205279330508360438281011315147578105966454344087225699378388309094140949428028313539634103047841948634832398526343605363013644180832752120081735152285507591096001749463421326282317713079361827765412853023201330345752038722069405404812511739634687282327711258974520622248165974215116400638833123609666501349513623 -2451734542868054449539778460457497703609327132304922810342762480808881050209276687756391911546806187586640918078231508181876445466503459873508196878629364924241891220686182517218825181707207808769770392864734466652524094735160185556148554260517746279303022469784592528209667497664672945900929888144529727881050106027775707933311860110618130543481573815538047460723253898548348335762406437618625388229555824532715231231491787570056329865617082709588903922431713098922691537317839185452018617461891748518176708607861270770493263960554805373552348256747200291438630960804647686832667981625018361034564086859426490014044 +8904745218945954122925368971373281181717900374288560370898675321443737679270628803070375452086325533130820775809791770655959012029518059311395787506461682905298116245339345074786289232964077829399227169172957135492227339473144223313994896482760691575451482243238829568808055832543119580321507040602756830324229224480584730347699455036959975422972770271687651917665055195592601092265907102015477659612392810299683295112113575443587850967135037372146248363075877997079057926103487096865694461899936016894676314593469074981545458108850599438959391377940082036272628013715974561333306233491382277798559522807074859499579 + ++ -1814184401790217165873937825605141478060935014868566665644215718762341535891730598045990231798382966074312671040257824056876679135909008140059087311700216658095793352051583071432744886316274989901835606602224927350560604355249919901932382803472476702792978322468747380191775778902733911968522382089332819162367884984027854067607561808704316828316820133400099093450636968732151876570835173932998599031643640476109466728761033062776578175554441947411139184426213290292577467587355369954997241091769769542810051228504545831588488726789173405585678190671534386784806998695797717346491308862362775748058331375692317599945 15466182953987394334491149436346080039471412309427279110582769586053943302670765125931570041904640518032832554998553018838321871748542118021556398569294085708441934948186080236498081517178574839977996802813431873543309853609838200338534343580791382510179184571852290959723696010410340740895530535423959476873857191548113125728667781953125153120447892632916574768078583174099545013854248664119997703948998871566374080719541931440495888606776561795893839624084254684939434035018741535261951124673664746010067859317726891535170781460914710499572006592206360512398012457295755926986236618644330364227754380084585899275327 +13651998552197177168617211610740938561410477294558712444938553867291601766779034527885579810106257551958519883958295194781445192612633109881497311257593869050346141596134497165065336630862299850076161196211206946192749249254588280436601960777318905807386206249383543579531920231507606828927008153334626657711489306564085271661060220144420836292131072499516475674627946205367393137283413490186999104917355231090264613990780898377719310431222119848482700439658041394646856567431386165306953883581894976467257808089222345703582292734125537093986328401534826125613205458599958209639745309781967588479696048708893581675382 + ++ -27127130599753372624001250456405972983012981437652156246797208697430661165612459362971759027335854588888552031022264244768883843080959804690580574272908031271224646245152017114094021048441971097191444782106551075175878815012595015584723250801765859461211934306789890718268168352614164589637346918581658850565274510502652089457352942736418509881708568727739912127781455473660768550022762222130489047215089836402367851853412705556570667960548570630054608024914653686223423908494006675057953013815512203710764854485332282975729323105427143207127239069826750682633272289409910001698385240596625059970587393681128674617278 5719655139276246085992066702308194672442413085748146924567717361937179810269300239821879673460959112727066470468217892213025828988023367028158410455624528688729907493639908638553730770145274142147983721694721139760883483821883267129411125364089207412089113869427479340283853501026803387874124668123626271531796990801822527792189514551888019206405597994403243358155410088320317141454525417323186389587327532772638942220300149829241141659063128602316305332848477566686425551944956989370838072872906293845914921103561360871571846865478762953536949621421094416539099628942010528483544062050170673327754206501716239719529 +-21407475460477126538009183754097778310570568351904009322229491335493481355343159123149879353874895476161485560554046352555858014092936437662422163817283502582494738751512108475540290278296696955043461060411829935414995331190711748455312125437676652049122820437362411377984314851587361201763222250458032579033477519700829561665163428184530490675302970733336668769626045385340451408568236804807302657627762303629728909633112555727329526301485442027738302692066176119536998356549049685687114940942605909864849933381770922104157476239948380253590289448405656266094172660467899473214841178546454386642833187179412434897749 + + +- 3872339191937382556 13437882608410293981 +-9565543416472911425 + +- 12702320881720530101 13823645380834800545 +-1121324499114270444 + +- 10222969257152373972 -3454292165863475982 +13677261423015849954 + +- 591233951053628288 -17639978232337836611 +18231212183391464899 + +- -7878405903223218778 9050739027069287469 +-16929144930292506247 + +- 11347120771894057376 8443917396834074370 +2903203375059983006 + +- 7831959259127703467 -257470007821066702597399141202130667973 +257470007821066702605231100461258371440 + +- 1092406341647857980 -325710450166845666190895573961860069495 +325710450166845666191987980303507927475 + +- -4220606126689357919 73461013742902296577411907972196819778 +-73461013742902296581632514098886177697 + +- -5112059189225304080 334306213789148650102245018234146620793 +-334306213789148650107357077423371924873 + +- 3093346224554776175 -204967241927023874963787190016588249299 +204967241927023874966880536241143025474 + +- -5735747638156472357 -3881750746805128137401544408305666047 +3881750746805128131665796770149193690 + +- 17639095392510638323 13312205908441007415860933757605397223142073616822325142416364932887680287063250296996056787873086490231950036662943632990219865746131453861285495087665017 +-13312205908441007415860933757605397223142073616822325142416364932887680287063250296996056787873086490231950036662943632990219865746131436222190102577026694 + +- 16304056910692545233 1463591032326743052350022746892396184459320617971409440301562638996633667625451301419074212369365394140737678584830314878769698416417465834928609990708982 +-1463591032326743052350022746892396184459320617971409440301562638996633667625451301419074212369365394140737678584830314878769698416417449530871699298163749 + +- -10347586523508777315 12614325304787850623826535169596975975360455924114817820074336137897280818245940873677389644701038550150832199897314137414727161192173691528917744363375331 +-12614325304787850623826535169596975975360455924114817820074336137897280818245940873677389644701038550150832199897314137414727161192173701876504267872152646 + +- 16875252323587344863 -10230183557696638447600885112945653217398839137450096120772416948425622105048400944465287395231588821521217980407867153259741079758527788318592431794213674 +10230183557696638447600885112945653217398839137450096120772416948425622105048400944465287395231588821521217980407867153259741079758527805193844755381558537 + +- 8574302739232756920 2945205250727759066959418729185252318153395797902208079569164623770839848878181416073351760975066439564334127158302281471631001294503759011790017443478716 +-2945205250727759066959418729185252318153395797902208079569164623770839848878181416073351760975066439564334127158302281471631001294503750437487278210721796 + +- -17657597319577965851 -470389901349206124503884936612357721199915776516939967013182926735009022045917047211666512521578494339222795740836335004070464944715357800461845632614015 +470389901349206124503884936612357721199915776516939967013182926735009022045917047211666512521578494339222795740836335004070464944715340142864526054648164 + +- 11472336850218354926 16764018932433717867649699977474298016589762238077229911249331402108995850754999065988360217500238643747316139204767820295123085026049273617874157749889925712672510963712964034497935503076689670786498045302562704435768723916334451317158760704743066709581593570757498670622547878516907127632802801541072452593999435195637193819500375063696114131057474475407791672955417184592088612921927282233762919112197264895445408873539746256555444555901857369535350160665235184955438709679669964546134487688796078142789125799020704969226557493354453298489954288702387159956161243151013189140749021799388406290339231792790773612376 +-16764018932433717867649699977474298016589762238077229911249331402108995850754999065988360217500238643747316139204767820295123085026049273617874157749889925712672510963712964034497935503076689670786498045302562704435768723916334451317158760704743066709581593570757498670622547878516907127632802801541072452593999435195637193819500375063696114131057474475407791672955417184592088612921927282233762919112197264895445408873539746256555444555901857369535350160665235184955438709679669964546134487688796078142789125799020704969226557493354453298489954288702387159956161243151013189140749021799388406290327759455940555257450 + +- 12682607562584942903 32133619583510009354538204193505267426986629771080807813988708187761849276650847958886764459302043799013813125903744946349479743277662066609741649009023451783267511140245797235200413941774959851628239089013586399425314412329003636059313583335807925401822165199322334470452126484173417518861322963430951772895619791799137157183662289329901964728384697377777905235894234370773419160283767144177627084271804319157013765325677633945370597318765372346484383325176768117059688792498687750479618961541872574768601477738410497806623403054372221338126223825515939164627992974469102910882915893925327931884157735553718792115929 +-32133619583510009354538204193505267426986629771080807813988708187761849276650847958886764459302043799013813125903744946349479743277662066609741649009023451783267511140245797235200413941774959851628239089013586399425314412329003636059313583335807925401822165199322334470452126484173417518861322963430951772895619791799137157183662289329901964728384697377777905235894234370773419160283767144177627084271804319157013765325677633945370597318765372346484383325176768117059688792498687750479618961541872574768601477738410497806623403054372221338126223825515939164627992974469102910882915893925327931884145052946156207173026 + +- 14621880654476679971 -10075923784619510279100488003620810539888599376089081798647754628017452762406215094511315867213396543200861274584884759429891242650999761503100661310915213260386281412125687376866399124849043409890009033179987278297335571911640353059036551139958369871790768643514550179661619387008678118363266091945225880595898524898713646458647465935791224159084684209727153050053537752111696883536364966526666445737103854446009305531519860527938394412863332757413309423156200192973778629503534709731073637828912608835085933003410694216843775182940057891552358942312728978810053715387504707194992816961400377579655168106377696154728 +10075923784619510279100488003620810539888599376089081798647754628017452762406215094511315867213396543200861274584884759429891242650999761503100661310915213260386281412125687376866399124849043409890009033179987278297335571911640353059036551139958369871790768643514550179661619387008678118363266091945225880595898524898713646458647465935791224159084684209727153050053537752111696883536364966526666445737103854446009305531519860527938394412863332757413309423156200192973778629503534709731073637828912608835085933003410694216843775182940057891552358942312728978810053715387504707194992816961400377579669789987032172834699 + +- -3220156644655019630 -8347829670073174550775641165362740628312221836466572623516708794243074870361401136762432100726575330214254748615114820602945887237367461962207075265579588481261313345359877816874924645801358760718027997416917747796144940020489321523749233377708490614979453376328244189926517907474704635785063100359787580409065317918203485474119227673185211436285930586838616288721370975925191964611302275354365110550116042403226844820172448647475637867255305805337047967053177320593337377763657329816935516961201488840745892529800883680912275812320160312651894919502389242002380151562481051684439333368396132543667539444686619670713 +8347829670073174550775641165362740628312221836466572623516708794243074870361401136762432100726575330214254748615114820602945887237367461962207075265579588481261313345359877816874924645801358760718027997416917747796144940020489321523749233377708490614979453376328244189926517907474704635785063100359787580409065317918203485474119227673185211436285930586838616288721370975925191964611302275354365110550116042403226844820172448647475637867255305805337047967053177320593337377763657329816935516961201488840745892529800883680912275812320160312651894919502389242002380151562481051684439333368396132543664319288041964651083 + +- 11628988978410243120 21091260149209133824278525560739673446778991946138130571540201996950100883736332286627324787663044982195445635023357027423513202277912840570399895946346028843517588470258087913846945044832851780108963206182331994065720076983528527849542421619745503796476103034657238118665288185878258232226731582201217795631247916614224227701409259346052937919425072595891571572960468193421257458185693656090215937518204243652916583730260295885562094977775951577484951577581277292356830523013216949489797535362720471761788697932265967910160407593278848113303674799017334692501935041730808945554336564957621028111014116286675587727714 +-21091260149209133824278525560739673446778991946138130571540201996950100883736332286627324787663044982195445635023357027423513202277912840570399895946346028843517588470258087913846945044832851780108963206182331994065720076983528527849542421619745503796476103034657238118665288185878258232226731582201217795631247916614224227701409259346052937919425072595891571572960468193421257458185693656090215937518204243652916583730260295885562094977775951577484951577581277292356830523013216949489797535362720471761788697932265967910160407593278848113303674799017334692501935041730808945554336564957621028111002487297697177484594 + +- -15960716439913426281 18799211173341989380260980155501104944815245973352765317821146163884181375747259542484535639646490774929026134833947975785613727050541297797675705933339289016115326958150660323801621778641184271728990164666383865587422591755046779736996211052149338115836473967202556153668963815595875844414662034458693455631979862997316049580586739835122770408911308146605671192538040301857163633538268589024651373766021087864982140201615461513687698136663128896835597598904095187715456109340116329587986878167776146023396961265667934659006280575496363066974484893764810659481361856335795455814679851690737943592227795474197104696127 +-18799211173341989380260980155501104944815245973352765317821146163884181375747259542484535639646490774929026134833947975785613727050541297797675705933339289016115326958150660323801621778641184271728990164666383865587422591755046779736996211052149338115836473967202556153668963815595875844414662034458693455631979862997316049580586739835122770408911308146605671192538040301857163633538268589024651373766021087864982140201615461513687698136663128896835597598904095187715456109340116329587986878167776146023396961265667934659006280575496363066974484893764810659481361856335795455814679851690737943592243756190637018122408 + +- -181065640455671431985325539445069267017 14120143334024043377 +-181065640455671431999445682779093310394 + +- -91295299684959299024846233061686623774 6891102275697080803 +-91295299684959299031737335337383704577 + +- -252582289949155881579950873916766853744 883304029266526072 +-252582289949155881580834177946033379816 + +- -10104159950635417603045689770006558103 17251490913777465304 +-10104159950635417620297180683784023407 + +- 288463495341489091297108607960869684860 -16376960611483226267 +288463495341489091313485568572352911127 + +- 204661965092367792468062569536290631004 7774991291341524479 +204661965092367792460287578244949106525 + +- 174559967167400201536723778015754014369 168183438971818617783400303174116396891 +6376528195581583753323474841637617478 + +- -253300708624436983509156598368557395374 -77166863757693227553099778725240875400 +-176133844866743755956056819643316519974 + +- -38587765028356074196061530813295290944 5999161273284748726648331130480323187 +-44586926301640822922709861943775614131 + +- -236400856885875891058508662756360145662 222191413471626205952456600591947275777 +-458592270357502097010965263348307421439 + +- 212937903940173587742882129816769611096 336470165768472077447806282475185249734 +-123532261828298489704924152658415638638 + +- -264812595676159375893264580577855253845 -247068943830535581577267897204259299723 +-17743651845623794315996683373595954122 + +- -1725732715479127274526681751197327660 -2279805492899538651574406423954277869507456204136276822451602661149698386520868702017367409743272511010382761246500508887739763323997191435566266331339917 +2279805492899538651574406423954277869507456204136276822451602661149698386520868702017367409743272511010382761246500507162007047844869916908884515134012257 + +- -220007189346579184019349894240059989979 9116030813176547770422918633286023943039811682891023288884273747820892639481842291616424036020927750322528731882517057595815179415042385175627374957565803 +-9116030813176547770422918633286023943039811682891023288884273747820892639481842291616424036020927750322528731882517277603004525994226404525521615017555782 + +- 139683266109784685815165642637380856544 5782493350903499652295971390391981928106911831248674750993968151944332845911526084530951283012280786005612601970108688202931002414214353708335212597807345 +-5782493350903499652295971390391981928106911831248674750993968151944332845911526084530951283012280786005612601970108548519664892629528538542692575216950801 + +- 239160165978290709841254489756277328273 5152132850125501873897264811465207492706871561577273155117982457627773151595716641409297120994045059130053034927464958986304380141364542178714472948085275 +-5152132850125501873897264811465207492706871561577273155117982457627773151595716641409297120994045059130053034927464719826138401850654700924224716670757002 + +- 315772704643232632782106484978382006176 -3689252327480456512393153800679864208480329729627292260734151097785848947569336194072922395859496552999163037466184616218582046814434719444842678248982224 +3689252327480456512393153800679864208480329729627292260734151097785848947569336194072922395859496552999163037466184931991286690047067501551327656630988400 + +- 82735713197488344149642668226610301853 -12473025194535761005577066561696471986140205263843017221991729197337093872383371857001077050460827652296473928714097816492579684543651922277865558518876774 +12473025194535761005577066561696471986140205263843017221991729197337093872383371857001077050460827652296473928714097899228292882031996071920533785129178627 + +- 63472235942371758467270296983419551089 -7866520408163137968600317959735552406794938230345293650627055135268307695389903092041438746530663083967329111232451176014649873249349534808700483360707382397988918594143264031213181385790969271527978925616276399184489007642142996251807222768397530946779296600805549276528669432847672215219943599871223372831999133812100481632278022608906065923652981249057846548868473376683960144009223047416366697876553049362242497225174860431577034875737250719899362881567590934060155436179316063810148362442197071642183371654740845983314705249832168923202400873364289483910868432511677656218937984504828452980698439495961392749596 +7866520408163137968600317959735552406794938230345293650627055135268307695389903092041438746530663083967329111232451176014649873249349534808700483360707382397988918594143264031213181385790969271527978925616276399184489007642142996251807222768397530946779296600805549276528669432847672215219943599871223372831999133812100481632278022608906065923652981249057846548868473376683960144009223047416366697876553049362242497225174860431577034875737250719899362881567590934060155436179316063810148362442197071642183371654740845983314705249832168923202400873364289483910868432511677656219001456740770824739165709792944812300685 + +- -284018520801241078671538235859630240269 -5529748211779294240854894683633173443789067073881249229985499707296461959655918837051490512357840133495603640185675483847478587849599477020706893805485599954539589062532211767295361120129440287144117406526027552427750375526095104163474774446716012360038076376952619723549765229763943818011605991300849052030142173100367582906381575666628005795818339029350398340616624791399526643991489247585213423174803853961438830286737553181353007081438503238779644371968004083452645077716952159339978836669723137339898471600546912430030276920763475622536295311290657163861398519747560279682401429552174530714298081464588450842581 +5529748211779294240854894683633173443789067073881249229985499707296461959655918837051490512357840133495603640185675483847478587849599477020706893805485599954539589062532211767295361120129440287144117406526027552427750375526095104163474774446716012360038076376952619723549765229763943818011605991300849052030142173100367582906381575666628005795818339029350398340616624791399526643991489247585213423174803853961438830286737553181353007081438503238779644371968004083452645077716952159339978836669723137339898471600546912430030276920763475622536295311290657163861398519747560279682117411031373289635626543228728820602312 + +- -171812101820192353275910956459431262142 11401673303315394031728944442295528921842441448377692701102691446500671963119794838260543877466107345474902885032629120622020177051592733148817057943390167845763358795044702079370835841331467130719834250134674578757640577473495192331790176510774020541399177011446664359866582351045889299070080989390219063301859447807907203943168891690028442190793548699886572720360741686677780644932612683647303776634496172481504075784427704287335805355801794320914944330891519283383694196486986108936857630373759865062862204149003789919218681050221366182434949855054760827976853645027544605870235074909890698574792562001595287630131 +-11401673303315394031728944442295528921842441448377692701102691446500671963119794838260543877466107345474902885032629120622020177051592733148817057943390167845763358795044702079370835841331467130719834250134674578757640577473495192331790176510774020541399177011446664359866582351045889299070080989390219063301859447807907203943168891690028442190793548699886572720360741686677780644932612683647303776634496172481504075784427704287335805355801794320914944330891519283383694196486986108936857630373759865062862204149003789919218681050221366182434949855054760827976853645027544605870406887011710890928068472958054718892273 + +- -243638660221338112796448050030955119997 -32214383478080953899491069562585164652288236626686985994647827422262342469970423345510055643470262764747630363450204055220886177681745412924556264758690138113272748656941509018308925555317383307928766093730384151056027828368474245304944063213926492719166086055718735381341569379006804236876950175122702350552198046290567043195716369691666842524594399597143281611765509174168738392889075290806378316647736667077047013214732267367344808724905727602402784621437141760604478301412768904784950365257469208085143467704875589485635570084387755189599791857576855454112556762755762408826226326879491415484319411662301650468948 +32214383478080953899491069562585164652288236626686985994647827422262342469970423345510055643470262764747630363450204055220886177681745412924556264758690138113272748656941509018308925555317383307928766093730384151056027828368474245304944063213926492719166086055718735381341569379006804236876950175122702350552198046290567043195716369691666842524594399597143281611765509174168738392889075290806378316647736667077047013214732267367344808724905727602402784621437141760604478301412768904784950365257469208085143467704875589485635570084387755189599791857576855454112556762755762408825982688219270077371522963612270695348951 + +- -126332081511349770866908261827634312283 31497387372874133218238910173378055967910722258532087598053588964599898753455370244114881403020152175272452951858324158004662566613339529101292284073176382818309096142522412043073218657587031893636358434796164444941535757484360125937835242214199979245499374972029624710574236962978707708765065292759037309958875006017588240959790355958632745299212449602934380927677385974488564420550408281673927387615657765312151272852486266800510090872812376232597458154951925709496664568906509814364388823105469855516803225244972466742963619633076158367569109107733990828830121948130235858799809203410103682003414364238243553515261 +-31497387372874133218238910173378055967910722258532087598053588964599898753455370244114881403020152175272452951858324158004662566613339529101292284073176382818309096142522412043073218657587031893636358434796164444941535757484360125937835242214199979245499374972029624710574236962978707708765065292759037309958875006017588240959790355958632745299212449602934380927677385974488564420550408281673927387615657765312151272852486266800510090872812376232597458154951925709496664568906509814364388823105469855516803225244972466742963619633076158367569109107733990828830121948130235858799935535491615031774281272500071187827544 + +- 219979452670016849533060110266815720199 3900115048441644499033281842448985956665866771934663536385503692700586024397767816761943054115584011069129310718114010862034970648115172218305599786238607524420973404711138276011261135403209178420948996472570042497859127324157786975578751148348046315727383390370594954695454631662061021971027739429505825056455676233533511412589936865597034183410893428831818716136282201523804692574965779771140320669492229416601369453681528301333865290947482219850340728455965391492610516639151652595539203632139883064874286555941718154489936421274731413286355640404192677546692090304496817063325766995908926108582896362623757323811 +-3900115048441644499033281842448985956665866771934663536385503692700586024397767816761943054115584011069129310718114010862034970648115172218305599786238607524420973404711138276011261135403209178420948996472570042497859127324157786975578751148348046315727383390370594954695454631662061021971027739429505825056455676233533511412589936865597034183410893428831818716136282201523804692574965779771140320669492229416601369453681528301333865290947482219850340728455965391492610516639151652595539203632139883064874286555941718154489936421274731413286355640404192677546692090304496817063105787543238909259049836252356941603612 + +- 585873325961105129055557280004608765382109855007674169500308242261038324959928764512890600512016613154122762798104714052579267789493643522748210870974797 -1855792162818946202 +585873325961105129055557280004608765382109855007674169500308242261038324959928764512890600512016613154122762798104714052579267789493645378540373689920999 + +- -3026050092505200332789765255096964033685859497096213532090644235603419347590512426830117415222669642053441336442247132403948783838396746566100575461602162 18009081534399282710 +-3026050092505200332789765255096964033685859497096213532090644235603419347590512426830117415222669642053441336442247132403948783838396764575182109860884872 + +- -11124638695599888462310706699308855434715251048597328942409434888923094027849143412724699165971400546471660924330688750607774759764580214088920441698992069 -4827559068742614723 +-11124638695599888462310706699308855434715251048597328942409434888923094027849143412724699165971400546471660924330688750607774759764580209261361372956377346 + +- 4950293428090696283711882613183655723616682297360442241017758383241177602498881186549809051670562038601658285833496694108818253845693871318067007752043113 17597810481352184048 +4950293428090696283711882613183655723616682297360442241017758383241177602498881186549809051670562038601658285833496694108818253845693853720256526399859065 + +- -5733769947958740467479139247420201065087494801172241127791526686385518674532830661413722661802560247463032020003355494614502034002778775472609306735864748 -3892174127829225880 +-5733769947958740467479139247420201065087494801172241127791526686385518674532830661413722661802560247463032020003355494614502034002778771580435178906638868 + +- 8320894458193427045187598554188178307429755504967209344418448624882517461814957461249858674758807195827056824653471934409067429988676743031117653237018365 -12861394200627120797 +8320894458193427045187598554188178307429755504967209344418448624882517461814957461249858674758807195827056824653471934409067429988676755892511853864139162 + +- 13033402737450594044106258936169013897237368708138118260402180886096095497725071502601849887805439844083105685971731015312020770945603825344926844435936044 236396022362585261770052671762207864597 +13033402737450594044106258936169013897237368708138118260402180886096095497725071502601849887805439844083105685971730778915998408360342055292255082228071447 + +- 12170667278114656173974716189098171384426379753661081475485441559687661443127166543908925678856145097632475832903680828294561265828775791256812588754280222 -276673555533799047589626400978981416789 +12170667278114656173974716189098171384426379753661081475485441559687661443127166543908925678856145097632475832903681104968116799627823380883213567735697011 + +- -12755594876262399860618168642932232021734362385933348033134635580177924615701078617214764415318471507488803810365565826229169313660087149542130819663319659 -157671440495648010763311068579191828684 +-12755594876262399860618168642932232021734362385933348033134635580177924615701078617214764415318471507488803810365565668557728818012076386231062240471490975 + +- 8664063140780163008577373335591938905735059211566906376953760862047748343846207426667781783874718320339071949903053785280430612875488847226724390758938740 54361107931665215623681874454167019934 +8664063140780163008577373335591938905735059211566906376953760862047748343846207426667781783874718320339071949903053730919322681210273223544849936591918806 + +- 3699576825118349347309026261327541749454660339251578894574483235547605815416603169143590292164644149607672871236942391817131531474661895913650810587431606 -50508350367572393968128467319633674717 +3699576825118349347309026261327541749454660339251578894574483235547605815416603169143590292164644149607672871236942442325481899047055864042118130221106323 + +- 5626548453644136572409808769267055618695663227750732922630041368983808478347120771651822300668480671524976882745306794511840379704578900504784165956486985 170502882789371639987361620116696459267 +5626548453644136572409808769267055618695663227750732922630041368983808478347120771651822300668480671524976882745306624008957590332938913143164049260027718 + +- -10859007735074693411217019392659638207496329895257318665547454149984863458541990037760564769787816800806064437172810158051442267508476778676439633382657890 -7558060977666720080449823996328496253877735754811271086853901493753796001778345391546991917892931500169890406340928835457635973812901681485438886367096185 +-3300946757407973330767195396331141953618594140446047578693552656231067456763644646213572851894885300636174030831881322593806293695575097191000747015561705 + +- 9842028993407961669727766131360795288615020071102475108883839785397865740828387076847892646234215787999498419839351470775471313077046438080666908734795616 8259939762466350877481193620364896193464602165170783019804380181692322874550956777598992104871440502758410340359413403619753571535498118388286469082729503 +1582089230941610792246572510995899095150417905931692089079459603705542866277430299248900541362775285241088079479938067155717741541548319692380439652066113 + +- 3122315115429970622394662815735050825423438028108957393747131991771456957037829402044934484343765915727397519247940959221091465331254497476137639859816450 10737995515603450913722681305571315249864367824351372254572936648132763616823019940208526402092654554035074813865303483747097673960803093638463005072804384 +-7615680400173480291328018489836264424440929796242414860825804656361306659785190538163591917748888638307677294617362524526006208629548596162325365212987934 + +- 11618335890332522671268040181306950825004789685088262996478365976802329054158653675768163009290064139158450983598701977173152384425333441365287895694522192 -13130287008197231017935223399369698658354829835061356451363818961959486828237111511740029441613108087354987794332115218978284937263725126538295501305403242 +24748622898529753689203263580676649483359619520149619447842184938761815882395765187508192450903172226513438777930817196151437321689058567903583396999925434 + +- -4829477140897377009195646150061276059814366801005389903693533021027427566117360765323647260121062827801190746646296803957067548167571028717513392985791293 10716557117391614298810040587314742187092120526669273567183969821384063434473189717686678450880765426943205955814024872764413373364846268902370055526485180 +-15546034258288991308005686737376018246906487327674663470877502842411491000590550483010325711001828254744396702460321676721480921532417297619883448512276473 + +- 1560421244904974852620371975782132605421448226892487453928759432083522187778803424020804578027100625536441377609275030418285893555753560195716001014786650 -11797558308994912054526619290334311429749533070145154703018977152548370444659962978040151671210413666186432921816690953994784423526183449271023503069393845 +13357979553899886907146991266116444035170981297037642156947736584631892632438766402060956249237514291722874299425965984413070317081937009466739504084180495 + +- -7701347923966912534344428538744620884561375267012102797292378941649984539207353887059064943586048644516121387166836442084007442716291792933061162738380376 5290969389374230541016502448421359606252744677802288901830045825873182202718418905866055323957065013553046698199939002159982374580735362593037515863844280108947533575824820196689891621498006303535207762625068798755031433921940066544809959896067184147997503827988613858484669349726945188167613248195147619673963531690938913245110754715059472477991342216448470339490385593605806518967792963339193162830698488489270925945408227996742278697477358272529028932771642478870844024835907350391770605391526921411004262446196112836319091260967898895009427182171643279100998182191816962677328417390867021108292139204864164048286 +-5290969389374230541016502448421359606252744677802288901830045825873182202718418905866055323957065013553046698199939002159982374580735362593037515863844280108947533575824820196689891621498006303535207762625068798755031433921940066544809959896067184147997503827988613858484669349726945188167613248195147619673963531690938913245110754715059472477991342216448470339490385593605806518967792963339193162830698488489270925945408227996742278697477358272529028932771642486572191948802819884736199144136147805972379529458298910128698032910952438102363314241236586865149642698313204129513770501398309737400085072266026902428662 + +- 9733743430220591762422540139212426729307515492818443460852332805653889275463385649305231919846970974905736816260992940027028218064265519723018527155353151 -29407855293830047984154639411082591337348779678279017647951764366455421210163494489475996514661359700145916243499452007595041420522019751347743105082745321262372977262641488359297167392118038994384136863563032667040671405618315550876997904307423736276844997706938133936081058323434935833614475654922773162140266784233792639117145232791514703532554345086520312281500696798706889025860427142771458666376271994240028586899592254884476941388776984078337603148583453255593120138178690189726206775893096279000909079330468718593887702543025737308336025198677457129910473491269839827087491228569718246503140134413881896746751 +29407855293830047984154639411082591337348779678279017647951764366455421210163494489475996514661359700145916243499452007595041420522019751347743105082745321262372977262641488359297167392118038994384136863563032667040671405618315550876997904307423736276844997706938133936081058323434935833614475654922773162140266784233792639117145232791514703532554345086520312281500696798706889025860427142771458666376271994240028586899592254884476941388776984078337603148583453265326863568399281952148746915105523008308424572148912179446220508196915012771721674503909376976881448397006656088080431255597936310768659857432409052099902 + +- -276731217243271862683214238489380950428392903790808046630969592255272629537001990355375434170910931115552132394269672247616298060929507021008951190291387 100289083769237476480554074865040988004216167545459907207847010762380733541100608695693297149249375537088329431700364201275915507683345148401600569951338052791424407090330310974243070931256108167365334162914085216447196038922091547331474328250886730614683299908003398886233860613008266913065047699535081030427106800418656336608005860846045905149012346378286475449307630537665901621055008855374148058291266835796203075976592585729940879567246284967856356337849150102261744547461816282538319258966892339056695718919291240188920586288417893106046698069355647145603908383687239983874164793005765733782432717429040621674 +-100289083769237476480554074865040988004216167545459907207847010762380733541100608695693297149249375537088329431700364201275915507683345148401600569951338052791424407090330310974243070931256108167365334162914085216447196038922091547331474328250886730614683299908003398886233860613008266913065047699535081030427106800418656336608005860846045905149012346378286475449307630537665901621055008855374148058291266835796203075976592585729940879567246284967856356337849150378992961790733678965752557748347842767449599509727337871158512841561047430108037053444789818056535023935819634253546412409303826663289453726380230913061 + +- 8505070389896098095621766692413480203366379968950158493268895987250690600795955783113900096527432416791184386061684833478921638080978014176210898461637606 -16410711613672171332126342754193842244915477287016327757357714698751777287458963458682349581881560880814595167244857846847668988374679430572782121021084683986742283012573569894084166107235597351093334125816075658348307113218478800035703971671113417712009419861470917307849916674203301497919242668373376352901312309673053175315189945730756118172940886476343290174961420986113367531057713782438374928471960914578818951372282574754612716278516397754222547513576728677459134022062202283647690649100602260948409511070624300011106517649666031530376191755817891213910847547809248990517666613043010292627100428536737652546738 +16410711613672171332126342754193842244915477287016327757357714698751777287458963458682349581881560880814595167244857846847668988374679430572782121021084683986742283012573569894084166107235597351093334125816075658348307113218478800035703971671113417712009419861470917307849916674203301497919242668373376352901312309673053175315189945730756118172940886476343290174961420986113367531057713782438374928471960914578818951372282574754612716278516397754222547513576728685964204411958300379269457341514082464314789480020782793280002504900356632326331974869717987741343264338993635052202500091964648373605114604747636114184344 + +- -12618010259109779267590315037969998053964054382853891516547435925972388025118492931596200697357628900783311183940584302426381939302632641549019984810957030 -30500906828861638007306362171210132987300359439962044769219457463653547834815716264412200930088623097530758080891972640000479943534665059199377729854850415258341537838023739964147532129877743393965857370995558748807382396090020006195649251292012405690725917389684473999400905751109361754679152179983739269026226054012963756892488872262522587481931950410504651253101938824790285623805566521723062029033001745636445860437154344665483641408727637784045030118212476306906983993748299291616038887011943864441807818857508443930272872365334665976442185494702520760793786640113779099219233665607521784524244604432396247693263 +30500906828861638007306362171210132987300359439962044769219457463653547834815716264412200930088623097530758080891972640000479943534665059199377729854850415258341537838023739964147532129877743393965857370995558748807382396090020006195649251292012405690725917389684473999400905751109361754679152179983739269026226054012963756892488872262522587481931950410504651253101938824790285623805566521723062029033001745636445860437154344665483641408727637784045030118212476294288973734638520024025723849041945810477753436003616927382836946392946640857949253898501823403164885856802595158634931239225582481891603055412411436736233 + +- 793528769616879938852241178439496352527042950647521648629732169156958768358523029837406526207126598190786120139491813624819360632811627576064199559812277 -7357484069649002655190557040768215614708659708788999334802985986235721030962928900092675952032143512196279092521450986819067071570862007086586132687661085824939677603953832219860573980632016025218580608321648907608385784471745482257672314890331358256478273312255285010343369949412955387472116587504557483184506548209831317705115523967163525846685455369176657510129844566195941925821733027993620517287411895496215426174909366458092382652675628195464969405904518323018004882611048769247228828875493680284766874334247375868318795940759082324831733175858991629741478124633015067484305547002438816473086042218906532116413 +7357484069649002655190557040768215614708659708788999334802985986235721030962928900092675952032143512196279092521450986819067071570862007086586132687661085824939677603953832219860573980632016025218580608321648907608385784471745482257672314890331358256478273312255285010343369949412955387472116587504557483184506548209831317705115523967163525846685455369176657510129844566195941925821733027993620517287411895496215426174909366458092382652675628195464969405904518323811533652227928708099470007314990032811809824981769024498050965097717850683354763013265517836868076315419135206976119171821799449284713618283106091928690 + +- 30958566711373255787092081401292877738974978442987704470984765018293851031728996862405055424093249924047528792113585028592262445810946419909807061004531455817427671594281537965628880611732831524185850161910304038646992464838306728350704966234151134620041799373762432970330864023007632010865749239024802839173884778578927209741320635135275002489733299806669933393428518104197594560039136096527206600870299327752296492029012993590212340409989598323540081430189567580333356380487749078595746626408529223195894600223743978246922817054226858311823994547784553612982586322603593335538875728113115443554199017672360091721648 9164115638960783470 +30958566711373255787092081401292877738974978442987704470984765018293851031728996862405055424093249924047528792113585028592262445810946419909807061004531455817427671594281537965628880611732831524185850161910304038646992464838306728350704966234151134620041799373762432970330864023007632010865749239024802839173884778578927209741320635135275002489733299806669933393428518104197594560039136096527206600870299327752296492029012993590212340409989598323540081430189567580333356380487749078595746626408529223195894600223743978246922817054226858311823994547784553612982586322603593335538875728113115443554189853556721130938178 + +- -22540807692474380279530794404584230073523360203115293035869063366926380719566516089428840111682263403627532047214106171892715667227836310498366393991106231487046533598391969789120283294510723096483520917309134391072655861112766764278247568027435618337967113341863713181603534251049249873125130781073437913954718595729437608729446837417196899902194261111827656247095442897532040935029872731410799530408713850806239149348700486268275019296069828199088780767614008685960242354118969741283398882689239770114582524756296906388861630890288875920861344939520380841337675934551587994259348267613541166769237154904791412049964 16928681651977808800 +-22540807692474380279530794404584230073523360203115293035869063366926380719566516089428840111682263403627532047214106171892715667227836310498366393991106231487046533598391969789120283294510723096483520917309134391072655861112766764278247568027435618337967113341863713181603534251049249873125130781073437913954718595729437608729446837417196899902194261111827656247095442897532040935029872731410799530408713850806239149348700486268275019296069828199088780767614008685960242354118969741283398882689239770114582524756296906388861630890288875920861344939520380841337675934551587994259348267613541166769254083586443389858764 + +- -5403850875869356031749551669837202919756114555261706106905659104903792701565965475066159243529680606410723686422444947172225540145977333194008702465610630608545009270872541652430806931212184915840724378685979865349848151917650322286497417985248678815214889868576385900691591784772762893647315325310416150353725001943778473686980157692817497562783521120544549784746647104651038037129984152623720529803205580894126664077380391379306511348324442512538418658728022685805514196592544294177914956734669359073791151050869328577099869772182315103156047405800398706114122356939316464974680113324979723289916823063616573634058 -10755560408227106818 +-5403850875869356031749551669837202919756114555261706106905659104903792701565965475066159243529680606410723686422444947172225540145977333194008702465610630608545009270872541652430806931212184915840724378685979865349848151917650322286497417985248678815214889868576385900691591784772762893647315325310416150353725001943778473686980157692817497562783521120544549784746647104651038037129984152623720529803205580894126664077380391379306511348324442512538418658728022685805514196592544294177914956734669359073791151050869328577099869772182315103156047405800398706114122356939316464974680113324979723289906067503208346527240 + +- 16201587974698660164372991183566748501003872177894450603471850345714117528335101264234127789041855420954511595895378320972957964222386731614839583078498685801156670229700092209313747849610762975747730086443186821337319452128253859293962343891549207804191088925361935683615063225197130192492652062735684739784075955094308092423304262201429421582566117390598395895220976999990205945523225411701169301910362640419341608407294018105959688929256136725564385243617240412649023368133778798063226772467915584333795357813292935080009919284755332034998122912861893282865727947810588086156919649131720183722427134042574317487793 -126159569916621842 +16201587974698660164372991183566748501003872177894450603471850345714117528335101264234127789041855420954511595895378320972957964222386731614839583078498685801156670229700092209313747849610762975747730086443186821337319452128253859293962343891549207804191088925361935683615063225197130192492652062735684739784075955094308092423304262201429421582566117390598395895220976999990205945523225411701169301910362640419341608407294018105959688929256136725564385243617240412649023368133778798063226772467915584333795357813292935080009919284755332034998122912861893282865727947810588086156919649131720183722427260202144234109635 + +- -9976758107386398142455037422077809088581080675608340830198269021688955930541332630075972471934165382030070969307731206728197760190279942894255740733209190331510591013089699837164445642396864912572863786290237335963836376543389815671640509582958465164874961381137096877288362944469137669502842448492172241151419831252572392809173900377271652074261706120638052379886108764460001026094198502028776365675088466580595870167840105746912975236851293882732079317535103041585285239081516202482201377111734010788198635874359396626004300532752450289119192633850562141516671742961938277967783337559307443617308447853505824391099 13449070890444925581 +-9976758107386398142455037422077809088581080675608340830198269021688955930541332630075972471934165382030070969307731206728197760190279942894255740733209190331510591013089699837164445642396864912572863786290237335963836376543389815671640509582958465164874961381137096877288362944469137669502842448492172241151419831252572392809173900377271652074261706120638052379886108764460001026094198502028776365675088466580595870167840105746912975236851293882732079317535103041585285239081516202482201377111734010788198635874359396626004300532752450289119192633850562141516671742961938277967783337559307443617321896924396269316680 + +- -8570952518585194406209873586517687582701183275108243979199329595605282282125006489076327154374449108678257552384372919282846744626955206382078850958298637157198962032090439427286914716782317030245513658212430127586764421559372214829010306717557679285031617989735914399954286846456953917915955558448774972943731602144914068097214910567329340361564904028964471241318105967747431610163083002382821902859161510204381788262611298660559327478615315484763561786397041779926288206767156863141140852268323253657685018587945456372648431446464389004257999049529945532453598011773843788498650935959375182414447893892341891463988 4431555062692055371 +-8570952518585194406209873586517687582701183275108243979199329595605282282125006489076327154374449108678257552384372919282846744626955206382078850958298637157198962032090439427286914716782317030245513658212430127586764421559372214829010306717557679285031617989735914399954286846456953917915955558448774972943731602144914068097214910567329340361564904028964471241318105967747431610163083002382821902859161510204381788262611298660559327478615315484763561786397041779926288206767156863141140852268323253657685018587945456372648431446464389004257999049529945532453598011773843788498650935959375182414452325447404583519359 + +- 4117976000917214601143188578494558474138167055110060832594841842655428229500889876131794484851166401425675703592388271925904534237338595998991043982676292549088043959446082382516734793718348862105938692342851330680670593768890094290655852108130945387988863730762717733881418314989528719379494082656897158942547008663543153236129762264443358316776532465284014215413819415615612452225913947961681691310132286840303081453109375175436902292224029179426794714036524361081174901146731799945483243427138748119832116750910126386838614645397770107366925613473924955965862778639046707637382775371488874447622330992324750207465 329466253508616383200261654231797136951 +4117976000917214601143188578494558474138167055110060832594841842655428229500889876131794484851166401425675703592388271925904534237338595998991043982676292549088043959446082382516734793718348862105938692342851330680670593768890094290655852108130945387988863730762717733881418314989528719379494082656897158942547008663543153236129762264443358316776532465284014215413819415615612452225913947961681691310132286840303081453109375175436902292224029179426794714036524361081174901146731799945483243427138748119832116750910126386838614645397770107366925613473924955965862778639046707637053309117980258064422069338092953070514 + +- 28857935543824608075326348244201981931023939250259142606733822094071772153858420201297951828741003977413353359215638528196235956061529059419904405354390715114239219947402126760298132539402386106279333968395498788354937020337343839325588433318100331044091923709732742795159387846354148919054314582749477292946200912006940503778924320301062789466388997936618573519744795661160190636101768486096961991215006236190655062992372061052426455063703038765465688361316141792840153608145888307784845264037109867657483109819380082597605481013612040648149090345778910883349230476481347645708269410828528742743794495302359380494607 126536164564464424337714470705049463978 +28857935543824608075326348244201981931023939250259142606733822094071772153858420201297951828741003977413353359215638528196235956061529059419904405354390715114239219947402126760298132539402386106279333968395498788354937020337343839325588433318100331044091923709732742795159387846354148919054314582749477292946200912006940503778924320301062789466388997936618573519744795661160190636101768486096961991215006236190655062992372061052426455063703038765465688361316141792840153608145888307784845264037109867657483109819380082597605481013612040648149090345778910883349230476481347645708142874663964278319456780831654331030629 + +- 3146199586408378667812619157270468624370984629500707476575291934586478540055436137993431548830607708293475788354970610669452058906009873485175438772484599603993015239438297747261356407887781450787482447252615210880612867127689283653562498484594955015919746443263740095372831444793239911996227663006098501180972347442107190398034048225264564325230296723559400768342331039755765597288518435463475921534765025262262798267314969774604439319964638461636007229819888743218820584570149249791727508891676067767073852694327748467914037392778283816153183422263956621516748627574334199731850712255885395479903525322397561293553 -169494171680584797187706369710105239124 +3146199586408378667812619157270468624370984629500707476575291934586478540055436137993431548830607708293475788354970610669452058906009873485175438772484599603993015239438297747261356407887781450787482447252615210880612867127689283653562498484594955015919746443263740095372831444793239911996227663006098501180972347442107190398034048225264564325230296723559400768342331039755765597288518435463475921534765025262262798267314969774604439319964638461636007229819888743218820584570149249791727508891676067767073852694327748467914037392778283816153183422263956621516748627574334199732020206427565980277091231692107666532677 + +- -17024716654716744558842421452239026542281806678754026383430912733874686056449261218428541803113383766132449624540209841726047308927951820311213785345168358108138304716549475322223600292513384537980742126687035576531330089447100646214364923043445903103768701639992829171572718403272488931980504461938688955457870904289239032709146514866818331202329982821151580491257491540240579366183525075936339515949345815704583685855315810611089822402567649542290589282153225725537026309623090382054078872576985425957096858376112688308214148412270019118710904983829984589093557307164347051152307499446188262820058714564165108542508 -26845770031559702758807696432929071597 +-17024716654716744558842421452239026542281806678754026383430912733874686056449261218428541803113383766132449624540209841726047308927951820311213785345168358108138304716549475322223600292513384537980742126687035576531330089447100646214364923043445903103768701639992829171572718403272488931980504461938688955457870904289239032709146514866818331202329982821151580491257491540240579366183525075936339515949345815704583685855315810611089822402567649542290589282153225725537026309623090382054078872576985425957096858376112688308214148412270019118710904983829984589093557307164347051152280653676156703117299906867732179470911 + +- -20875354448001792153279041347864644172439177882677780548397567327274288309764204295853633150227327732322157811413794613378828291977852467550695289535036337326494269114787031260705326469002279939986228049380615128280814933748700667874022724707001736732724010699175779382411342385842744973636495738468838244099596215421975861650998954057316519632062827510021706536194961332185926551767127180751211669386674770139039516623606727799489291663572125587356845055646322930167536458093283930082765496058330805117442824718962237069840252138957395570892073194575112213410604881673785921789655406716271370732069643455590690035701 -321447426701397438572265325285879998363 +-20875354448001792153279041347864644172439177882677780548397567327274288309764204295853633150227327732322157811413794613378828291977852467550695289535036337326494269114787031260705326469002279939986228049380615128280814933748700667874022724707001736732724010699175779382411342385842744973636495738468838244099596215421975861650998954057316519632062827510021706536194961332185926551767127180751211669386674770139039516623606727799489291663572125587356845055646322930167536458093283930082765496058330805117442824718962237069840252138957395570892073194575112213410604881673785921789333959289569973293497378130304810037338 + +- -6750548706930727136186675393752693335334383613941059024795513640678178119089262068912855951615043660442324823673049951182143778744824110223137384940032268718291241014850714197673735719784663896993460156686600813524168487673234842233781654493200950459723884918456280719440022930492599128086690014332139955274261568563155723011697763382009890186816226119314994799655369791620499988988986590903148198659095740939986627235565633349906453726759224441608018598520571182643709143072528030332708598472074166415467718451869993686505339408706320298338691467040585228617379086727764240955696690287600957842671916189752415855520 132223863177855649509430852484092802671 +-6750548706930727136186675393752693335334383613941059024795513640678178119089262068912855951615043660442324823673049951182143778744824110223137384940032268718291241014850714197673735719784663896993460156686600813524168487673234842233781654493200950459723884918456280719440022930492599128086690014332139955274261568563155723011697763382009890186816226119314994799655369791620499988988986590903148198659095740939986627235565633349906453726759224441608018598520571182643709143072528030332708598472074166415467718451869993686505339408706320298338691467040585228617379086727764240955828914150778813492181347042236508658191 + +- 15737797902964168014939893286340956118635524170934156177365242966267432695262586636031957242055461736359478270642576860414422844075672388559647477705484719667060463718865742735598799928335211410004369240278699196301127699945374217439676378682879115442203681638050752745036508637214733712716867800216723838016099572951915042604603457902610639317648800296497583507890473114507231814851908526534709496988648572353272479026750068932474334642929727977996779536604912743446197670724757690108283368934769626461285961947257397454619164856011847736479229692086038931510067165282571276049292116713101550911614590774659556899356 -6114512833799784097991148713266650451765474382378581896952003894922931741133332233338460555227243451198289670274036744955599177213449957470212981501678055 +15737797902964168014939893286340956118635524170934156177365242966267432695262586636031957242055461736359478270642576860414422844075672388559647477705484719667060463718865742735598799928335211410004369240278699196301127699945374217439676378682879115442203681638050752745036508637214733712716867800216723838016099572951915042604603457902610639317648800296497583507890473114507231814851908526534709496988648572353272479026750068932474334642929727977996779536604912749560710504524541788099432082201420078226760344325839294406623059778943588869811463030546594158753518363572241550086037072312278764361572060987641058577411 + +- -26633154627863501044020127597209297142657179797586777727331879111280843451446814109347357601013807189824906954310855123313836812409388745541128842840054310853220032505914307470215180950497357091093642400638925719682307925365402618310180378684705799724964274776149984064608716300479893889145492885897234574442542501896696821902329473018442082678749291668341477914681413039643187020003425962922948452894682558162414623956491734656939841377698702802567258906642912449969621455596132708975438173455827361542712483153981422051943690720556013580161324856788091093465837542336129629269227369781823515673967591796132853515009 3321161637038961370471515250185392889390643163295535903347391615170504064647249127732639364682803744773593849851778894972403397573953564801884397178069327 +-26633154627863501044020127597209297142657179797586777727331879111280843451446814109347357601013807189824906954310855123313836812409388745541128842840054310853220032505914307470215180950497357091093642400638925719682307925365402618310180378684705799724964274776149984064608716300479893889145492885897234574442542501896696821902329473018442082678749291668341477914681413039643187020003425962922948452894682558162414623956491734656939841377698702802567258906642912453290783092635094079446953423641220250933355646449517325399335305891060078227410452589427455776269582315929979481048122342185221089627532393680530031584336 + +- 27668394897866653012794531261739800318882766882548843941974485394983434533400277607364280566269718161470415771058329222680901477416257843578362127708934184467195154000133252468684612556324066063725677629160438683034201285122508880444372096430021219637788794365539396242345208611990491721052691567092029622640533057073151980959055665792776356282961971341363712186503783566960850166774438868528799819047163739437906559674823146932668464230936946321915236658512741918196732794332451120218658490129307932187658010681746557120172585093207839141764683325214902696969028472942954863209641597556494684135445935915485525220911 204625459185084436546676461283890328511903949966691877662249903659689934813784661695047569885195881142676761876303280806728760511429260843727967794322777 +27668394897866653012794531261739800318882766882548843941974485394983434533400277607364280566269718161470415771058329222680901477416257843578362127708934184467195154000133252468684612556324066063725677629160438683034201285122508880444372096430021219637788794365539396242345208611990491721052691567092029622640533057073151980959055665792776356282961971341363712186503783566960850166774438868528799819047163739437906559674823146932668464230936946321915236658512741917992107335147366683671982028845417603675754060715054679457922681433517904327980021630167332811773147330266192986906360790827734172706185092187517730898134 + +- 18944451653774463090918576081661764936021793389045063662102219434278236461286997354190032851092512146937346521704215170240383659165117708716738711782597164244188741818096207452074083439983059414271417130274747048227795964884943105011205424198661201055104372863019759130697888820715782179466491256695453118035286889359217448004524564796840711987314064158194625731263591557915838970249677548534895064545467992194029425250039951132361639559343536937119283951538321037694842089561504643350632756961329867761604760788760440497535611072991056505806805291706178639395690245460397975614715123591611301423752799666149495108752 994321141213369910357526037382331323092462599623554452705525887587326552002660849455542761618020243106424015447778226642816634338781654345001677083881111 +18944451653774463090918576081661764936021793389045063662102219434278236461286997354190032851092512146937346521704215170240383659165117708716738711782597164244188741818096207452074083439983059414271417130274747048227795964884943105011205424198661201055104372863019759130697888820715782179466491256695453118035286889359217448004524564796840711987314064158194625731263591557915838970249677548534895064545467992194029425250039951132361639559343536937119283951538321036700520948348134732993106719578998544669142161165205987792009723485664504503145955836163417021375447139036382527836488480774976962642098454664472411227641 + +- -25075128489482657321316021943980016828761861550379828525731288423212311433274066958090940464803020097932875912251380196071686918459370667428905844496548191635733867314315152547202859654044591981512687559437417616479425752991419002108503390319869665933757684966460526631533822984311725217788657567199485442486045019468844265484117570385156844404625735176559901986920712550964238722824122000259551821135404274194791706113272773768366572120227974096419295159271316157215551931810740200836725504693738229444336470213883741520460842708733150362983831267583568258736572295448486287825894301201018490203520738439038977754991 -7402949251688548738762242219263594861535354011996392637087346760786292549376145193266590582054224293289596877537643409310483743293801574030358189880866069 +-25075128489482657321316021943980016828761861550379828525731288423212311433274066958090940464803020097932875912251380196071686918459370667428905844496548191635733867314315152547202859654044591981512687559437417616479425752991419002108503390319869665933757684966460526631533822984311725217788657567199485442486045019468844265484117570385156844404625735176559901986920712550964238722824122000259551821135404274194791706113272773768366572120227974096419295159271316149812602680122191462074483285430143367908982458217491104433114081922440600986838638000992986204512279005851608750182484990717275196401946708080849096888922 + +- -26509487378481600038412836495388065888781507388737194948728047318975269277448073484403390476243134990463394380967295356958474984927721196047241216945988250219075749832868804186657201899994373052648345989716938779173325348547767647529160988985542438998030764420175306438858518207072038513664360905985908879070216069156102379349899544471658754952888660878997691670566078979940005195987259493512159628198906090101827331841914429358969184839073862821059400943312264269215878469013316796620921077244799814690434355127994011220041638393750697699141479399553359747084811371804524490919966410379714725200415331414459870271869 -9247155945465656153397925559476432992975541781462281935278489123804934847762489500833913193183733932905776020790478662969835879365116238125565077744775032 +-26509487378481600038412836495388065888781507388737194948728047318975269277448073484403390476243134990463394380967295356958474984927721196047241216945988250219075749832868804186657201899994373052648345989716938779173325348547767647529160988985542438998030764420175306438858518207072038513664360905985908879070216069156102379349899544471658754952888660878997691670566078979940005195987259493512159628198906090101827331841914429358969184839073862821059400943312264259968722523547660643222995517768366821714892573665712075941552514588815849936651978565640166563350878466028503700441303440543835360084177205849382125496837 + +- -17010604274474750006607667808593883725990508452473783283717890546525148212376267233909567638545898628257361383837671935903199638230375408397752251127816717091041943873728526445398525706450929660366518707254053655364610471112296477865068960744948010561798109833411657930112293904378353445961131058136287425064317621271289456901138718557297733713446119244533144377470099270824020439428168481914824420861176457152299497728390918971852021025089592998997807574907789524112450146545688385954763667980124432645276563626082835790429598328230426471161191074551543308732791287559033843466623138171520961684959997180979203053477 -17319079025684619178510812811805110270463447771889107440996086020812918555191263705580533644731591929176480040622705607552852994906782176254877135818109655911838591767583157894999741648979817400330572419476101372927546509769818404491634583907246692993992514876697330603464497645633398167129555001859772111887143352351860130929715392173452396253437927361301990735683539169040916027268831202732178553152351117118606495416985612909248422655861312689027789401950549626643389790516560291620711705848717875304929186131258525831197192620523261738944873398924939726689336762464320190834794155527335576391767307110012289717973 +308474751209869171903145003211226544472939319415324157278195474287770342814996471670966006185693300919118656785033671649653356676406767857124884690292938820796647893854631449601215942528887739964053712222047717562936038657521926626565623162298682432194405043285672673352203741255044721168423943723484686822825731080570674028576673616154662539991808116768846358213439898216895587840662720817354132291174659966306997688594693937396401630771719690029981827042760102530939643970871905665948037868593442659652622505175690040767594292292835267783682324373396417956545474905286347368171017355814614706807309929033086664496 + +- -28362352496476494327713713233021518136860402239251781438945998574753662942796270292818595738100959519541952077905620088422871490191217157269435052965329201030095268586136492980900212955645939325800541690754639292707053269767151001292253701853012092829784482071789669480438026889625605099744553642207773753943711175375843649210118677569597324789367425691177169929576236753018329085700397911235750600921874606148324025962628852167093806152864269874177214562322576097931390470469397118268354868919899638376323751276807304678316688836173746719723312665764603485606350244811113608471530958617108833879194264695174468397461 -4081062111675377984305281082755054920741203741273067094307824323728798665450292976016160959354997082250970415737745853292134965575242789548167162064123232363464302136338349828801951197252612093077640695564825095503535921549690447893467349156939791370286866987224201115453216606688305427702274940837032716124925028835914047967887674858015919302546781010326385758988488478290741665427521820112231266659657169118374988259423444686317389869729817643396097464874333968181509317307320406521221309011946212308190273531009796563611621389720223920155554879800901239072885025170342349379379336047732368458185953903872634982504 +-24281290384801116343408432150266463216119198497978714344638174251024864277345977316802434778745962437290981662167874235130736524615974367721267890901205968666630966449798143152098261758393327232722900995189814197203517348217460553398786352696072301459497615084565468364984810282937299672042278701370741037818786146539929601242231002711581405486820644680850784170587748274727587420272876091123519334262217437029949037703205407480776416283134452230781117097448242129749881153162076711747133559907953426068133477745797508114705067446453522799567757785963702246533465219640771259092151622569376465421008310791301833414957 + +- 10367142604728811799331249565431331488313655422005202933702176605382043644320209814639311439871418581341534233560256605231366966869093495784665834232350567124110194965198962966795893926025854156729633358240069116588609932539289897499402463770167927610848388138020589286461244557962368497723086593344721146859584146431437967506007518396464517349944129896971137720357645026281243138165214047233258394590454775153944241555543594427555914116439316287902470043292624597940465373006598913770411505099332700167695871387948271302951230983772351549087620538875967635100644404345317626621438913980275970160864401622986870735123 -13323117602411502623386235160326625769048477819798659261203460002048250420188223753407093545503703207645050883770850457071863684414849353264890601744588860687970804808452855795406182324143949747985869939791374195222513169904228914579995165180964917538177994190229733465224857616114628815752065632238207474599531507602861647623695058640735949593381112671690796335596142010430124683781417828023076027476816068202219709673411776556090962187853799456968290579708094595903778622705850818245685205707447012659247018940946510378371952655457988959551256869060428488498330109152756599450626641948447980234503249330875085656261 +23690260207140314422717484725757957257362133241803862194905636607430294064508433568046404985375121788986585117331107062303230651283942849049556435976939427812080999773651818762202076250169803904715503298031443311811123102443518812079397628951132845149026382328250322751686102174076997313475152225582928621459115654034299615129702577037200466943325242568661934055953787036711367821946631875256334422067270843356163951228955370983646876304293115744870760623000719193844243995712449732016096710806779712826942890328894781681323183639230340508638877407936396123598974513498074226072065555928723950395367650953861956391384 + +- -25321281404861286799950777949097462701962113587443565138655462269365151737118518315058035825695270231347401755128007072923189452859397209062457461602335603630181865680063451525170253746137368267674863889514153713728814272332433431604233690200451816570240227260445028630591376891139306370205846627093813889699170594185178241812081296510140572331372738998993116117098817936927692238682202717231675283209016857095739468507690090676681400453024293870135659990528969837132054786661560150259115734877162158755858653364070279937027014730947342216816307219127474721622123875699701715404820384545693058511056735799834754890692 -15870257059811626693754498423136372480069134596343998984549199283973854570508228359295418026089909378687774627821225399931314225867711515277913855368473873536462450935842786002269065816311054834857109074848803122494252885020527074586145467185882674518032764708782999568002770206995683800833252068328835778749976046128872525287656002968632147457840467536682726059599593635219947081138082647985895437016641903078766878782632503812736486529143041369932038649270950453231711525943737962179463585338023463992816994328519710963267459007592689204838965317062070771191372220277256094361390952025057574056586665509010902583686 +-9451024345049660106196279525961090221892978991099566154106262985391297166610289955762617799605360852659627127306781672991875226991685693784543606233861730093719414744220665522901187929826313432817754814665350591234561387311906357018088223014569142052207462551662029062588606684143622569372594558764978110949194548056305716524425293541508424873532271462310390057499224301707745157544120069245779846192374954016972589725057586863944913923881252500203621341258019383900343260717822188079652149539138694763041659035550568973759555723354653011977341902065403950430751655422445621043429432520635484454470070290823852307006 + +- -10064759312484387184876313010284016458560725440641239737323234767636591183611201479885347260175161165340917225306019885202675573016295152797559983194160634880140345743489989007821872426587698574795394887035658449467358615185057180305109018898637903449135520486663185036663238956537895356325733583128141439025002140924158670346599492383552938312402521066705186885506193758499006001382444818328802338159713646715901977137011576113434170842422373328479181457354927400927267448788528116619711184792932525071391797130057189079431487557270366699175956757661488296856660145077706273571985222726397848614141194988258117115194 -3689074607001776735792882994440038588887963294487080609346609068733026224735369468180206799966728461935654851527895876039403151156669223687679382665269013769686991783531091821265184956524448064027733731862929686596729449196238312997460578818232100254940830907672953344544031914926653652310468671685310332327057444910423081752028857828828473637496272809899061573593874011995802487442092326045415689987885712749026491545159340468151000027397821404233369034594141219014219707193746581364791219277489927025992135462852894714639406751538919395016165215641239054420028872350709704191189169571752512626755385998505584006855 +-6375684705482610449083430015843977869672762146154159127976625698903564958875832011705140460208432703405262373778124009163272421859625929109880600528891621110453353959958897186556687470063250510767661155172728762870629165988818867307648440080405803194194689578990231692119207041611241704015264911442831106697944696013735588594570634554724464674906248256806125311912319746503203513940352492283386648171827933966875485591852235645283170815024551924245812422760786181913047741594781535254919965515442598045399661667204294364792080805731447304159791542020249242436631272726996569380796053154645335987385808989752533108339 + +- -4621513851362114851854472268081584822344822740665629177305004335694395719163541988311496405455186973857145245414214464449674464879082042971313025249648887349614046805778335573547862191522938924075560443632614665169520240664970180760364771373836023824195690134618554368845612471858027311791638881380352344527105480173917778084361560336490212845414303819150625355111300877737042696291233444311426721588476948565949641149735838580313236869041013210454558557732497012037162735013212361842433337324577522358968152852532145622765032318936569346015498130151789662274686368870963891262060214274101000058555635785833724062234 20283847238128227963042817384468009365120280641032764409860857066215336820785816567924217697745867082423864450685360959383940995237907453126362378908108545669654749698030305432673477271848544313029448526561606175059997663752601262173667861202924953502866611309434183496911206954880840674239880495147451496219568787221129244201657487090244435562896841733049066453539864301122516559479757096183362477594406691085946787803323712522074578611082872627361465163804239673539339633332349145205596371287028267780080937728455742966681547897652607170788637996317683436193829274172400558140357237480809582038468874094877651383053 +-24905361089490342814897289652549594187465103381698393587165861401909732539949358556235714103201054056281009696099575423833615460116989496097675404157757433019268796503808641006221339463371483237105008970194220840229517904417571442934032632576760977327062301444052737865756819426738867986031519376527803840746674267395047022286019047426734648408311145552199691808651165178859559255770990540494789199182883639651896428953059551102387815480123885837816023721536736685576502368345561507048029708611605790139049090580987888589446580216589176516804136126469473098468515643043364449402417451754910582097024509880711375445287 + + +* -1412797070596191471 -15492755620416346417 +21888119755986895161222137392796809407 + +* 16686841096925954110 1491135775021813104 +24882345731730524499708005167300657440 + +* 13262412958100188045 -18379071970155621919 +-243750842254847872704698616507823758355 + +* 889503034794263569 -16600674457216690894 +-14766350309325860687849239111838240686 + +* 3148165694020236318 -11771070679825280729 +-37057280896113409834434531491271315822 + +* -4443818546267181727 -12001052312087213799 +53330498839175802532024121011435050873 + +* 8305259347214213793 -229351169208067535459370186456659711595 +-1904820941859811670566233132773219565154696335396051029835 + +* -18273334758510166901 290047155020180552782039318570071650475 +-5300128759437251944808204783222405076790289915320785927975 + +* -703280433697652940 91110448009482115063492795153459771021 +-64076195390496041906141380919369524419358692517527451740 + +* 15279634596127882146 -220998726467849290098339792307263567896 +-3376779786638352686104608499923871317791563686466157184816 + +* -4472497681184076830 325612942672822430032905460436166528379 +-1456303131067722058341139305566346079551678140995111358570 + +* -6180420673489141029 -161157288800853703711204405567379740552 +996019839388256252540244286609069684717518686623358308008 + +* 14044956603588468379 10163190459901171254101452124764637970005230126310661589196828892266636678427020930101076689732526935899135126391465178494895371156141265424428405590113790 +142741568963316278148132287599703960511135825069792278910440475692913696263448088587778211787403889397993501704943449376875999977937418748662459138952952917221024170426846410 + +* 2133283347509865817 10577710515843519541178984366353275630877942729579274295972091544607384358263130633386329706527832990861547566574369528634541156662300858851752195966167381 +22565253698228972909216255630133478029433774404794962869038558824053350969301054394347471181756471783852326407546652836376109109470959746153989521923555764579738243072315277 + +* 7812722507014599311 -5055959518947106416800910724733658104378582281318226107212861190073091017493970778425583956006925004399967175604321778956828368132273155364830637407968648 +-39500808728232764770485117356353304373275127104839804121600969932458363071148383405901570717732548020267052999198017578112731079638156026910705662052515278317807704170401528 + +* -17560801708050275829 9842515227842383346577123873881045824143545509071137371075701856197189100217561683579562062872293951325890789283651221922663521213150065638405410634222129 +-172842458224605375239887212582262805312641302639067963604956593404910080268476692854082531021580381176489626536608405283010496488558204787140272050713264572452317265305619941 + +* 16743386830114877156 7347065846171565625701636575261347705942035850951855454324853850791855951431141198155170102434274509450315416946729031216385536668189501958761688618635668 +123014765528775807847206414290825117502032199391400884957413813554539073118943905948723779020186281150198999824020769031248882909461419778092564985979904308229718874140000208 + +* 12697192948029671719 -11416780209809507417142822520376617951137069007568339428552592261458272400645205700952156716454820410468812274673183389934216970221062627926131479014990611 +-144961061169197993494569769162151457365959287966302572862364500950127981616038900865036521107816831702945678695331078399461327412574397914795455218447174498277798426197230309 + +* 17005139720743105479 -29990519259587469661876904501488342396062731024702923152492275204626478246142153608222329335341363164148761307659972897552084842238285026253664841395295138667328930482145590159132144957515157474957872335043653264146346772142483721767458961320947069718037828473530001033848282453826154763424789967441239969918856795769965946388666154136004597297855416503729657013008165049478441197537144135384444157408972370236442813734429031404855591324183846423588871065272526864866155918285777640819778251612915859290336548446745308788013234099839998683451658620461972798204104633072664604846231692505409653434538208644416538994256 +-509992970306921990341332390474393215554862069848994183152714032617297815196921655222705396130464246880845576204295466273071779248718654338767559016551390771145212884412809612574391658668778295682412755916528976282396155832617323980694289208942491001345059122414240884660276842648466533488559879226195446807748573906940273568334343093922652142252689341425941673567630236228358747411926991658260241924294146562230425295426217833820067881064577380516936937782688004146531121831211284735538742160763820814174631414364095096099434285754767091040812242751724012532803037860394426031234340719537172735695313262283511554154662650333168783128624 + +* -15877530153400521290 27863984127681242643954505352420303514833683768731313003271701952957204538094398204984051331105594788039352443762851136101330385230866919393696564428736685568762923746771275677491379334452751710169529933675128178840986001684425353245791752781476028565228371147542431713985092322787978914276414008774443194161599919167210582437024618824616489802661351916633993681556274980075051797120207655478780052593534285265078265845445633803877185868676955831374479850746658711791169579387317321983669227930929736238215792068273805543745311609083833407544342964285215427999724272264458975101474080574470499647168865409458531868592 +-442411248181132450919255517905812929771246981404050821923231762557171158858876183536414772404562764742655092127161703706239729646027465795612501446223663310668879007072125975886873343449629108246953385822769744013416908613100114754904323190537317463286500657291202287742354250227377164455244103312266617146454847578457073139633297517170508179596166314955134347046515455569689877574427319658085169791949003021426613961459610227430636932814700361914589752207776142403364490846294795496119883683491811246550808038342285518518431538295199537270236275774546666026424361019715280652576803278928827199810150387207105149968313623040090578323680 + +* -14162897687527555611 -23016403916121951319848021112075986869602408568431399211927062304968548663313037929311574133954267816204873252195499803324830278637331653769648377216095499136975244697758388851688873078022850203685120154634090802825656419418077380419130449990938627982123188424119187922828250625318327074513352279785514062876718714640725789938556578327139793467832731546881422469843509318627826856881082450937188956068348931459011923844607158528494902828851692203126881727638511348944908726926619613375594042390434147948508706733126737304560579515324106834237197081860910657003346633962662773394999353766192391746258372744063777808796 +325978973798843759388794644178802841408656469654887121096165875654577046313115917671847505813174070119516580105483409446057747653173640660143855580491229746795572929387698247460831363721394707501497262525550824977473864621747159715947297817600227665840640555029633517390896890601028716769035575763283168066843141870124768085499453574902575378368669494153555135898430469356384416638130459557518713454927909937610851489821263029886989981438507377741962130296498574556444168140838201069779040087521405032426995145166201901368032136008107323350679784004016321425234898132080844200202007395427054392280809376612533414505539109579739614954356 + +* 10844738523441551664 13010289169828379103330191247192587220592807931898339555723704078985668371901953113936581573750666143303899278973814509164982887504269303358034042953769514772858989849512527461308415676004712388964136857232374888643347097138114199889581495448978914022318770898259317738823514820591042321773469959130347470144905381758960436645008051488666423115693738341045851119808222048272924385188356021826450267608127588500233526688704136268009202730309974485584784539415807259862449203760469406037505772435323036790641520939576046423540699016607317147689982042035523118533555744274806239272109508745089640043900389441390176681340 +141093184161152226992592021994885140117836445291515772908453669279294934817987511015413332614094493905560980363483549300117114491702466085602279965168041684355125886388302948336158133555051817733078300668260616983283027038746214728386770752826764135491650323133831923154477800324207350667020747545837613879364064704092093040155243919335078139087599906324684688427176309081290932504214653249366429592335409761783188358003723753633106574740731573467850133547164922532633897844647383889253777956821171583261238607289172489135768839436605233457738153233579088224808850428203888700116300637190661108848906846940291749737998056247719674749760 + +* -16402132873169057380 8202725117980211375579199554494319645475746305836527475507064811368616698686329266053570766100878145903342129595869654087772486685252653587846560946850102095086896668181099435964053041678323706849735936082196618754721606824996486473796843333331029865501790248862590712245450877098960007272754260813822886287008295409755783478345202299352891066800825979067590290793893933819913530599309037639082839491869155044147367415785329077864525961799400923643936705317921900308490987828345313709179960659814100113658528990241758360711799009722683007157350272749544178688961738222930753008443755881419398858537860612954576778456 +-134542187307192759584182063854799850608007421111316277594191532129597970622559949723743396309231347084450105499455916612009290113746722460358793168839937004812915757145655285798961178877391232945062437277255128401572171216279188126380587081673725314534095093062983435026047851041796084651601813918099532876684901239903769891552275465470747567830660442193995685219383258617057944010709906130655663966913354414611799232001438943448374556294933488875450563987147224709383408815994320229340710143082135667640802837699940654151297907451396297241124380508001357553893328703788960812706653503939250831164194874527033594779746890593262611805280 + +* -12094905083549825231 -7303327854122277566083382629094740392048421584433028903125893639493993705575691832165314461496849401726460344615713884253150283931509897329926825128629833541892164122168618243719393446304446866677253728405617434021389128710195093788280203239300086905325641224801020413858421914412156234316517981228056539721130386645649016559425091470643854813419057026759188125291655398451427686659900364573485593902992038773538760663063071699966278379037038361219424927031644750173900916227834573604566165762753650347331082640552394430002401423199016978155236550541225512734287851807727860645247391524620773399994302380387697957581 +88333057189654571362020288527489792875655269960629008914349561689924145109953656394378545526256758871407020025766992398117775520525507898420898102744530402370720932219749861094609497366188371774072368034971851022164946370916317410415503705484491514312339956381120953283812334833067601825812118392757289250628861166579446800637104996060739031010579056633535166403083327528575504427815713481850979373113173151813491831551023902022537957860211597622343157802805275942920911544696695931809085743355666792408029743911424760065578742910735408262758198787195579745280191859776661700139596074108035867940154338953640690242795671183308201526211 + +* -81618231044418675360403541307856740187 9751573706924018395 +-795906195858402819552264165081526765614024708979523739865 + +* -167600745660011044249531125104202473984 -12960244919927910377 +2172146712516287908809731894157839567367040369214826131968 + +* 90306383312124738690336097936949488486 156109477991590792 +14097682358164298866835386043901377722456291173827620912 + +* 126202800261728727198105694812165074067 -17404362862588500316 +-2196479330029905727399352310201914876903532806486592905172 + +* -80093647977875266525946940496137725572 -9499399805878278852 +760841584053111508349403804472960020663660465509267203344 + +* 304052889577333477963637861956318521374 7233536405885618691 +2199377646072361697737485358722028853038393128548297401434 + +* -124787646062877233829165925777950698937 -125798384154373172164515376683173327013 +15698084237137783175768362160964949930745617334715009097620154581879012485181 + +* 259623502197082370239517374851053110076 307089583871541575627915295134832918432 +79727673252974285068387698133566605944659309374400074880377824560177225320832 + +* -245358177397026033963771466683003477163 -285087883756432161967673595037725276963 +69948643556453419103498093570621669430956866597291662675473644085666220495969 + +* 46731711386059374483493216849082745840 -216522280665540473581476116002923812173 +-10118456728713381305690589407461434638634240429858378588644634276171257110320 + +* -301422430661955757433852743238845048860 -737194742467573013847855072675441356 +222207031145790358162820429948896977201848379524899474475604149595884654160 + +* 109781582310220385246795023904554278713 -273317662617851276579672019029762858338 +-30005245475518685175699313262818315773200953201653075289648004177366787958994 + +* -312236719893391897821327608828679767006 -661158307192284418474080017860142217763949256471548515134335997907628404839044913830388499435166012788226998900468665646723366842553747501004752506346280 +206437901167986463762021023207669068873036145952740267172145693855475451354717023377588805030022300923600718715029262618794758202955817341818233889201852381575043965927328029955969846754837680 + +* -134379788461141842858846278268259347105 -5535479645589936472405910397299739073641612836770238183712206042659632410776896398062277742229906915852933418684231779996404071421767274180368154310128427 +743856583805332082970350662728998610690268824090148728726850517499798631519601137183443104910590855501252539324674812560702657332874686395923181633958702249128106139207076314713649515720653835 + +* 278271843790644800793473851247546123375 -3845690285506025443856370771250487683891303505653819308540635173436088084480277686684743918745832832765066355874381847690771330587033980524869033600561589 +-1070147326395532917564114389205677334125034378502074943828571411806344559859053091006175486397820822872698474899835730026158782698085673635033947150554253148685482702599776833910878579880042875 + +* 22345490710865165412267189692679994671 -13168094845644809414256057134926669929759930873747535851687323456073141938879368460977723280750841588750507348317544461824280674332488497533955177541413394 +-294247541053147552931885013427268298282376074124656716577088212043667912662239091316191145352314750820026626159649861330384837204227899202392764926604802655267738710003310052268554637728023374 + +* -223445051950608517881717261787296926498 -2609806601119499724524852022247741111662431776874117401343811680374867931883996125145979162937751368655661775097445043144114599069842524778189198926688379 +583148371568187658089071213924575304457465978545376486297236105670932990897420147110485946155066725440999079357995678147717407410446012970360780626554347417807723098476525833332400212113766742 + +* 12604140228725912459681435851589379433 10671266866958584640992033560488052420339425977492420594983497264069815016478448589306666811246532193922229713077112601565462530332258877522384022088660628 +134502144009302626262781543880199144227907004673612064586081220538754991037447647926963488301214672345398823354945333417956344119228084327815583754032364976497975702972112644238248704660063924 + +* -221289678591114384943252477126208006780 20020996887149770966522122735176842174467884990518978494604707026520269232864200848420530223248762875769520715632742683760311747174524709550334825291720803698613541109690224185041740294906022358446325921538593105347423518731748623037078340006459454656405997570119591344894717789372844612253617591807770017562530034107842444403952657949565007792107071767260484233194674888488789619319597151367813735192433631007526015463229060702510632792171187339118004038505860316305860704455466207113207893106982258864355430481457640304138738182009363353560090082819036973601710432437342931523433079941958203038050750205966472435692 +-4430439966231074415853738608900692925851705818190624801199561884242897308817127146763274284287396980593383317678766559004881552228480591814939402896201244425805503258878061459604511214900528594870260206969839682573246490602076070316760182753341371682323914671418233629420599310422437691170629449435494697829163966912842611408632129590129483811802031178053300073562716917597174161526976287351465154825036851645956354853960835948518860624747958440181683978083391663149733813297698623499283645627889274004656942800842013709298338912226207338477579862672216831422765369078886850523202897989792734789430796029206661261129141144642117177625405158700499049991760 + +* 180785619668676509441152734583033930295 -11909038209406834057075682058438206007134213485822042209417443270921391661498900475635417780140585878716264253792335317341527677051828500780153492153490249297998660274828986996948999762620400587091118252205695562417522111840305140989214300921122857271717052213225664738544344394774362885331856170636862181712515248810239601812262573113794334115259873527539564296101166439562124016438281173202196876398090029995104489712272260608848551754611421227761245487365953257890749115194455096508613617028024932657498899001119282498614739316599704645009607294747043489655424155986912576002393048535846081096337705941547991821928 +-2152982852345560218506186041143281789706715672110278207735389192913214838321097754496849942223194392302524369156102301165660674797665128931611291246607346536492650554391248756408556789391955568308599431054809433808337036546281323840555452571430884302696950144068129601527530304907460164571704857360215834011779559395577299313379666503707563751314135201994045874159291100986903645360754621200008830207429980872071814202801994486961737459218017354210479544121100423399040398021780750351097082070296255480707530391964970754186799748521538525274241709676878827522138880241734356460339681718690408853314007343934035505873192699052380699509877559455199604508760 + +* -196121729286794751535600080816329923561 31755463535476988506639447113088283661031267977524968610501132544098607201258848456920865390506381665724254592728643925608893982794532243733117636645689751360224314774452374503339856173343683819017479955914451013484169313685311530532055735999039466721411777061709328450052490025363788971916050033904534189719389237878257877112162843506491071470067738867693853480174965212750301808781573369342701195147083717623066339671595077736036738235636996351642097684597005928843274525502529735435418805821748637387888409663397547514467435322454217015563134545731593492200855670248739786405074231658957946422903165662016649229286 +-6227936422881500100190187768375947805694946596622670066116457374856427496311253030141271922822486386675428302332027411428470488965226898801659352566022706152307022438261392466548357753526474097246042956052374187605144719189465046544498482461077851578811186829094445089366592317045580466302238653533114619908864036973070346979261546801894831273337217021756025770590122176562027129481076270727248949609326868225755958667670279949371399535144788247565199415296122873444199709788941984099349149684384486618280260678252604631431089580057102263617056951788273430713908768738965854953667135156866028646584137788146112300214498814212865170902491169332389942607446 + +* -149247491509558553673630984739524508601 -9241905448313719916485289537122695595500213295294799660583133638026091750542612875183284894676615989153030773719811347110864468582634048542108726080717551794580656021381515769591713295631818532114918070215760259364277583650102628486861397602958930509695263902920994329409932518607260720657755504091822028630927071374796474717671220452208310602827254296323761245420486376569048549643478954846020045263141546849795367522490793641049509748005893155533480849922847230018411440739584477452313387881413141538766185123978087175960946255649923135634987656065468774634483495944248865774633962770893338531522570776854773975281 +1379331204929344851843348280532786532350930013132149419346606977890849868537539899667631713548510207947097949976792337278764045110931774279794402312944786743575421497528669859045492875676005849752425421867514661792129580445000023570590786705609341859529483054902802038173138834528021423393677908655442991197348183257271932188161681770513283703502340499171444058119260228931558784004778969491586252899270869275893402714040693571919281494643765571068045362364213060063345212881008657925426024923296369533374671614852576576041747836643356665301762059898161073609265572267138950725010661453917338098901465732991316661901878681888138048552901254914604845891881 + +* -246070233154436622785727814428081917418 29761582253452470642591719346200231425423204062498655510037025199574178834762931489817919404889920159374886981199608181795387339523762458361385170203883094308920011218315748466148953320570427838912637152446837553950810011344492780712558515815917745810385725989241835877316836808088478276603934260581342710503593237081689944686263274319354100341139245512159619947319496638082702549196795236216458749363904150768879765280332386830831409591769966706351022328535490587838695167807967607003680703048770719240872629379640571077329748828739281770075441660330884779539288220944313294762143588847790653176774089774033399559617 +-7323439484151992757431054484912931979861244043627630118213112440051387392428853497035249623931234821362770902740177541812170377563064854590834087655133962963430877452052749127605572395112726398103244974178157574726551814002744001021805127518246639418981066588073652668879613252372759895389345727455380224104332342029151667860553645106555190741775758687650292791318963679857313030729683299101577207875499929500963723267185390425716927303375831321783415003339099100562942730763231688479910689887284950156875532151104047755803876078837921949287811575034368641167438367411569736575067233548122814012421044943430647665260439418887639347030312118291762161708906 + +* 203826295936164259559522643510940430939 428315860474710981601019542870649234168732095026625500771233691514247613083810271191136212287636290276352210600151884730196161003906066671915478570992925366265552107746965374246537358349673161970290367972281768471743836339191023211359427335141701167253694144280251188008871929010775436125645541749886873478179599464478734149706121117222690271210887178499620737860802605991262799781279373870647695125320153193063528861104479576369448865373971847676465682752435142074973627172566791961541105525781297462635428308325033717669972726101583722868689418677558787287897456521530400671342257419067050354522203242849353639864 +87302035331271280954456598486072605056704393103691656908943847729634903654600322194677794243221825233700566108459784062758955025931450719283517278054268553004951352280583820782976072352456972931479389375165173986780482062859853305469143408707179895843295115510597584169486406323435925707638987591151227843652210256611991940374072593149367903739596883229844326054223707236369465710416960023659329202073724249764308867733476242261506975691004092043954515337899900837434270833782490145948781128533218641649564543508314976001614187701395586824982250794852925954991265270537649691628899148413763865280007928191637215283244406869662872539567459561720369352296 + +* -5899540498246269366107488541138263797694914692322476860852796858749106720144552037986906792251681094769894732746138541066810195167688318229720888479512583 5834015210744942902 +-34418009003174534626858248456163154666511779871358190892629413477534042866009573638264296461516598238780495750056279721797403178867717911762916049857737963922333901125535866 + +* -7558198374656605586076446665394545534375963428962439959101805545423930654069723860456022097647139432324162475685494459942871728608277717748075653794546685 -2079670855873590264 +15718564882684481784074014915267371190416032453294568239793060140651422710113447422494938907375595456199203928496644205320139985222135619659630853564447794621716315309474840 + +* -9442744083812363570102321552182535031605446031706376100893354933468482520577272174689455502380973733378565213055641110431767353396963744600184737808983381 -7204974197101757391 +68034727473703353914019458883709211780958983263702756416891835054494728840771498925306650413027883039860202168095834137357212487561983607389479135319040711944281262212918971 + +* -10658732210276096534851972646242288663170038580488752611749460640657411087860047053151548660331707024718100598181073744715506934778234716535781332588396176 9193953347013373121 +-97995886679587166046252015742839992974979220158813197140160489510432960510418039749924861744197553021702396544307690217470606424904065359660871469041838900287446937257585296 + +* 3330096979672637104536573277593029682675932033891010715180474877149733802060455951241981993421466123791200840797318740359792251505430948855600408060492000 -9413190658845804679 +-31346837782105095097578725347257193539696338226258990009265748336528353873277500144838721882313026604404426563737656928378230261942407473822851842589487713775609448642068000 + +* 2224201331350479188470378485954814766783857696988331736807430786504130570570323948774102396158334805040994159865821844362926631687258969480929122732089195 10226747830478556903 +22746346139936030910929166328517425029735137934434969334578972386859485783192993228082340012742115893176871887387993591191632260444955081663604449277961804869872353878963085 + +* -12394770820700925077767705800588617445613665027183406054209162910642613421436080064653443098327137503596792411463268187212855350864330592654862321763110243 336135860956209890623046930607725140868 +-4166326961171213704571179876442248501325782360170764344978629523457550315208845439497110652079907652744850691289494398473488033083739905461347650605270023127087625641779424751335704552988710924 + +* 11792778994619176404079667787533709801900490264171877873621265044313417667869688303207909681289642260521608966405181881416781694320672906600599581862090088 -197661229068721548419113517262926820105 +-2330975190212228827672814304508257223671550753091700552243633152084831515892056240354560520878171696176381845689952044935988868477421447557890739834031207059212175922089523097911477486879619240 + +* 11608994516281296345925963401821217560860934641820086911326880657644311461955556832927259499969983808078591149768068360172431078248807463030805586293656663 -40654941048774156019243747229920736005 +-471962987694958552110784676392477007070112288398143925079396435246284471999814508543057304008480666763661066976653446723271982094424149279649226771823800871458389214002872916339341019732251315 + +* 4821517917539756801293776911844480642406562140007084392649374723119190602353617113036081438891134008988421494142194891002983491670246762173236312873933599 -255528396376819316172341014108564420589 +-1232034741571035406264710387186737842510579499938716343220834781077329515145216794636313459582844773420679078031627466542930137302257934575129329529129776153159694412903937370462708576694469811 + +* 7638751115643228563298483305056828584775811590562130101723525925933790010789130133831569153863129513189315440899053288261039147463032870669035935364282061 114438828287750304954799140618669114911 +874169727255956505920153418854946321208907128396839975975317705220623267360648189969313978740314703015845506506608054761304647627635292132043887080298168302864314697920637105700927041824911571 + +* -3653826017463740005170218884285271512636869606149686475539243914909566619638259666405831445823138528809165270360144267462878986866506114069923299116957450 215752050445782448772085819939961259625 +-788320455239949216234629350585027855111249573063377172522422069903710014529292638311216050777840734448624510386643245486023092483841464815987597578151663227035102742664709136512524899527956250 + +* -43242564273985683175827997542883970694363047476880657467026050730764924897992516355909421962249292250047896135687573746158665836208681548975073555418266 4424346097667245771102179669235543742385176589624011161914909311078645828684936231569739522607200308028372644149306431599085361996722603718517735348761218 +-191320070498733614136284309000213964486426347688040889144514933290125387693498098446328694172047943298442181705949005984031677324306763731212307716485454004382079159622650481983102917517993601466178931324415483972311904823997211920702201161092866663969163567426868740120661073974542958600768774774949607988 + +* -5093597555679260616199210906198149266592665304134802327659606846977583233938836318559188141955851256260954289429418183711191354912372372976165948043123133 -2240632735861652612028397136046974907251405868353380459030143407902436514978447480884513019736738955326732458088791830752499716417751919868492224207936623 +11412881426559848135724717164530530041659963797467536748076144863846600718211858527283843975968920120508569299672573958424908957105703597501013710262110218780710678312197455759181436286391257283676806548463507528765947919856827004176416634630489598937924092540289712219714362500246928243091408698274649199859 + +* 6049789822056553589237940133475342650218069231558204589924996117723031491205673061674252841792149409384720347601549237626288416453061224734057079515141650 -826416247951451524584060567988229017033981218652490450160817307801130685352465013890931297548015267655971295627931896259998420078888499206031390299169584 +-4999644605638856588581238481465237523157457201817697008198975191261856978252081380810200468420738807464233192102972784271159116426108806200426852134469939032473362689081653859652824862066224063273799612269941254948709760659691148103622071316554194507524610166457990087959160807415102946877307193349131573600 + +* -1175978338162966145239180473229656000174129248706173549637767835154921467129547950144109700900405904250603515318348888619371004435353505449762899046094747 8633693716102199391202401198009047492431980605560930404972542822133579985462906768067706391388213605203282586546130434156768523403030127356256666478340720 +-10153036788469908062299722391986722149392791936544969945546931764708792252481931153733789787389051773529081688846141949513463792442701686406966696738286561777611293604311491896230769507535896070984747493738525389837795316954065260075941524322954935690803870500012809797698319359975893462672845329776468197840 + +* -5083395547684319640767882199938390155755986838939007846911062687871291096073452055061784159768637502151635665247461348347470360218957222873087414506633886 10813098236568616588240471432239693891825284805405416395976866126102880121934298269375465735278296789484402954117593716698067735458182402220278016922449294 +-54967255432446073625448401244836956268872685687128644401372608170106281377801209665004925733448944141633739594240156882328181133879414641109484442890809130544146420476457200729843868300396656004198615619691952536924980482714767859804902602805398865249514544806725162402291122143659939645240358379962457176484 + +* -8944626200084865988157251013718979706166428261352840753194709093968177704853157211364231059892647813839391802007588961807572842923682104089512428902387812 3814836951264415657788614449012480613328314590744410079075164918748648723114236698412482309581077603776489883375576245233128800002373843611668945838558629 +-34122290543331565327874124324135450224668275222811493728051290368641401807963502623692504750924543845019291736982354932620821594287780848608647686402233097059022704206628297180782771812500512744911371653368388270442874670230118309469599458827222162362901084328510647514081302476000779049412605744638457029748 + +* 5186176030253526423885531264483408352469356233262336223619904269047786350470477526433506158542551137478071074193659876898065998079440819597952826155782068 21428324964794197485898135923805540163916541943812058590308650649384013587098638034673796533027113673143959572855470411726978105342739938341516634354246514986124789451866589211982659199267654387148420461876524076040233779391563396552267276880650559148637067641021059664960876301072636635299261389450890094318429077561092553337025096293793433968243940381587994428364726938534453507046761494257538813861046058298873206568935790790373886840765817404479239485444563488020955730741209738203470138117422899051269778988135668626686262669881048094388220931264751830393793846372816717368806996496715219806062282836392457741918 +111131065300898907482632501071313138589398597291097276435916516379173430095773463468344138866282820740991088290299992221985607057347883717514843661030457396422379155394966857856069231504805779448809986906434617741485942621643754096548512120178021034054648207248963478122178145159262707381679354401629366698488021743300737044695960363216253889163551918513521913593214414139637549577618641974388739304727218804595402055185824193445089425262833385286117064481648652550355832014346131722965510192584901901111154083186713580209077544982897821477349293279848852596241762198202012197892321827305803333334823616660229870976569043453639028059771892706354703750763908127611939169337399882784092285804830644630059487027413697220038110815990084742241055099963659761569486906596326424 + +* -12615422028124847936088012564413126213419674293830655240645918456932358053670311316461359727921727680491520480380615359506308571290338231702217134487397730 21538722931308708400287621200994476771789912594554241036641406577761480056366647329031140922034590767810855360008375309986798226712928670905618807986829790199948665185268081173685941421700542631395958882077936923141152528333121096909688700106365468854487023847026564219531968849793109908193037522063952753477768381591929787242143631287330811801315216116212154423972654430356675401769729358415036943501470085182304183033246682446978634892995900678975109490698283226559860736462409705544079080978470202336645384768211440438501339641775269445439018148409151795830925198162301321965042997632479354427154223366199106583051 +-271720079725309675925162538296715595434811519956795637977932956405490708202732964133816538801099235844279338645471102896234318181092598033040518838847055114923365599862266767493227393553801736813141780001130539648588341196802606083178208108557367013886856183999712817955194261262279080641101769944037282423238147653270651419282545398168930625797556638625301898893565965773914460998322350526545278664715332414172614761548301364063397364632709194713561073496860524124460861314674679928692398440036071116570829193414179054372604203478369755566003622621281005164747628075596444178089558747835994702060740334079222508147598079351187013336751322569865313532407367116553748939535664259669808534100091049960040092785009707220249025633808590643620557093069849490009472441113874230 + +* 10381022953674450046578890619826448644067144294659610359943634722044183130638243233110364436029778310048006743033299956844491228999113516347401915490861208 -20974871685432829994714153210121536409377362402944992609230062091789259307033495284524234519701670462495676590513192861649457148897274608767543942797542628100823017887236899471151903799837558453043431373811892813126194662218472834650841742305925226558315372771353677064933578639099452438843500601586038910108679737480263349221244638463171088589123712367802373159421798288708123925853179931628847579314900787361946716531755600236755527982132768286927549323465697241340003870259800347640599467922823203446834792229595507968354687630029075884034263531531423883902851487995214646322431057626558858528344843531280263328354 +-217740624416854507100100919338835880277259264187442792458843251425095703739537223785767883764746809214920580060316177442387941385712712426957388995082877226019966428812240179251716274377143798847348759498926420314709056615470455134468678662646006408843897699718742372199854223008996321568642038054564397441209859567556502098420151667437837356649730396360374136203172669776530655738388121236079327354422138744456395348910073462618440421257604563050031602590345028438897601523520973759458890228893913090702884911857207117714231568437403212806578764580006787626657709435954760239671948147344463295520930250155876010414461245194991189183956653772752290656063730950237649394743456230607077768595983629559996700837383822873994717987698780007691157576205450973669241823945091632 + +* -3984492646329789478973994496812455855595578196959138558282015917391108383154917581748539892089090551298072688793487597623310815918942283997753800645644511 22199897116873160263914990610762123553075230334116099569358672964060004245706770678771431369917479502828754815568950371273785689812698287446020480951417047185190067265849637510591502642000414540862689426343523077229502494771352820057572619644085930901096534031496492870227890836816886496090287321502805172125273822231241073590840684742085641304915656543831190976008986490532066597410386596132766422026234488163435487889876791504407434387555507637783709991326338482319227500686541368087892665100076351075069628862376686619537655838590687615291898971286325099164241688147975845320979841704002364545072665891829427213069 +-88455326811459002089798581395024759975871889172872668466370443703433800509268320055453743803627754859670391415348970278548381190662701716228279482045339649051139909543850883613464992501666524385524517648069873862957915620016943364950043289963237718026629805297916194484838158010754666017024585366330526135823515744339445036315966714684052345462172808299142368905939297220895721123725415007532441824406115746741972351142687017849809593982432484296719999502992792447259391592152463664807498752410740679664044620898308783634092355737296495489953554685938970593890496829484673393665321572846542839714620847185428664388282452532264810310019327395691530430185946743995669191791841546685206884247468693248673484055915613115527492005264289557719000245333079386593840592027314259 + +* -10672574004830373997900438516438419278676753890756925443116289034080220708922677740383425352837266631691319394850521121221541344600832530724104047804922665 -7307684417326792807224298894786988180161884427390942431653062127076829842696634441114228528164049031680536693195116703321494895319862805505304314401000204515985676763063862569446064343853536464020413910728442475032187317639476018710375702206456631041987826826225461927793241495220512935434301833094232834266749666697332380140380619185254354273073522191066457437931022783436360434167505326773192959291779779370530770935758482422581712556111319611455306383173529090289274267200543081481693078804068524057891845603351773722737987393428313340760607600482724483853560340630587029610437280601010173185018227638972500038072 +77991802747865927212086621295493124451256238920588746597961055391511562690441964216934615500942858653797884925704270904527938466874924049039962754703188019915846345804228044693122758075602494985337649496117180241872910247079655077012999375809878184011356481981590430241786534827516536543734645410817621964035091467871491521760928486006653992134635010794346993161329777270345449763927429735191213854873362673179799811714902439637861750855639857969259787075469241319618538795721956528400353086156169058060112255274542232054021662809196965752800525093125763127895334967094763817500702626282397394521201385439419885607578137159972521677923972708827090645776826953976605193554447841693259586575931864396484621463004541561908426383260772786784541411548146173991869741515701880 + +* 1420855003086789510813111205540636553863493314684153860389816109865085846062678305775289632805233481596171530412925552158799875183492757047174905459819169 13897739053062356545217161606361735964779941697726983959749295377836209520566715597422965426908191354971972501742952706730523748574796773473606175934144970768662226027157110240776527834790487577863781140089347362129598158760833470434895693782503529955845076709376071972727346128409008293671217324995682020009675316075606538241192607139905488719485728099428376369506685875348346231688684483781160648420909364963718027571565217314827671844485031440079254478598236877074793221578612249882886835580737423192061550370069895525711885220268707201966615936769696379335772521903910689934596134239331592980694745008817040569590 +19746672065138309742065153069587996891492444461032276894328314121573439684229636534026409362850111716212254549198595854140809664451286626009917828620279583631575940837712663100442879662416765138504151063632823014639305658882804073655537352377258786105147057375069447099908107785635606190515362082317465738205179108333064680370909383338688734129396788764959056886328471374018961975554190739706996184818378586233017775166959010668462907838359485424792026496574369912033757997469014639705459505746723512361959074802456098328538419933637295482429555127226978561859965498424173552676019033370307387047798600024901453757451579262061785051932535359410827170361533603618131510421439128567361259204833501190218719779570258541358012741265599985490513564378203502703406698160470710 + +* -25117824099635104147178796272946098711514362630774369209876335291088434247131228189812265510495277875692804180473811834186270331245779845635089547499275113671007257221593872123397418355506777725721168216892830217596134983713752526559153149600553468865338887605949011743043425900799896245185282419637806859906582214420191794114207677635194054239563071023206500505880052007267243210206807805387341085613436600843317096291021780624738422589234020279836961194869688005260369009833026575446099544900581955685627511787510900479881434909308757027825050977932238481841909425598834367032841935054158448815026264505726593064239 7846111496222858966 +-197077248428250572361351389692146917243277049539013604789802566767174747369897711991559940484392921619974209620152008632450612546796556905740493507885376190913893140368029841033442857949219716681475253727058707723386016055991276120001690579154370788782636181079931076758384034193266737114305362492836167078199155929937891579224024229182935372106924021709421948701131654358516297806197381566809357458374057189773041520552821330635689748583803171230633654728360451100477472934847975252390985102859262992904778849652221553818627134153578436315973777720706502751232660284910468721430874674021521629540714057383398858244828214000543075116874 + +* -12000343217458212092754251360179138661969968218789048702097501439124892987400633614429800307263114371624489988815324366411323242909652002510513570900627875514001409309670202055060404640758548257776155562167062337394219073071639153822126554525439988062676648294108951003012550815746564810508912122306190725453386412796036693387315128514162061147675205485143205925649214342646148112549805850530430229663418469577245456944558387628002442451042105749848177325651852669794048215063957689756465788955050513359977166122710392613631703123491357791351447110169966270916789849428298930624807758982400706608788793481972190953569 15463017349709835150 +-185561515374029078700596518575548896805308728003103939537818954646551372890610870275966055765608887701776880889777402229764948269089126750201922167386201171243298907675542965323275634529293654817279957832652909009385491998537031060285890512199675273422070784691446251899120095880199298512230290860589352290462643231396804350623684034400741386070220057232978556614620855818271117742675632435727751812101639747357642295230273344552327870600519422276996860893842363996198017494117619585153346745838853026029459826407782259598477529242420507010652705302341725948095720110508044256096963772599572721279996322424269691990173052929936294150350 + +* 20244597897909303129995907707212050478823487084391413473821544089492035634291726811145005824559631386634261268723753786161463497881725871168747275110149007801865428978596190887145324535224079986377522166727137028753272158887188902047835658826867304220850429481233026043496635847568448251753504834367809877190895369288045026559783632709799678639927825194847005181499299410953860627694080906167346078299421796974815616608326704894611151743720515377248152215241639534004099341398238713597030368980166731393247619511322804984829747216779359780372801101821087516269912916462719248736442644433057333788741151270815989388229 17931151643499274580 +363008954869078360197158713265773114114991766614027768774402465306840646219477262855625957403406166192075865834283840624408916170935610374573318606346031792128003204902147985329385955814330782527184421959263266167048755628089412213360508944817963403092490479480264538027768728303095523018598016863928762335410109567604756183580676503045557867957273324581082608248341332512325136675167966306268035077761004923732568405295901819511346235524577361289712297365403327125212199451099538443576479787130510546755789504852631291774614010584650672707483555436445926222945298928326313943231688436271883746272589347954697213098866117569339490918820 + +* 18134862906191691435095953372467318196853760384894170022863300447691250350836421337333332682828557871096554531436829166444150586004379181099133295174348038948038399079336722004125999533719492457544642570217406286811480006881054375314838605871238868968956868878182133492469763282800195060849734382249696543089869191257451321764806079423169235271658993054867624410589213892458246001270123109841429271429275464249821855221014782727398959126117031823977229309775211695677345378510417534328974531801634095862859684508240122911023047425473036305928743193594967362216559973174709883576295373749738633873828863608550295977368 15082354452174510460 +273516430292774638949326170314933525797985748367549139070674899956657807928629067317576809269188258819686207094298714770978509118959142516619521080722291318367607601498107007447014759288176261262818034997399866363248136237609824401265450913244758024085739876914482935655100890803279961929047974391299795570244708811454483314898873277493486428279875241232025231140855860469097028388778917980779775554139507550577255217032521719099071084956515691364008526064349956553916033914728254580848198941020806723485184338914882588931083516851849558411503129184026079582257756707601984686901646494090820169212279581209612798749779318126482639269280 + +* 19213874382308276075905228027166553836726993832150876980655958901416537033385379180983129528081628446454583401834309285184752924794893846406622935494758142810049493348116192315865522516744262115026742103678965417868790607689989205765793528434388393584537260717130892518011447327847533083474230074174308157934463971640826422302901570010591182715932658037868980053012095115562188975692530473556182305847290196895478280679341869546292639446526021874910117953225154204035612531584978136604161393474554294315903436682283787080297348697922389355209790646124024053098888687638640826064745026930980189268652291562437512941810 3155416591710364359 +60627778016974262766014671335614995348970065077989108071534610098195400001445248886220725085881796599270026085183075312353388418711598523030563716616967792282609748819081238929738105086199457414615236966895805539596649555457494710621217412773036416007129418290246899690911654008867819945724649185574237527152410775686803449108977881160831441280833577932476667657759420192656716352190871667386955409426879693856001112340390304980532208752863058384169885129364117656404549585836664647784765508649117301622797243353610345828189312360124462238989888436478381583689386509617357901461416012201469794664889076397809504626996523928173064949790 + +* -6561903839860415551587224953276060627466820222543175464705113686962550773423611522044145975606965294164125376820288981286542044306677764776675868357117109664125730405280822770267329297542599719353907954399688197248115043785617436343303277493146049939491224480136371029084354063731401026459653680017632996944506546122253686805764620116169065663214526857151412139439538335533979733329962892417175374550305659302592107472151941922230309227785266745974334776462642676959433923828440435340579340133192678341787895007461237846313005612116885419002449356480017828933592324336731295317076205553526568668826499450826560670163 14908715577157091280 +-97829557993133908713082095435440645457469053259814412551982534425389603663024461131358343104414088618618030154957456050473312402460589893359522167472060177968099538846750606564761307960896264958539903740023783283814849937681270591589750181462708056758506230073751440847913386576449367635057595344744119561166438538811561109125506233466453974371464999669336530949393433719456191822836826214814780222021267726528396849558417851727452246676857867278196266042327956933753121947589485377148388716839519782819642328655117625818256334190717182923260613562191698788004591479576661108985313450029332968584240383859113741485244318702724563478640 + +* -10378013547095983701124686671659666242518351347561698092030999302329372512356819420877395264401390796163955327080881297568412490286247154759694714275858127906305200295043241717769593877683535229411640745872559018085757273530771413156968541499388413497221629366848027355125816131586610997516488552323667400115617175682996681969687885201321292153656071894385242141321468096793766926179134511319941715949712230831768643024119693594235207988046511542691719002262040067921088838755337917414526554050602539873232518619281766327369577617796816586064895744680567067970817494102948032924671421242699225194947982378019119315136 30004910492448871409155105619400474385 +-311391367570036811050052853596227388481520279736812036769684195465110674594690412517879149770622679377262288447706750813509857551308594851067359841826754786725926298013483569424123912020079066150719085450400229896983461212531213110847425940968466564079253939695853896434719530729030897976597410468081535234663568150722646854183317007227669132983719314653861536414057481478039579810285535699518386214012059191958557306338432321511585867535008319640705419431310336566447165302011113284064246284641707577414470505948868362067233709611758700034131461348997580441628136979257037186480770286846026250437141175360847735150981343952303257191661069675154710791360 + +* 6311357747888359229575837883366949670125882865462293491587368290797766017168248637163030339387377997726585769250585768079027576213724941259801478313127113803503561717311996500019522893295813684259416551410025111443510215766297835872165689077882298506134885487991732718254835036694083204758447948541157893533099634169589161496492972953698758234452126564385255035294546278732684663873459439615228706684138982066055370429797835904846166362278557095045056472775166294675997320598469599722704075215700819354957397052721573993997624711445698656580401684113096559767093466880001548887739825916626416328760047783071058963451 -212654096583990292869707082365869207538 +-1342136080095566600483524091094048745061145155430997807005186206704767933140306297188996797343723817220160636373424666345108189275851749622201429179882167381735732553825696482751584102093819432866729465599060815670807282181979889263381844726842751894916887860819210652174987999919869623292751389157233409465756974677789790982740267208982768450215563288024088369480574425410032306456026930809228182100949940216614156925537929648841127727165386031716586596638254705402653861723407930666152691102484352058909219619985877341630210918347460471644327858114815713557305185589162775699323253049631349906791700893878999711846225062306568467992135934882289075693638 + +* 25104391676237653962996674810232896003857294806799086059884413856421530328279649263948893056601611073815235439115612155497964541323584159786678357898152394779494741995735881624055133443980324145256438160990490767324719276757840825641421547232460969806196141938571103617707677351907526127993230143577974386169402623023560579220343920203666762052525898442578990183400559087522259053245822827313206196194989095468393682721753147596892214609346047051670610252732846805143964713621673722554204896154742594858056891979146566683467510164875593192581407047920719605560716270697985110227952698114701527191421628561835164291236 -205991315859231724218751687295926841150 +-5171286675233738337789203670843122752625713948587464573381323151628930998435518250812603433784823922283042037694290795352461861058217142213862777203850665369756106838860420507328654214723398688455622487003912073924323587826356928211672752672052670663842775836967587150049181838707784871641183683742967716787111671792311389517753578360293551031540853470719098360013225516593755039537796518619542838794169319227197212817921098393499332268929332950035803734983497370378852859829228973012039890600437082235032378948656232679080766068869430262740600476498399803176452431728914806536862849281928869092524387549297345184969051926149006293586531930828748109161400 + +* -25971587288596053786734900662696128734726180676323130693160397208008930123341700520454723462226657743365779183466120836187720332442041321870351823609046027805781414454998487673927365486893294110931852680018706479684281928396163669935417207859889405108139261480861908067489849403284000981453574189898304616775302917687860062501465417706095450121596418236563421425311420755550335597318818628123183624214438801254105808079227429950505879366254661664881055965092586612702279548151277733307180663770432418397550642136953750720624507617115504303570076531620003848642167562950736271141440609700821621532583527124386811144839 -182748557863603655835821910989658558236 +4746270122419629115710902425435990509747636609113505336611751359043717100752575149404352359855260443259846554733621122684788488984010741203981300775978945529551335641218319619542248418128319220383298229263331638090009313676486209764655429828385994626323209879925281409485074778611946493692237774852428345451174837474328995186242262565013937544898941834362941815633750896882758939509605799422068815435202904271722442099465950700886702949580264958171808372530471918175963644209760378395316412115175988232945569517230829200985652504383431054550902852797293952515652017940918628980037316292352828228005975466732028971159947131994753006597870175664981312344004 + +* 2117427896392849163304163145095251890404997781812823978967013619233450901604407363671467658244435728579079751353560538034596183240362499870272373308111405924505741579887345118857908796509418246599428633956038017783178050402412769812823236255234302205027282366926174916871858199918908361186936687654278623156607813451034087735179167324944824913226799346886951212979149617678949292799645035425029596869092844906629996914674904522806258932192931217652241231736891642224851547474205131131019084734780208254203537633402057673465583362982905095029133132240839391503135932501785844503813910210348239157828902668852795945482 -296778668392678698960782643314222141731 +-628407431508980610909134894336322264939705333430111861505965183839156278363647883745193463537783397824947515214540990712455315080515980803996660089847066076833542492719707493333185909990202372284811233272987993068106356248349054482194817336258302692039392400931536481136340269417905505366385505196886218794044229758585631131853635721528813397816307666671727692971421531381290925317161326036075629905443938124481334173158440927555118173661486114828362551889594188958723424604273078091320087897088472418346754088900034854230711982602435635574895960156993014703292551046970069204857846207328434544990709459402656908170089318995291341536347275682867153109342 + +* 24743327715258194976385899813930363006464428087412805068703455203318769863096919192538751530954777047772548306936907016751357570434930538612382851621309732767199276228580401695793317612267605312672263736938703887622824117576912830029817460033437752668221355377879837833796222831371174014543622739933433581963103361464022058091243110136610854806189138108937004805781857031030005354158991203388998364340053773883952742645161560754545458260688560269655272249435540890073696261770299845722705104648358053080678920468895189601731801025555650490534399590288852165862135571140382055044665678298182909026026068995867606241201 309156501491030456401354118244509785044 +7649560631695275371386748526795333430293346807872366006552933839286343590101586516802834568317627508914888989005968805867728947519409222814667350103434422356009252082456906520988877859152125402282765775845766265340707473525444185795403554160270722809642681642831847296672303556012796775586274347178092325226458743113317655523655255626670958156216225968018208281266858684283741496986683426354716284780229004376492833583965647875097951642088252875535823145900129967026856898970545720526282798418382467634180690243423325770596949644122541224189780082061715230852249880601371985342796525016176048518593825361248232406051886794538203297084423942036889326397844 + +* 31345149697924857384985323414506591310628538098830133854928154990821019223495435414394178930529373634315044777562902565397455028894455733092896622048288278424884040917250546068175763309233883078972879622697667174865833277342334219810618450605650614585133187005110148963483824629405555603493157452295284935004578187488673124814714326405406894084902824045787647963172437833905574178160343833139650913077173865287057167288286708807322607983179910358234015596109655900840652230258122852488289951986129788952718105898226951651151495867246384586164892018870981480003722043190639707903266193064807571586900961788679579912089 2067227180806746570739122295766566373146995767544546241400900414826379465803168632854028593293108913670556431832056563218709444199286888840721753894461468 +64797545442006646811970698282511426059102976298051534827345388707272469591333019870381858263624490336448197115781363489554169207652559213486772008013638214870324260793199674746523791257170452738018910619029072942848422098770309928561867618844814267276213608306045020686764830302020953883994906997293368193331696747777630621086600981981357507299729947717565760536305785574555255589190221698706036770081438750974356437738060098906046001271392354762036427049946092656701257615490057677558059955825843182799904828201890893555678855718728417223845757559310912618029462136640226686626513375024547351747669476392735304999046232068947570708757930233036922714350584650744960478326257916948676866148362166017752159953504981324652709881831381637989229842766220141292801807437886652 + +* 1965759082776833678304908699214846485256126608825750175641683294458978302204367346739996602241053060915897480812220051082619942907491598551933638540412113496542245474287364500698693202553692963910123752514310355402167440783023542848697962967771951714434359320001430281377747193083851165947498546085410216620013287853719686698746328198021011905482303248172483782066908570502837009924228011993318265674390462360820566174204659723461994730913995303015012684826295802887547970851558451858623353950391701673651959262042520584275132971807158231859672678070714276061110616753309305801080136339206017351200193800253572481467 -11092241138073130060021642325471345789108575712118027611362686690749327689527135459714040658411176246054106270789083336195599640521602432629024562630323934 +-21804673765518097879589124792137157558586438669762099454880024920520894260754279593873244443852337739758694535682558790532827482894104906218015712179591886600693703465749571299271429989154199263793230178266758966678432691901731270899259065726530463438316383699558373053423999416350780342222940065486831353604365192968606300436304827279383661172824549131179471364227618431414928702407510473319879188990689163932586727702195573766225861364297410904859137393184592815970592502081722125458353280743087607273547490382023433724488604177909671497082747464946083901888849483505451426245881736990810339421864101129619181017696837017966116165703320918568645290788634265522956017905246042460811062666193790657969385648522736090098231379029903772234867701846824572274796526421531178 + +* -4067457132547237558852016696244696525033953641638067592741078194074861352472861925779476293767777560910963786727886946479865734639031042985368829200802420611189793957001730656623744670821921724417176679009632346904384261431052972127975733031277489967119978909321422086102208644766894305071609385305464547231057263658903212521469801833214062476735046735467944834107695748433481665714184831786462886261252526036621257865158497049125410241033365487816324425563483999957660557670189397770488996359512245971368638615503320507431381893539767352426795415898379765583574977542068222040889423739693921998717145084904555464058 9635268828818063607505341812331931088336041632536136269505180222913464638532245578488168867093853062326136774925531196873279749483997619950077042084971972 +-39191042921786100943542578352486285322085069425292685238158202937549417928185097567102615300826629615520476316505465412722375794150552330462353356124896483739321653441446703127728441315609093330694305784991844511900128172079464896650958648496336601612657347012294121239821167759496102233234525084695798195547141521849769350204659392602605928907953707277320590923278178152903602506284861018886300148663530071056792375593665422754923886137410482547324901798328311927545105456397213670390651819229021443747424183114992653572959318104053511452473611466305149349027962240989590453237778130260105665310067480846969449221473610614214933278048389171979184119355459010233147440293881252851501522689209874112819966647846701257081192324007280573826673895648273593609466000383382376 + +* -22047771987573494284336211037167956208924595972749016352929724093971147687332865088249749580556015503923927321586913446367676445848750229391300778587369581738560634537089081840938984779012854694220894920437076215176060179241185151442003472788530160589267677502568156006531439509890061829154786579353177129190813899423306499631144919702707240832059008168851983259611724134448165201725432622521420667808597545410136493805873769372831833878868603946583848422310946469083400330960925084024624317866822897278934924368888332618046649078771617892961267312226309927786691384460940015979582201446635756024251269978545916298961 7481502540911026808093162425787184755732317118387068406204973030847892995155568099553397887864257088525242568880427634318737874025160499293315047534753494 +-164950462146458057264341765173378248123415893870534274075422323606836246718538063890359159423074703472625232511667875897808555123518162244263016096627959208397334135559180524195701526029092734741010866589515172934676451385008535538102832400604699294088534999994990970130226363762230944961249818769566697211068918154629209895730969522747736738946126971914549491889482944152891334838234907190697109929512401661529882587076352559260375439428815896053844621297552401396168240947357044985051323834074355418902009161796886350497072010833513601114819625605048943438304411954380599728561071485061414856047768286383287807924135081902458690495890129203192613070824670256334683011083767124852354110322463725619194174195587835939047474059288568764831570274891727391545546467943319734 + +* 22607201423790553279447786193696575272983924506336369475058795405894123712509544256099524616893423762658394830755129501447553593365768543361107397299007141714383407862976654294384881771985218996697067215804348472693636567074361380875512341556932579903687576929186215185312685712277482751425466251201421842248749944123326048360909954588266368306843116245625635467041934524547983478110533044085242847795585598341867070787331785945399446665919396062565614516404861115244243161694059679274045050270546536781907061002623188435269769778378780371158624481539046590932125320888745103158180784231722265376331553893647061533815 10075764395489719205294189472045365742345400155046712954334138069917417587273618147303160957788995022989479371576840422540097479703418600112174202202728054 +227784835187493343385594867881830022845566753253174983274076326016001091958812135049265213053390506720261776960833046225700903422206015373488419693650378821159134369608830936915027161415300759990632038898164509761337714774392506802504397626551196717184785586630245704512525844329038355790338277254618639554796026366029578805283659986085947726260520495140332204643887370987929304924491772630534558682402396784510750317396488402942581973350428066695976988812610467654886227733900635715495731445319565054848075104982244316563526232071957624002266648721592744376122065531440026836549316222728280595228806728872537793522244957258060730038589170810090676474272044568671474692128168357087077816573419470273384256552275636517940058764711467508281344270125535855785388198570146010 + +* 21997874907846585575969651776904015812729615626636027149446399573806943459105370044846476738175828244018281160136531735881270437472624605280356112191272531838028896521621800558410217146758345955334174583639352151367532676985598470747138461153212653362188252002768647808852054182649808145379073620834551216386805267446360709820441771932135218282126427988826945094538034579367527908530151926679515746133600376612899354099328788736038811470295396365432559354070365548930628714861826464935305416998192532029724853617023971964507955475554955277722555849603716733374588174421463022213135839490633927005539569058361144905451 -1400498192750070094581812894241996480373581610489471746158083224360249880335094841398529960182484181641387946900090289855375996313447832474435929084180606 +-30807984052781257825246153008277875918087659020905755686964119182052911551148620538090633516362197112383237624321406969368641524681503231262834662890145617622830207559490089313283375890353617292096501953380469351747504928597461154633889236826060654886877907382241867167198409355653371944304660938495445848950444683274236538890057643038410268234731745456035923559528706349316582901179686671568504971088561096469997823300883298811440849031903066114422309644669680078733839046643542078157684064686933779591609758494599988463628362190034612412739669041368897594110022347872452261447359402810277413572637740870748949093642723240662839444216981630862346445890780016393330114883270596630385367407921496982236074288475142085411632630374714528706189796772213264952893973677883306 + + +/ 7853255233330224291 -3336928547114505419 +-3 -2157530408013291966 + +/ 14068934522023857270 16292006600125740074 +0 14068934522023857270 + +/ 10985143198741137410 2820546847025452162 +3 2523502657664780924 + +/ -8108344024060626734 17657489924906565585 +-1 9549145900845938851 + +/ 9793321542618752251 18086526939764980195 +0 9793321542618752251 + +/ -17146297557940039430 12031974228591547856 +-2 6917650899243056282 + +/ -11002425733929018635 317353195315898710776749437474283191162 +-1 317353195315898710765747011740354172527 + +/ -7045772845268193739 -89864936462331315327547597221973588375 +0 -7045772845268193739 + +/ -3519209948682231610 336646025916394184314065151550367317320 +-1 336646025916394184310545941601685085710 + +/ -14043580588957562420 -222396907959379818229719625081455476397 +0 -14043580588957562420 + +/ -9551193686327136711 75278849342865310446781730521816023755 +-1 75278849342865310437230536835488887044 + +/ -5159693807678286583 -271047212583781769863421872046042817014 +0 -5159693807678286583 + +/ -468628810409465000 4096333242794181573565417750313678008664626221486481835906739336082806890901464399325558358976385254948318232837795406709018062193288308568894691407903633 +-1 4096333242794181573565417750313678008664626221486481835906739336082806890901464399325558358976385254948318232837795406709018062193288308100265880998438633 + +/ -5244261521146873643 4921965954460062114436531997676458648483262898583452823054595308486273272424691697564822554202559195140246785942292198280651275061385828405859241079703409 +-1 4921965954460062114436531997676458648483262898583452823054595308486273272424691697564822554202559195140246785942292198280651275061385823161597719932829766 + +/ -7155077620310044967 8812163272024170253854686552273644496512722723120222844613502711675792665532423218585794856067792762737333036865054171911349107383532358686613366666267909 +-1 8812163272024170253854686552273644496512722723120222844613502711675792665532423218585794856067792762737333036865054171911349107383532351531535746356222942 + +/ -6816314458740361202 12075686527410596248188273566406493470578404199548730085391183041200273406509336777783445118538744079686672261898288597639090345296674064493442338947240247 +-1 12075686527410596248188273566406493470578404199548730085391183041200273406509336777783445118538744079686672261898288597639090345296674057677127880206879045 + +/ -1890766085089344496 -7408951123352997019624764400646833541081866912262454311449242781355699966135127365870537542305320515188627454777019144990896651509964466134772403923321917 +0 -1890766085089344496 + +/ -17760474272160473768 10671898954663586353020741847122236830297651230263271051052178674502913972809415507874936117140992859978582852208528956402833042707438860216609301338079639 +-1 10671898954663586353020741847122236830297651230263271051052178674502913972809415507874936117140992859978582852208528956402833042707438842456135029177605871 + +/ 17203040353218460773 -2087162439393804055245476778327445230668947133920484097696589818636894139336038424853265430700117127209095408439503149361955520500062568298413951113007815520417010757436097548975086796164496676384718866185751276003374984886645837833063272909444571294955778352876647505220277070946109654595325494251711661808952794932834009121950804034627054856954463330684354292113876259596011570415522831755074832239680022834732540878425796300024337145992774113324576942861121693541507092307262607720008434123040550199401165667835993232451530119579950363100932584067714571588474895040209606410481479858029354372704983248137195749442 +-1 -2087162439393804055245476778327445230668947133920484097696589818636894139336038424853265430700117127209095408439503149361955520500062568298413951113007815520417010757436097548975086796164496676384718866185751276003374984886645837833063272909444571294955778352876647505220277070946109654595325494251711661808952794932834009121950804034627054856954463330684354292113876259596011570415522831755074832239680022834732540878425796300024337145992774113324576942861121693541507092307262607720008434123040550199401165667835993232451530119579950363100932584067714571588474895040209606410481479858029354372687780207783977288669 + +/ -669078238914427842 -2079683283534812796000190145696654156804430677578086735229530182730363039425073862859919937645919418081821481495275802097707156965850792454067873053943961704938117974314269568411834037549827330778321735965757574814268024643310969429104824266062514450939201453648232801402417276979456869834703061150723202264463831469016146310260625377771326468627250569024458585844135123967133684658903385261562998370487428947011971199332502829621504477139578507890165895257527873153869666215808793006714467381179632834850697420959155528830942491523671012905291756947015477293335637719271764878661266856465788562934083754663149622043 +0 -669078238914427842 + +/ 7327608361251253460 -11696871058150243071087622230096689496181624466205249401376348902965608647385664229774648233384515557654898456058685820740766304363182039373157014626506361274439760335671626498691756724181737933201816787306863365879304649516313681157144055793349950077334014446025781487249267998958541998722387302987853596488202173446477234627919885268339967510098356435511938337505060706688248914394292741973765745281640737883810857064447143652281758869836897346742791897670275478332406904414773228555812810686551346417028168391700814494167715137071887755902595471083839770070956620494992791342919563605887139693691074220696797086954 +-1 -11696871058150243071087622230096689496181624466205249401376348902965608647385664229774648233384515557654898456058685820740766304363182039373157014626506361274439760335671626498691756724181737933201816787306863365879304649516313681157144055793349950077334014446025781487249267998958541998722387302987853596488202173446477234627919885268339967510098356435511938337505060706688248914394292741973765745281640737883810857064447143652281758869836897346742791897670275478332406904414773228555812810686551346417028168391700814494167715137071887755902595471083839770070956620494992791342919563605887139693683746612335545833494 + +/ 294675307927425004 -9548883545732446060252072829565833006884749934819836721462955213854827688043000170303649263042149519854567992890424662453846646328000352698376041008580371118805742958892669824820331301849741711055204656684075622499980143446475914450482604391515448002257899292716976191315018524528823574850034894770209999447615978958086058881855069519056370913875205852377738400734853568252684386414540818555760214483168738870291995504569592275824832746669965860852593991220739290892286776371571179317949624919667076576237866033103387061005489130212740740367035039239029315995502459013799346342754507246725497131746887762156527202924 +-1 -9548883545732446060252072829565833006884749934819836721462955213854827688043000170303649263042149519854567992890424662453846646328000352698376041008580371118805742958892669824820331301849741711055204656684075622499980143446475914450482604391515448002257899292716976191315018524528823574850034894770209999447615978958086058881855069519056370913875205852377738400734853568252684386414540818555760214483168738870291995504569592275824832746669965860852593991220739290892286776371571179317949624919667076576237866033103387061005489130212740740367035039239029315995502459013799346342754507246725497131746593086848599777920 + +/ 14347605674351973575 16781108313723465798753322947549009969874803839178924211234447140560213638432451682705091051257464849827979202356053729300113370530117630421654608371240669238603643549128642643200948947465613904905607632005913664436128507306181734648889038561843797684348733053132088104433891457988693229945931772136047702319895617567109469705064237812979865949533824478284021673220084274688077361876364538071423609125747407610677136416454538389383594373243559248761308999294417964846813960148782443892858983199711111035244418968070018974928177662516580063782128203214203321005133744081558995859628472663680467632605080081460859254457 +0 14347605674351973575 + +/ -13687180458303546035 -19965807258690898827399047857329749541944041888361526571512375013574840494217451526698619297248029675659091093507275385543103914023886879938416253663550378748995953654394575052030527774451712268159052917447058332864628753215292671454945836228787137495936035900814206996568155366945096185932229795907560606361081170245972586794828085080152240904698184990372898689089164281206741448028676272453255619644103230837682506759429084083781590844603079867247577101977877828608823440256009103689824572652465343135798570848914557962085239341382269526989381779235955669274286569338275810475113610787290242075588029043316765795281 +0 -13687180458303546035 + +/ -18925243707105550517292233464727657082 9190307209239173280 +-2059261271274988371 2728041824926269798 + +/ -2268729715267556753620172461333386061 2091801429708129323 +-1084581778674907194 1902605633041663601 + +/ 240105871177433673480858499581307555362 -18137436669813972164 +-13238136984210147236 -7622864679137983342 + +/ -262280851402380072992262860428676408462 -16900353177245877861 +15519252683754978805 -1413263307002672357 + +/ 232088877459084367318095567773397104766 6879279671528853911 +33737380734733937751 5401711328350210605 + +/ 28127709642032836544705999676759725409 10932535648386303235 +2572844081801336427 10537089091686284064 + +/ -306937202176830380291551701368328539229 -296436198830834971825518518346845101512 +1 -10501003345995408466033183021483437717 + +/ 53182351345555180643155906117731554036 -239211638592486301383761475381736024507 +-1 -186029287246931120740605569264004470471 + +/ 280879521805922529084106054377533635149 86259942955448859132329436894499980462 +3 22099692939575951687117743694033693763 + +/ 270479016313808755540727122565065208981 324912300098276215771804552548480817149 +0 270479016313808755540727122565065208981 + +/ -36931767825051824504116995033958612864 319307981922121038618879980001580483695 +-1 282376214097069214114762984967621870831 + +/ 107405040163562716679444439233633957914 -141652913538112981614728925754765362880 +-1 -34247873374550264935284486521131404966 + +/ 184790583924323275311064238756309559899 9405763068924930772523308542613843828189472529194204592633223103364008985533829727911596099340788835060163612329614413033927995456162272921833539208882402 +0 184790583924323275311064238756309559899 + +/ -232146313651929815140292892576650425884 127874042542498530491272428412953321188339770863893691736473766303033699167382382296818126363236257463426694846772750451609560841554300961480864245159756 +-1 127874042542498530491272428412953321188339770863893691736473766303033699167382382296818126363236257463426694846772518305295908911739160668588287594733872 + +/ 8728026917336931021644334642968359261 -4025737651718879467086249421940400743845718511565541383201031450532496911741243591880148294568060736532264934821412351158153056198557273134546519285439196 +-1 -4025737651718879467086249421940400743845718511565541383201031450532496911741243591880148294568060736532264934821412342430126138861626251490211876317079935 + +/ -21247806463473594155821742168531145877 9874623814693251335287095436063900170315895560443374584548447111313954604343515085076726174459237734570523327188441412904116251434030306488421699399082287 +-1 9874623814693251335287095436063900170315895560443374584548447111313954604343515085076726174459237734570523327188441391656309787960436150666679530867936410 + +/ 286071012976663674972497644839812778135 8117963470049102990205107814791130105126121536354646949522748212681559213640860255350655907507344188574393108661850973221437234442503731759547377868482253 +0 286071012976663674972497644839812778135 + +/ -31984072490103846239901254171623218449 5692584577100138206704178766874348327662006017031045734962382737701553237360572309466111381682998567474445884555969155997528896962520199289069413649945515 +-1 5692584577100138206704178766874348327662006017031045734962382737701553237360572309466111381682998567474445884555969124013456406858673959387815242026727066 + +/ -118786321298036108045826901623494422286 -13104687393529855246189161514016061367829206536234756076440701117699438900634808212710497707688898041370523685367660273492555927331371333817584456923437812254937564791326005224466260676246116823279440552837098144298072108885163702544652442198253976130758036230979750526665070549806140578651660117367824058664768255971820333797528263566114021182845197585116811208610765478558726491630221448907739229750716647693886681440731920621825762708622208585945445501310666367635378515829380161368994361558247741326227866296163878689192696502960274515396418704173356117106149549811507516298161494760216869495774889685228097554343 +0 -118786321298036108045826901623494422286 + +/ -243504960196882294351576664380857968998 -21587937181410571895883243216668490389074705797891859245233974857468088393972175783210897703383951091997858590608339069503309630378648345491373074180865032523580053667127292664354305946146706860083207200717386662398495588729927890691458790805600368902559075452315483293077820819619965677979632040202844747133012382862591603017396270452230496812292060122531819615885125296991557805051862986461441377986436031061301043809602913037623299360710776463563079053501286633046825608308134411478010516309133407298319800662521169341026639016773610132691088293480775753098006068864600975258749129292334356727402877150032641596013 +0 -243504960196882294351576664380857968998 + +/ -232153120215337418381368832017470265942 30369869998519160635038439385899036531726274899243107662193573776561025086030508184579231487739439638990032661366370630132003795988534362627493292132764049369361254377811101088096885616264297545296183017838644655031800075015262568240918564555865889092855166242732548296118581123607728694721796055009838837557999873738362196508994398492388283391000153200151108985088421583241872508945550713986000643154770628316896294096213588875028875159647959209523043158262383137987983806573391208706198841017176042216759223869369001945418516819972673432636326816040752486579776281338052626173120273068890045743383055115108151149136 +-1 30369869998519160635038439385899036531726274899243107662193573776561025086030508184579231487739439638990032661366370630132003795988534362627493292132764049369361254377811101088096885616264297545296183017838644655031800075015262568240918564555865889092855166242732548296118581123607728694721796055009838837557999873738362196508994398492388283391000153200151108985088421583241872508945550713986000643154770628316896294096213588875028875159647959209523043158262383137987983806573391208706198841017176042216759223869369001945418516819972673432636326816040752486579776281338052626172888119948674708325001686283090680883194 + +/ -259060284965774946595436776839639567158 -21169437398760148980356190208468018621145492999838564155598571671707045137252408058263155870788514173361612648418947991691931017566701206182096411121805142388703524692384188551391839307655250861401059757668540803453257330609840260191124331324376455691656017965418225557221321063048013134701879342357330536176553693912196231407222911508076200690973402778332647229863066211873019193191946948707769594605725700542299351271155436324879464212647734978854222464094486372144771948169422844206687239103281296767882465060755610256361348437002775612357787333990788742488962974294617778863800504758143312112916148210635178700875 +0 -259060284965774946595436776839639567158 + +/ -306407220440527520801126245152934343544 23776904469041910002999929986640006577749806048379780143306978176280330224333859620859305982085584460504452577150094943864349273103240498588108336792264540481132832425653265046674475461727185564436156797869482735166850827691928781085870354146365177855543550807539877079785312955597311473866546268560309551339704009052837019611095284429480282839060448969927393502922337023493529232757886223011597039473438267362519775173848264262428693937605086393282678388857244739695710639089112115821491015511241179389623611704128686301802869094211111183507607222840602723592206395532989319304742353354849715544201896997806941793031 +-1 23776904469041910002999929986640006577749806048379780143306978176280330224333859620859305982085584460504452577150094943864349273103240498588108336792264540481132832425653265046674475461727185564436156797869482735166850827691928781085870354146365177855543550807539877079785312955597311473866546268560309551339704009052837019611095284429480282839060448969927393502922337023493529232757886223011597039473438267362519775173848264262428693937605086393282678388857244739695710639089112115821491015511241179389623611704128686301802869094211111183507607222840602723592206395532989319304435946134409188023400770752654007449487 + +/ -195344089877883615473604540022799066156 3662747206396136814780716441352319011395583880744440623779519524132715621398922492090110915810914165138435343905061143614931233310311653264955206314385113727380922840781471233570237875610273247058183529977038444774890036917021403875269188605058323112809901006876685005810599540694986877767813641408707144817551209014119080068666341132956917595575721029282915413432788003670940873484464479035116187361986468817764174983360339341760904395062935668506379254915132158079325547293981795720009601997338615068852024788929328937759791477545407878160402653256560646902444406842976796122784294159073232675729939439754133572694 +-1 3662747206396136814780716441352319011395583880744440623779519524132715621398922492090110915810914165138435343905061143614931233310311653264955206314385113727380922840781471233570237875610273247058183529977038444774890036917021403875269188605058323112809901006876685005810599540694986877767813641408707144817551209014119080068666341132956917595575721029282915413432788003670940873484464479035116187361986468817764174983360339341760904395062935668506379254915132158079325547293981795720009601997338615068852024788929328937759791477545407878160402653256560646902444406842976796122588950069195349060256334899731334506538 + +/ 9264891165355739565190184416501921531110089392234481682785663537978152834210043205322426048946857682928713233093454298987718151514375312698287977140085629 7037874176512351849 +1316433191754927814343121270936699199595699278862016004420926865721433832616431797693201713416985056700387482358940219476958754936526117 3725966351458345296 + +/ 4200307032774171266112621290628586259036811354993551074625290503853725932886889027855146647188583607214617803535833936888841730747156500044307235280341823 1284895891758199540 +3268986273297707492966046995014614916998242647231210827092764188090140439361457237468815188241626314878458369000334553644889671709204514 686385384999618263 + +/ 4633581426210593840116821588308941075113609939505374670564216205139790589811590001728944069668539972864711443927654873778749890460797761818777105143436310 -2962967916964005701 +-1563831116658993795279542101101791208522139405163357327177345954575873595639420625598557391282445499923781922172975036467881769387028636 -2305528351410817526 + +/ 12717552468498637611743035736272812618998169576927971614333302239794587794960622996075191568720328790968571121428403438724885597787608886055291630047062012 13314634913503466421 +955155928128432692468469826248574845938797371431354312846155816839032798664341688948121282840353830287784283921940330056923616110441287 3054936783550538185 + +/ 12062294501874959722722826646961478850437392058690935960962546316456440331759806886386062852332964852589981740852222339370133938328830299239599161668985128 -16763340409247566945 +-719563894032763567901416565485723597235798340734444183600832520303761799320002945942723929422229544393458648487318438477478730610009081 -8784252901936442417 + +/ 3044847544275515285523698330617680562098304367055502952571345800359603621492376638228899902071703358030930686556600015395721763778348780143354036450288026 -346996692199522190 +-8774860431593785670059820118794469144363980632658525112469174051232043754755411530275560319174086707639904288765947602493995823751245651 -238735003065207664 + +/ 2509912747961300370421908848463238376627618739583712646450568707494390365647434163992939257405972498499963653610934135188560348612028561201284997136414467 -103539220471632131715513528655357150478 +-24241178719797015812049763065806339919024561304709065585003455368093218973185992185403749066840410575011438662859226 -96472873114015658715754743886276195561 + +/ 903584156603757584665316743639288345339689627067279191353819345586609786559389840933430566508676924801215959188934167984436460131912383813696092800782502 331768089796013775863175470747253254043 +2723541486944457282998823558887230609702011905460231484608706829329099534108144630923237396663979818212898288888162 206235332161320659528701690369799443536 + +/ 5673315779132616764913839385550510388979090539569302085655840858178135330568442902409259209441411672724870197775936862625748838206738247202566588717953664 174314453261637397091517016624017866910 +32546445076572334426551736182476341541840123356058415149120265601377736759669652080905093959104527787429999911903291 116692109360780668718335531020688952854 + +/ -1042174796681883628708939541235268651605049912220225874766096393016094308719465113556241387514699104237113811358810257758037463299809921161083533137718034 -222161050831883826404696847740663687900 +4691077903977550484144586938760520043889189188344556635834217114054282381408390083982959968194813858480017307031811 -99596643070173880773679158795261931134 + +/ 514620852714641065399283188571474686623886013323210978774935263256584816823822985571618774823719005380283570413767760460152207111047429811764462688164598 -249379962778237023348159084806351533829 +-2063601449697349844851896329155481297775287042332288194884672211603708613220802144442049441368964502372230090742486 -95322380544583572818596570976868394296 + +/ 4269600385716644031357008587416832262633392539755787971157199376810061034292825362806383550016756143465036715717235613514913396047178769910869994174370663 285417980748490192096950554131180300340 +14959114960171371128118740711875740516756226597819143207915053559296218628291049058344647983358827408473567791787490 241314195989544958480263376470519624063 + +/ 6257762944066060502889009610562625466881502649685643619949901701091938859953222566838296129679249506961399045004819501125771060525963805468605899184284857 2403901388951678895074087838271029994884292204266026258064846860089259517515609086601789420041871201768080771502802345608184755582676267305500365461987443 +2 1449960166162702712740833934020565477112918241153591103820207980913419824922004393634717289595507103425237501999214809909401549360611270857605168260309971 + +/ -11993484600336960787296361130458404039545119394875472848786275539588308627572632551925095053909141197737731742215007587172062102742960429155637516154567478 -10601606227266649325153572936490025724316912773656025997862708934485961793376598665748396482615905529759961488391911046336857148489489874009257983913322770 +1 -1391878373070311462142788193968378315228206621219446850923566605102346834196033886176698571293235667977770253823096540835204954253470555146379532241244708 + +/ -8099353584218727402496117482244315513694479151831901237119469501251316930293170263154474101121202090352876083340578448720444998702069748972927117400667238 3544314361305784888681580682051379178614889611356920001389193108853540699763802421224233206957629368752496825814976307915371579698349690086107445288844845 +-3 2533589499698627263548624563909822022150189682238858767048109825309305168998237000518225519751686015904614394104350475025669740392979321285395218465867297 + +/ -2978635751180326260478380464684854159186938695694356726729544155114559498149136630265180287452727795680214540092030170434165319182046797389540988376220992 -11044560433803724847345327269572156674554851849210901400046696801586678634230888624140822828361962132102290537138655249676050009778590939633587416463610415 +0 -2978635751180326260478380464684854159186938695694356726729544155114559498149136630265180287452727795680214540092030170434165319182046797389540988376220992 + +/ 13340538348613289212198643243040710073441733600226599862111857175429956610033827913042723029883753278922272401272143183977342753296395660841047710386806625 -12505012580354283714273335370179473407347376833608196161777727706326565713505585671787333065372262611606233460666323939469037058474587043990269917174242835 +-2 -11669486812095278216348027497318236741253020066989792461443598237223174816977343430531943100860771944290194520060504694960731363652778427139492123961679045 + +/ -3452499360915990528305851079744650498637858290301947784400848134276013289408593218875969649149167701046100505360083493320272180164238716241369492874982856 -2841735595132421145030480470066423226109731223189024018875906078709951146850527470934664784555907173906888591079310297248898808599295604861461544021697908 +1 -610763765783569383275370609678227272528127067112923765524942055566062142558065747941304864593260527139211914280773196071373371564943111379907948853284948 + +/ -12837726014506093148099404481188410844577204272048603185395045047349565142592994407238657335674129551317417810603584063833795494760777857363295221929632592 -5400672038070763758928059302309515863608512780764658336296609520612929827128710430832496604060142364086061442480255889774184624927820479974807630834638627053061502551989845184646873008822134065011205730341955561862333543891572685472831543459711809182159609688999832404181689665081467086838440500602969410420032314939424736094367166550148605570451238900811316075091818613634658092771400323953464835124475741159313252328998401218032670762735091435255199537815920258090766640501899817468173756745613666544200642455036991967916355826412137450931256645775349763991939102903236911444655964118925598580974976427195132959063 +0 -12837726014506093148099404481188410844577204272048603185395045047349565142592994407238657335674129551317417810603584063833795494760777857363295221929632592 + +/ 6578123342051874578301723989032639872736755816906003001838468752363433436477439123182015773040480790196872643618542950300401093404997067180920740266777136 30227534847135453106177887048045830910966009330493682541426640262878585964314188306388368740002450969080153107270718818194095421631888634061517731024627240634873991685067246458822125591350775226166707968203074411304172314390138208453777131423508042090910198631120304798774511984974823253005683386272038346156403540545900138637964682890471751795844957640450350739073841005154467987598913264762974403365908691068629911370262907886883158252280853615622312950774196097179304856495254526220900137020823369927850276669023897589972466867500466273084668060310345544901231841857904496215091732346299276586847479136514947400671 +0 6578123342051874578301723989032639872736755816906003001838468752363433436477439123182015773040480790196872643618542950300401093404997067180920740266777136 + +/ 12403944923950846249231475663107427245396603223802100275104415443763524399953865335344297718622152249002880852792246968794311649331174399649925724682365691 -12888526328266820415927145054786787145889555649382608442248161845973745960691528655550360088202102799744537495406020539568745771467756467183613368156733378808019164713600978887019905699871417123365613340681673916006626966649924170996253273073733852663021384681382051909095614154557333966638418401258055946731820806422207820872515345183005729150326803604361512295955159943694411555067679327842378949865992521340416781113735936437694218150384351520004302169462501770232864597266654390198260481651327159066598217103147752784312005857617885666670915444340917773878003125490279146098981790584757296196254721882965728596834 +-1 -12888526328266820415927145054786787145889555649382608442248161845973745960691528655550360088202102799744537495406020539568745771467756467183613368156733378808019164713600978887019905699871417123365613340681673916006626966649924170996253273073733852663021384681382051909095614154557333966638418401258055946731820806422207820872515345183005729150326803604361512295955159943694411555067679327842378949865992521340416781113735936437694218150384351520004302169462501757828919673315808140966784818543899913669994993301047477679896562094093485712805580100043199151725754122609426353852012996273107965021855071957241046231143 + +/ 10948926687039959668263034501276830818625159748756208667794639002017960671515169323966433371280908617310031465455790806503858320395456591407286777524363944 15489368888944605373650389556533647343231613227589912336942045556008797399070972592836944702007913599282985878262526911196097512734678645706417084706692047107683744531285802197270639216129852640210205547180574081479052109734758880839510339349237982383997307379952940072013079809351763653427560483156946719035644278683384662348499193303099798764213152890786143522348717903997482162057330324665060570551176318195643585920374603137371921517627526609290876303058743280212715019326447206678291320236859616579863769668727778545628289338458285997742887048186232356815344233169348134282594953756462483528235310248146271138654 +0 10948926687039959668263034501276830818625159748756208667794639002017960671515169323966433371280908617310031465455790806503858320395456591407286777524363944 + +/ 11464576772189320419261665628466949872551044398628568459420638211702970737332076526558331636045640676437486199912474581890841933557822038200509907802936313 -22467560825853172479037421968188386486420879368653733048793951227030601094142939166908251610058992921146938740897141065727494001469172869273657374887890530345239013255211792126218659779037493454354985213639803588241949068595974842726845550080803721118359501094994460828504625140423100683538018569820187798018489678377090273255263020908779694768888896507998040871877361662889436371169588814603494476932238384716309803290555316924487478792149178773379217766514013266795383492328680695659480010501872205935050314706513127406414046031915649062646891311739001532048668245929929298605152418405630093221583825629790103566902 +-1 -22467560825853172479037421968188386486420879368653733048793951227030601094142939166908251610058992921146938740897141065727494001469172869273657374887890530345239013255211792126218659779037493454354985213639803588241949068595974842726845550080803721118359501094994460828504625140423100683538018569820187798018489678377090273255263020908779694768888896507998040871877361662889436371169588814603494476932238384716309803290555316924487478792149178773379217766514013255330806720139360276397814382034922333384005916077944667985775834328944911730570364753407365486407991808443729386130570527563696535399545625119882300630589 + +/ 8938867625495530153186421114297747048040597826172184076413640033054738602015847458230139602813250873350372960139469151926251587994978713194477868118851140 32078375072910127937267277353555359769886046039319229674839329288579279294400524798815635423380870475101996161416889444654312193048344262195882716890119398121411812112449203794619431500846260449790771355510015457009235934372609093350279916863051987911050185220414029935306290325311701907978862607656115228781358052463349741922708496995681137879712967466047121123132315277071458202185149672157950295326835513685167006326116804121841993768353882369640078584371838201520000042076939249781755766637255305083290727182613012935978235950698890245889658464214791358409590524627630419359189066595841389501082705783042993303059 +0 8938867625495530153186421114297747048040597826172184076413640033054738602015847458230139602813250873350372960139469151926251587994978713194477868118851140 + +/ 8722682937170431050833187146084378340001260037987475887522226657702929497643381574300242436757970258082372353322466430729560481174965555877218498123235127972650642792093407571195465205419575987213014443482975328738475261835806620444520634849116095235065774422144459742716160248838471185163698000228745859478158104063095766855794756463409807333016407203497851105737138480852953389782284594500871170431951978989370255765328498664056929197514187424012915608945460596104718351333193752820591185791608965252963085687083606400804224201063308456591357967802486377986437493144904666736661926951141864058594174814702200209409 17412472792254218853 +500944526446044913245854704095754296476914947449773601840238266000641308362538841153234090543298285278030309320940403936325404298435972916448525262079072487832742313980507689645024811947475736929391921911894727993931203210832413457668815952782782949239897550712034201783959782842323270598876666346488371315244615931371511266927466746002593260083813695350859233907674818312186201150675556646229110170730257749054381148001252368819048293261475322932890858943640868603051493461480912552795132518076648186629059596407623028993206501153697237322059149347109211519809718392500920402797396035977014151301 13717640401691531656 + +/ 28355649069936648964480577421600521514076854589748655538478527484386692848040560920378270353295750065522502408426901852657385194373002516782134462463403619404085937808470896616603996134884288656277511001166614643845546560002367982733683422291378278295503032478358197654941019479109634084898326003113897170044006328598213579789377859798284810297339455100733353158770429621612618690067430491463355522146151298846940191011096497912423339688256950368577997428204387561345591508638698782496102293891198658979297125431373501637906166414269821235570899541062164735301390814949753951678580623524363615439367796948526661114273 8633859053023126034 +3284238125245742016855698157684933898976923158153988299571059835059738533454483417199061360717949675767825259147742326435714596823475742487511565640155717557287585352355724513814056546156034557507060565711152037955657280498048473524523497495066421726381315710512047045674071154161308957202034177182801687183543929989744017034272683534975659632492076519296858840261469439577557507171156150551483889412665000065287799665100306570592222219949067222441707356112651967102951988787499729560738632591896472356839552808850928783757605816255738706660766653939585897846868373880810762740938652879077841365007 8480542101902822035 + +/ 695804210379264643979583966247939985121477063798866833769029083451619832314943028815111717357207629373097980486420088049066444875218919515464168730784010670913213630418336413807551517181427706050442981412812560910674568907086122536915068847926867597666432252300779682993163510641754139318056267379096886236384894321926559804092457910184502775704660359338939407870665678723351102313495695271753077268239934810069567863069912893064736153816317584230084541663742856849855885433861537874945479324326871485256528265492542514387227516996450338546971631790859699133015684902494112907242906699969802617955900147949678000702 6681774530135049718 +104134643759851814351147451693200005177949603843035721223567744815305881538588822665583702468671678158123657386652154620049781061631961328262453617035662063853637317024464861861946266314086911857166878173456014196637218631863393572542109069255296051532325807248516303174349857623773438852122643289226264801658953137748153296436411578898337203701260654377419646372368642303334799023574274991890918545934625239992964365706183254509198057965378271765137299881104144690382322308740670412140492093203922319672781421992523823019735773183797196153517598307995654976625437636949948229208840097078305544042 753510048969320546 + +/ 23793846839231724395641481096168600821165863841834871677817770057997115862268350281729411419898015728222764385314741974298570203231459954889026804768374989204737469979104608191592488617915776065920825549826544170491376474603565313317103382409887303248767928207436610335921923003830631904883475966155908755916279914050461058817604412376332860234542206749755476863494453482771627934874030957391163592500144106720406855489772599751499515034904792907346159848425450017939743424553771285003901024075692162881112993617392057913260097840038314973483416617082316428401467936188718832437149607288888734084245887947167600476942 14251636779665730013 +1669551870223134151303903342674455433642542678166026329327716199552424270739267924882373474967130314854205248773422856606186537084529373808518256136223710801915037299873741750309033582768093390015183894640465684163714303454407585554851774795653260663343812169894506517621409762650950455752948417517702896344204545008148927831629685230911115972567815995382195887880974480058637498609515045000261275580213450984765289759768577989085446697886224919418538617830152205807256503108619376475835548388022966261347231830397100233962945745245326537590040744120991719469590787920917431072137837209281279829978 13846244437708747228 + +/ 5157109352853249971318864071100809054313778706912280725237530653514718017885603638808994268309662830522966266131723296476071219223553003512249189605920498878997009866938158487186969488205976122254188731320527219934117903279062529020046834522969499499006879142996400588997334123741485390256248878878301063076286830584096271875934275097457786951285271567141607668681320623976130528011279831938224824782139301311433982239041406699687231975177586985955350651094601995816681979048980035660883809964491229857363116776116215144288428315817606755704237448380042774619545204701291306469557276264456990236770565662987790837189 -10013270743141522218 +-515027455577944332776599219259294388811187969682242219239790206822600106136108551964834930674442778634657645567794824061780492131991288077428371773780758138983535394992930162959647831148714333399217585802721010849103285390761891312665489859363343081240130486926587552859706337223282598959658826457912110150229463850079329163597771797337822709730777565370953422223408595882843118286956939988955846229075579330815290845722563420243422752650828997772215730429207767090119357203878107337884921705286570049639435686342039828502912430702417787311741719990865608911989344378016685337998208215872207554274 -920760231421022543 + +/ 6494479986256116541104296929948745881595810377963953264991072983439840586308068782167240611739100078965050725068818882183756338815160036859027539278041681781661292616356111680897493765185363894557565156777508945778492400417041556530072176186473028646756075186621344659621007329706013704866019055199752880400583535752226648517005237843095412900402261752786336175778761394870284838510406493285835836510084377115102990988520712094164167646661458280428910514014249834132126729469995977022911113044853980749549364759880528586645949565758303087716543654543586861828638440479331739588108393797432477382024670823562517526646 5646044948886972124 +1150270684178027997729638025310141697535651329313468129284627631873742535270359534840413194117215304029010709513909008920532146218787185595227269977210380838656683242728568521891853665038056999715825712357842110695616824097006318159870253326008969365677078769387050477555367309756230306675796151995910498740461667569866473279089249936015985131524549691332989894205425117195925594673141706705491188937755834316410829046680432307112608812689099288880996341318638564624566449876699247700823589547245730672763432749619618807750355174780947014272103851229064036660484535227006468477136474311066305263226 3785857158573214622 + +/ -31360864680221422030140415094508859225037184177278863690330048646310966010764143021431123151508104188281687033827937090369965061135751835578522568620174870624326364591751719134017727476315825913896303730502231987133828454596458972192605628269836766485752863341723932456882638706013143064500915331711791221807863285445277518932935928879510111580179238430718068897684115464091110803073393785115849134031970572952341156248906594900067269723116623961645695573540685417690666812895681389324387236630659047254288574908905115640816788218380202062908240234242061857266935270268131606785945960533363545765413571210356588614272 158731480268028865375787109312788454557 +-197571802564094255018835618501836237470451780852464910702917947610328490115600879542277218044587953977482033832066853445897949746594340123621010571525423525283265694134335278880857970722363261435835440841138075424370755023622549046990506755720823389094963930373207384718204282462412342807880786115573288541223692700693236309165845190755419998468567231783954117079063183652374494384463667165956585068985935341133217173168221529758860606207789154326413767211275484817995028454435144966634656180655573506329003296926488159246532512031996848763667993503525898127688305577417054301089 22128982064863884081225183621483498301 + +/ 6972688127252138459564959519023672734797720350352562732688801457617542500099509865574973489633915033719003377224851429368921013349700580153017928134596084737004044411366993842119504403376959959480789333673755655063117787017101298662107070542628327980482897959181812093955456006698786827411711446400770298021381400926276799694253767942575674933967663793055141073318481678170963516082521833862017762649825976536694779299584090771859213561628651925244725218133964348562464473355036335901024895778660125505334188112559810228121975479770611339038859346371817450184722764849484125019138077786325611240207689562646177214862 212229494710249959442576275005814060115 +32854472639496801513625266171869868980490382632885983229822011721729192329270622778562010916862699719016516950898363598128121229189750116863530046554995732003476968354657890095157193194509736774942176733534056571834590366060961121671289385934253368995757730753667329275656966343378668081666905678760988072374390324867142186156887369444294667350127131255070935565628787125782523108523608392770938414227072810340675454608542379054058487857197652824285799185253955626498482144435788461420918351383205430463919873128183751743772403931916447474872579786344165522578418757820262494633 202602247999430336716424978770050352067 + +/ 16146477881044981814872185978005786543642189695578037880128585850657582227346785609949631520306446259385630057714250080634804935779512167368598228578137970013047546260870411704532863029107179381174130047754949458719207263855344249323475379597824225167477700328511898116621808311449385399905867898699564040099050435706021172544369947305690899952295406293447506146823515423102232662385522802025721933657658703659553626211653699561780784625213816227416468145185123310306319508640423290747072654284738002434784128270456278391972933415297337582959158202392632248570451671740383769937167912379559021602080900933189192375421 -20310422672054016316639412907518719590 +-794984828319777650694385094127543908631549711061207837016795411776676915426259473368853160688756737034823729254185786121643208845684214393842790938031685686187522759314947140818495607328399712740082388950671812621562726517214947696883370445215206445278987678057453772575168165658852145270562955365137991966349779422574705948837912807073553643238677887356778699590679423929808592396737386221680984966466476609537799000714595283015387280145210362603689829188312020767177147038887361232908826207585485585717770550283815412266616448789002231614634998653868395118016500112836083648523 -11799925147898511149643800539362290149 + +/ -13333386402689284528955650425278647711888918022382802858930735354762601039626630790596510115878972126652469762677159574447095103432811719733783031083771953511074739306201836852090205483055377050725227473420407492132102122918222222661174330427122421429872287465154597256840268514193104109407101719163350519145025605863849817679140939130213133277602018019855074419919415918787364400403970284236218639542926948339939839929520062948689163759711059171596722904754429438151009753108425737178467425999202826949155777669899192519705543679208977557190789421238295365156644743397621766685402593133067165504533347334868817548870 107895899863035416090758241115761545328 +-123576395577726995467509810094692559795501216776624732643654929468261569953970620516962817836961555200657486752392956631475337670018968072148956742753944568338154539759184635744464807131713977468703479999463417456632584312098725347773265375061594795112459950102134313707118281441803361728717545150343139196161174674989680811767284271234273952364761695483244122531302983909975719498713709629935106791776293856798291478157380963024628448230576871460269799644149756524167414024818290038046315016119726155391403548599527926110598142014566712845146844517328059271906387663525750970076 88916219636034843584282198824028056058 + +/ 17396957313719309181655810621342598276769983942993211094806395284201495622288603530182348401034532670779772026613959220329662328631746748840677422913897389222211979435071300283979361385673923168895608787064277711550661080470385607194607715097404666355341496134600730286695020337462621706659365279865288817231582460135505994785679557826378614057840182487774381015927951405829305139118747627556929090278010945530871104877221509669969637405745641517281958386251071953311649218819211648218403974139963448683937267881136641329201897530450274203549882462469989176813129608929688681288830465486480047730094845155570312862849 104186547224209978415781641589908237686 +166978921724711432713743436094800137505239032223722714964309089009506614535895519867110422644603923287110323367280581626196181748165083370852210731028149827924135420402322644021166934074442323617970930798783252483767604446788478644579242675426052418474560530381252273076756647943610269802844827006798614570113552490465455602498689920520721194207399033534120048155924790694559525815508064767545247429922864279482054330586837508112647624007190343625308333109186981945018060033849826856038685383194624273212670683254990051450794748656874003602039616482791671154071071940218956513118 9689041137085346765396894259391897901 + +/ 26296668029363560180578472840636347646283515109787546573773133131169235303278230292620358193183994696837168281782424510501462932715289179113988102147665778902989371433141498385910026219307721742927641650208826808587628688330897125412814594284814050899367007933796796475217266168539594147019546294897573039387284123267787652073296654465797846788961105602455421819896924940988943972769457383697764285807789932427485615761627567609238909525216550007481876466503244451415718439500787125259366798747857812030734235875561021422659158282642054300429948383202431010170473103089719168265166132037042278884136592980721399898848 -145151976679874338336058059363314537 +-181166447959297097309632649706277310462287087273328158550559504514077025970552462585976881228509338936991115036681066334762526041621219901153411893847383132947014037561714726163730996723689359625350327562359371926025189504119439124542211108302015324941630060663441114185165191476531119095379776949183747807676488899740428054170566283213350742091333006110702325305863276507428847828894436119391753135134140918882254262557249907254757481730047992171259860818951015886963391969368286964814563381601392580107247382831740092922947642920341973871311541502943677603328463821550096331015449 -110577937214275422567781046193383265 + +/ -19994387832358687564296482880955715923586831477222916410597818184152857711804843023778287694318724091304456899035183525127967566364649938785565453184727547598671504088541688780249133503129117660562371945085803761853547276588271572826270417538713473845464667687405215144939251922714172491714494707146884856110703801489923219443900691887441959990915961809557008130581221371957930424791112777130507507175854695038524747304496126685506502272017923871345301904089286401283977464945598827719321374155830270851832155999413594990591210770207211404582078047399265487243377424091264415562555479103296829671606309227114918559411 -12290727751262531359522445814787932748157034294075544178515373320704162497192584943859172528304649072004548036058435558708785921268699678655127651925033127 +1626786325187686206254527912536268233229423573477257270053889346415589499220953871015742625081244397640382477041911954315181713027690095532216560352291641519082404655148801601548476351855968135320238312168182690718232048560912434032449056943124604465352409935769208624710904989903415445402236977791654195811881494578919123294926181940156782515461241607944723498957864382829554865120206763098110715049165623436566759235599826844834593966722456940122296028858654743 -9304614710679256891239598861411560332304324920434685965899525875715186019084445758952863067134832791421060008856516918122255022315420901512824783987888050 + +/ 29916764548363516315213840380698377536192609328827808911066465815108876776179490417834258307994776308911573596103626976278007330105709262864909645777842169226871227147054313657605129607087589187123503174807369386299991859056839377153696490642393964375926230602462747312790529261100240727992473466712957240583216041201088172946856263144111813006598318253235946940685055230369936673556047728831107329646718919450916347559724523856479721600705891348359262178740659519774585983326669798219391447115538287743254388570949370216658299333436961807924794235560183973249587386381643272943817567729138289488897133971529039695757 8476334091496252719253471844340714224307718865976339399900779938495486609491382968889867250990569753004524377073297071391348967807704774866029247355531618 +3529446129120492576886933802306348265603546401021071145489371491655402978871242160302127070993421776756377896228018319062438889879832901903630775379375474362476945682118589732246954574638082665328364641573132346715748566497144048040413137331852635046499249704620713985392482170976329565026453768403409435575238922748145312555307402543771398751056417238642358386670853730480256387244547289563589645563705382910276075835741806800308617595407635175179549186349623407 1121598720662440742988451944089192258208677258393969079460095922943603039995215206852920630773807538691962340221388452566848135314318278502721876458313231 + +/ -30744743258986840974825934925400463284355151584175701001085034454282509186617636999187209301942468580138651572968436145883508307958710953672541548503099036345172087541186583358599035971842953063792288589346835317805238011836025866089112384348158834439025788655670125182111347815257445264926636792871229400549827577321670531162443103810493815924151448637886392962948822485797619344455378736429734156821345757186019132123507024835777255364976075865806996283635073664232613154449751312293463363442122204887362535000774934247468263702545257658938264075634847752288018235968497021028555508346290918717339789336670514080457 9934634309555858804270980126309615970053320780519373855793924975583204316518242194797388422238716941854010764960600214725109000476250489194524103405530739 +-3094703066162616114961725586598761886126440000450319657542750158557542422972265973994772432239434133706930345695126019787070526540238058168248601279554005834087067802027491094801318957724338773860374084056857766237802500357570852135835098188033303615905411388899090151866056131910609183545491275118194384669083917607888152562507000146957250511707959497676133004912560741040750644659008842081402439245159076407113469036885418577123982203712293205143214370192534124 4583596897489701137708573201509728063693689464812281935974761545728663079569821440573072545991527054461975034582959468944964678550288258358252037074357179 + +/ 24643502519803888691586762836893695554672668882609252497967357925433058477865049537403781351424439957461939549800248300068008928283760882152105280755728186165336818042947891471506568092832294726444765034759769551554808855491501013689374698042749154404230203678454785382207604303955330803002216399957247238380783055267792042696128953561495320963977422797650867160007702447898131066450706964885725041540562948429789541526915479497874153956586286026740960933319749986950590370162278200833957789772606366304927219928607487195862425188567871883130961914293433624774413725427494180502314592349900074269004608052919421088368 2717450167284225996753981867457220829510500841312068511238048532529427648370573174539553585183069673827813696207263846346713773366926177883038970327669336 +9068612486988930041452615817812610262395413380750714216172624755617167719146150429506179809229439286481063525474263171526993584649905549895379552966354856688607031124385144196292585204982548068977456744404073207215088901248836694827956726571697679223311674675917482789361649212542123782029147076771863216074461137218175127751607336902080642274682219411530926094644805942929937073627694007279189212959755881318530033893691479717411893927131774283641886860168845194 2630363844657415178536247490974293939778607568925570410250370678327072798984411202414449734187268017772490491616031334354084510652181851150192386816317184 + +/ 1857471144762621226078309760048781819853808902834007243224510947324703640579895884811355396539307768563652552656687272466757001311647739575925654454617797724205998847128737511426204839085303003071906438273332520201294499647651016586411018765198623236956366866836742835368817430092030307634950795461633849978479504285561783793523293571899866834127333885745234267982498066548934305641020559144124243564372784715821253591393010485777361901806233522170298666402318233462164642692695586402286093353406704474285764942607399336515494566976327035651095257443119526233432484730549923908643983835638400244551332963304703341991 362612878824948204766041465498605121983038344328923889813821008133012829955553725429013872301357114460102737976404433762806451909620639548491755012601032 +5122463247256360173380682454830667735994909336257131094636868172398931453441644851286253058553683806107714242214425234484025628771776577335533690016729030113210376620329656025707882934177847398968795670307669192526890837607471635669459016090183544133427748745285201703705268883110778706355536331762965138118319801447738265213248123408984033840769670544118622199016226728679039309116650227118501163822195946867970684116882706924781160720494209768961175300546225784 299760236823448232299335077352435634442705524236228994156709162318327253866985319237312495500717724311867426639654094420477947147244359720759776119932903 + +/ -15512250050509017569431653406374218468191654707236673402325185299702995515514048891881128604127259128940966966514594946451486134694476273851559935124397715410737724278746590654901453457150411276548615199767807502093455391898384739629715289928761836102897197661380120487438391395985221415090269132778914962364055598932950972975325423902250870789291669816092405224971234605691036355433974706922342506063057313987759468269851537540937400869685698983579620650290591582484180497444639057157757973753179907365231695893268694300775677678977541920955165836544926807198464657683328635157477957240884116233992874779933793654204 3358541127950670805593397985550958371637657834938091938230318311713415017140090677340291997759883758478618605945079413061265904587739160855880393703427541 +-4618746491270258642196486131268015112521835423420717615093174172980857448220383551186758990917205762587881579670823547496170992786073126440511182401809159567300795932372630208424356476743332043619346311088384079059435373790499576304419282368802375623485321283238175405485184862968207039638749406944454331623523507687428477960605913568521627594126207284882249627708423470608914416155986620408793531083231293214124556072929628635430705354422888702360112306375650944 3005102575063606384680566102915562580638230938191281335082512965430334791588194853068845397234960243041587407778382851298719244486316278864423824018594500 + +/ -7795907266149417935880848054805638357926463272237741460640947569644048588447722511612181517607450880049915905557897989678236704307693907736247058983016545076253121570693500226105057772914231058133990872553093077477246778482972908671114493634160355090012802248700706280069417479488180111886570768297449284497993155476453139747448123977064178202693174347058572053686548952330935409613585349836865130827901523001778569465270141666026392762544863707172069660941583975586471593214627992137708766736399392527511202198595607734119123667475481080585901379489987633325665012548140296404667154265922084962379380813206642961141 3266322496024458154155982586177367205241879884975448114046439840444265881425140636480586339953113858345978121218157346057285955670573379845528853928778649121198573716895875372950665878135491926664618467827249347334921049358968885123014320008238518111491029267663871866232098994215220584035091470875989416180732388925174422587186795087858194659347743269411322176680238569785902471836835335166112452646235228206195081915725782645939016905926964169376697807870023636080042287120562847150399762713196525578131013908505157176421364232903071413436786302567498639276925676203136397430977438669196132229839190136276524068288 +-3 2003060221923956526587099703726463257799176382688602881498371951688749055827699397829577502251890694988018458096574048493621162704026231800339502803319402287342599579994125892746939861492244721859864530928654964527516369593933746697928466390555199244460285554290909318626879503157481640218703644330518964044204011299070128014112261286510405775350055461175394476354166757026772005896920655661472227110804161616806676281907206271790657955236028800958023762668486932653655268147060549313490521403190184206881839526919863795144969031233733159724457528212508284505112016061268895888265161741666311727138189595622929243723 + +/ 11613527140000568192482756738253388762224453894786686024705324634643336086154640452808765642911437980109688560443031730927780624431102238262306897218656077453053740901919490705730434109001630505554615644472687423755323577388664893480041595787444776915986509240983988768996984961291424649229547505632144971266842770877960364848817464904515794389771780820407027606373375049573795106795237627834701205331678265805568350911815774813018183599320960878173351437520785364220474506113269284418886794765499703487689302735252054649064328797761871733823998210784410557300554983737071496100764692702245794941065431902323619975581 7587249511488455291481574592043097850017617150850944854151430663857438629054661904725186504711545142985024178310267212940242316767124495452290378757849233879415880641974894427201010329762690544890887138507664079244754338469394276854749839357593153274763991475638253761900794536890806900100092838214316658164432730592513062527370014192403678448098350118128696785002089891158354556141292773760767431084006166964515144211080310413063362654706066932594296395740249062444674285627590937186704887659632200604451417990996813320236575100574875972599186794102474813935877205799664448604353627460661673753990274449050152179527 +1 4026277628512112901001182146210290912206836743935741170553893970785897457099978548083579138199892837124664382132764517987538307663977742810016518460806843573637860259944596278529423779238939960663728505965023344510569238919270616625291756429851623641222517765345735007096190424400617749129454667417828313102410040285447302321447450712112115941673430702278330821371285158415440550653944854073933774247672098841053206700735464399954820944614893945579055041780536301775800220485678347232181907105867502883237884744255241328827753697186995761224811416681935743364677777937407047496411065241584121187075157453273467796054 + +/ -7305781671042805086205879027901218372025263744748476920370936801215407682690507181906856060500614379569331564964369672229742583709815447579920249246854986585035499467980872113078927984775191185433409825799075510115669850322522523768142598684481781858254267858499128954637862215107851239397355311785880257523971110240017078879170794103136756816178851155500522519324993786026829680880063502983144325993463559477297984845168078230705008144229777150602704982383890956566628052218375163513110904344758464487972471262152773773566261138354198738738504010228322403241824600361830597196538559114936352203702189317843297305612 -11937423884971453925821024083751259499642447259766781297912835275709052311299551864192658177304486399283699058384364751376568714457361090099824205847610164922413207048832164141112741434740690851620879666916091683114805059397520385319965609291355819302668926633114618391597972110270104195729524521712155052417250364832858246777602394209216012243471719585422463867924602236259070514326289155605995143153426519592268534452020820494702940294360007255909971183689215427975382460838239606147437401771039532404179010791325434773433170447691511043481857627662741058910094855501029573945308325534013127380693322055343068093073 +0 -7305781671042805086205879027901218372025263744748476920370936801215407682690507181906856060500614379569331564964369672229742583709815447579920249246854986585035499467980872113078927984775191185433409825799075510115669850322522523768142598684481781858254267858499128954637862215107851239397355311785880257523971110240017078879170794103136756816178851155500522519324993786026829680880063502983144325993463559477297984845168078230705008144229777150602704982383890956566628052218375163513110904344758464487972471262152773773566261138354198738738504010228322403241824600361830597196538559114936352203702189317843297305612 + +/ 17209502052655993695999949678678881817732805080961191774214537634467104669997820051278559964292611469011761231649311262525642916746094372962648405298787080759403024379693612743720557853611946286870304197122036126838107226222151419136402861650399432372524638862075613534601535719033760228742681750726602275976151585576407980278472053570791272866409613117043122126348697545507835143620584132787128003876247255766444872271317786602207376422907474443135464267851639815814827163426470735478362760843575789201717413221027220343295978806345517086041608240978720520050196395624694268550345436208830801369557317350938753101681 -25492366031674865714603800130440722297248675391454036922399382654290017832413394991423912006244487394226853780856833445493556957899686872020567315114599762113500546176766623020864155665149937580279148318691466089932874147998457966225079089491216886106456935347049916843309661581230809710698689198580162954443484103521650233141750002940462046212610545804018834324858009370780731978549694951052575612824613445810777717659953126577086301947235560041125102454055651800601741899891885690488712713049609045874194541526013910595588271596050758340197535035493872183554373796733932414738278642683363241812520579194986842248406 +-1 -8282863979018872018603850451761840479515870310492845148184845019822913162415574940145352041951875925215092549207522182967914041153592499057918909815812681354097521797073010277143597811537991293408844121569429963094766921776306547088676227840817453733932296484974303308708125862197049481956007447853560678467332517945242252863277949369670773346200932686975712198509311825272896834929110818265447608948366190044332845388635339974878925524328085597989638186204011984786914736465414955010349952206033256672477128304986690252292292789705241254155926794515151663504177401109238146187933206474532440442963261844048089146725 + +/ -8264418404762904935971540586212553189369665581268178138984287355704267971035469114147004054957406066365602520300857203610010249719556414712966642864630190534880504909484466848336120976452116710282569157883499369927614177722612839620755683557142607127318366680113418741529273669250116389195268121444810596743918472573214391707744942602210637166261703132552343842498400457951297867678005414165609775233338084350550320105330065321705398390488050253072298385134487973139665686164664754618413684815943926055617247418686500187322182211450203615430634564054858334306443836925011966962933359925972720453678168762605413687214 5659270408666391096654451960229302202479994399681938594290004159630195588392730314741316662127233421095165804881532974934939182755121225362126950700260935754690609887023434283325604581248499606543036334396521593447377999307285561199879892626309791285854660250329582118972672086769638010239857182094917068742207521552140484875546416914612991626798295801703343486711676899362639653362756374154843829963089403048720579483739833657436575344900585844370562669493206999896738589003961397524062927929845521761767153927579517203070144813897742036471334901681673999002492983666998353060616069539877603853178826800408499832716 +-2 3054122412569877257337363334246051215590323218095699049595720963556123205749991515335629269297060775824729089462208746259868115790686036011287258535891680974500714864562401718315088186044882502803503510909543816967141820891958282779004101695476975444390953820545745496416070504289159631284446242745023540740496570531066578043347891227015346087334888470854343130924953340773981439047507334144077884692840721746890838862149601993167752299313121435668826953851926026653811491843258040429712171043747117467917060436472534218818107416345280457512035239308489663698542130408984739158298779153782487252679484838211585978218 + +/ 4195250682093770808206657488351701663023767531608220002369022288787894743733460069813218793361969185403525213558437941222523116136985128137372308027033996674188008302853626391013869026313385724383785051935256506784450227965020957296318645208598488161413279403683346821629439117327209071527878791167163980953326296620631874274522101994118890706372879111377721965738161511000396672294975037034526549811513789043244305969633354034500541113123255063232374386942908997046756046017320772897856114275234707703184161983806610888947627465435762937561231247173215724615365040435541390265704981699351563837340527277633975576176 -11523670060248463390302653643248573080204626895667435502164973305972185748470395654926094006744012148505197326052121545710242241891352369937317053719774706293090545099894988182012473466703170157783937873302934404896538337261995680454862441735502556865637909145839304694688324140057229642104588273654096346180116632418345589170248829444934471179206795690822184585196864577932274205101316913141872261673241280391442045565990088555353753789476615480189520529331131737084431448177942371080785938641454814169544682945510387155405628842164935394830439534834414364899293061798829902686953424000191565445443376301466821322836 +-1 -7328419378154692582095996154896871417180859364059215499795951017184291004736935585112875213382042963101672112493683604487719125754367241799944745692740709618902536797041361790998604440389784433400152821367677898112088109296974723158543796526904068704224629742155957873058885022730020570576709482486932365226790335797713714895726727450815580472833916579444462619458703066931877532806341876107345711861727491348197739596356734520853212676353360416957146142388222740037675402160621598182929824366220106466360520961703776266458001376729172457269208287661198640283928021363288512421248442300840001608102849023832845746660 diff --git a/src/big-int/vax-alloca.mar b/src/big-int/vax-alloca.mar new file mode 100644 index 00000000000..5bbc3b2174e --- /dev/null +++ b/src/big-int/vax-alloca.mar @@ -0,0 +1,33 @@ +;/* Macro help routines for the BISON/VMS program +; Gabor Karsai, Vanderbilt University +; +; BISON is distributed in the hope that it will be useful, but WITHOUT ANY +; WARRANTY. No author or distributor accepts responsibility to anyone +; for the consequences of using it or for whether it serves any +; particular purpose or works at all, unless he says so in writing. +; Refer to the BISON General Public License for full details. +; +; Everyone is granted permission to copy, modify and redistribute BISON, +; but only under the conditions described in the BISON General Public +; License. A copy of this license is supposed to have been given to you +; along with BISON so you can know your rights and responsibilities. It +; should be in a file named COPYING. Among other things, the copyright +; notice and this notice must be preserved on all copies. +; +; In other words, you are welcome to use, share and improve this program. +; You are forbidden to forbid anyone else to use, share and improve +; what you give them. Help stamp out software-hoarding! */ +; + + .psect vmshlp pic,usr,rel,ovr,shr,long,exe,nowrt + +alloca:: + .word 0 + subl2 ^X4(ap),sp + movl ^X10(fp),r1 + movq ^X8(fp),ap + bicl2 #03,sp + addl2 #^X1c,sp + movl sp,r0 + jmp (r1) + .end diff --git a/src/big-int/vms.com b/src/big-int/vms.com new file mode 100644 index 00000000000..c4fe4d0f9a3 --- /dev/null +++ b/src/big-int/vms.com @@ -0,0 +1,16 @@ +$! $Id: vms.com,v 1.1.1.1 2002-06-13 22:00:31 kroening Exp $ -*- dcl -*- +$! +$! Compile and run BigInt class with test code on VMS. +$! The VAX compiler seems to screw things up with optimization. +$! +$ if f$getsyi("arch_name") .eqs. "VAX" +$ then +$ debug == /debug +$ nodeb == /nodeb +$ alloca == ",vax-alloca" +$ macro vax-alloca.mar +$ endif +$ cxx'debug' bigint.cc,bigint-func.cc,bigint-test.cc +$ link'debug' bigint-test,bigint,bigint-func'alloca' +$ purge +$ run'nodeb' bigint-test diff --git a/src/cbmc/Makefile b/src/cbmc/Makefile new file mode 100644 index 00000000000..d7c08de162e --- /dev/null +++ b/src/cbmc/Makefile @@ -0,0 +1,67 @@ +SRC = main.cpp parseoptions.cpp bmc.cpp dimacs.cpp languages.cpp \ + bv_cbmc.cpp symex_bmc.cpp show_vcc.cpp cbmc_solvers.cpp \ + xml_interface.cpp \ + counterexample_beautification.cpp \ + counterexample_beautification_greedy.cpp \ + counterexample_beautification_pbs.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) \ + ../ansi-c/ansi-c$(LIBEXT) \ + ../big-int/bigint$(OBJEXT) \ + ../goto-programs/goto-programs$(LIBEXT) \ + ../goto-symex/goto-symex$(LIBEXT) \ + ../pointer-analysis/pointer-analysis$(LIBEXT) \ + ../langapi/langapi$(LIBEXT) \ + ../xmllang/xmllang$(LIBEXT) \ + ../solvers/solvers$(LIBEXT) \ + ../util/util$(LIBEXT) + +INCLUDES= -I .. -I ../util + +LIBS = + +include ../config.inc +include ../common + +all: cbmc$(EXEEXT) + +ifdef MODULE_BV_REFINEMENT + OBJ += ../bv_refinement/bv_refinement$(LIBEXT) + CXXFLAGS += -DHAVE_BV_REFINEMENT +endif + +ifdef MODULE_CPP + OBJ += ../cpp/cpp$(LIBEXT) + CXXFLAGS += -DHAVE_CPP +endif + +ifdef MODULE_JAVA + OBJ += ../java/java$(LIBEXT) + CXXFLAGS += -DHAVE_JAVA +endif + +ifdef MODULE_SPECC + OBJ += ../specc/specc$(LIBEXT) + CXXFLAGS += -DHAVE_SPECC +endif + +ifdef MODULE_PHP + OBJ += ../php/php$(LIBEXT) + CXXFLAGS += -DHAVE_PHP +endif + +ifneq ($(Z3),) + CXXFLAGS += -DHAVE_Z3 +endif + +ifneq ($(BOOLECTOR),) + CXXFLAGS += -DHAVE_BOOLECTOR +endif + +############################################################################### + +cbmc$(EXEEXT): $(OBJ) + $(CXX) $(LINKFLAGS) -o $@ $(OBJ) $(LIBS) + +clean: + rm -f $(SRC:.cpp=$(OBJEXT)) cbmc$(EXEEXT) diff --git a/src/cbmc/bmc.cpp b/src/cbmc/bmc.cpp new file mode 100644 index 00000000000..a0dc9eaf689 --- /dev/null +++ b/src/cbmc/bmc.cpp @@ -0,0 +1,524 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "bmc.h" +#include "counterexample_beautification_greedy.h" + +/*******************************************************************\ + +Function: bmc_baset::do_unwind_module + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::do_unwind_module( + decision_proceduret &decision_procedure) +{ +} + +/*******************************************************************\ + +Function: bmc_baset::error_trace + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::error_trace(const prop_convt &prop_conv) +{ + status("Building error trace"); + + goto_tracet goto_trace; + build_goto_trace(equation, prop_conv, goto_trace); + + switch(ui) + { + case ui_message_handlert::PLAIN: + std::cout << std::endl << "Counterexample:" << std::endl; + show_goto_trace(std::cout, ns, goto_trace); + break; + + case ui_message_handlert::OLD_GUI: + show_goto_trace_gui(std::cout, ns, goto_trace); + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml; + convert(ns, goto_trace, xml); + std::cout << xml << std::endl; + } + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: bmc_baset::do_conversion + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::do_conversion(prop_convt &prop_conv) +{ + // convert HDL + do_unwind_module(prop_conv); + + // convert SSA + equation.convert(prop_conv); + + // the 'extra constraints' + forall_expr_list(it, bmc_constraints) + prop_conv.set_to_true(*it); +} + +/*******************************************************************\ + +Function: bmc_baset::run_decision_procedure + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt +bmc_baset::run_decision_procedure(prop_convt &prop_conv) +{ + status("Passing problem to "+prop_conv.decision_procedure_text()); + + prop_conv.set_message_handler(get_message_handler()); + prop_conv.set_verbosity(get_verbosity()); + + // stop the time + fine_timet sat_start=current_time(); + + do_conversion(prop_conv); + + status("Running "+prop_conv.decision_procedure_text()); + + decision_proceduret::resultt dec_result=prop_conv.dec_solve(); + // output runtime + + { + std::ostringstream str; + fine_timet sat_stop=current_time(); + + str << "Runtime decision procedure: "; + output_time(sat_stop-sat_start, str); + str << "s"; + status(str.str()); + } + + return dec_result; +} + +/*******************************************************************\ + +Function: bmc_baset::report_success + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::report_success() +{ + status("VERIFICATION SUCCESSFUL"); + + switch(ui) + { + case ui_message_handlert::OLD_GUI: + std::cout << "SUCCESS" << std::endl + << "Verification successful" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl; + break; + + case ui_message_handlert::PLAIN: + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml("cprover-status"); + xml.data="SUCCESS"; + std::cout << xml; + std::cout << std::endl; + } + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: bmc_baset::report_failure + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::report_failure() +{ + status("VERIFICATION FAILED"); + + switch(ui) + { + case ui_message_handlert::OLD_GUI: + break; + + case ui_message_handlert::PLAIN: + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml("cprover-status"); + xml.data="FAILURE"; + std::cout << xml; + std::cout << std::endl; + } + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: bmc_baset::show_program + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::show_program() +{ + unsigned count=1; + + languagest languages(ns, new_ansi_c_language()); + + std::cout << std::endl << "Program constraints:" << std::endl; + + for(symex_target_equationt::SSA_stepst::const_iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); it++) + { + if(it->is_assignment()) + { + std::string string_value; + languages.from_expr(it->cond_expr, string_value); + std::cout << "(" << count << ") " << string_value << std::endl; + + if(!it->guard_expr.is_true()) + { + languages.from_expr(it->guard_expr, string_value); + std::cout << std::string(i2string(count).size()+3, ' '); + std::cout << "guard: " << string_value << std::endl; + } + + count++; + } + else if(it->is_assert()) + { + std::string string_value; + languages.from_expr(it->cond_expr, string_value); + std::cout << "(" << count << ") ASSERT(" + << string_value <<") " << std::endl; + + if(!it->guard_expr.is_true()) + { + languages.from_expr(it->guard_expr, string_value); + std::cout << std::string(i2string(count).size()+3, ' '); + std::cout << "guard: " << string_value << std::endl; + } + + count++; + } + } +} + +/*******************************************************************\ + +Function: bmc_baset::run + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::run(const goto_functionst &goto_functions) +{ + //symex.total_claims=0; + symex.set_message_handler(get_message_handler()); + symex.set_verbosity(get_verbosity()); + symex.options=options; + + status("Starting Bounded Model Checking"); + + symex.last_location.make_nil(); + + try + { + // get unwinding info + setup_unwind(); + + symex(goto_functions); + } + + catch(std::string &error_str) + { + message_streamt message_stream(get_message_handler()); + message_stream.err_location(symex.last_location); + message_stream.error(error_str); + return true; + } + + catch(const char *error_str) + { + message_streamt message_stream(get_message_handler()); + message_stream.err_location(symex.last_location); + message_stream.error(error_str); + return true; + } + + catch(std::bad_alloc) + { + message_streamt message_stream(get_message_handler()); + message_stream.error("Out of memory"); + return true; + } + + print(8, "size of program expression: "+ + i2string(equation.SSA_steps.size())+ + " assignments"); + + try + { + if(options.get_option("slice-by-trace")!="") + { + symex_slice_by_tracet symex_slice_by_trace(ns); + + symex_slice_by_trace.slice_by_trace + (options.get_option("slice-by-trace"), equation); + } + + if(options.get_bool_option("slice-formula")) + { + slice(equation); + print(8, "slicing removed "+ + i2string(equation.count_ignored_SSA_steps())+" assignments"); + } + else + { + simple_slice(equation); + print(8, "simple slicing removed "+ + i2string(equation.count_ignored_SSA_steps())+" assignments"); + } + + if(options.get_bool_option("program-only")) + { + show_program(); + return false; + } + + { + std::string msg; + msg="Generated "+i2string(symex.total_claims)+ + " VCC(s), "+i2string(symex.remaining_claims)+ + " remaining after simplification"; + print(8, msg); + } + + if(options.get_bool_option("show-vcc")) + { + show_vcc(); + return false; + } + + if(symex.remaining_claims==0) + { + report_success(); + return false; + } + + if(options.get_bool_option("boolector")) + return decide_boolector(); + else if(options.get_bool_option("cvc")) + return decide_cvc(); + else if(options.get_bool_option("dimacs")) + return write_dimacs(); + else if(options.get_bool_option("refine")) + return decide_bv_refinement(); + else if(options.get_bool_option("smt1")) + // this is the 'default' smt1 solver + return decide_smt1(smt1_dect::BOOLECTOR); + else if(options.get_bool_option("smt2")) + // this is the 'default' smt2 solver + return decide_smt2(smt2_dect::BOOLECTOR); + else if(options.get_bool_option("yices")) + return decide_yices(); + else if(options.get_bool_option("z3")) + return decide_z3(); + else + return decide_default(); + } + + catch(std::string &error_str) + { + error(error_str); + return true; + } + + catch(const char *error_str) + { + error(error_str); + return true; + } + + catch(std::bad_alloc) + { + error("Out of memory"); + return true; + } +} + +/*******************************************************************\ + +Function: bmc_baset::decide + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::decide(prop_convt &prop_conv) +{ + if(options.get_bool_option("beautify-pbs") || + options.get_bool_option("beautify-greedy")) + throw "sorry, this solver does not support beautification"; + + prop_conv.set_message_handler(get_message_handler()); + prop_conv.set_verbosity(get_verbosity()); + + bool result=true; + + switch(run_decision_procedure(prop_conv)) + { + case decision_proceduret::D_UNSATISFIABLE: + result=false; + report_success(); + break; + + case decision_proceduret::D_SATISFIABLE: + error_trace(prop_conv); + report_failure(); + break; + + default: + error("decision procedure failed"); + } + + return result; +} + +/*******************************************************************\ + +Function: bmc_baset::setup_unwind + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::setup_unwind() +{ + const std::string &set=options.get_option("unwindset"); + unsigned int length=set.length(); + + for(unsigned int idx=0; idx +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "symex_bmc.h" + +class bmc_baset:public messaget +{ +public: + bmc_baset( + const namespacet &_ns, + symex_bmct &_symex, + symex_target_equationt &_equation, + message_handlert &_message_handler): + messaget(_message_handler), + ns(_ns), + symex(_symex), + equation(_equation), + ui(ui_message_handlert::PLAIN) + { + } + + optionst options; + + virtual bool run(const goto_functionst &goto_functions); + virtual ~bmc_baset() { } + + // additional stuff + expr_listt bmc_constraints; + + friend class cbmc_satt; + friend class hw_cbmc_satt; + friend class counterexample_beautification_greedyt; + + void set_ui(language_uit::uit _ui) { ui=_ui; } + +protected: + const namespacet &ns; + symex_bmct &symex; + symex_target_equationt &equation; + + // use gui format + language_uit::uit ui; + + virtual decision_proceduret::resultt + run_decision_procedure(prop_convt &prop_conv); + + virtual bool decide(prop_convt &prop_conv); + + // the solvers we have + virtual bool decide_default(); + virtual bool decide_bv_refinement(); + virtual bool decide_cvc(); + virtual bool decide_yices(); + virtual bool decide_smt1(smt1_dect::solvert solver); + virtual bool decide_smt2(smt2_dect::solvert solver); + virtual bool decide_boolector(); + virtual bool decide_z3(); + virtual void smt1_convert(std::ostream &out); + virtual void smt2_convert(std::ostream &out); + virtual bool write_dimacs(); + virtual bool write_dimacs(std::ostream &out); + + // unwinding + virtual void setup_unwind(); + + virtual void do_unwind_module( + decision_proceduret &decision_procedure); + void do_conversion(prop_convt &solver); + + virtual void show_vcc(); + virtual void show_vcc(std::ostream &out); + virtual void show_program(); + virtual void report_success(); + virtual void report_failure(); + + virtual void error_trace( + const prop_convt &prop_conv); +}; + +class bmct:public bmc_baset +{ +public: + bmct( + const contextt &_context, + message_handlert &_message_handler): + bmc_baset(_ns, _symex, _equation, _message_handler), + _ns(_context, new_context), + _equation(ns), + _symex(ns, new_context, _equation) + { + } + +protected: + contextt new_context; + namespacet _ns; + symex_target_equationt _equation; + symex_bmct _symex; +}; + +#endif diff --git a/src/cbmc/bv_cbmc.cpp b/src/cbmc/bv_cbmc.cpp new file mode 100644 index 00000000000..259333b877c --- /dev/null +++ b/src/cbmc/bv_cbmc.cpp @@ -0,0 +1,191 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "bv_cbmc.h" + +/*******************************************************************\ + +Function: bv_cbmct::convert_waitfor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_cbmct::convert_waitfor(const exprt &expr, bvt &bv) +{ + if(expr.operands().size()!=4) + throw "waitfor expected to have four operands"; + + exprt new_cycle; + const exprt &old_cycle=expr.op0(); + const exprt &cycle_var=expr.op1(); + const exprt &bound=expr.op2(); + const exprt &predicate=expr.op3(); + + make_free_bv_expr(expr.type(), new_cycle); + + mp_integer bound_value; + if(to_integer(bound, bound_value)) + throw "waitfor bound must be a constant"; + + { + // constraint: new_cycle>=old_cycle + + exprt rel_expr(">=", bool_typet()); + rel_expr.copy_to_operands(new_cycle, old_cycle); + set_to_true(rel_expr); + } + + { + // constraint: new_cycle<=bound+1 + + exprt one=from_integer(1, bound.type()); + + exprt bound_plus1("+", bound.type()); + bound_plus1.reserve_operands(2); + bound_plus1.copy_to_operands(bound); + bound_plus1.move_to_operands(one); + + exprt rel_expr("<=", bool_typet()); + rel_expr.copy_to_operands(new_cycle, bound_plus1); + set_to_true(rel_expr); + } + + for(mp_integer i=0; i<=bound_value; i=i+1) + { + // replace cycle_var by old_cycle+i; + + exprt old_cycle_plus_i("+", old_cycle.type()); + old_cycle_plus_i.operands().resize(2); + old_cycle_plus_i.op0()=old_cycle; + old_cycle_plus_i.op1()=from_integer(i, old_cycle.type()); + + exprt tmp_predicate=predicate; + replace_expr(cycle_var, old_cycle_plus_i, tmp_predicate); + + // CONSTRAINT: + // if((cycle)<=bound) { + // if((cycle)", bool_typet()); + top_impl.reserve_operands(2); + top_impl.move_to_operands(and_expr); + top_impl.copy_to_operands(tmp_predicate); + top_impl.op1().make_not(); + + set_to_true(top_impl); + } + + { + exprt cycle_le_bound("<=", bool_typet()); + cycle_le_bound.operands().resize(2); + cycle_le_bound.op0()=old_cycle_plus_i; + cycle_le_bound.op1()=bound; + + exprt cycle_eq_new_cycle("=", bool_typet()); + cycle_eq_new_cycle.operands().resize(2); + cycle_eq_new_cycle.op0()=old_cycle_plus_i; + cycle_eq_new_cycle.op1()=new_cycle; + + exprt and_expr("and", bool_typet()); + and_expr.operands().resize(2); + and_expr.op0().swap(cycle_le_bound); + and_expr.op1().swap(cycle_eq_new_cycle); + + exprt top_impl("=>", bool_typet()); + top_impl.reserve_operands(2); + top_impl.move_to_operands(and_expr); + top_impl.copy_to_operands(tmp_predicate); + + set_to_true(top_impl); + } + } + + // result: new_cycle + return convert_bitvector(new_cycle, bv); +} + +/*******************************************************************\ + +Function: bv_cbmct::convert_waitfor_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_cbmct::convert_waitfor_symbol(const exprt &expr, bvt &bv) +{ + if(expr.operands().size()!=1) + throw "waitfor_symbol expected to have one operand"; + + exprt result; + const exprt &bound=expr.op0(); + + make_free_bv_expr(expr.type(), result); + + // constraint: result<=bound + + exprt rel_expr("<=", bool_typet()); + rel_expr.copy_to_operands(result, bound); + set_to_true(rel_expr); + + return convert_bitvector(result, bv); +} + +/*******************************************************************\ + +Function: bv_cbmct::convert_bitvector + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_cbmct::convert_bitvector(const exprt &expr, bvt &bv) +{ + if(expr.id()=="waitfor") + return convert_waitfor(expr, bv); + + if(expr.id()=="waitfor_symbol") + return convert_waitfor_symbol(expr, bv); + + return bv_pointerst::convert_bitvector(expr, bv); +} diff --git a/src/cbmc/bv_cbmc.h b/src/cbmc/bv_cbmc.h new file mode 100644 index 00000000000..6aafae71537 --- /dev/null +++ b/src/cbmc/bv_cbmc.h @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CBMC_BV_CBMC_H +#define CPROVER_CBMC_BV_CBMC_H + +#include + +class bv_cbmct:public bv_pointerst +{ +public: + bv_cbmct( + const namespacet &_ns, + propt &_prop):bv_pointerst(_ns, _prop) { } + virtual ~bv_cbmct() { } + +protected: + // overloading + virtual void convert_bitvector(const exprt &expr, bvt &bv); // no cache + + virtual void convert_waitfor(const exprt &expr, bvt &bv); + virtual void convert_waitfor_symbol(const exprt &expr, bvt &bv); +}; + +#endif diff --git a/src/cbmc/cbmc_solvers.cpp b/src/cbmc/cbmc_solvers.cpp new file mode 100644 index 00000000000..965defddb86 --- /dev/null +++ b/src/cbmc/cbmc_solvers.cpp @@ -0,0 +1,362 @@ +/*******************************************************************\ + +Module: Solvers for VCs Generated by Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#ifdef HAVE_BV_REFINEMENT +#include +#endif + +#ifdef HAVE_BOOLECTOR +#include +#endif + +#ifdef HAVE_Z3 +#include +#endif + +#include +#include +#include + +#include "bmc.h" +#include "bv_cbmc.h" +#include "counterexample_beautification_greedy.h" +#include "version.h" + +/*******************************************************************\ + +Function: bmc_baset::decide_default + + Inputs: + + Outputs: + + Purpose: Decide using "default" decision procedure + +\*******************************************************************/ + +bool bmc_baset::decide_default() +{ + #if 1 + sat_minimizert satcheck; + satcheck.set_message_handler(get_message_handler()); + satcheck.set_verbosity(get_verbosity()); + + bv_cbmct bv_cbmc(ns, satcheck); + + if(options.get_option("arrays-uf")=="never") + bv_cbmc.unbounded_array=bv_cbmct::U_NONE; + else if(options.get_option("arrays-uf")=="always") + bv_cbmc.unbounded_array=bv_cbmct::U_ALL; + + bool result=true; + + switch(run_decision_procedure(bv_cbmc)) + { + case decision_proceduret::D_UNSATISFIABLE: + result=false; + report_success(); + break; + + case decision_proceduret::D_SATISFIABLE: + if(options.get_bool_option("beautify-pbs")) + throw "beautify-pbs is no longer supported"; + else if(options.get_bool_option("beautify-greedy")) + counterexample_beautification_greedyt()( + satcheck, bv_cbmc, equation, ns); + + error_trace(bv_cbmc); + report_failure(); + break; + + default: + error("decision procedure failed"); + } + #else + + satcheck_minisat_simpt satcheck; + satcheck.set_message_handler(get_message_handler()); + satcheck.set_verbosity(get_verbosity()); + + bv_cbmct bv_cbmc(satcheck); + + if(options.get_option("arrays-uf")=="never") + bv_cbmc.unbounded_array=bv_cbmct::U_NONE; + else if(options.get_option("arrays-uf")=="always") + bv_cbmc.unbounded_array=bv_cbmct::U_ALL; + + bool result=true; + + switch(run_decision_procedure(bv_cbmc)) + { + case decision_proceduret::D_UNSATISFIABLE: + result=false; + report_success(); + break; + + case decision_proceduret::D_SATISFIABLE: + error_trace(bv_cbmc); + report_failure(); + break; + + default: + error("decision procedure failed"); + } + + #endif + + return result; +} + +/*******************************************************************\ + +Function: bmc_baset::bv_refinement + + Inputs: + + Outputs: + + Purpose: Decide using refinement decision procedure + +\*******************************************************************/ + +bool bmc_baset::decide_bv_refinement() +{ + #ifdef HAVE_BV_REFINEMENT + satcheckt satcheck; + satcheck.set_message_handler(get_message_handler()); + satcheck.set_verbosity(get_verbosity()); + + bv_refinement_loopt bv_refinement_loop(ns, satcheck); + + return decide(bv_refinement_loop); + #else + throw "bv refinement not linked in"; + #endif +} + +/*******************************************************************\ + +Function: bmc_baset::decide_smt1 + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::decide_smt1(smt1_dect::solvert solver) +{ + const std::string &filename=options.get_option("outfile"); + + if(filename=="") + { + smt1_dect smt1_dec( + ns, + "cbmc", + "Generated by CBMC " CBMC_VERSION, + "QF_AUFBV", + solver); + return decide(smt1_dec); + } + else if(filename=="-") + smt1_convert(std::cout); + else + { + std::ofstream out(filename.c_str()); + if(!out) + { + std::cerr << "failed to open " << filename << std::endl; + return false; + } + + smt1_convert(out); + } + + return false; +} + +/*******************************************************************\ + +Function: bmc_baset::smt1_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::smt1_convert(std::ostream &out) +{ + smt1_convt smt1_conv( + ns, + "cbmc", + "Generated by CBMC " CBMC_VERSION, + "QF_AUFBV", + out); + + smt1_conv.set_message_handler(get_message_handler()); + + do_conversion(smt1_conv); + + smt1_conv.dec_solve(); +} + +/*******************************************************************\ + +Function: bmc_baset::decide_smt2 + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::decide_smt2(smt2_dect::solvert solver) +{ + const std::string &filename=options.get_option("outfile"); + + if(filename=="") + { + smt2_dect smt2_dec( + ns, + "cbmc", + "Generated by CBMC " CBMC_VERSION, + "QF_AUFBV", + solver); + return decide(smt2_dec); + } + else if(filename=="-") + smt2_convert(std::cout); + else + { + std::ofstream out(filename.c_str()); + if(!out) + { + std::cerr << "failed to open " << filename << std::endl; + return false; + } + + smt2_convert(out); + } + + return false; +} + +/*******************************************************************\ + +Function: bmc_baset::smt2_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::smt2_convert(std::ostream &out) +{ + smt2_convt smt2_conv( + ns, + "cbmc", + "Generated by CBMC " CBMC_VERSION, + "QF_AUFBV", + out); + + smt2_conv.set_message_handler(get_message_handler()); + + do_conversion(smt2_conv); + + smt2_conv.dec_solve(); +} + +/*******************************************************************\ + +Function: bmc_baset::decide_cvc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::decide_cvc() +{ + return decide_smt1(smt1_dect::CVC3); +} + +/*******************************************************************\ + +Function: bmc_baset::decide_boolector + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::decide_boolector() +{ + #ifdef HAVE_BOOLECTOR + boolector_dect boolector_dec; + return decide(boolector_dec); + #else + return decide_smt1(smt1_dect::BOOLECTOR); + #endif +} + +/*******************************************************************\ + +Function: bmc_baset::decide_z3 + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::decide_z3() +{ + #ifdef HAVE_Z3 + z3_dect z3_dec; + return decide(z3_dec); + #else + return decide_smt1(smt1_dect::Z3); + #endif +} + +/*******************************************************************\ + +Function: bmc_baset::decide_yices + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::decide_yices() +{ + return decide_smt1(smt1_dect::YICES); +} diff --git a/src/cbmc/counterexample_beautification.cpp b/src/cbmc/counterexample_beautification.cpp new file mode 100644 index 00000000000..170be4fb2e6 --- /dev/null +++ b/src/cbmc/counterexample_beautification.cpp @@ -0,0 +1,78 @@ +/*******************************************************************\ + +Module: Counterexample Beautification using Incremental SAT + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "counterexample_beautification.h" + +/*******************************************************************\ + +Function: counterexample_beautificationt::get_minimization_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautificationt::get_minimization_symbols( + const bv_cbmct &bv_cbmc, + const symex_target_equationt &equation, + const symex_target_equationt::SSA_stepst::const_iterator failed, + minimization_symbolst &minimization_symbols) +{ + // remove the ones that are assigned under false guards + + for(symex_target_equationt::SSA_stepst::const_iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); it++) + { + if(it->is_assignment() && + it->assignment_type==symex_targett::STATE) + { + if(!bv_cbmc.prop.l_get(it->guard_literal).is_false()) + if(it->original_lhs.type().id()!="bool") + minimization_symbols.insert(it->lhs); + } + + // reached failed assertion? + if(it==failed) + break; + } +} + +/*******************************************************************\ + +Function: counterexample_beautificationt::get_failed_claim + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +symex_target_equationt::SSA_stepst::const_iterator +counterexample_beautificationt::get_failed_claim( + const bv_cbmct &bv_cbmc, + const symex_target_equationt &equation) +{ + // find failed claim + + for(symex_target_equationt::SSA_stepst::const_iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); it++) + if(it->is_assert() && + bv_cbmc.prop.l_get(it->guard_literal).is_true() && + bv_cbmc.prop.l_get(it->cond_literal).is_false()) + return it; + + assert(false); + return equation.SSA_steps.end(); +} diff --git a/src/cbmc/counterexample_beautification.h b/src/cbmc/counterexample_beautification.h new file mode 100644 index 00000000000..9fa23d3742a --- /dev/null +++ b/src/cbmc/counterexample_beautification.h @@ -0,0 +1,39 @@ +/*******************************************************************\ + +Module: Counterexample Beautification + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CBMC_COUNTEREXAMPLE_BEAUTIFICATION_H +#define CPROVER_CBMC_COUNTEREXAMPLE_BEAUTIFICATION_H + +#include +#include +#include + +#include "bv_cbmc.h" + +class counterexample_beautificationt +{ +public: + virtual ~counterexample_beautificationt() + { + } + +protected: + typedef std::set minimization_symbolst; + + void get_minimization_symbols( + const bv_cbmct &bv_cbmc, + const symex_target_equationt &equation, + const symex_target_equationt::SSA_stepst::const_iterator failed, + minimization_symbolst &minimization_symbols); + + symex_target_equationt::SSA_stepst::const_iterator get_failed_claim( + const bv_cbmct &bv_cbmc, + const symex_target_equationt &equation); +}; + +#endif diff --git a/src/cbmc/counterexample_beautification_greedy.cpp b/src/cbmc/counterexample_beautification_greedy.cpp new file mode 100644 index 00000000000..205134a4856 --- /dev/null +++ b/src/cbmc/counterexample_beautification_greedy.cpp @@ -0,0 +1,456 @@ +/*******************************************************************\ + +Module: Counterexample Beautification using Incremental SAT + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "counterexample_beautification_greedy.h" + +/*******************************************************************\ + +Function: counterexample_beautification_greedyt::beautify_try + + Inputs: + + Outputs: + + Purpose: try specific assignment v to literal l + +\*******************************************************************/ + +void counterexample_beautification_greedyt::beautify_try( + solvert &solver, + literalt l, + bool v) +{ + if(solver.l_get(l)==tvt(v)) + { + // already ok, but fix it + solver.l_set_to(l, v); + //std::cout << "ALREADY OK\n"; + return; + } + + // won't change if constant + if(l.is_constant()) return; + + literalt clause_lit(l); + if(!v) clause_lit=solver.lnot(clause_lit); + + bvt constraints; + constraints.push_back(clause_lit); + solver.set_assumptions(constraints); + + // save assignment, in case we get UNSAT + save_assignment(solver); + + if(solver.prop_solve()==propt::P_SATISFIABLE) + { + // make this permanent + solver.l_set_to(l, v); + //std::cout << "SAT!\n"; + } + else + { + //std::cout << "UNSAT!\n"; + + // restore the assignment + restore_assignment(solver); + } +} + +/*******************************************************************\ + +Function: counterexample_beautification_greedyt::minimize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_greedyt::minimize( + solvert &solver, + bv_cbmct &bv_cbmc, + const namespacet &ns, + const exprt &expr, + const typet &type, + unsigned offset, + unsigned bit_nr) // starting from _most_ significant bit +{ + // array or struct? + + if(type.id()==ID_array) + { + // get size + const exprt &size_expr=to_array_type(type).size(); + mp_integer size_int, i; + + if(to_integer(size_expr, size_int)) return; + + boolbv_widtht boolbv_width(ns); + + // get element width + unsigned width=boolbv_width(type.subtype()); + + if(width==0) + return; + + for(i=0; i(it->find(ID_type)); + + unsigned width=boolbv_width(subtype); + + if(width==0) continue; + + minimize(solver, bv_cbmc, ns, expr, subtype, offset, bit_nr); + + offset+=width; + } + } + else if(type.id()==ID_symbol) + { + const symbolt &s=ns.lookup(type.get(ID_identifier)); + minimize(solver, bv_cbmc, ns, expr, s.type, offset, bit_nr); + } + else if(type.id()==ID_pointer) + { + // no beautification for pointers right now + } + else if(type.id()==ID_signedbv || + type.id()==ID_unsignedbv) + { + boolbv_widtht boolbv_width(ns); + + unsigned width=boolbv_width(type); + + if(bit_nr>=width) return; + + unsigned bit=offset+width-bit_nr-1; + + // std::cout << "XX: " << expr.get("identifier") + // << " bit=" << bit_nr <(it->find(ID_type)); + max_width=std::max(max_width, get_max_width(ns, subtype)); + } + + return max_width; + } + else if(type.id()==ID_symbol) + { + const symbolt &s=ns.lookup(type.get(ID_identifier)); + return get_max_width(ns, s.type); + } + else if(type.id()==ID_pointer) + { + } + else + { + boolbv_widtht boolbv_width(ns); + return boolbv_width(type); + } + + return 0; +} + +/*******************************************************************\ + +Function: counterexample_beautification_greedyt::beautify_values + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_greedyt::beautify_values( + solvert &solver, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns) +{ + // get symbols we care about + minimization_symbolst minimization_symbols; + + get_minimization_symbols(bv_cbmc, equation, failed, minimization_symbols); + + // get max width + unsigned max_width=0; + + for(minimization_symbolst::const_iterator it=minimization_symbols.begin(); + it!=minimization_symbols.end(); it++) + max_width=std::max(max_width, get_max_width(ns, it->type())); + + for(unsigned i=0; itype(), 0, i); +} + +/*******************************************************************\ + +Function: counterexample_beautification_greedyt::beautify_guards + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +struct guard_structt +{ + guard_structt(literalt _literal, unsigned _count): + literal(_literal), count(_count) { } + + literalt literal; + unsigned count; +}; + +struct guard_comp: + public std::binary_function +{ + bool operator()(const guard_structt &x, + const guard_structt &y) const + { return x.countcond_literal)); + solver.lcnf(clause); + } + + // compute weights + typedef std::map guard_countt; + guard_countt guard_count; + + for(symex_target_equationt::SSA_stepst::const_iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); it++) + { + if(it->is_assignment() && + it->assignment_type!=symex_targett::HIDDEN) + { + if(!it->guard_literal.is_constant()) + guard_count[it->guard_literal]++; + } + + // reached failed assertion? + if(it==failed) + break; + } + + // put it in a vector to sort it + typedef std::vector guard_vectort; + guard_vectort guard_vector; + + guard_vector.reserve(guard_count.size()); + + for(guard_countt::const_iterator + it=guard_count.begin(); + it!=guard_count.end(); + it++) + guard_vector.push_back(guard_structt(it->first, it->second)); + + // sort it + std::sort(guard_vector.begin(), guard_vector.end(), guard_comp()); + + // minimize, starting with the largest one + + for(guard_vectort::const_reverse_iterator + it=guard_vector.rbegin(); + it!=static_cast(guard_vector).rend(); + it++) + beautify_try(solver, it->literal, false); +} + +/*******************************************************************\ + +Function: counterexample_beautification_greedyt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_greedyt::operator()( + solvert &solver, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns) +{ + // find failed claim + + failed=get_failed_claim(bv_cbmc, equation); + + bv_cbmc.status("Beautifying Counterexample (Guards)"); + beautify_guards(solver, bv_cbmc, equation, ns); + + bv_cbmc.status("Beautifying Counterexample (Values)"); + beautify_values(solver, bv_cbmc, equation, ns); +} + +/*******************************************************************\ + +Function: counterexample_beautification_greedyt::save_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_greedyt::save_assignment(propt &prop) +{ + assignment.resize(prop.no_variables()); + + for(unsigned i=1; i + +#include + +#include "counterexample_beautification.h" + +class counterexample_beautification_greedyt: + public counterexample_beautificationt +{ +public: + typedef sat_minimizert solvert; + + void operator()( + solvert &solver, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns); + +protected: + void beautify_guards( + solvert &cnf, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns); + + void beautify_values( + solvert &solver, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns); + + // this minimizes the absolute value + void minimize( + solvert &solver, + bv_cbmct &bv_cbmc, + const namespacet &ns, + const exprt &expr, + const typet &type, + unsigned offset, + unsigned bit_nr); + + void beautify_try( + solvert &solver, + literalt l, + bool v); + + unsigned get_max_width(const namespacet &ns, const typet &type); + + // the failed claim + symex_target_equationt::SSA_stepst::const_iterator failed; + + // the currently best assignment + std::vector assignment; + + void save_assignment(propt &prop); + void restore_assignment(propt &prop); +}; + +#endif diff --git a/src/cbmc/counterexample_beautification_pbs.cpp b/src/cbmc/counterexample_beautification_pbs.cpp new file mode 100644 index 00000000000..a09ec604524 --- /dev/null +++ b/src/cbmc/counterexample_beautification_pbs.cpp @@ -0,0 +1,358 @@ +/*******************************************************************\ + +Module: Counterexample Beautification using PBS + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "counterexample_beautification_pbs.h" + +/*******************************************************************\ + +Function: counterexample_beautification_pbst::beautify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_pbst::beautify( + pbs_dimacs_cnft &pbs, + bv_cbmct &bv_cbmc, + const namespacet &ns, + const exprt &expr, + const typet &type, + unsigned offset) +{ + // array or struct? + + if(type.id()=="array") + { + // get size + const exprt &size_expr=(exprt &)type.find("size"); + mp_integer size_int, i; + + if(to_integer(size_expr, size_int)) return; + + boolbv_widtht boolbv_width(ns); + + // get element width + unsigned width=boolbv_width(type.subtype()); + + if(width==0) + return; + + for(i=0; ifind("type"); + unsigned width=boolbv_width(subtype); + + if(width==0) continue; + + beautify(pbs, bv_cbmc, ns, expr, subtype, offset); + + offset+=width; + } + } + else if(type.id()=="symbol") + { + const symbolt &s=ns.lookup(type.get("identifier")); + beautify(pbs, bv_cbmc, ns, expr, s.type, offset); + } + else if(type.id()=="pointer") + { + // no beautification for pointers right now + } + else if(type.id()=="signedbv" || + type.id()=="unsignedbv") + { + bool is_signed=type.id()=="signedbv"; + + boolbv_widtht boolbv_width(ns); + + unsigned width=boolbv_width(type); + + for(unsigned i=0; isize()==1) + { + tvt value=pbs_solver.l_get(it->front()); + assert(value.is_true()); + } + + #if 0 + unsigned states=0; + + for(symex_target_equationt::statest::const_iterator + it=bv_cbmc.bmc.equation.states.begin(); + it!=bv_cbmc.bmc.equation.states.end(); it++) + if(it->symbol!="") + { + tvt value=pbs_solver.l_get(it->guard_literal); + assert(!value.is_unknown()); + if(value.is_true()) states++; + } + + std::cout << "DEBUG 1 " << states << std::endl; + #endif +} + +/*******************************************************************\ + +Function: counterexample_beautification_pbst::counterexample_beautification_guards + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_pbst::counterexample_beautification_guards( + cnf_clause_listt &cnf, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns) +{ + typedef std::map guard_countt; + guard_countt guard_count; + + for(symex_target_equationt::SSA_stepst::const_iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); it++) + { + if(it->is_assignment() && + it->assignment_type!=symex_targett::HIDDEN) + { + if(it->guard_literal!=const_literal(false) && + it->guard_literal!=const_literal(true)) + guard_count[it->guard_literal]++; + } + + // reached failed assertion? + if(it==failed) + break; + } + + pbs_dimacs_cnft pbs_solver; + setup_pbs(cnf, pbs_solver); + + // one of the assertions up to the failed + // assertion must fail + { + bvt assertion_clause; + + for(symex_target_equationt::SSA_stepst::const_iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); it++) + { + if(it->is_assert()) + assertion_clause.push_back(pbs_solver.lnot(it->cond_literal)); + + if(it==failed) break; + } + + guard_constraints.push_back(assertion_clause); + pbs_solver.lcnf(assertion_clause); + } + + // assign weights + + for(guard_countt::const_iterator + it=guard_count.begin(); + it!=guard_count.end(); + it++) + pbs_solver.pb_constraintmap[it->first]=it->second; + + run_pbs(pbs_solver); + + // copy satisfying assignment + cnf.copy_assignment_from(pbs_solver); + + // lock the guards in place + for(guard_countt::const_iterator + it=guard_count.begin(); + it!=guard_count.end(); + it++) + { + literalt l=it->first; + tvt value=pbs_solver.l_get(l); + assert(!value.is_unknown()); + bvt clause; + clause.push_back(value.is_true()?l:cnf.lnot(l)); + guard_constraints.push_back(clause); + } +} + +/*******************************************************************\ + +Function: counterexample_beautification_pbst::counterexample_beautification + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_pbst::counterexample_beautification( + cnf_clause_listt &cnf, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns) +{ + // find failed claim + failed=get_failed_claim(bv_cbmc, equation); + + bv_cbmc.status("Beautifying Counterexample using PBS (Guards)"); + counterexample_beautification_guards(cnf, bv_cbmc, equation, ns); + + bv_cbmc.status("Beautifying Counterexample using PBS (Values)"); + counterexample_beautification_values(cnf, bv_cbmc, equation, ns); +} + +/*******************************************************************\ + +Function: counterexample_beautification_pbst::setup_pbs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_pbst::setup_pbs( + const cnf_clause_listt &cnf, + pbs_dimacs_cnft &pbs) +{ + // copy clauses + cnf.copy_to(pbs); + + pbs.optimize=true; + pbs.binary_search=true; + pbs.maximize=false; + pbs.goal=0; +} + +/*******************************************************************\ + +Function: counterexample_beautification_pbst::run_pbs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_beautification_pbst::run_pbs( + pbs_dimacs_cnft &pbs) +{ + if(pbs.prop_solve()!=propt::P_SATISFIABLE) + throw "unexpected result from PBS"; +} diff --git a/src/cbmc/counterexample_beautification_pbs.h b/src/cbmc/counterexample_beautification_pbs.h new file mode 100644 index 00000000000..458a6237fa9 --- /dev/null +++ b/src/cbmc/counterexample_beautification_pbs.h @@ -0,0 +1,61 @@ +/*******************************************************************\ + +Module: Counterexample Beautification using PBS + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CBMC_COUNTEREXAMPLE_BEAUTIFICATION_PBS_H +#define CPROVER_CBMC_COUNTEREXAMPLE_BEAUTIFICATION_PBS_H + +#include + +#include + +#include "counterexample_beautification.h" + +class counterexample_beautification_pbst: + public counterexample_beautificationt +{ +public: + void counterexample_beautification( + cnf_clause_listt &cnf, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns); + +protected: + void beautify( + pbs_dimacs_cnft &pbs, + bv_cbmct &bv_cbmc, + const namespacet &ns, + const exprt &expr, + const typet &type, + unsigned offset); + + void counterexample_beautification_guards( + cnf_clause_listt &cnf, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns); + + void counterexample_beautification_values( + cnf_clause_listt &cnf, + bv_cbmct &bv_cbmc, + symex_target_equationt &equation, + const namespacet &ns); + + void setup_pbs( + const cnf_clause_listt &cnf, + pbs_dimacs_cnft &pbs); + + void run_pbs(pbs_dimacs_cnft &pbs); + + typedef std::list guard_constraintst; + guard_constraintst guard_constraints; + + symex_target_equationt::SSA_stepst::const_iterator failed; +}; + +#endif diff --git a/src/cbmc/dimacs.cpp b/src/cbmc/dimacs.cpp new file mode 100644 index 00000000000..95fbad25c35 --- /dev/null +++ b/src/cbmc/dimacs.cpp @@ -0,0 +1,85 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "bmc.h" +#include "bv_cbmc.h" + +/*******************************************************************\ + +Function: bmc_baset::write_dimacs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::write_dimacs() +{ + const std::string &filename=options.get_option("outfile"); + + if(filename.empty() || filename=="-") + return write_dimacs(std::cout); + + std::ofstream out(filename.c_str()); + if(!out) + { + std::cerr << "failed to open " << filename << std::endl; + return false; + } + + return write_dimacs(out); +} + +/*******************************************************************\ + +Function: bmc_baset::write_dimacs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bmc_baset::write_dimacs(std::ostream &out) +{ + dimacs_cnft dimacs_cnf; + dimacs_cnf.set_message_handler(get_message_handler()); + + bv_cbmct bv_cbmc(ns, dimacs_cnf); + + do_conversion(bv_cbmc); + + bv_cbmc.dec_solve(); + + dimacs_cnf.write_dimacs_cnf(out); + + // we dump the propositionals + for(prop_convt::symbolst::const_iterator + s_it=bv_cbmc.get_symbols().begin(); + s_it!=bv_cbmc.get_symbols().end(); + s_it++) + { + if(s_it->second.is_constant()) + out << "c " << (s_it->second.is_true()?"TRUE":"FALSE") << " " + << s_it->first << std::endl; + else + out << "c " << s_it->second.dimacs() << " " + << s_it->first << std::endl; + } + + return false; +} diff --git a/src/cbmc/dist-linux b/src/cbmc/dist-linux new file mode 100644 index 00000000000..7b903339b2b --- /dev/null +++ b/src/cbmc/dist-linux @@ -0,0 +1,63 @@ +#!/bin/bash +umask u=rwx,g=rx,o=rx + +make +strip cbmc + +VERSION=`./cbmc --version` +VERSION_FILE=`echo $VERSION | sed "y/./-/"` +BITS=`getconf LONG_BIT` +# UNAME_M=`uname -m` +DEB_ARCH=`dpkg --print-architecture` + +echo $VERSION_FILE + +(cd ../goto-cc; make; strip goto-cc) +(cd ../hw-cbmc; make; strip hw-cbmc) +(cd ../goto-instrument; make; strip goto-instrument) + +mkdir /tmp/cbmc-dist +cp ../hw-cbmc/hw-cbmc ../cbmc/cbmc ../goto-cc/goto-cc \ + ../goto-instrument/goto-instrument /tmp/cbmc-dist/ +cp ../../LICENSE /tmp/cbmc-dist/ +cd /tmp/cbmc-dist +tar cfz cbmc-${VERSION_FILE}-linux-${BITS}.tgz hw-cbmc cbmc \ + goto-cc goto-instrument LICENSE + +mkdir debian +mkdir debian/DEBIAN +mkdir debian/usr +mkdir debian/usr/bin +cp hw-cbmc cbmc goto-cc goto-instrument \ + debian/usr/bin/ + +cat > debian/DEBIAN/control << EOM +Package: cbmc +Version: $VERSION +Section: devel +Priority: optional +Architecture: ${DEB_ARCH} +Essential: no +Depends: gcc +Recommends: mozilla | netscape +Suggests: docbook +Installed-Size: 4971 +Maintainer: Daniel Kroening [kroening@comlab.ox.ac.uk] +Provides: cbmc +Description: CBMC is a Bounded Model Checker for C and C++ programs. + . + CBMC generates traces that demonstrate how an assertion can be violated, + or proves that the assertion cannot be violated within a given number + of loop iterations. +EOM + +echo Building cbmc_${VERSION}_${DEB_ARCH}.deb +dpkg -b debian cbmc_${VERSION}_${DEB_ARCH}.deb + +echo Copying. +scp cbmc-${VERSION_FILE}-linux-${BITS}.tgz \ + cbmc_${VERSION}_${DEB_ARCH}.deb \ + kroening@dkr0.inf.ethz.ch:/home/www/cprover.org/cbmc/download/ + +cd /tmp +rm -R /tmp/cbmc-dist diff --git a/src/cbmc/dist-macos b/src/cbmc/dist-macos new file mode 100644 index 00000000000..2a0ecd4319c --- /dev/null +++ b/src/cbmc/dist-macos @@ -0,0 +1,46 @@ +#!/bin/bash +umask u=rwx,g=rx,o=rx + +# http://s.sudre.free.fr/Stuff/PackageMaker_Howto.html + +make +strip cbmc + +VERSION=`./cbmc --version` +VERSION_FILE=`echo $VERSION | sed "y/./-/"` +BITS=`getconf LONG_BIT` + +echo $VERSION_FILE + +(cd ../goto-cc; make; strip goto-cc) +(cd ../hw-cbmc; make; strip hw-cbmc) +(cd ../goto-instrument; make; strip goto-instrument) + +mkdir /tmp/cbmc-dist +mkdir /tmp/cbmc-dist/package-root +mkdir /tmp/cbmc-dist/package-root/usr +mkdir /tmp/cbmc-dist/package-root/usr/bin +mkdir /tmp/cbmc-dist/resources +mkdir /tmp/cbmc-dist/resources/en.lproj + +cp ../hw-cbmc/hw-cbmc ../cbmc/cbmc ../goto-cc/goto-cc \ + ../goto-instrument/goto-instrument /tmp/cbmc-dist/package-root/usr/bin +cp ../../LICENSE /tmp/cbmc-dist/resources/License.txt + +echo "Building cbmc-${VERSION_FILE}.pkg (${BITS} bits)" + +/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker \ + --root /tmp/cbmc-dist/package-root/ \ + -o /tmp/cbmc-dist/cbmc-${VERSION_FILE}.pkg \ + --version $VERSION \ + --title "CBMC ${VERSION}" \ + --resources /tmp/cbmc-dist/resources/ \ + --target 10.5 \ + --id org.cprover.cbmc + +echo Copying. +scp /tmp/cbmc-dist/cbmc-${VERSION_FILE}.pkg \ + kroening@dkr0.inf.ethz.ch:/home/www/cprover.org/cbmc/download/ + +cd /tmp +rm -R /tmp/cbmc-dist diff --git a/src/cbmc/dist-win b/src/cbmc/dist-win new file mode 100644 index 00000000000..28cb2b6b477 --- /dev/null +++ b/src/cbmc/dist-win @@ -0,0 +1,29 @@ +#!/bin/bash + +make +strip cbmc.exe + +VERSION=`./cbmc.exe --version` +VERSION_FILE=`echo $VERSION | sed "y/./-/"` + +echo $VERSION_FILE + +(cd ../goto-cc; make; strip goto-cc.exe ; cp goto-cc.exe goto-cl.exe) +(cd ../hw-cbmc; make; strip hw-cbmc.exe) +(cd ../goto-instrument; make; strip goto-instrument.exe) + +mkdir /tmp/cbmc-dist +cp ../hw-cbmc/hw-cbmc.exe ../cbmc/cbmc.exe \ + ../goto-cc/goto-cl.exe ../goto-instrument/goto-instrument.exe \ + /tmp/cbmc-dist/ +cp ../../LICENSE /tmp/cbmc-dist/LICENSE.txt +unix2dos /tmp/cbmc-dist/LICENSE.txt +cd /tmp/cbmc-dist +zip -9 cbmc-${VERSION_FILE}-win.zip hw-cbmc.exe cbmc.exe \ + goto-instrument.exe LICENSE.txt README.txt + +echo Copying. +scp cbmc-${VERSION_FILE}-win.zip kroening@dkr0.inf.ethz.ch:/home/www/cprover.org/cbmc/download/ + +cd /tmp +rm -R /tmp/cbmc-dist diff --git a/src/cbmc/languages.cpp b/src/cbmc/languages.cpp new file mode 100644 index 00000000000..22cf9ca60ef --- /dev/null +++ b/src/cbmc/languages.cpp @@ -0,0 +1,47 @@ +/*******************************************************************\ + +Module: Language Registration + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#ifdef HAVE_CPP +#include +#endif + +#ifdef HAVE_SPECC +#include +#endif + +#include "parseoptions.h" + +/*******************************************************************\ + +Function: cbmc_parseoptionst::register_languages + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cbmc_parseoptionst::register_languages() +{ + register_language(new_ansi_c_language); + + #ifdef HAVE_CPP + register_language(new_cpp_language); + #endif + + #ifdef HAVE_SPECC + register_language(new_specc_language); + #endif +} + diff --git a/src/cbmc/main.cpp b/src/cbmc/main.cpp new file mode 100644 index 00000000000..233cd8efcf3 --- /dev/null +++ b/src/cbmc/main.cpp @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: Main Module + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +/* + + CBMC + Bounded Model Checking for ANSI-C + Copyright (C) 2001-2005 Daniel Kroening + +*/ + +#include "parseoptions.h" + +/*******************************************************************\ + +Function: main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int main(int argc, const char **argv) +{ + cbmc_parseoptionst parseoptions(argc, argv); + return parseoptions.main(); +} diff --git a/src/cbmc/parseoptions.cpp b/src/cbmc/parseoptions.cpp new file mode 100644 index 00000000000..4534af7ad5f --- /dev/null +++ b/src/cbmc/parseoptions.cpp @@ -0,0 +1,813 @@ +/*******************************************************************\ + +Module: Main Module + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "parseoptions.h" +#include "bmc.h" +#include "version.h" +#include "xml_interface.h" + +/*******************************************************************\ + +Function: cbmc_parseoptionst::cbmc_parseoptionst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cbmc_parseoptionst::cbmc_parseoptionst(int argc, const char **argv): + parseoptions_baset(CBMC_OPTIONS, argc, argv), + xml_interfacet(cmdline), + language_uit("CBMC " CBMC_VERSION, cmdline) +{ +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::cbmc_parseoptionst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +::cbmc_parseoptionst::cbmc_parseoptionst( + int argc, + const char **argv, + const std::string &extra_options): + parseoptions_baset(CBMC_OPTIONS+extra_options, argc, argv), + xml_interfacet(cmdline), + language_uit("CBMC " CBMC_VERSION, cmdline) +{ +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::set_verbosity + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cbmc_parseoptionst::set_verbosity(messaget &message) +{ + int v=8; + + if(cmdline.isset("verbosity")) + { + v=atoi(cmdline.getval("verbosity")); + if(v<0) + v=0; + else if(v>9) + v=9; + } + + message.set_verbosity(v); +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::get_command_line_options + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cbmc_parseoptionst::get_command_line_options(optionst &options) +{ + if(config.set(cmdline)) + { + usage_error(); + exit(1); + } + + if(cmdline.isset("program-only")) + options.set_option("program-only", true); + + if(cmdline.isset("show-vcc")) + options.set_option("show-vcc", true); + + if(cmdline.isset("no-simplify")) + options.set_option("simplify", false); + else + options.set_option("simplify", true); + + if(cmdline.isset("all-claims")) + options.set_option("all-claims", true); + else + options.set_option("all-claims", false); + + if(cmdline.isset("unwind")) + options.set_option("unwind", cmdline.getval("unwind")); + + if(cmdline.isset("depth")) + options.set_option("depth", cmdline.getval("depth")); + + if(cmdline.isset("debug-level")) + options.set_option("debug-level", cmdline.getval("debug-level")); + + if(cmdline.isset("slice-by-trace")) + options.set_option("slice-by-trace", cmdline.getval("slice-by-trace")); + + if(cmdline.isset("unwindset")) + options.set_option("unwindset", cmdline.getval("unwindset")); + + // substitution previous expressions + if(cmdline.isset("no-substitution")) + options.set_option("substitution", false); + else + options.set_option("substitution", true); + + // check array bounds + if(cmdline.isset("bounds-check")) + options.set_option("bounds-check", true); + else + options.set_option("bounds-check", false); + + // check division by zero + if(cmdline.isset("div-by-zero-check")) + options.set_option("div-by-zero-check", true); + else + options.set_option("div-by-zero-check", false); + + // check overflow/underflow + if(cmdline.isset("overflow-check")) + options.set_option("overflow-check", true); + else + options.set_option("overflow-check", false); + + // check for NaN (not a number) + if(cmdline.isset("nan-check")) + options.set_option("nan-check", true); + else + options.set_option("nan-check", false); + + // check pointers + if(cmdline.isset("pointer-check")) + options.set_option("pointer-check", true); + else + options.set_option("pointer-check", false); + + // check assertions + if(cmdline.isset("no-assertions")) + options.set_option("assertions", false); + else + options.set_option("assertions", true); + + // use assumptions + if(cmdline.isset("no-assumptions")) + options.set_option("assumptions", false); + else + options.set_option("assumptions", true); + + // magic error label + if(cmdline.isset("error-label")) + options.set_option("error-label", cmdline.getval("error-label")); + + // generate unwinding assertions + options.set_option("unwinding-assertions", + !cmdline.isset("no-unwinding-assertions")); + + // generate unwinding assumptions otherwise + options.set_option("partial-loops", + cmdline.isset("partial-loops")); + + // remove unused equations + options.set_option("slice-formula", + cmdline.isset("slice-formula")); + + // simplify if conditions and branches + if(cmdline.isset("no-simplify-if")) + options.set_option("simplify-if", false); + else + options.set_option("simplify-if", true); + + if(cmdline.isset("arrays-uf-always")) + options.set_option("arrays-uf", "always"); + else if(cmdline.isset("arrays-uf-never")) + options.set_option("arrays-uf", "never"); + else + options.set_option("arrays-uf", "auto"); + + if(cmdline.isset("dimacs")) + options.set_option("dimacs", true); + + if(cmdline.isset("refine")) + options.set_option("refine", true); + + if(cmdline.isset("boolector")) + options.set_option("boolector", true); + + if(cmdline.isset("cvc")) + options.set_option("cvc", true); + + if(cmdline.isset("smt1")) + options.set_option("smt1", true); + + if(cmdline.isset("smt2")) + options.set_option("smt2", true); + + if(cmdline.isset("yices")) + options.set_option("yices", true); + + if(cmdline.isset("z3")) + options.set_option("z3", true); + + if(cmdline.isset("beautify-pbs")) + options.set_option("beautify-pbs", true); + + if(cmdline.isset("beautify-greedy")) + options.set_option("beautify-greedy", true); + + options.set_option("pretty-names", + !cmdline.isset("no-pretty-names")); + + if(cmdline.isset("outfile")) + options.set_option("outfile", cmdline.getval("outfile")); +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::doit + + Inputs: + + Outputs: + + Purpose: invoke main modules + +\*******************************************************************/ + +int cbmc_parseoptionst::doit() +{ + if(cmdline.isset("version")) + { + std::cout << CBMC_VERSION << std::endl; + return 0; + } + + // + // unwinding of transition systems + // + + if(cmdline.isset("module") || + cmdline.isset("gen-interface")) + + { + error("This version of CBMC has no support for " + " hardware modules. Please use hw-cbmc."); + return 1; + } + + register_languages(); + + bmct bmc(context, ui_message_handler); + + // + // command line options + // + + get_command_line_options(bmc.options); + set_verbosity(bmc); + set_verbosity(*this); + + if(cmdline.isset("preprocess")) + { + preprocessing(); + return 0; + } + + goto_functionst goto_functions; + + if(get_goto_program(bmc, goto_functions)) + return 6; + + if(cmdline.isset("show-claims")) + { + const namespacet ns(context); + show_claims(ns, get_ui(), goto_functions); + return 0; + } + + if(set_claims(goto_functions)) + return 7; + + // do actual BMC + return do_bmc(bmc, goto_functions); +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::set_claims + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cbmc_parseoptionst::set_claims(goto_functionst &goto_functions) +{ + try + { + if(cmdline.isset("claim")) + ::set_claims(goto_functions, cmdline.get_values("claim")); + } + + catch(const char *e) + { + error(e); + return true; + } + + catch(const std::string e) + { + error(e); + return true; + } + + catch(int) + { + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::get_goto_program + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cbmc_parseoptionst::get_goto_program( + bmc_baset &bmc, + goto_functionst &goto_functions) +{ + if(cmdline.args.size()==0) + { + error("Please provide a program to verify"); + return true; + } + + try + { + if(cmdline.args.size()==1 && + is_goto_binary(cmdline.args[0])) + { + status("Reading GOTO program from file"); + + if(read_goto_binary(cmdline.args[0], + context, goto_functions, get_message_handler())) + return true; + + config.ansi_c.set_from_context(context); + + if(cmdline.isset("show-symbol-table")) + { + show_symbol_table(); + return true; + } + + if(context.symbols.find(ID_main)==context.symbols.end()) + { + error("The goto binary has no entry point; please complete linking"); + return true; + } + } + else + { + if(parse()) return true; + if(typecheck()) return true; + if(get_modules(bmc)) return true; + if(final()) return true; + + // we no longer need any parse trees or language files + clear_parse(); + + if(cmdline.isset("show-symbol-table")) + { + show_symbol_table(); + return true; + } + + if(context.symbols.find(ID_main)==context.symbols.end()) + { + error("No entry point; please provide a main function"); + return true; + } + + status("Generating GOTO Program"); + + goto_convert( + context, bmc.options, goto_functions, + ui_message_handler); + } + + if(cmdline.isset("interpreter")) + { + status("Starting interpreter"); + interpreter(context, goto_functions); + return true; + } + + if(process_goto_program(bmc, goto_functions)) + return true; + } + + catch(const char *e) + { + error(e); + return true; + } + + catch(const std::string e) + { + error(e); + return true; + } + + catch(int) + { + return true; + } + + catch(std::bad_alloc) + { + error("Out of memory"); + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::preprocessing + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cbmc_parseoptionst::preprocessing() +{ + try + { + if(cmdline.args.size()!=1) + { + error("Please provide one program to preprocess"); + return; + } + + std::string filename=cmdline.args[0]; + + std::ifstream infile(filename.c_str()); + + if(!infile) + { + error("failed to open input file"); + return; + } + + languaget *ptr=get_language_from_filename(filename); + + if(ptr==NULL) + { + error("failed to figure out type of file"); + return; + } + + std::auto_ptr language(ptr); + + if(language->preprocess( + infile, filename, std::cout, get_message_handler())) + error("PREPROCESSING ERROR"); + } + + catch(const char *e) + { + error(e); + } + + catch(const std::string e) + { + error(e); + } + + catch(int) + { + } + + catch(std::bad_alloc) + { + error("Out of memory"); + } +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::process_goto_program + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cbmc_parseoptionst::process_goto_program( + bmc_baset &bmc, + goto_functionst &goto_functions) +{ + try + { + namespacet ns(context); + + if(cmdline.isset("string-abstraction")) + string_instrumentation( + context, get_message_handler(), goto_functions); + + status("Function Pointer Removal"); + remove_function_pointers(ns, goto_functions); + + status("Partial Inlining"); + // do partial inlining + goto_partial_inline(goto_functions, ns, ui_message_handler); + + status("Generic Property Instrumentation"); + // add generic checks + goto_check(ns, bmc.options, goto_functions); + + if(cmdline.isset("string-abstraction")) + { + status("String Abstraction"); + string_abstraction(context, + get_message_handler(), goto_functions); + } + + // add failed symbols + // needs to be done before pointer analysis + add_failed_symbols(context); + + if(cmdline.isset("pointer-check") || + cmdline.isset("show-value-sets")) + { + status("Pointer Analysis"); + value_set_analysist value_set_analysis(ns); + value_set_analysis(goto_functions); + + // show it? + if(cmdline.isset("show-value-sets")) + { + show_value_sets(get_ui(), goto_functions, value_set_analysis); + return true; + } + + status("Adding Pointer Checks"); + + // add pointer checks + pointer_checks( + goto_functions, ns, bmc.options, value_set_analysis); + } + + // recalculate numbers, etc. + goto_functions.update(); + + // add loop ids + goto_functions.compute_loop_numbers(); + + // show it? + if(cmdline.isset("show-loops")) + { + show_loop_numbers(get_ui(), goto_functions); + return true; + } + + // show it? + if(cmdline.isset("show-goto-functions")) + { + goto_functions.output(ns, std::cout); + return true; + } + } + + catch(const char *e) + { + error(e); + return true; + } + + catch(const std::string e) + { + error(e); + return true; + } + + catch(int) + { + return true; + } + + catch(std::bad_alloc) + { + error("Out of memory"); + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::do_bmc + + Inputs: + + Outputs: + + Purpose: invoke main modules + +\*******************************************************************/ + +int cbmc_parseoptionst::do_bmc( + bmc_baset &bmc, + const goto_functionst &goto_functions) +{ + bmc.set_ui(get_ui()); + + // do actual BMC + if(bmc.run(goto_functions)) + return 10; + + return 0; +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::help + + Inputs: + + Outputs: + + Purpose: display command line help + +\*******************************************************************/ + +void cbmc_parseoptionst::help() +{ + std::cout << + "\n" + "* * CBMC " CBMC_VERSION " - Copyright (C) 2001-2008 * *\n" + "* * Daniel Kroening, Edmund Clarke * *\n" + "* * Carnegie Mellon University, Computer Science Department * *\n" + "* * kroening@kroening.com * *\n" + "* * Protected in part by U.S. patent 7,225,417 * *\n" + "\n" + "Usage: Purpose:\n" + "\n" + " cbmc [-?] [-h] [--help] show help\n" + " cbmc file.c ... source file names\n" + "\n" + "Frontend options:\n" + " -I path set include path (C/C++)\n" + " -D macro define preprocessor macro (C/C++)\n" + " --preprocess stop after preprocessing\n" + " --16, --32, --64 set width of int\n" + " --LP64, --ILP64, --LLP64,\n" + " --ILP32, --LP32 set width of int, long and pointers\n" + " --little-endian allow little-endian word-byte conversions\n" + " --big-endian allow big-endian word-byte conversions\n" + " --unsigned-char make \"char\" unsigned by default\n" + " --show-symbol-table show symbol table\n" + " --show-goto-functions show goto program\n" + " --ppc-macos set MACOS/PPC architecture\n" + #ifdef _WIN32 + " --i386-macos set MACOS/I386 architecture\n" + " --i386-linux set Linux/I386 architecture\n" + " --i386-win32 set Windows/I386 architecture (default)\n" + " --winx64 set Windows/X64 architecture\n" + #else + #ifdef __APPLE__ + " --i386-macos set MACOS/I386 architecture (default)\n" + " --i386-linux set Linux/I386 architecture\n" + " --i386-win32 set Windows/I386 architecture\n" + " --winx64 set Windows/X64 architecture\n" + #else + " --i386-macos set MACOS/I386 architecture\n" + " --i386-linux set Linux/I386 architecture (default)\n" + " --i386-win32 set Windows/I386 architecture\n" + " --winx64 set Windows/X64 architecture\n" + #endif + #endif + " --no-arch don't set up an architecture\n" + " --no-library disable built-in abstract C library\n" + " --round-to-nearest IEEE floating point rounding mode (default)\n" + " --round-to-plus-inf IEEE floating point rounding mode\n" + " --round-to-minus-inf IEEE floating point rounding mode\n" + " --round-to-zero IEEE floating point rounding mode\n" + " --interpreter do concrete execution\n" + "\n" + "Program instrumentation options:\n" + " --bounds-check enable array bounds checks\n" + " --div-by-zero-check enable division by zero checks\n" + " --pointer-check enable pointer checks\n" + " --overflow-check enable arithmetic over- and underflow checks\n" + " --nan-check check floating-point for NaN\n" + " --all-claims keep all claims\n" + " --show-claims only show claims\n" + " --show-loops show the loops in the program\n" + " --no-assertions ignore user assertions\n" + " --no-assumptions ignore user assumptions\n" + " --error-label label check that label is unreachable\n" + "\n" + "BMC options:\n" + " --function name set main function name\n" + " --claim nr only check specific claim\n" + " --program-only only show program expression\n" + " --depth nr limit search depth\n" + " --unwind nr unwind nr times\n" + " --unwindset L:B,... unwind loop L with a bound of B\n" + " (use --show-loops to get the loop IDs)\n" + " --show-vcc show the verification conditions\n" + " --slice-formula remove assignments unrelated to property\n" + " --no-unwinding-assertions do not generate unwinding assertions\n" + " --no-pretty-names do not simplify identifiers\n" + "\n" + "Backend options:\n" + " --dimacs generate CNF in DIMACS format\n" + " --beautify-greedy beautify the counterexample (greedy heuristic)\n" + " --smt1 output subgoals in SMT1 syntax (experimental)\n" + " --smt2 output subgoals in SMT2 syntax (experimental)\n" + " --boolector use Boolector (experimental)\n" + " --cvc use CVC3 (experimental)\n" + " --yices use Yices (experimental)\n" + " --z3 use Z3 (experimental)\n" + " --refine use refinement procedure (experimental)\n" + " --outfile Filename output to given file\n" + " --arrays-uf-never never turn arrays into uninterpreted functions\n" + " --arrays-uf-always always turn arrays into uninterpreted functions\n" + "\n" + "Other options:\n" + " --version show version and exit\n" + " --xml-ui use XML-formatted output\n" + " --xml-interface stdio-XML interface\n" + "\n"; +} diff --git a/src/cbmc/parseoptions.h b/src/cbmc/parseoptions.h new file mode 100644 index 00000000000..0b07dae1a7d --- /dev/null +++ b/src/cbmc/parseoptions.h @@ -0,0 +1,88 @@ +/*******************************************************************\ + +Module: Command Line Parsing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CBMC_PARSEOPTIONS_H +#define CPROVER_CBMC_PARSEOPTIONS_H + +#include +#include + +#include + +#include "xml_interface.h" +#include "bmc.h" + +#define CBMC_OPTIONS \ + "(program-only)(function):(preprocess)(slice-by-trace):" \ + "(no-simplify)(unwind):(unwindset):(slice-formula)" \ + "(debug-level):(no-substitution)(no-simplify-if)" \ + "(bounds-check)(outfile):(pointer-check)" \ + "(document-subgoals)(all-claims)D:I:(depth):" \ + "(div-by-zero-check)(no-unwinding-assertions)" \ + "(partial-loops)" \ + "(xml-ui)(xml-interface)" \ + "(cvc)(smt1)(smt2)(boolector)(yices)(z3)" \ + "(no-pretty-names)(overflow-check)(beautify-greedy)(beautify-pbs)" \ + "(floatbv)(fixedbv)(no-assertions)(no-assumptions)(nan-check)" \ + "(dimacs)(refine)" \ + "(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \ + "(little-endian)(big-endian)" \ + "(show-goto-functions)(show-value-sets)(show-loops)" \ + "(show-symbol-table)(show-vcc)(show-claims)(claim):" \ + "(error-label):(verbosity):(no-library)" \ + "(version)" \ + "(i386-linux)(i386-macos)(i386-win32)(win32)(winx64)" \ + "(ppc-macos)(unsigned-char)" \ + "(arrays-uf-always)(arrays-uf-never)(interpreter)" \ + "(string-abstraction)(no-arch)" \ + "(round-to-nearest)(round-to-plus-inf)(round-to-minus-inf)(round-to-zero)" \ + "(decide)" // legacy, and will eventually disappear + +class cbmc_parseoptionst: + public parseoptions_baset, + public xml_interfacet, + public language_uit +{ +public: + virtual int doit(); + virtual void help(); + + cbmc_parseoptionst(int argc, const char **argv); + cbmc_parseoptionst( + int argc, + const char **argv, + const std::string &extra_options); + +protected: + virtual void register_languages(); + + virtual void get_command_line_options(optionst &options); + virtual int do_bmc(bmc_baset &bmc, const goto_functionst &goto_functions); + + virtual bool get_goto_program( + bmc_baset &bmc, + goto_functionst &goto_functions); + + virtual bool process_goto_program( + bmc_baset &bmc, + goto_functionst &goto_functions); + + bool set_claims(goto_functionst &goto_functions); + + void set_verbosity(messaget &message); + + // get any additional stuff before finalizing + virtual bool get_modules(bmc_baset &bmc) + { + return false; + } + + void preprocessing(); +}; + +#endif diff --git a/src/cbmc/show_vcc.cpp b/src/cbmc/show_vcc.cpp new file mode 100644 index 00000000000..a8bdef2ff80 --- /dev/null +++ b/src/cbmc/show_vcc.cpp @@ -0,0 +1,114 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include +#include + +#include + +#include "bmc.h" + +/*******************************************************************\ + +Function: bmc_baset::show_vcc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::show_vcc(std::ostream &out) +{ + switch(ui) + { + case ui_message_handlert::OLD_GUI: + case ui_message_handlert::XML_UI: + error("not supported"); + return; + + case ui_message_handlert::PLAIN: + break; + + default: + assert(false); + } + + out << std::endl << "VERIFICATION CONDITIONS:" << std::endl << std::endl; + + languagest languages(ns, new_ansi_c_language()); + + for(symex_target_equationt::SSA_stepst::iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); it++) + { + if(!it->is_assert()) continue; + + if(it->source.pc->location.is_not_nil()) + out << it->source.pc->location << std::endl; + + if(it->comment!="") + out << it->comment << std::endl; + + symex_target_equationt::SSA_stepst::const_iterator + p_it=equation.SSA_steps.begin(); + + for(unsigned count=1; p_it!=it; p_it++) + if(p_it->is_assume() || p_it->is_assignment()) + if(!p_it->ignore) + { + std::string string_value; + languages.from_expr(p_it->cond_expr, string_value); + out << "{-" << count << "} " << string_value << std::endl; + count++; + } + + out << "|--------------------------" << std::endl; + + std::string string_value; + languages.from_expr(it->cond_expr, string_value); + out << "{" << 1 << "} " << string_value << std::endl; + + out << std::endl; + } +} + +/*******************************************************************\ + +Function: bmc_baset::show_vcc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bmc_baset::show_vcc() +{ + const std::string &filename=options.get_option("outfile"); + + if(filename.empty() || filename=="-") + show_vcc(std::cout); + else + { + std::ofstream out(filename.c_str()); + if(!out) + std::cerr << "failed to open " << filename << std::endl; + else + show_vcc(out); + } +} + diff --git a/src/cbmc/symex_bmc.cpp b/src/cbmc/symex_bmc.cpp new file mode 100644 index 00000000000..b105ed83848 --- /dev/null +++ b/src/cbmc/symex_bmc.cpp @@ -0,0 +1,160 @@ +/*******************************************************************\ + +Module: Bounded Model Checking for ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "symex_bmc.h" + +/*******************************************************************\ + +Function: symex_bmct::symex_bmct + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +symex_bmct::symex_bmct( + const namespacet &_ns, + contextt &_new_context, + symex_targett &_target): + goto_symext(_ns, _new_context, _target) +{ +} + +/*******************************************************************\ + +Function: symex_bmct::symex_step + + Inputs: + + Outputs: + + Purpose: show progress + +\*******************************************************************/ + +void symex_bmct::symex_step( + const goto_functionst &goto_functions, + statet &state) +{ + const locationt &location=state.source.pc->location; + + if(!location.is_nil() && last_location!=location) + { + print(9, "File "+location.get_string("file")+ + " line "+location.get_string("line")+ + " function "+location.get_string("function")); + + last_location=location; + } + + goto_symext::symex_step(goto_functions, state); +} + +/*******************************************************************\ + +Function: symex_bmct::get_unwind + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool symex_bmct::get_unwind( + const symex_targett::sourcet &source, + unsigned unwind) +{ + irep_idt id=id2string(source.pc->function)+"."+ + i2string(source.pc->loop_number); + unsigned long this_loop_max_unwind=max_unwind; + + if(unwind_set.count(id)!=0) + this_loop_max_unwind=unwind_set[id]; + + #if 1 + { + std::string msg= + "Unwinding loop "+id2string(id)+" iteration "+i2string(unwind)+ + " "+source.pc->location.as_string(); + print(8, msg); + } + #endif + + return this_loop_max_unwind!=0 && + unwind>=this_loop_max_unwind; +} + +/*******************************************************************\ + +Function: symex_bmct::get_unwind_recursion + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool symex_bmct::get_unwind_recursion( + const irep_idt &identifier, + unsigned unwind) +{ + unsigned long this_loop_max_unwind=max_unwind; + + #if 1 + if(unwind!=0) + { + const symbolt &symbol=ns.lookup(identifier); + + std::string msg= + "Unwinding recursion "+ + id2string(symbol.display_name())+ + " iteration "+i2string(unwind); + + if(this_loop_max_unwind!=0) + msg+=" ("+i2string(this_loop_max_unwind)+" max)"; + + print(8, msg); + } + #endif + + return this_loop_max_unwind!=0 && + unwind>=this_loop_max_unwind; +} + +/*******************************************************************\ + +Function: symex_bmct::no_body + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_bmct::no_body(const irep_idt &identifier) +{ + if(body_warnings.insert(identifier).second) + { + std::string msg= + "**** WARNING: no body for function "+id2string(identifier); + + print(2, msg); + } +} diff --git a/src/cbmc/symex_bmc.h b/src/cbmc/symex_bmc.h new file mode 100644 index 00000000000..76f9bd0b959 --- /dev/null +++ b/src/cbmc/symex_bmc.h @@ -0,0 +1,55 @@ +/*******************************************************************\ + +Module: Bounded Model Checking for ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CBMC_SYMEX_BMC_H +#define CPROVER_CBMC_SYMEX_BMC_H + +#include +#include +#include + +class symex_bmct: + public goto_symext, + public messaget +{ +public: + symex_bmct( + const namespacet &_ns, + contextt &_new_context, + symex_targett &_target); + + // To show progress + irept last_location; + + // control unwinding + unsigned long max_unwind; + std::map unwind_set; + +protected: + // + // overloaded from goto_symext + // + virtual void symex_step( + const goto_functionst &goto_functions, + statet &state); + + // for loop unwinding + virtual bool get_unwind( + const symex_targett::sourcet &source, + unsigned unwind); + + virtual bool get_unwind_recursion( + const irep_idt &identifier, + unsigned unwind); + + virtual void no_body(const irep_idt &identifier); + + hash_set_cont body_warnings; +}; + +#endif diff --git a/src/cbmc/version.h b/src/cbmc/version.h new file mode 100644 index 00000000000..8cd86a2627b --- /dev/null +++ b/src/cbmc/version.h @@ -0,0 +1 @@ +#define CBMC_VERSION "3.9" diff --git a/src/cbmc/xml_interface.cpp b/src/cbmc/xml_interface.cpp new file mode 100644 index 00000000000..a2bbd01a0b7 --- /dev/null +++ b/src/cbmc/xml_interface.cpp @@ -0,0 +1,85 @@ +/*******************************************************************\ + +Module: XML Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "xml_interface.h" + +/*******************************************************************\ + +Function: xml_interfacet::get_xml_options + + Inputs: + + Outputs: + + Purpose: XML User Interface + +\*******************************************************************/ + +void xml_interfacet::get_xml_options(cmdlinet &cmdline) +{ + if(cmdline.isset("xml-interface")) + { + null_message_handlert message_handler; + xmlt xml; + + parse_xml(std::cin, "", message_handler, xml); + + get_xml_options(xml, cmdline); + + cmdline.set("xml-ui"); + } +} + +/*******************************************************************\ + +Function: xml_interfacet::get_xml_options + + Inputs: + + Outputs: + + Purpose: XML User Interface + +\*******************************************************************/ + +void xml_interfacet::get_xml_options( + const xmlt &xml, + cmdlinet &cmdline) +{ + for(xmlt::elementst::const_iterator + e_it=xml.elements.begin(); + e_it!=xml.elements.end(); + e_it++) + { + // recursive call + get_xml_options(*e_it, cmdline); + } + + if(xml.name=="valueOption") + { + std::string name=xml.get_attribute("name"); + std::string value=xml.get_attribute("actual"); + + if(name=="") + cmdline.args.push_back(value); + else + cmdline.set(name, value); + } + else if(xml.name=="flagOption") + { + if(xml.get_attribute("actual")=="on") + { + cmdline.set(xml.get_attribute("name")); + } + } +} + diff --git a/src/cbmc/xml_interface.h b/src/cbmc/xml_interface.h new file mode 100644 index 00000000000..38a440f39c0 --- /dev/null +++ b/src/cbmc/xml_interface.h @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: XML Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_XML_INTERFACE_H +#define CPROVER_XML_INTERFACE_H + +#include + +class xml_interfacet +{ +public: + xml_interfacet(cmdlinet &_cmdline) + { + get_xml_options(_cmdline); + } + +protected: + void get_xml_options(cmdlinet &cmdline); + void get_xml_options(const class xmlt &xml, cmdlinet &cmdline); +}; + +#endif diff --git a/src/common b/src/common new file mode 100644 index 00000000000..045fc60b5c6 --- /dev/null +++ b/src/common @@ -0,0 +1,36 @@ +first_target: all + +CFLAGS+=$(GCCFLAGS) $(INCLUDES) +CXXFLAGS+=$(GCCFLAGS) $(INCLUDES) + +.SUFFIXES: .C .d .cpp + +.C.o: + $(CXX) -c $(CXXFLAGS) -o $@ $< + +.cpp.o: + $(CXX) -c $(CXXFLAGS) -o $@ $< + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $< + +# this one is for Visual Studio's compiler: +%.obj:%.cpp + $(CXX) $(CXXFLAGS) /nologo /c /GF /EHsc $< /Fo$@ + +%.obj:%.c + $(CC) $(INCLUDES) /nologo /c /GF /EHsc $< /Fo$@ + +# +# include a dependency file if one exists +# + +# ifeq (.depend,$(wildcard .depend)) +# include .depend +# endif + +D_FILES1 = $(SRC:.c=.d) +D_FILES = $(D_FILES1:.cpp=.d) + +-include $(D_FILES) + diff --git a/src/config.inc b/src/config.inc new file mode 100644 index 00000000000..19cbe0dc9c2 --- /dev/null +++ b/src/config.inc @@ -0,0 +1,70 @@ +CC = gcc +CXX = g++ +LD = ld +AR = ar +YACC = bison -y +YFLAGS = -v +LEX = flex +#CC = icc +#CXX = icpc + +# Linux etc. +LIBEXT = .a +OBJEXT = .o +EXEEXT = +GCCFLAGS = -Wall -MMD -O2 +LINKFLAGS = -static +LINKLIB = ld -r -o $@ $^ + +# MacOS Fat Binaries +# LIBEXT = .a +# OBJEXT = .o +# EXEEXT = +# GCCFLAGS = -Wall -MMD -O2 -force_cpusubtype_ALL -arch i386 -arch ppc -arch x86_64 +# LINKFLAGS = -force_cpusubtype_ALL -arch i386 -arch ppc -arch x86_64 +# LINKLIB = ld -r -o $@ $^ + +# use these for Cygwin: +# LIBEXT = .a +# OBJEXT = .o +# EXEEXT = +# GCCFLAGS = -Wall -MMD -O2 -mno-cygwin +# LINKFLAGS = -mno-cygwin +# LINKLIB = ld -r -o $@ $^ + +# use these for Visual Studio: +# LIBEXT = .lib +# OBJEXT = .obj +# EXEEXT = .exe +# CXX = cl +# CC = cl +# LINKLIB = lib /NOLOGO /OUT:$@ $^ + +# SAT-solvers we have +#CHAFF = ../../zChaff +#BOOLEFORCE = ../../booleforce-0.4 +#MINISAT = ../../MiniSat-p_v1.14 +MINISAT2 = ../../minisat-2.2.0 +#SMVSAT = + +LANGUAGES = ansi-c csp intrep pvs smvlang verilog vhdl netlist cpp specc \ + xmllang promela pascal bplang csharp smtlang nsf mdllang java + +# modules that we have +#MODULE_PROVER = 1 +#MODULE_IPP = 1 +#MODULE_INTERPOLATION = 1 +#MODULE_BV_REFINEMENT = 1 +#MODULE_SATQE = 1 +MODULE_CPP = 1 +#MODULE_SMTLANG = 1 +#MODULE_SPECC = 1 +#MODULE_VERILOG = 1 +#MODULE_VHDL = 1 +#MODULE_PASCAL = 1 +#MODULE_PHP = 1 +#MODULE_SIMPLIFYLANG = 1 +#MODULE_HW_CBMC = 1 +#MODULE_CEMC = 1 +#MODULE_FLOATBV = 1 +#MODULE_SMV = 1 diff --git a/src/cpp/Makefile b/src/cpp/Makefile new file mode 100644 index 00000000000..b7edd07e134 --- /dev/null +++ b/src/cpp/Makefile @@ -0,0 +1,48 @@ +SRC = cpp_id.cpp cpp_language.cpp expr2cpp.cpp cpp_parser.cpp \ + lex.yy.cpp cpp_typecheck.cpp cpp_convert_type.cpp \ + cpp_typecheck_expr.cpp cpp_typecheck_code.cpp \ + cpp_typecheck_type.cpp parse.cpp cpp_parse_tree.cpp \ + cpp_token_buffer.cpp cpp_typecheck_fargs.cpp \ + cpp_typecheck_resolve.cpp cpp_util.cpp \ + cpp_typecheck_function.cpp cpp_typecheck_namespace.cpp \ + cpp_name.cpp cpp_is_pod.cpp cpp_scope.cpp \ + cpp_typecheck_find_constructor.cpp template_map.cpp \ + cpp_scopes.cpp cpp_declarator.cpp cpp_instantiate_template.cpp \ + internal_additions.cpp cpp_type2name.cpp \ + cpp_typecheck_linkage_spec.cpp \ + cpp_typecheck_template.cpp cpp_typecheck_function_bodies.cpp \ + cpp_typecheck_initializer.cpp cpp_typecheck_compound_type.cpp \ + cpp_constructor.cpp cpp_destructor.cpp \ + cpp_final.cpp cpp_typecheck_conversions.cpp\ + cpp_typecheck_declaration.cpp cpp_declarator_converter.cpp \ + cpp_declaration.cpp cpp_namespace_spec.cpp \ + cpp_typecheck_using.cpp \ + cpp_typecheck_enum_type.cpp cpp_typecheck_bases.cpp \ + cpp_typecheck_constructor.cpp cpp_typecheck_virtual_table.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) + +INCLUDES= -I .. -I ../util + +include ../config.inc +include ../common + +all: cpp$(LIBEXT) + +GCCFLAGS += -D CPP_SYSTEMC_EXTENSION + +############################################################################### + +cpp$(LIBEXT): $(OBJ) + $(LINKLIB) + +lex.yy.cpp: scanner.l + $(LEX) -Pyycpp -olex.yy.cpp scanner.l + +lex.yy$(OBJEXT): lex.yy.cpp + +clean: + rm -f $(OBJ) cpp$(LIBEXT) lex.yy.cpp y.tab.cpp.output + +dep: lex.yy.cpp + diff --git a/src/cpp/cpp_class_type.h b/src/cpp/cpp_class_type.h new file mode 100644 index 00000000000..4c4c474fc6e --- /dev/null +++ b/src/cpp/cpp_class_type.h @@ -0,0 +1,65 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CPP_CLASS_TYPE_H +#define CPROVER_CPP_CLASS_TYPE_H + +#include + +// used for C++ structs and classes + +class class_typet:public struct_union_typet +{ +public: + inline class_typet():struct_union_typet(ID_struct) + { + } + + inline bool is_class() const + { + return get_bool(ID_C_class); + } + + inline irep_idt default_access() const + { + return is_class()?ID_private:ID_public; + } + + inline const irept::subt &bases() const + { + return find(ID_bases).get_sub(); + } + + bool has_base(const irep_idt &id) const + { + const irept::subt &b=bases(); + forall_irep(it, b) + { + assert(it->id()==ID_base); + const irept &type=it->find(ID_type); + assert(type.id()==ID_symbol); + if(type.get(ID_identifier)==id) return true; + } + + return false; + } +}; + +extern inline const class_typet &to_class_type(const typet &type) +{ + assert(type.id()==ID_struct); + return static_cast(type); +} + +extern inline class_typet &to_class_type(typet &type) +{ + assert(type.id()==ID_struct); + return static_cast(type); +} + +#endif diff --git a/src/cpp/cpp_constructor.cpp b/src/cpp/cpp_constructor.cpp new file mode 100644 index 00000000000..a13f5e3300e --- /dev/null +++ b/src/cpp/cpp_constructor.cpp @@ -0,0 +1,379 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include + +#include + +#include "cpp_typecheck.h" +#include "cpp_util.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::cpp_constructor + + Inputs: non-typchecked object, non-typechecked operands + + Outputs: typechecked code + + Purpose: + +\*******************************************************************/ + +codet cpp_typecheckt::cpp_constructor( + const locationt &location, + const exprt &object, + const exprt::operandst &operands) +{ + exprt object_tc=object; + + typecheck_expr(object_tc); + + typet tmp_type(object_tc.type()); + follow_symbol(tmp_type); + + assert(!is_reference(tmp_type)); + + if(tmp_type.id()==ID_array) + { + // We allow only one operand and it must be tagged with '#array_ini'. + // Note that the operand is an array that is used for copy-initialization. + // In the general case, a program is not allow to use this form of + // construct. This way of initializing an array is used internaly only. + // The purpose of the tag #arra_ini is to rule out ill-formed + // programs. + + if(!operands.empty() && !operands.front().get_bool("#array_ini")) + { + err_location(location); + str << "bad array initializer"; + throw 0; + } + + assert(operands.empty() || operands.size()==1); + + if(operands.empty() && cpp_is_pod(tmp_type)) + { + codet nil; + nil.make_nil(); + return nil; + } + + const exprt &size_expr= + to_array_type(tmp_type).size(); + + if(size_expr.id()=="infinity") + { + // don't initialize + codet nil; + nil.make_nil(); + return nil; + } + + mp_integer s; + if(to_integer(size_expr, s)) + { + err_location(tmp_type); + str << "array size `" << to_string(size_expr) + << "' is not a constant"; + throw 0; + } + + /*if(cpp_is_pod(tmp_type)) + { + code_expressiont new_code; + exprt op_tc = operands.front(); + typecheck_expr(op_tc); + // Override constantness + object_tc.type().set("#constant", false); + object_tc.set("#lvalue", true); + side_effect_exprt assign("assign"); + assign.location()=location; + assign.copy_to_operands(object_tc, op_tc); + typecheck_side_effect_assignment(assign); + new_code.expression()=assign; + return new_code; + } + else*/ + { + codet new_code(ID_block); + + // for each element of the array, call the default constructor + for(mp_integer i = 0; i < s; ++i) + { + exprt::operandst tmp_operands; + + exprt constant=from_integer(i, int_type()); + constant.location()=location; + + exprt index(ID_index); + index.copy_to_operands(object); + index.copy_to_operands(constant); + index.location()=location; + + if(!operands.empty()) + { + exprt operand(ID_index); + operand.copy_to_operands(operands.front()); + operand.copy_to_operands(constant); + operand.location()=location; + tmp_operands.push_back(operand); + } + + exprt i_code = + cpp_constructor(location, index, tmp_operands); + + if(i_code.is_nil()) + { + new_code.is_nil(); + break; + } + + new_code.move_to_operands(i_code); + } + return new_code; + } + } + else if(cpp_is_pod(tmp_type)) + { + code_expressiont new_code; + exprt::operandst operands_tc=operands; + + for(exprt::operandst::iterator + it=operands_tc.begin(); + it!=operands_tc.end(); + it++) + { + typecheck_expr(*it); + add_implicit_dereference(*it); + } + + if(operands_tc.size()==0) + { + // a POD is NOT initialized + new_code.make_nil(); + } + else if(operands_tc.size()==1) + { + // Override constantness + object_tc.type().set(ID_C_constant, false); + object_tc.set(ID_C_lvalue, true); + side_effect_exprt assign(ID_assign); + assign.location()=location; + assign.copy_to_operands(object_tc, operands_tc.front()); + typecheck_side_effect_assignment(assign); + new_code.expression()=assign; + } + else + { + err_location(location); + str << "initialization of POD requires one argument, " + "but got " << operands.size() << std::endl; + throw 0; + } + + return new_code; + } + else if(tmp_type.id()==ID_union) + { + assert(0); // Todo: union + } + else if(tmp_type.id()==ID_struct) + { + exprt::operandst operands_tc=operands; + + for(exprt::operandst::iterator + it=operands_tc.begin(); + it!=operands_tc.end(); + it++) + { + typecheck_expr(*it); + add_implicit_dereference(*it); + } + + const struct_typet &struct_type= + to_struct_type(tmp_type); + + // set most-derived bits + codet block(ID_block); + for(unsigned i=0; i < struct_type.components().size(); i++) + { + const irept &component = struct_type.components()[i]; + if(component.get(ID_base_name) != "@most_derived") + continue; + + exprt member(ID_member, bool_typet()); + member.set(ID_component_name, component.get(ID_name)); + member.copy_to_operands(object_tc); + member.location() = location; + member.set(ID_C_lvalue, object_tc.get_bool(ID_C_lvalue)); + + exprt val; + val.make_false(); + + if(!component.get_bool("from_base")) + val.make_true(); + + side_effect_exprt assign(ID_assign); + assign.location()=location; + assign.move_to_operands(member,val); + typecheck_side_effect_assignment(assign); + code_expressiont code_exp; + code_exp.expression()=assign; + block.move_to_operands(code_exp); + } + + // enter struct scope + cpp_save_scopet save_scope(cpp_scopes); + cpp_scopes.set_scope(struct_type.get(ID_name)); + + // find name of constructor + const struct_typet::componentst &components= + struct_type.components(); + + irep_idt constructor_name; + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + const typet &type=it->type(); + + if(!it->get_bool(ID_from_base) && + type.id()==ID_code && + type.find(ID_return_type).id()==ID_constructor) + { + constructor_name=it->get(ID_base_name); + break; + } + } + + // there is always a constructor for non-PODs + assert(constructor_name!=""); + + irept cpp_name(ID_cpp_name); + cpp_name.get_sub().push_back(irept(ID_name)); + cpp_name.get_sub().back().set(ID_identifier, constructor_name); + cpp_name.get_sub().back().set(ID_C_location, location); + + side_effect_expr_function_callt function_call; + function_call.location()=location; + function_call.function().swap(static_cast(cpp_name)); + function_call.arguments().reserve(operands_tc.size()); + + for(exprt::operandst::iterator + it=operands_tc.begin(); + it!=operands_tc.end(); + it++) + function_call.op1().copy_to_operands(*it); + + typecheck_side_effect_function_call(function_call); + assert(function_call.get(ID_statement) == ID_temporary_object); + + exprt &initializer = + static_cast(function_call.add(ID_initializer)); + + assert(initializer.id()==ID_code + && initializer.get(ID_statement)==ID_expression); + + side_effect_expr_function_callt& func_ini = + to_side_effect_expr_function_call(initializer.op0()); + + exprt& tmp_this = func_ini.arguments().front(); + assert(tmp_this.id() == ID_address_of + && tmp_this.op0().id() == "new_object"); + + exprt address_of(ID_address_of, typet(ID_pointer)); + address_of.type().subtype() = object_tc.type(); + address_of.copy_to_operands(object_tc); + tmp_this.swap(address_of); + + if(block.operands().empty()) + return to_code(initializer); + else + { + block.move_to_operands(initializer); + return block; + } + } + else + assert(false); + + codet nil; + nil.make_nil(); + return nil; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::new_temporary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::new_temporary( + const locationt &location, + const typet &type, + const exprt::operandst &ops, + exprt &temporary) +{ + // create temporary object + exprt tmp_object_expr=exprt(ID_sideeffect, type); + tmp_object_expr.set(ID_statement, ID_temporary_object); + tmp_object_expr.location()= location; + + exprt new_object(ID_new_object); + new_object.location() = tmp_object_expr.location(); + new_object.set(ID_C_lvalue, true); + new_object.type() = tmp_object_expr.type(); + + already_typechecked(new_object); + + codet new_code = + cpp_constructor(location, new_object, ops); + + if(new_code.is_not_nil()) + { + if(new_code.get(ID_statement)==ID_assign) + tmp_object_expr.move_to_operands(new_code.op1()); + else + tmp_object_expr.add(ID_initializer)=new_code; + } + + temporary.swap(tmp_object_expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::new_temporary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::new_temporary( + const locationt &location, + const typet &type, + const exprt &op, + exprt &temporary) +{ + exprt::operandst ops; + ops.push_back(op); + new_temporary(location,type,ops,temporary); +} diff --git a/src/cpp/cpp_convert_type.cpp b/src/cpp/cpp_convert_type.cpp new file mode 100644 index 00000000000..7b1f9175814 --- /dev/null +++ b/src/cpp/cpp_convert_type.cpp @@ -0,0 +1,645 @@ +/*******************************************************************\ + +Module: C++ Language Type Conversion + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include + +#include +#include +#include + +#include + +#include "cpp_convert_type.h" +#include "cpp_declaration.h" +#include "cpp_name.h" + +class cpp_convert_typet +{ +public: + unsigned unsigned_cnt, signed_cnt, char_cnt, int_cnt, short_cnt, + long_cnt, const_cnt, typedef_cnt, volatile_cnt, + double_cnt, float_cnt, complex_cnt, bool_cnt, + extern_cnt, wchar_t_cnt, + int8_cnt, int16_cnt, int32_cnt, int64_cnt, ptr32_cnt, ptr64_cnt; + + void read(const typet &type); + void write(typet &type); + + std::list other; + + cpp_convert_typet() { } + cpp_convert_typet(const typet &type) { read(type); } + +protected: + void read_rec(const typet &type); + void read_function_type(const typet &type); + void read_template(const typet &type); +}; + +/*******************************************************************\ + +Function: cpp_convert_typet::read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_convert_typet::read(const typet &type) +{ + unsigned_cnt=signed_cnt=char_cnt=int_cnt=short_cnt= + long_cnt=const_cnt=typedef_cnt=volatile_cnt= + double_cnt=float_cnt=complex_cnt=bool_cnt=extern_cnt= + wchar_t_cnt=int8_cnt=int16_cnt=int32_cnt= + int64_cnt=ptr32_cnt=ptr64_cnt=0; + + other.clear(); + + #if 0 + std::cout << "cpp_convert_typet::read: " << type.pretty() << std::endl; + #endif + + read_rec(type); +} + +/*******************************************************************\ + +Function: cpp_convert_typet::read_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_convert_typet::read_rec(const typet &type) +{ + #if 0 + std::cout << "cpp_convert_typet::read_rec: " + << type.pretty() << std::endl; + #endif + + if(type.id()==ID_merged_type) + { + forall_subtypes(it, type) + read_rec(*it); + } + else if(type.id()==ID_signed) + signed_cnt++; + else if(type.id()==ID_unsigned) + unsigned_cnt++; + else if(type.id()==ID_volatile) + volatile_cnt++; + else if(type.id()==ID_char) + char_cnt++; + else if(type.id()==ID_int) + int_cnt++; + else if(type.id()==ID_short) + short_cnt++; + else if(type.id()==ID_long) + long_cnt++; + else if(type.id()==ID_double) + double_cnt++; + else if(type.id()==ID_float) + float_cnt++; + else if(type.id()=="__complex__" || type.id()=="_Complex") + complex_cnt++; + else if(type.id()==ID_bool) + bool_cnt++; + else if(type.id()==ID_wchar_t) + wchar_t_cnt++; + else if(type.id()=="__int8") + int8_cnt++; + else if(type.id()=="__int16") + int16_cnt++; + else if(type.id()=="__int32") + int32_cnt++; + else if(type.id()=="__int64") + int64_cnt++; + else if(type.id()=="__ptr32") + ptr32_cnt++; + else if(type.id()=="__ptr64") + ptr64_cnt++; + else if(type.id()==ID_const) + const_cnt++; + else if(type.id()==ID_extern) + extern_cnt++; + else if(type.id()=="function_type") + { + read_function_type(type); + } + else if(type.id()==ID_typedef) + typedef_cnt++; + else if(type.id()==ID_identifier) + { + // from arguments + } + else if(type.id()==ID_cpp_name) + { + // from typedefs + other.push_back(type); + } + else if(type.id()==ID_array) + { + other.push_back(type); + cpp_convert_plain_type(other.back().subtype()); + } + else if(type.id()==ID_template) + { + read_template(type); + } + else if(type.id()==ID_void) + { + // we store 'void' as 'empty' + typet tmp=type; + tmp.id(ID_empty); + other.push_back(tmp); + } + else + { + other.push_back(type); + } +} + +/*******************************************************************\ + +Function: cpp_covnert_typet::read_template + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_convert_typet::read_template(const typet &type) +{ + other.push_back(type); + typet &t=other.back(); + + cpp_convert_plain_type(t.subtype()); + + irept &arguments=t.add(ID_arguments); + + Forall_irep(it, arguments.get_sub()) + { + exprt &decl=static_cast(*it); + + // may be type or expression + bool is_type=decl.get_bool(ID_is_type); + + if(is_type) + { + } + else + { + cpp_convert_plain_type(decl.type()); + } + + // TODO: initializer + } +} + +/*******************************************************************\ + +Function: cpp_convert_typet::read_function_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_convert_typet::read_function_type(const typet &type) +{ + other.push_back(type); + typet &t=other.back(); + t.id(ID_code); + + // change subtype to return_type + typet &return_type= + static_cast(t.add(ID_return_type)); + + return_type.swap(t.subtype()); + t.remove(ID_subtype); + + if(return_type.is_not_nil()) + cpp_convert_plain_type(return_type); + + // take care of argument types + irept &arguments=t.add(ID_arguments); + + // see if we have an ellipsis + if(!arguments.get_sub().empty() && + arguments.get_sub().back().id()==ID_ellipsis) + { + arguments.set(ID_ellipsis, true); + arguments.get_sub().erase(--arguments.get_sub().end()); + } + + Forall_irep(it, arguments.get_sub()) + { + exprt &argument_expr=static_cast(*it); + + if(argument_expr.id()==ID_cpp_declaration) + { + cpp_declarationt &declaration=to_cpp_declaration(argument_expr); + locationt type_location=declaration.type().location(); + + cpp_convert_plain_type(declaration.type()); + + // there should be only one declarator + assert(declaration.declarators().size()==1); + + cpp_declaratort &declarator= + declaration.declarators().front(); + + // do we have a declarator? + if(declarator.is_nil()) + { + argument_expr=exprt(ID_argument, declaration.type()); + argument_expr.location()=type_location; + } + else + { + const cpp_namet &cpp_name=declarator.name(); + typet final_type=declarator.merge_type(declaration.type()); + + // see if it's an array type + if(final_type.id()==ID_array || final_type.id()==ID_incomplete_array) + { + final_type.id(ID_pointer); + final_type.remove(ID_size); + } + + code_typet::argumentt new_argument(final_type); + + if(cpp_name.is_nil()) + { + new_argument.location()=type_location; + } + else + { + std::string identifier, base_name; + cpp_name.convert(identifier, base_name); + assert(!identifier.empty()); + new_argument.set_identifier(identifier); + new_argument.set_base_name(identifier); + new_argument.location()=cpp_name.location(); + } + + if(declarator.value().is_not_nil()) + new_argument.default_value().swap(declarator.value()); + + argument_expr.swap(new_argument); + } + } + else if(argument_expr.id()==ID_ellipsis) + throw "ellipsis only allowed as last argument"; + else + assert(0); + } + + // if we just have one argument of type void, remove it + if(arguments.get_sub().size()==1 && + arguments.get_sub().front().find(ID_type).id()==ID_empty) + arguments.get_sub().clear(); +} + +/*******************************************************************\ + +Function: cpp_convert_typet::write + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_convert_typet::write(typet &type) +{ + type.clear(); + + // first, do "other" + + if(!other.empty()) + { + if(double_cnt || float_cnt || signed_cnt || + unsigned_cnt || int_cnt || bool_cnt || + short_cnt || char_cnt || wchar_t_cnt || + int8_cnt || int16_cnt || int32_cnt || + int64_cnt) + throw "type modifier not applicable"; + + if(other.size()!=1) + throw "illegal combination of types"; + + type.swap(other.front()); + } + else if(double_cnt) + { + if(signed_cnt || unsigned_cnt || int_cnt || bool_cnt || + short_cnt || char_cnt || wchar_t_cnt || float_cnt || + int8_cnt || int16_cnt || int32_cnt || + int64_cnt || ptr32_cnt || ptr64_cnt) + throw "illegal type modifier for double"; + + if(long_cnt) + { + type=long_double_type(); + type.set(ID_C_cpp_type, ID_long_double); + } + else + { + type=double_type(); + type.set(ID_C_cpp_type, ID_double); + } + } + else if(float_cnt) + { + if(signed_cnt || unsigned_cnt || int_cnt || bool_cnt || + short_cnt || char_cnt || wchar_t_cnt || double_cnt || + int8_cnt || int16_cnt || int32_cnt || + int64_cnt || ptr32_cnt || ptr64_cnt) + throw "illegal type modifier for float"; + + if(long_cnt) + throw "float can't be long"; + + type=float_type(); + type.set(ID_C_cpp_type, ID_float); + } + else if(bool_cnt) + { + if(signed_cnt || unsigned_cnt || int_cnt || short_cnt || + char_cnt || wchar_t_cnt || + int8_cnt || int16_cnt || int32_cnt || + int64_cnt || ptr32_cnt || ptr64_cnt) + throw "illegal type modifier for bool"; + + type.id(ID_bool); + } + else if(char_cnt) + { + if(int_cnt || short_cnt || wchar_t_cnt || long_cnt || + int8_cnt || int16_cnt || int32_cnt || + int64_cnt || ptr32_cnt || ptr64_cnt) + throw "illegal type modifier for char"; + + // there are really three distinct char types in C++ + if(unsigned_cnt) + { + type.id(ID_unsignedbv); + type.set(ID_width, config.ansi_c.char_width); + type.set(ID_C_cpp_type, ID_unsigned_char); + } + else if(signed_cnt) + { + type.id(ID_signedbv); + type.set(ID_width, config.ansi_c.char_width); + type.set(ID_C_cpp_type, ID_signed_char); + } + else + { + type.id(config.ansi_c.char_is_unsigned?ID_unsignedbv:ID_signedbv); + type.set(ID_C_cpp_type, ID_char); + type.set(ID_width, config.ansi_c.char_width); + } + } + else if(wchar_t_cnt) + { + // This is a distinct type, and can't be made signed/unsigned. + // This is tolerated by most compilers, however. + + if(int_cnt || short_cnt || char_cnt || long_cnt || + int8_cnt || int16_cnt || int32_cnt || + int64_cnt || ptr32_cnt || ptr64_cnt) + throw "illegal type modifier for wchar_t"; + + type.id(ID_signedbv); + type.set(ID_width, config.ansi_c.wchar_t_width); + type.set(ID_C_cpp_type, ID_wchar_t); + } + else + { + // it must be integer -- signed or unsigned? + + if(signed_cnt && unsigned_cnt) + throw "integer cannot be both signed and unsigned"; + + if(short_cnt) + { + if(long_cnt) + throw "cannot combine short and long"; + + if(unsigned_cnt) + { + type.id(ID_unsignedbv); + type.set(ID_C_cpp_type, ID_unsigned_short_int); + } + else + { + type.id(ID_signedbv); + type.set(ID_C_cpp_type, ID_signed_short_int); + } + + type.set(ID_width, config.ansi_c.short_int_width); + } + else if(int8_cnt) + { + if(long_cnt) + throw "illegal type modifier for __int8"; + + // in terms of overloading, this behaves like "char" + if(unsigned_cnt) + { + type.id(ID_unsignedbv); + type.set(ID_C_cpp_type, ID_unsigned_char); + } + else if(signed_cnt) + { + type.id(ID_signedbv); + type.set(ID_C_cpp_type, ID_signed_char); + } + else + { + type.id(ID_signedbv); + type.set(ID_C_cpp_type, ID_char); + } + + type.set(ID_width, 8); + } + else if(int16_cnt) + { + if(long_cnt) + throw "illegal type modifier for __int16"; + + // in terms of overloading, this behaves like "short" + if(unsigned_cnt) + { + type.id(ID_unsignedbv); + type.set(ID_C_cpp_type, ID_unsigned_short_int); + } + else + { + type.id(ID_signedbv); + type.set(ID_C_cpp_type, ID_signed_short_int); + } + + type.set(ID_width, 16); + } + else if(int32_cnt) + { + if(long_cnt) + throw "illegal type modifier for __int32"; + + // in terms of overloading, this behaves like "int" + if(unsigned_cnt) + { + type.id(ID_unsignedbv); + type.set(ID_C_cpp_type, ID_unsigned_int); + } + else + { + type.id(ID_signedbv); + type.set(ID_C_cpp_type, ID_signed_int); + } + + type.set(ID_width, 32); + } + else if(int64_cnt) + { + if(long_cnt) + throw "illegal type modifier for __int64"; + + // in terms of overloading, this behaves like "long long" + if(unsigned_cnt) + { + type.id(ID_unsignedbv); + type.set(ID_C_cpp_type, ID_unsigned_long_long_int); + } + else + { + type.id(ID_signedbv); + type.set(ID_C_cpp_type, ID_signed_long_long_int); + } + + type.set(ID_width, 64); + } + else if(long_cnt==0) + { + if(unsigned_cnt) + { + type.set(ID_C_cpp_type, ID_unsigned_int); + type.id(ID_unsignedbv); + } + else + { + type.set(ID_C_cpp_type, ID_signed_int); + type.id(ID_signedbv); + } + + type.set(ID_width, config.ansi_c.int_width); + } + else if(long_cnt==1) + { + if(unsigned_cnt) + { + type.set(ID_C_cpp_type, ID_unsigned_long_int); + type.id(ID_unsignedbv); + } + else + { + type.set(ID_C_cpp_type, ID_signed_long_int); + type.id(ID_signedbv); + } + + type.set(ID_width, config.ansi_c.long_int_width); + } + else if(long_cnt==2) + { + if(unsigned_cnt) + { + type.set(ID_C_cpp_type, ID_unsigned_long_long_int); + type.id(ID_unsignedbv); + } + else + { + type.set(ID_C_cpp_type, ID_signed_long_long_int); + type.id(ID_signedbv); + } + + type.set(ID_width, config.ansi_c.long_long_int_width); + } + else + throw "illegal combination of type modifiers"; + } + + // is it constant? + if(const_cnt) + type.set(ID_C_constant, true); + + // is it volatile? + if(volatile_cnt) + type.set(ID_C_volatile, true); +} + +/*******************************************************************\ + +Function: cpp_convert_plain_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_convert_plain_type(typet &type) +{ + if(type.id()==ID_cpp_name || + type.id()==ID_struct || + type.id()==ID_union || + type.id()==ID_pointer || + type.id()==ID_array || + type.id()==ID_code || + type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bool || + type.id()==ID_floatbv || + type.id()==ID_empty || + type.id()==ID_symbol || + type.id()==ID_constructor || + type.id()==ID_destructor) + { + } + else if(type.id()==ID_c_enum) + { + // add width -- we use int, but the standard + // doesn't guarantee that + type.set(ID_width, config.ansi_c.int_width); + } + else + { + cpp_convert_typet cpp_convert_type(type); + cpp_convert_type.write(type); + } +} diff --git a/src/cpp/cpp_convert_type.h b/src/cpp/cpp_convert_type.h new file mode 100644 index 00000000000..7f14923ca98 --- /dev/null +++ b/src/cpp/cpp_convert_type.h @@ -0,0 +1,16 @@ +/*******************************************************************\ + +Module: C++ Language Conversion + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_CONVERT_TYPE_H +#define CPROVER_CPP_CONVERT_TYPE_H + +#include + +void cpp_convert_plain_type(typet &type); + +#endif diff --git a/src/cpp/cpp_declaration.cpp b/src/cpp/cpp_declaration.cpp new file mode 100644 index 00000000000..f8d8027cac7 --- /dev/null +++ b/src/cpp/cpp_declaration.cpp @@ -0,0 +1,37 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "cpp_declaration.h" + +/*******************************************************************\ + +Function: cpp_declarationt::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_declarationt::output(std::ostream &out) const +{ + out << "is_template: " << is_template() << std::endl; + out << "storage: " << storage_spec().pretty() << std::endl; + out << "template_type: " << template_type().pretty() << std::endl; + out << "type: " << type().pretty() << std::endl; + + out << "Declarators:" << std::endl; + + forall_cpp_declarators(it, *this) + { + it->output(out); + out << std::endl; + } +} diff --git a/src/cpp/cpp_declaration.h b/src/cpp/cpp_declaration.h new file mode 100644 index 00000000000..5c21ebe96c1 --- /dev/null +++ b/src/cpp/cpp_declaration.h @@ -0,0 +1,135 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_DECLARATION_H +#define CPROVER_CPP_DECLARATION_H + +#include + +#include "cpp_declarator.h" +#include "cpp_storage_spec.h" +#include "cpp_member_spec.h" +#include "cpp_template_type.h" +#include "cpp_template_args.h" + +class cpp_declarationt:public exprt +{ +public: + typedef std::vector declaratorst; + + inline cpp_declarationt():exprt(ID_cpp_declaration) + { + } + + inline bool is_constructor() const + { + return type().id()==ID_constructor; + } + + inline bool is_static_assert() const + { + return get_bool(ID_is_static_assert); + } + + inline bool is_destructor() const + { + return type().id()==ID_destructor; + } + + inline bool is_template() const + { + return get_bool(ID_is_template); + } + + inline bool is_template_class() const + { + return is_template() && + type().id()==ID_struct && + declarators().empty(); + } + + inline const declaratorst &declarators() const + { + return (const declaratorst &)operands(); + } + + inline declaratorst &declarators() + { + return (declaratorst &)operands(); + } + + inline const cpp_storage_spect &storage_spec() const + { + return static_cast( + find(ID_storage_spec)); + } + + inline cpp_storage_spect &storage_spec() + { + return static_cast( + add(ID_storage_spec)); + } + + inline const cpp_member_spect &member_spec() const + { + return static_cast( + find(ID_member_spec)); + } + + inline cpp_member_spect &member_spec() + { + return static_cast( + add(ID_member_spec)); + } + + inline template_typet &template_type() + { + return static_cast(add(ID_template_type)); + } + + inline const template_typet &template_type() const + { + return static_cast(find(ID_template_type)); + } + + inline cpp_template_args_non_tct &partial_specialization_args() + { + return static_cast(add("partial_specialization_args")); + } + + inline const cpp_template_args_non_tct &partial_specialization_args() const + { + return static_cast(find("partial_specialization_args")); + } + + inline void set_specialization_of(const irep_idt &id) + { + set("specialization_of", id); + } + + inline irep_idt get_specialization_of() const + { + return get("specialization_of"); + } + + void output(std::ostream &out) const; +}; + +extern inline cpp_declarationt &to_cpp_declaration(irept &irep) +{ + assert(irep.id()==ID_cpp_declaration); + return static_cast(irep); +} + +extern inline const cpp_declarationt &to_cpp_declaration(const irept &irep) +{ + assert(irep.id()==ID_cpp_declaration); + return static_cast(irep); +} + +#endif diff --git a/src/cpp/cpp_declarator.cpp b/src/cpp/cpp_declarator.cpp new file mode 100644 index 00000000000..7744ffb4449 --- /dev/null +++ b/src/cpp/cpp_declarator.cpp @@ -0,0 +1,72 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_declarator.h" + +/*******************************************************************\ + +Function: cpp_declaratort::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_declaratort::output(std::ostream &out) const +{ + out << " name: " << name().pretty() << std::endl; + out << " type: " << type().pretty() << std::endl; + out << " value: " << value().pretty() << std::endl; + out << " init_args: " << init_args().pretty() << std::endl; + out << " method_qualifier: " << method_qualifier().pretty() << std::endl; +} + +/*******************************************************************\ + +Function: cpp_declaratort::merge_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet cpp_declaratort::merge_type(const typet &declaration_type) const +{ + typet dest_type=type(); + + if(declaration_type.id()=="cpp-cast-operator") + return dest_type; + + typet *p=&dest_type; + + // walk down subtype until we hit nil + while(true) + { + typet &t=*p; + if(t.is_nil()) + { + t=declaration_type; + break; + } + else + { + assert(t.id()!=""); + p=&t.subtype(); + } + } + + return dest_type; +} diff --git a/src/cpp/cpp_declarator.h b/src/cpp/cpp_declarator.h new file mode 100644 index 00000000000..de10108e9a2 --- /dev/null +++ b/src/cpp/cpp_declarator.h @@ -0,0 +1,66 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_DECLARATOR_H +#define CPROVER_CPP_DECLARATOR_H + +#include + +#include "cpp_name.h" + +class cpp_declaratort:public exprt +{ +public: + cpp_declaratort():exprt(ID_cpp_declarator) + { + value().make_nil(); + name().make_nil(); + location().make_nil(); + } + + cpp_declaratort(const typet &type):exprt(ID_cpp_declarator, type) + { + value().make_nil(); + name().make_nil(); + location().make_nil(); + } + + cpp_namet &name() { return static_cast(add(ID_name)); } + const cpp_namet &name() const { return static_cast(find(ID_name)); } + + exprt &value() { return static_cast(add(ID_value)); } + const exprt &value() const { return static_cast(find(ID_value)); } + + // initializers for function arguments + exprt &init_args() { return static_cast(add("init_args")); } + const exprt &init_args() const { return static_cast(find("init_args")); } + + irept &method_qualifier() { return add(ID_method_qualifier); } + const irept &method_qualifier() const { return find(ID_method_qualifier); } + + irept &member_initializers() { return add(ID_member_initializers); } + const irept &member_initializers() const { return find(ID_member_initializers); } + + irept &throw_decl() { return add("throw_decl"); } + const irept &throw_decl() const { return find("throw_decl"); } + + void output(std::ostream &out) const; + + typet merge_type(const typet &declaration_type) const; +}; + +#define forall_cpp_declarators(it, expr) \ + for(cpp_declarationt::declaratorst::const_iterator it=(expr).declarators().begin(); \ + it!=(expr).declarators().end(); it++) + +#define Forall_cpp_declarators(it, expr) \ + if((expr).has_operands()) \ + for(cpp_declarationt::declaratorst::iterator it=(expr).declarators().begin(); \ + it!=(expr).declarators().end(); it++) + +#endif diff --git a/src/cpp/cpp_declarator_converter.cpp b/src/cpp/cpp_declarator_converter.cpp new file mode 100644 index 00000000000..a56d4616785 --- /dev/null +++ b/src/cpp/cpp_declarator_converter.cpp @@ -0,0 +1,675 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include + +#include "cpp_type2name.h" +#include "cpp_declarator_converter.h" +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_declarator_convertert::cpp_declarator_convertert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cpp_declarator_convertert::cpp_declarator_convertert( + class cpp_typecheckt &_cpp_typecheck): + is_typedef(false), + is_template(false), + is_template_argument(false), + is_friend(false), + mode(_cpp_typecheck.current_mode), + cpp_typecheck(_cpp_typecheck) +{ +} + +/*******************************************************************\ + +Function: cpp_declarator_convertert::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +symbolt &cpp_declarator_convertert::convert( + const typet &type, + const cpp_storage_spect &storage_spec, + const cpp_member_spect &member_spec, + cpp_declaratort &declarator) +{ + assert(type.is_not_nil()); + + if(type.id()=="cpp-cast-operator") + { + typet type; + type.swap(declarator.name().get_sub().back()); + declarator.type().subtype()=type; + std::string tmp; + cpp_typecheck.typecheck_type(type); + irept name(ID_name); + name.set(ID_identifier, "("+cpp_type2name(type)+")"); + declarator.name().get_sub().back().swap(name); + } + + assert(declarator.id()==ID_cpp_declarator); + final_type=declarator.merge_type(type); + assert(final_type.is_not_nil()); + + cpp_template_args_non_tct template_args; + + { + cpp_save_scopet save_scope(cpp_typecheck.cpp_scopes); + + cpp_typecheck_resolvet cpp_typecheck_resolve(cpp_typecheck); + + cpp_typecheck_resolve.resolve_scope( + declarator.name(), base_name, template_args); + + scope=&cpp_typecheck.cpp_scopes.current_scope(); + } + + cpp_typecheck.typecheck_type(final_type); + is_code=is_code_type(final_type); + + if(scope->is_global_scope()) + cpp_typecheck.check_array_types(final_type); + + get_final_identifier(); + + // first see if it is a member + if(scope->id_class==cpp_idt::CLASS && !is_friend) + { + // it's a member! it must be declared already + + typet &method_qualifier= + static_cast(declarator.method_qualifier()); + + // adjust template type + if(final_type.id()==ID_template) + { + assert(0); + typet tmp; + tmp.swap(final_type.subtype()); + final_type.swap(tmp); + } + + // try static first + contextt::symbolst::iterator c_it= + cpp_typecheck.context.symbols.find(final_identifier); + + if(c_it==cpp_typecheck.context.symbols.end()) + { + // adjust type if it's a non-static member function + if(final_type.id()==ID_code) + cpp_typecheck.adjust_method_type( + scope->identifier, final_type, method_qualifier); + + get_final_identifier(); + + // try again + c_it = cpp_typecheck.context.symbols.find(final_identifier); + + if(c_it==cpp_typecheck.context.symbols.end()) + { + cpp_typecheck.err_location(declarator.name()); + cpp_typecheck.str << "member `" << base_name + << "' not found in scope `" + << scope->identifier << "'"; + throw 0; + } + } + + assert(c_it!=cpp_typecheck.context.symbols.end()); + + symbolt &symbol=c_it->second; + + combine_types(declarator.name().location(), final_type, symbol); + enforce_rules(symbol); + + // If it is a constructor, we take care of the + // object initialization + if(final_type.get(ID_return_type)==ID_constructor) + { + const cpp_namet &name=declarator.name(); + + exprt symbol_expr= + cpp_typecheck.resolve( + name, + cpp_typecheck_resolvet::TYPE, + cpp_typecheck_fargst()); + + if(symbol_expr.id()!=ID_type || + symbol_expr.type().id()!=ID_symbol) + { + cpp_typecheck.err_location(name.location()); + cpp_typecheck.str << "error: expected type"; + throw 0; + } + + irep_idt identifier=symbol_expr.type().get(ID_identifier); + const symbolt &symb=cpp_typecheck.lookup(identifier); + const typet &type = symb.type; + assert(type.id()==ID_struct); + + if(declarator.find(ID_member_initializers).is_nil()) + declarator.set(ID_member_initializers, ID_member_initializers); + + cpp_typecheck.check_member_initializers( + type.find(ID_bases), + to_struct_type(type).components(), + declarator.member_initializers()); + + cpp_typecheck.full_member_initialization( + to_struct_type(type), + declarator.member_initializers()); + } + + if(!storage_spec.is_extern()) + symbol.is_extern=false; + + // initializer? + handle_initializer(symbol, declarator); + + return symbol; + } + else + { + // no, it's no way a method + + // we won't allow the constructor/destructor type + if(final_type.id()==ID_code && + to_code_type(final_type).return_type().id()==ID_constructor) + { + cpp_typecheck.err_location(declarator.name().location()); + cpp_typecheck.str << "function must have return type"; + throw 0; + } + + // already there? + contextt::symbolst::iterator c_it= + cpp_typecheck.context.symbols.find(final_identifier); + + if(c_it==cpp_typecheck.context.symbols.end()) + return convert_new_symbol(storage_spec, member_spec, declarator); + + symbolt &symbol=c_it->second; + + if(!storage_spec.is_extern()) + symbol.is_extern = false; + + if(declarator.get_bool("#template_case")) + return symbol; + + combine_types(declarator.name().location(), final_type, symbol); + enforce_rules(symbol); + + // initializer? + handle_initializer(symbol, declarator); + + if(symbol.type.id()=="cpp-template-type") + { + cpp_scopet::id_sett id_set; + + scope->lookup_identifier(symbol.name, cpp_idt::TEMPLATE_ARGUMENT, id_set); + + if(id_set.empty()) + { + cpp_idt &identifier= + cpp_typecheck.cpp_scopes.put_into_scope(symbol,*scope); + identifier.id_class=cpp_idt::TEMPLATE_ARGUMENT; + } + } + + return symbol; + } +} + +/*******************************************************************\ + +Function: cpp_declarator_convertert::combine_types + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_declarator_convertert::combine_types( + const locationt &location, + const typet &decl_type, + symbolt &symbol) +{ + if(symbol.type.id()==decl_type.id() && + decl_type.id()==ID_code) + { + // functions need special treatment due + // to argument names, default values, and inlined-ness + const code_typet &decl_code_type=to_code_type(decl_type); + code_typet &symbol_code_type=to_code_type(symbol.type); + + if(decl_code_type.get_inlined()) + symbol_code_type.set_inlined(true); + + if(decl_code_type.return_type()==symbol_code_type.return_type() && + decl_code_type.arguments().size()==symbol_code_type.arguments().size()) + { + for(unsigned i=0; iprefix=="") + { + mode=ID_C; + } + + if(is_code && + mode!=ID_C) + identifier+=id2string(cpp_typecheck.function_identifier(final_type)); + + final_identifier= + cpp_identifier_prefix(mode)+"::"+ + scope->prefix+ + identifier; +} + +/*******************************************************************\ + +Function: cpp_declarator_convertert::convert_new_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +symbolt &cpp_declarator_convertert::convert_new_symbol( + const cpp_storage_spect &storage_spec, + const cpp_member_spect &member_spec, + cpp_declaratort &declarator) +{ + irep_idt pretty_name=get_pretty_name(); + + symbolt symbol; + + symbol.name=final_identifier; + symbol.base_name=base_name; + symbol.value = declarator.value(); + symbol.location=declarator.name().location(); + symbol.mode=mode; + symbol.module=cpp_typecheck.module; + symbol.type = final_type; + symbol.is_type=is_typedef; + symbol.is_macro=is_typedef && !is_template_argument; + symbol.pretty_name=pretty_name; + symbol.mode=cpp_typecheck.current_mode; + + if(member_spec.is_inline()) + symbol.type.set(ID_C_inlined, true); + + if(!symbol.is_type) + { + if(!is_code) + { + // it is a variable + symbol.is_statevar=true; + symbol.lvalue = !is_reference(symbol.type) && + !(symbol.type.get_bool(ID_C_constant) && + is_number(symbol.type) && + symbol.value.id() == ID_constant); + + if(cpp_typecheck.cpp_scopes.current_scope().is_global_scope()) + { + symbol.static_lifetime=true; + + if(storage_spec.is_extern()) + symbol.is_extern=true; + } + else + { + if(storage_spec.is_static()) + { + symbol.static_lifetime=true; + symbol.file_local=true; + } + else if(storage_spec.is_extern()) + { + cpp_typecheck.err_location(storage_spec); + throw "external storage not permitted here"; + } + } + } + else + symbol.theorem=true; + } + + if(symbol.static_lifetime) + cpp_typecheck.dinis.push_back(symbol.name); + + // move early, it must be visible before doing any value + symbolt *new_symbol; + + if(cpp_typecheck.context.move(symbol, new_symbol)) + throw "cpp_typecheckt::convert_declarator: context.move() failed"; + + if(!is_code) + { + cpp_scopest::id_sett id_set; + + cpp_typecheck.cpp_scopes.current_scope().lookup( + base_name, cpp_scopet::SCOPE_ONLY, id_set); + + for(cpp_scopest::id_sett::const_iterator + id_it=id_set.begin(); + id_it!=id_set.end(); + id_it++) + { + const cpp_idt &id=**id_it; + // the name is already in the scope + // this is ok if they belong to different categories + + if(!id.is_class() && !id.is_enum()) + { + cpp_typecheck.err_location(new_symbol->location); + cpp_typecheck.str << "`" << base_name << "' already in scope"; + throw 0; + } + } + } + + // put into scope + cpp_idt &identifier= + cpp_typecheck.cpp_scopes.put_into_scope(*new_symbol, *scope, is_friend); + + if(is_template) + identifier.id_class=cpp_idt::TEMPLATE; + else if(is_template_argument) + identifier.id_class=cpp_idt::TEMPLATE_ARGUMENT; + else if(is_typedef) + identifier.id_class=cpp_idt::TYPEDEF; + else + identifier.id_class=cpp_idt::SYMBOL; + + // do the value + if(!new_symbol->is_type) + { + if(is_code && declarator.type().id()!=ID_template) + cpp_typecheck.add_function_body(new_symbol); + + if(!is_code) + cpp_typecheck.convert_initializer(*new_symbol); + } + + enforce_rules(*new_symbol); + + return *new_symbol; +} + +/*******************************************************************\ + +Function: cpp_declarator_convertert::get_pretty_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +irep_idt cpp_declarator_convertert::get_pretty_name() +{ + if(is_code) + { + const irept::subt &arguments= + final_type.find(ID_arguments).get_sub(); + + std::string result=scope->prefix+base_name+"("; + + forall_irep(it, arguments) + { + const typet &argument_type=((exprt &)*it).type(); + + if(it!=arguments.begin()) + result+=", "; + + result+=cpp_typecheck.to_string(argument_type); + } + + result+=")"; + + return result; + } + + return scope->prefix+base_name; +} + +/*******************************************************************\ + +Function: cpp_declarator_convertert::operator_overloading_rules + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_declarator_convertert::operator_overloading_rules( + const symbolt &symbol) +{ + +} + +/*******************************************************************\ + +Function: cpp_declarator_convertert::main_function_rules + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_declarator_convertert::main_function_rules( + const symbolt &symbol) +{ + if(symbol.name=="c::main") + { + if(symbol.type.id()!=ID_code) + { + cpp_typecheck.err_location(symbol.location); + throw "main must be function"; + } + + const typet &return_type= + static_cast( + symbol.type.find(ID_return_type)); + + if(return_type!=int_type()) + { + cpp_typecheck.err_location(symbol.location); + throw "main must return int"; + } + } +} diff --git a/src/cpp/cpp_declarator_converter.h b/src/cpp/cpp_declarator_converter.h new file mode 100644 index 00000000000..5f6077bc9ce --- /dev/null +++ b/src/cpp/cpp_declarator_converter.h @@ -0,0 +1,93 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_DECLARATOR_CONVERTER_H +#define CPROVER_CPP_DECLARATOR_CONVERTER_H + +#include + +#include "cpp_declarator.h" +#include "cpp_declaration.h" +#include "cpp_scope.h" + +// converts a cpp_declator plus some +// additional information stored in the class +// into a symbol + +class cpp_declarator_convertert +{ +public: + cpp_declarator_convertert( + class cpp_typecheckt &_cpp_typecheck); + + bool is_typedef; + bool is_template; + bool is_template_argument; + bool is_friend; + irep_idt mode; + + symbolt &convert( + const typet &type, // already typechecked + const cpp_storage_spect &storage_spec, + const cpp_member_spect &member_spec, + cpp_declaratort &declarator); + + symbolt &convert( + const cpp_declarationt &declaration, + cpp_declaratort &declarator) + { + return convert( + declaration.type(), + declaration.storage_spec(), + declaration.member_spec(), + declarator); + } + + class cpp_typecheckt &cpp_typecheck; + +protected: + std::string base_name; + typet final_type; + cpp_scopet *scope; + irep_idt final_identifier; + bool is_code; + + void get_final_identifier(); + irep_idt get_pretty_name(); + + symbolt &convert_new_symbol( + const cpp_storage_spect &storage_spec, + const cpp_member_spect &member_spec, + cpp_declaratort &declarator); + + void handle_initializer( + symbolt &symbol, + cpp_declaratort &declarator); + + void operator_overloading_rules(const symbolt &symbol); + void main_function_rules(const symbolt &symbol); + + void enforce_rules(const symbolt &symbol); + + void check_array_types( + typet &type, + bool force_constant); + + bool is_code_type(const typet &type) const + { + return type.id()==ID_code || + (type.id()==ID_template && type.subtype().id()==ID_code); + } + + void combine_types( + const locationt &location, + const typet &decl_type, + symbolt &symbol); +}; + +#endif diff --git a/src/cpp/cpp_destructor.cpp b/src/cpp/cpp_destructor.cpp new file mode 100644 index 00000000000..4da61ebe2e3 --- /dev/null +++ b/src/cpp/cpp_destructor.cpp @@ -0,0 +1,150 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::cpp_destructor + + Inputs: + + Outputs: NOT typechecked code + + Purpose: + +\*******************************************************************/ + +codet cpp_typecheckt::cpp_destructor( + const locationt &location, + const typet &type, + const exprt &object) +{ + codet new_code; + new_code.location()=location; + + typet tmp_type(type); + follow_symbol(tmp_type); + + assert(!is_reference(tmp_type)); + + // PODs don't need a destructor + if(cpp_is_pod(tmp_type)) + { + new_code.make_nil(); + return new_code; + } + + if(tmp_type.id()==ID_array) + { + const exprt &size_expr= + to_array_type(tmp_type).size(); + + if(size_expr.id()=="infinity") + { + // don't initialize + new_code.make_nil(); + return new_code; + } + + mp_integer s; + if(to_integer(size_expr, s)) + { + err_location(tmp_type); + str << "array size `" << to_string(size_expr) + << "' is not a constant"; + throw 0; + } + + new_code.type().id(ID_code); + new_code.location()=location; + new_code.set_statement(ID_block); + + // for each element of the array, call the destructor + for(mp_integer i = 0; i < s; ++i) + { + exprt constant=from_integer(i, int_type()); + constant.location()=location; + + exprt index(ID_index); + index.copy_to_operands(object); + index.copy_to_operands(constant); + index.location()=location; + + exprt i_code = + cpp_destructor(location, tmp_type.subtype(), index); + + new_code.move_to_operands(i_code); + } + } + else + { + const struct_typet &struct_type= + to_struct_type(tmp_type); + + // find name of destructor + const struct_typet::componentst &components= + struct_type.components(); + + irep_idt dtor_name; + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + const typet &type=it->type(); + + if(!it->get_bool(ID_from_base) && + type.id()==ID_code && + type.find(ID_return_type).id()==ID_destructor) + { + dtor_name=it->get(ID_base_name); + break; + } + } + + // there is always a destructor for non-PODs + assert(dtor_name!=""); + + const symbolt &symb=lookup(struct_type.get(ID_name)); + + irept cpp_name(ID_cpp_name); + + cpp_name.get_sub().push_back(irept(ID_name)); + cpp_name.get_sub().back().set(ID_identifier, symb.base_name); + cpp_name.get_sub().back().set(ID_C_location, location); + + cpp_name.get_sub().push_back(irept("::")); + + cpp_name.get_sub().push_back(irept(ID_name)); + cpp_name.get_sub().back().set(ID_identifier, dtor_name); + cpp_name.get_sub().back().set(ID_C_location, location); + + exprt member_expr(ID_member); + member_expr.copy_to_operands(object); + member_expr.op0().type().set(ID_C_constant, false); + member_expr.add("component_cpp_name").swap(cpp_name); + member_expr.location()=location; + + side_effect_expr_function_callt function_call; + function_call.function()=member_expr; + function_call.location()=location; + + new_code.set_statement(ID_expression); + new_code.location()=location; + new_code.move_to_operands(function_call); + } + + return new_code; +} + diff --git a/src/cpp/cpp_enum_type.h b/src/cpp/cpp_enum_type.h new file mode 100644 index 00000000000..d7d55bdb517 --- /dev/null +++ b/src/cpp/cpp_enum_type.h @@ -0,0 +1,68 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_ENUM_TYPE_H +#define CPROVER_CPP_ENUM_TYPE_H + +#include + +#include +#include + +class cpp_enum_typet:public typet +{ +public: + cpp_enum_typet():typet(ID_c_enum) + { + set(ID_width, config.ansi_c.int_width); + } + + const irep_idt &get_name() const + { + return get(ID_name); + } + + void set_name(const irep_idt &name) + { + set(ID_name, name); + } + + const irept &body() const + { + return find(ID_body); + } + + irept &body() + { + return add(ID_body); + } + + bool has_body() const + { + return find(ID_body).is_not_nil(); + } + + bool get_tag_only_declaration() const + { + return get_bool(ID_C_tag_only_declaration); + } +}; + +extern inline const cpp_enum_typet &to_cpp_enum_type(const irept &irep) +{ + assert(irep.id()==ID_c_enum); + return static_cast(irep); +} + +extern inline cpp_enum_typet &to_cpp_enum_type(irept &irep) +{ + assert(irep.id()==ID_c_enum); + return static_cast(irep); +} + +#endif diff --git a/src/cpp/cpp_final.cpp b/src/cpp/cpp_final.cpp new file mode 100644 index 00000000000..08a9c13ca58 --- /dev/null +++ b/src/cpp/cpp_final.cpp @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_final.h" + +/*******************************************************************\ + +Function: cpp_final + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_final( + contextt &context, + message_handlert &message_handler) +{ + return false; +} diff --git a/src/cpp/cpp_final.h b/src/cpp/cpp_final.h new file mode 100644 index 00000000000..c8b7eb8922f --- /dev/null +++ b/src/cpp/cpp_final.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_FINAL_H +#define CPROVER_CPP_FINAL_H + +#include +#include + +bool cpp_final( + contextt &context, + message_handlert &message_handler); + +#endif diff --git a/src/cpp/cpp_id.cpp b/src/cpp/cpp_id.cpp new file mode 100644 index 00000000000..eb7bd4f60a2 --- /dev/null +++ b/src/cpp/cpp_id.cpp @@ -0,0 +1,173 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "cpp_id.h" +#include "cpp_scope.h" + +/*******************************************************************\ + +Function: cpp_idt::cpp_idt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cpp_idt::cpp_idt(): + is_member(false), + is_method(false), + is_static_member(false), + is_scope(false), + is_constructor(false), + id_class(UNKNOWN), + this_expr(static_cast(get_nil_irep())), + compound_counter(0), + parent(NULL) +{ +} + +/*******************************************************************\ + +Function: cpp_idt::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_idt::print(std::ostream &out, unsigned indent) const +{ + print_fields(out, indent); + + if(!sub.empty()) + { + for(cpp_id_mapt::const_iterator it=sub.begin(); + it!=sub.end(); + it++) + it->second.print(out, indent+2); + + out << std::endl; + } +} + +/*******************************************************************\ + +Function: cpp_idt::print_fields + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_idt::print_fields(std::ostream &out, unsigned indent) const +{ + for(unsigned i=0; i + +#include +#include +#include +#include +#include + +#include +#include + +typedef std::multimap cpp_id_mapt; + +class cpp_scopet; + +class cpp_idt +{ +public: + cpp_idt(); + + typedef enum + { + UNKNOWN, SYMBOL, TYPEDEF, CLASS, ENUM, TEMPLATE, + TEMPLATE_ARGUMENT, NAMESPACE, BLOCK_SCOPE, + TEMPLATE_SCOPE, ROOT_SCOPE + } id_classt; + + bool is_member, is_method, is_static_member, + is_scope, is_constructor; + + id_classt id_class; + + inline bool is_class() const + { + return id_class==CLASS; + } + + inline bool is_enum() const + { + return id_class==ENUM; + } + + inline bool is_namespace() const + { + return id_class==NAMESPACE; + } + + inline bool is_typedef() const + { + return id_class==TYPEDEF; + } + + irep_idt identifier, base_name; + + // if it is a member or method, what class is it in? + irep_idt class_identifier; + exprt this_expr; + + // scope data + std::string prefix; + unsigned compound_counter; + + cpp_idt &insert(const irep_idt &_base_name) + { + cpp_id_mapt::iterator it= + sub.insert(std::pair + (_base_name, cpp_idt())); + + it->second.base_name=_base_name; + it->second.set_parent(*this); + + return it->second; + } + + cpp_idt &insert(const cpp_idt &cpp_id) + { + cpp_id_mapt::iterator it= + sub.insert(std::pair + (cpp_id.base_name, cpp_id)); + + it->second.set_parent(*this); + + return it->second; + } + + inline cpp_idt &get_parent() const + { + assert(parent!=NULL); + return *parent; + } + + inline void set_parent(cpp_idt &_parent) + { + assert(_parent.is_scope); + parent=&_parent; + } + + inline void clear() + { + *this=cpp_idt(); + } + + void print(std::ostream &out, unsigned indent=0) const; + void print_fields(std::ostream &out, unsigned indent=0) const; + + friend class cpp_scopet; + +protected: + cpp_id_mapt sub; + +private: + // These are used for base classes and 'using' clauses. + typedef std::vector scope_listt; + scope_listt using_scopes, secondary_scopes; + cpp_idt *parent; +}; + +std::ostream &operator<<(std::ostream &out, const cpp_idt &cpp_id); +std::ostream &operator<<(std::ostream &out, const cpp_idt::id_classt &id_class); + +#endif diff --git a/src/cpp/cpp_instantiate_template.cpp b/src/cpp/cpp_instantiate_template.cpp new file mode 100644 index 00000000000..bd8f449a205 --- /dev/null +++ b/src/cpp/cpp_instantiate_template.cpp @@ -0,0 +1,434 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include "cpp_type2name.h" +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::template_suffix + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_typecheckt::template_suffix( + const cpp_template_args_tct &template_args) +{ + // quick hack + std::string result="<"; + bool first=true; + + const cpp_template_args_tct::argumentst &arguments= + template_args.arguments(); + + for(cpp_template_args_tct::argumentst::const_iterator + it=arguments.begin(); + it!=arguments.end(); + it++) + { + if(first) first=false; else result+=","; + + const exprt expr=*it; + + assert(expr.id()!="ambiguous"); + + if(expr.id()==ID_type) + { + const typet &type=expr.type(); + if(type.id()==ID_symbol) + result+=type.get_string(ID_identifier); + else + result+=cpp_type2name(type); + } + else // expression + { + exprt e=expr; + make_constant(e); + + // this must be a constant, which includes true/false + mp_integer i; + + if(e.is_true()) + i=1; + else if(e.is_false()) + i=0; + else if(to_integer(e, i)) + { + err_location(*it); + str << "template argument expression expected to be " + "scalar constant, but got `" + << to_string(e) << "'"; + throw 0; + } + + result+=integer2string(i); + } + } + + result+='>'; + + return result; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::show_instantiation_stack + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::show_instantiation_stack(std::ostream &out) +{ + for(instantiation_stackt::const_iterator + s_it=instantiation_stack.begin(); + s_it!=instantiation_stack.end(); + s_it++) + { + const symbolt &symbol=lookup(s_it->identifier); + out << "instantiating `" << symbol.pretty_name << "' with <"; + + forall_expr(a_it, s_it->full_template_args.arguments()) + { + if(a_it!=s_it->full_template_args.arguments().begin()) + out << ", "; + + if(a_it->id()==ID_type) + out << to_string(a_it->type()); + else + out << to_string(*a_it); + } + + out << "> at " << s_it->location << std::endl; + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::instantiate_template + + Inputs: location of the instantiation, + the identifier of the template symbol, + typechecked template arguments, + an (optional) specialization + + Outputs: + + Purpose: + +\*******************************************************************/ + +const symbolt &cpp_typecheckt::instantiate_template( + const locationt &location, + const symbolt &template_symbol, + const cpp_template_args_tct &specialization_template_args, + const cpp_template_args_tct &full_template_args, + const typet &specialization) +{ + if(instantiation_stack.size()==50) + { + err_location(location); + throw "reached maximum template recursion depth"; + } + + instantiation_levelt i_level(instantiation_stack); + instantiation_stack.back().location=location; + instantiation_stack.back().identifier=template_symbol.name; + instantiation_stack.back().full_template_args=full_template_args; + + #if 0 + std::cout << "L: " << location << std::endl; + std::cout << "I: " << template_symbol.name << std::endl; + #endif + + cpp_save_scopet cpp_saved_scope(cpp_scopes); + cpp_saved_template_mapt saved_map(template_map); + + bool specialization_given=specialization.is_not_nil(); + + // we should never get 'unassigned' here + assert(!specialization_template_args.has_unassigned()); + assert(!full_template_args.has_unassigned()); + + #if 0 + std::cout << "A: <"; + forall_expr(it, specialization_template_args.arguments()) + { + if(it!=specialization_template_args.arguments().begin()) std::cout << ", "; + if(it->id()==ID_type) + std::cout << to_string(it->type()); + else + std::cout << to_string(*it); + } + std::cout << ">" << std::endl; + #endif + + // do we have args? + if(full_template_args.arguments().empty()) + { + err_location(location); + str << "`" << template_symbol.base_name + << "' is a template; thus, expected template arguments"; + throw 0; + } + + // produce new symbol name + std::string suffix=template_suffix(full_template_args); + + // we need the template scope to see the parameters + cpp_scopet *template_scope= + static_cast(cpp_scopes.id_map[template_symbol.name]); + + if(template_scope==NULL) + { + err_location(location); + str << "identifier: " << template_symbol.name << std::endl; + throw "template instantiation error: scope not found"; + } + + assert(template_scope!=NULL); + + // produce new declaration + cpp_declarationt new_decl=to_cpp_declaration(template_symbol.type); + + // the new one is not a template any longer, but we remember the + // template type + template_typet template_type=new_decl.template_type(); + new_decl.remove(ID_is_template); + new_decl.remove(ID_template_type); + new_decl.set(ID_C_template, template_symbol.name); + new_decl.set(ID_C_template_arguments, specialization_template_args); + + // save old scope + cpp_save_scopet saved_scope(cpp_scopes); + + // mapping from template parameters to values/types + template_map.build(template_type, specialization_template_args); + + // enter the template scope + cpp_scopes.go_to(*template_scope); + + // Is it a template method? + // It's in the scope of a class, and not a class itself. + bool is_template_method= + cpp_scopes.current_scope().get_parent().is_class() && + new_decl.type().id()!=ID_struct; + + irep_idt class_name; + + if(is_template_method) + class_name=cpp_scopes.current_scope().get_parent().identifier; + + // sub-scope for fixing the prefix + std::string subscope_name=id2string(template_scope->identifier)+suffix; + + // let's see if we have the instance already + cpp_scopest::id_mapt::iterator scope_it= + cpp_scopes.id_map.find(subscope_name); + + if(scope_it!=cpp_scopes.id_map.end()) + { + cpp_scopet &scope=cpp_scopes.get_scope(subscope_name); + + cpp_scopet::id_sett id_set; + scope.lookup(template_symbol.base_name, cpp_scopet::SCOPE_ONLY, id_set); + + if(id_set.size()==1) + { + // It has already been instantianted! + const cpp_idt &cpp_id = **id_set.begin(); + + assert(cpp_id.id_class == cpp_idt::CLASS || + cpp_id.id_class == cpp_idt::SYMBOL); + + const symbolt &symb=lookup(cpp_id.identifier); + + // continue if the type is incomplete only + if(cpp_id.id_class == cpp_idt::CLASS && + symb.type.id() == ID_struct) + return symb; + else if(symb.value.is_not_nil()) + return symb; + } + + cpp_scopes.go_to(scope); + } + else + { + // set up a scope as subscope of the template scope + std::string prefix=template_scope->get_parent().prefix+suffix; + cpp_scopet &sub_scope= + cpp_scopes.current_scope().new_scope(subscope_name); + sub_scope.prefix=prefix; + sub_scope.add_using_scope(template_scope->get_parent()); + cpp_scopes.go_to(sub_scope); + cpp_scopes.id_map.insert( + cpp_scopest::id_mapt::value_type(subscope_name, &sub_scope)); + } + + // store the information that the template has + // been instantiated using these arguments + { + // need non-const handle on template symbol + symbolt &s=context.symbols.find(template_symbol.name)->second; + irept &instantiated_with=s.value.add("instantiated_with"); + instantiated_with.get_sub().push_back(specialization_template_args); + } + + #if 0 + std::cout << "MAP:" << std::endl; + template_map.print(std::cout); + #endif + + // fix the type + { + typet declaration_type=new_decl.type(); + + // specialization? + if(specialization_given) + { + if(declaration_type.id()==ID_struct) + { + declaration_type=specialization; + declaration_type.location()=location; + } + else + { + irept tmp=specialization; + new_decl.declarators()[0].swap(tmp); + } + } + + template_map.apply(declaration_type); + new_decl.type().swap(declaration_type); + } + + if(new_decl.type().id()==ID_struct) + { + convert_non_template_declaration(new_decl); + + // also instantiate all the template methods + const exprt &template_methods= + static_cast( + template_symbol.value.find("template_methods")); + + for(unsigned i=0; i( + static_cast(template_methods.operands()[i])); + + // copy the type of the template method + template_typet method_type= + method_decl.template_type(); + + // do template arguments + // this also sets up the template scope of the method + cpp_scopet &method_scope= + typecheck_template_parameters(method_type); + + cpp_scopes.go_to(method_scope); + + // mapping from template arguments to values/types + template_map.build(method_type, specialization_template_args); + + method_decl.remove(ID_template_type); + method_decl.remove(ID_is_template); + + convert(method_decl); + } + + const symbolt &new_symb= + lookup(new_decl.type().get(ID_identifier)); + + return new_symb; + } + + if(is_template_method) + { + contextt::symbolst::iterator it = + context.symbols.find(class_name); + + assert(it!=context.symbols.end()); + + symbolt &symb = it->second; + + assert(new_decl.declarators().size() == 1); + + if(new_decl.member_spec().is_virtual()) + { + err_location(new_decl); + str << "invalid use of `virtual' in template declaration"; + throw 0; + } + + if(convert_typedef(new_decl.type())) + { + err_location(new_decl); + str << "template declaration for typedef"; + throw 0; + } + + if(new_decl.storage_spec().is_extern() || + new_decl.storage_spec().is_auto() || + new_decl.storage_spec().is_register() || + new_decl.storage_spec().is_mutable()) + { + err_location(new_decl); + str << "invalid storage class specified for template field"; + throw 0; + } + + bool is_static=new_decl.storage_spec().is_static(); + irep_idt access = new_decl.get(ID_C_access); + + assert(access != irep_idt()); + assert(symb.type.id()==ID_struct); + + typecheck_compound_declarator( + symb, + new_decl, + new_decl.declarators()[0], + to_struct_type(symb.type).components(), + access, + is_static, + false, + false); + + return lookup(to_struct_type(symb.type).components().back().get(ID_name)); + } + + // not a template class, not a template method, + // it must be a template function! + + assert(new_decl.declarators().size()==1); + + convert_non_template_declaration(new_decl); + + const symbolt &symb= + lookup(new_decl.declarators()[0].get(ID_identifier)); + + return symb; +} diff --git a/src/cpp/cpp_is_pod.cpp b/src/cpp/cpp_is_pod.cpp new file mode 100644 index 00000000000..f96e5b4ca0b --- /dev/null +++ b/src/cpp/cpp_is_pod.cpp @@ -0,0 +1,122 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::cpp_is_pod + + Inputs: + + Outputs: + + Standard: + "Arithmetic types (3.9.1), enumeration types, pointer types, and + pointer to member types (3.9.2), and cvqualified versions of + these types (3.9.3) are collectively called scalar types. Scalar + types, POD-struct types, POD-union types (clause 9), arrays of + such types and cv-qualified versions of these types (3.9.3) are + collectively called POD types." + + "A POD-struct is an aggregate class that has no non-static data + members of type non-POD-struct, non-POD-union (or array of such + types) or reference, and has no user-defined copy assignment + operator and no user-defined destructor. Similarly, a POD-union + is an aggregate union that has no non-static data members of type + non-POD-struct, non-POD-union (or array of such types) or reference, + and has no userdefined copy assignment operator and no user-defined + destructor. A POD class is a class that is either a POD-struct or + a POD-union." + + "An aggregate is an array or a class (clause 9) with no + user-declared constructors (12.1), no private or protected + non-static data members (clause 11), no base classes (clause 10), + and no virtual functions (10.3)." + +\*******************************************************************/ + +bool cpp_typecheckt::cpp_is_pod(const typet &type) const +{ + if(type.id()==ID_struct) + { + // Not allowed in PODs: + // * Non-PODs + // * Constructors/Destructors + // * virtuals + // * private/protected, unless static + // * overloading assignment operator + // * Base classes + + const struct_typet &struct_type=to_struct_type(type); + + if(!type.find(ID_bases).get_sub().empty()) + return false; + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it->get_bool(ID_is_type)) + continue; + + if(it->get_base_name()=="operator=") + return false; + + if(it->get_bool(ID_is_virtual)) + return false; + + const typet &sub_type=it->type(); + + if(sub_type.id()==ID_code) + { + if(it->get_bool(ID_is_virtual)) + return false; + + const typet &return_type=to_code_type(sub_type).return_type(); + + if(return_type.id()==ID_constructor || + return_type.id()==ID_destructor) + return false; + } + else if(it->get(ID_access)!=ID_public && + !it->get_bool(ID_is_static)) + return false; + + if(!cpp_is_pod(sub_type)) + return false; + } + + return true; + } + else if(type.id()==ID_array) + { + return cpp_is_pod(type.subtype()); + } + else if(type.id()==ID_pointer) + { + if(is_reference(type)) // references are not PODs + return false; + + // but pointers are PODs! + return true; + } + else if(type.id()==ID_symbol) + { + const symbolt &symb=lookup(type.get(ID_identifier)); + assert(symb.is_type); + return cpp_is_pod(symb.type); + } + + // everything else is POD + return true; +} diff --git a/src/cpp/cpp_item.h b/src/cpp/cpp_item.h new file mode 100644 index 00000000000..4325122103f --- /dev/null +++ b/src/cpp/cpp_item.h @@ -0,0 +1,123 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_ITEM_H +#define CPROVER_CPP_ITEM_H + +#include + +#include "cpp_declaration.h" +#include "cpp_linkage_spec.h" +#include "cpp_namespace_spec.h" +#include "cpp_using.h" + +class cpp_itemt:public irept +{ +public: + // declaration + + cpp_declarationt &make_declaration() + { + id(ID_cpp_declaration); + return (cpp_declarationt &)*this; + } + + cpp_declarationt &get_declaration() + { + assert(is_declaration()); + return (cpp_declarationt &)*this; + } + + const cpp_declarationt &get_declaration() const + { + assert(is_declaration()); + return (const cpp_declarationt &)*this; + } + + bool is_declaration() const + { + return id()==ID_cpp_declaration; + } + + // linkage spec + + cpp_linkage_spect &make_linkage_spec() + { + id(ID_cpp_linkage_spec); + return (cpp_linkage_spect &)*this; + } + + cpp_linkage_spect &get_linkage_spec() + { + assert(is_linkage_spec()); + return (cpp_linkage_spect &)*this; + } + + const cpp_linkage_spect &get_linkage_spec() const + { + assert(is_linkage_spec()); + return (const cpp_linkage_spect &)*this; + } + + bool is_linkage_spec() const + { + return id()==ID_cpp_linkage_spec; + } + + // namespace + + cpp_namespace_spect &make_namespace_spec() + { + id(ID_cpp_namespace_spec); + return (cpp_namespace_spect &)*this; + } + + cpp_namespace_spect &get_namespace_spec() + { + assert(is_namespace_spec()); + return (cpp_namespace_spect &)*this; + } + + const cpp_namespace_spect &get_namespace_spec() const + { + assert(is_namespace_spec()); + return (const cpp_namespace_spect &)*this; + } + + bool is_namespace_spec() const + { + return id()==ID_cpp_namespace_spec; + } + + // using + + cpp_usingt &make_using() + { + id(ID_cpp_using); + return (cpp_usingt &)*this; + } + + cpp_usingt &get_using() + { + assert(is_using()); + return (cpp_usingt &)*this; + } + + const cpp_usingt &get_using() const + { + assert(is_using()); + return (const cpp_usingt &)*this; + } + + bool is_using() const + { + return id()==ID_cpp_using; + } +}; + +#endif diff --git a/src/cpp/cpp_language.cpp b/src/cpp/cpp_language.cpp new file mode 100644 index 00000000000..3007f7e8572 --- /dev/null +++ b/src/cpp/cpp_language.cpp @@ -0,0 +1,433 @@ +/*******************************************************************\ + +Module: C++ Language Module + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "internal_additions.h" +#include "cpp_language.h" +#include "expr2cpp.h" +#include "cpp_parser.h" +#include "cpp_typecheck.h" +#include "cpp_final.h" + +/*******************************************************************\ + +Function: cpp_languaget::extensions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::set cpp_languaget::extensions() const +{ + std::set s; + + s.insert("cpp"); + s.insert("cc"); + s.insert("ipp"); + s.insert("cxx"); + + #ifndef _WIN32 + s.insert("C"); + #endif + + return s; +} + +/*******************************************************************\ + +Function: cpp_languaget::modules_provided + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_languaget::modules_provided(std::set &modules) +{ + modules.insert(translation_unit(parse_path)); +} + +/*******************************************************************\ + +Function: cpp_languaget::preprocess + + Inputs: + + Outputs: + + Purpose: ANSI-C preprocessing + +\*******************************************************************/ + +bool cpp_languaget::preprocess( + std::istream &instream, + const std::string &path, + std::ostream &outstream, + message_handlert &message_handler) +{ + if(path=="") + return c_preprocess(instream, outstream, message_handler); + + // check extension + + const char *ext=strrchr(path.c_str(), '.'); + if(ext!=NULL && std::string(ext)==".ipp") + { + std::ifstream infile(path.c_str()); + + char ch; + + while(infile.read(&ch, 1)) + outstream << ch; + + return false; + } + + return c_preprocess(path, outstream, message_handler); +} + +/*******************************************************************\ + +Function: cpp_languaget::parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_languaget::parse( + std::istream &instream, + const std::string &path, + message_handlert &message_handler) +{ + // store the path + + parse_path=path; + + // preprocessing + + std::ostringstream o_preprocessed; + + cpp_internal_additions(o_preprocessed); + + if(preprocess(instream, path, o_preprocessed, message_handler)) + return true; + + std::istringstream i_preprocessed(o_preprocessed.str()); + + // parsing + + cpp_parser.clear(); + cpp_parser.filename=path; + cpp_parser.in=&i_preprocessed; + cpp_parser.set_message_handler(message_handler); + cpp_parser.grammar=cpp_parsert::LANGUAGE; + + if(config.ansi_c.os==configt::ansi_ct::OS_WIN) + cpp_parser.mode=cpp_parsert::MSC; + else + cpp_parser.mode=cpp_parsert::GCC; + + cpp_scanner_init(); + + bool result=cpp_parser.parse(); + + // save result + cpp_parse_tree.swap(cpp_parser.parse_tree); + + // save some memory + cpp_parser.clear(); + + return result; +} + +/*******************************************************************\ + +Function: cpp_languaget::typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_languaget::typecheck( + contextt &context, + const std::string &module, + message_handlert &message_handler) +{ + if(module=="") return false; + + contextt new_context; + + if(cpp_typecheck(cpp_parse_tree, new_context, module, message_handler)) + return true; + + return c_link(context, new_context, message_handler); +} + +/*******************************************************************\ + +Function: cpp_languaget::final + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_languaget::final( + contextt &context, + message_handlert &message_handler) +{ + if(cpp_final(context, message_handler)) return true; + if(c_main(context, "c::", "c::main", message_handler)) return true; + + return false; +} + +/*******************************************************************\ + +Function: cpp_languaget::show_parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_languaget::show_parse(std::ostream &out) +{ + for(cpp_parse_treet::itemst::const_iterator it= + cpp_parse_tree.items.begin(); + it!=cpp_parse_tree.items.end(); + it++) + show_parse(out, *it); +} + +/*******************************************************************\ + +Function: cpp_languaget::show_parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_languaget::show_parse( + std::ostream &out, + const cpp_itemt &item) +{ + if(item.is_linkage_spec()) + { + const cpp_linkage_spect &linkage_spec= + item.get_linkage_spec(); + + out << "LINKAGE " << linkage_spec.linkage().get("value") + << ":" << std::endl; + + for(cpp_linkage_spect::itemst::const_iterator + it=linkage_spec.items().begin(); + it!=linkage_spec.items().end(); + it++) + show_parse(out, *it); + + out << std::endl; + } + else if(item.is_namespace_spec()) + { + const cpp_namespace_spect &namespace_spec= + item.get_namespace_spec(); + + out << "NAMESPACE " << namespace_spec.get_namespace() + << ":" << std::endl; + + for(cpp_namespace_spect::itemst::const_iterator + it=namespace_spec.items().begin(); + it!=namespace_spec.items().end(); + it++) + show_parse(out, *it); + + out << std::endl; + } + else if(item.is_using()) + { + const cpp_usingt &cpp_using=item.get_using(); + + out << "USING "; + if(cpp_using.get_namespace()) + out << "NAMESPACE "; + out << cpp_using.name() << std::endl; + out << std::endl; + } + else if(item.is_declaration()) + { + item.get_declaration().output(out); + } + else + out << "UNKNOWN: " << item << std::endl; +} + +/*******************************************************************\ + +Function: new_cpp_language + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +languaget *new_cpp_language() +{ + return new cpp_languaget; +} + +/*******************************************************************\ + +Function: cpp_languaget::from_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_languaget::from_expr( + const exprt &expr, + std::string &code, + const namespacet &ns) +{ + code=expr2cpp(expr, ns); + return false; +} + +/*******************************************************************\ + +Function: cpp_languaget::from_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_languaget::from_type( + const typet &type, + std::string &code, + const namespacet &ns) +{ + code=type2cpp(type, ns); + return false; +} + +/*******************************************************************\ + +Function: cpp_languaget::to_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_languaget::to_expr( + const std::string &code, + const std::string &module, + exprt &expr, + message_handlert &message_handler, + const namespacet &ns) +{ + expr.make_nil(); + + // no preprocessing yet... + + std::istringstream i_preprocessed(code); + + // parsing + + cpp_parser.clear(); + cpp_parser.filename=""; + cpp_parser.in=&i_preprocessed; + cpp_parser.set_message_handler(message_handler); + cpp_parser.grammar=cpp_parsert::EXPRESSION; + cpp_scanner_init(); + + bool result=cpp_parser.parse(); + + if(cpp_parser.parse_tree.items.empty()) + result=true; + else + { + // TODO + //expr.swap(cpp_parser.parse_tree.declarations.front()); + + // typecheck it + result=cpp_typecheck(expr, message_handler, ns); + } + + // save some memory + cpp_parser.clear(); + + return result; +} + +/*******************************************************************\ + +Function: cpp_languaget::~cpp_languaget + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cpp_languaget::~cpp_languaget() +{ +} diff --git a/src/cpp/cpp_language.h b/src/cpp/cpp_language.h new file mode 100644 index 00000000000..a65697580a5 --- /dev/null +++ b/src/cpp/cpp_language.h @@ -0,0 +1,95 @@ +/*******************************************************************\ + +Module: C++ Language Module + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_LANGUAGE_H +#define CPROVER_CPP_LANGUAGE_H + +#include + +#include "cpp_parse_tree.h" + +class cpp_languaget:public languaget +{ +public: + virtual bool preprocess( + std::istream &instream, + const std::string &path, + std::ostream &outstream, + message_handlert &message_handler); + + virtual bool parse( + std::istream &instream, + const std::string &path, + message_handlert &message_handler); + + virtual bool typecheck( + contextt &context, + const std::string &module, + message_handlert &message_handler); + + bool merge_context( + contextt &dest, + contextt &src, + message_handlert &message_handler, + const std::string &module, + class replace_symbolt &replace_symbol) const; + + virtual bool final( + contextt &context, + message_handlert &message_handler); + + virtual void show_parse(std::ostream &out); + + // constructor, destructor + virtual ~cpp_languaget(); + cpp_languaget() { } + + // conversion from expression into string + virtual bool from_expr( + const exprt &expr, + std::string &code, + const namespacet &ns); + + // conversion from type into string + virtual bool from_type( + const typet &type, + std::string &code, + const namespacet &ns); + + // conversion from string into expression + virtual bool to_expr( + const std::string &code, + const std::string &module, + exprt &expr, + message_handlert &message_handler, + const namespacet &ns); + + virtual languaget *new_language() + { return new cpp_languaget; } + + virtual std::string id() const { return "cpp"; } + virtual std::string description() const { return "C++"; } + virtual std::set extensions() const; + + virtual void modules_provided(std::set &modules); + +protected: + cpp_parse_treet cpp_parse_tree; + std::string parse_path; + + void show_parse(std::ostream &out, const cpp_itemt &item); + + virtual std::string main_symbol() + { + return "c::main"; + } +}; + +languaget *new_cpp_language(); + +#endif diff --git a/src/cpp/cpp_linkage_spec.h b/src/cpp/cpp_linkage_spec.h new file mode 100644 index 00000000000..a4bfc382e7a --- /dev/null +++ b/src/cpp/cpp_linkage_spec.h @@ -0,0 +1,42 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_LINKAGE_SPEC_H +#define CPROVER_CPP_LINKAGE_SPEC_H + +class cpp_linkage_spect:public exprt +{ +public: + cpp_linkage_spect():exprt(ID_cpp_linkage_spec) + { + } + + typedef std::vector itemst; + + const itemst &items() const + { + return (const itemst &)operands(); + } + + itemst &items() + { + return (itemst &)operands(); + } + + irept &linkage() + { + return add(ID_linkage); + } + + const irept &linkage() const + { + return find(ID_linkage); + } +}; + +#endif diff --git a/src/cpp/cpp_member_spec.h b/src/cpp/cpp_member_spec.h new file mode 100644 index 00000000000..fb2c4b38311 --- /dev/null +++ b/src/cpp/cpp_member_spec.h @@ -0,0 +1,40 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_CPP_MEMBER_SPEC_H +#define CPROVER_CPP_CPP_MEMBER_SPEC_H + +#include "location.h" + +class cpp_member_spect:public irept +{ +public: + cpp_member_spect():irept(ID_cpp_member_spec) + { + } + + bool is_virtual() const { return get_bool(ID_virtual); } + bool is_inline() const { return get_bool(ID_inline); } + bool is_friend() const { return get_bool(ID_friend); } + bool is_explicit() const { return get_bool(ID_explicit); } + + void set_virtual(bool value) { set(ID_virtual, value); } + void set_inline(bool value) { set(ID_inline, value); } + void set_friend(bool value) { set(ID_friend, value); } + void set_explicit(bool value) { set(ID_explicit, value); } + + bool is_empty() const + { + return !is_virtual() && + !is_inline() && + !is_friend() && + !is_explicit(); + } +}; + +#endif diff --git a/src/cpp/cpp_name.cpp b/src/cpp/cpp_name.cpp new file mode 100644 index 00000000000..e70fceb7611 --- /dev/null +++ b/src/cpp/cpp_name.cpp @@ -0,0 +1,85 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include + +#include "cpp_name.h" + +/*******************************************************************\ + +Function: cpp_namet::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_namet::convert( + std::string &identifier, + std::string &base_name) const +{ + forall_irep(it, get_sub()) + { + const irep_idt id=it->id(); + + std::string name_component; + + if(id==ID_name) + name_component=it->get_string(ID_identifier); + else if(id==ID_template_args) + { + std::stringstream ss; + ss << location() << std::endl; + ss << "no template arguments allowed here"; + throw ss.str(); + } + else + name_component=it->id_string(); + + identifier+=name_component; + + if(id=="::") + base_name=""; + else + base_name+=name_component; + } +} + +/*******************************************************************\ + +Function: cpp_namet::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_namet::to_string() const +{ + std::string str; + + forall_irep(it, get_sub()) + { + if(it->id()=="::") + str += it->id_string(); + else if(it->id()==ID_template_args) + str += "<...>"; + else + str+=it->get_string(ID_identifier); + } + + return str; +} diff --git a/src/cpp/cpp_name.h b/src/cpp/cpp_name.h new file mode 100644 index 00000000000..f703c7e896e --- /dev/null +++ b/src/cpp/cpp_name.h @@ -0,0 +1,74 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_CPP_NAME_H +#define CPROVER_CPP_CPP_NAME_H + +#include "location.h" + +class cpp_namet:public irept +{ +public: + cpp_namet():irept(ID_cpp_name) + { + } + + const locationt &location() const + { + if(get_sub().empty()) + return static_cast(get_nil_irep()); + else + return static_cast(get_sub().front().find(ID_C_location)); + } + + void convert(std::string &identifier, std::string &base_name) const; + + bool is_operator() const + { + if(get_sub().empty()) return false; + return get_sub().front().id()==ID_operator; + } + + bool is_typename() const + { + return get_bool(ID_typename); + } + + bool is_qualified() const + { + forall_irep(it, get_sub()) + if(it->id()=="::") + return true; + return false; + } + + bool has_template_args() const + { + forall_irep(it, get_sub()) + if(it->id()==ID_template_args) + return true; + + return false; + } + + std::string to_string() const; +}; + +inline cpp_namet &to_cpp_name(irept &cpp_name) +{ + assert(cpp_name.id() == ID_cpp_name); + return static_cast(cpp_name); +} + +inline const cpp_namet &to_cpp_name(const irept &cpp_name) +{ + assert(cpp_name.id() == ID_cpp_name); + return static_cast(cpp_name); +} + +#endif diff --git a/src/cpp/cpp_namespace_spec.cpp b/src/cpp/cpp_namespace_spec.cpp new file mode 100644 index 00000000000..13e44d6850e --- /dev/null +++ b/src/cpp/cpp_namespace_spec.cpp @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "cpp_namespace_spec.h" +#include "cpp_item.h" + +/*******************************************************************\ + +Function: cpp_namespace_spect::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_namespace_spect::output(std::ostream &out) const +{ + out << " namespace: " << get_namespace() << std::endl; +} diff --git a/src/cpp/cpp_namespace_spec.h b/src/cpp/cpp_namespace_spec.h new file mode 100644 index 00000000000..3f76cb95b24 --- /dev/null +++ b/src/cpp/cpp_namespace_spec.h @@ -0,0 +1,46 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_NAMESPACE_SPEC_H +#define CPROVER_CPP_NAMESPACE_SPEC_H + +#include + +class cpp_namespace_spect:public exprt +{ +public: + cpp_namespace_spect():exprt(ID_cpp_namespace_spec) + { + } + + typedef std::vector itemst; + + const itemst &items() const + { + return (const itemst &)operands(); + } + + itemst &items() + { + return (itemst &)operands(); + } + + const irep_idt &get_namespace() const + { + return get(ID_namespace); + } + + void set_namespace(const irep_idt &_namespace) + { + set(ID_namespace, _namespace); + } + + void output(std::ostream &out) const; +}; + +#endif diff --git a/src/cpp/cpp_parse_tree.cpp b/src/cpp/cpp_parse_tree.cpp new file mode 100644 index 00000000000..f00d85f94b4 --- /dev/null +++ b/src/cpp/cpp_parse_tree.cpp @@ -0,0 +1,44 @@ +/*******************************************************************\ + +Module: C++ Parser + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "cpp_parse_tree.h" + +/*******************************************************************\ + +Function: cpp_parse_treet::swap + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_parse_treet::swap(cpp_parse_treet &cpp_parse_tree) +{ + cpp_parse_tree.items.swap(items); +} + +/*******************************************************************\ + +Function: cpp_parse_treet::clear + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_parse_treet::clear() +{ + items.clear(); +} + diff --git a/src/cpp/cpp_parse_tree.h b/src/cpp/cpp_parse_tree.h new file mode 100644 index 00000000000..bf1eb0ad5e4 --- /dev/null +++ b/src/cpp/cpp_parse_tree.h @@ -0,0 +1,26 @@ +/*******************************************************************\ + +Module: C++ Parser + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_PARSE_TREE_H +#define CPROVER_CPP_PARSE_TREE_H + +#include "cpp_item.h" + +class cpp_parse_treet +{ +public: + // the (top-level) declarations/definitions + + typedef std::list itemst; + itemst items; + + void swap(cpp_parse_treet &cpp_parse_tree); + void clear(); +}; + +#endif diff --git a/src/cpp/cpp_parser.cpp b/src/cpp/cpp_parser.cpp new file mode 100644 index 00000000000..61b1e31e2ae --- /dev/null +++ b/src/cpp/cpp_parser.cpp @@ -0,0 +1,50 @@ +/*******************************************************************\ + +Module: C++ Parser + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "cpp_parser.h" + +cpp_parsert cpp_parser; + +/*******************************************************************\ + +Function: cpp_parsert::parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_parse(); + +bool cpp_parsert::parse() +{ + return cpp_parse(); +} + +/*******************************************************************\ + +Function: yycpperror + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +extern char *yycpptext; + +int yycpperror(const std::string &error) +{ + cpp_parser.parse_error(error, yycpptext); + return error.size()+1; +} diff --git a/src/cpp/cpp_parser.h b/src/cpp/cpp_parser.h new file mode 100644 index 00000000000..0fa576a06c2 --- /dev/null +++ b/src/cpp/cpp_parser.h @@ -0,0 +1,67 @@ +/*******************************************************************\ + +Module: C++ Parser + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_PARSER_H +#define CPROVER_CPP_PARSER_H + +#include + +#include +#include +#include +#include + +#include "cpp_parse_tree.h" +#include "cpp_token_buffer.h" + +class cpp_parsert:public parsert +{ +public: + cpp_parse_treet parse_tree; + + virtual bool parse(); + + virtual void clear() + { + parsert::clear(); + parse_tree.clear(); + token_buffer.clear(); + } + +public: + // internal state + + enum { LANGUAGE, EXPRESSION } grammar; + enum { ANSI, GCC, MSC } mode; + + cpp_token_buffert token_buffer; + + cpp_tokent ¤t_token() + { + return token_buffer.current_token(); + } + + void set_location() + { + token_buffer.current_token().line_no=line_no-1; + token_buffer.current_token().filename=filename; + } + + cpp_parsert():mode(ANSI) + { + } + + // scanner + unsigned parenthesis_counter; +}; + +extern cpp_parsert cpp_parser; +int yycpperror(const std::string &error); +void cpp_scanner_init(); + +#endif diff --git a/src/cpp/cpp_scope.cpp b/src/cpp/cpp_scope.cpp new file mode 100644 index 00000000000..c8c04f6df2a --- /dev/null +++ b/src/cpp/cpp_scope.cpp @@ -0,0 +1,281 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_typecheck.h" +#include "cpp_scope.h" + +/*******************************************************************\ + +Function: cpp_scopet::operator << + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::ostream &operator << (std::ostream &out, cpp_scopet::lookup_kindt kind) +{ + switch(kind) + { + case cpp_scopet::QUALIFIED: return out << "QUALIFIED"; + case cpp_scopet::SCOPE_ONLY: return out << "SCOPE_ONLY"; + case cpp_scopet::RECURSIVE: return out << "RECURSIVE"; + default: assert(false); + } + + return out; +} + +/*******************************************************************\ + +Function: cpp_scopet::lookup + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_scopet::lookup( + const irep_idt &base_name, + lookup_kindt kind, + id_sett &id_set) +{ + cpp_id_mapt::iterator + lower_it=sub.lower_bound(base_name); + + if(lower_it!=sub.end()) + { + cpp_id_mapt::iterator + upper_it=sub.upper_bound(base_name); + + for(cpp_id_mapt::iterator n_it=lower_it; + n_it!=upper_it; n_it++) + id_set.insert(&n_it->second); + } + + if(this->base_name==base_name) + id_set.insert(this); + + if(kind==SCOPE_ONLY) return; // done + + // using scopes + for(scope_listt::iterator + it=using_scopes.begin(); + it!=using_scopes.end(); + it++) + { + cpp_scopet &other_scope=static_cast(**it); + + // Recursive call. + // Note the different kind! + other_scope.lookup(base_name, QUALIFIED, id_set); + } + + if(!id_set.empty()) return; // done, upwards scopes are hidden + + // secondary scopes + for(scope_listt::iterator + it=secondary_scopes.begin(); + it!=secondary_scopes.end(); + it++) + { + cpp_scopet &other_scope=static_cast(**it); + + // Recursive call. + // Note the different kind! + other_scope.lookup(base_name, QUALIFIED, id_set); + } + + if(kind==QUALIFIED) return; // done + if(!id_set.empty()) return; // done + + // ask parent, recursive call + if(!is_root_scope()) + get_parent().lookup(base_name, kind, id_set); +} + +/*******************************************************************\ + +Function: cpp_scopet::lookup + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_scopet::lookup( + const irep_idt &base_name, + lookup_kindt kind, + cpp_idt::id_classt id_class, + id_sett &id_set) +{ + // we have a hack to do full search in case we + // are looking for templates! + + #if 0 + std::cout << "B: " << base_name << std::endl; + std::cout << "K: " << kind << std::endl; + std::cout << "I: " << id_class << std::endl; + std::cout << "THIS: " << this->base_name << " " << this->id_class + << " " << this->identifier << std::endl; + #endif + + cpp_id_mapt::iterator + lower_it=sub.lower_bound(base_name); + + if(lower_it!=sub.end()) + { + cpp_id_mapt::iterator + upper_it=sub.upper_bound(base_name); + + for(cpp_id_mapt::iterator n_it=lower_it; + n_it!=upper_it; n_it++) + { + if(n_it->second.id_class == id_class) + id_set.insert(&n_it->second); + } + } + + if(this->base_name == base_name && + this->id_class == id_class) + id_set.insert(this); + + if(kind==SCOPE_ONLY) return; // done + + // using scopes + for(scope_listt::iterator + it=using_scopes.begin(); + it!=using_scopes.end(); + it++) + { + cpp_scopet &other_scope=static_cast(**it); + + // Recursive call. + // Note the different kind! + other_scope.lookup(base_name, QUALIFIED, id_class, id_set); + } + + if(!id_set.empty() && + id_class!=TEMPLATE) return; // done, upwards scopes are hidden + + // secondary scopes + for(scope_listt::iterator + it=secondary_scopes.begin(); + it!=secondary_scopes.end(); + it++) + { + cpp_scopet &other_scope=static_cast(**it); + + // Recursive call. + // Note the different kind! + other_scope.lookup(base_name, QUALIFIED, id_class, id_set); + } + + if(kind==QUALIFIED) return; // done + + if(!id_set.empty() && + id_class!=TEMPLATE) return; // done, upwards scopes are hidden + + // ask parent, recursive call + if(!is_root_scope()) + get_parent().lookup(base_name, kind, id_class, id_set); +} + +/*******************************************************************\ + +Function: cpp_scopet::lookup_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_scopet::lookup_identifier( + const irep_idt &identifier, + cpp_idt::id_classt id_class, + id_sett &id_set) +{ + for(cpp_id_mapt::iterator n_it=sub.begin(); + n_it!=sub.end(); n_it++) + { + if(n_it->second.identifier == identifier + && n_it->second.id_class == id_class) + id_set.insert(&n_it->second); + } + + if(this->identifier == identifier + && this->id_class == id_class) + id_set.insert(this); + + #if 0 + for(unsigned i=0; i +#include + +#include "cpp_id.h" + +class cpp_scopet:public cpp_idt +{ +public: + cpp_scopet() + { + is_scope=true; + } + + typedef std::set id_sett; + + enum lookup_kindt { SCOPE_ONLY, QUALIFIED, RECURSIVE }; + + void lookup( + const irep_idt &base_name, + lookup_kindt kind, + id_sett &id_set); + + void lookup( + const irep_idt &base_name, + lookup_kindt kind, + cpp_idt::id_classt id_class, + id_sett &id_set); + + void lookup_identifier( + const irep_idt &identifier, + cpp_idt::id_classt id_class, + id_sett &id_set); + + bool contains(const irep_idt &base_name); + + bool is_root_scope() const + { + return id_class==ROOT_SCOPE; + } + + bool is_global_scope() const + { + return id_class==ROOT_SCOPE || + id_class==NAMESPACE; + } + + cpp_scopet &get_parent() const + { + return static_cast(cpp_idt::get_parent()); + } + + cpp_scopet &get_global_scope() + { + cpp_scopet *p=this; + + while(!p->is_global_scope()) + p=&(p->get_parent()); + + return *p; + } + + void add_secondary_scope(cpp_scopet &other) + { + assert(other.is_scope); + secondary_scopes.push_back(&other); + } + + void add_using_scope(cpp_scopet &other) + { + assert(other.is_scope); + using_scopes.push_back(&other); + } + + class cpp_scopet &new_scope( + const irep_idt &new_scope_name); +}; + +class cpp_root_scopet:public cpp_scopet +{ +public: + cpp_root_scopet() + { + id_class=ROOT_SCOPE; + identifier="::"; + } +}; + +std::ostream &operator << (std::ostream &out, cpp_scopet::lookup_kindt); + +#endif diff --git a/src/cpp/cpp_scopes.cpp b/src/cpp/cpp_scopes.cpp new file mode 100644 index 00000000000..8cc9e2c8289 --- /dev/null +++ b/src/cpp/cpp_scopes.cpp @@ -0,0 +1,116 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_scopes.h" + +/*******************************************************************\ + +Function: cpp_scopest::new_block_scope + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cpp_scopet &cpp_scopest::new_block_scope() +{ + unsigned prefix=++current_scope().compound_counter; + cpp_scopet &n=new_scope(i2string(prefix)); + n.id_class=cpp_idt::BLOCK_SCOPE; + return n; +} + +/*******************************************************************\ + +Function: cpp_scopest::put_into_scope + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cpp_idt &cpp_scopest::put_into_scope( + const symbolt &symbol, + cpp_scopet &scope, + bool is_friend) +{ + assert(!symbol.name.empty()); + assert(!symbol.base_name.empty()); + + // functions are also scopes + if(symbol.type.id()==ID_code) + { + cpp_scopest::id_mapt::iterator id_it = id_map.find(symbol.name); + if(id_it == id_map.end()) + { + irep_idt block_base_name(std::string("$block:") + symbol.base_name.c_str()); + cpp_idt &id = scope.insert(block_base_name); + id.id_class=cpp_idt::BLOCK_SCOPE; + id.identifier=symbol.name; + id.is_scope=true; + id.prefix = id2string(scope.prefix) + id2string(symbol.base_name) + "::"; + id_map[symbol.name]=&id; + } + } + + // should go away, and be replaced by the 'tag only declaration' rule + if(is_friend) + { + cpp_save_scopet saved_scope(*this); + go_to(scope); + go_to_global_scope(); + + cpp_idt &id=current_scope().insert(symbol.base_name); + id.identifier=symbol.name; + id.id_class = cpp_idt::SYMBOL; + if(id_map.find(symbol.name)==id_map.end()) + id_map[symbol.name]=&id; + return id; + } + else + { + cpp_idt &id=scope.insert(symbol.base_name); + id.identifier=symbol.name; + id.id_class = cpp_idt::SYMBOL; + if(id_map.find(symbol.name)==id_map.end()) + id_map[symbol.name]=&id; + return id; + } +} + +/*******************************************************************\ + +Function: cpp_scopest::print_current + + Inputs: + + Outputs: + Purpose: + +\*******************************************************************/ + +void cpp_scopest::print_current(std::ostream &out) const +{ + const cpp_scopet *scope=current_scope_ptr; + + do + { + scope->print_fields(out); + out << std::endl; + scope=&scope->get_parent(); + } + while(!scope->is_root_scope()); +} diff --git a/src/cpp/cpp_scopes.h b/src/cpp/cpp_scopes.h new file mode 100644 index 00000000000..015decd6db9 --- /dev/null +++ b/src/cpp/cpp_scopes.h @@ -0,0 +1,149 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_SCOPES_H +#define CPROVER_CPP_SCOPES_H + +#include + +#include +#include +#include + +#include "cpp_scope.h" + +class cpp_scopest +{ +public: + cpp_scopest() + { + current_scope_ptr=&root_scope; + } + + typedef std::set scope_sett; + typedef std::set id_sett; + + cpp_scopet ¤t_scope() + { + return *current_scope_ptr; + } + + cpp_scopet &new_scope(const irep_idt &new_scope_name) + { + assert(!new_scope_name.empty()); + cpp_scopet &n=current_scope_ptr->new_scope(new_scope_name); + id_map[n.identifier]=&n; + current_scope_ptr=&n; + return n; + } + + cpp_scopet &new_namespace(const irep_idt &new_scope_name) + { + cpp_scopet &n=new_scope(new_scope_name); + n.id_class=cpp_idt::NAMESPACE; + return n; + } + + cpp_scopet &new_block_scope(); + + cpp_idt &put_into_scope( + const symbolt &symbol, + cpp_scopet &scope, + bool is_friend = false); + + cpp_idt &put_into_scope(const symbolt &symbol, bool is_friend = false) + { + return put_into_scope(symbol, current_scope(), is_friend); + } + + // mapping from function/class/scope names to their cpp_idt + typedef hash_map_cont id_mapt; + id_mapt id_map; + + cpp_scopet *current_scope_ptr; + + cpp_idt &get_id(const irep_idt &identifier) + { + id_mapt::const_iterator it=id_map.find(identifier); + if(it==id_map.end()) + throw "id `"+id2string(identifier)+"' not found"; + return *(it->second); + } + + cpp_scopet &get_scope(const irep_idt &identifier) + { + cpp_idt &n=get_id(identifier); + assert(n.is_scope); + return (cpp_scopet &)n; + } + + cpp_scopet &set_scope(const irep_idt &identifier) + { + current_scope_ptr=&get_scope(identifier); + return current_scope(); + } + + cpp_scopet &get_root_scope() + { + return root_scope; + } + + void go_to_root_scope() + { + current_scope_ptr=&root_scope; + } + + void go_to(cpp_idt &id) + { + assert(id.is_scope); + current_scope_ptr=(cpp_scopet *)&id; + } + + // move up to next global scope + void go_to_global_scope() + { + current_scope_ptr=&get_global_scope(); + } + + cpp_scopet &get_global_scope() + { + return current_scope().get_global_scope(); + } + + void print_current(std::ostream &out) const; + +protected: + // the root scope + cpp_root_scopet root_scope; +}; + +class cpp_save_scopet +{ +public: + cpp_save_scopet(cpp_scopest &_cpp_scopes): + cpp_scopes(_cpp_scopes), + saved_scope(_cpp_scopes.current_scope_ptr) + { + } + + ~cpp_save_scopet() + { + restore(); + } + + void restore() + { + cpp_scopes.current_scope_ptr=saved_scope; + } + +protected: + cpp_scopest &cpp_scopes; + cpp_scopet *saved_scope; +}; + +#endif diff --git a/src/cpp/cpp_storage_spec.h b/src/cpp/cpp_storage_spec.h new file mode 100644 index 00000000000..2fad8fb4e46 --- /dev/null +++ b/src/cpp/cpp_storage_spec.h @@ -0,0 +1,49 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_CPP_STORAGE_SPEC_H +#define CPROVER_CPP_CPP_STORAGE_SPEC_H + +#include + +class cpp_storage_spect:public irept +{ +public: + cpp_storage_spect():irept(ID_cpp_storage_spec) + { + } + + locationt &location() + { + return static_cast(add(ID_C_location)); + } + + const locationt &location() const + { + return static_cast(find(ID_C_location)); + } + + bool is_static() const { return get(ID_storage)==ID_static; } + bool is_extern() const { return get(ID_storage)==ID_extern; } + bool is_auto() const { return get(ID_storage)==ID_auto; } + bool is_register() const { return get(ID_storage)==ID_register; } + bool is_mutable() const { return get(ID_storage)==ID_mutable; } + + void set_static () { set(ID_storage, ID_static); } + void set_extern () { set(ID_storage, ID_extern); } + void set_auto () { set(ID_storage, ID_auto); } + void set_register() { set(ID_storage, ID_register); } + void set_mutable () { set(ID_storage, ID_mutable); } + + bool is_empty() const + { + return get(ID_storage)==irep_idt(); + } +}; + +#endif diff --git a/src/cpp/cpp_template_args.h b/src/cpp/cpp_template_args.h new file mode 100644 index 00000000000..7fd5db86b3f --- /dev/null +++ b/src/cpp/cpp_template_args.h @@ -0,0 +1,88 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_TEMPLATE_ARGS_H +#define CPROVER_CPP_TEMPLATE_ARGS_H + +#include + +// A data structures for template arguments, i.e., +// expressions of the form . +// Not to be confused with the template parameters! + +class cpp_template_args_baset:public irept +{ +public: + cpp_template_args_baset():irept(ID_template_args) + { + } + + typedef std::vector argumentst; + + argumentst &arguments() + { + return (argumentst &)(add(ID_arguments).get_sub()); + } + + const argumentst &arguments() const + { + return (const argumentst &)(find(ID_arguments).get_sub()); + } +}; + +// the non-yet typechecked variant + +class cpp_template_args_non_tct:public cpp_template_args_baset +{ +}; + +extern inline cpp_template_args_non_tct &to_cpp_template_args_non_tc(irept &irep) +{ + assert(irep.id()==ID_template_args); + return static_cast(irep); +} + +extern inline const cpp_template_args_non_tct &to_cpp_template_args_non_tc(const irept &irep) +{ + assert(irep.id()==ID_template_args); + return static_cast(irep); +} + +// the already typechecked variant + +class cpp_template_args_tct:public cpp_template_args_baset +{ +public: + bool has_unassigned() const + { + const argumentst &_arguments=arguments(); + for(argumentst::const_iterator + it=_arguments.begin(); + it!=_arguments.end(); + it++) + if(it->id()==ID_unassigned || + it->type().id()==ID_unassigned) + return true; + + return false; + } +}; + +extern inline cpp_template_args_tct &to_cpp_template_args_tc(irept &irep) +{ + assert(irep.id()==ID_template_args); + return static_cast(irep); +} + +extern inline const cpp_template_args_tct &to_cpp_template_args_tc(const irept &irep) +{ + assert(irep.id()==ID_template_args); + return static_cast(irep); +} + +#endif diff --git a/src/cpp/cpp_template_parameter.h b/src/cpp/cpp_template_parameter.h new file mode 100644 index 00000000000..1f210f2b192 --- /dev/null +++ b/src/cpp/cpp_template_parameter.h @@ -0,0 +1,57 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_TEMPLATE_TYPE_H +#define CPROVER_CPP_TEMPLATE_TYPE_H + +#include + +// A data structure for expressions of the form +// +// Not to be confused with template arguments! + +struct cpp_template_parametert:public irept +{ +public: + cpp_template_parametert():irept(ID_template_parameter) + { + } + + bool get_is_type() const + { + return get_bool(ID_is_type); + } + + void set_is_type(bool value) + { + set(ID_is_type, value); + } + + irep_idt get_identifier() const + { + return get(ID_identifier); + } + + void set_identifier(const irep_idt &identifier) + { + return set(ID_identifier, identifier); + } + + // the type of expression parameters + typet &type() + { + return static_cast(add(ID_type)); + } + + const typet &type() const + { + return static_cast(find(ID_type)); + } +}; + +#endif diff --git a/src/cpp/cpp_template_type.h b/src/cpp/cpp_template_type.h new file mode 100644 index 00000000000..3c8b4b8343c --- /dev/null +++ b/src/cpp/cpp_template_type.h @@ -0,0 +1,67 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_TEMPLATE_TYPE_H +#define CPROVER_CPP_TEMPLATE_TYPE_H + +#include + +#include "cpp_template_parameter.h" + +class template_typet:public typet +{ +public: + template_typet() + { + id(ID_template); + } + + typedef exprt::operandst parameterst; + + parameterst ¶meters() + { + // todo: will change to 'parameters' + return (parameterst &)add(ID_arguments).get_sub(); + } + + const parameterst ¶meters() const + { + // todo: will change to 'parameters' + return (const parameterst &)find(ID_arguments).get_sub(); + } +}; + +inline template_typet &to_template_type(typet &type) +{ + assert(type.id()==ID_template); + return static_cast(type); +} + +inline const template_typet &to_template_type(const typet &type) +{ + assert(type.id()==ID_template); + return static_cast(type); +} + +inline const typet &template_subtype(const typet &type) +{ + if(type.id()==ID_template) + return type.subtype(); + + return type; +} + +inline typet &template_subtype(typet &type) +{ + if(type.id()==ID_template) + return type.subtype(); + + return type; +} + +#endif diff --git a/src/cpp/cpp_token.h b/src/cpp/cpp_token.h new file mode 100644 index 00000000000..784ed1d4681 --- /dev/null +++ b/src/cpp/cpp_token.h @@ -0,0 +1,47 @@ +/*******************************************************************\ + +Module: C++ Parser: Token + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_TOKEN_H +#define CPROVER_CPP_TOKEN_H + +#include + +#include + +class cpp_tokent +{ +public: + int kind; + exprt data; + std::string text; + unsigned line_no; + irep_idt filename; + unsigned pos; + + void clear() + { + kind=0; + data.clear(); + text=""; + line_no=0; + filename=""; + pos=0; + } + + void swap(cpp_tokent &token) + { + std::swap(kind, token.kind); + data.swap(token.data); + text.swap(token.text); + std::swap(line_no, token.line_no); + filename.swap(token.filename); + std::swap(pos, token.pos); + } +}; + +#endif diff --git a/src/cpp/cpp_token_buffer.cpp b/src/cpp/cpp_token_buffer.cpp new file mode 100644 index 00000000000..89a8e5143db --- /dev/null +++ b/src/cpp/cpp_token_buffer.cpp @@ -0,0 +1,180 @@ +/*******************************************************************\ + +Module: C++ Parser: Token Buffer + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_token_buffer.h" +#include "tokens.h" + +/*******************************************************************\ + +Function: cpp_token_buffert::LookAhead + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int cpp_token_buffert::LookAhead(unsigned offset) +{ + assert(current_pos<=token_vector.size()); + + offset+=current_pos; + + while(offset>=token_vector.size()) + read_token(); + + return token_vector[offset]->kind; +} + +/*******************************************************************\ + +Function: cpp_token_buffert::GetToken + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int cpp_token_buffert::GetToken(cpp_tokent &token) +{ + assert(current_pos<=token_vector.size()); + + if(token_vector.size()==current_pos) read_token(); + + token=*token_vector[current_pos]; + + current_pos++; + + return token.kind; +} + +/*******************************************************************\ + +Function: cpp_token_buffert::GetToken + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int cpp_token_buffert::GetToken() +{ + assert(current_pos<=token_vector.size()); + + if(token_vector.size()==current_pos) read_token(); + + int kind=token_vector[current_pos]->kind; + + current_pos++; + + return kind; +} + +/*******************************************************************\ + +Function: cpp_token_buffert::LookAhead + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int cpp_token_buffert::LookAhead(unsigned offset, cpp_tokent &token) +{ + assert(current_pos<=token_vector.size()); + + offset+=current_pos; + + while(offset>=token_vector.size()) + read_token(); + + token=*token_vector[offset]; + + return token.kind; +} + +/*******************************************************************\ + +Function: cpp_token_buffert::read_token + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int yycpplex(); +extern char yycpptext[]; + +void cpp_token_buffert::read_token() +{ + tokens.push_back(cpp_tokent()); + token_vector.push_back(--tokens.end()); + + int kind; + + do + { + kind=yycpplex(); + } + while(kind==Ignore); + + tokens.back().kind=kind; + tokens.back().pos=token_vector.size()-1; + //std::cout << "II: " << token_vector.back()->kind << std::endl; + //std::cout << "I2: " << token_vector.size() << std::endl; +} + +/*******************************************************************\ + +Function: cpp_token_buffert::Save + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cpp_token_buffert::post cpp_token_buffert::Save() +{ + return current_pos; +} + +/*******************************************************************\ + +Function: cpp_token_buffert::Restore + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_token_buffert::Restore(post pos) +{ + current_pos=pos; +} diff --git a/src/cpp/cpp_token_buffer.h b/src/cpp/cpp_token_buffer.h new file mode 100644 index 00000000000..fa29a1a01b6 --- /dev/null +++ b/src/cpp/cpp_token_buffer.h @@ -0,0 +1,61 @@ +/*******************************************************************\ + +Module: C++ Parser: Token Buffer + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_TOKEN_BUFFER_H +#define CPROVER_CPP_TOKEN_BUFFER_H + +#include "cpp_token.h" + +class cpp_token_buffert +{ +public: + cpp_token_buffert():current_pos(0) + { + } + + typedef unsigned int post; + + int LookAhead(unsigned offset); + int GetToken(cpp_tokent &token); + int GetToken(); + int LookAhead(unsigned offset, cpp_tokent &token); + + post Save(); + void Restore(post pos); + //void GetOnlyClosingBracket(Token&); + + //void GetComments(irept &p); + //void GetComments2(irept &p); + + void clear() + { + tokens.clear(); + token_vector.clear(); + current_pos=0; + } + + // the token that is currently being read from the file + cpp_tokent ¤t_token() + { + assert(!tokens.empty()); + return tokens.back(); + } + +protected: + typedef std::list tokenst; + tokenst tokens; + + std::vector token_vector; + + post current_pos; + + // get another token from lexer + void read_token(); +}; + +#endif diff --git a/src/cpp/cpp_type2name.cpp b/src/cpp/cpp_type2name.cpp new file mode 100644 index 00000000000..570a735708f --- /dev/null +++ b/src/cpp/cpp_type2name.cpp @@ -0,0 +1,213 @@ +/*******************************************************************\ + +Module: C++ Language Module + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include +#include + +/*******************************************************************\ + +Function: do_prefix + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static std::string do_prefix(const std::string &s) +{ + if(s.find(',')!=std::string::npos || + (s!="" && isdigit(s[0]))) + return i2string(s.size())+"_"+s; + + return s; +} + +/*******************************************************************\ + +Function: irep2name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void irep2name(const irept &irep, std::string &result) +{ + result=""; + + if(is_reference(static_cast(irep))) + result+="reference"; + + if(irep.id()!="") + result+=do_prefix(irep.id_string()); + + if(irep.get_named_sub().empty() && + irep.get_sub().empty() && + irep.get_comments().empty()) + return; + + result+="("; + bool first=true; + + forall_named_irep(it, irep.get_named_sub()) + { + if(first) first=false; else result+=","; + + result+=do_prefix(name2string(it->first)); + + result+="="; + std::string tmp; + irep2name(it->second, tmp); + result+=tmp; + } + + forall_named_irep(it, irep.get_comments()) + if(it->first==ID_C_constant || + it->first==ID_C_volatile || + it->first==ID_C_restricted) + { + if(first) first=false; else result+=","; + result+=do_prefix(name2string(it->first)); + result+="="; + std::string tmp; + irep2name(it->second, tmp); + result+=tmp; + } + + forall_irep(it, irep.get_sub()) + { + if(first) first=false; else result+=","; + std::string tmp; + irep2name(*it, tmp); + result+=tmp; + } + + result+=")"; +} + +/*******************************************************************\ + +Function: cpp_type2name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_type2name(const typet &type) +{ + std::string result; + + if(type.get_bool(ID_C_constant) || + type.get(ID_C_qualifier)==ID_const) + result+="const_"; + + if(type.get_bool(ID_C_restricted)) + result+="restricted_"; + + if(type.get_bool(ID_C_volatile)) + result+="volatile_"; + + if(type.id()==ID_empty || type.id()==ID_void) + result+="void"; + else if(type.id()==ID_bool) + result+="bool"; + else if(type.id()==ID_pointer) + { + if(is_reference(type)) + result+="ref_"+cpp_type2name(type.subtype()); + else if(is_rvalue_reference(type)) + result+="rref_"+cpp_type2name(type.subtype()); + else + result+="ptr_"+cpp_type2name(type.subtype()); + } + else if(type.id()==ID_signedbv || type.id()==ID_unsignedbv) + { + // we try to use #cpp_type + const irep_idt cpp_type=type.get(ID_C_cpp_type); + + if(cpp_type!=irep_idt()) + result+=id2string(cpp_type); + else if(type.id()==ID_unsignedbv) + result+="unsigned_int"; + else + result+="signed_int"; + } + else if(type.id()==ID_fixedbv || type.id()==ID_floatbv) + { + // we try to use #cpp_type + const irep_idt cpp_type=type.get(ID_C_cpp_type); + + if(cpp_type!=irep_idt()) + result+=id2string(cpp_type); + else + result+="double"; + } + else if(type.id()==ID_code) + { + // we do (args)->(return_type) + const code_typet::argumentst &arguments=to_code_type(type).arguments(); + const typet &return_type=to_code_type(type).return_type(); + + result+="("; + + for(code_typet::argumentst::const_iterator + arg_it=arguments.begin(); + arg_it!=arguments.end(); + arg_it++) + { + if(arg_it!=arguments.begin()) result+=","; + result+=cpp_type2name(arg_it->type()); + } + + result+=")"; + result+="->("; + result+=cpp_type2name(return_type); + result+=")"; + } + else + { + // give up + std::string tmp; + irep2name(type, tmp); + return tmp; + } + + return result; +} + +/*******************************************************************\ + +Function: cpp_expr2name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_expr2name(const exprt &expr) +{ + std::string tmp; + irep2name(expr, tmp); + return tmp; +} + diff --git a/src/cpp/cpp_type2name.h b/src/cpp/cpp_type2name.h new file mode 100644 index 00000000000..968e667599d --- /dev/null +++ b/src/cpp/cpp_type2name.h @@ -0,0 +1,13 @@ +/*******************************************************************\ + +Module: C++ Language Module + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include + +std::string cpp_type2name(const typet &type); +std::string cpp_expr2name(const exprt &expr); diff --git a/src/cpp/cpp_typecast.cpp b/src/cpp/cpp_typecast.cpp new file mode 100644 index 00000000000..d93c1ca897c --- /dev/null +++ b/src/cpp/cpp_typecast.cpp @@ -0,0 +1,746 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "cpp_typecast.h" +#include "cpp_typecheck.h" +#include "cpp_util.h" + +/*******************************************************************\ + +Function: cpp_typecastt::cpp_typecastt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cpp_typecastt::cpp_typecastt(cpp_typecheckt &cpp_typecheck): + c_typecastt(cpp_typecheck), + cpp_typecheck(cpp_typecheck) +{ +} + +/*******************************************************************\ + +Function: cpp_typecastt::check_qualifiers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecastt::check_qualifiers( + const typet &from, + const typet &to) +{ + // check qualifiers + c_qualifierst q_from(from), q_to(to); + + if(q_from.is_constant && + !q_to.is_constant) + { + errors.push_back("disregards const"); + return; + } + + if(q_from.is_volatile && + !q_to.is_volatile) + { + errors.push_back("disregards volatile"); + return; + } + + if(q_from.is_restricted && + !q_to.is_restricted) + { + errors.push_back("disregards restricted"); + return; + } +} + +/*******************************************************************\ + +Function: cpp_typecastt::get_bases + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecastt::get_bases( + const irep_idt &identifier, + std::map &base_count) +{ + const symbolt &symbol=ns.lookup(identifier); + + if(symbol.type.id()!=ID_struct) + return; + + const irept::subt &bases=symbol.type.find("bases").get_sub(); + + forall_irep(it, bases) + { + assert(it->id()==ID_type); + assert(it->get(ID_type) == ID_symbol); + const irep_idt &base=it->find(ID_type).get(ID_identifier); + base_count[base]++; + get_bases(base, base_count); + } +} + +/*******************************************************************\ + +Function: cpp_typecastt::subtype_typecast + + Inputs: + + Outputs: false when ok, true when error + + Purpose: + +\*******************************************************************/ + +bool cpp_typecastt::subtype_typecast( + const typet &from, + const typet &to, + std::string &err) +{ + if(from==to) return false; // ok + + if(ns.follow(from)==ns.follow(to)) return false; // ok + + if(is_reference(from) && !is_reference(to)) + { + err = "types are incompatible"; + return true; // not ok + } + + if(!is_reference(from) && is_reference(to)) + { + err = "types are incompatible"; + return true; // not ok + } + + // "to" must be subtype of "from" + + irep_idt from_name; + + if(from.id()==ID_struct) + from_name=from.get(ID_name); + else if(from.id()==ID_symbol) + from_name=from.get(ID_identifier); + else + { + err = "types are incompatible"; + return true; + } + + irep_idt to_name; + + if(to.id()==ID_struct) + to_name=to.get(ID_name); + else if(to.id()==ID_symbol) + to_name = to.get(ID_identifier); + else + { + err = "types are incompatible"; + return true; + } + + std::map base_count; + get_bases(from_name, base_count); + + unsigned c=base_count[to_name]; + + if(c==1) + return false; // ok + else if(c>1) + { + err = "base is ambiguous"; + return true; + } + + err ="types are incompatible"; + return true; +} + + +/*******************************************************************\ + +Function: cpp_typecastt::make_ptr_subtypecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecastt::make_ptr_typecast( + exprt &expr, + const typet &src_type, + const typet &dest_type) +{ + assert(src_type.id()==ID_pointer); + assert(dest_type.id()==ID_pointer); + + struct_typet src_struct = + to_struct_type(static_cast(ns.follow(src_type.subtype()))); + + struct_typet dest_struct = + to_struct_type(static_cast(ns.follow(dest_type.subtype()))); + + exprt offset = subtype_offset(src_struct, dest_struct); + if(offset.is_not_nil()) + { + cpp_typecheck.typecheck_expr(offset); + simplify_exprt simplify; + simplify.simplify(offset); + + if(!offset.is_zero()) + { + typet pvoid(ID_pointer); + pvoid.subtype().id(ID_signedbv); + pvoid.subtype().set(ID_width, "8"); + + if(expr.type().subtype().get_bool("#constant")) + pvoid.subtype().set("#constant",true); + + expr.make_typecast(pvoid); + + already_typechecked(expr); + + exprt tmp("+"); + tmp.move_to_operands(expr); + tmp.move_to_operands(offset); + cpp_typecheck.typecheck_expr(tmp); + expr.swap(tmp); + } + expr.make_typecast(dest_type); + return; + } + + offset = subtype_offset(dest_struct, src_struct); + if(offset.is_not_nil()) + { + cpp_typecheck.typecheck_expr(offset); + simplify_exprt simplify; + simplify.simplify(offset); + + if(!offset.is_zero()) + { + typet pvoid(ID_pointer); + pvoid.subtype().id(ID_signedbv); + pvoid.subtype().set(ID_width, "8"); + + if(expr.type().subtype().get_bool("#constant")) + pvoid.subtype().set("#constant", true); + + expr.make_typecast(pvoid); + + already_typechecked(expr); + + exprt tmp("-"); + tmp.move_to_operands(expr); + tmp.move_to_operands(offset); + cpp_typecheck.typecheck_expr(tmp); + expr.swap(tmp); + } + + expr.make_typecast(dest_type); + return; + } + + errors.push_back("conversion not permitted"); + return; + +} + +/*******************************************************************\ + +Function: cpp_typecastt::implicit_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecastt::implicit_typecast( + exprt &expr, + const typet &type) +{ + c_typecastt::implicit_typecast(expr, type); +} + +/*******************************************************************\ + +Function: cpp_typecastt::implicit_typecast_followed + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecastt::implicit_typecast_followed( + exprt &expr, + const typet &src_type, + const typet &dest_type) +{ + if(is_reference(dest_type)) + { + if(is_reference(src_type)) + { + std::string err; + if(subtype_typecast(src_type.subtype(), dest_type.subtype(),err)) + { + errors.push_back(err); + return; + } + + check_qualifiers(src_type.subtype(), dest_type.subtype()); + + if(src_type==dest_type) + expr.type()=dest_type; // because of qualifiers + else + make_ptr_typecast(expr,src_type, dest_type); + } + else // expr is not a reference + { + if(expr.get_bool(ID_C_lvalue)) + { + std::string err; + if(subtype_typecast(src_type, dest_type.subtype(),err)) + { + errors.push_back(err); + return; + } + + check_qualifiers(src_type, dest_type.subtype()); + + typet reference=reference_typet(); + reference.subtype() = expr.type(); + + exprt tmp(ID_address_of, reference); + tmp.set(ID_C_lvalue, true); + tmp.location() = expr.location(); + tmp.move_to_operands(expr); + expr.swap(tmp); + + // redo, but this time expr is a reference + implicit_typecast_followed(expr,reference, dest_type); + return; + } + else + { + // need temporary object + if(dest_type.subtype().get_bool(ID_C_constant)) + { + if(integral_conversion(src_type, dest_type.subtype())) + { + std::string err; + if(subtype_typecast(src_type, dest_type.subtype(),err)) + { + errors.push_back(err); + return; + } + } + else + { + // do an integral conversion first + if(src_type !=dest_type.subtype()) + { + expr.make_typecast(dest_type.subtype()); + // redo but this time src_type = dest_type.subtype() + implicit_typecast_followed(expr, expr.type(), dest_type); + return; + } + } + + check_qualifiers(src_type.subtype(), dest_type.subtype()); + + // create temporary object + exprt tmp_object_expr=exprt(ID_sideeffect, expr.type()); + + tmp_object_expr.type().set(ID_C_constant, false); + tmp_object_expr.set(ID_statement, ID_temporary_object); + tmp_object_expr.set(ID_C_lvalue, true); + tmp_object_expr.location()=expr.location(); + + + if(cpp_typecheck.cpp_is_pod(src_type)) + tmp_object_expr.move_to_operands(expr); + else + { + + exprt new_object("new_object", tmp_object_expr.type()); + new_object.set(ID_C_lvalue, true); + new_object.location()=tmp_object_expr.location(); + + already_typechecked(new_object); + + exprt reference(ID_address_of, reference_typet()); + reference.location() = expr.location(); + reference.type().subtype() = expr.type(); + reference.copy_to_operands(expr); + + already_typechecked(reference); + + exprt::operandst ops; + ops.push_back(reference); + + // Note that implicit_typecast is called again but + // this time both dest and sources are references. + codet new_code = + cpp_typecheck.cpp_constructor(expr.location(),new_object, ops); + assert(new_code.is_not_nil()); + tmp_object_expr.add("initializer") = new_code; + } + + typet new_src_type = follow_with_qualifiers(tmp_object_expr.type()); + + // redo but this time expr is an lvalue. + implicit_typecast_followed(tmp_object_expr,new_src_type,dest_type); + expr.swap(tmp_object_expr); + return; + } + else + { + errors.push_back("converting non-lvalue to non-const reference"); + return; + } + } + } + return; // ok + } + else if(dest_type.id()==ID_pointer) + { + assert(!is_reference(dest_type)); + + if(src_type.id()==ID_pointer || src_type.id()==ID_array) + { + if(is_reference(src_type)) + { + errors.push_back("conversion not permitted"); + return; + } + else if(dest_type.subtype().id()==ID_empty) + { + // to and from void * is ok, unless it's a function pointer + } + else if(src_type.subtype().id()==ID_empty) + { + if(dest_type.subtype().id()==ID_code) + { + errors.push_back("converting void pointer to function pointer"); + return; + } + } + else if(dest_type.find("to-member").is_not_nil()) + { + if(dest_type != src_type) + { + errors.push_back("pointer-to-member typecast error"); + return; + } + } + else if(src_type.find("to-member").is_not_nil()) + { + errors.push_back("converting pointer-to-member to pointer"); + return; + } + else + { + std::string err; + if(integral_conversion(src_type.subtype(), dest_type.subtype()) + && subtype_typecast(src_type.subtype(), dest_type.subtype(),err)) + { + errors.push_back(err); + return; + } + } + + // check qualifiers in either case + check_qualifiers(src_type.subtype(), dest_type.subtype()); + + if(src_type==dest_type) + expr.type()=dest_type; // because of qualifiers + else if(dest_type.subtype().id() == ID_symbol + && ns.follow(dest_type.subtype()).id() == ID_struct) + make_ptr_typecast(expr,src_type,dest_type); + else + do_typecast(expr, dest_type); + return; // ok + } + } + else if(dest_type.id()==ID_struct) + { + if(is_reference(src_type)) + { + exprt dereference(ID_dereference, expr.type().subtype()); + dereference.set(ID_C_implicit, true); + dereference.location() = expr.location(); + dereference.copy_to_operands(expr); + typet new_src_type = follow_with_qualifiers(dereference.type()); + + // redo, but this time expr is not a reference + implicit_typecast_followed(dereference, new_src_type, dest_type); + expr.swap(dereference); + return; + } + + // Derived to base conversion + std::string err; + if(!subtype_typecast(src_type, dest_type,err)) + { + + c_qualifierst src_qualifiers(src_type); + typet src_sym_type(ID_symbol); + src_sym_type.set(ID_identifier, src_type.get(ID_name)); + src_qualifiers.write(src_sym_type); + + c_qualifierst dest_qualifiers(dest_type); + typet dest_sym_type(ID_symbol); + dest_sym_type.set(ID_identifier, dest_type.get(ID_name)); + dest_qualifiers.write(dest_sym_type); + + if(src_type == dest_type) + { + expr.type()=dest_sym_type; // because of qualifiers + } + else + { + typet pointer_src(ID_pointer); + pointer_src.subtype() = src_sym_type; + exprt pointer_to_expr(ID_address_of, pointer_src); + pointer_to_expr.copy_to_operands(expr); + + typet pointer_dest(ID_pointer); + pointer_dest.subtype() = dest_sym_type; + make_ptr_typecast(pointer_to_expr, pointer_to_expr.type(), pointer_dest); + exprt dereference(ID_dereference, dest_sym_type); + dereference.move_to_operands(pointer_to_expr); + dereference.set(ID_C_lvalue, expr.get_bool(ID_C_lvalue)); + expr.swap(dereference); + } + return; + } + else + { + // constructor conversion + + const struct_typet &struct_type= + to_struct_type(dest_type); + + const struct_typet::componentst &components= + struct_type.components(); + + // let's look for a suitable constructor + int count = 0; + exprt arg1; + + forall_expr(it, components) + { + const typet &type=it->type(); + + if(it->get_bool("from_base") || + type.id()!=ID_code || + type.find(ID_return_type).id()!=ID_constructor) + continue; + + // TODO: ellipsis + + const irept &arguments = type.find(ID_arguments); + + if(arguments.get_sub().size() != 2) + continue; + + exprt curr_arg1 = static_cast (arguments.get_sub()[1]); + + typet arg1_type = curr_arg1.type(); + + if(is_reference(arg1_type)) + { + typet tmp=arg1_type.subtype(); + arg1_type.swap(tmp); + } + + std::string err; + if(subtype_typecast(src_type, cpp_typecheck.follow(arg1_type), err)) + continue; + count++; + + arg1 = curr_arg1; + } + + if(count == 0) + { + errors.push_back("type are incompatible"); + return; + } + else if(count > 1) + { + errors.push_back("constructor-conversion is ambigious"); + return; + } + + exprt tmp_expr(expr); + typet new_dest_type = follow_with_qualifiers(arg1.type()); + implicit_typecast_followed(tmp_expr,src_type, new_dest_type); + already_typechecked(tmp_expr); + + c_qualifierst dest_qualifiers(dest_type); + typet dest_sym_type(ID_symbol); + dest_sym_type.set(ID_identifier, dest_type.get(ID_name)); + dest_qualifiers.write(dest_sym_type); + + // create temporary object + exprt tmp_object_expr=exprt(ID_sideeffect, dest_sym_type); + tmp_object_expr.set(ID_statement, ID_temporary_object); + tmp_object_expr.location()=expr.location(); + tmp_object_expr.set(ID_C_lvalue, true); + + assert(!cpp_typecheck.cpp_is_pod(dest_type)); + + exprt new_object("new_object",tmp_object_expr.type()); + new_object.location() = tmp_object_expr.location(); + new_object.set(ID_C_lvalue, true); + + already_typechecked(new_object); + + exprt::operandst ops; + ops.push_back(tmp_expr); + + codet new_code = + cpp_typecheck.cpp_constructor(expr.location(),new_object, ops); + assert(new_code.is_not_nil()); + tmp_object_expr.add("initializer") = new_code; + + expr.swap(tmp_object_expr); + return; + } + } + + // C++, in contrast to C, does not allow conversions between + // diferent enum types + + if(src_type.id()=="c_enum" && + dest_type.id()=="c_enum" && + src_type!=dest_type) + { + errors.push_back("conversion between enum-types not permitted"); + return; + } + else if(src_type.id()==ID_pointer) + { + if(dest_type.id()!=ID_bool) + { + errors.push_back("conversion not permitted"); + return; + } + } + + c_typecastt::implicit_typecast_followed(expr, src_type, dest_type); +} + +/*******************************************************************\ + +Function: cpp_typecastt::integral_conversion + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecastt::integral_conversion( + const typet &src_type, + const typet &dest_type) +{ + + if(src_type.id() != ID_signedbv + && src_type.id() != ID_unsignedbv + && src_type.id() != ID_c_enum + && src_type.id() != ID_integer + && src_type.id() != ID_char + && src_type.id() != ID_bool) + return true; // not ok + + if(dest_type.id() != ID_signedbv + && dest_type.id() != ID_unsignedbv + && dest_type.id() != ID_c_enum + && dest_type.id() != ID_integer + && dest_type.id() != ID_char + && dest_type.id() != ID_bool) + return true; //not ok + + return false; // ok + +} + +/*******************************************************************\ + +Function: cpp_typecastt::implicit_typecast_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecastt::implicit_typecast_arithmetic(exprt &expr) +{ + //c_typecastt::implicit_typecast_arithmetic(expr); +} + +/*******************************************************************\ + +Function: cpp_typecastt::implicit_typecast_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecastt::implicit_typecast_arithmetic( + exprt &expr1, + exprt &expr2) +{ + c_typecastt::implicit_typecast_arithmetic(expr1, expr2); +} diff --git a/src/cpp/cpp_typecast.h b/src/cpp/cpp_typecast.h new file mode 100644 index 00000000000..db4543e4d62 --- /dev/null +++ b/src/cpp/cpp_typecast.h @@ -0,0 +1,68 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_TYPECAST_H +#define CPROVER_CPP_TYPECAST_H + +#include + +class cpp_typecheckt; + +class cpp_typecastt:public c_typecastt +{ +public: + cpp_typecastt(cpp_typecheckt &cpp_typecheck); + + virtual void implicit_typecast( + exprt &expr, + const typet &type); + + virtual void implicit_typecast_arithmetic( + exprt &expr); + + virtual void implicit_typecast_arithmetic( + exprt &expr1, + exprt &expr2); + +protected: + virtual void implicit_typecast_followed( + exprt &expr, + const typet &src_type, + const typet &dest_type); + + void get_bases( + const irep_idt &identifier, + std::map &base_count); + +public: + void check_qualifiers( + const typet &from, + const typet &to); + + bool subtype_typecast( + const typet &from, + const typet &to, + std::string& err); + + bool integral_conversion( + const typet &src_type, + const typet &dest_type); + + exprt subtype_offset( + const struct_typet &from, + const struct_typet &to); + + void make_ptr_typecast( + exprt &expr, + const typet & src_type, + const typet & dest_type); + + cpp_typecheckt &cpp_typecheck; +}; + +#endif diff --git a/src/cpp/cpp_typecheck.cpp b/src/cpp/cpp_typecheck.cpp new file mode 100644 index 00000000000..ae48badbe79 --- /dev/null +++ b/src/cpp/cpp_typecheck.cpp @@ -0,0 +1,508 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "cpp_typecheck.h" +#include "expr2cpp.h" +#include "cpp_convert_type.h" +#include "cpp_declarator.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::this_struct_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const struct_typet &cpp_typecheckt::this_struct_type() +{ + const exprt &this_expr= + cpp_scopes.current_scope().this_expr; + + assert(this_expr.is_not_nil()); + assert(this_expr.type().id()==ID_pointer); + + const typet &t=follow(this_expr.type().subtype()); + assert(t.id()==ID_struct); + return to_struct_type(t); +} + +/*******************************************************************\ + +Function: cpp_identifier_prefix + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_identifier_prefix(const irep_idt &mode) +{ + // we need to be able to link to c code + return "c"; + + #if 0 + if(mode==ID_cpp) + return "cpp"; + else if(mode==ID_C) + return "c"; + else + return id2string(mode); + #endif +} + +/*******************************************************************\ + +Function: cpp_typecheckt::to_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_typecheckt::to_string(const exprt &expr) +{ + return expr2cpp(expr, *this); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::to_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_typecheckt::to_string(const typet &type) +{ + return type2cpp(type, *this); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert(cpp_itemt &item) +{ + if(item.is_declaration()) + convert(to_cpp_declaration(item)); + else if(item.is_linkage_spec()) + convert(item.get_linkage_spec()); + else if(item.is_namespace_spec()) + convert(item.get_namespace_spec()); + else if(item.is_using()) + convert(item.get_using()); + else + { + err_location(item); + throw "unknown parse-tree element: "+item.id_string(); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck() +{ + // default linkage is C++ + current_mode=ID_cpp; + + for(cpp_parse_treet::itemst::iterator + it=cpp_parse_tree.items.begin(); + it!=cpp_parse_tree.items.end(); + it++) + convert(*it); + + static_initialization(); + + do_not_typechecked(); + + clean_up(); +} + +/*******************************************************************\ + +Function: cpp_typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecheck( + cpp_parse_treet &cpp_parse_tree, + contextt &context, + const std::string &module, + message_handlert &message_handler) +{ + cpp_typecheckt cpp_typecheck(cpp_parse_tree, context, module, message_handler); + return cpp_typecheck.typecheck_main(); +} + +/*******************************************************************\ + +Function: cpp_typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecheck( + exprt &expr, + message_handlert &message_handler, + const namespacet &ns) +{ + contextt context; + cpp_parse_treet cpp_parse_tree; + + cpp_typecheckt cpp_typecheck(cpp_parse_tree, context, + ns.get_context(), "", message_handler); + + try + { + cpp_typecheck.typecheck_expr(expr); + } + + catch(int e) + { + cpp_typecheck.error(); + } + + catch(const char *e) + { + cpp_typecheck.error(e); + } + + catch(const std::string &e) + { + cpp_typecheck.error(e); + } + + return cpp_typecheck.get_error_found(); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::static_initialization + + Inputs: + + Outputs: + + Purpose: Initialization of static objects: + + "Objects with static storage duration (3.7.1) shall be zero-initialized + (8.5) before any other initialization takes place. Zero-initialization + and initialization with a constant expression are collectively called + static initialization; all other initialization is dynamic + initialization. Objects of POD types (3.9) with static storage duration + initialized with constant expressions (5.19) shall be initialized before + any dynamic initialization takes place. Objects with static storage + duration defined in namespace scope in the same translation unit and + dynamically initialized shall be initialized in the order in which their + definition appears in the translation unit. [Note: 8.5.1 describes the + order in which aggregate members are initialized. The initialization + of local static objects is described in 6.7. ]" + +\*******************************************************************/ + +void cpp_typecheckt::static_initialization() +{ + code_blockt block_sini; // Static Initialization Block + code_blockt block_dini; // Dynamic Initialization Block + + disable_access_control = true; + + // first do zero initialization + forall_symbols(s_it, context.symbols) + { + const symbolt &symbol=s_it->second; + + if(!symbol.static_lifetime || symbol.mode!=current_mode) + continue; + + // magic value + if(symbol.name=="c::__CPROVER::constant_infinity_uint") + continue; + + // it has a non-code initializer already? + if(symbol.value.is_not_nil() && + symbol.value.id()!=ID_code) + continue; + + // it's a declaration only + if(symbol.is_extern) + continue; + + if(!symbol.lvalue) + continue; + + zero_initializer( + cpp_symbol_expr(symbol), + symbol.type, + symbol.location, + block_sini.operands()); + } + + while(!dinis.empty()) + { + symbolt &symbol=context.symbols.find(dinis.front())->second; + dinis.pop_front(); + + if(symbol.is_extern) + continue; + + if(symbol.mode!=current_mode) + continue; + + assert(symbol.static_lifetime); + assert(!symbol.is_type); + assert(symbol.type.id()!=ID_code); + + exprt symexpr=cpp_symbol_expr(symbol); + + if(symbol.value.is_not_nil()) + { + if(!cpp_is_pod(symbol.type)) + { + block_dini.move_to_operands(symbol.value); + } + else + { + exprt symbexpr(ID_symbol, symbol.type); + symbexpr.set(ID_identifier, symbol.name); + + codet code; + code.set_statement(ID_assign); + code.copy_to_operands(symbexpr, symbol.value); + code.location()=symbol.location; + + if(symbol.value.id()==ID_constant) + block_sini.move_to_operands(code); + else + block_dini.move_to_operands(code); + } + + // Make it nil because we do not want + // global_init to try to initialize the + // object + symbol.value.make_nil(); + } + else + { + exprt::operandst ops; + + codet call= + cpp_constructor(locationt(), + symexpr, ops); + + if(call.is_not_nil()) + block_dini.move_to_operands(call); + } + } + + block_sini.move_to_operands(block_dini); + + // Create the initialization procedure + symbolt init_symbol; + + init_symbol.name="c::#ini#"+id2string(module); + init_symbol.base_name="#ini#"+id2string(module); + init_symbol.value.swap(block_sini); + init_symbol.mode=current_mode; + init_symbol.module=module; + init_symbol.type=code_typet(); + init_symbol.type.add(ID_return_type)=typet(ID_empty); + init_symbol.type.set("initialization", true); + init_symbol.is_type=false; + init_symbol.is_macro=false; + init_symbol.theorem=true; + + context.move(init_symbol); + + disable_access_control=false; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::do_not_typechecked + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::do_not_typechecked() +{ + bool cont; + + do + { + cont = false; + + Forall_symbols(s_it, context.symbols) + { + symbolt &symbol=s_it->second; + + if(symbol.value.id()=="cpp_not_typechecked" + && symbol.value.get_bool("is_used")) + { + assert(symbol.type.id()==ID_code); + + if(symbol.base_name =="operator=") + { + cpp_declaratort declarator; + declarator.location() = symbol.location; + default_assignop_value( + lookup(symbol.type.get("#member_name")),declarator); + symbol.value.swap(declarator.value()); + convert_function(symbol); + cont = true; + } + else if(symbol.value.operands().size() == 1) + { + exprt tmp = symbol.value.operands()[0]; + symbol.value.swap(tmp); + convert_function(symbol); + cont = true; + } + else + assert(0); // Don't know what to do! + } + } + } + while(cont); + + Forall_symbols(s_it, context.symbols) + { + symbolt &symbol=s_it->second; + if(symbol.value.id()=="cpp_not_typechecked") + symbol.value.make_nil(); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::clean_up + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::clean_up() +{ + contextt::symbolst::iterator it=context.symbols.begin(); + + while(it!=context.symbols.end()) + { + contextt::symbolst::iterator cur_it = it; + it++; + + symbolt &symbol = cur_it->second; + + if(symbol.type.get_bool(ID_is_template)) + { + context.symbols.erase(cur_it); + continue; + } + else if(symbol.type.id()==ID_struct || + symbol.type.id()==ID_union) + { + struct_union_typet &struct_union_type= + to_struct_union_type(symbol.type); + + const struct_union_typet::componentst &components= + struct_union_type.components(); + + struct_union_typet::componentst data_members; + data_members.reserve(components.size()); + + struct_union_typet::componentst &function_members= + (struct_union_typet::componentst &) + (struct_union_type.add("methods").get_sub()); + + function_members.reserve(components.size()); + + for(struct_typet::componentst::const_iterator + compo_it=components.begin(); + compo_it!=components.end(); + compo_it++) + { + if(compo_it->get_bool(ID_is_static) || + compo_it->get_bool(ID_is_type)) + { + // skip it + } + else if(compo_it->type().id()==ID_code) + { + function_members.push_back(*compo_it); + } + else + { + data_members.push_back(*compo_it); + } + } + + struct_union_type.components().swap(data_members); + } + } +} diff --git a/src/cpp/cpp_typecheck.h b/src/cpp/cpp_typecheck.h new file mode 100644 index 00000000000..d5519949bb6 --- /dev/null +++ b/src/cpp/cpp_typecheck.h @@ -0,0 +1,595 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_TYPECHECK_H +#define CPROVER_CPP_TYPECHECK_H + +#include + +#include +#include +#include + +#include +#include + +#include + +#include "cpp_parse_tree.h" +#include "cpp_scopes.h" +#include "cpp_typecheck_resolve.h" +#include "template_map.h" +#include "cpp_member_spec.h" +#include "cpp_template_type.h" +#include "cpp_util.h" + +bool cpp_typecheck( + cpp_parse_treet &cpp_parse_tree, + contextt &context, + const std::string &module, + message_handlert &message_handler); + +bool cpp_typecheck( + exprt &expr, + message_handlert &message_handler, + const namespacet &ns); + +class cpp_typecheckt:public c_typecheck_baset +{ +public: + cpp_typecheckt( + cpp_parse_treet &_cpp_parse_tree, + contextt &_context, + const std::string &_module, + message_handlert &message_handler): + c_typecheck_baset(_context, _module, message_handler), + cpp_parse_tree(_cpp_parse_tree), + template_counter(0), + anon_counter(0), + disable_access_control(false) + { + } + + cpp_typecheckt( + cpp_parse_treet &_cpp_parse_tree, + contextt &_context1, + const contextt &_context2, + const std::string &_module, + message_handlert &message_handler): + c_typecheck_baset(_context1, _context2, + _module, message_handler), + cpp_parse_tree(_cpp_parse_tree), + template_counter(0), + anon_counter(0), + disable_access_control(false) + { + } + + virtual ~cpp_typecheckt() { } + + virtual void typecheck(); + + // overload to use C++ syntax + + virtual std::string to_string(const typet &type); + virtual std::string to_string(const exprt &expr); + + friend class cpp_typecheck_resolvet; + friend class cpp_declarator_convertert; + + exprt resolve( + const cpp_namet &cpp_name, + const cpp_typecheck_resolvet::wantt want, + const cpp_typecheck_fargst &fargs, + bool fail_with_exception=true) + { + cpp_typecheck_resolvet cpp_typecheck_resolve(*this); + return cpp_typecheck_resolve.resolve( + cpp_name, want, fargs, fail_with_exception); + } + + virtual void typecheck_expr(exprt &expr); + + bool cpp_is_pod(const typet &type) const; + + codet cpp_constructor( + const locationt &location, + const exprt &object, + const exprt::operandst &operands); + +protected: + //cpp_typecheck_resolvet cpp_typecheck_resolve; + + cpp_scopest cpp_scopes; + + cpp_parse_treet &cpp_parse_tree; + irep_idt current_mode; + + void convert(cpp_linkage_spect &); + void convert(cpp_namespace_spect &); + void convert(cpp_usingt &); + void convert(cpp_itemt &); + void convert(cpp_declarationt &); + void convert(cpp_declaratort &); + + void convert_initializer(symbolt &symbol); + void convert_function(symbolt &symbol); + + void convert_pmop(exprt& expr); + void convert_anonymous_union( + cpp_declarationt &declaration, + codet& new_code); + + void convert_compound_ano_union( + const cpp_declarationt &declaration, + const irep_idt &access, + struct_typet::componentst &components); + + // + // Templates + // + + void check_template_restrictions( + const irept &cpp_name, + const irep_idt &final_identifier, + const typet &final_type); + + void convert_template_declaration(cpp_declarationt &declaration); + + void convert_non_template_declaration(cpp_declarationt &declaration); + + void convert_template_function_or_member_specialization( + cpp_declarationt &declaration); + + void convert_template_class_specialization( + cpp_declarationt &declaration); + + void typecheck_template_class(cpp_declarationt &declaration); + + void typecheck_template_function(cpp_declarationt &declaration); + + std::string template_class_identifier( + const irep_idt &base_name, + const template_typet &template_type, + const cpp_template_args_non_tct &partial_specialization_args); + + std::string template_function_identifier( + const irep_idt &base_name, + const template_typet &template_type, + const typet &function_type); + + cpp_template_args_tct typecheck_template_args( + const locationt &location, + const symbolt &template_symbol, + const cpp_template_args_non_tct &template_args); + + class instantiationt + { + public: + locationt location; + irep_idt identifier; + cpp_template_args_tct full_template_args; + }; + + typedef std::list instantiation_stackt; + instantiation_stackt instantiation_stack; + + void show_instantiation_stack(std::ostream &); + + class instantiation_levelt + { + public: + instantiation_levelt( + instantiation_stackt &_instantiation_stack): + instantiation_stack(_instantiation_stack) + { + instantiation_stack.push_back(instantiationt()); + } + + ~instantiation_levelt() + { + instantiation_stack.pop_back(); + } + + private: + instantiation_stackt &instantiation_stack; + }; + + const symbolt &instantiate_template( + const locationt &location, + const symbolt &symbol, + const cpp_template_args_tct &specialization_template_args, + const cpp_template_args_tct &full_template_args, + const typet &specialization=typet(ID_nil)); + + unsigned template_counter; + unsigned anon_counter; + + template_mapt template_map; + + std::string template_suffix( + const cpp_template_args_tct &template_args); + + void convert_arguments( + const irep_idt &mode, + code_typet &function_type); + + void convert_argument( + const irep_idt &mode, + code_typet::argumentt &argument); + + // + // Misc + // + + void find_constructor( + const typet &dest_type, + exprt &symbol_expr); + + void default_ctor( + const locationt& location, + const irep_idt &base_name, + cpp_declarationt& ctor) const; + + void default_cpctor( + const symbolt&, cpp_declarationt& cpctor) const; + + void default_assignop( + const symbolt& symbol, cpp_declarationt& cpctor); + + void default_assignop_value( + const symbolt& symbol, cpp_declaratort& declarator); + + void default_dtor(const symbolt& symb, cpp_declarationt& dtor); + + codet dtor(const symbolt &symb); + + void check_member_initializers( + const irept &bases, + const struct_typet::componentst &components, + const irept &initializers); + + bool check_component_access( + const struct_union_typet::componentt &component, + const struct_union_typet &struct_union_type); + + void full_member_initialization( + const struct_typet &struct_type, + irept &initializers); + + bool find_cpctor(const symbolt& symbol)const; + bool find_assignop(const symbolt& symbol)const; + bool find_dtor(const symbolt& symbol)const; + + bool find_parent( + const symbolt& symb, + const irep_idt &base_name, + irep_idt &identifier); + + bool get_component( + const locationt& location, + const exprt& object, + const irep_idt& component_name, + exprt& member); + + void new_temporary(const locationt &location, + const typet &, + const exprt::operandst &ops, + exprt &temporary); + + void new_temporary(const locationt &location, + const typet &, + const exprt &op, + exprt &temporary); + + void static_initialization(); + void do_not_typechecked(); + void clean_up(); + + void add_base_components( + const struct_typet &from, + const irep_idt& access, + struct_typet &to, + std::set& bases, + std::set& vbases, + bool is_virtual); + + bool cast_away_constness(const typet &t1, + const typet &t2) const; + + void do_virtual_table(const symbolt& symbol); + + // we need to be able to delay the typechecking + // of function bodies to handle methods with + // bodies in the class definition + struct function_bodyt + { + public: + function_bodyt( + symbolt *_function_symbol, + const template_mapt &_template_map, + const instantiation_stackt &_instantiation_stack): + function_symbol(_function_symbol), + template_map(_template_map), + instantiation_stack(_instantiation_stack) + { + } + + symbolt *function_symbol; + template_mapt template_map; + instantiation_stackt instantiation_stack; + }; + + typedef std::list function_bodiest; + function_bodiest function_bodies; + + void add_function_body(symbolt *_function_symbol) + { + function_bodies.push_back(function_bodyt( + _function_symbol, template_map, instantiation_stack)); + } + + // types + + bool convert_typedef(typet &type); + void typecheck_type(typet &type); + + cpp_scopet &typecheck_template_parameters( + template_typet &type); + + void typecheck_compound_type(typet &type); + void check_array_types(typet &type); + void typecheck_enum_type(typet &type); + + // determine the scope into which a tag goes + // (enums, structs, union, classes) + cpp_scopet &tag_scope( + const irep_idt &_elaborated_base_name, // includes template instance + const irep_idt &_base_name, + bool has_body, + bool tag_only_declaration); + + void typecheck_compound_declarator( + const symbolt &symbol, + const cpp_declarationt &declaration, + cpp_declaratort &declarator, + struct_typet::componentst &components, + const irep_idt &access, + bool is_static, + bool is_typedef, + bool is_mutable); + + void typecheck_friend_declaration( + symbolt &symbol, + cpp_declarationt &cpp_declaration); + + void put_compound_into_scope(const struct_union_typet::componentt &component); + void typecheck_compound_body(symbolt &symbol); + void typecheck_enum_body(symbolt &symbol); + void typecheck_function_bodies(); + void typecheck_compound_bases(struct_typet &type); + + void move_member_initializers( + irept &initializers, + const typet &type, + exprt &value); + + void typecheck_member_function( + const irep_idt &compound_symbol, + struct_typet::componentt &component, + irept &initializers, + typet &method_qualifier, + exprt &value); + + void adjust_method_type( + const irep_idt &compound_symbol, + typet &method_type, + typet &type); + + // for function overloading + irep_idt function_identifier(const typet &type); + + void zero_initializer( + const exprt &object, + const typet &type, + const locationt &location, + exprt::operandst &ops); + + // code conversion + virtual void typecheck_code(codet &code); + virtual void typecheck_catch(codet &code); + virtual void typecheck_member_initializer(codet &code); + virtual void typecheck_decl(codet &code); + virtual void typecheck_block(codet &code); + + const struct_typet &this_struct_type(); + + codet cpp_destructor( + const locationt &location, + const typet &type, + const exprt &object); + + // expressions + void explicit_typecast_ambiguity(exprt &expr); + void typecheck_expr_main(exprt &expr); + void typecheck_expr_member(exprt &expr); + void typecheck_expr_ptrmember(exprt &expr); + void typecheck_expr_throw(exprt &expr); + void typecheck_function_expr(exprt &expr, + const cpp_typecheck_fargst &fargs); + void typecheck_expr_cpp_name(exprt &expr, + const cpp_typecheck_fargst &fargs); + void typecheck_expr_member(exprt &expr, + const cpp_typecheck_fargst &fargs); + void typecheck_expr_ptrmember(exprt &expr, + const cpp_typecheck_fargst &fargs); + void typecheck_cast_expr(exprt &expr); + void typecheck_expr_trinary(if_exprt &expr); + void typecheck_expr_binary_arithmetic(exprt &expr); + void typecheck_expr_explicit_typecast(exprt &expr); + void typecheck_expr_explicit_constructor_call(exprt &expr); + void typecheck_expr_address_of(exprt &expr); + void typecheck_expr_dereference(exprt &expr); + void typecheck_expr_function_identifier(exprt &expr); + void typecheck_expr_reference_to(exprt &expr); + void typecheck_expr_this(exprt &expr); + void typecheck_expr_new(exprt &expr); + void typecheck_expr_sizeof(exprt &expr); + void typecheck_expr_delete(exprt &expr); + void typecheck_expr_side_effect(side_effect_exprt &expr); + void typecheck_side_effect_assignment(exprt &expr); + void typecheck_side_effect_inc_dec(side_effect_exprt &expr); + void typecheck_expr_typecast(exprt &expr); + void typecheck_expr_index(exprt& expr); + void typecheck_expr_rel(exprt& expr); + void typecheck_expr_comma(exprt &expr); + + void typecheck_function_call_arguments( + side_effect_expr_function_callt &expr); + + bool operator_is_overloaded(exprt &expr); + bool overloadable(const exprt &expr); + + void add_implicit_dereference(exprt &expr); + + void typecheck_side_effect_function_call( + side_effect_expr_function_callt &expr); + + void typecheck_method_application( + side_effect_expr_function_callt &expr); + void function_call_add_this( + side_effect_expr_function_callt &expr); + + void typecheck_assign(codet &code); + + #ifdef CPP_SYSTEMC_EXTENSION + void typecheck_expr_sc_index(exprt &expr); + void typecheck_expr_sc_member(exprt &expr, + const cpp_typecheck_fargst &fargs); + #endif + + public: + // + // Type Conversions + // + + bool standard_conversion_lvalue_to_rvalue( + const exprt &expr, exprt &new_expr) const; + + bool standard_conversion_array_to_pointer( + const exprt &expr, exprt &new_expr) const; + + bool standard_conversion_function_to_pointer( + const exprt &expr, exprt &new_expr) const; + + bool standard_conversion_qualification( + const exprt &expr, const typet&, exprt &new_expr) const; + + bool standard_conversion_integral_promotion( + const exprt &expr, exprt &new_expr) const; + + bool standard_conversion_floating_point_promotion( + const exprt &expr, exprt &new_expr) const; + + bool standard_conversion_integral_conversion( + const exprt &expr, const typet &type, exprt &new_expr) const; + + bool standard_conversion_floating_integral_conversion( + const exprt &expr, const typet &type, exprt &new_expr) const; + + bool standard_conversion_floating_point_conversion( + const exprt &expr, const typet &type, exprt &new_expr) const; + + bool standard_conversion_pointer( + const exprt &expr, const typet &type, exprt &new_expr); + + bool standard_conversion_pointer_to_member( + const exprt &expr, const typet &type, exprt &new_expr); + + bool standard_conversion_boolean( + const exprt &expr, exprt &new_expr) const; + + #ifdef CPP_SYSTEMC_EXTENSION + bool standard_conversion_verilogbv( + const exprt &expr, const typet &type, exprt &new_expr) const; + #endif + + bool standard_conversion_sequence( + const exprt &expr, const typet &type, exprt &new_expr, unsigned &rank); + + bool user_defined_conversion_sequence( + const exprt &expr, const typet &type, exprt &new_expr, unsigned &rank); + + bool reference_related( + const exprt &expr, const typet &type) const; + + bool reference_compatible( + const exprt &expr, const typet &type, unsigned &rank) const; + + bool reference_binding( + exprt expr, const typet &type, exprt &new_expr, unsigned &rank); + + bool implicit_conversion_sequence( + const exprt &expr, const typet &type, exprt &new_expr, unsigned &rank); + + bool implicit_conversion_sequence( + const exprt &expr, const typet &type, unsigned &rank); + + bool implicit_conversion_sequence( + const exprt &expr, const typet &type, exprt &new_expr); + + void reference_initializer(exprt &expr, const typet &type); + + virtual void implicit_typecast(exprt &expr, const typet &type); + + void get_bases(const struct_typet &type, + std::set &set_bases) const; + + void get_virtual_bases(const struct_typet &type, + std::list &vbases) const; + + bool subtype_typecast( + const struct_typet &from, + const struct_typet &to) const; + + void make_ptr_typecast( + exprt &expr, + const typet & dest_type); + + // the C++ typecasts + + bool const_typecast( + const exprt &expr, + const typet &type, + exprt &new_expr); + + bool dynamic_typecast( + const exprt &expr, + const typet &type, + exprt &new_expr); + + bool reinterpret_typecast( + const exprt &expr, + const typet &type, + exprt &new_expr, + bool check_constantness=true); + + bool static_typecast( + const exprt &expr, + const typet &type, + exprt &new_expr, + bool check_constantness=true); + +private: + std::list dinis; // Static Default-Initialization List + bool disable_access_control; // Disable protect and private +}; + +std::string cpp_identifier_prefix(const irep_idt &mode); + +#endif diff --git a/src/cpp/cpp_typecheck_bases.cpp b/src/cpp/cpp_typecheck_bases.cpp new file mode 100644 index 00000000000..bb9047b942e --- /dev/null +++ b/src/cpp/cpp_typecheck_bases.cpp @@ -0,0 +1,231 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::typcheck_compound_bases + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_compound_bases(struct_typet &type) +{ + std::set bases; + std::set vbases; + + irep_idt default_class_access= + type.get_bool(ID_C_class)?ID_private:ID_public; + + irept::subt &bases_irep=type.add(ID_bases).get_sub(); + + Forall_irep(base_it, bases_irep) + { + const cpp_namet &name= + to_cpp_name(base_it->find(ID_name)); + + exprt base_symbol_expr= + resolve( + name, + cpp_typecheck_resolvet::TYPE, + cpp_typecheck_fargst()); + + if(base_symbol_expr.id()!=ID_type || + base_symbol_expr.type().id()!=ID_symbol) + { + err_location(name.location()); + str << "expected type as struct/class base"; + throw 0; + } + + const symbolt &base_symbol= + lookup(base_symbol_expr.type()); + + if(base_symbol.type.id()==ID_incomplete_struct) + { + err_location(name.location()); + str << "base type is incomplete"; + throw 0; + } + else if(base_symbol.type.id()!=ID_struct) + { + err_location(name.location()); + str << "expected struct or class as base, but got `" + << to_string(base_symbol.type) << "'"; + throw 0; + } + + bool virtual_base = base_it->get_bool(ID_virtual); + irep_idt class_access = base_it->get(ID_protection); + + if(class_access==irep_idt()) + class_access = default_class_access; + + base_symbol_expr.id(ID_base); + base_symbol_expr.set(ID_access, class_access); + + if(virtual_base) + base_symbol_expr.set(ID_virtual, true); + + base_it->swap(base_symbol_expr); + + // Add base scopes as parents to the current scope + cpp_scopes.current_scope().add_secondary_scope( + static_cast(*cpp_scopes.id_map[base_symbol.name])); + + const struct_typet &base_struct_type= + to_struct_type(base_symbol.type); + + add_base_components( + base_struct_type, + class_access, + type, + bases, + vbases, + virtual_base); + } + + if(!vbases.empty()) + { + // add a flag to determine + // if this is the most-derived-object + struct_typet::componentt most_derived; + + most_derived.type()=bool_typet(); + most_derived.set_access(ID_public); + most_derived.set(ID_base_name, "@most_derived"); + most_derived.set_name(cpp_identifier_prefix(current_mode)+"::"+ + cpp_scopes.current_scope().prefix+"::"+"@most_derived"); + most_derived.set(ID_pretty_name, "@most_derived"); + most_derived.location()=type.location(); + put_compound_into_scope(most_derived); + + to_struct_type(type).components().push_back(most_derived); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::add_base_components + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::add_base_components( + const struct_typet &from, + const irep_idt &access, + struct_typet &to, + std::set &bases, + std::set &vbases, + bool is_virtual) +{ + const irep_idt &from_name = from.get(ID_name); + + if(is_virtual && vbases.find(from_name)!=vbases.end()) + return; + + if(bases.find(from_name)!=bases.end()) + { + err_location(to); + str << "error: non-virtual base class " << from_name + << " inherited multiple times"; + throw 0; + } + + bases.insert(from_name); + + if(is_virtual) + vbases.insert(from_name); + + // look at the the parents of the base type + forall_irep(it, from.find(ID_bases).get_sub()) + { + irep_idt sub_access=it->get(ID_access); + + if(access==ID_private) + sub_access=ID_private; + else if(access==ID_protected && sub_access!=ID_private) + sub_access=ID_protected; + + const symbolt &symb= + lookup(it->find(ID_type).get(ID_identifier)); + + bool is_virtual=it->get_bool(ID_virtual); + + // recursive call + add_base_components( + to_struct_type(symb.type), + sub_access, + to, + bases, + vbases, + is_virtual); + } + + // add the components + const struct_typet::componentst &src_c=from.components(); + struct_typet::componentst &dest_c=to.components(); + + for(struct_typet::componentst::const_iterator + it=src_c.begin(); + it!=src_c.end(); + it++) + { + if(it->get_bool(ID_from_base)) + continue; + + // copy the component + dest_c.push_back(*it); + + // now twiddle the copy + struct_typet::componentt &component=dest_c.back(); + component.set(ID_from_base, true); + + irep_idt comp_access=component.get_access(); + + if(access==ID_public) + { + if(comp_access==ID_private) + component.set_access("noaccess"); + } + else if(access == ID_protected) + { + if(comp_access==ID_private) + component.set_access("noaccess"); + else + component.set_access(ID_private); + } + else if(access == ID_private) + { + if(comp_access == "noaccess" || comp_access == ID_private) + component.set_access("noaccess"); + else + component.set_access(ID_private); + } + else + assert(false); + + // put into scope + + } +} + + diff --git a/src/cpp/cpp_typecheck_code.cpp b/src/cpp/cpp_typecheck_code.cpp new file mode 100644 index 00000000000..8202f25c10d --- /dev/null +++ b/src/cpp/cpp_typecheck_code.cpp @@ -0,0 +1,404 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include + +#include "cpp_typecheck.h" +#include "cpp_convert_type.h" +#include "cpp_declarator_converter.h" +#include "cpp_template_type.h" +#include "cpp_util.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_code(codet &code) +{ + const irep_idt &statement=code.get(ID_statement); + + if(statement==ID_catch) + { + code.type()=code_typet(); + typecheck_catch(code); + } + else if(statement==ID_member_initializer) + { + code.type()=code_typet(); + typecheck_member_initializer(code); + } + else + c_typecheck_baset::typecheck_code(code); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_catch + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_catch(codet &code) +{ +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_member_initializer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_member_initializer(codet &code) +{ + const cpp_namet &member= + to_cpp_name(code.find(ID_member)); + + // Let's first typecheck the operands. + Forall_operands(it, code) + typecheck_expr(*it); + + // The initializer may be a data member (non-type) + // or a parent class (type). + // We ask for VAR only, as we get the parent classes via their + // constructor! + cpp_typecheck_fargst fargs; + fargs.in_use=true; + fargs.operands=code.operands(); + + // We should only really resolve in qualified mode, + // no need to look into the parent. + // Plus, this should happen in class scope, not the scope of + // the constructor because of the constructor arguments. + exprt symbol_expr= + resolve(member, cpp_typecheck_resolvet::VAR, fargs); + + if(symbol_expr.type().id()==ID_code) + { + const code_typet &code_type=to_code_type(symbol_expr.type()); + + assert(code_type.arguments().size()>=1); + + // It's a parent. Call the constructor that we got. + side_effect_expr_function_callt function_call; + + function_call.function()=symbol_expr; + function_call.location()=code.location(); + function_call.arguments().reserve(code.operands().size()+1); + + // we have to add 'this' + exprt this_expr = cpp_scopes.current_scope().this_expr; + assert(this_expr.is_not_nil()); + + make_ptr_typecast( + this_expr, + code_type.arguments().front().type()); + + function_call.arguments().push_back(this_expr); + + forall_operands(it, code) + function_call.arguments().push_back(*it); + + // done building the expression, check the argument types + typecheck_function_call_arguments(function_call); + + if(symbol_expr.get_bool("#not_accessible")) + { + irep_idt access = symbol_expr.get(ID_C_access); + + assert(access==ID_private || + access==ID_protected || + access=="noaccess"); + + if(access==ID_private || access=="noaccess") + { + #if 0 + err_location(code.location()); + str << "error: constructor of `" + << to_string(symbol_expr) + << "' is not accessible"; + throw 0; + #endif + } + } + + code_expressiont code_expression; + code_expression.expression()=function_call; + + code.swap(code_expression); + } + else + { + // a reference member + if(symbol_expr.id() == ID_dereference && + symbol_expr.op0().id() == ID_member && + symbol_expr.get_bool(ID_C_implicit) == true) + { + // treat references as normal pointers + exprt tmp = symbol_expr.op0(); + symbol_expr.swap(tmp); + } + + if(symbol_expr.id() == ID_symbol && + symbol_expr.type().id()!=ID_code) + { + // maybe the name of the member collides with a parameter of the constructor + symbol_expr.make_nil(); + cpp_typecheck_fargst fargs; + exprt dereference(ID_dereference, cpp_scopes.current_scope().this_expr.type().subtype()); + dereference.copy_to_operands(cpp_scopes.current_scope().this_expr); + fargs.add_object(dereference); + + { + cpp_save_scopet cpp_saved_scope(cpp_scopes); + cpp_scopes.go_to(*(cpp_scopes.id_map[cpp_scopes.current_scope().class_identifier])); + symbol_expr=resolve(member, cpp_typecheck_resolvet::VAR, fargs); + } + + if(symbol_expr.id() == ID_dereference && + symbol_expr.op0().id() == ID_member && + symbol_expr.get_bool(ID_C_implicit) == true) + { + // treat references as normal pointers + exprt tmp = symbol_expr.op0(); + symbol_expr.swap(tmp); + } + } + + if(symbol_expr.id() == ID_member && + symbol_expr.op0().id() == ID_dereference && + symbol_expr.op0().op0() == cpp_scopes.current_scope().this_expr) + { + if(is_reference(symbol_expr.type())) + { + // it's a reference member + if(code.operands().size()!= 1) + { + err_location(code); + str << " reference `"+to_string(symbol_expr)+"' expects one initializer"; + throw 0; + } + + reference_initializer(code.op0(), symbol_expr.type()); + + // assign the pointers + symbol_expr.type().remove("#reference"); + symbol_expr.set("#lvalue", true); + code.op0().type().remove("#reference"); + + side_effect_exprt assign(ID_assign); + assign.location() = code.location(); + assign.copy_to_operands(symbol_expr, code.op0()); + typecheck_side_effect_assignment(assign); + code_expressiont new_code; + new_code.expression()=assign; + code.swap(new_code); + } + else + { + // it's a data member + already_typechecked(symbol_expr); + + Forall_operands(it, code) + already_typechecked(*it); + + exprt call= + cpp_constructor(code.location(), symbol_expr, code.operands()); + + if(call.is_nil()) + { + call=codet(ID_skip); + call.location()=code.location(); + } + + code.swap(call); + } + } + else + { + err_location(code); + str << "invalid member initializer `" << to_string(symbol_expr) << "'"; + throw 0; + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_decl + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_decl(codet &code) +{ + if(code.operands().size()!=1) + { + err_location(code); + throw "declaration expected to have 1 operand"; + } + + assert(code.op0().id()==ID_cpp_declaration); + + cpp_declarationt &declaration= + to_cpp_declaration(code.op0()); + + bool is_typedef= + convert_typedef(declaration.type()); + + typecheck_type(declaration.type()); + assert(declaration.type().is_not_nil()); + + if(declaration.declarators().empty() && + follow(declaration.type()).get_bool("#is_anonymous")) + { + if(follow(declaration.type()).id()!=ID_union) + { + err_location(code); + throw "declaration statement does not declare anything"; + } + + convert_anonymous_union(declaration, code); + return; + } + + codet new_code(ID_decl_block); + new_code.reserve_operands(declaration.declarators().size()); + + // do the declarators (optional) + Forall_cpp_declarators(it, declaration) + { + cpp_declaratort &declarator = *it; + cpp_declarator_convertert cpp_declarator_converter(*this); + + cpp_declarator_converter.is_typedef=is_typedef; + + const symbolt &symbol= + cpp_declarator_converter.convert(declaration, declarator); + + if(is_typedef) continue; + + codet decl_statement(ID_decl); + decl_statement.reserve_operands(2); + decl_statement.location()=symbol.location; + decl_statement.copy_to_operands(cpp_symbol_expr(symbol)); + + // do we have an initializer? + // and please, it's not code? + if(symbol.value.is_not_nil()) + { + if(symbol.value.id()!=ID_code) + { + decl_statement.copy_to_operands(symbol.value); + assert(follow(decl_statement.op1().type())==follow(symbol.type)); + } + } + + new_code.move_to_operands(decl_statement); + + // is there a constructor to be called? + if(symbol.value.is_not_nil()) + { + assert(it->find("init_args").is_nil()); + if(symbol.value.id()==ID_code) + new_code.copy_to_operands(symbol.value); + } + else + { + exprt object_expr=cpp_symbol_expr(symbol); + + already_typechecked(object_expr); + + exprt constructor_call= + cpp_constructor( + symbol.location, + object_expr, + declarator.init_args().operands()); + + if(constructor_call.is_not_nil()) + new_code.move_to_operands(constructor_call); + } + } + + code.swap(new_code); +} + +/*******************************************************************\ + +Function: cpp_typecheck_codet::typecheck_block + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_block(codet &code) +{ + cpp_save_scopet saved_scope(cpp_scopes); + cpp_scopes.new_block_scope(); + + c_typecheck_baset::typecheck_block(code); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_assign(codet &code) +{ + + if(code.operands().size()!=2) + throw "assignment statement expected to have two operands"; + + // turn into a sideeffect + side_effect_exprt expr(code.get(ID_statement)); + expr.operands() = code.operands(); + typecheck_expr(expr); + + code_expressiont code_expr; + code_expr.expression()=expr; + code_expr.location() = code.location(); + + code.swap(code_expr); +} diff --git a/src/cpp/cpp_typecheck_compound_type.cpp b/src/cpp/cpp_typecheck_compound_type.cpp new file mode 100644 index 00000000000..cadd93f128d --- /dev/null +++ b/src/cpp/cpp_typecheck_compound_type.cpp @@ -0,0 +1,1758 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include "cpp_type2name.h" +#include "cpp_declarator_converter.h" +#include "cpp_typecheck.h" +#include "cpp_convert_type.h" +#include "cpp_name.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::tag_scope + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +cpp_scopet &cpp_typecheckt::tag_scope( + const irep_idt &elaborated_base_name, + const irep_idt &base_name, + bool has_body, + bool tag_only_declaration) +{ + // The scope of a compound identifier is difficult, + // and is different from C. + // + // For instance: + // class A { class B {} } --> A::B + // class A { class B; } --> A::B + // class A { class B *p; } --> ::B + // class B { }; class A { class B *p; } --> ::B + // class B { }; class A { class B; class B *p; } --> A::B + + // If there is a body, or it's a tag-only declaration, + // it's always in the current scope, even if we already have + // it in an upwards scope. + + if(has_body || tag_only_declaration) + return cpp_scopes.current_scope(); + + // No body. Not a tag-only-declaration. + // Check if we have it already. If so, take it. + + // we should only look for tags, but we don't + cpp_scopet::id_sett id_set; + cpp_scopes.current_scope().lookup(base_name, cpp_scopet::RECURSIVE, id_set); + + for(cpp_scopet::id_sett::const_iterator it=id_set.begin(); + it!=id_set.end(); + it++) + if((*it)->is_class()) + return static_cast((*it)->get_parent()); + + // Tags without body that we don't have already + // and that are not a tag-only declaration go into + // the global scope of the namespace. + return cpp_scopes.get_global_scope(); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_compound_type + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_compound_type(typet &type) +{ + assert(type.id()==ID_struct || + type.id()==ID_union); + + // first save qualifiers + c_qualifierst qualifiers(type); + + // now clear them from the type + type.remove(ID_C_constant); + type.remove(ID_C_volatile); + type.remove(ID_C_restricted); + + // get the tag name + + bool anonymous=type.find(ID_tag).is_nil(); + + std::string identifier, base_name; + + if(anonymous) + { + base_name=identifier="#anon"+i2string(anon_counter++); + type.set("#is_anonymous", true); + } + else + { + const cpp_namet &cpp_name= + to_cpp_name(type.find(ID_tag)); + + cpp_name.convert(identifier, base_name); + + if(identifier!=base_name) + { + err_location(cpp_name.location()); + throw "no namespaces allowed in compound names"; + } + } + + bool has_body=type.find(ID_body).is_not_nil(); + bool tag_only_declaration=type.get_bool(ID_C_tag_only_declaration); + + cpp_scopet &dest_scope= + tag_scope(identifier, base_name, has_body, tag_only_declaration); + + const irep_idt symbol_name= + cpp_identifier_prefix(current_mode)+"::"+ + dest_scope.prefix+ + "tag."+identifier; + + // check if we have it already + + contextt::symbolst::iterator previous_symbol= + context.symbols.find(symbol_name); + + if(previous_symbol!=context.symbols.end()) + { + // we do! + + symbolt &symbol=previous_symbol->second; + + if(has_body) + { + if(symbol.type.id()=="incomplete_"+type.id_string()) + { + // a previously incomplete struct/union becomes complete + symbol.type.swap(type); + typecheck_compound_body(symbol); + } + else + { + err_location(type.location()); + str << "error: struct symbol `" << base_name + << "' declared previously" << std::endl; + str << "location of previous definition: " + << symbol.location; + throw 0; + } + } + } + else + { + // produce new symbol + symbolt symbol; + + symbol.name=symbol_name; + symbol.base_name=base_name; + symbol.value.make_nil(); + symbol.location=type.location(); + symbol.mode=current_mode; + symbol.module=module; + symbol.type.swap(type); + symbol.is_type=true; + symbol.is_macro=false; + symbol.pretty_name=cpp_scopes.current_scope().prefix+id2string(symbol.base_name); + symbol.type.set(ID_tag, symbol.pretty_name); + + // move early, must be visible before doing body + symbolt *new_symbol; + + if(context.move(symbol, new_symbol)) + throw "cpp_typecheckt::typecheck_compound_type: context.move() failed"; + + // put into dest_scope + cpp_idt &id=cpp_scopes.put_into_scope(*new_symbol, dest_scope); + + id.id_class=cpp_idt::CLASS; + id.is_scope=true; + id.prefix=cpp_scopes.current_scope().prefix+ + id2string(new_symbol->base_name)+"::"; + id.class_identifier=new_symbol->name; + id.id_class=cpp_idt::CLASS; + + if(has_body) + typecheck_compound_body(*new_symbol); + else + { + typet new_type("incomplete_"+new_symbol->type.id_string()); + new_type.set(ID_tag, new_symbol->base_name); + new_symbol->type.swap(new_type); + } + } + + // create type symbol + typet symbol_type(ID_symbol); + symbol_type.set(ID_identifier, symbol_name); + qualifiers.write(symbol_type); + type.swap(symbol_type); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_compound_declarator + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_compound_declarator( + const symbolt &symbol, + const cpp_declarationt &declaration, + cpp_declaratort &declarator, + struct_typet::componentst &components, + const irep_idt &access, + bool is_static, + bool is_typedef, + bool is_mutable) +{ + bool is_cast_operator= + declaration.type().id()=="cpp-cast-operator"; + + if(is_cast_operator) + { + assert(declarator.name().get_sub().size()==2 && + declarator.name().get_sub().front().id()==ID_operator); + + typet type=static_cast(declarator.name().get_sub()[1]); + declarator.type().subtype()=type; + + irept name(ID_name); + name.set(ID_identifier, "("+cpp_type2name(type)+")"); + declarator.name().get_sub().back().swap(name); + } + + typet final_type= + declarator.merge_type(declaration.type()); + + cpp_namet cpp_name; + cpp_name.swap(declarator.name()); + + typecheck_type(final_type); + + std::string full_name, base_name; + cpp_name.convert(full_name, base_name); + + bool is_method=!is_typedef && final_type.id()==ID_code; + bool is_constructor=declaration.is_constructor(); + bool is_destructor=declaration.is_destructor(); + bool is_virtual=declaration.member_spec().is_virtual(); + bool is_explicit=declaration.member_spec().is_explicit(); + bool is_inline=declaration.member_spec().is_inline(); + + final_type.set("#member_name", symbol.name); + + // first do some sanity checks + + if(is_virtual && !is_method) + { + err_location(cpp_name.location()); + str << "only methods can be virtual"; + throw 0; + } + + if(is_inline && !is_method) + { + err_location(cpp_name.location()); + str << "only methods can be inlined"; + throw 0; + } + + if(is_virtual && is_static) + { + err_location(cpp_name.location()); + str << "static methods cannot be virtual"; + throw 0; + } + + if(is_cast_operator && is_static) + { + err_location(cpp_name.location()); + str << "cast operators cannot be static`"; + throw 0; + } + + if(is_constructor && is_virtual) + { + err_location(cpp_name.location()); + str << "constructors cannot be virtual"; + throw 0; + } + + if(!is_constructor && is_explicit) + { + err_location(cpp_name.location()); + str << "only constructors can be explicit"; + throw 0; + } + + if(is_constructor && + base_name!=id2string(symbol.base_name)) + { + err_location(cpp_name.location()); + str << "member function must return a value or void"; + throw 0; + } + + if(is_destructor && + base_name!="~"+id2string(symbol.base_name)) + { + err_location(cpp_name.location()); + str << "destructor with wrong name"; + throw 0; + } + + // now do actual work + + struct_typet::componentt component; + + irep_idt identifier= + cpp_identifier_prefix(current_mode)+"::"+ + cpp_scopes.current_scope().prefix+ + base_name; + + component.set(ID_name, identifier); + component.type()=final_type; + component.set(ID_access, access); + component.set(ID_base_name, base_name); + component.set(ID_pretty_name, base_name); + component.location() = cpp_name.location(); + + if(cpp_name.is_operator()) + { + component.set("is_operator", true); + component.type().set("#is_operator", true); + } + + if(is_cast_operator) + component.set("is_cast_operator", true); + + if(declaration.member_spec().is_explicit()) + component.set("is_explicit", true); + + typet &method_qualifier= + (typet &)declarator.add("method_qualifier"); + + if(is_static) + { + component.set(ID_is_static, true); + component.type().set("#is_static", true); + } + + if(is_typedef) + component.set("is_type", true); + + if(is_mutable) + component.set("is_mutable", true); + + exprt &value=declarator.value(); + irept &initializers=declarator.member_initializers(); + + if(is_method) + { + component.set(ID_is_inline, declaration.member_spec().is_inline()); + + // the 'virtual' name of the function + std::string virtual_name= + component.get_string(ID_base_name)+ + id2string( + function_identifier(static_cast(component.find(ID_type)))); + + if(method_qualifier.id()==ID_const) + virtual_name += "$const"; + + if(component.type().get(ID_return_type) == ID_destructor) + virtual_name= "@dtor"; + + // The method may be virtual implicitly. + std::set virtual_bases; + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it->get_bool("is_virtual")) + { + if(it->get("virtual_name")==virtual_name) + { + is_virtual=true; + const code_typet& code_type = to_code_type(it->type()); + assert(code_type.arguments().size()>0); + const typet& pointer_type = code_type.arguments()[0].type(); + assert(pointer_type.id() == ID_pointer); + virtual_bases.insert(pointer_type.subtype().get(ID_identifier)); + } + } + } + + if(!is_virtual) + { + typecheck_member_function( + symbol.name, component, initializers, + method_qualifier, value); + + if(!value.is_nil() && !is_static) + { + err_location(cpp_name.location()); + str << "no initialization allowed here"; + throw 0; + } + } + else // virtual + { + component.type().set("#is_virtual", true); + component.type().set("#virtual_name",virtual_name); + + // Check if it is a pure virtual method + if(is_virtual) + { + if(value.is_not_nil() && value.id() == ID_constant) + { + mp_integer i; + to_integer(value, i); + if(i!=0) + { + err_location(declarator.name().location()); + str << "expected 0 to mark pure virtual method, got " << i; + } + component.set("is_pure_virtual", true); + value.make_nil(); + } + } + + typecheck_member_function( + symbol.name, + component, + initializers, + method_qualifier, + value); + + // get the virtual-table symbol type + irep_idt vt_name = "virtual_table::"+symbol.name.as_string(); + + contextt::symbolst::iterator vtit = + context.symbols.find(vt_name); + + if(vtit == context.symbols.end()) + { + // first time: create a virtual-table symbol type + symbolt vt_symb_type; + vt_symb_type.name= vt_name; + vt_symb_type.base_name="virtual_table::"+symbol.base_name.as_string(); + vt_symb_type.pretty_name = vt_symb_type.base_name; + vt_symb_type.mode=current_mode; + vt_symb_type.module=module; + vt_symb_type.location=symbol.location; + vt_symb_type.type = struct_typet(); + vt_symb_type.type.set(ID_name, vt_symb_type.name); + vt_symb_type.is_type = true; + + bool failed = context.move(vt_symb_type); + assert(!failed); + vtit = context.symbols.find(vt_name); + + // add a virtual-table pointer + struct_typet::componentt compo; + compo.type() = pointer_typet(symbol_typet(vt_name)); + compo.set_name(symbol.name.as_string() +"::@vtable_pointer"); + compo.set(ID_base_name, "@vtable_pointer"); + compo.set(ID_pretty_name, symbol.base_name.as_string() +"@vtable_pointer"); + compo.set("is_vtptr", true); + compo.set(ID_access, ID_public); + components.push_back(compo); + put_compound_into_scope(compo); + } + + assert(vtit->second.type.id()==ID_struct); + + struct_typet &virtual_table= + to_struct_type(vtit->second.type); + + component.set("virtual_name", virtual_name); + component.set("is_virtual", is_virtual); + + // add an entry to the virtual table + struct_typet::componentt vt_entry; + vt_entry.type() = pointer_typet(component.type()); + vt_entry.set_name(vtit->first.as_string()+"::"+virtual_name); + vt_entry.set(ID_base_name, virtual_name); + vt_entry.set(ID_pretty_name, virtual_name); + vt_entry.set(ID_access, ID_public); + vt_entry.location() = symbol.location; + virtual_table.components().push_back(vt_entry); + + // take care of overloading + while(!virtual_bases.empty()) + { + irep_idt virtual_base = *virtual_bases.begin(); + + // a new function that does 'late casting' of the 'this' parameter + symbolt func_symb; + func_symb.name=component.get_name().as_string() + "::" +virtual_base.as_string(); + func_symb.base_name=component.get(ID_base_name); + func_symb.pretty_name = component.get(ID_base_name); + func_symb.mode=current_mode; + func_symb.module=module; + func_symb.location=component.location(); + func_symb.type=component.type(); + + // change the type of the 'this' pointer + code_typet& code_type = to_code_type(func_symb.type); + code_typet::argumentt& arg= code_type.arguments().front(); + arg.type().subtype().set(ID_identifier, virtual_base); + + // create symbols for the arguments + code_typet::argumentst& args = code_type.arguments(); + for(unsigned i =0; i < args.size(); i++) + { + code_typet::argumentt& arg = args[i]; + irep_idt base_name = arg.get_base_name(); + + if(base_name==irep_idt()) + base_name = "arg" + i2string(i); + + symbolt arg_symb; + arg_symb.name = func_symb.name.as_string() + "::"+ base_name.as_string(); + arg_symb.base_name = base_name; + arg_symb.pretty_name = base_name; + arg_symb.mode=current_mode; + arg_symb.location=func_symb.location; + arg_symb.type = arg.type(); + + arg.set(ID_C_identifier, arg_symb.name); + + // add the argument to the symbol table + bool failed = context.move(arg_symb); + assert(!failed); + } + + // do the body of the function + typecast_exprt late_cast(to_code_type(component.type()).arguments()[0].type()); + + late_cast.op0()= + symbol_expr(namespacet(context).lookup( + args[0].get(ID_C_identifier))); + + if(code_type.return_type().id()!=ID_empty && + code_type.return_type().id()!=ID_destructor) + { + side_effect_expr_function_callt expr_call; + expr_call.function() = symbol_exprt(component.get_name(),component.type()); + expr_call.type() = to_code_type(component.type()).return_type(); + expr_call.arguments().reserve(args.size()); + expr_call.arguments().push_back(late_cast); + + for(unsigned i=1; i < args.size(); i++) + { + expr_call.arguments().push_back( + symbol_expr(namespacet(context).lookup( + args[i].get(ID_C_identifier)))); + } + + code_returnt code_return; + code_return.return_value() = expr_call; + + func_symb.value = code_return; + } + else + { + code_function_callt code_func; + code_func.function() = symbol_exprt(component.get_name(),component.type()); + code_func.arguments().reserve(args.size()); + code_func.arguments().push_back(late_cast); + + for(unsigned i=1; i < args.size(); i++) + { + code_func.arguments().push_back( + symbol_expr(namespacet(context).lookup( + args[i].get(ID_C_identifier)))); + } + + func_symb.value = code_func; + } + + // add this new function to the list of components + + struct_typet::componentt new_compo = component; + new_compo.type() = func_symb.type; + new_compo.set_name(func_symb.name); + components.push_back(new_compo); + + // add the function to the symbol table + { + bool failed = context.move(func_symb); + assert(!failed); + } + + // next base + virtual_bases.erase(virtual_bases.begin()); + } + } + } + + if(is_static && !is_method) // static non-method member + { + // add as global variable to context + symbolt static_symbol; + static_symbol.mode=symbol.mode; + static_symbol.name=identifier; + static_symbol.type=component.type(); + static_symbol.base_name=component.get(ID_base_name); + static_symbol.lvalue=true; + static_symbol.static_lifetime=true; + static_symbol.location = cpp_name.location(); + static_symbol.is_extern = true; + + dinis.push_back(static_symbol.name); + + symbolt * new_symbol; + if(context.move(static_symbol, new_symbol)) + { + err_location(cpp_name.location()); + str << "redeclaration of symbol `" + << static_symbol.base_name.as_string() + <<"'"; + throw 0; + } + + if(value.is_not_nil()) + { + if(cpp_is_pod(new_symbol->type)) + { + new_symbol->value.swap(value); + c_typecheck_baset::do_initializer(*new_symbol); + } + else + { + symbol_exprt symexpr; + symexpr.set_identifier(new_symbol->name); + + exprt::operandst ops; + ops.push_back(value); + codet defcode = + cpp_constructor(locationt(), symexpr, ops); + + new_symbol->value.swap(defcode); + } + } + } + + check_array_types(component.type()); + + put_compound_into_scope(component); + + components.push_back(component); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::check_array_types + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::check_array_types(typet &type) +{ + if(type.id()==ID_array) + { + array_typet &array_type=to_array_type(type); + make_constant_index(array_type.size()); + check_array_types(array_type.subtype()); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::put_compound_into_scope + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::put_compound_into_scope( + const struct_union_typet::componentt &compound) +{ + const irep_idt &base_name=compound.get_base_name(); + const irep_idt &name=compound.get_name(); + + // nothing to do if no base_name (e.g., an anonymous bitfield) + if(base_name==irep_idt()) + return; + + if(compound.type().id()==ID_code) + { + // put the symbol into scope + cpp_idt &id=cpp_scopes.current_scope().insert(base_name); + id.id_class=compound.get_bool("is_type")?cpp_idt::TYPEDEF:cpp_idt::SYMBOL; + id.identifier=name; + id.class_identifier=cpp_scopes.current_scope().identifier; + id.is_member = true; + id.is_constructor = + compound.find(ID_type).get(ID_return_type) == ID_constructor; + id.is_method = true; + id.is_static_member=compound.get_bool(ID_is_static); + + // create function block-scope in the scope + cpp_idt &id_block= + cpp_scopes.current_scope().insert( + irep_idt(std::string("$block:") + base_name.c_str())); + + id_block.id_class=cpp_idt::BLOCK_SCOPE; + id_block.identifier=name; + id_block.class_identifier=cpp_scopes.current_scope().identifier; + id_block.is_method=true; + id_block.is_static_member=compound.get_bool(ID_is_static); + + id_block.is_scope=true; + id_block.prefix=compound.get_string("prefix"); + cpp_scopes.id_map[id.identifier]=&id_block; + } + else + { + // check if it's already there + cpp_scopest::id_sett id_set; + + cpp_scopes.current_scope().lookup(base_name, cpp_scopet::SCOPE_ONLY, id_set); + + for(cpp_scopest::id_sett::const_iterator + id_it=id_set.begin(); + id_it!=id_set.end(); + id_it++) + { + const cpp_idt &id=**id_it; + // the name is already in the scope + // this is ok if they belong to different categories + + if(!id.is_class() && !id.is_enum()) + { + err_location(compound); + str << "`" << base_name + << "' already in compound scope"; + throw 0; + } + } + + // put into the scope + cpp_idt &id=cpp_scopes.current_scope().insert(base_name); + id.id_class=compound.get_bool(ID_is_type)?cpp_idt::TYPEDEF:cpp_idt::SYMBOL; + id.identifier=name; + id.class_identifier=cpp_scopes.current_scope().identifier; + id.is_member=true; + id.is_method=false; + id.is_static_member=compound.get_bool(ID_is_static); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_friend_declaration + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_friend_declaration( + symbolt &symbol, + cpp_declarationt &declaration) +{ + // A friend of a class can be a function/method, + // or a struct/class/union type. + + if(declaration.is_template()) + { + err_location(declaration.location()); + str << "friend template not supported"; + throw 0; + } + + // we distinguish these whether there is a declarator + if(declaration.declarators().empty()) + { + typet &ftype=declaration.type(); + + // must be struct or union + if(ftype.id()!=ID_struct && ftype.id()!=ID_union) + { + err_location(declaration.type()); + str << "unexpected friend"; + throw 0; + } + + if(ftype.find(ID_body).is_not_nil()) + { + err_location(declaration.type()); + str << "friend declaration must not have compound body"; + throw 0; + } + + // typecheck ftype + + // TODO +// typecheck_type(ftype); +// assert(ftype.id()==ID_symbol); +// symbol.type.add("#friends").move_to_sub(ftype); + + return; + } + + // It should be a friend function. + // Do the declarators. + + Forall_cpp_declarators(sub_it, declaration) + { + bool has_value = sub_it->value().is_not_nil(); + + if(!has_value) + { + // If no value is found, then we jump to the + // global scope, and we convert the declarator + // as if it were declared there + cpp_save_scopet saved_scope(cpp_scopes); + cpp_scopes.go_to_global_scope(); + cpp_declarator_convertert cpp_declarator_converter(*this); + const symbolt &conv_symb = cpp_declarator_converter.convert( + declaration.type(), declaration.storage_spec(), + declaration.member_spec(), *sub_it); + exprt symb_expr = cpp_symbol_expr(conv_symb); + symbol.type.add("#friends").move_to_sub(symb_expr); + } + else + { + cpp_declarator_convertert cpp_declarator_converter(*this); + cpp_declarator_converter.is_friend = true; + + declaration.member_spec().set_inline(true); + + const symbolt &conv_symb = cpp_declarator_converter.convert( + declaration.type(), declaration.storage_spec(), + declaration.member_spec(), *sub_it); + + exprt symb_expr = cpp_symbol_expr(conv_symb); + + symbol.type.add("#friends").move_to_sub(symb_expr); + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_compound_body + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_compound_body(symbolt &symbol) +{ + cpp_save_scopet saved_scope(cpp_scopes); + + // enter scope of compound + cpp_scopes.set_scope(symbol.name); + + assert(symbol.type.id()==ID_struct || + symbol.type.id()==ID_union); + + struct_union_typet &type= + to_struct_union_type(symbol.type); + + // pull the base types in + if(!type.find(ID_bases).get_sub().empty()) + { + if(type.id()==ID_union) + { + err_location(symbol.location); + throw "union types must not have bases"; + } + + typecheck_compound_bases(to_struct_type(type)); + } + + exprt &body=(exprt &)type.add(ID_body); + struct_union_typet::componentst &components=type.components(); + + symbol.type.set(ID_name, symbol.name); + + // default access + irep_idt access=type.get_bool(ID_C_class)?ID_private:ID_public; + + bool found_ctor=false; + bool found_dtor=false; + + // we first do everything but the constructors + + Forall_operands(it, body) + { + if(it->id()==ID_cpp_declaration) + { + cpp_declarationt &declaration= + to_cpp_declaration(*it); + + if(declaration.member_spec().is_friend()) + { + typecheck_friend_declaration(symbol, declaration); + continue; // done + } + + if(declaration.is_template()) + { + // remember access mode + declaration.set("#access", access); + convert_template_declaration(declaration); + continue; + } + + if(declaration.type().id()=="") // empty? + continue; + + bool is_typedef= + convert_typedef(declaration.type()); + + // is it tag-only? + if(declaration.type().id()==ID_struct || + declaration.type().id()==ID_union || + declaration.type().id()==ID_c_enum) + if(declaration.declarators().empty()) + declaration.type().set(ID_C_tag_only_declaration, true); + + typecheck_type(declaration.type()); + + bool is_static=declaration.storage_spec().is_static(); + bool is_mutable=declaration.storage_spec().is_mutable(); + + if(declaration.storage_spec().is_extern() || + declaration.storage_spec().is_auto() || + declaration.storage_spec().is_register()) + { + err_location(declaration.storage_spec()); + str << "invalid storage class specified for field"; + throw 0; + } + + typet final_type = follow(declaration.type()); + + if(declaration.declarators().empty() && + final_type.get_bool("#is_anonymous")) + { + if(final_type.id()!=ID_union && + final_type.id()!=ID_struct) + { + err_location(declaration.type()); + throw "declaration in compound does not declare anything"; + } + + convert_compound_ano_union( + declaration, access, components); + + continue; + } + + // declarators + Forall_cpp_declarators(d_it, declaration) + { + cpp_declaratort &declarator=*d_it; + + // Skip the constructors until all the data members + // are discovered + if(declaration.is_destructor()) + found_dtor = true; + + if(declaration.is_constructor()) + { + found_ctor = true; + continue; + } + + typecheck_compound_declarator( + symbol, + declaration, declarator, components, + access, is_static, is_typedef, is_mutable); + } + } + else if(it->id()=="cpp-public") + access=ID_public; + else if(it->id()=="cpp-private") + access=ID_private; + else if(it->id()=="cpp-protected") + access=ID_protected; + else + { + } + } + + // Add the default dtor, if needed + // (we have to do the destructor before building the virtual tables, + // as the destructor may be virtual!) + + if((found_ctor || !cpp_is_pod(symbol.type)) && !found_dtor) + { + // build declaration + cpp_declarationt dtor; + default_dtor(symbol, dtor); + + typecheck_compound_declarator( + symbol, + dtor, dtor.declarators()[0], components, + ID_public, false, false, false); + } + + // setup virtual tables before doing the constructors + do_virtual_table(symbol); + + if(!found_ctor && !cpp_is_pod(symbol.type)) + { + // it's public! + exprt cpp_public("cpp-public"); + body.move_to_operands(cpp_public); + + // build declaration + cpp_declarationt ctor; + default_ctor(symbol.type.location(), symbol.base_name, ctor); + body.add(ID_operands).move_to_sub(ctor); + } + + // Reset the access type + access=type.get_bool(ID_C_class)?ID_private:ID_public; + + // All the data members are known now. + // So let's deal with the constructors that we are given. + Forall_operands(it, body) + { + if(it->id()==ID_cpp_declaration) + { + cpp_declarationt &declaration= + to_cpp_declaration(*it); + + if(!declaration.is_constructor()) + continue; + + Forall_cpp_declarators(d_it, declaration) + { + cpp_declaratort &declarator=*d_it; + + std::string ctor_full_name, ctor_base_name; + declarator.name().convert(ctor_full_name, ctor_base_name); + + if(declarator.value().is_not_nil()) + { + if(declarator.find(ID_member_initializers).is_nil()) + declarator.set(ID_member_initializers, ID_member_initializers); + + check_member_initializers( + type.add(ID_bases), + type.components(), + declarator.member_initializers() + ); + + full_member_initialization( + to_struct_type(type), + declarator.member_initializers() + ); + } + + // Finally, we typecheck the constructor with the + // full member-initialization list + bool is_static=declaration.storage_spec().is_static(); // Shall be false + bool is_mutable=declaration.storage_spec().is_mutable(); // Shall be false + bool is_typedef=convert_typedef(declaration.type()); // Shall be false + + typecheck_compound_declarator( + symbol, + declaration, declarator, components, + access, is_static, is_typedef, is_mutable); + } + } + else if(it->id()=="cpp-public") + access=ID_public; + else if(it->id()=="cpp-private") + access=ID_private; + else if(it->id()=="cpp-protected") + access=ID_protected; + else + { + } + } + + if(!cpp_is_pod(symbol.type)) + { + // Add the default copy constructor + struct_typet::componentt component; + + if(!find_cpctor(symbol)) + { + // build declaration + cpp_declarationt cpctor; + default_cpctor(symbol, cpctor); + assert(cpctor.declarators().size()==1); + + exprt value("cpp_not_typechecked"); + value.copy_to_operands(cpctor.declarators()[0].value()); + cpctor.declarators()[0].value() = value; + + typecheck_compound_declarator( + symbol, + cpctor, cpctor.declarators()[0], components, + ID_public, false, false, false); + } + + // Add the default assignment operator + if(!find_assignop(symbol)) + { + // build declaration + cpp_declarationt assignop; + default_assignop(symbol, assignop); + assert(assignop.declarators().size()==1); + + // The value will be typechecked only if the operator + // is actually used + cpp_declaratort declarator; + assignop.declarators().push_back(declarator); + assignop.declarators()[0].value() = exprt("cpp_not_typechecked"); + + typecheck_compound_declarator( + symbol, + assignop, assignop.declarators()[0], components, + ID_public, false, false, false); + } + } + + // clean up! + symbol.type.remove(ID_body); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::move_member_initializers + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::move_member_initializers( + irept &initializers, + const typet &type, + exprt &value) +{ + bool is_constructor= + type.find(ID_return_type).id()==ID_constructor; + + // see if we have initializers + if(!initializers.get_sub().empty()) + { + if(!is_constructor) + { + err_location(initializers); + str << "only constructors are allowed to " + "have member initializers"; + throw 0; + } + + if(value.is_nil()) + { + err_location(initializers); + str << "only constructors with body are allowed to " + "have member initializers"; + throw 0; + } + + to_code(value).make_block(); + + exprt::operandst::iterator o_it=value.operands().begin(); + forall_irep(it, initializers.get_sub()) + { + o_it=value.operands().insert(o_it,(exprt&)*it); + o_it++; + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_member_function + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_member_function( + const irep_idt &compound_symbol, + struct_typet::componentt &component, + irept &initializers, + typet &method_qualifier, + exprt &value) +{ + symbolt symbol; + + typet &type=component.type(); + + if(component.get_bool(ID_is_static)) + { + if(method_qualifier.id()!="") + { + err_location(component); + throw "method is static -- no qualifiers allowed"; + } + } + else + { + adjust_method_type( + compound_symbol, + type, + method_qualifier); + } + + if(value.id()=="cpp_not_typechecked") + move_member_initializers(initializers, type, value.op0()); + else + move_member_initializers(initializers, type, value); + + irep_idt f_id= + function_identifier(component.type()); + + const irep_idt identifier= + id2string(component.get_name())+id2string(f_id); + + component.set_name(identifier); + + component.set("prefix", + cpp_scopes.current_scope().prefix+ + component.get_string(ID_base_name)+ + id2string(f_id)+"::"); + + if(value.is_not_nil()) + type.set(ID_C_inlined, true); + + symbol.name=identifier; + symbol.base_name=component.get(ID_base_name); + symbol.value.swap(value); + symbol.mode=current_mode; + symbol.module=module; + symbol.type=type; + symbol.is_type=false; + symbol.is_macro=false; + symbol.theorem=true; + symbol.location=component.location(); + + // move early, it must be visible before doing any value + symbolt *new_symbol; + + if(context.move(symbol, new_symbol)) + { + err_location(symbol.location); + str << "failed to insert new symbol: " << symbol.name.c_str() << std::endl; + + contextt::symbolst::iterator symb_it = + context.symbols.find(symbol.name); + + if(symb_it != context.symbols.end()) + { + str << "name of previous symbol: " << symb_it->second.name << std::endl; + str << "location of previous symbol: "; + err_location(symb_it->second.location); + } + + throw 0; + } + + // remember for later typechecking of body + add_function_body(new_symbol); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::adjust_method_type + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::adjust_method_type( + const irep_idt &compound_symbol, + typet &type, + typet &method_type) +{ + irept &arguments=type.add(ID_arguments); + + arguments.get_sub().insert(arguments.get_sub().begin(), irept(ID_argument)); + + exprt &argument=(exprt &)arguments.get_sub().front(); + argument.type()=typet(ID_pointer); + + argument.type().subtype()=typet(ID_symbol); + argument.type().subtype().set(ID_identifier, compound_symbol); + + argument.set(ID_C_identifier, ID_this); + argument.set(ID_C_base_name, ID_this); + + if(method_type.id()=="" || method_type.is_nil()) + { + } + else if(method_type.id()==ID_const) + argument.type().subtype().set("#constant", true); + else + { + err_location(method_type); + throw "invalid method qualifier"; + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_compound_ano_union + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_compound_ano_union( + const cpp_declarationt &declaration, + const irep_idt &access, + struct_typet::componentst &components) +{ + symbolt &union_symbol = + context.symbols[follow(declaration.type()).get(ID_name)]; + + if(declaration.storage_spec().is_static() + || declaration.storage_spec().is_mutable()) + { + err_location(union_symbol.type.location()); + throw "storage class is not allowed here"; + } + + // unnamed object + irep_idt base_name = "#anon"+i2string(anon_counter++); + irep_idt identifier= + cpp_identifier_prefix(current_mode)+"::"+ + cpp_scopes.current_scope().prefix+ + base_name.c_str(); + + typet symbol_type(ID_symbol); + symbol_type.set(ID_identifier, union_symbol.name); + + struct_typet::componentt component; + component.set(ID_name, identifier); + component.type() = symbol_type; + component.set(ID_access, access); + component.set(ID_base_name, base_name); + component.set(ID_pretty_name, base_name); + + components.push_back(component); + + if(!cpp_is_pod(union_symbol.type)) + { + err_location(union_symbol.type.location()); + str << "anonymous union is not POD"; + throw 0; + } + + // do scoping + forall_irep(it, union_symbol.type.add(ID_components).get_sub()) + { + if(it->find(ID_type).id()==ID_code) + { + err_location(union_symbol.type.location()); + str << "anonymous union " << union_symbol.base_name + << " shall not have function members"; + throw 0; + } + + const irep_idt& base_name = it->get(ID_base_name); + + if(cpp_scopes.current_scope().contains(base_name)) + { + err_location(union_symbol.type.location()); + str << "`" << base_name << "' already in scope"; + throw 0; + } + + cpp_idt &id=cpp_scopes.current_scope().insert(base_name); + id.id_class = cpp_idt::SYMBOL; + id.identifier=it->get(ID_name); + id.class_identifier=union_symbol.name; + id.is_member=true; + } + + put_compound_into_scope(component); + + union_symbol.type.set("#unnamed_object", base_name); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::get_component + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::get_component( + const locationt &location, + const exprt &object, + const irep_idt &component_name, + exprt &member) +{ + const typet &followed_type=follow(object.type()); + + assert(followed_type.id()==ID_struct || + followed_type.id()==ID_union); + + struct_union_typet final_type= + to_struct_union_type(followed_type); + + const struct_union_typet::componentst &components= + final_type.components(); + + for(struct_union_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + const struct_union_typet::componentt &component = *it; + + exprt tmp(ID_member, component.type()); + tmp.set(ID_component_name, component.get_name()); + tmp.location()=location; + tmp.copy_to_operands(object); + + if(component.get_name()==component_name) + { + member.swap(tmp); + + bool not_ok=check_component_access(component, final_type); + if(not_ok) + { + if(disable_access_control) + { + member.set("#not_accessible", true); + member.set("#access", component.get(ID_access)); + } + else + { + #if 0 + err_location(location); + str << "error: member `" << component_name + << "' is not accessible (" << component.get(ID_access) << ")"; + str << "\nstruct name: " << final_type.get(ID_name); + throw 0; + #endif + } + } + + if(object.get_bool(ID_C_lvalue)) + member.set(ID_C_lvalue, true); + + if(object.type().get_bool(ID_C_constant) && + !component.get_bool("is_mutable")) + member.type().set(ID_C_constant, true); + + member.location() = location; + + return true; // component found + } + else if( + follow(component.type()).find("#unnamed_object").is_not_nil()) + { + // could be anonymous union or struct + + const typet &component_type=follow(component.type()); + + if(component_type.id()==ID_union || + component_type.id()==ID_struct) + { + // recursive call! + if(get_component(location, tmp, component_name, member)) + { + if(check_component_access(component, final_type)) + { + #if 0 + err_location(location); + str << "error: member `" << component_name + << "' is not accessible"; + throw 0; + #endif + } + + if(object.get_bool(ID_C_lvalue)) + member.set(ID_C_lvalue, true); + + if(object.get_bool(ID_C_constant) && + !component.get_bool("is_mutable")) + member.type().set(ID_C_constant, true); + + member.location() = location; + return true; // component found + } + } + } + } + + return false; // component not found +} + +/*******************************************************************\ + +Function: cpp_typecheckt::check_component_access + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::check_component_access( + const struct_union_typet::componentt &component, + const struct_union_typet &struct_union_type) +{ + const irep_idt &access=component.get(ID_access); + + if(access=="noaccess") + return true; // not ok + + if(access == ID_public) + return false; // ok + + assert(access == ID_private || + access == ID_protected); + + const irep_idt &struct_identifier= + struct_union_type.get(ID_name); + + cpp_scopet *pscope = &(cpp_scopes.current_scope()); + while(!(pscope->is_root_scope())) + { + if(pscope->is_class()) + { + if(pscope->identifier == struct_identifier) + return false; // ok + + const struct_typet &scope_struct= + to_struct_type(lookup(pscope->identifier).type); + + if(subtype_typecast( + to_struct_type(struct_union_type), scope_struct)) + return false; // ok + + else break; + } + pscope = &(pscope->get_parent()); + } + + // check friendship + forall_irep(f_it, struct_union_type.find("#friends").get_sub()) + { + const irept& friend_symb = *f_it; + + const cpp_scopet& friend_scope = + cpp_scopes.get_scope(friend_symb.get(ID_identifier)); + + cpp_scopet* pscope = &(cpp_scopes.current_scope()); + + while(!(pscope->is_root_scope())) + { + if(friend_scope.identifier == pscope->identifier) + return false; // ok + + if(pscope->is_class()) + break; + + pscope = &(pscope->get_parent()); + } + } + + return true; //not ok +} + +/*******************************************************************\ + +Function: cpp_typecheckt::get_bases + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::get_bases( + const struct_typet &type, + std::set &set_bases) const +{ + const irept::subt &bases=type.find(ID_bases).get_sub(); + + forall_irep(it, bases) + { + assert(it->id()==ID_base); + assert(it->get(ID_type) == ID_symbol); + + const struct_typet &base= + to_struct_type(lookup(it->find(ID_type).get(ID_identifier)).type); + + set_bases.insert(base.get(ID_name)); + get_bases(base,set_bases); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::get_virtual_bases + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::get_virtual_bases( + const struct_typet& type, + std::list &vbases) const +{ + if(std::find(vbases.begin(), vbases.end(), type.get(ID_name)) != vbases.end()) + return; + + const irept::subt &bases=type.find(ID_bases).get_sub(); + + forall_irep(it, bases) + { + assert(it->id()==ID_base); + assert(it->get(ID_type) == ID_symbol); + + const struct_typet &base= + to_struct_type(lookup(it->find(ID_type).get(ID_identifier)).type); + + if(it->get_bool(ID_virtual)) + vbases.push_back(base.get(ID_name)); + + get_virtual_bases(base,vbases); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::subtype_typecast + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::subtype_typecast( + const struct_typet &from, + const struct_typet &to) const +{ + if(from.get(ID_name)==to.get(ID_name)) + return true; + + std::set bases; + + get_bases(from, bases); + + return bases.find(to.get(ID_name)) != bases.end(); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::make_ptr_subtypecast + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::make_ptr_typecast( + exprt &expr, + const typet & dest_type) +{ + typet src_type = expr.type(); + + assert(src_type.id()== ID_pointer); + assert(dest_type.id()== ID_pointer); + + struct_typet src_struct = + to_struct_type(static_cast(follow(src_type.subtype()))); + + struct_typet dest_struct = + to_struct_type(static_cast(follow(dest_type.subtype()))); + + assert(subtype_typecast(src_struct, dest_struct) || + subtype_typecast(dest_struct, src_struct)); + + expr.make_typecast(dest_type); +} + diff --git a/src/cpp/cpp_typecheck_constructor.cpp b/src/cpp/cpp_typecheck_constructor.cpp new file mode 100644 index 00000000000..22fbc81cb05 --- /dev/null +++ b/src/cpp/cpp_typecheck_constructor.cpp @@ -0,0 +1,1332 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include +#include + +#include + +#include "cpp_typecheck.h" +#include "cpp_util.h" + +/*******************************************************************\ + +Function: copy_parent + + Inputs: + parent_base_name: base name of typechecked parent + block: non-typechecked block + + Outputs: + generate code to copy the parent + + Purpose: + +\*******************************************************************/ + +static void copy_parent( + const locationt &location, + const irep_idt &parent_base_name, + const irep_idt &arg_name, + exprt &block) +{ + block.operands().push_back(codet()); + + codet &code=to_code(block.operands().back()); + code.location()=location; + + code.set_statement(ID_assign); + code.operands().push_back(exprt(ID_dereference)); + + code.op0().operands().push_back(exprt("explicit-typecast")); + + exprt &op0=code.op0().op0(); + + op0.operands().push_back(exprt("cpp-this")); + op0.type().id(ID_pointer); + op0.type().add(ID_subtype).id(ID_cpp_name); + op0.type().add(ID_subtype).get_sub().push_back(irept(ID_name)); + op0.type().add(ID_subtype).get_sub().back().set(ID_identifier, parent_base_name); + op0.type().add(ID_subtype).get_sub().back().set(ID_C_location, location); + op0.location() = location; + + code.operands().push_back(exprt("explicit-typecast")); + exprt &op1 = code.op1(); + + op1.type().id(ID_pointer); + op1.type().set("#reference", true); + op1.type().add(ID_subtype).set("#constant", true); + op1.type().add(ID_subtype).id(ID_cpp_name); + op1.type().add(ID_subtype).get_sub().push_back(irept(ID_name)); + op1.type().add(ID_subtype).get_sub().back().set(ID_identifier, parent_base_name); + op1.type().add(ID_subtype).get_sub().back().set(ID_C_location, location); + + op1.operands().push_back(exprt(ID_cpp_name)); + op1.op0().get_sub().push_back(irept(ID_name)); + op1.op0().get_sub().back().set(ID_identifier, arg_name); + op1.op0().get_sub().back().set(ID_C_location, location); + op1.location() = location; +} + +/*******************************************************************\ + +Function: copy_member + + Inputs: + member_base_name: name of a member + block: non-typechecked block + + Outputs: + generate code to copy the member + + Purpose: + +\*******************************************************************/ + +static void copy_member( + const locationt &location, + const irep_idt &member_base_name, + const irep_idt &arg_name, + exprt &block) +{ + block.operands().push_back(exprt(ID_code)); + exprt &code=block.operands().back(); + + code.set(ID_statement, ID_expression); + code.add(ID_type)=typet(ID_code); + code.operands().push_back(exprt(ID_sideeffect)); + code.op0().set(ID_statement, ID_assign); + code.op0().operands().push_back(exprt(ID_cpp_name)); + code.location() = location; + + exprt& op0 = code.op0().op0(); + op0.location() = location; + + op0.get_sub().push_back(irept(ID_name)); + op0.get_sub().back().set(ID_identifier, member_base_name); + op0.get_sub().back().set(ID_C_location, location); + + code.op0().operands().push_back(exprt(ID_member)); + + exprt& op1 = code.op0().op1(); + + op1.add("component_cpp_name").id(ID_cpp_name); + op1.add("component_cpp_name").get_sub().push_back(irept(ID_name)); + op1.add("component_cpp_name").get_sub().back().set(ID_identifier, member_base_name); + op1.add("component_cpp_name").get_sub().back().set(ID_C_location, location); + + op1.operands().push_back(exprt(ID_cpp_name)); + op1.op0().get_sub().push_back(irept(ID_name)); + op1.op0().get_sub().back().set(ID_identifier, arg_name); + op1.op0().get_sub().back().set(ID_C_location, location); + op1.location() = location; +} + +/*******************************************************************\ + +Function: copy_array + + Inputs: + member_base_name: name of array member + index: index to copy + block: non-typechecked block + + Outputs: + generate code to copy the member + + Purpose: + +\*******************************************************************/ + +static void copy_array( + const locationt& location, + const irep_idt &member_base_name, + mp_integer i, + const irep_idt &arg_name, + exprt &block) +{ + // Build the index expression + exprt constant=from_integer(i, int_type()); + + block.operands().push_back(exprt(ID_code)); + exprt& code = block.operands().back(); + code.location() = location; + + code.set(ID_statement, ID_expression); + code.add(ID_type)=typet(ID_code); + code.operands().push_back(exprt(ID_sideeffect)); + code.op0().set(ID_statement, ID_assign); + code.op0().operands().push_back(exprt(ID_index)); + exprt& op0 = code.op0().op0(); + op0.operands().push_back(exprt(ID_cpp_name)); + op0.location() = location; + + op0.op0().get_sub().push_back(irept(ID_name)); + op0.op0().get_sub().back().set(ID_identifier, member_base_name); + op0.op0().get_sub().back().set(ID_C_location, location); + op0.copy_to_operands(constant); + + code.op0().operands().push_back(exprt(ID_index)); + + exprt& op1 = code.op0().op1(); + op1.operands().push_back(exprt(ID_member)); + op1.op0().add("component_cpp_name").id(ID_cpp_name); + op1.op0().add("component_cpp_name").get_sub().push_back(irept(ID_name)); + op1.op0().add("component_cpp_name").get_sub().back().set(ID_identifier, member_base_name); + op1.op0().add("component_cpp_name").get_sub().back().set(ID_C_location, location); + + op1.op0().operands().push_back(exprt(ID_cpp_name)); + op1.op0().op0().get_sub().push_back(irept(ID_name)); + op1.op0().op0().get_sub().back().set(ID_identifier, arg_name); + op1.op0().op0().get_sub().back().set(ID_C_location, location); + op1.copy_to_operands(constant); + + op1.location() = location; +} + + +/*******************************************************************\ + +Function: cpp_typecheckt::default_ctor + + Inputs: + + Outputs: + + Purpose: Generate code for implicit default constructors + +\*******************************************************************/ + +void cpp_typecheckt::default_ctor( + const locationt &location, + const irep_idt &base_name, + cpp_declarationt &ctor) const +{ + exprt name; + name.id(ID_name); + name.set(ID_identifier, base_name); + name.location() = location; + + cpp_declaratort decl; + decl.name().id(ID_cpp_name); + decl.name().move_to_sub(name); + decl.type()=typet("function_type"); + decl.type().subtype().make_nil(); + decl.location() = location; + + decl.value().id(ID_code); + decl.value().type()=typet(ID_code); + decl.value().set(ID_statement, ID_block); + decl.add("cv").make_nil(); + decl.add("throw_decl").make_nil(); + + ctor.type().id(ID_constructor); + ctor.add("storage_spec").id(ID_cpp_storage_spec); + ctor.move_to_operands(decl); + ctor.location() = location; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::default_cpctor + + Inputs: + + Outputs: + + Purpose: Generate code for implicit default copy constructor + +\*******************************************************************/ + +void cpp_typecheckt::default_cpctor( + const symbolt& symbol, + cpp_declarationt& cpctor) const +{ + locationt location = symbol.type.location(); + + location.set_function( + id2string(symbol.base_name)+ + "::"+id2string(symbol.base_name)+ + "( const "+id2string(symbol.base_name)+"&)"); + + default_ctor(location, symbol.base_name, cpctor); + cpp_declaratort& decl0 = cpctor.declarators()[0]; + + std::string arg_name("ref"); + + // Compound name + irept compname(ID_name); + compname.set(ID_identifier, symbol.base_name); + compname.set(ID_C_location, location); + + cpp_namet cppcomp; + cppcomp.move_to_sub(compname); + + // Argument name + exprt argname(ID_name); + argname.location()=location; + argname.set(ID_identifier, arg_name); + + cpp_namet cpparg; + cpparg.move_to_sub(argname); + + // Argument declarator + cpp_declaratort argtor; + argtor.add(ID_value).make_nil(); + argtor.set(ID_name, cpparg); + argtor.type()=reference_typet(); + argtor.type().subtype().make_nil(); + argtor.type().add("#qualifier").make_nil(); + argtor.location() = location; + + // Argument declaration + cpp_declarationt argdecl; + argdecl.set(ID_type, "merged_type"); + irept& subt = argdecl.add(ID_type).add("subtypes"); + subt.get_sub().push_back(cppcomp); + irept constnd("const"); + subt.get_sub().push_back(constnd); + argdecl.move_to_operands(argtor); + argdecl.location() = location; + + // Add argument to function type + decl0.add(ID_type).add("arguments").get_sub().push_back(argdecl); + decl0.location() = location; + + irept& initializers = decl0.add("member_initializers"); + initializers.id("member_initializers"); + + cpp_declaratort& declarator = (cpp_declaratort&) cpctor.op0(); + exprt& block = declarator.value(); + + // First, we need to call the parent copy constructors + const irept& bases = symbol.type.find("bases"); + forall_irep(parent_it, bases.get_sub()) + { + assert(parent_it->id() == "base"); + assert(parent_it->get(ID_type) == ID_symbol); + + const symbolt &parsymb= + lookup(parent_it->find(ID_type).get(ID_identifier)); + + if(cpp_is_pod(parsymb.type)) + copy_parent(location, parsymb.base_name, arg_name, block); + else + { + irep_idt ctor_name = parsymb.base_name; + + // Call the parent default copy constructor + exprt name(ID_name); + name.set(ID_identifier, ctor_name); + name.location() = location; + + cpp_namet cppname; + cppname.move_to_sub(name); + + codet mem_init("member_initializer"); + mem_init.location() = location; + mem_init.set(ID_member, cppname); + mem_init.add(ID_operands).get_sub().push_back(cpparg); + initializers.move_to_sub(mem_init); + } + } + + // Then, we add the member initializers + const struct_typet::componentst& components = to_struct_type(symbol.type).components(); + for(struct_typet::componentst::const_iterator mem_it = components.begin(); + mem_it != components.end(); mem_it++) + { + // Take care of virtual tables + if(mem_it->get_bool("is_vtptr")) + { + exprt name(ID_name); + name.set(ID_identifier,mem_it->get("base_name")); + name.location()=location; + + cpp_namet cppname; + cppname.move_to_sub(name); + + const symbolt &virtual_table_symbol_type = + namespacet(context).lookup(mem_it->type().subtype().get(ID_identifier)); + + const symbolt &virtual_table_symbol_var = + namespacet(context).lookup(virtual_table_symbol_type.name.as_string() + "@" + symbol.name.as_string()); + + exprt var = symbol_expr(virtual_table_symbol_var); + address_of_exprt address(var); + assert(address.type() == mem_it->type()); + + already_typechecked(address); + + exprt ptrmember("ptrmember"); + ptrmember.set("component_name", mem_it->get(ID_name)); + ptrmember.operands().push_back(exprt("cpp-this")); + + code_assignt assign(ptrmember, address); + initializers.move_to_sub(assign); + continue; + } + + if( mem_it->get_bool("from_base") + || mem_it->get_bool("is_type") + || mem_it->get_bool("is_static") + || mem_it->type().id() == "code") + continue; + + irep_idt mem_name = mem_it->get(ID_base_name); + + exprt name(ID_name); + name.set(ID_identifier, mem_name); + name.location() = location; + + cpp_namet cppname; + cppname.move_to_sub(name); + + codet mem_init("member_initializer"); + mem_init.set(ID_member, cppname); + mem_init.location() = location; + + exprt memberexpr(ID_member); + memberexpr.set("component_cpp_name",cppname); + memberexpr.add(ID_operands).get_sub().push_back(cpparg); + memberexpr.location() = location; + + if(mem_it->type().id()==ID_array) + memberexpr.set("#array_ini", true); + + mem_init.add(ID_operands).get_sub().push_back(memberexpr); + initializers.move_to_sub(mem_init); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::default_assignop + + Inputs: + + Outputs: + + Purpose: Generate declarartion of the implicit default assignment + operator + +\*******************************************************************/ + +void cpp_typecheckt::default_assignop( + const symbolt& symbol, + cpp_declarationt& cpctor) +{ + locationt location = symbol.type.location(); + + location.set_function( + id2string(symbol.base_name) + + "& "+ + id2string(symbol.base_name)+ + "::operator=( const "+id2string(symbol.base_name)+"&)"); + + std::string arg_name("ref"); + + cpctor.add("storage_spec").id(ID_cpp_storage_spec); + cpctor.type().id(ID_symbol); + cpctor.type().add(ID_identifier).id(symbol.name); + cpctor.operands().push_back(exprt(ID_cpp_declarator)); + cpctor.location() = location; + + cpp_declaratort &declarator = (cpp_declaratort&) cpctor.op0(); + declarator.location() = location; + + cpp_namet &declarator_name = declarator.name(); + typet &declarator_type = declarator.type(); + + declarator_type.location() = location; + + declarator_name.id(ID_cpp_name); + declarator_name.get_sub().push_back(irept("operator")); + declarator_name.get_sub().push_back(irept("=")); + + declarator_type.id("function_type"); + declarator_type.subtype()=reference_typet(); + declarator_type.subtype().add("#qualifier").make_nil(); + declarator_type.subtype().subtype().make_nil(); + + exprt& args = (exprt&) declarator.type().add("arguments"); + args.location() = location; + + args.get_sub().push_back(irept(ID_cpp_declaration)); + + cpp_declarationt& args_decl = (cpp_declarationt&) args.get_sub().back(); + + irept& args_decl_type_sub = args_decl.type().add("subtypes"); + + args_decl.type().id("merged_type"); + args_decl_type_sub.get_sub().push_back(irept(ID_cpp_name)); + args_decl_type_sub.get_sub().back().get_sub().push_back(irept(ID_name)); + args_decl_type_sub.get_sub().back().get_sub().back().set(ID_identifier, symbol.base_name); + args_decl_type_sub.get_sub().back().get_sub().back().set(ID_C_location, location); + + args_decl_type_sub.get_sub().push_back(irept("const")); + args_decl.operands().push_back(exprt(ID_cpp_declarator)); + args_decl.location() = location; + + cpp_declaratort &args_decl_declor= + (cpp_declaratort&) args_decl.operands().back(); + + args_decl_declor.name().id(ID_cpp_name); + args_decl_declor.name().get_sub().push_back(irept(ID_name)); + args_decl_declor.name().get_sub().back().add(ID_identifier).id(arg_name); + args_decl_declor.location() = location; + + args_decl_declor.type().id("pointer"); + args_decl_declor.type().set("#reference", true); + args_decl_declor.type().add("#qualifier").make_nil(); + args_decl_declor.type().add("subtype").make_nil(); + args_decl_declor.value().make_nil(); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::default_assignop + + Inputs: + + Outputs: + + Purpose: Generate code for the implicit default assignment operator + +\*******************************************************************/ + +void cpp_typecheckt::default_assignop_value( + const symbolt &symbol, + cpp_declaratort &declarator) +{ + // save location + locationt location=declarator.location(); + declarator.make_nil(); + + declarator.value().location()=location; + declarator.value().id(ID_code); + declarator.value().set(ID_statement, ID_block); + declarator.value().type()=code_typet(); + + exprt &block=declarator.value(); + + std::string arg_name("ref"); + + // First, we copy the parents + const irept &bases = symbol.type.find(ID_bases); + + forall_irep(parent_it, bases.get_sub()) + { + assert(parent_it->id() == ID_base); + assert(parent_it->get(ID_type) == ID_symbol); + + const symbolt &symb= + lookup(parent_it->find(ID_type).get(ID_identifier)); + + copy_parent(location, symb.base_name, arg_name, block); + } + + // Then, we copy the members + const irept &components=symbol.type.find(ID_components); + + forall_irep(mem_it, components.get_sub()) + { + if(mem_it->get_bool("from_base") || + mem_it->get_bool("is_type") || + mem_it->get_bool("is_static") || + mem_it->get_bool("is_vtptr") || + mem_it->get(ID_type)==ID_code) + continue; + + irep_idt mem_name=mem_it->get(ID_base_name); + + if(mem_it->get(ID_type)==ID_array) + { + const exprt &size_expr= + to_array_type((typet&)mem_it->find(ID_type)).size(); + + if(size_expr.id()==ID_infinity) + { + // err_location(object); + // err << "cannot copy array of infinite size" << std::endl; + // throw 0; + continue; + } + + mp_integer size; + bool to_int = to_integer(size_expr, size); + assert(!to_int); + assert(size>=0); + + exprt::operandst empty_operands; + for(mp_integer i = 0; i < size; ++i) + copy_array(location, mem_name,i,arg_name,block); + } + else + copy_member(location, mem_name, arg_name, block); + } + + // Finally we add the return statement + block.operands().push_back(exprt(ID_code)); + exprt &ret_code = declarator.value().operands().back(); + ret_code.operands().push_back(exprt(ID_dereference)); + ret_code.op0().operands().push_back(exprt("cpp-this")); + ret_code.set(ID_statement, ID_return); + ret_code.type()=code_typet(); +} + +/*******************************************************************\ + +Function: check_member_initializers + +Inputs: bases: the parents of the class + components: the components of the class + initializers: the constructor initializers + + Outputs: If an invalid initializer is found, then + the method outputs an error message and + throws a 0 exception. + + Purpose: Check a constructor initialization-list. + An initalizer has to be a data member declared + in this class or a direct-parent constructor. + +\*******************************************************************/ + +void cpp_typecheckt::check_member_initializers( + const irept &bases, + const struct_typet::componentst &components, + const irept &initializers) +{ + assert(initializers.id() == "member_initializers"); + + forall_irep(init_it, initializers.get_sub()) + { + const irept &initializer = *init_it; + assert(initializer.is_not_nil()); + + std::string identifier, base_name; + + assert(initializer.get("member") == ID_cpp_name); + + const cpp_namet &member_name= + to_cpp_name(initializer.find("member")); + + bool has_template_args = member_name.has_template_args(); + + if(has_template_args) + { + // it has to be a parent constructor + typet member_type = (typet&) initializer.find("member"); + typecheck_type(member_type); + + // check for a direct parent + bool ok = false; + forall_irep(parent_it, bases.get_sub()) + { + assert(parent_it->get(ID_type) == ID_symbol); + + if(member_type.get(ID_identifier) + == parent_it->find(ID_type).get(ID_identifier)) + { + ok = true; + break; + } + } + + if(!ok) + { + err_location(member_name.location()); + str << "invalid initializer `" << member_name.to_string() << "'"; + throw 0; + } + return; + } + + member_name.convert(identifier, base_name); + + bool ok = false; + + for(struct_typet::componentst::const_iterator + c_it=components.begin(); + c_it!=components.end(); + c_it++) + { + if(c_it->get("base_name") != base_name ) continue; + + // Data member + if(!c_it->get_bool("from_base") && + !c_it->get_bool("is_static") && + c_it->get(ID_type) != ID_code) + { + ok = true; + break; + } + + // Maybe it is a parent constructor? + if(c_it->get_bool("is_type")) + { + typet type = static_cast(c_it->find(ID_type)); + if(type.id() != ID_symbol) + continue; + + const symbolt& symb = lookup(type.get(ID_identifier)); + if(symb.type.id() != ID_struct) + break; + + // check for a direct parent + forall_irep(parent_it, bases.get_sub()) + { + assert(parent_it->get(ID_type) == ID_symbol ); + if(symb.name == parent_it->find(ID_type).get(ID_identifier)) + { + ok = true; + break; + } + } + continue; + } + + // Parent constructor + if( c_it->get_bool("from_base") + && !c_it->get_bool("is_type") + && !c_it->get_bool("is_static") + && c_it->get(ID_type) == ID_code + && c_it->find(ID_type).get(ID_return_type) == ID_constructor) + { + typet member_type = (typet&) initializer.find(ID_member); + typecheck_type(member_type); + + // check for a direct parent + forall_irep(parent_it, bases.get_sub()) + { + assert(parent_it->get(ID_type) == ID_symbol ); + + if(member_type.get(ID_identifier) + == parent_it->find(ID_type).get(ID_identifier)) + { + ok = true; + break; + } + } + break; + } + } + + if(!ok) + { + err_location(member_name.location()); + str << "invalid initializer `" << base_name << "'"; + throw 0; + } + } +} + +/*******************************************************************\ + +Function: full_member_initialization + + Inputs: bases: the class base types + components: the class components + initializers: the constructor initializers + + Outputs: initializers is updated. + + Purpose: Build the full initialization list of the constructor. + First, all the direct-parent constructors are called. + Second, all the non-pod data members are initialized. + + Note: The initialization order follows the decalration order. + +\*******************************************************************/ + +void cpp_typecheckt::full_member_initialization( + const struct_typet &struct_type, + irept &initializers) +{ + const irept &bases=struct_type.find("bases"); + + const struct_typet::componentst &components= + struct_type.components(); + + assert(initializers.id() == "member_initializers"); + + irept final_initializers("member_initializers"); + + // First, if we are the most-derived object, then + // we need to construct the virtual bases + std::list vbases; + get_virtual_bases(struct_type,vbases); + + if(!vbases.empty()) + { + codet cond("ifthenelse"); + + { + cpp_namet most_derived; + most_derived.get_sub().push_back(irept(ID_name)); + most_derived.get_sub().back().set(ID_identifier, "@most_derived"); + + exprt tmp; + tmp.swap(most_derived); + cond.move_to_operands(tmp); + } + + codet block(ID_block); + + while(!vbases.empty()) + { + const symbolt& symb = lookup(vbases.front()); + if(!cpp_is_pod(symb.type)) + { + // default initializer + irept name(ID_name); + name.set(ID_identifier, symb.base_name); + + cpp_namet cppname; + cppname.move_to_sub(name); + + codet mem_init("member_initializer"); + mem_init.set("member", cppname); + block.move_to_sub(mem_init); + } + vbases.pop_front(); + } + cond.move_to_operands(block); + final_initializers.move_to_sub(cond); + } + + // Subsequenlty, we need to call the non-POD parent constructors + forall_irep(parent_it, bases.get_sub()) + { + assert(parent_it->id() == "base"); + assert(parent_it->get(ID_type) == ID_symbol); + + const symbolt &ctorsymb= + lookup(parent_it->find(ID_type).get(ID_identifier)); + + if(cpp_is_pod(ctorsymb.type)) + continue; + + irep_idt ctor_name=ctorsymb.base_name; + + // Check if the initialization list of the constructor + // explicitly calls the parent constructor + bool found = false; + + forall_irep(m_it, initializers.get_sub()) + { + irept initializer = *m_it; + std::string identifier; + std::string base_name; + + assert(initializer.get("member") == ID_cpp_name); + + const cpp_namet &member_name= + to_cpp_name(initializer.find("member")); + + bool has_template_args = member_name.has_template_args(); + + if(!has_template_args) + { + member_name.convert(identifier, base_name); + + // check if the initializer is a data + bool is_data = false; + + for(struct_typet::componentst::const_iterator c_it = + components.begin(); c_it != components.end(); c_it++) + { + if (c_it->get("base_name") == base_name + && c_it->get(ID_type) != "code" + && !c_it->get_bool("is_type")) + { + is_data = true; + break; + } + } + + if(is_data) + continue; + } + + typet member_type= + static_cast(initializer.find("member")); + + typecheck_type(member_type); + + if(member_type.id()!=ID_symbol) + break; + + if(parent_it->find(ID_type).get(ID_identifier)== + member_type.get(ID_identifier)) + { + final_initializers.move_to_sub(initializer); + found = true; + break; + } + } + + // Call the parent default constructor + if(!found) + { + irept name(ID_name); + name.set(ID_identifier, ctor_name); + + cpp_namet cppname; + cppname.move_to_sub(name); + + codet mem_init("member_initializer"); + mem_init.set("member", cppname); + final_initializers.move_to_sub(mem_init); + } + + if(parent_it->get_bool("virtual")) + { + codet cond("ifthenelse"); + + { + cpp_namet most_derived; + most_derived.get_sub().push_back(irept(ID_name)); + most_derived.get_sub().back().set(ID_identifier, "@most_derived"); + + exprt tmp; + tmp.swap(most_derived); + cond.move_to_operands(tmp); + } + + { + codet tmp("member_initializer"); + tmp.swap(final_initializers.get_sub().back()); + cond.move_to_operands(tmp); + final_initializers.get_sub().back().swap(cond); + } + } + } + + // Then, we add the member initializers + for(struct_typet::componentst::const_iterator mem_it = + components.begin(); mem_it != components.end(); mem_it++) + { + // Take care of virtual tables + if(mem_it->get_bool("is_vtptr")) + { + exprt name(ID_name); + name.set(ID_identifier,mem_it->get("base_name")); + name.location() = mem_it->location(); + + cpp_namet cppname; + cppname.move_to_sub(name); + + const symbolt& virtual_table_symbol_type = + lookup(mem_it->type().subtype().get(ID_identifier)); + + const symbolt& virtual_table_symbol_var = + lookup(virtual_table_symbol_type.name.as_string() + "@" + + struct_type.get(ID_name).as_string()); + + exprt var =symbol_expr(virtual_table_symbol_var); + address_of_exprt address(var); + assert(address.type() == mem_it->type()); + + already_typechecked(address); + + exprt ptrmember("ptrmember"); + ptrmember.set("component_name",mem_it->get(ID_name)); + ptrmember.operands().push_back(exprt("cpp-this")); + + code_assignt assign(ptrmember, address); + final_initializers.move_to_sub(assign); + continue; + } + + if( mem_it->get_bool("from_base") + || mem_it->type().id() == "code" + || mem_it->get_bool("is_type") + || mem_it->get_bool("is_static")) + continue; + + irep_idt mem_name = mem_it->get("base_name"); + + // Check if the initialization list of the constructor + // explicitly initializes the data member + bool found = false; + Forall_irep(m_it, initializers.get_sub()) + { + irept &initializer = *m_it; + std::string identifier; + std::string base_name; + + if(initializer.get("member")!=ID_cpp_name) continue; + cpp_namet &member_name=(cpp_namet&) initializer.add("member"); + + if(member_name.has_template_args()) + continue; // base-type initializer + + member_name.convert(identifier, base_name); + + if(mem_name==base_name) + { + final_initializers.move_to_sub(initializer); + found = true; + break; + } + } + + // If the data member is a reference, it must be explicitly + // initialized + if(!found && + mem_it->find(ID_type).id()==ID_pointer && + mem_it->find(ID_type).get_bool(ID_C_reference)) + { + err_location(*mem_it); + str << "reference must be explicitly initialized"; + throw 0; + } + + // If the data member is not POD and is not explicitly initialized, + // then its default constructor is called. + if(!found && !cpp_is_pod((const typet&) (mem_it->find(ID_type)))) + { + irept name(ID_name); + name.set(ID_identifier, mem_name); + + cpp_namet cppname; + cppname.move_to_sub(name); + + codet mem_init("member_initializer"); + mem_init.set("member", cppname); + final_initializers.move_to_sub(mem_init); + } + } + + initializers.swap(final_initializers); +} + +/*******************************************************************\ + +Function: find_cpctor + + Inputs: typechecked compound symbol + Outputs: return true if a copy constructor is found + + Note: + "A non-template constructor for class X is a copy constructor + if its first parameter is of type X&, const X&, volatile X& + or const volatile X&, and either there are no other parameters + or else all other parameters have default arguments (8.3.6).106) + [Example: X::X(const X&) and X::X(X&, int=1) are copy constructors." + +\*******************************************************************/ + +bool cpp_typecheckt::find_cpctor(const symbolt &symbol) const +{ + const struct_typet &struct_type = to_struct_type(symbol.type); + const struct_typet::componentst &components = struct_type.components(); + + for(struct_typet::componentst::const_iterator + cit=components.begin(); + cit!=components.end(); + cit++) + { + // Skip non-ctor + const struct_typet::componentt& component = *cit; + + if(component.type().id() != "code" + || to_code_type(component.type()).return_type().id() !=ID_constructor) + continue; + + // Skip inherited constructor + if(component.get_bool("from_base")) + continue; + + const code_typet& code_type = to_code_type(component.type()); + + const code_typet::argumentst& args = code_type.arguments(); + + // First argument is the this pointer. Therefore, copy + // constructors have at least two arguments + if(args.size() < 2) + continue; + + const code_typet::argumentt& arg1 = args[1]; + + const typet &arg1_type=arg1.type(); + + if(!is_reference(arg1_type)) + continue; + + if(arg1_type.subtype().get(ID_identifier)!=symbol.name) + continue; + + bool defargs = true; + for(unsigned i=2; i get(ID_base_name)=="~"+id2string(symbol.base_name)) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: default_dtor + + Inputs: + + Outputs: + + Purpose: + + Note: + +\*******************************************************************/ + +void cpp_typecheckt::default_dtor( + const symbolt &symb, + cpp_declarationt &dtor) +{ + assert(symb.type.id()==ID_struct); + + irept name; + name.id(ID_name); + name.set(ID_identifier, "~"+id2string(symb.base_name)); + name.set(ID_C_location, symb.location); + + cpp_declaratort decl; + decl.name().id(ID_cpp_name); + decl.name().move_to_sub(name); + decl.type().id("function_type"); + decl.type().subtype().make_nil(); + + decl.value().id(ID_code); + decl.value().add(ID_type).id(ID_code); + decl.value().set(ID_statement, ID_block); + decl.add("cv").make_nil(); + decl.add("throw_decl").make_nil(); + + dtor.add(ID_type).id(ID_destructor); + dtor.add(ID_storage_spec).id(ID_cpp_storage_spec); + dtor.add(ID_operands).move_to_sub(decl); +} + +/*******************************************************************\ + +Function: dtor + + Inputs: + + Outputs: + + Purpose: produces destructor code for a class object + + Note: + +\*******************************************************************/ + +codet cpp_typecheckt::dtor(const symbolt &symb) +{ + assert(symb.type.id() == ID_struct); + + locationt location=symb.type.location(); + + location.set_function( + id2string(symb.base_name)+ + "::~"+id2string(symb.base_name)+"()"); + + code_blockt block; + + const struct_typet::componentst &components = + to_struct_type(symb.type).components(); + + // take care of virtual methods + for(struct_typet::componentst::const_iterator + cit=components.begin(); + cit!=components.end(); + cit++) + { + if(cit->get_bool("is_vtptr")) + { + exprt name(ID_name); + name.set(ID_identifier, cit->get(ID_base_name)); + + cpp_namet cppname; + cppname.move_to_sub(name); + + const symbolt &virtual_table_symbol_type = + namespacet(context).lookup( + cit->type().subtype().get(ID_identifier)); + + const symbolt &virtual_table_symbol_var = + namespacet(context).lookup( + virtual_table_symbol_type.name.as_string() + "@" + symb.name.as_string()); + + exprt var=symbol_expr(virtual_table_symbol_var); + address_of_exprt address(var); + assert(address.type()==cit->type()); + + already_typechecked(address); + + exprt ptrmember(ID_ptrmember); + ptrmember.set(ID_component_name, cit->get(ID_name)); + ptrmember.operands().push_back(exprt("cpp-this")); + + code_assignt assign(ptrmember, address); + block.operands().push_back(assign); + continue; + } + } + + // call the data member destructors in the reverse order + for(struct_typet::componentst::const_reverse_iterator + cit=components.rbegin(); + cit!=components.rend(); + cit++) + { + const typet &type=cit->type(); + + if(cit->get_bool("from_base") || + cit->get_bool("is_type") || + cit->get_bool("is_static") || + type.id()==ID_code || + is_reference(type) || + cpp_is_pod(type)) + continue; + + irept name(ID_name); + name.set(ID_identifier, cit->get(ID_base_name)); + name.set(ID_C_location, location); + + cpp_namet cppname; + cppname.get_sub().push_back(name); + + exprt member(ID_ptrmember); + member.set("component_cpp_name", cppname); + member.operands().push_back(exprt("cpp-this")); + member.location() = location; + + codet dtor_code= + cpp_destructor(location, cit->type(), member); + + if(dtor_code.is_not_nil()) + block.move_to_operands(dtor_code); + } + + const irept::subt &bases=symb.type.find("bases").get_sub(); + + // call the base destructors in the reverse order + for(irept::subt::const_reverse_iterator + bit=bases.rbegin(); + bit!=bases.rend(); + bit++) + { + assert(bit->id()==ID_base); + assert(bit->find(ID_type).id()==ID_symbol); + const symbolt& psymb = lookup(bit->find(ID_type).get(ID_identifier)); + + exprt object(ID_dereference); + object.operands().push_back(exprt("cpp-this")); + object.location() = location; + + exprt dtor_code = + cpp_destructor(location, psymb.type, object); + + if(dtor_code.is_not_nil()) + block.move_to_operands(dtor_code); + } + + return block; +} + diff --git a/src/cpp/cpp_typecheck_conversions.cpp b/src/cpp/cpp_typecheck_conversions.cpp new file mode 100644 index 00000000000..fa5998b7fdb --- /dev/null +++ b/src/cpp/cpp_typecheck_conversions.cpp @@ -0,0 +1,2403 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: standard_conversion_lvalue_to_rvalue + + Inputs: A typechecked lvalue expression + + Outputs: True iff the lvalue-to-rvalue conversion is possible. + 'new_type' contains the result of the conversion. + + Purpose: Lvalue-to-rvalue conversion + + An lvalue (3.10) of a non-function, non-array type T can be + converted to an rvalue. If T is an incomplete type, a program + that necessitates this conversion is ill-formed. If the object + to which the lvalue refers is not an object of type T and is + not an object of a type derived from T, or if the object is + uninitialized, a program that necessitates this conversion has + undefined behavior. If T is a non-class type, the type of the + rvalue is the cv-unqualified version of T. Otherwise, the type of + the rvalue is T. + + The value contained in the object indicated by the lvalue + is the rvalue result. When an lvalue-to-rvalue conversion + occurs within the operand of sizeof (5.3.3) the value contained + in the referenced object is not accessed, since that operator + does not evaluate its operand. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_lvalue_to_rvalue( + const exprt &expr, + exprt &new_expr) const +{ + assert(expr.get_bool(ID_C_lvalue)); + + if(expr.type().id()== ID_code || + expr.type().id()== ID_incomplete_array || + expr.type().id()== ID_incomplete_struct || + expr.type().id()== ID_incomplete_union) + return false; + + new_expr=expr; + new_expr.remove(ID_C_lvalue); + + return true; +} + +/*******************************************************************\ + +Function: standard_conversion_array_to_pointer + + Inputs: An array expression + + Outputs: True iff the array-to-pointer conversion is possible. + The result of the conversion is stored in 'new_expr'. + + Purpose: Array-to-pointer conversion + + An lvalue or rvalue of type "array of N T" or "array of unknown + bound of T" can be converted to an rvalue of type "pointer to T." + The result is a pointer to the first element of the array. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_array_to_pointer( + const exprt &expr, + exprt &new_expr) const +{ + assert(expr.type().id()==ID_array || + expr.type().id()==ID_incomplete_array); + + exprt index(ID_index, expr.type().subtype()); + index.copy_to_operands(expr, from_integer(0, int_type())); + index.set(ID_C_lvalue, true); + + pointer_typet pointer; + pointer.subtype()=expr.type().subtype(); + + new_expr=exprt(ID_address_of, pointer); + new_expr.move_to_operands(index); + + return true; +} + +/*******************************************************************\ + +Function: standard_conversion_function_to_pointer + + Inputs: A function expression + + Outputs: True iff the array-to-pointer conversion is possible. + The result of the conversion is stored in 'new_expr'. + + + Purpose: Function-to-pointer conversion + + An lvalue of function type T can be converted to an rvalue of type + "pointer to T." The result is a pointer to the function.50) + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_function_to_pointer( + const exprt &expr, exprt &new_expr) const +{ + const code_typet &func_type=to_code_type(expr.type()); + + if(!expr.get_bool(ID_C_lvalue)) + return false; + + pointer_typet pointer; + pointer.subtype() = func_type; + + new_expr = exprt(ID_address_of); + new_expr.copy_to_operands(expr); + new_expr.type() = pointer; + + return true; +} + +/*******************************************************************\ + +Function: standard_conversion_qualification + + Inputs: A typechecked expression 'expr', a destination + type 'type' + + Outputs: True iff the qualification conversion is possible. + The result of the conversion is stored in 'new_expr'. + + Purpose: Qualification conversion + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_qualification( + const exprt &expr, + const typet &type, + exprt &new_expr) const +{ + if(expr.type().id()!=ID_pointer || + is_reference(expr.type())) + return false; + + if(expr.get_bool(ID_C_lvalue)) + return false; + + if(expr.type()!=type) + return false; + + typet sub_from=expr.type().subtype(); + typet sub_to=type.subtype(); + bool const_to=true; + + while(sub_from.id()==ID_pointer) + { + c_qualifierst qual_from(sub_from); + c_qualifierst qual_to(sub_to); + + if(!qual_to.is_constant) + const_to=false; + + if(qual_from.is_constant && !qual_to.is_constant) + return false; + + if(qual_from!=qual_to && !const_to) + return false; + + typet tmp1=sub_from.subtype(); + sub_from.swap(tmp1); + + typet tmp2=sub_to.subtype(); + sub_to.swap(tmp2); + } + + c_qualifierst qual_from(sub_from); + c_qualifierst qual_to(sub_to); + + if(qual_from.is_subset_of(qual_to)) + { + new_expr=expr; + new_expr.type()=type; + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: standard_conversion_integral_promotion + + Inputs: A typechecked expression 'expr' + + Outputs: True iff the integral pormotion is possible. + The result of the conversion is stored in 'new_expr'. + + + Purpose: Integral-promotion conversion + + An rvalue of type char, signed char, unsigned char, short int, + or unsigned short int can be converted to an rvalue of type int + if int can represent all the values of the source type; otherwise, + the source rvalue can be converted to an rvalue of type unsigned int. + + An rvalue of type wchar_t (3.9.1) or an enumeration type (7.2) can + be converted to an rvalue of the first of the following types that + can represent all the values of its underlying type: int, unsigned int, + long, or unsigned long. + + An rvalue for an integral bit-field (9.6) can be converted + to an rvalue of type int if int can represent all the values of the + bit-field; otherwise, it can be converted to unsigned int if + unsigned int can represent all the values of the bit-field. + If the bit-field is larger yet, no integral promotion applies to + it. If the bit-field has an enumerated type, it is treated as + any other value of that type for promotion purposes. + + An rvalue of type bool can be converted to an rvalue of type int, + with false becoming zero and true becoming one. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_integral_promotion( + const exprt &expr, + exprt &new_expr) const +{ + if(expr.get_bool(ID_C_lvalue)) + return false; + + c_qualifierst qual_from; + qual_from.read(expr.type()); + + typet int_type(ID_signedbv); + int_type.set(ID_width, config.ansi_c.int_width); + qual_from.write(int_type); + + if(expr.type().id()==ID_signedbv) + { + unsigned width = bv_width(expr.type()); + if(width >= config.ansi_c.int_width) + return false; + new_expr = expr; + new_expr.make_typecast(int_type); + return true; + } + + if(expr.type().id()==ID_unsignedbv) + { + unsigned width=bv_width(expr.type()); + if(width >= config.ansi_c.int_width) + return false; + new_expr = expr; + if(width == config.ansi_c.int_width) + int_type.id(ID_unsignedbv); + new_expr.make_typecast(int_type); + return true; + } + + if(follow(expr.type()).id()==ID_c_enum) + { + new_expr = expr; + new_expr.make_typecast(int_type); + return true; + } + + return false; +} + + +/*******************************************************************\ + +Function: standard_conversion_floating_point_promotion + + Inputs: A typechecked expression 'expr' + + Outputs: True iff the integral promotion is possible. + The result of the conversion is stored in 'new_expr'. + + Purpose: Floating-point-promotion conversion + + An rvalue of type float can be converted to an rvalue of type + double. The value is unchanged. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_floating_point_promotion( + const exprt &expr, + exprt &new_expr) const +{ + if(expr.get_bool(ID_C_lvalue)) + return false; + + // we only do that with 'float', + // not with 'double' or 'long double' + if(expr.type()!=float_type()) + return false; + + unsigned width=bv_width(expr.type()); + + if(width!=config.ansi_c.single_width) + return false; + + c_qualifierst qual_from; + qual_from.read(expr.type()); + + new_expr=expr; + new_expr.make_typecast(double_type()); + qual_from.write(new_expr.type()); + + return true; +} + +/*******************************************************************\ + +Function: standard_conversion_integral_conversion + + Inputs: A typechecked expression 'expr', a destination + type 'type' + + Outputs: True iff the integral pormotion is possible. + The result of the conversion is stored in 'new_expr'. + + + Purpose: Integral conversion + + An rvalue of type char, signed char, unsigned char, short int, + An rvalue of an integer type can be converted to an rvalue of + another integer type. An rvalue of an enumeration type can be + converted to an rvalue of an integer type. + + If the destination type is unsigned, the resulting value is the + least unsigned integer congruent to the source integer (modulo + 2n where n is the number of bits used to represent the unsigned + type). [Note: In a two's complement representation, this + conversion is conceptual and there is no change in the bit + pattern (if there is no truncation). ] + + If the destination type is signed, the value is unchanged if it + can be represented in the destination type (and bit-field width); + otherwise, the value is implementation-defined. + + If the destination type is bool, see 4.12. If the source type is + bool, the value false is converted to zero and the value true is + converted to one. + + The conversions allowed as integral promotions are excluded from + the set of integral conversions. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_integral_conversion( + const exprt &expr, + const typet &type, + exprt &new_expr) const +{ + if(type.id() != ID_signedbv && + type.id() != ID_unsignedbv) + return false; + + if(expr.type().id() != ID_signedbv && + expr.type().id() != ID_unsignedbv && + expr.type().id() != ID_bool && + follow(expr.type()).id() != ID_c_enum) + return false; + + if(expr.get_bool(ID_C_lvalue)) + return false; + + c_qualifierst qual_from; + qual_from.read(expr.type()); + new_expr = expr; + new_expr.make_typecast(type); + qual_from.write(new_expr.type()); + + return true; +} + +/*******************************************************************\ + +Function: standard_conversion_floating_integral_conversion + + Inputs: A typechecked expression 'expr' + + Outputs: True iff the conversion is possible. + The result of the conversion is stored in 'new_expr'. + + Purpose: Floating-integral conversion + + An rvalue of a floating point type can be converted to an rvalue + of an integer type. The conversion truncates; that is, the + fractional part is discarded. The behavior is undefined if the + truncated value cannot be represented in the destination type. + [Note: If the destination type is bool, see 4.12. ] + + An rvalue of an integer type or of an enumeration type can be + converted to an rvalue of a floating point type. The result is + exact if possible. Otherwise, it is an implementation-defined + choice of either the next lower or higher representable value. + [Note: loss of precision occurs if the integral value cannot be + represented exactly as a value of the floating type. ] If the + source type is bool, the value false is converted to zero and the + value true is converted to one. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_floating_integral_conversion( + const exprt &expr, + const typet &type, + exprt &new_expr) const +{ + if(expr.get_bool(ID_C_lvalue)) + return false; + + if(expr.type().id() == ID_floatbv || + expr.type().id() == ID_fixedbv) + { + if(type.id()!=ID_signedbv && + type.id()!=ID_unsignedbv) + return false; + } + else if(expr.type().id() == ID_signedbv || + expr.type().id() == ID_unsignedbv || + follow(expr.type()).id() == ID_c_enum) + { + if(type.id() != ID_fixedbv && + type.id() != ID_floatbv) + return false; + } + else + return false; + + c_qualifierst qual_from; + qual_from.read(expr.type()); + new_expr = expr; + new_expr.make_typecast(type); + qual_from.write(new_expr.type()); + + return true; +} + + +/*******************************************************************\ + +Function: standard_conversion_floating_point_conversion + + Inputs: A typechecked expression 'expr', a destination + type 'type' + + Outputs: True iff the floating-point conversion is possible. + The result of the conversion is stored in 'new_expr'. + + Purpose: Floating-point conversion + + An rvalue of floating point type can be converted to an rvalue + of another floating point type. If the source value can be exactly + represented in the destination type, the result of the conversion + is that exact representation. If the source value is between two + adjacent destination values, the result of the conversion is an + implementation-defined choice of either of those values. Otherwise, + the behavior is undefined. + + The conversions allowed as floating point promotions are excluded + from the set of floating point conversions. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_floating_point_conversion( + const exprt &expr, + const typet &type, + exprt &new_expr) const +{ + if(expr.type().id()!=ID_floatbv && + expr.type().id()!=ID_fixedbv) + return false; + + if(type.id()!=ID_floatbv && + type.id()!=ID_fixedbv) + return false; + + if(expr.get_bool(ID_C_lvalue)) + return false; + + c_qualifierst qual_from; + + qual_from.read(expr.type()); + new_expr = expr; + new_expr.make_typecast(type); + qual_from.write(new_expr.type()); + + return true; +} + +/*******************************************************************\ + +Function: standard_conversion_pointer + + Inputs: A typechecked expression 'expr', a destination + type 'type' + + Outputs: True iff the pointer conversion is possible. + The result of the conversion is stored in 'new_expr'. + + Purpose: Pointer conversion + + A null pointer constant is an integral constant expression + (5.19) rvalue of integer type that evaluates to zero. A null + pointer constant can be converted to a pointer type; the result + is the null pointer value of that type and is distinguishable + from every other value of pointer to object or pointer to + function type. Two null pointer values of the same type shall + compare equal. The conversion of a null pointer constant to a + pointer to cv-qualified type is a single conversion, and not the + sequence of a pointer conversion followed by a qualification + conversion (4.4). + + An rvalue of type "pointer to cv T," where T is an object type, + can be converted to an rvalue of type "pointer to cv void." The + result of converting a "pointer to cv T" to a "pointer to cv + void" points to the start of the storage location where the + object of type T resides, as if the object is a most derived + object (1.8) of type T (that is, not a base class subobject). + + An rvalue of type "pointer to cv D," where D is a class type, + can be converted to an rvalue of type "pointer to cv B," where + B is a base class (clause 10) of D. If B is an inaccessible + (clause 11) or ambiguous (10.2) base class of D, a program that + necessitates this conversion is ill-formed. The result of the + conversion is a pointer to the base class sub-object of the + derived class object. The null pointer value is converted to + the null pointer value of the destination type. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_pointer( + const exprt &expr, + const typet &type, + exprt &new_expr) +{ + if(type.id()!=ID_pointer || + is_reference(type) || + type.find("to-member").is_not_nil()) + return false; + + if(expr.get_bool(ID_C_lvalue)) + return false; + + // integer 0 to NULL pointer conversion? + if(expr.is_zero() && + expr.type().id()!=ID_pointer) + { + new_expr = expr; + new_expr.set(ID_value, ID_NULL); + new_expr.type() = type; + return true; + } + + if(expr.type().id() != ID_pointer || + expr.type().find("to-member").is_not_nil()) + return false; + + typet sub_from = follow(expr.type().subtype()); + typet sub_to = follow(type.subtype()); + + // anything but function pointer to void * + if(sub_from.id()!=ID_code && sub_to.id()==ID_empty) + { + c_qualifierst qual_from; + qual_from.read(expr.type().subtype()); + new_expr = expr; + new_expr.make_typecast(type); + qual_from.write(new_expr.type().subtype()); + return true; + } + + // struct * to struct * + if(sub_from.id()==ID_struct && sub_to.id() == ID_struct) + { + const struct_typet& from_struct = to_struct_type(sub_from); + const struct_typet& to_struct = to_struct_type(sub_to); + if(subtype_typecast(from_struct, to_struct)) + { + c_qualifierst qual_from; + qual_from.read(expr.type().subtype()); + new_expr = expr; + make_ptr_typecast(new_expr, type); + qual_from.write(new_expr.type().subtype()); + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: standard_conversion_pointer + + Inputs: A typechecked expression 'expr', a destination + type 'type' + + Outputs: True iff the pointer-to-member conversion is possible. + The result of the conversion is stored in 'new_expr'. + + + Purpose: Pointer-to-member conversion + + A null pointer constant (4.10) can be converted to a pointer to + member type; the result is the null member pointer value of that + type and is distinguishable from any pointer to member not created + from a null pointer constant. Two null member pointer values of + the same type shall compare equal. The conversion of a null pointer + constant to a pointer to member of cv-qualified type is a single + conversion, and not the sequence of a pointer to member conversion + followed by a qualification conversion (4.4). + + An rvalue of type "pointer to member of B of type cv T," where B + is a class type, can be converted to an rvalue of type "pointer + to member of D of type cv T," where D is a derived class + (clause 10) of B. If B is an inaccessible (clause 11), ambiguous + (10.2) or virtual (10.1) base class of D, a program that + necessitates this conversion is ill-formed. The result of the + conversion refers to the same member as the pointer to member + before the conversion took place, but it refers to the base class + member as if it were a member of the derived class. The result + refers to the member in D"s instance of B. Since the result has + type "pointer to member of D of type cv T," it can be dereferenced + with a D object. The result is the same as if the pointer to + member of B were dereferenced with the B sub-object of D. The null + member pointer value is converted to the null member pointer value + of the destination type.52) + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_pointer_to_member( + const exprt &expr, + const typet &type, + exprt &new_expr) +{ + if(type.id()!=ID_pointer || + is_reference(type) || + type.find("to-member").is_nil()) + return false; + + if(expr.type().id() != ID_pointer || + expr.type().find("to-member").is_nil()) + return false; + + if(type.subtype()!=expr.type().subtype()) + { + // subtypes different + if(type.subtype().id() == ID_code && + expr.type().subtype().id() == ID_code) + { + code_typet code1 = to_code_type(expr.type().subtype()); + assert(code1.arguments().size()>0); + code_typet::argumentt this1 = code1.arguments()[0]; + assert(this1.get(ID_C_base_name) == ID_this); + code1.arguments().erase(code1.arguments().begin()); + + code_typet code2 = to_code_type(type.subtype()); + assert(code2.arguments().size()>0); + code_typet::argumentt this2 = code2.arguments()[0]; + assert(this2.get(ID_C_base_name) == ID_this); + code2.arguments().erase(code2.arguments().begin()); + + if(this2.type().subtype().get_bool(ID_C_constant) && + !this1.type().subtype().get_bool(ID_C_constant)) + return false; + + // give a second chance ignoring `this' + if(code1!=code2) + return false; + } + else + return false; + } + + if(expr.get_bool(ID_C_lvalue)) + return false; + + if(expr.id()==ID_constant && + expr.get(ID_value)==ID_NULL) + { + new_expr = expr; + new_expr.make_typecast(type); + return true; + } + + struct_typet from_struct = + to_struct_type(follow(static_cast + (expr.type().find("to-member")))); + + struct_typet to_struct = + to_struct_type(follow(static_cast + (type.find("to-member")))); + + if(subtype_typecast(to_struct, from_struct)) + { + new_expr = expr; + new_expr.make_typecast(type); + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: standard_conversion_boolean + + Inputs: A typechecked expression 'expr' + + Outputs: True iff the boolean conversion is possible. + The result of the conversion is stored in 'new_expr'. + + + Purpose: Boolean conversion + + An rvalue of arithmetic, enumeration, pointer, or pointer to + member type can be converted to an rvalue of type bool. + A zero value, null pointer value, or null member pointer value is + converted to false; any other value is converted to true. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_boolean( + const exprt &expr, exprt &new_expr) const +{ + if(expr.get_bool(ID_C_lvalue)) + return false; + + if(expr.type().id() != ID_signedbv + && expr.type().id() != ID_unsignedbv + && expr.type().id() != ID_pointer + && follow(expr.type()).id() != ID_c_enum) + return false; + + c_qualifierst qual_from; + qual_from.read(expr.type()); + + typet Bool(ID_bool); + qual_from.write(Bool); + + new_expr = expr; + new_expr.make_typecast(Bool); + return true; +} + +#ifdef CPP_SYSTEMC_EXTENSION + +/*******************************************************************\ + +Function: standard_conversion_verilogbv + + Inputs: A typechecked expression 'expr' + + Outputs: True iff the boolean conversion is possible. + The result of the conversion is stored in 'new_expr'. + + Purpose: standard conversion for sc_logic type. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_verilogbv( + const exprt &expr, + const typet &type, + exprt &new_expr) const +{ + if(expr.get_bool(ID_C_lvalue)) + return false; + + if(expr.id()==ID_string_constant) + { + std::string value=id2string(expr.get(ID_value)); + + if(value.size()!=(unsigned)atoi(type.get(ID_width).c_str())) + return false; + + for(unsigned i = 0; i < value.size(); i++) + { + if(value[i] == '1' || value[i] == '0' || + value[i] == 'x' || value[i] == 'X' || + value[i] == 'z' || value[i] == 'Z') + { + // ok + } + else + return false; + } + + new_expr =exprt(ID_constant, type); + new_expr.set(ID_value, value); + return true; + } + + if(expr.type().id()!=ID_signedbv && + expr.type().id()!=ID_unsignedbv && + expr.type().id()!=ID_bool) + return false; + + if(expr.type().id()==ID_bool && + type.get(ID_width)==ID_1) + { + // ok + } + else if(expr.type().get(ID_width)==type.get(ID_width)) + { + // ok + } + else + return false; + + new_expr = expr; + new_expr.make_typecast(type); + + return true; +} + +#endif + +/*******************************************************************\ + +Function: standard_conversion_sequence + + Inputs: A typechecked expression 'expr', a destination + type 'type'. + + Outputs: True iff a standard conversion sequence exists. + The result of the conversion is stored in 'new_expr'. + The reference 'rank' is incremented. + + + Purpose: Standard Conversion Sequence + + A standard conversion sequence is a sequence of standard conversions + in the following order: + + * Zero or one conversion from the following set: lvalue-to-rvalue + conversion, array-to-pointer conversion, and function-to-pointer + conversion. + + * Zero or one conversion from the following set: integral + promotions, floating point promotion, integral conversions, + floating point conversions, floating-integral conversions, + pointer conversions, pointer to member conversions, and boolean + conversions. + + * Zero or one qualification conversion. + +\*******************************************************************/ + +bool cpp_typecheckt::standard_conversion_sequence( + const exprt &expr, + const typet &type, + exprt &new_expr, + unsigned &rank) +{ + assert(!is_reference(expr.type()) && !is_reference(type)); + + exprt curr_expr=expr; + + if(curr_expr.type().id()==ID_array || + curr_expr.type().id()==ID_incomplete_array) + { + if(type.id()==ID_pointer) + { + if(!standard_conversion_array_to_pointer(curr_expr, new_expr)) + return false; + } + } + else if(curr_expr.type().id()==ID_code && + type.id()==ID_pointer) + { + if(!standard_conversion_function_to_pointer(curr_expr, new_expr)) + return false; + } + else if(curr_expr.get_bool(ID_C_lvalue)) + { + if(!standard_conversion_lvalue_to_rvalue(curr_expr, new_expr)) + return false; + } + else + new_expr=curr_expr; + + curr_expr.swap(new_expr); + + // need to consider #cpp_type + if(curr_expr.type()!=type || + curr_expr.type().get(ID_C_cpp_type)!=type.get(ID_C_cpp_type)) + { + if(type.id()==ID_signedbv || + type.id() == ID_unsignedbv || + follow(type).id() == ID_c_enum) + { + if(!standard_conversion_integral_promotion(curr_expr, new_expr) || + new_expr.type() != type) + { + if(!standard_conversion_integral_conversion(curr_expr, type, new_expr)) + { + if(!standard_conversion_floating_integral_conversion(curr_expr, type, new_expr)) + return false; + } + rank+=3; + } + else + rank+=2; + } + else if(type.id() == ID_floatbv || type.id() == ID_fixedbv) + { + if(!standard_conversion_floating_point_promotion(curr_expr, new_expr) || + new_expr.type() != type) + { + if(!standard_conversion_floating_point_conversion(curr_expr, type, new_expr) && + !standard_conversion_floating_integral_conversion(curr_expr, type, new_expr)) + return false; + + rank += 3; + } + else + rank += 2; + } + else if(type.id() == ID_pointer) + { + if(!standard_conversion_pointer(curr_expr, type, new_expr)) + { + if(!standard_conversion_pointer_to_member(curr_expr, type, new_expr)) + return false; + } + rank += 3; + } + else if(type.id() == ID_bool) + { + if(!standard_conversion_boolean(curr_expr,new_expr)) + return false; + rank += 3; + } + #ifdef CPP_SYSTEMC_EXTENSION + else if(type.id() == ID_verilogbv) + { + if(!standard_conversion_verilogbv(curr_expr, type, new_expr)) + return false; + rank += 3; + } + #endif + else + return false; + } + else + new_expr = curr_expr; + + curr_expr.swap(new_expr); + + if(curr_expr.type().id() == ID_pointer) + { + typet sub_from=curr_expr.type(); + typet sub_to=type; + + bool do_qual = false; + + do + { + typet tmp_from = sub_from.subtype(); + sub_from.swap(tmp_from); + typet tmp_to = sub_to.subtype(); + sub_to.swap(tmp_to); + + c_qualifierst qual_from; + qual_from.read(sub_from); + + c_qualifierst qual_to; + qual_to.read(sub_to); + + if(qual_from!=qual_to) + { + do_qual=true; + rank+=1; + break; + } + + } + while(sub_from.id() == ID_pointer); + + if(!standard_conversion_qualification(curr_expr, type, new_expr)) + return false; + } + else + { + new_expr = curr_expr; + new_expr.type() = type; + } + + return true; +} + +/*******************************************************************\ + +Function: user_defined_conversion_sequence + + Inputs: A typechecked expression 'expr', a destination + type 'type'. + + Outputs: True iff a user-defined conversion sequence exists. + The result of the conversion is stored in 'new_expr'. + + Purpose: User-defined conversion sequence + +\*******************************************************************/ + +bool cpp_typecheckt::user_defined_conversion_sequence( + const exprt &expr, + const typet &type, + exprt &new_expr, + unsigned &rank) +{ + assert(!is_reference(expr.type())); + assert(!is_reference(type)); + + const typet &from = follow(expr.type()); + const typet &to = follow(type); + + new_expr.make_nil(); + + // special case: + // A conversion from a type to the same type is given an exact + // match rank even though a user-defined conversion is used + + if(from==to) + rank+=0; + else + rank+=4; // higher than all the standard conversions + + if(to.id()==ID_struct) + { + std::string err_msg; + + if(cpp_is_pod(to)) + { + if(from.id() == ID_struct) + { + const struct_typet &from_struct=to_struct_type(from); + const struct_typet &to_struct=to_struct_type(to); + + // potentially requires + // expr.get_bool(ID_C_lvalue) ?? + + if(subtype_typecast(from_struct, to_struct)) + { + exprt address(ID_address_of, pointer_typet()); + address.copy_to_operands(expr); + address.type().subtype() = expr.type(); + + // simplify address + if(expr.id()==ID_dereference) + address=expr.op0(); + + pointer_typet ptr_sub; + ptr_sub.subtype() = type; + c_qualifierst qual_from; + qual_from.read(expr.type()); + qual_from.write(ptr_sub.subtype()); + make_ptr_typecast(address, ptr_sub); + + exprt deref(ID_dereference); + deref.copy_to_operands(address); + deref.type() = address.type().subtype(); + + // create temporary object + exprt tmp_object_expr=exprt(ID_sideeffect, type); + tmp_object_expr.set(ID_statement, ID_temporary_object); + tmp_object_expr.location()=expr.location(); + tmp_object_expr.copy_to_operands(deref); + tmp_object_expr.set(ID_C_lvalue, true); + + new_expr.swap(tmp_object_expr); + return true; + } + } + } + else + { + struct_typet from_struct; + from_struct.make_nil(); + + if(from.id()==ID_struct) + from_struct=to_struct_type(from); + + struct_typet to_struct=to_struct_type(to); + + bool found = false; + + for(struct_typet::componentst::const_iterator + it = to_struct.components().begin(); + it != to_struct.components().end(); + it++) + { + const irept& component = *it; + + if(component.get_bool(ID_from_base)) + continue; + + if(component.get_bool("is_explicit")) + continue; + + const typet& comp_type = + static_cast(component.find(ID_type)); + + if(comp_type.id() !=ID_code) + continue; + + if(comp_type.find(ID_return_type).id() !=ID_constructor) + continue; + + // TODO: ellipsis + + const irept &arguments = comp_type.find(ID_arguments); + + if(arguments.get_sub().size() != 2) + continue; + + exprt curr_arg1 = static_cast (arguments.get_sub()[1]); + typet arg1_type = curr_arg1.type(); + + if(is_reference(arg1_type)) + { + typet tmp=arg1_type.subtype(); + arg1_type.swap(tmp); + } + + struct_typet arg1_struct; + arg1_struct.make_nil(); + { + typet tmp = follow(arg1_type); + if(tmp.id()==ID_struct) + arg1_struct = to_struct_type(tmp); + } + + unsigned tmp_rank = 0; + if(arg1_struct.is_nil()) + { + exprt tmp_expr; + if(standard_conversion_sequence(expr, arg1_type, tmp_expr, tmp_rank)) + { + // check if it's ambigious + if(found) + return false; + found = true; + + if(expr.get_bool(ID_C_lvalue)) + tmp_expr.set(ID_C_lvalue, true); + + tmp_expr.location() = expr.location(); + + exprt func_symb = cpp_symbol_expr(lookup(component.get(ID_name))); + func_symb.type() = comp_type; + { + exprt tmp("already_typechecked"); + tmp.copy_to_operands(func_symb); + func_symb.swap(func_symb); + } + + // create temporary object + side_effect_expr_function_callt ctor_expr; + ctor_expr.location() = expr.location(); + ctor_expr.function().swap(func_symb); + ctor_expr.arguments().push_back(tmp_expr); + typecheck_side_effect_function_call(ctor_expr); + + new_expr.swap(ctor_expr); + assert(new_expr.get(ID_statement)==ID_temporary_object); + + if(to.get_bool(ID_C_constant)) + new_expr.type().set(ID_C_constant, true); + + rank += tmp_rank; + } + } + else if(from_struct.is_not_nil() && arg1_struct.is_not_nil()) + { + // try derived-to-base conversion + exprt expr_pfrom(ID_address_of, pointer_typet()); + expr_pfrom.type().subtype() = expr.type(); + expr_pfrom.copy_to_operands(expr); + + pointer_typet pto; + pto.subtype() = arg1_type; + + exprt expr_ptmp; + tmp_rank = 0; + if(standard_conversion_sequence(expr_pfrom, pto, expr_ptmp, tmp_rank)) + { + // check if it's ambigious + if(found) + return false; + found = true; + + rank+=tmp_rank; + + // create temporary object + exprt expr_deref = exprt(ID_dereference, expr_ptmp.type().subtype()); + expr_deref.set(ID_C_lvalue, true); + expr_deref.copy_to_operands(expr_ptmp); + expr_deref.location() = expr.location(); + + exprt new_object("new_object", type); + new_object.set(ID_C_lvalue, true); + new_object.type().set(ID_C_constant, false); + + exprt func_symb = cpp_symbol_expr(lookup(component.get(ID_name))); + func_symb.type() = comp_type; + { + exprt tmp("already_typechecked"); + tmp.copy_to_operands(func_symb); + func_symb.swap(func_symb); + } + + side_effect_expr_function_callt ctor_expr; + ctor_expr.location() = expr.location(); + ctor_expr.function().swap(func_symb); + ctor_expr.arguments().push_back(expr_deref); + typecheck_side_effect_function_call(ctor_expr); + + new_expr.swap(ctor_expr); + + assert(new_expr.get(ID_statement)==ID_temporary_object); + + if(to.get_bool(ID_C_constant)) + new_expr.type().set(ID_C_constant, true); + } + } + } + if(found) + return true; + } + } + + // conversion operators + if(from.id()==ID_struct) + { + struct_typet from_struct = to_struct_type(from); + + bool found = false; + for(struct_typet::componentst::const_iterator + it = from_struct.components().begin(); + it != from_struct.components().end(); it++) + { + const irept& component = *it; + const typet comp_type = static_cast(component.find(ID_type)); + + if(component.get_bool(ID_from_base)) + continue; + + if(!component.get_bool("is_cast_operator")) + continue; + + assert(component.get(ID_type) == ID_code && + component.find(ID_type).find(ID_arguments).get_sub().size() == 1); + + typet this_type = + static_cast(comp_type.find(ID_arguments) + .get_sub() + .front() + .find(ID_type)); + this_type.set("#reference", true); + + exprt this_expr(expr); + this_type.set("#this", true); + + unsigned tmp_rank = 0; + exprt tmp_expr; + + if(implicit_conversion_sequence( + this_expr, this_type, tmp_expr, tmp_rank)) + { + // To take care of the possible virtual case, + // we build the function as a member expression. + irept func_name(ID_name); + func_name.set(ID_identifier, component.get(ID_base_name)); + cpp_namet cpp_func_name; + cpp_func_name.get_sub().push_back(func_name); + + exprt member_func(ID_member); + member_func.add("component_cpp_name") = cpp_func_name; + exprt ac("already_typechecked"); + ac.copy_to_operands(expr); + member_func.copy_to_operands(ac); + + side_effect_expr_function_callt func_expr; + func_expr.location() = expr.location(); + func_expr.function().swap(member_func); + typecheck_side_effect_function_call(func_expr); + + exprt tmp_expr; + if(standard_conversion_sequence(func_expr,type, tmp_expr, tmp_rank)) + { + // check if it's ambigious + if(found) + return false; + found = true; + + rank+=tmp_rank; + new_expr.swap(tmp_expr); + } + } + } + if(found) + return true; + } + + return new_expr.is_not_nil(); +} + +/*******************************************************************\ + +Function: reference_related + + Inputs: A typechecked expression 'expr', a + reference 'type'. + + Outputs: True iff an the reference 'type' is reference-related + to 'expr'. + + Purpose: Reference-related + +\*******************************************************************/ + +bool cpp_typecheckt::reference_related( + const exprt &expr, + const typet &type) const +{ + assert(is_reference(type)); + assert(!is_reference(expr.type())); + + typet from = follow(expr.type()); + typet to = follow(type.subtype()); + + // need to check #cpp_type + if(from.get(ID_C_cpp_type)!=to.get(ID_C_cpp_type)) + return false; + + if(from==to) + return true; + + if(from.id() == ID_struct && + to.id() == ID_struct) + return subtype_typecast(to_struct_type(from), + to_struct_type(to)); + + if(from.id() == ID_struct && + type.get_bool("#this") && + type.subtype().id()==ID_empty) + { + // virtual-call case + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: reference_compatible + + Inputs: A typechecked expression 'expr', a + reference 'type'. + + Outputs: True iff an the reference 'type' is reference-compatible + to 'expr'. + + Purpose: Reference-compatible + +\*******************************************************************/ + +bool cpp_typecheckt::reference_compatible( + const exprt &expr, + const typet &type, + unsigned &rank) const +{ + assert(is_reference(type)); + assert(!is_reference(expr.type())); + + if(!reference_related(expr, type)) + return false; + + if(expr.type()!=type.subtype()) + rank+=3; + + c_qualifierst qual_from; + qual_from.read(expr.type()); + + c_qualifierst qual_to; + qual_to.read(type.subtype()); + + if(qual_from!=qual_to) + rank+=1; + + if(qual_from.is_subset_of(qual_to)) + return true; + + return false; +} + +/*******************************************************************\ + +Function: reference_binding + + Inputs: A typechecked expression 'expr', a + reference 'type'. + + Outputs: True iff an the reference can be bound to the expression. + The result of the conversion is stored in 'new_expr'. + + + Purpose: Reference binding + + When a parameter of reference type binds directly (8.5.3) to an + argument expression, the implicit conversion sequence is the + identity conversion, unless the argument expression has a type + that is a derived class of the parameter type, in which case the + implicit conversion sequence is a derived-to-base Conversion + (13.3.3.1). + + If the parameter binds directly to the result of applying a + conversion function to the argument expression, the implicit + conversion sequence is a user-defined conversion sequence + (13.3.3.1.2), with the second standard conversion sequence + either an identity conversion or, if the conversion function + returns an entity of a type that is a derived class of the + parameter type, a derived-to-base Conversion. + + When a parameter of reference type is not bound directly to + an argument expression, the conversion sequence is the one + required to convert the argument expression to the underlying + type of the reference according to 13.3.3.1. Conceptually, this + conversion sequence corresponds to copy-initializing a temporary + of the underlying type with the argument expression. Any + difference in top-level cv-qualification is subsumed by the + initialization itself and does not constitute a conversion. + + A standard conversion sequence cannot be formed if it requires + binding a reference to non-const to an rvalue (except when + binding an implicit object parameter; see the special rules + for that case in 13.3.1). + +\*******************************************************************/ + +bool cpp_typecheckt::reference_binding( + exprt expr, + const typet &type, + exprt &new_expr, + unsigned &rank) +{ + assert(is_reference(type)); + assert(!is_reference(expr.type())); + + unsigned backup_rank = rank; + + if(type.get_bool("#this") && + !expr.get_bool(ID_C_lvalue)) + { + // `this' has to be an lvalue + if(expr.get(ID_statement)==ID_temporary_object) + expr.set(ID_C_lvalue, true); + else if(expr.get(ID_statement)==ID_function_call) + expr.set(ID_C_lvalue, true); + else if(expr.get_bool("#temporary_avoided")) + { + expr.remove("#temporary_avoided"); + exprt temporary; + new_temporary(expr.location(),expr.type(), expr, temporary); + expr.swap(temporary); + expr.set(ID_C_lvalue, true); + } + else + return false; + } + + if(expr.get_bool(ID_C_lvalue)) + { + if(reference_compatible(expr, type, rank)) + { + { + address_of_exprt tmp; + tmp.location()=expr.location(); + tmp.object()=expr; + tmp.type()=pointer_typet(); + tmp.type().set(ID_C_reference, true); + tmp.type().subtype()=tmp.op0().type(); + new_expr.swap(tmp); + } + + if(expr.type()!=type.subtype()) + { + c_qualifierst qual_from; + qual_from.read(expr.type()); + new_expr.make_typecast(type); + qual_from.write(new_expr.type().subtype()); + } + + return true; + } + + rank = backup_rank; + } + + // conversion operators + typet from_type = follow(expr.type()); + if(from_type.id() == ID_struct) + { + struct_typet from_struct = to_struct_type(from_type); + + for(struct_typet::componentst::const_iterator + it = from_struct.components().begin(); + it != from_struct.components().end(); it++) + { + const irept& component = *it; + + if(component.get_bool(ID_from_base)) + continue; + + if(!component.get_bool("is_cast_operator")) + continue; + + const code_typet& component_type = + to_code_type(static_cast(component.find(ID_type))); + + // otherwise it cannot bind directly (not an lvalue) + if(!is_reference(component_type.return_type())) + continue; + + assert(component_type.arguments().size() == 1); + + typet this_type = + component_type.arguments().front().type(); + this_type.set("#reference", true); + + exprt this_expr(expr); + + this_type.set("#this", true); + + unsigned tmp_rank = 0; + + exprt tmp_expr; + if(implicit_conversion_sequence( + this_expr, this_type, tmp_expr, tmp_rank)) + { + // To take care of the possible virtual case, + // we build the function as a member expression. + irept func_name(ID_name); + func_name.set(ID_identifier, component.get(ID_base_name)); + cpp_namet cpp_func_name; + cpp_func_name.get_sub().push_back(func_name); + + exprt member_func(ID_member); + member_func.add("component_cpp_name") = cpp_func_name; + exprt ac("already_typechecked"); + ac.copy_to_operands(expr); + member_func.copy_to_operands(ac); + + side_effect_expr_function_callt func_expr; + func_expr.location() = expr.location(); + func_expr.function().swap(member_func); + typecheck_side_effect_function_call(func_expr); + + // let's check if the returned value binds directly + exprt returned_value = func_expr; + add_implicit_dereference(returned_value); + + if(returned_value.get_bool(ID_C_lvalue) + && reference_compatible(returned_value,type, rank)) + { + // returned values are lvalues in case of references only + assert(returned_value.id() == ID_dereference && + is_reference(returned_value.op0().type())); + + new_expr = returned_value.op0(); + + if(returned_value.type() != type.subtype()) + { + c_qualifierst qual_from; + qual_from.read(returned_value.type()); + make_ptr_typecast(new_expr,type); + qual_from.write(new_expr.type().subtype()); + } + rank+=4+tmp_rank; + return true; + } + } + } + } + + // No temporary allowed for `this' + if(type.get_bool("#this")) + return false; + + if(!type.subtype().get_bool(ID_C_constant) || + type.subtype().get_bool("#volatile")) + return false; + + // TODO: hanlde the case for implicit parameters + if(!type.subtype().get_bool(ID_C_constant) && + !expr.get_bool(ID_C_lvalue)) + return false; + + exprt arg_expr = expr; + + if(follow(arg_expr.type()).id() == ID_struct) + { + // required to initialize the temporary + arg_expr.set(ID_C_lvalue, true); + } + + if(user_defined_conversion_sequence(arg_expr,type.subtype(), new_expr, rank)) + { + address_of_exprt tmp; + tmp.type()=pointer_typet(); + tmp.object()=new_expr; + tmp.type().set(ID_C_reference, true); + tmp.type().subtype()= new_expr.type(); + tmp.location()=new_expr.location(); + new_expr.swap(tmp); + return true; + } + + rank = backup_rank; + if(standard_conversion_sequence(expr,type.subtype(),new_expr,rank)) + { + { + // create temporary object + exprt tmp=exprt(ID_sideeffect, type.subtype()); + tmp.set(ID_statement, ID_temporary_object); + tmp.location()=expr.location(); + //tmp.set(ID_C_lvalue, true); + tmp.move_to_operands(new_expr); + new_expr.swap(tmp); + } + + exprt tmp(ID_address_of, pointer_typet()); + tmp.copy_to_operands(new_expr); + tmp.type().set(ID_C_reference, true); + tmp.type().subtype()= new_expr.type(); + tmp.location()=new_expr.location(); + new_expr.swap(tmp); + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: implicit_conversion_sequence + + Inputs: A typechecked expression 'expr', a destination + type 'type'. + + Outputs: True iff an implicit conversion sequence exists. + The result of the conversion is stored in 'new_expr'. + The rank of the sequence is stored in 'rank' + + Purpose: implicit conversion sequence + +\*******************************************************************/ + +bool cpp_typecheckt::implicit_conversion_sequence( + const exprt &expr, + const typet &type, + exprt &new_expr, + unsigned &rank) +{ + unsigned backup_rank = rank; + + exprt e=expr; + add_implicit_dereference(e); + + if(is_reference(type)) + { + if(!reference_binding(e, type, new_expr, rank)) + return false; + + #if 0 + simplify_exprt simplify(*this); + simplify.simplify(new_expr); + new_expr.type().set(ID_C_reference, true); + #endif + } + else if(!standard_conversion_sequence(e, type, new_expr, rank)) + { + rank = backup_rank; + if(!user_defined_conversion_sequence(e, type, new_expr, rank)) + return false; + + #if 0 + simplify_exprt simplify(*this); + simplify.simplify(new_expr); + #endif + } + + return true; +} + +/*******************************************************************\ + +Function: implicit_conversion_sequence + + Inputs: A typechecked expression 'expr', a destination + type 'type'. + + Outputs: True iff an implicit conversion sequence exists. + The result of the conversion is stored in 'new_expr'. + + Purpose: implicit conversion sequence + +\*******************************************************************/ + +bool cpp_typecheckt::implicit_conversion_sequence( + const exprt &expr, + const typet &type, + exprt &new_expr) +{ + unsigned rank = 0; + return implicit_conversion_sequence(expr, type, new_expr, rank); +} + +/*******************************************************************\ + +Function: implicit_conversion_sequence + + Inputs: A typechecked expression 'expr', a destination + type 'type'. + + Outputs: True iff an implicit conversion sequence exists. + The rank of the sequence is stored in 'rank' + + + Purpose: implicit conversion sequence + + +\*******************************************************************/ + +bool cpp_typecheckt::implicit_conversion_sequence( + const exprt &expr, + const typet &type, + unsigned &rank) +{ + exprt new_expr; + return implicit_conversion_sequence(expr, type, new_expr, rank); +} + +/*******************************************************************\ + +Function: cpp_typecheck_baset::implicit_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::implicit_typecast(exprt &expr, const typet &type) +{ + exprt e = expr; + + if(!implicit_conversion_sequence(e, type, expr)) + { + show_instantiation_stack(str); + err_location(e); + str << "invalid implicit conversion from `" + << to_string(e.type()) << "' to `" + << to_string(type) << "' "; + throw 0; + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_baset::reference_initializer + + Inputs: + + Outputs: + + Purpose: + + A reference to type "cv1 T1" is initialized by an expression of + type "cv2 T2" as follows: + + - If the initializer expression + - is an lvalue (but is not a bit-field), and "cv1 T1" is + reference-compatible with "cv2 T2," or + - has a class type (i.e., T2 is a class type) and can be + implicitly converted to an lvalue of type "cv3 T3," where + "cv1 T1" is reference-compatible with "cv3 T3" 92) (this + conversion is selected by enumerating the applicable conversion + functions (13.3.1.6) and choosing the best one through overload + resolution (13.3)), + + then the reference is bound directly to the initializer + expression lvalue in the first case, and the reference is + bound to the lvalue result of the conversion in the second + case. In these cases the reference is said to bind directly + to the initializer expression. + + - Otherwise, the reference shall be to a non-volatile const type + - If the initializer expression is an rvalue, with T2 a class + type, and "cv1 T1" is reference-compatible with "cv2 T2," the + reference is bound in one of the following ways (the choice is + implementation-defined): + + - The reference is bound to the object represented by the + rvalue (see 3.10) or to a sub-object within that object. + + - A temporary of type "cv1 T2" [sic] is created, and a + constructor is called to copy the entire rvalue object into + the temporary. The reference is bound to the temporary or + to a sub-object within the temporary. + + The constructor that would be used to make the copy shall be + callable whether or not the copy is actually done. + + Otherwise, a temporary of type "cv1 T1" is created and + initialized from the initializer expression using the rules for + a non-reference copy initialization (8.5). The reference is then + bound to the temporary. If T1 is reference-related to T2, cv1 + must be the same cv-qualification as, or greater cvqualification + than, cv2; otherwise, the program is ill-formed. + +\*******************************************************************/ + +void cpp_typecheckt::reference_initializer( + exprt &expr, + const typet &type) +{ + assert(is_reference(type)); + add_implicit_dereference(expr); + + unsigned rank=0; + exprt new_expr; + if(reference_binding(expr,type,new_expr,rank)) + { + expr.swap(new_expr); + return; + } + + err_location(expr); + str << "bad reference initializer"; + throw 0; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::cast_away_constness + + Inputs: + + Outputs: + + Purpose: + + +\*******************************************************************/ + +bool cpp_typecheckt::cast_away_constness( + const typet &t1, + const typet &t2) const +{ + assert(t1.id() == ID_pointer && t2.id() == ID_pointer); + typet nt1 = t1; + typet nt2 = t2; + + if(is_reference(nt1)) + nt1.remove(ID_C_reference); + + if(is_reference(nt2)) + nt2.remove(ID_C_reference); + + // substitute final subtypes + std::vector snt1; + snt1.push_back(nt1); + + while(snt1.back().find(ID_subtype).is_not_nil()) + { + snt1.reserve(snt1.size()+1); + snt1.push_back(snt1.back().subtype()); + } + + c_qualifierst q1; + q1.read(snt1.back()); + + bool_typet newnt1; + q1.write(newnt1); + snt1.back() = newnt1; + + std::vector snt2; + snt2.push_back(nt2); + while(snt2.back().find(ID_subtype).is_not_nil()) + { + snt2.reserve(snt2.size()+1); + snt2.push_back(snt2.back().subtype()); + } + + c_qualifierst q2; + q2.read(snt2.back()); + + bool_typet newnt2; + q2.write(newnt2); + snt2.back() = newnt2; + + const int k = snt1.size() < snt2.size() ? snt1.size() : snt2.size(); + + for(int i = k; i > 1; i--) + { + snt1[snt1.size()-2].subtype() = snt1[snt1.size()-1]; + snt1.pop_back(); + + snt2[snt2.size()-2].subtype() = snt2[snt2.size()-1]; + snt2.pop_back(); + } + + exprt e1("Dummy", snt1.back()); + exprt e2; + + return !standard_conversion_qualification(e1, snt2.back(), e2); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::const_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::const_typecast( + const exprt &expr, + const typet &type, + exprt &new_expr) +{ + assert(is_reference(expr.type())==false); + + exprt curr_expr = expr; + + if(curr_expr.type().id()==ID_array || + curr_expr.type().id()==ID_incomplete_array) + { + if(type.id()==ID_pointer) + { + if(!standard_conversion_array_to_pointer(curr_expr, new_expr)) + return false; + } + } + else if(curr_expr.type().id()==ID_code && + type.id() == ID_pointer) + { + if(!standard_conversion_function_to_pointer(curr_expr, new_expr)) + return false; + } + else if(curr_expr.get_bool(ID_C_lvalue)) + { + if(!standard_conversion_lvalue_to_rvalue(curr_expr, new_expr)) + return false; + } + else + new_expr = curr_expr; + + if(is_reference(type)) + { + if(!expr.get_bool(ID_C_lvalue)) + return false; + + if(new_expr.type()!=type.subtype()) + return false; + + exprt address_of(ID_address_of, type); + address_of.copy_to_operands(expr); + add_implicit_dereference(address_of); + new_expr.swap(address_of); + return true; + } + else if(type.id()==ID_pointer) + { + if(type!=new_expr.type()) + return false; + + // add proper typecast + typecast_exprt typecast_expr(expr, type); + new_expr.swap(typecast_expr); + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::dynamic_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::dynamic_typecast( + const exprt &expr, + const typet &type, + exprt &new_expr) +{ + exprt e(expr); + + if(type.id() == ID_pointer) + { + if(e.id() == ID_dereference && e.get_bool(ID_C_implicit)) + e = expr.op0(); + + if(e.type().id() == ID_pointer && + cast_away_constness(e.type(), type)) + return false; + } + + add_implicit_dereference(e); + + if(is_reference(type)) + { + if(follow(type.subtype()).id() != ID_struct) + return false; + } + else if(type.id()==ID_pointer) + { + if(type.find("to-member").is_not_nil()) + return false; + + if(type.subtype().id()==ID_empty) + { + if(!e.get_bool(ID_C_lvalue)) + return false; + assert(0); // currently not supported + } + else if(follow(type.subtype()).id()==ID_struct) + { + if(e.get_bool(ID_C_lvalue)) + { + exprt tmp(e); + + if(!standard_conversion_lvalue_to_rvalue(tmp,e)) + return false; + } + } + else return false; + } + else return false; + + return static_typecast(e,type, new_expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::reinterpret_typecastcast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::reinterpret_typecast( + const exprt &expr, + const typet &type, + exprt &new_expr, + bool check_constantness) +{ + exprt e=expr; + + if(check_constantness && type.id()==ID_pointer) + { + if(e.id() == ID_dereference && e.get_bool(ID_C_implicit)) + e = expr.op0(); + + if(e.type().id() == ID_pointer && + cast_away_constness(e.type(), type)) + return false; + } + + add_implicit_dereference(e); + + if(!is_reference(type)) + { + exprt tmp; + + if(e.id()==ID_code) + { + if(standard_conversion_function_to_pointer(e,tmp)) + e.swap(tmp); + else + return false; + } + + if(e.type().id() == ID_array || + e.type().id() == ID_incomplete_array ) + { + if(standard_conversion_array_to_pointer(e,tmp)) + e.swap(tmp); + else + return false; + } + + if(e.get_bool(ID_C_lvalue)) + { + if(standard_conversion_lvalue_to_rvalue(e,tmp)) + e.swap(tmp); + else + return false; + } + } + + if(e.type().id() == ID_pointer && + (type.id()==ID_unsignedbv || type.id() == ID_signedbv)) + { + // pointer to integer, always ok + new_expr=e; + new_expr.make_typecast(type); + return true; + } + + if((e.type().id() == ID_unsignedbv || + e.type().id() == ID_signedbv || + e.type().id() == ID_bool) + && type.id() == ID_pointer + && !is_reference(type)) + { + // integer to pointer + if(e.is_zero()) + { + // NULL + new_expr = e; + new_expr.set(ID_value, ID_NULL); + new_expr.type() = type; + } + else + { + new_expr = e; + new_expr.make_typecast(type); + } + return true; + } + + if(e.type().id() == ID_pointer && + type.id() == ID_pointer && + !is_reference(type)) + { + if(e.type().subtype().id() == ID_code + && type.subtype().id() != ID_code ) + return false; + else if (e.type().subtype().id() != ID_code + && type.subtype().id() == ID_code ) + return false; + + // this is more generous than the standard + new_expr = expr; + new_expr.make_typecast(type); + return true; + } + + if(is_reference(type) && e.get_bool(ID_C_lvalue)) + { + exprt tmp(ID_address_of, pointer_typet()); + tmp.type().subtype() = e.type(); + tmp.copy_to_operands(e); + tmp.make_typecast(type); + new_expr.swap(tmp); + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::static_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::static_typecast( + const exprt &expr, + const typet &type, + exprt &new_expr, + bool check_constantness) +{ + exprt e=expr; + + if(check_constantness && type.id() == ID_pointer) + { + if(e.id() == ID_dereference && e.get_bool(ID_C_implicit)) + e = expr.op0(); + + if(e.type().id() == ID_pointer && + cast_away_constness(e.type(), type)) + return false; + } + + add_implicit_dereference(e); + + if(type.get_bool("#reference")) + { + unsigned rank=0; + if(reference_binding(e,type,new_expr,rank)) + return true; + + typet subto = follow(type.subtype()); + typet from = follow(e.type()); + + if(subto.id() == ID_struct && from.id() == ID_struct) + { + if(!expr.get_bool(ID_C_lvalue)) + return false; + + c_qualifierst qual_from; + qual_from.read(e.type()); + + c_qualifierst qual_to; + qual_to.read(type.subtype()); + + if(!qual_to.is_subset_of(qual_from)) + return false; + + struct_typet from_struct = to_struct_type(from); + struct_typet subto_struct = to_struct_type(subto); + + if(subtype_typecast(subto_struct, from_struct)) + { + if(e.id() == ID_dereference) + { + make_ptr_typecast(e.op0(),type); + new_expr.swap(e.op0()); + return true; + } + + exprt address_of(ID_address_of, pointer_typet()); + address_of.type().subtype() == e.type(); + address_of.copy_to_operands(e); + make_ptr_typecast(address_of ,type); + new_expr.swap(address_of); + return true; + } + } + return false; + } + + if(type.id() == ID_empty) + { + new_expr = e; + new_expr.make_typecast(type); + return true; + } + + if (follow(type).id() == ID_c_enum + && (e.type().id() == ID_signedbv + || e.type().id() == ID_unsignedbv + || follow(e.type()).id() == ID_c_enum)) + { + new_expr = e; + new_expr.make_typecast(type); + if(new_expr.get_bool(ID_C_lvalue)) + new_expr.remove(ID_C_lvalue); + return true; + } + + if(implicit_conversion_sequence(e, type, new_expr)) + { + if(!cpp_is_pod(type)) + { + exprt tc("already_typechecked"); + tc.copy_to_operands(new_expr); + exprt temporary; + new_temporary(e.location(), type, tc, temporary); + new_expr.swap(temporary); + } + else + { + // try to avoid temporary + new_expr.set("#temporary_avoided", true); + if(new_expr.get_bool(ID_C_lvalue)) + new_expr.remove(ID_C_lvalue); + } + + return true; + } + + #ifdef CPP_SYSTEMC_EXTENSION + if(type.id() == ID_unsignedbv && + e.type().id() == ID_verilogbv && + type.get(ID_width) == e.type().get(ID_width)) + { + new_expr = e; + new_expr.make_typecast(type); + return true; + } + else if(type.id() == ID_bool && + e.type().id() == ID_verilogbv && + e.type().get(ID_width) == ID_1) + { + new_expr = e; + new_expr.make_typecast(type); + return true; + } + #endif + + if(type.id() == ID_pointer && e.type().id() == ID_pointer) + { + if(type.find("to-member").is_nil() + && e.type().find("to-member").is_nil()) + { + typet to = follow(type.subtype()); + typet from = follow(e.type().subtype()); + + if(from.id() == ID_empty) + { + e.make_typecast(type); + new_expr.swap(e); + return true; + } + + if(to.id() == ID_struct && from.id() == ID_struct) + { + + if(e.get_bool(ID_C_lvalue)) + { + exprt tmp(e); + if(!standard_conversion_lvalue_to_rvalue(tmp,e)) + return false; + } + + struct_typet from_struct = to_struct_type(from); + struct_typet to_struct = to_struct_type(to); + if(subtype_typecast(to_struct, from_struct)) + { + make_ptr_typecast(e,type); + new_expr.swap(e); + return true; + } + } + + return false; + } + else if (type.find("to-member").is_not_nil() + && e.type().find("to-member").is_not_nil()) + { + if(type.subtype() != e.type().subtype()) + return false; + + struct_typet from_struct = + to_struct_type(follow(static_cast(e.type().find("to-member")))); + + struct_typet to_struct = + to_struct_type(follow(static_cast(type.find("to-member")))); + + if(subtype_typecast(from_struct, to_struct)) + { + new_expr = e; + new_expr.make_typecast(type); + return true; + } + } + else + return false; + } + + return false; +} diff --git a/src/cpp/cpp_typecheck_declaration.cpp b/src/cpp/cpp_typecheck_declaration.cpp new file mode 100644 index 00000000000..bc26e6e6443 --- /dev/null +++ b/src/cpp/cpp_typecheck_declaration.cpp @@ -0,0 +1,232 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\********************************************************************/ + +#include +#include + +#include "cpp_typecheck.h" +#include "cpp_declarator_converter.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_typedef + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::convert_typedef(typet &type) +{ + if(type.id()==ID_merged_type && + type.subtypes().size()>=2 && + type.subtypes()[0].id()==ID_typedef) + { + type.subtypes().erase(type.subtypes().begin()); + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_anonymous_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_anonymous_union( + cpp_declarationt &declaration, + codet &code) +{ + codet new_code(ID_decl_block); + new_code.reserve_operands(declaration.declarators().size()); + + // unnamed object + std::string identifier = "#anon"+i2string(anon_counter++); + + irept name(ID_name); + name.set(ID_identifier, identifier); + name.set(ID_C_location, declaration.location()); + + cpp_namet cpp_name; + cpp_name.move_to_sub(name); + cpp_declaratort declarator; + declarator.name()=cpp_name; + + cpp_declarator_convertert cpp_declarator_converter(*this); + + const symbolt &symbol= + cpp_declarator_converter.convert(declaration, declarator); + + if(!cpp_is_pod(declaration.type())) + { + err_location(follow(declaration.type()).location()); + str << "anonymous union is not POD"; + throw 0; + } + + codet decl_statement(ID_decl); + decl_statement.reserve_operands(2); + decl_statement.copy_to_operands(cpp_symbol_expr(symbol)); + + new_code.move_to_operands(decl_statement); + + // do scoping + symbolt union_symbol=context.symbols[follow(symbol.type).get(ID_name)]; + const irept::subt &components=union_symbol.type.add(ID_components).get_sub(); + + forall_irep(it, components) + { + if(it->find(ID_type).id()==ID_code) + { + err_location(union_symbol.type.location()); + str << "anonymous union `" << union_symbol.base_name + << "' shall not have function members\n"; + throw 0; + } + + const irep_idt &base_name=it->get(ID_base_name); + + if(cpp_scopes.current_scope().contains(base_name)) + { + err_location(union_symbol.type.location()); + str << "identifier `" << base_name << "' already in scope"; + throw 0; + } + + cpp_idt &id=cpp_scopes.current_scope().insert(base_name); + id.id_class = cpp_idt::SYMBOL; + id.identifier=it->get(ID_name); + id.class_identifier=union_symbol.name; + id.is_member=true; + } + + context.symbols[union_symbol.name].type.set("#unnamed_object", symbol.base_name); + + code.swap(new_code); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert(cpp_declarationt &declaration) +{ + // see if the declaration is empty + if(declaration.find(ID_type).is_nil() && + !declaration.has_operands()) + return; + + // Record the function bodies so we can check them later. + // This function is used recursively, so we save them. + function_bodiest old_function_bodies=function_bodies; + function_bodies.clear(); + + // templates are done in a dedicated function + if(declaration.is_template()) + convert_template_declaration(declaration); + else + convert_non_template_declaration(declaration); + + typecheck_function_bodies(); + function_bodies=old_function_bodies; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_non_template_declaration + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_non_template_declaration( + cpp_declarationt &declaration) +{ + assert(!declaration.is_template()); + + // do the first part, the type + typet &type=declaration.type(); + bool is_typedef=convert_typedef(type); + + typecheck_type(type); + + if(declaration.declarators().empty() && + follow(declaration.type()).get_bool("#is_anonymous")) + { + typet final_type=follow(declaration.type()); + + if(final_type.id()!=ID_union) + { + err_location(final_type.location()); + throw "top-level declaration does not declare anything"; + } + + codet dummy; + convert_anonymous_union(declaration, dummy); + } + + // do the declarators (optional) + Forall_cpp_declarators(it, declaration) + { + // copy the declarator (we destroy the original) + cpp_declaratort declarator=*it; + + cpp_declarator_convertert cpp_declarator_converter(*this); + + cpp_declarator_converter.is_typedef=is_typedef; + + symbolt &symbol=cpp_declarator_converter.convert( + type, declaration.storage_spec(), + declaration.member_spec(), declarator); + + // any template instance to remember? + if(declaration.find(ID_C_template).is_not_nil()) + { + symbol.type.set(ID_C_template, declaration.find(ID_C_template)); + symbol.type.set(ID_C_template_arguments, declaration.find(ID_C_template_arguments)); + } + + // replace declarator by symbol expression + exprt tmp=cpp_symbol_expr(symbol); + it->swap(tmp); + + // is there a constructor to be called for the declarator? + if(symbol.lvalue && + declarator.init_args().has_operands()) + { + symbol.value= + cpp_constructor( + symbol.location, + cpp_symbol_expr(symbol), + declarator.init_args().operands()); + } + } +} diff --git a/src/cpp/cpp_typecheck_enum_type.cpp b/src/cpp/cpp_typecheck_enum_type.cpp new file mode 100644 index 00000000000..a4707058f2b --- /dev/null +++ b/src/cpp/cpp_typecheck_enum_type.cpp @@ -0,0 +1,183 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include "cpp_typecheck.h" +#include "cpp_enum_type.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_enum_body + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_enum_body(symbolt &enum_symbol) +{ + typet &type=enum_symbol.type; + + exprt &body=static_cast(type.add(ID_body)); + irept::subt &components=body.get_sub(); + + typet enum_type(ID_symbol); + enum_type.set(ID_identifier, enum_symbol.name); + + mp_integer i=0; + + Forall_irep(it, components) + { + const irep_idt &name=it->get(ID_name); + + if(it->find(ID_value).is_not_nil()) + { + exprt &value=static_cast(it->add(ID_value)); + typecheck_expr(value); + make_constant_index(value); + if(to_integer(value, i)) + throw "failed to produce integer for enum"; + } + + exprt final_value(ID_constant, enum_type); + final_value.set(ID_value, integer2string(i)); + + symbolt symbol; + + symbol.name=id2string(enum_symbol.name)+"::"+id2string(name); + symbol.base_name=name; + symbol.value.swap(final_value); + symbol.location=static_cast(it->find(ID_C_location)); + symbol.mode=current_mode; + symbol.module=module; + symbol.type=enum_type; + symbol.is_type=false; + symbol.is_macro=true; + + symbolt *new_symbol; + if(context.move(symbol, new_symbol)) + throw "cpp_typecheckt::typecheck_enum_body: context.move() failed"; + + cpp_idt &scope_identifier= + cpp_scopes.put_into_scope(*new_symbol); + + scope_identifier.id_class=cpp_idt::SYMBOL; + + ++i; + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_enum_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_enum_type(typet &type) +{ + // first save qualifiers + c_qualifierst qualifiers; + qualifiers.read(type); + + // these behave like special struct types + // replace by type symbol + + cpp_enum_typet &enum_type=to_cpp_enum_type(type); + + bool has_body=enum_type.has_body(); + std::string base_name=id2string(enum_type.get_name()); + bool anonymous=base_name.empty(); + bool tag_only_declaration=enum_type.get_tag_only_declaration(); + + if(anonymous) + base_name="#anon"+i2string(anon_counter++); + + cpp_scopet &dest_scope= + tag_scope(base_name, base_name, has_body, tag_only_declaration); + + const irep_idt symbol_name= + dest_scope.prefix+"tag."+base_name; + + // check if we have it + + contextt::symbolst::iterator previous_symbol= + context.symbols.find(symbol_name); + + if(previous_symbol!=context.symbols.end()) + { + // we do! + + symbolt &symbol=previous_symbol->second; + + if(has_body) + { + err_location(type); + str << "error: enum symbol `" << base_name + << "' declared previously" << std::endl; + str << "location of previous definition: " + << symbol.location << std::endl; + throw 0; + } + } + else if(has_body) + { + std::string pretty_name= + cpp_scopes.current_scope().prefix+base_name; + + symbolt symbol; + + symbol.name=symbol_name; + symbol.base_name=base_name; + symbol.value.make_nil(); + symbol.location=type.location(); + symbol.mode=current_mode; + symbol.module=module; + symbol.type.swap(type); + symbol.is_type=true; + symbol.is_macro=false; + symbol.pretty_name=pretty_name; + + // move early, must be visible before doing body + symbolt *new_symbol; + if(context.move(symbol, new_symbol)) + throw "cpp_typecheckt::typecheck_enum_type: context.move() failed"; + + // put into scope + cpp_idt &scope_identifier= + cpp_scopes.put_into_scope(*new_symbol, dest_scope); + + scope_identifier.id_class=cpp_idt::CLASS; + + typecheck_enum_body(*new_symbol); + } + else + { + err_location(type); + str << "use of enum `" << base_name + << "' without previous declaration"; + throw 0; + } + + // create type symbol + type=typet(ID_symbol); + type.set(ID_identifier, symbol_name); + qualifiers.write(type); +} diff --git a/src/cpp/cpp_typecheck_expr.cpp b/src/cpp/cpp_typecheck_expr.cpp new file mode 100644 index 00000000000..9a0d9e63d10 --- /dev/null +++ b/src/cpp/cpp_typecheck_expr.cpp @@ -0,0 +1,3095 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cpp_type2name.h" +#include "cpp_typecheck.h" +#include "cpp_convert_type.h" +#include "cpp_class_type.h" +#include "expr2cpp.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::find_parent + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::find_parent( + const symbolt& symb, + const irep_idt &base_name, + irep_idt &identifier) +{ + forall_irep(bit, symb.type.find(ID_bases).get_sub()) + { + if(lookup(bit->find(ID_type).get(ID_identifier)).base_name == base_name) + { + identifier = bit->find(ID_type).get(ID_identifier); + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_main + +Inputs: + +Outputs: + +Purpose: Called after the operands are done + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_main(exprt &expr) +{ + if(expr.id()==ID_cpp_name) + typecheck_expr_cpp_name(expr, cpp_typecheck_fargst()); + else if(expr.id()=="cpp-this") + typecheck_expr_this(expr); + else if(expr.id()=="pointer-to-member") + convert_pmop(expr); + else if(expr.id()=="new_object") + { + } + else if(operator_is_overloaded(expr)) + { + } + else if(expr.id()=="explicit-typecast") + typecheck_expr_explicit_typecast(expr); + else if(expr.id()=="explicit-constructor-call") + typecheck_expr_explicit_constructor_call(expr); + else if(expr.id()==ID_string_constant) + { + c_typecheck_baset::typecheck_expr_main(expr); + + // we need to adjust the subtype to 'char' + assert(expr.type().id()==ID_array); + expr.type().subtype().set(ID_C_cpp_type, ID_char); + } + else if(expr.is_nil()) + { + std::cerr << "cpp_typecheckt::typecheck_expr_main got nil" << std::endl; + abort(); + } + else if(expr.id()==ID_code) + { + std::cerr << "cpp_typecheckt::typecheck_expr_main got code" << std::endl; + abort(); + } + else if(expr.id()==ID_symbol) + { + #if 0 + std::cout << "E: " << expr.pretty() << std::endl; + std::cerr << "cpp_typecheckt::typecheck_expr_main got symbol" << std::endl; + abort(); + #endif + } + else if(expr.id()=="__is_base_of") + { + // an MS extension + // http://msdn.microsoft.com/en-us/library/ms177194(v=vs.80).aspx + + typet base=static_cast(expr.find("type_arg1")); + typet deriv=static_cast(expr.find("type_arg2")); + + typecheck_type(base); + typecheck_type(deriv); + + follow_symbol(base); + follow_symbol(deriv); + + if(base.id()!=ID_struct || deriv.id()!=ID_struct) + expr.make_false(); + else + { + irep_idt base_name=base.get(ID_name); + const class_typet &class_type=to_class_type(deriv); + + if(class_type.has_base(base_name)) + expr.make_true(); + else + expr.make_false(); + } + } + else + c_typecheck_baset::typecheck_expr_main(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_trinary + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_trinary(if_exprt &expr) +{ + assert(expr.operands().size()==3); + + implicit_typecast(expr.op0(), bool_typet()); + + if(expr.op1().type().id()==ID_empty || + expr.op1().type().id()==ID_empty) + { + if(expr.op1().get_bool(ID_C_lvalue)) + { + exprt e1(expr.op1()); + if(!standard_conversion_lvalue_to_rvalue(e1, expr.op1())) + { + err_location(e1); + str << "error: lvalue to rvalue conversion"; + throw 0; + } + } + + if(expr.op1().type().id()==ID_array) + { + exprt e1(expr.op1()); + if(!standard_conversion_array_to_pointer(e1, expr.op1())) + { + err_location(e1); + str << "error: array to pointer conversion"; + throw 0; + } + } + + if(expr.op1().type().id()==ID_code) + { + exprt e1(expr.op1()); + if(!standard_conversion_function_to_pointer(e1, expr.op1())) + { + err_location(e1); + str << "error: function to pointer conversion"; + throw 0; + } + } + + if(expr.op2().get_bool(ID_C_lvalue)) + { + exprt e2(expr.op2()); + if(!standard_conversion_lvalue_to_rvalue(e2, expr.op2())) + { + err_location(e2); + str << "error: lvalue to rvalue conversion"; + throw 0; + } + } + + if(expr.op2().type().id()==ID_array) + { + exprt e2(expr.op2()); + if(!standard_conversion_array_to_pointer(e2, expr.op2())) + { + err_location(e2); + str << "error: array to pointer conversion"; + throw 0; + } + } + + if(expr.op2().type().id()==ID_code) + { + exprt e2(expr.op2()); + if(!standard_conversion_function_to_pointer(e2, expr.op2())) + { + err_location(expr); + str << "error: function to pointer conversion"; + throw 0; + } + } + + if(expr.op1().get(ID_statement)==ID_throw && + expr.op2().get(ID_statement)!=ID_throw) + expr.type()=expr.op2().type(); + else if(expr.op2().get(ID_statement)==ID_throw && + expr.op1().get(ID_statement)!=ID_throw) + expr.type()=expr.op1().type(); + else if(expr.op1().type().id()==ID_empty && + expr.op2().type().id()==ID_empty) + expr.type()=empty_typet(); + else + { + err_location(expr); + str << "error: bad types for operands"; + throw 0; + } + return; + } + + if(expr.op1().type() == expr.op2().type()) + { + c_qualifierst qual1, qual2; + qual1.read(expr.op1().type()); + qual2.read(expr.op2().type()); + + if(qual1.is_subset_of(qual2)) + expr.type() = expr.op1().type(); + else + expr.type() = expr.op2().type(); + } + else + { + exprt e1 = expr.op1(); + exprt e2 = expr.op2(); + + if(implicit_conversion_sequence(expr.op1(), expr.op2().type(), e1)) + { + expr.type()=e1.type(); + expr.op1().swap(e1); + } + else if(implicit_conversion_sequence(expr.op2(),expr.op1().type(), e2)) + { + expr.type()=e2.type(); + expr.op2().swap(e2); + } + else if(expr.op1().type().id()==ID_array && + expr.op2().type().id()==ID_array && + expr.op1().type().subtype() == expr.op2().type().subtype()) + { + // array-to-pointer conversion + + index_exprt index1; + index1.array() = expr.op1(); + index1.index() = from_integer(0,int_type()); + index1.type() = expr.op1().type().subtype(); + + index_exprt index2; + index2.array() = expr.op2(); + index2.index() = from_integer(0,int_type()); + index2.type() = expr.op2().type().subtype(); + + address_of_exprt addr1(index1); + address_of_exprt addr2(index2); + + expr.op1() = addr1; + expr.op2() = addr2; + expr.type() = addr1.type(); + return; + } + else + { + err_location(expr); + str << "error: types are incompatible.\n" + << "I got `" << type2cpp(expr.op1().type(), *this) + << "' and `" << type2cpp(expr.op2().type(), *this) + << "'."; + throw 0; + } + } + + if(expr.op1().get_bool(ID_C_lvalue) && + expr.op2().get_bool(ID_C_lvalue)) + expr.set(ID_C_lvalue, true); + + return; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_member + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_member(exprt &expr) +{ + typecheck_expr_member(expr, cpp_typecheck_fargst()); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_sizeof + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_sizeof(exprt &expr) +{ + // we need to overload, as "sizeof-type" may be + // parsed as an expression! + + if(expr.operands().size()==0) + { + const typet &type= + static_cast(expr.find(ID_type_arg)); + + if(type.id()==ID_cpp_name) + { + // this may be ambiguous -- it can be either a type or + // an expression + + cpp_typecheck_fargst fargs; + + exprt symbol_expr=resolve( + to_cpp_name(static_cast(type)), + cpp_typecheck_resolvet::BOTH, + fargs); + + if(symbol_expr.id()!=ID_type) + { + expr.copy_to_operands(symbol_expr); + expr.remove(ID_type_arg); + } + } + } + + c_typecheck_baset::typecheck_expr_sizeof(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_ptrmember + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_ptrmember(exprt &expr) +{ + typecheck_expr_ptrmember(expr, cpp_typecheck_fargst()); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_function_expr + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_function_expr( + exprt &expr, + const cpp_typecheck_fargst &fargs) +{ + if(expr.id()==ID_cpp_name) + typecheck_expr_cpp_name(expr, fargs); + else if(expr.id()==ID_member) + { + typecheck_expr_operands(expr); + typecheck_expr_member(expr, fargs); + } + else if(expr.id()==ID_ptrmember) + { + typecheck_expr_operands(expr); + add_implicit_dereference(expr.op0()); + + // is operator-> overloaded? + if(expr.op0().type().id() != ID_pointer) + { + std::string op_name="operator->"; + + // turn this into a function call + side_effect_expr_function_callt functioncall; + functioncall.arguments().reserve(expr.operands().size()); + functioncall.location()=expr.location(); + + // first do function/operator + cpp_namet cpp_name; + cpp_name.get_sub().push_back(irept(ID_name)); + cpp_name.get_sub().back().set(ID_identifier, op_name); + cpp_name.get_sub().back().add(ID_C_location)=expr.location(); + + functioncall.function()= + static_cast( + static_cast(cpp_name)); + + // now do the argument + functioncall.arguments().push_back(expr.op0()); + typecheck_side_effect_function_call(functioncall); + + exprt tmp("already_typechecked"); + tmp.copy_to_operands(functioncall); + functioncall.swap(tmp); + + expr.op0().swap(functioncall); + typecheck_function_expr(expr, fargs); + return; + } + + typecheck_expr_ptrmember(expr, fargs); + } + else + typecheck_expr(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::overloadable + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +bool cpp_typecheckt::overloadable(const exprt &expr) +{ + forall_operands(it, expr) + { + typet t(it->type()); + + follow_symbol(t); + + if(is_reference(t)) + t=t.subtype(); + + if(t.id()==ID_struct || + t.id()==ID_union) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::operator_is_overloaded + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +struct operator_entryt +{ + const char *id_name; + const char *op_name; +} const operators[] = +{ + { "+", "+" }, + { "-", "-" }, + { "*", "*" }, + { "/", "/" }, + { "bitnot", "~" }, + { "bitand", "&" }, + { "bitor", "|" }, + { "bitxor", "^" }, + { "not", "!" }, + { "unary-", "-" }, + { "and", "&&" }, + { "or", "||" }, + { "not", "!" }, + { "index", "[]" }, + { "=", "==" }, + { "<", "<"}, + { "<=", "<="}, + { ">", ">"}, + { ">=", ">="}, + { "shl", "<<"}, + { "shr", ">>"}, + { "notequal", "!=" }, + { "dereference", "*" }, + { "ptrmember", "->" }, + { NULL, NULL } +}; + +bool cpp_typecheckt::operator_is_overloaded(exprt &expr) +{ + // Check argument types first. + // At least one struct/enum operand is required. + + if(!overloadable(expr)) + return false; + else if(expr.id()==ID_dereference && + expr.get_bool(ID_C_implicit)) + return false; + + assert(expr.operands().size()>=1); + + if(expr.id()=="explicit-typecast") + { + // the cast operator can be overloaded + + typet t=expr.type(); + typecheck_type(t); + std::string op_name=std::string("operator")+"("+cpp_type2name(t)+")"; + + // turn this into a function call + side_effect_expr_function_callt functioncall; + functioncall.arguments().reserve(expr.operands().size()); + functioncall.location()=expr.location(); + + cpp_namet cpp_name; + cpp_name.get_sub().push_back(irept(ID_name)); + cpp_name.get_sub().back().set(ID_identifier, op_name); + cpp_name.get_sub().back().add(ID_C_location)=expr.location(); + + // See if the struct decalares the cast operator as a member + bool found_in_struct = false; + assert(!expr.operands().empty()); + typet t0(follow(expr.op0().type())); + + if(t0.id()==ID_struct) + { + const struct_typet &struct_type= + to_struct_type(t0); + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(!it->get_bool(ID_from_base) && + it->get(ID_base_name) == op_name) + { + found_in_struct = true; + break; + } + } + } + + if(!found_in_struct) + return false; + + { + exprt member(ID_member); + member.add("component_cpp_name")= cpp_name; + + exprt tmp("already_typechecked"); + tmp.copy_to_operands(expr.op0()); + member.copy_to_operands(tmp); + + functioncall.function()=member; + } + + if(expr.operands().size()>1) + { + for(exprt::operandst::const_iterator + it=(expr.operands().begin()+1); + it!=(expr).operands().end(); + it++) + functioncall.arguments().push_back(*it); + } + + typecheck_side_effect_function_call(functioncall); + + if(expr.id()==ID_ptrmember) + { + add_implicit_dereference(functioncall); + exprt tmp("already_typechecked"); + tmp.move_to_operands(functioncall); + expr.op0().swap(tmp); + typecheck_expr(expr); + return true; + } + + expr.swap(functioncall); + return true; + } + + for(const operator_entryt *e=operators; + e->id_name!=NULL; + e++) + if(expr.id()==e->id_name) + { + if(expr.id()==ID_dereference) + assert(!expr.get_bool(ID_C_implicit)); + + std::string op_name=std::string("operator")+e->op_name; + + // first do function/operator + cpp_namet cpp_name; + cpp_name.get_sub().push_back(irept(ID_name)); + cpp_name.get_sub().back().set(ID_identifier, op_name); + cpp_name.get_sub().back().add(ID_C_location)=expr.location(); + + // turn this into a function call + side_effect_expr_function_callt functioncall; + functioncall.arguments().reserve(expr.operands().size()); + functioncall.location()=expr.location(); + + // There are two options to overload an operator: + // + // 1. In the scope of a as a.operator(b, ...) + // 2. Anywhere in scope as operator(a, b, ...) + // + // Both are not allowed. + // + // We try and fail silently, maybe conversions will work + // instead. + + // go into scope of first operand + if(expr.op0().type().id()==ID_symbol && + follow(expr.op0().type()).id()==ID_struct) + { + const irep_idt &struct_identifier= + expr.op0().type().get(ID_identifier); + + // get that scope + cpp_save_scopet save_scope(cpp_scopes); + cpp_scopes.set_scope(struct_identifier); + + // build fargs for resolver + cpp_typecheck_fargst fargs; + fargs.operands=expr.operands(); + fargs.has_object=true; + fargs.in_use=true; + + // should really be a qualified search + exprt resolve_result=resolve( + cpp_name, cpp_typecheck_resolvet::VAR, fargs, false); + + if(resolve_result.is_not_nil()) + { + // Found! We turn op(a, b, ...) into a.op(b, ...) + { + exprt member(ID_member); + member.add("component_cpp_name")=cpp_name; + + exprt tmp("already_typechecked"); + tmp.copy_to_operands(expr.op0()); + member.copy_to_operands(tmp); + + functioncall.function()=member; + } + + if(expr.operands().size()>1) + { + // skip first + for(exprt::operandst::const_iterator + it=expr.operands().begin()+1; + it!=expr.operands().end(); + it++) + functioncall.arguments().push_back(*it); + } + + typecheck_side_effect_function_call(functioncall); + + expr=functioncall; + + return true; + } + } + + // 2nd option! + { + cpp_typecheck_fargst fargs; + fargs.operands=expr.operands(); + fargs.has_object=false; + fargs.in_use=true; + + exprt resolve_result=resolve( + cpp_name, cpp_typecheck_resolvet::VAR, fargs, false); + + if(resolve_result.is_not_nil()) + { + // found! + functioncall.function()= + static_cast( + static_cast(cpp_name)); + + // now do arguments + forall_operands(it, expr) + functioncall.arguments().push_back(*it); + + typecheck_side_effect_function_call(functioncall); + + if(expr.id()==ID_ptrmember) + { + add_implicit_dereference(functioncall); + exprt tmp("already_typechecked"); + tmp.move_to_operands(functioncall); + expr.op0()=tmp; + typecheck_expr(expr); + return true; + } + + expr=functioncall; + + return true; + } + } + + } + + return false; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_address_of + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_address_of(exprt &expr) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + throw "address_of expects one operand"; + } + + exprt &op=expr.op0(); + + if(!op.get_bool(ID_C_lvalue) && expr.type().id()==ID_code) + { + err_location(expr.location()); + str << "expr not an lvalue"; + throw 0; + } + + if(expr.op0().type().id()==ID_code) + { + // we take the address of the method. + assert(expr.op0().id()==ID_member); + exprt symb = cpp_symbol_expr(lookup(expr.op0().get(ID_component_name))); + exprt address(ID_address_of, typet(ID_pointer)); + address.copy_to_operands(symb); + address.type().subtype()=symb.type(); + address.set(ID_C_implicit, true); + expr.op0().swap(address); + } + + if(expr.op0().id()==ID_address_of && + expr.op0().get_bool(ID_C_implicit)) + { + // must be the address of a function + code_typet& code_type = to_code_type(op.type().subtype()); + + code_typet::argumentst& args = code_type.arguments(); + if(args.size() > 0 && args[0].get(ID_C_base_name)==ID_this) + { + // it's a pointer to member function + typet symbol(ID_symbol); + symbol.set(ID_identifier, code_type.get("#member_name")); + expr.op0().type().add("to-member") = symbol; + + if(code_type.get_bool("#is_virtual")) + { + err_location(expr.location()); + str << "error: pointers to virtual methods" + << " are currently not implemented"; + throw 0; + } + } + } + + c_typecheck_baset::typecheck_expr_address_of(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_throw + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_throw(exprt &expr) +{ + // these are of type void + expr.type()=empty_typet(); + + assert(expr.operands().size()==1 || + expr.operands().size()==0); + + // nothing really to do; one can throw _almost_ anything + if(expr.operands().size()==1) + { + if(follow(expr.op0().type()).id()==ID_empty) + { + err_location(expr.op0()); + throw "cannot throw void"; + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_new + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_new(exprt &expr) +{ + // next, find out if we do an array + + if(expr.type().id()==ID_array) + { + // first typecheck subtype + typecheck_type(expr.type().subtype()); + + // typecheck the size + exprt &size=to_array_type(expr.type()).size(); + typecheck_expr(size); + + bool size_is_unsigned=(size.type().id()==ID_unsignedbv); + typet integer_type(size_is_unsigned?ID_unsignedbv:ID_signedbv); + integer_type.set(ID_width, config.ansi_c.int_width); + implicit_typecast(size, integer_type); + + expr.set(ID_statement, ID_cpp_new_array); + + // this must not have an initializer + if(expr.operands().size()!=0) + { + err_location(expr.op0()); + str << "new with array type must not use explicit construction"; + throw 0; + } + + // save the size expression + expr.set(ID_size, to_array_type(expr.type()).size()); + + // new actually returns a pointer, not an array + pointer_typet ptr_type; + ptr_type.subtype()=expr.type().subtype(); + expr.type().swap(ptr_type); + } + else + { + // first typecheck type + typecheck_type(expr.type()); + + expr.set(ID_statement, ID_cpp_new); + + pointer_typet ptr_type; + ptr_type.subtype().swap(expr.type()); + expr.type().swap(ptr_type); + } + + exprt object_expr("new_object", expr.type().subtype()); + object_expr.set(ID_C_lvalue, true); + + { + exprt tmp("already_typechecked"); + tmp.move_to_operands(object_expr); + object_expr.swap(tmp); + } + + // not yet typechecked-stuff + exprt &initializer=static_cast(expr.add(ID_initializer)); + + exprt code= + cpp_constructor( + expr.find_location(), + object_expr, + initializer.operands()); + + expr.add(ID_initializer).swap(code); + + // we add the size of the object for convenience of the + // runtime library + + exprt &sizeof_expr=static_cast(expr.add(ID_sizeof)); + sizeof_expr=c_sizeof(expr.type().subtype(), *this); + sizeof_expr.add("#c_sizeof_type")=expr.type().subtype(); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_explicit_typecast + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_explicit_typecast(exprt &expr) +{ + typecheck_type(expr.type()); + + // these can have 0 or 1 arguments + + if(expr.operands().size()==0) + { + // default value + exprt new_expr=gen_zero(expr.type()); + + if(new_expr.is_nil()) + { + err_location(expr); + str << "no default value for `" << to_string(expr.type()) + << "'"; + throw 0; + } + + new_expr.location()=expr.location(); + expr=new_expr; + } + else if(expr.operands().size()==1) + { + // explicitly given value + exprt new_expr; + + if(const_typecast(expr.op0(), expr.type(), new_expr) || + static_typecast(expr.op0(), expr.type(), new_expr, false) || + reinterpret_typecast(expr.op0(), expr.type(), new_expr, false)) + { + expr=new_expr; + } + else + { + err_location(expr); + str << "invalid explicit cast:" << std::endl; + str << "operand type: `" << to_string(expr.op0().type()) << "'" << std::endl; + str << "casting to: `" << to_string(expr.type()) << "'"; + throw 0; + } + } + else + throw "explicit typecast expects 0 or 1 operands"; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_explicit_constructor_call + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_explicit_constructor_call(exprt &expr) +{ + typecheck_type(expr.type()); + + if(cpp_is_pod(expr.type())) + { + expr.id("explicit-typecast"); + typecheck_expr_main(expr); + } + else + { + assert(expr.type().id()==ID_struct); + + typet symb(ID_symbol); + symb.set(ID_identifier, expr.type().get(ID_name)); + symb.location()=expr.location(); + + exprt e=expr; + new_temporary(e.location(), symb, e.operands(), expr); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_this + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_this(exprt &expr) +{ + if(cpp_scopes.current_scope().class_identifier.empty()) + { + err_location(expr); + error("`this' is not allowed here"); + throw 0; + } + + const exprt &this_expr=cpp_scopes.current_scope().this_expr; + const locationt location=expr.find_location(); + + assert(this_expr.is_not_nil()); + assert(this_expr.type().id()==ID_pointer); + + expr=this_expr; + expr.location()=location; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_delete + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_delete(exprt &expr) +{ + if(expr.operands().size()!=1) + throw "delete expects one operand"; + + if(expr.get(ID_statement)==ID_cpp_delete) + { + } + else if(expr.get(ID_statement)==ID_cpp_delete_array) + { + } + else + assert(false); + + typet pointer_type=follow(expr.op0().type()); + + if(pointer_type.id()!=ID_pointer) + { + err_location(expr); + str << "delete takes a pointer type operand, but got `" + << to_string(pointer_type) << "'"; + throw 0; + } + + // these are always void + expr.type()=typet(ID_empty); + + // we provide the right destructor, for the convenience + // of later stages + exprt new_object(ID_new_object, pointer_type.subtype()); + new_object.location()=expr.location(); + new_object.set(ID_C_lvalue, true); + + already_typechecked(new_object); + + codet destructor_code=cpp_destructor( + expr.location(), + pointer_type.subtype(), + new_object); + + // this isn't typechecked yet + if(destructor_code.is_not_nil()) + typecheck_code(destructor_code); + + expr.set(ID_destructor, destructor_code); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_typecast + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_typecast(exprt &expr) +{ + // should not be called + #if 0 + std::cout << "E: " << expr.pretty() << std::endl; + assert(0); + #endif +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_member + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_member( + exprt &expr, + const cpp_typecheck_fargst &fargs) +{ + if(expr.operands().size()!=1) + { + err_location(expr); + str << "error: member operator expects one operand"; + throw 0; + } + + add_implicit_dereference(expr.op0()); + exprt &op0=expr.op0(); + + #ifdef CPP_SYSTEMC_EXTENSION + if(expr.op0().type().id()==ID_signedbv || + expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_verilogbv) + { + // might be a SystemC expression + typecheck_expr_sc_member(expr, fargs); + return; + } + #endif + + if(op0.type().id()!=ID_symbol) + { + err_location(expr); + str << "error: member operator requires type symbol " + "on left hand side but got `" + << to_string(op0.type()) << "'"; + throw 0; + } + + const irep_idt &struct_identifier= + op0.type().get(ID_identifier); + + const symbolt &struct_symbol=lookup(struct_identifier); + + if(struct_symbol.type.id()==ID_incomplete_struct || + struct_symbol.type.id()==ID_incomplete_union) + { + err_location(expr); + str << "error: member operator got incomplete structure type " + "on left hand side"; + throw 0; + } + + if(struct_symbol.type.id()!=ID_struct && + struct_symbol.type.id()!=ID_union) + { + err_location(expr); + str << "error: member operator requires structure type " + "on left hand side but got `" + << to_string(struct_symbol.type) << "'"; + throw 0; + } + + const struct_typet &type=to_struct_type(struct_symbol.type); + + if(expr.find("component_cpp_name").is_not_nil()) + { + cpp_namet component_cpp_name= + static_cast(expr.find("component_cpp_name")); + + // get that scope + cpp_save_scopet save_scope(cpp_scopes); + cpp_scopes.set_scope(struct_identifier); + + // resolve member + cpp_typecheck_fargst new_fargs(fargs); + new_fargs.add_object(op0); + + exprt symbol_expr=resolve( + component_cpp_name, + cpp_typecheck_resolvet::VAR, + new_fargs); + + if(symbol_expr.id()==ID_dereference) + { + assert(symbol_expr.get_bool(ID_C_implicit)); + exprt tmp = symbol_expr.op0(); + symbol_expr.swap(tmp); + } + + assert(symbol_expr.id()==ID_symbol || + symbol_expr.id()==ID_member || + symbol_expr.id()==ID_constant); + + // If it is a symbol or a constant, just return it! + // note: the resolver returns a symbol if the member + // is static or if it is a constructor + + if(symbol_expr.id()==ID_symbol) + { + if(symbol_expr.type().id() == ID_code + && symbol_expr.type().get(ID_return_type)==ID_constructor) + { + err_location(expr); + str << "error: member `" + << lookup(symbol_expr.get(ID_identifier)).base_name + << "' is a constructor"; + throw 0; + } + else + { + // it must be a static component + const struct_typet::componentt pcomp= + type.get_component(to_symbol_expr(symbol_expr).get_identifier()); + + if(pcomp.is_nil()) + { + err_location(expr); + str << "error: `" + << symbol_expr.get(ID_identifier) + << "' is not static member " + << "of class `" << struct_symbol.base_name << "'"; + throw 0; + } + } + expr = symbol_expr; + return; + } + else if(symbol_expr.id()==ID_constant) + { + expr = symbol_expr; + return; + } + + const irep_idt component_name=symbol_expr.get(ID_component_name); + + expr.remove("component_cpp_name"); + expr.set(ID_component_name, component_name); + } + + const irep_idt &component_name=expr.get(ID_component_name); + + assert(component_name!=""); + + exprt component; + component.make_nil(); + + assert(follow(expr.op0().type()).id()==ID_struct || + follow(expr.op0().type()).id()==ID_union); + + exprt member; + if(get_component(expr.location(), + expr.op0(), + component_name, + member)) + { + // because of possible anonymous unions + expr.swap(member); + } + else + { + err_location(expr); + str << "error: member `" << component_name + << "' of `" << struct_symbol.pretty_name + << "' not found"; + throw 0; + } + + add_implicit_dereference(expr); + + if(expr.type().id()==ID_code) + { + // Check if the function body has to be typechecked + contextt::symbolst::iterator it= + context.symbols.find(component_name); + + assert(it!=context.symbols.end()); + + symbolt &func_symb=it->second; + + if(func_symb.value.id()=="cpp_not_typechecked") + func_symb.value.set("is_used", true); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_ptrmember + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_ptrmember( + exprt &expr, + const cpp_typecheck_fargst &fargs) +{ + assert(expr.id()==ID_ptrmember); + + if(expr.operands().size()!=1) + { + err_location(expr); + str << "error: ptrmember operator expects one operand"; + throw 0; + } + + add_implicit_dereference(expr.op0()); + + if(expr.op0().type().id()!=ID_pointer) + { + err_location(expr); + str << "error: ptrmember operator requires pointer type " + "on left hand side, but got `" + << to_string(expr.op0().type()) << "'"; + throw 0; + } + + exprt tmp; + exprt &op=expr.op0(); + + op.swap(tmp); + + op.id(ID_dereference); + op.move_to_operands(tmp); + op.set(ID_C_location, expr.find(ID_C_location)); + typecheck_expr_dereference(op); + + expr.id(ID_member); + typecheck_expr_member(expr, fargs); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_cast_expr + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_cast_expr(exprt &expr) +{ + side_effect_expr_function_callt e = + to_side_effect_expr_function_call(expr); + + if(e.arguments().size() != 1) + { + err_location(expr); + throw "cast expressions expect one operand"; + } + + exprt &f_op=e.function(); + exprt &cast_op=e.arguments().front(); + + add_implicit_dereference(cast_op); + + const irep_idt &id= + f_op.get_sub().front().get(ID_identifier); + + if(f_op.get_sub().size()!=2 || + f_op.get_sub()[1].id()!=ID_template_args) + { + err_location(expr); + str << id << " expects template argument"; + throw 0; + } + + irept &template_arguments=f_op.get_sub()[1].add(ID_arguments); + + if(template_arguments.get_sub().size()!=1) + { + err_location(expr); + str << id << " expects one template argument"; + throw 0; + } + + irept &template_arg=template_arguments.get_sub().front(); + + if(template_arg.id()!=ID_type && + template_arg.id()!="ambiguous") + { + err_location(expr); + str << id << " expects a type as template argument"; + throw 0; + } + + typet &type=static_cast( + template_arguments.get_sub().front().add(ID_type)); + + typecheck_type(type); + + locationt location=expr.location(); + + exprt new_expr; + if(id==ID_const_cast) + { + if(!const_typecast(cast_op, type, new_expr)) + { + err_location(cast_op); + str << "type mismatch on const_cast:" << std::endl; + str << "operand type: `" << to_string(cast_op.type()) << "'" << std::endl; + str << "cast type: `" << to_string(type) << "'"; + throw 0; + } + } + else if(id==ID_dynamic_cast) + { + if(!dynamic_typecast(cast_op, type, new_expr)) + { + err_location(cast_op); + str << "type mismatch on dynamic_cast:" << std::endl; + str << "operand type: `" << to_string(cast_op.type()) << "'" << std::endl; + str << "cast type: `" << to_string(type) << "'"; + throw 0; + } + } + else if(id==ID_reinterpret_cast) + { + if(!reinterpret_typecast(cast_op, type, new_expr)) + { + err_location(cast_op); + str << "type mismatch on reinterpret_cast:" << std::endl; + str << "operand type: `" << to_string(cast_op.type()) << "'" << std::endl; + str << "cast type: `" << to_string(type) << "'"; + throw 0; + } + } + else if(id==ID_static_cast) + { + if(!static_typecast(cast_op, type, new_expr)) + { + err_location(cast_op); + str << "type mismatch on static_cast:" << std::endl; + str << "operand type: `" << to_string(cast_op.type()) << "'" << std::endl; + str << "cast type: `" << to_string(type) << "'"; + throw 0; + } + } + else + assert(false); + + expr.swap(new_expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_cpp_name + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_cpp_name( + exprt &expr, + const cpp_typecheck_fargst &fargs) +{ + locationt location= + to_cpp_name(expr).location(); + + for(unsigned i=0; i(expr.get_sub()[i]); + typecheck_type(type); + + std::string tmp="("+cpp_type2name(type)+")"; + + typet name(ID_name); + name.set(ID_identifier, tmp); + name.location()=location; + + type=name; + } + } + + if(expr.get_sub().size()>=1 && + expr.get_sub().front().id()==ID_name) + { + const irep_idt &id=expr.get_sub().front().get(ID_identifier); + + if(id==ID_const_cast || + id==ID_dynamic_cast || + id==ID_reinterpret_cast || + id==ID_static_cast) + { + expr.id("cast_expression"); + return; + } + } + + exprt symbol_expr= + resolve( + to_cpp_name(expr), + cpp_typecheck_resolvet::VAR, + fargs); + + // we want VAR + assert(symbol_expr.id()!=ID_type); + + if(symbol_expr.id()==ID_member) + { + if(symbol_expr.operands().empty() || + symbol_expr.op0().is_nil()) + { + if(symbol_expr.type().get(ID_return_type)!=ID_constructor) + { + if(cpp_scopes.current_scope().this_expr.is_nil()) + { + if(symbol_expr.type().id()!=ID_code) + { + err_location(location); + str << "object missing"; + throw 0; + } + + // may still be good for address of + } + else + { + // Try again + exprt ptrmem(ID_ptrmember); + ptrmem.operands().push_back( + cpp_scopes.current_scope().this_expr); + + ptrmem.add("component_cpp_name") = expr; + + ptrmem.location()=location; + typecheck_expr_ptrmember(ptrmem, fargs); + symbol_expr.swap(ptrmem); + } + } + } + } + + symbol_expr.location()=location; + expr=symbol_expr; + + if(expr.id()==ID_symbol) + typecheck_expr_function_identifier(expr); + + add_implicit_dereference(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::add_implicit_dereference + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::add_implicit_dereference(exprt &expr) +{ + if(is_reference(expr.type())) + { + // add implicit dereference + exprt tmp(ID_dereference, expr.type().subtype()); + tmp.set(ID_C_implicit, true); + tmp.location()=expr.location(); + tmp.move_to_operands(expr); + tmp.set(ID_C_lvalue, true); + expr.swap(tmp); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_side_effect_function_call + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_side_effect_function_call( + side_effect_expr_function_callt &expr) +{ + // For virtual functions, it is important to check whether + // the function name is qualified. If it is qualified, then + // the call is not virtual. + bool is_qualified = false; + + if(expr.function().id()==ID_member || + expr.function().id()==ID_ptrmember) + { + if(expr.function().get("component_cpp_name")==ID_cpp_name) + { + const cpp_namet &cpp_name= + to_cpp_name(expr.function().find("component_cpp_name")); + is_qualified=cpp_name.is_qualified(); + } + } + else if(expr.function().id()==ID_cpp_name) + { + const cpp_namet &cpp_name=to_cpp_name(expr.function()); + is_qualified=cpp_name.is_qualified(); + } + + // Backup of the original operand + exprt op0=expr.function(); + + // now do the function -- this has been postponed + typecheck_function_expr(expr.function(), cpp_typecheck_fargst(expr)); + + if(expr.function().id()=="pod_constructor") + { + assert(expr.function().type().id()==ID_code); + + // This must be a POD. + const typet &pod=to_code_type(expr.function().type()).return_type(); + assert(cpp_is_pod(pod)); + + // These aren't really function calls, but either conversions or + // initializations. + if(expr.arguments().size()==0) + { + // create temporary object + exprt tmp_object_expr(ID_sideeffect, pod); + tmp_object_expr.set(ID_statement, ID_temporary_object); + tmp_object_expr.set(ID_C_lvalue, true); + tmp_object_expr.set(ID_mode, current_mode); + tmp_object_expr.location()=expr.location(); + expr.swap(tmp_object_expr); + } + else if(expr.arguments().size()==1) + { + exprt typecast("explicit-typecast"); + typecast.type()=pod; + typecast.location() = expr.location(); + typecast.copy_to_operands(expr.arguments().front()); + typecheck_expr_explicit_typecast(typecast); + expr.swap(typecast); + } + else + { + err_location(expr.location()); + str << "zero or one argument excpected\n"; + throw 0; + } + + return; + } + + #ifdef CPP_SYSTEMC_EXTENSION + if(expr.function().id() == "sc_extension") + { + exprt tmp = expr.function().op0(); + expr.swap(tmp); + return; + } + #endif + + if(expr.function().id()=="cast_expression") + { + // These are not really function calls, + // but usually just type adjustments. + typecheck_cast_expr(expr); + add_implicit_dereference(expr); + return; + } + + follow_symbol(expr.function().type()); + + if(expr.function().type().id()==ID_pointer) + { + if(expr.function().type().find("to-member").is_not_nil()) + { + const exprt &bound= + static_cast(expr.function().type().find("#bound")); + + if(bound.is_nil()) + { + err_location(expr.location()); + str << "pointer-to-member not bound"; + throw 0; + } + + // add `this' + assert(bound.type().id()==ID_pointer); + expr.arguments().insert(expr.arguments().begin(), bound); + + // we don't need the object anymore + expr.function().type().remove("#bound"); + } + + // do implicit dereference + if((expr.function().id()=="implicit_address_of" || + expr.function().id()==ID_address_of) && + expr.function().operands().size()==1) + { + exprt tmp; + tmp.swap(expr.function().op0()); + expr.function().swap(tmp); + } + else + { + assert(expr.function().type().id()==ID_pointer); + exprt tmp(ID_dereference, expr.function().type().subtype()); + tmp.location()=expr.op0().location(); + tmp.move_to_operands(expr.function()); + expr.function().swap(tmp); + } + + if(expr.function().type().id()!=ID_code) + { + err_location(expr.op0()); + throw "expecting code as argument"; + } + } + else if(expr.function().type().id()==ID_code) + { + if(expr.function().type().get_bool("#is_virtual") && + !is_qualified) + { + exprt vtptr_member; + if(op0.id()==ID_member || op0.id()==ID_ptrmember) + { + vtptr_member.id(op0.id()); + vtptr_member.move_to_operands(op0.op0()); + } + else + { + vtptr_member.id(ID_ptrmember); + exprt this_expr("cpp-this"); + vtptr_member.move_to_operands(this_expr); + } + + // get the virtual table + typet this_type = to_code_type(expr.function().type()).arguments().front().type(); + irep_idt vtable_name = this_type.subtype().get(ID_identifier).as_string() +"::@vtable_pointer"; + + const struct_typet &vt_struct= + to_struct_type(follow(this_type.subtype())); + + const struct_typet::componentt &vt_compo= + vt_struct.get_component(vtable_name); + + assert(vt_compo.is_not_nil()); + + vtptr_member.set(ID_component_name, vtable_name); + + // look for the right entry + irep_idt vtentry_component_name = vt_compo.type().subtype().get(ID_identifier).as_string() + + "::" + expr.function().type().get("#virtual_name").as_string(); + + exprt vtentry_member(ID_ptrmember); + vtentry_member.copy_to_operands(vtptr_member); + vtentry_member.set(ID_component_name, vtentry_component_name ); + typecheck_expr(vtentry_member); + + assert(vtentry_member.type().id()==ID_pointer); + + { + exprt tmp(ID_dereference, vtentry_member.type().subtype()); + tmp.location()=expr.op0().location(); + tmp.move_to_operands(vtentry_member); + vtentry_member.swap(tmp); + } + + // Typcheck the expresssion as if it was not virtual + // (add the this pointer) + + expr.type()= + to_code_type(expr.function().type()).return_type(); + + typecheck_method_application(expr); + + // Let's make the call virtual + expr.function().swap(vtentry_member); + + typecheck_function_call_arguments(expr); + add_implicit_dereference(expr); + return; + } + } + else if(expr.function().type().id()==ID_struct) + { + irept name(ID_name); + name.set(ID_identifier, "operator()"); + name.set(ID_C_location, expr.location()); + + cpp_namet cppname; + cppname.get_sub().push_back(name); + + exprt member(ID_member); + member.add("component_cpp_name") = cppname; + + member.move_to_operands(op0); + + expr.function().swap(member); + typecheck_side_effect_function_call(expr); + + return; + } + else + { + err_location(expr.function()); + str << "function call expects function/function " + << "pointer as argument, but got `" + << to_string(expr.op0().type()) << "'"; + throw 0; + } + + expr.type()= + to_code_type(expr.function().type()).return_type(); + + if(expr.type().id()==ID_constructor) + { + assert(expr.function().id() == ID_symbol); + + const code_typet::argumentst &arguments= + to_code_type(expr.function().type()).arguments(); + + assert(arguments.size()>=1); + + const typet &this_type=arguments[0].type(); + + // change type from 'constructor' to object type + expr.type() = this_type.subtype(); + + // create temporary object + exprt tmp_object_expr(ID_sideeffect, this_type.subtype()); + tmp_object_expr.set(ID_statement, ID_temporary_object); + tmp_object_expr.set(ID_C_lvalue, true); + tmp_object_expr.set(ID_mode, current_mode); + tmp_object_expr.location()=expr.location(); + + exprt member; + + exprt new_object("new_object", tmp_object_expr.type()); + new_object.set(ID_C_lvalue, true); + + assert(follow(tmp_object_expr.type()).id()==ID_struct); + + get_component(expr.location(), + new_object, + expr.function().get(ID_identifier), + member); + + // special case for the initialization of parents + if(member.get_bool("#not_accessible")) + { + assert(id2string(member.get(ID_C_access))!=""); + tmp_object_expr.set("#not_accessible", true); + tmp_object_expr.set(ID_C_access, member.get(ID_C_access)); + } + + expr.function().swap(member); + + typecheck_method_application(expr); + typecheck_function_call_arguments(expr); + + codet new_code(ID_expression); + new_code.copy_to_operands(expr); + + tmp_object_expr.add(ID_initializer) = new_code; + expr.swap(tmp_object_expr); + return; + } + + assert(expr.operands().size()==2); + + if(expr.function().id()==ID_member) + typecheck_method_application(expr); + else + { + // for the object of a method call, + // we are willing to add an "address_of" + // for the sake of operator overloading + + const irept::subt &arguments= + expr.function().type().find(ID_arguments).get_sub(); + + if(arguments.size()>=1 && + arguments.front().get(ID_C_base_name)==ID_this && + expr.arguments().size()>=1) + { + const exprt &argument= + static_cast(arguments.front()); + + exprt &operand=expr.op1(); + assert(argument.type().id()==ID_pointer); + + if(operand.type().id()!=ID_pointer && + operand.type()==argument.type().subtype()) + { + exprt tmp(ID_address_of, typet(ID_pointer)); + tmp.type().subtype()=operand.type(); + tmp.location()=operand.location(); + tmp.move_to_operands(operand); + operand.swap(tmp); + } + } + } + + assert(expr.operands().size()==2); + + typecheck_function_call_arguments(expr); + + assert(expr.operands().size()==2); + + add_implicit_dereference(expr); + + // we will deal with some 'special' functions here + do_special_functions(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_function_call_arguments + + Inputs: type-checked arguments, type-checked function + + Outputs: type-adjusted function arguments + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_function_call_arguments( + side_effect_expr_function_callt &expr) +{ + exprt &f_op=expr.function(); + const code_typet &code_type=to_code_type(f_op.type()); + const code_typet::argumentst &arguments=code_type.arguments(); + + // do default arguments + + if(arguments.size()>expr.arguments().size()) + { + unsigned i=expr.arguments().size(); + + for(; i* operators"; + throw 0; + } + + c_typecheck_baset::typecheck_expr_dereference(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_pmop + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_pmop(exprt& expr) +{ + assert(expr.id()=="pointer-to-member"); + assert(expr.operands().size() == 2); + + if(expr.op1().type().id()!=ID_pointer + || expr.op1().type().find("to-member").is_nil()) + { + err_location(expr.location()); + str << "pointer-to-member expected\n"; + throw 0; + } + + typet t0 = expr.op0().type().id()==ID_pointer ? + expr.op0().type().subtype(): expr.op0().type(); + + typet t1((const typet&)expr.op1().type().find("to-member")); + + t0 = follow(t0); + t1 = follow(t1); + + if(t0.id()!=ID_struct) + { + err_location(expr.location()); + str << "pointer-to-member type error"; + throw 0; + } + + const struct_typet &from_struct = to_struct_type(t0); + const struct_typet &to_struct = to_struct_type(t1); + + if(!subtype_typecast(from_struct, to_struct)) + { + err_location(expr.location()); + str << "pointer-to-member type error"; + throw 0; + } + + if(expr.op1().type().subtype().id()!=ID_code) + { + err_location(expr); + str << "pointers to data member are not supported"; + throw 0; + } + + typecheck_expr_main(expr.op1()); + + if(expr.op0().type().id()!=ID_pointer) + { + if(expr.op0().id()==ID_dereference) + { + exprt tmp = expr.op0().op0(); + expr.op0().swap(tmp); + } + else + { + assert(expr.op0().get_bool(ID_C_lvalue)); + exprt address_of(ID_address_of, typet(ID_pointer)); + address_of.copy_to_operands(expr.op0()); + address_of.type().subtype() = address_of.op0().type(); + expr.op0().swap(address_of); + } + } + + exprt tmp(expr.op1()); + tmp.type().set("#bound", expr.op0()); + expr.swap(tmp); + return; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_function_identifier + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_function_identifier(exprt &expr) +{ + if(expr.id()==ID_symbol) + { + // Check if the function body has to be typechecked + contextt::symbolst::iterator it= + context.symbols.find(expr.get(ID_identifier)); + + assert(it != context.symbols.end()); + + symbolt &func_symb = it->second; + + if(func_symb.value.id()=="cpp_not_typechecked") + func_symb.value.set("is_used", true); + } + + c_typecheck_baset::typecheck_expr_function_identifier(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr(exprt &expr) +{ + bool override_constantness= + expr.get_bool("#override_constantness"); + + // we take care of an ambiguity in the C++ grammar + // needs to be done before the operands! + explicit_typecast_ambiguity(expr); + + c_typecheck_baset::typecheck_expr(expr); + + if(override_constantness) + expr.type().set(ID_C_constant, false); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::explict_typecast_ambiguity + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::explicit_typecast_ambiguity(exprt &expr) +{ + // There is an ambiguity in the C++ grammar as follows: + // (TYPENAME) + expr (typecast of unary plus) vs. + // (expr) + expr (sum of two expressions) + // Same issue with the operators & and - and * + + // We figure this out by resolving the type argument + // and re-writing if needed + + if(expr.id()!="explicit-typecast") + return; + + assert(expr.operands().size()==1); + + irep_idt op0_id=expr.op0().id(); + + if(expr.type().id()==ID_cpp_name && + expr.op0().operands().size()==1 && + (op0_id==ID_unary_plus || + op0_id==ID_unary_minus || + op0_id==ID_address_of || + op0_id==ID_dereference)) + { + exprt resolve_result= + resolve( + to_cpp_name(expr.type()), + cpp_typecheck_resolvet::BOTH, + cpp_typecheck_fargst()); + + if(resolve_result.id()!=ID_type) + { + // need to re-write the expression + // e.g., (ID) +expr -> ID+expr + exprt new_binary_expr; + + new_binary_expr.operands().resize(2); + new_binary_expr.op0().swap(expr.type()); + new_binary_expr.op1().swap(expr.op0().op0()); + + if(op0_id==ID_unary_plus) + new_binary_expr.id(ID_plus); + else if(op0_id==ID_unary_minus) + new_binary_expr.id(ID_minus); + else if(op0_id==ID_address_of) + new_binary_expr.id(ID_bitand); + else if(op0_id==ID_dereference) + new_binary_expr.id(ID_mult); + + new_binary_expr.location()=expr.op0().location(); + expr.swap(new_binary_expr); + } + } + +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_binary_arithmetic + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_binary_arithmetic(exprt &expr) +{ + if(expr.operands().size()!=2) + { + err_location(expr); + str << "operator `" << expr.id() << "' expects two operands"; + throw 0; + } + + add_implicit_dereference(expr.op0()); + add_implicit_dereference(expr.op1()); + + #ifdef CPP_SYSTEMC_EXTENSION + if(expr.id()==ID_bitnot || expr.id()==ID_bitand || + expr.id()==ID_bitor || expr.id()==ID_bitxor) + { + if(expr.op0().type().id()=="verilogbv") + { + implicit_typecast(expr.op1(), expr.op0().type()); + expr.type() = expr.op0().type(); + return; + } + else if(expr.op1().type().id()=="verilogbv") + { + implicit_typecast(expr.op0(), expr.op1().type()); + expr.type() = expr.op1().type(); + return; + } + } + #endif + + c_typecheck_baset::typecheck_expr_binary_arithmetic(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_index + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_index(exprt &expr) +{ + #ifdef CPP_SYSTEMC_EXTENSION + if(expr.operands().size()!=2) + { + err_location(expr); + str << "operator `" << expr.id_string() + << "' expects two operands"; + throw 0; + } + + if(expr.op0().type().id()==ID_signedbv || + expr.op0().type().id()==ID_unsignedbv) + { + typecheck_expr_sc_index(expr); + return; + } + #endif + + c_typecheck_baset::typecheck_expr_index(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_rel + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_comma(exprt &expr) +{ + if(expr.operands().size()!=2) + { + err_location(expr); + str << "comma operator expects two operands"; + throw 0; + } + + if(follow(expr.op0().type()).id()==ID_struct) + { + // TODO: check if the comma operator has been overloaded! + } + + #if 0 + #ifdef CPP_SYSTEMC_EXTENSION + if(expr.op0().type().id()==ID_verilogbv || + expr.op0().type().id()==ID_signedbv || + expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_bool) + { + // do concatenation + + int width0 = expr.op0().type().id() ==ID_bool ? 1 : + atoi(expr.op0().type().get(ID_width).c_str()); + + int width1 = expr.op1().type().id() == ID_bool ? 1 : + atoi(expr.op1().type().get(ID_width).c_str()); + + irep_idt type_id= + (expr.op0().type().id()==ID_verilogbv || + expr.op1().type().id()==ID_verilogbv)? ID_verilogbv : ID_unsignedbv; + + typet new_type0(type_id); + new_type0.set(ID_width, width0); + implicit_typecast(expr.op0(), new_type0); + + typet new_type1(type_id); + new_type1.set(ID_width, width1); + implicit_typecast(expr.op1(), new_type1); + + expr.id(ID_concatenation); + + typet new_type(type_id); + new_type.set(ID_width, width0 + width1); + expr.type()=new_type; + + return; + } + #endif + #endif + + c_typecheck_baset::typecheck_expr_comma(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_rel + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_expr_rel(exprt &expr) +{ + if(expr.operands().size() != 2) + { + err_location(expr); + str << "operator `" << expr.id() + << "' expects two operands"; + throw 0; + } + + #ifdef CPP_SYSTEMC_EXTENSION + if(expr.op0().type().id()==ID_verilogbv) + { + if(expr.id()==ID_equal || expr.id()==ID_notequal) + { + implicit_typecast(expr.op1(), expr.op0().type()); + expr.type()=typet(ID_bool); + return; + } + } + #endif + + c_typecheck_baset::typecheck_expr_rel(expr); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_sc_member + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +#ifdef CPP_SYSTEMC_EXTENSION +void cpp_typecheckt::typecheck_expr_sc_member( + exprt &expr, + const cpp_typecheck_fargst &fargs) +{ + cpp_namet cpp_name= + to_cpp_name(expr.find("component_cpp_name")); + + if(cpp_name.get_sub().size()!=1 && + cpp_name.get_sub()[0].id()!=ID_name) + { + err_location(expr); + str << "error: bad SystemC member expression"; + throw 0; + } + + irep_idt name=cpp_name.get_sub()[0].get(ID_identifier); + + if(name=="range") + { + if(fargs.operands.size()!=2) + { + err_location(expr); + str << "error: `range' expects two arguments"; + throw 0; + } + + exprt arg0=fargs.operands[0]; + exprt arg1=fargs.operands[1]; + + make_constant_index(arg0); + make_constant_index(arg1); + + mp_integer o0, o1; + + if(to_integer(arg0, o0)) + { + err_location(arg0); + str << "failed to convert index 0"; + throw 0; + } + + if(o0<0) + { + err_location(arg1); + str << "index 1 out of range"; + throw 0; + } + + if(to_integer(arg1, o1)) + { + err_location(arg1); + str << "failed to convert index 2"; + throw 0; + } + + if(o1<0) + { + err_location(arg1); + str << "index 2 out of range"; + throw 0; + } + + if(o1>o0) + { + err_location(expr); + str << "index 2 greater than index 1"; + } + + exprt extractbits(ID_extractbits, expr.op0().type()); + extractbits.type().set(ID_width, integer2string(o0-o1+1)); + extractbits.copy_to_operands(expr.op0(), arg0, arg1); + + expr=exprt("sc_extension"); + expr.copy_to_operands(extractbits); + return; + } + else + { +// ps_irep("sc_expr",expr); + std::cout << "MEMBER: " << expr.pretty() << std::endl; + assert(0); + } +} +#endif + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_expr_sc_index + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +#ifdef CPP_SYSTEMC_EXTENSION +void cpp_typecheckt::typecheck_expr_sc_index(exprt &expr) +{ + exprt &index_expr=expr.op1(); + + make_index_type(index_expr); + + expr.id(ID_extractbit); + expr.type()=typet(ID_bool); +} +#endif diff --git a/src/cpp/cpp_typecheck_fargs.cpp b/src/cpp/cpp_typecheck_fargs.cpp new file mode 100644 index 00000000000..184cdc92e33 --- /dev/null +++ b/src/cpp/cpp_typecheck_fargs.cpp @@ -0,0 +1,177 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include + +#include + +#include "cpp_typecheck_fargs.h" +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheck_fargst::has_class_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_typecheck_fargst::has_class_type() const +{ + for(exprt::operandst::const_iterator it=operands.begin(); + it!=operands.end(); + it++) + { + if(it->type().id()==ID_struct) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: cpp_typecheck_fargst::build + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheck_fargst::build( + const side_effect_expr_function_callt &function_call) +{ + in_use=true; + + operands.clear(); + operands.reserve(function_call.op1().operands().size()); + + for(unsigned i=0; iops.size()) + { + // Check for default values. + ops.reserve(arguments.size()); + + for(unsigned i=ops.size(); i=arguments.size()) + { + // Ellipsis is the 'worst' of the conversion sequences + distance+=1000; + continue; + } + + exprt argument=arguments[i]; + + exprt &operand=ops[i]; + + #if 0 + // unclear, todo + if(is_reference(operand.type())) + std::cout << "O: " << operand.pretty() << std::endl; + + assert(!is_reference(operand.type())); + #endif + + // "this" is a special case -- we turn the pointer type + // into a reference type to do the type matching + if(i==0 && argument.get("#base_name")==ID_this) + { + argument.type().set("#reference", true); + argument.type().set("#this", true); + } + + unsigned rank = 0; + exprt new_expr; + + #if 0 + std::cout << "C: " << cpp_typecheck.to_string(operand.type()) + << " -> " << cpp_typecheck.to_string(argument.type()) << std::endl; + #endif + + // can we do the standard conversion sequence? + if(cpp_typecheck.implicit_conversion_sequence( + operand, argument.type(), new_expr, rank)) + { + // ok + distance+=rank; + #if 0 + std::cout << "OK " << rank << std::endl; + #endif + } + else + { + #if 0 + std::cout << "NOT OK" << std::endl; + #endif + return false; // no conversion possible + } + } + + return true; +} diff --git a/src/cpp/cpp_typecheck_fargs.h b/src/cpp/cpp_typecheck_fargs.h new file mode 100644 index 00000000000..0966fa6cabd --- /dev/null +++ b/src/cpp/cpp_typecheck_fargs.h @@ -0,0 +1,59 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_TYPECHECK_FARGS_H +#define CPROVER_CPP_TYPECHECK_FARGS_H + +#include +class cpp_typecheckt; + +class cpp_typecheck_fargst // for function overloading +{ +public: + bool in_use, has_object; + exprt::operandst operands; + + // has_object indicates that the first element of + // 'operands' is the 'this' pointer (with the object type, + // not pointer to object type) + + cpp_typecheck_fargst():in_use(false), has_object(false) { } + + bool has_class_type() const; + + void build( + const side_effect_expr_function_callt &function_call); + + explicit cpp_typecheck_fargst( + const side_effect_expr_function_callt &function_call): + in_use(false), has_object(false) + { + build(function_call); + } + + bool match( + const code_typet &code_type, + unsigned &distance, + cpp_typecheckt &cpp_typecheck) const; + + void add_object(const exprt &expr) + { + //if(!in_use) return; + has_object=true; + operands.insert(operands.begin(), expr); + } + + void remove_object() + { + assert(has_object); + operands.erase(operands.begin()); + has_object = false; + } +}; + +#endif diff --git a/src/cpp/cpp_typecheck_find_constructor.cpp b/src/cpp/cpp_typecheck_find_constructor.cpp new file mode 100644 index 00000000000..8730614d18c --- /dev/null +++ b/src/cpp/cpp_typecheck_find_constructor.cpp @@ -0,0 +1,79 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::find_constructor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::find_constructor( + const typet &start_dest_type, + exprt &constructor_expr) +{ + constructor_expr.make_nil(); + + locationt location=start_dest_type.location(); + typet dest_type(start_dest_type); + follow_symbol(dest_type); + + if(dest_type.id()!=ID_struct) + return; + + const struct_typet::componentst &components= + to_struct_type(dest_type).components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + const struct_typet::componentt &component=*it; + const typet &type=component.type(); + + if(type.find(ID_return_type).id()==ID_constructor) + { + const irept::subt &arguments= + type.find(ID_arguments).get_sub(); + + namespacet ns(context); + + if(arguments.size()==1) + { + const exprt &argument=(exprt &)arguments.front(); + const typet &arg_type=argument.type(); + + if(arg_type.id()==ID_pointer && + type_eq(arg_type.subtype(), dest_type, ns)) + { + // found! + const irep_idt &identifier= + component.get(ID_name); + + if(identifier=="") + throw "constructor without identifier"; + + constructor_expr=exprt(ID_symbol, type); + constructor_expr.set(ID_identifier, identifier); + constructor_expr.location()=location; + return; + } + } + } + } +} diff --git a/src/cpp/cpp_typecheck_function.cpp b/src/cpp/cpp_typecheck_function.cpp new file mode 100644 index 00000000000..613155f7e41 --- /dev/null +++ b/src/cpp/cpp_typecheck_function.cpp @@ -0,0 +1,227 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include +#include + +#include "cpp_template_type.h" +#include "cpp_typecheck.h" +#include "cpp_type2name.h" +#include "cpp_util.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_argument + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_argument( + const irep_idt &mode, + code_typet::argumentt &argument) +{ + std::string identifier=id2string(argument.get_identifier()); + + if(identifier.empty()) + { + identifier="#anon_arg"+i2string(anon_counter++); + argument.set_base_name(identifier); + } + + identifier=cpp_identifier_prefix(mode)+"::"+ + cpp_scopes.current_scope().prefix+ + id2string(identifier); + + argument.set_identifier(identifier); + + symbolt symbol; + + symbol.name=identifier; + symbol.base_name=argument.get_base_name(); + symbol.location=argument.location(); + symbol.mode=mode; + symbol.module=module; + symbol.type=argument.type(); + symbol.is_statevar=true; + symbol.lvalue=!is_reference(symbol.type); + + assert(!symbol.base_name.empty()); + + symbolt *new_symbol; + + if(context.move(symbol, new_symbol)) + { + err_location(symbol.location); + str << "cpp_typecheckt::convert_argument: context.move(" + << symbol.name << ") failed"; + throw 0; + } + + // put into scope + cpp_scopes.put_into_scope(*new_symbol); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_arguments + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_arguments( + const irep_idt &mode, + code_typet &function_type) +{ + code_typet::argumentst &arguments= + function_type.arguments(); + + for(code_typet::argumentst::iterator + it=arguments.begin(); + it!=arguments.end(); + it++) + convert_argument(mode, *it); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_function(symbolt &symbol) +{ + code_typet &function_type= + to_code_type(template_subtype(symbol.type)); + + // only a prototype? + if(symbol.value.is_nil()) + return; + + // if it is a destructor, add the implicit code + if(symbol.type.get(ID_return_type)==ID_destructor) + { + const symbolt &msymb=lookup(symbol.type.get("#member_name")); + + assert(symbol.value.id()==ID_code); + assert(symbol.value.get(ID_statement)==ID_block); + + symbol.value.copy_to_operands(dtor(msymb)); + } + + // enter appropriate scope + cpp_save_scopet saved_scope(cpp_scopes); + cpp_scopet &function_scope=cpp_scopes.set_scope(symbol.name); + + // fix the scope's prefix + function_scope.prefix+=id2string(symbol.name)+"::"; + + // genuine function definition -- do the parameter declarations + convert_arguments(symbol.mode, function_type); + + // create "this" if it's a non-static method + if(function_scope.is_method && + !function_scope.is_static_member) + { + code_typet::argumentst &arguments=function_type.arguments(); + assert(arguments.size()>=1); + code_typet::argumentt &this_argument_expr=arguments.front(); + function_scope.this_expr=exprt(ID_symbol, this_argument_expr.type()); + function_scope.this_expr.set(ID_identifier, this_argument_expr.get(ID_C_identifier)); + } + else + function_scope.this_expr.make_nil(); + + // do the function body + start_typecheck_code(); + + // save current return type + typet old_return_type = return_type; + + return_type=function_type.return_type(); + typecheck_code(to_code(symbol.value)); + + symbol.value.type()=symbol.type; + + return_type = old_return_type; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::function_identifier + + Inputs: + + Outputs: + + Purpose: for function overloading + +\*******************************************************************/ + +irep_idt cpp_typecheckt::function_identifier(const typet &type) +{ + const code_typet &function_type= + to_code_type(template_subtype(type)); + + const code_typet::argumentst &arguments= + function_type.arguments(); + + std::string result; + bool first=true; + + result+='('; + + // the name of the function should not depend on + // the class name that is encoded in the type of this, + // but we must distinguish "const" and "non-const" member + // functions + + code_typet::argumentst::const_iterator it= + arguments.begin(); + + if(it!=arguments.end() && + it->get_identifier()==ID_this) + { + const typet &pointer=it->type(); + const typet &symbol =pointer.subtype(); + if(symbol.get_bool(ID_C_constant)) result+="const$"; + result+="this"; + first=false; + it++; + } + + // we skipped the "this", on purpose! + + for(; it!=arguments.end(); it++) + { + if(first) first=false; else result+=","; + typet tmp_type=it->type(); + result+=cpp_type2name(it->type()); + } + + result+=')'; + + return result; +} diff --git a/src/cpp/cpp_typecheck_function_bodies.cpp b/src/cpp/cpp_typecheck_function_bodies.cpp new file mode 100644 index 00000000000..69619f57eea --- /dev/null +++ b/src/cpp/cpp_typecheck_function_bodies.cpp @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_function_bodies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_function_bodies() +{ + instantiation_stackt old_instantiation_stack; + old_instantiation_stack.swap(instantiation_stack); + + while(!function_bodies.empty()) + { + symbolt &function_symbol=*function_bodies.front().function_symbol; + template_map.swap(function_bodies.front().template_map); + instantiation_stack.swap(function_bodies.front().instantiation_stack); + + function_bodies.pop_front(); + + if(function_symbol.name=="c::main") + add_argc_argv(function_symbol); + + exprt &body=function_symbol.value; + if(body.id()=="cpp_not_typechecked") + continue; + + if(body.is_not_nil() && + !body.is_zero()) + { + convert_function(function_symbol); + } + } + + old_instantiation_stack.swap(instantiation_stack); +} + diff --git a/src/cpp/cpp_typecheck_initializer.cpp b/src/cpp/cpp_typecheck_initializer.cpp new file mode 100644 index 00000000000..e2aa54bbd6e --- /dev/null +++ b/src/cpp/cpp_typecheck_initializer.cpp @@ -0,0 +1,353 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include +#include + +#include +#include + +#include "cpp_typecheck.h" +#include "cpp_util.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_initializer + + Inputs: + + Outputs: + + Purpose: Initialize an object with a value + +\*******************************************************************/ + +void cpp_typecheckt::convert_initializer(symbolt &symbol) +{ + // this is needed for template arguments that are types + + if(symbol.is_type) + { + if(symbol.value.is_nil()) return; + + if(symbol.value.id()!=ID_type) + { + err_location(symbol.location); + str << "expected type as initializer for `" + << symbol.base_name << "'"; + throw 0; + } + + typecheck_type(symbol.value.type()); + + return; + } + + // do we have an initializer? + if(symbol.value.is_nil()) + { + // do we need one? + if(is_reference(symbol.type)) + { + err_location(symbol.location); + str << "`" << symbol.base_name + << "' is declared as reference but is not initialized"; + throw 0; + } + + // done + return; + } + + // we do have an initializer + + if(is_reference(symbol.type)) + { + typecheck_expr(symbol.value); + reference_initializer(symbol.value, symbol.type); + } + else if(cpp_is_pod(symbol.type)) + { + if(symbol.type.id() == ID_pointer && + symbol.type.subtype().id() == ID_code && + symbol.value.id() == ID_address_of && + symbol.value.op0().id() == ID_cpp_name) + { + // initialization of a function pointer with + // the address of a function: use pointer type information + // for the sake of overload resolution + + cpp_typecheck_fargst fargs; + fargs.in_use = true; + + const code_typet &code_type=to_code_type(symbol.type.subtype()); + + for(code_typet::argumentst::const_iterator + ait=code_type.arguments().begin(); + ait!=code_type.arguments().end(); + ait++) + { + exprt new_object("new_object"); + new_object.set(ID_C_lvalue, true); + new_object.type() = ait->type(); + + if(ait->get(ID_C_base_name)==ID_this) + { + fargs.has_object = true; + new_object.type() = ait->type().subtype(); + } + + fargs.operands.push_back(new_object); + } + + exprt resolved_expr=resolve( + to_cpp_name(static_cast(symbol.value.op0())), + cpp_typecheck_resolvet::BOTH, fargs); + + assert(symbol.type.subtype() == resolved_expr.type()); + + if(resolved_expr.id()==ID_symbol) + { + symbol.value= + address_of_exprt(resolved_expr); + } + else if(resolved_expr.id()==ID_member) + { + symbol.value = + address_of_exprt(symbol_expr(lookup(resolved_expr.get(ID_component_name)))); + + symbol.value.type().add("to-member") = resolved_expr.op0().type(); + } + else + assert(false); + + if(symbol.type != symbol.value.type()) + { + err_location(symbol.location); + str << "conversion from `" + << to_string(symbol.value.type()) << "' to `" + << to_string(symbol.type) << "' "; + throw 0; + } + + return; + } + + typecheck_expr(symbol.value); + + if(symbol.value.id()==ID_initializer_list || + symbol.value.id()==ID_string_constant) + { + do_initializer(symbol.value, symbol.type, true); + + if(symbol.type.id()==ID_incomplete_array) + symbol.type=symbol.value.type(); + } + else + implicit_typecast(symbol.value, symbol.type); + + simplify_exprt simplify(*this); + exprt tmp_value = symbol.value; + if(!simplify.simplify(tmp_value)) + symbol.value.swap(tmp_value); + } + else + { + // we need a constructor + + symbol_exprt expr_symbol(symbol.name, symbol.type); + already_typechecked(expr_symbol); + + exprt::operandst ops; + ops.push_back(symbol.value); + + symbol.value = cpp_constructor( + symbol.value.location(), + expr_symbol, + ops); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::zero_initializer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::zero_initializer( + const exprt &object, + const typet &type, + const locationt &location, + exprt::operandst &ops) +{ + const typet &final_type=follow(type); + + if(final_type.id()==ID_struct) + { + std::list lst; + + forall_irep(cit, final_type.find(ID_components).get_sub()) + { + const exprt &component=static_cast(*cit); + + if(component.type().id()==ID_code) + continue; + + if(component.get_bool(ID_is_type)) + continue; + + if(component.get_bool(ID_is_static)) + continue; + + exprt member(ID_member); + member.copy_to_operands(object); + member.set(ID_component_name, component.get(ID_name)); + + // recursive call + zero_initializer(member, component.type(), location, ops); + } + } + else if(final_type.id()==ID_array) + { + if(cpp_is_pod(final_type.subtype())) + { + exprt value= + c_typecheck_baset::zero_initializer(final_type, location); + + exprt obj=object; + typecheck_expr(obj); + + code_assignt assign; + assign.lhs()=obj; + assign.rhs()=value; + assign.location()=location; + ops.push_back(assign); + } + else + { + const array_typet &array_type=to_array_type(type); + const exprt &size_expr=array_type.size(); + + if(size_expr.id()=="infinity") + return; // don't initialize + + mp_integer size; + + bool to_int=to_integer(size_expr, size); + assert(!to_int); + assert(size>=0); + + exprt::operandst empty_operands; + for(mp_integer i=0; i(*it); + + assert(component.type().is_not_nil()); + + if(component.type().id()==ID_code) + continue; + + exprt exs=so(component.type()); + + mp_integer size; + bool to_int = !to_integer(exs,size); + assert(to_int); + + if(size>comp_size) + { + comp_size=size; + comp=component; + } + } + + if(comp_size>0) + { + irept name(ID_name); + name.set(ID_identifier, comp.get(ID_base_name)); + name.set(ID_C_location, location); + + cpp_namet cpp_name; + cpp_name.move_to_sub(name); + + exprt member(ID_member); + member.copy_to_operands(object); + member.set("component_cpp_name", cpp_name); + zero_initializer(member, comp.type(), location, ops); + } + } + else if(final_type.id()==ID_c_enum) + { + typet enum_type(ID_unsignedbv); + enum_type.add(ID_width)=final_type.find(ID_width); + + exprt zero(gen_zero(enum_type)); + zero.make_typecast(type); + already_typechecked(zero); + + code_assignt assign; + assign.lhs()=object; + assign.rhs()=zero; + assign.location()=location; + + typecheck_expr(assign.lhs()); + assign.lhs().type().set(ID_C_constant, false); + already_typechecked(assign.lhs()); + + typecheck_code(assign); + ops.push_back(assign); + } + else if(final_type.id()==ID_incomplete_struct || + final_type.id()==ID_incomplete_union) + { + err_location(location); + str << "cannot zero-initialize incomplete compound"; + throw 0; + } + else + { + assert(gen_zero(final_type).is_not_nil()); + + code_assignt assign; + assign.lhs()=object; + assign.rhs()=gen_zero(final_type); + assign.location()=location; + + typecheck_expr(assign.op0()); + assign.lhs().type().set(ID_C_constant, false); + already_typechecked(assign.lhs()); + + typecheck_code(assign); + ops.push_back(assign); + } +} diff --git a/src/cpp/cpp_typecheck_linkage_spec.cpp b/src/cpp/cpp_typecheck_linkage_spec.cpp new file mode 100644 index 00000000000..b4be5a4bd1d --- /dev/null +++ b/src/cpp/cpp_typecheck_linkage_spec.cpp @@ -0,0 +1,42 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert(cpp_linkage_spect &linkage_spec) +{ + irep_idt old_mode=current_mode; + + current_mode=linkage_spec.linkage().get(ID_value); + + // there is a linkage spec "C++", which we know as "cpp" + if(current_mode=="C++") + current_mode=ID_cpp; + + // do the declarations + for(cpp_linkage_spect::itemst::iterator + it=linkage_spec.items().begin(); + it!=linkage_spec.items().end(); + it++) + convert(*it); + + // back to previous linkage spec + current_mode=old_mode; +} diff --git a/src/cpp/cpp_typecheck_namespace.cpp b/src/cpp/cpp_typecheck_namespace.cpp new file mode 100644 index 00000000000..282e61a7e30 --- /dev/null +++ b/src/cpp/cpp_typecheck_namespace.cpp @@ -0,0 +1,86 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert(cpp_namespace_spect &namespace_spec) +{ + // save the scope + cpp_save_scopet saved_scope(cpp_scopes); + + const irep_idt &name=namespace_spec.get_namespace(); + + if(name=="") + { + // unique namespace + err_location(namespace_spec); + throw "unique namespace not supported yet"; + } + + irep_idt final_name(name); + + std::string identifier= + cpp_identifier_prefix(current_mode)+"::"+ + cpp_scopes.current_scope().prefix+id2string(final_name); + + contextt::symbolst::const_iterator it= + context.symbols.find(identifier); + + if(it!=context.symbols.end()) + { + if(it->second.type.id()!="namespace") + { + err_location(namespace_spec); + str << "symbol `" << final_name << "' previously declared" << std::endl; + str << "location of previous declaration: " + << it->second.location << std::endl; + throw 0; + } + + // enter that scope + cpp_scopes.set_scope(it->first); + } + else + { + symbolt symbol; + + symbol.name=identifier; + symbol.base_name=final_name; + symbol.value.make_nil(); + symbol.location=namespace_spec.location(); + symbol.mode=current_mode; + symbol.module=module; + symbol.type=typet("namespace"); + + if(context.move(symbol)) + throw "cpp_typecheckt::convert_namespace: context.move() failed"; + + cpp_scopes.new_namespace(final_name); + } + + // do the declarations + for(cpp_namespace_spect::itemst::iterator + it=namespace_spec.items().begin(); + it!=namespace_spec.items().end(); + it++) + convert(*it); +} diff --git a/src/cpp/cpp_typecheck_resolve.cpp b/src/cpp/cpp_typecheck_resolve.cpp new file mode 100644 index 00000000000..a292bd4d5e3 --- /dev/null +++ b/src/cpp/cpp_typecheck_resolve.cpp @@ -0,0 +1,2725 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cpp_typecheck.h" +#include "cpp_typecheck_resolve.h" +#include "cpp_template_type.h" +#include "cpp_type2name.h" +#include "cpp_util.h" +#include "cpp_convert_type.h" + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::cpp_typecheck_resolvet + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +cpp_typecheck_resolvet::cpp_typecheck_resolvet(cpp_typecheckt &_cpp_typecheck): + cpp_typecheck(_cpp_typecheck), + this_expr(_cpp_typecheck.cpp_scopes.current_scope().this_expr) +{ +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::convert_identifiers + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::convert_identifiers( + const cpp_scopest::id_sett &id_set, + const wantt want, + const cpp_typecheck_fargst &fargs, + resolve_identifierst &identifiers) +{ + for(cpp_scopest::id_sett::const_iterator + it=id_set.begin(); + it!=id_set.end(); + it++) + { + const cpp_idt &identifier=**it; + exprt e=convert_identifier(identifier, want, fargs); + + if(e.is_not_nil()) + { + if(e.id()==ID_type) + assert(e.type().is_not_nil()); + + identifiers.push_back(e); + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::apply_template_args + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::apply_template_args( + resolve_identifierst &identifiers, + const cpp_template_args_non_tct &template_args, + const cpp_typecheck_fargst &fargs) +{ + resolve_identifierst old_identifiers; + old_identifiers.swap(identifiers); + + for(resolve_identifierst::const_iterator + it=old_identifiers.begin(); + it!=old_identifiers.end(); + it++) + { + exprt e=*it; + apply_template_args(e, template_args, fargs); + + if(e.is_not_nil()) + { + if(e.id()==ID_type) + assert(e.type().is_not_nil()); + + identifiers.push_back(e); + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::guess_function_template_args + +Inputs: + +Outputs: + +Purpose: guess arguments of function templates + +\*******************************************************************/ + +void cpp_typecheck_resolvet::guess_function_template_args( + resolve_identifierst &identifiers, + const cpp_typecheck_fargst &fargs) +{ + resolve_identifierst old_identifiers; + old_identifiers.swap(identifiers); + + for(resolve_identifierst::const_iterator + it=old_identifiers.begin(); + it!=old_identifiers.end(); + it++) + { + exprt e=guess_function_template_args(*it, fargs); + + if(e.is_not_nil()) + { + assert(e.id()!=ID_type); + identifiers.push_back(e); + } + } + + disambiguate_functions(identifiers, fargs); + + // there should only be one left, or we have failed to disambiguate + if(identifiers.size()==1) + { + // instantiate that one + exprt e=*identifiers.begin(); + assert(e.id()=="function_template_instance"); + + const symbolt &template_symbol= + cpp_typecheck.lookup(e.type().get(ID_C_template)); + + const cpp_template_args_tct &template_args= + to_cpp_template_args_tc(e.type().find(ID_C_template_arguments)); + + // Let's build the instance. + + const symbolt &new_symbol= + cpp_typecheck.instantiate_template( + location, + template_symbol, + template_args, + template_args); + + identifiers.clear(); + identifiers.push_back( + symbol_exprt(new_symbol.name, new_symbol.type)); + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::remove_templates + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::remove_templates( + resolve_identifierst &identifiers) +{ + resolve_identifierst old_identifiers; + old_identifiers.swap(identifiers); + + for(resolve_identifierst::const_iterator + it=old_identifiers.begin(); + it!=old_identifiers.end(); + it++) + { + if(!cpp_typecheck.follow(it->type()).get_bool(ID_is_template)) + identifiers.push_back(*it); + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::remove_duplicates + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::remove_duplicates( + resolve_identifierst &identifiers) +{ + resolve_identifierst old_identifiers; + old_identifiers.swap(identifiers); + + std::set ids; + std::set other; + + for(resolve_identifierst::const_iterator + it=old_identifiers.begin(); + it!=old_identifiers.end(); + it++) + { + irep_idt id; + + if(it->id()==ID_symbol) + id=it->get(ID_identifier); + else if(it->id()==ID_type && it->type().id()==ID_symbol) + id=it->type().get(ID_identifier); + + if(id=="") + { + if(other.insert(*it).second) + identifiers.push_back(*it); + } + else + { + if(ids.insert(id).second) + identifiers.push_back(*it); + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::convert_template_argument + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +exprt cpp_typecheck_resolvet::convert_template_argument( + const cpp_idt &identifier) +{ + // look up in template map + exprt e=cpp_typecheck.template_map.lookup(identifier.identifier); + + if(e.is_nil() || + (e.id()==ID_type && e.type().is_nil())) + { + cpp_typecheck.err_location(location); + cpp_typecheck.str << "internal error: template parameter without instance:" + << std::endl + << identifier << std::endl; + throw 0; + } + + e.location()=location; + + return e; +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::convert_identifier + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +exprt cpp_typecheck_resolvet::convert_identifier( + const cpp_idt &identifier, + const wantt want, + const cpp_typecheck_fargst &fargs) +{ + if(identifier.id_class==cpp_scopet::TEMPLATE_ARGUMENT) + return convert_template_argument(identifier); + + exprt e; + + if(identifier.is_member && + !identifier.is_constructor && + !identifier.is_static_member) + { + // a regular struct or union member + + const symbolt &compound_symbol= + cpp_typecheck.lookup(identifier.class_identifier); + + assert(compound_symbol.type.id()==ID_struct || + compound_symbol.type.id()==ID_union); + + const struct_union_typet &struct_union_type= + to_struct_union_type(compound_symbol.type); + + const exprt component= + struct_union_type.get_component(identifier.identifier); + + const typet &type=component.type(); + assert(type.is_not_nil()); + + if(identifier.id_class==cpp_scopet::TYPEDEF) + { + e=type_exprt(type); + } + else if(identifier.id_class==cpp_scopet::SYMBOL) + { + // A non-static, non-type member. There has to be an object. + e=exprt(ID_member); + e.set(ID_component_name, identifier.identifier); + e.location()=location; + + exprt object; + object.make_nil(); + + #if 0 + std::cout << "I: " << identifier.class_identifier + << " " + << cpp_typecheck.cpp_scopes.current_scope().this_class_identifier << std::endl; + #endif + + // find the object of the member expression + if(compound_symbol.type.find("#unnamed_object").is_not_nil()) + { + cpp_scopet::id_sett id_set; + + cpp_typecheck.cpp_scopes.current_scope().lookup( + compound_symbol.type.get("#unnamed_object"), + cpp_scopet::RECURSIVE, + id_set); + + assert(id_set.size()==1); + + // recursive call + object=convert_identifier( + **(id_set.begin()), + VAR, fargs); + + assert(object.is_not_nil()); + } + else if(fargs.has_object) + { + // the object is given to us in fargs + assert(!fargs.operands.empty()); + object=fargs.operands[0]; + } + else if(this_expr.is_not_nil()) + { + // use this->... + assert(this_expr.type().id()==ID_pointer); + object=exprt(ID_dereference, this_expr.type().subtype()); + object.copy_to_operands(this_expr); + object.type().set(ID_C_constant, + this_expr.type().subtype().get_bool(ID_C_constant)); + object.set(ID_C_lvalue, true); + object.location()=location; + } + + // check if the member can be applied to the object + typet object_type=cpp_typecheck.follow(object.type()); + + if(object_type.id()==ID_struct || + object_type.id()==ID_union) + { + const struct_union_typet &object_struct=to_struct_union_type(object_type); + + if(object_struct.get_component(identifier.identifier).is_nil()) + object.make_nil(); + } + else + object.make_nil(); + + if(object.is_not_nil()) + { + // we got an object + e.move_to_operands(object); + + bool old_value = cpp_typecheck.disable_access_control; + cpp_typecheck.disable_access_control = true; + cpp_typecheck.typecheck_expr_member(e); + cpp_typecheck.disable_access_control = old_value; + } + else + { + // this has to be a method + if(identifier.is_method) + e = cpp_symbol_expr(cpp_typecheck.lookup(identifier.identifier)); + else + e.make_nil(); + } + } + } + else + { + const symbolt &symbol= + cpp_typecheck.lookup(identifier.identifier); + + if(symbol.is_type) + { + e=type_exprt(); + + if(symbol.is_macro) + { + e.type()=symbol.type; + assert(symbol.type.is_not_nil()); + } + else + e.type()=symbol_typet(symbol.name); + } + else if(symbol.is_macro) + { + e=symbol.value; + assert(e.is_not_nil()); + } + else + { + typet followed_type=symbol.type; + bool constant=followed_type.get_bool(ID_C_constant); + + while(followed_type.id()==ID_symbol) + { + typet tmp=cpp_typecheck.lookup(followed_type).type; + followed_type=tmp; + constant |= followed_type.get_bool(ID_C_constant); + } + + if(constant && + symbol.value.is_not_nil() && + is_number(followed_type) && + symbol.value.id() == ID_constant) + { + e=symbol.value; + } + else + { + e=cpp_symbol_expr(symbol); + } + } + } + + e.location()=location; + + return e; +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::disambiguate + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::filter( + resolve_identifierst &identifiers, + const wantt want) +{ + resolve_identifierst old_identifiers; + old_identifiers.swap(identifiers); + + for(resolve_identifierst::const_iterator + it=old_identifiers.begin(); + it!=old_identifiers.end(); + it++) + { + bool match=false; + + switch(want) + { + case TYPE: + match=(it->id()==ID_type); + break; + + case VAR: + match=(it->id()!=ID_type); + break; + + case BOTH: + match=true; + break; + + default: + assert(false); + } + + if(match) + identifiers.push_back(*it); + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::disambiguate_functions + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::disambiguate_functions( + resolve_identifierst &identifiers, + const cpp_typecheck_fargst &fargs) +{ + resolve_identifierst old_identifiers; + old_identifiers.swap(identifiers); + + // sort according to distance + std::multimap distance_map; + + for(resolve_identifierst::const_iterator + it=old_identifiers.begin(); + it!=old_identifiers.end(); + it++) + { + unsigned args_distance; + + if(disambiguate_functions(*it, args_distance, fargs)) + { + unsigned template_distance=0; + + if(it->type().get(ID_C_template)!="") + template_distance=it->type(). + find(ID_C_template_arguments).find(ID_arguments).get_sub().size(); + + // we give strong preference to functions that have + // fewer template arguments + unsigned total_distance= + 1000*template_distance+args_distance; + + distance_map.insert( + std::pair(total_distance, *it)); + } + } + + identifiers.clear(); + + // put in the top ones + if(!distance_map.empty()) + { + unsigned distance=distance_map.begin()->first; + + for(std::multimap::const_iterator + it=distance_map.begin(); + it!=distance_map.end() && it->first==distance; + it++) + identifiers.push_back(it->second); + } + + if(identifiers.size()>1 && fargs.in_use) + { + // try to further disambiguate functions + + for(resolve_identifierst::iterator + it1 = identifiers.begin(); + it1 != identifiers.end(); + it1++) + { + if(it1->type().id()!=ID_code) continue; + + const code_typet &f1= + to_code_type(it1->type()); + + for(resolve_identifierst::iterator it2= + identifiers.begin(); + it2!=identifiers.end(); + ) // no it2++ + { + if(it1 == it2) + { + it2++; + continue; + } + + if(it2->type().id()!=ID_code) + { + it2++; + continue; + } + + const code_typet &f2 = + to_code_type(it2->type()); + + // TODO: may fail when using ellipsis + assert(f1.arguments().size() == f2.arguments().size()); + + bool f1_better=true; + bool f2_better=true; + + for(unsigned i=0; i < f1.arguments().size() && (f1_better || f2_better); i++) + { + typet type1 = f1.arguments()[i].type(); + typet type2 = f2.arguments()[i].type(); + + if(type1 == type2) + continue; + + if(is_reference(type1) != is_reference(type2)) + continue; + + if(type1.id()==ID_pointer) + { + typet tmp = type1.subtype(); + type1 = tmp; + } + + if(type2.id()==ID_pointer) + { + typet tmp = type2.subtype(); + type2 = tmp; + } + + const typet &followed1 = cpp_typecheck.follow(type1); + const typet &followed2 = cpp_typecheck.follow(type2); + + if(followed1.id() != ID_struct || followed2.id() != ID_struct) + continue; + + const struct_typet &struct1 = to_struct_type(followed1); + const struct_typet &struct2 = to_struct_type(followed2); + + if(f1_better && cpp_typecheck.subtype_typecast(struct1, struct2)) + { + f2_better = false; + } + else if(f2_better && cpp_typecheck.subtype_typecast(struct2, struct1)) + { + f1_better = false; + } + } + + resolve_identifierst::iterator prev_it = it2; + it2++; + + if(f1_better && !f2_better) + identifiers.erase(prev_it); + } + } + } + +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::make_constructors + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::make_constructors( + resolve_identifierst &identifiers) +{ + resolve_identifierst new_identifiers; + + resolve_identifierst::iterator next; + + for(resolve_identifierst::iterator + it=identifiers.begin(); + it!=identifiers.end(); + it++) + { + if(it->id()!=ID_type) + { + // already an expression + new_identifiers.push_back(*it); + continue; + } + + const typet &symbol_type= + cpp_typecheck.follow(it->type()); + + // is it a POD? + + if(cpp_typecheck.cpp_is_pod(symbol_type)) + { + // there are two pod constructors: + + // 1. no arguments, default initialization + code_typet t1; + t1.return_type()=it->type(); + exprt pod_constructor1("pod_constructor", t1); + new_identifiers.push_back(pod_constructor1); + + // 2. one argument, copy/conversion + code_typet t2; + t2.return_type()=it->type(); + t2.arguments().resize(1); + t2.arguments()[0].type()=it->type(); + exprt pod_constructor2("pod_constructor", t2); + new_identifiers.push_back(pod_constructor2); + } + else if(symbol_type.id()==ID_struct) + { + const struct_typet &struct_type=to_struct_type(symbol_type); + + const struct_typet::componentst &components = + struct_type.components(); + + // go over components + for(struct_typet::componentst::const_iterator + itc=components.begin(); + itc!=components.end(); + itc++) + { + const struct_typet::componentt &component = *itc; + const typet &type=component.type(); + + if(component.get_bool(ID_from_base)) + continue; + + if(type.find(ID_return_type).id()==ID_constructor) + { + const symbolt &symb = + cpp_typecheck.lookup(component.get_name()); + exprt e = cpp_symbol_expr(symb); + e.type() = type; + new_identifiers.push_back(e); + } + } + } + } + + identifiers.swap(new_identifiers); +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::do_builtin + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +exprt cpp_typecheck_resolvet::do_builtin( + const irep_idt &base_name, + const cpp_template_args_non_tct &template_args) +{ + exprt dest; + + const cpp_template_args_non_tct::argumentst &arguments= + template_args.arguments(); + + if(base_name==ID_unsignedbv || + base_name==ID_signedbv) + { + if(arguments.size()!=1) + { + cpp_typecheck.err_location(location); + throw id2string(base_name)+" expects one template argument, " + "but got "+i2string(arguments.size()); + } + + const exprt &argument=arguments[0]; + + if(argument.id()==ID_type) + { + cpp_typecheck.err_location(location); + throw id2string(base_name)+" expects one integer template argument, " + "but got type"; + } + + mp_integer i; + if(to_integer(argument, i)) + { + cpp_typecheck.err_location(location); + throw "template argument must be constant"; + } + + if(i<1) + { + cpp_typecheck.err_location(location); + throw "template argument must be greater than zero"; + } + + dest=type_exprt(typet(base_name)); + dest.type().set(ID_width, integer2string(i)); + } + else if(base_name==ID_fixedbv) + { + if(arguments.size()!=2) + { + cpp_typecheck.err_location(location); + throw id2string(base_name)+" expects two template arguments, " + "but got "+i2string(arguments.size()); + } + + const exprt &argument0=arguments[0]; + const exprt &argument1=arguments[1]; + + if(argument0.id()==ID_type) + { + cpp_typecheck.err_location(argument0); + throw id2string(base_name)+" expects two integer template arguments, " + "but got type"; + } + + if(argument1.id()==ID_type) + { + cpp_typecheck.err_location(argument1); + throw id2string(base_name)+" expects two integer template arguments, " + "but got type"; + } + + mp_integer width, integer_bits; + + if(to_integer(argument0, width)) + { + cpp_typecheck.err_location(argument0); + throw "template argument must be constant"; + } + + if(to_integer(argument1, integer_bits)) + { + cpp_typecheck.err_location(argument1); + throw "template argument must be constant"; + } + + if(width<1) + { + cpp_typecheck.err_location(argument0); + throw "template argument must be greater than zero"; + } + + if(integer_bits<0) + { + cpp_typecheck.err_location(argument1); + throw "template argument must be greater or equal zero"; + } + + if(integer_bits>width) + { + cpp_typecheck.err_location(argument1); + throw "template argument must be smaller or equal width"; + } + + dest=type_exprt(typet(base_name)); + dest.type().set(ID_width, integer2string(width)); + dest.type().set(ID_integer_bits, integer2string(integer_bits)); + } + else if(base_name==ID_integer) + { + if(arguments.size()!=0) + { + cpp_typecheck.err_location(location); + throw id2string(base_name)+" expects no template arguments"; + } + + dest=type_exprt(typet(base_name)); + } + else if(has_prefix(id2string(base_name), "constant_infinity")) + { + // ok, but type missing + dest=exprt(ID_infinity, size_type()); + } + else if(base_name=="dump_scopes") + { + dest=exprt(ID_constant, typet(ID_empty)); + cpp_typecheck.str << "Scopes in location " << location << std::endl; + cpp_typecheck.cpp_scopes.get_root_scope().print(cpp_typecheck.str); + cpp_typecheck.warning(); + } + else if(base_name=="current_scope") + { + dest=exprt(ID_constant, typet(ID_empty)); + cpp_typecheck.str << "Scope in location " << location + << ": " + << original_scope->prefix; + cpp_typecheck.warning(); + } + else if(base_name=="size_t") + { + dest=type_exprt(size_type()); + } + else if(base_name=="ssize_t") + { + dest=type_exprt(signed_size_type()); + } + else + { + cpp_typecheck.err_location(location); + cpp_typecheck.str + << "unknown built-in identifier: " << base_name; + throw 0; + } + + return dest; +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::resolve_scope + +Inputs: a cpp_name + +Outputs: a base_name, and potentially template arguments for + the base name; as side-effect, we got to the right + scope + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::resolve_scope( + const cpp_namet &cpp_name, + std::string &base_name, + cpp_template_args_non_tct &template_args) +{ + assert(cpp_name.id()==ID_cpp_name); + assert(!cpp_name.get_sub().empty()); + + original_scope=&cpp_typecheck.cpp_scopes.current_scope(); + location=cpp_name.location(); + + irept::subt::const_iterator pos=cpp_name.get_sub().begin(); + + bool recursive=true; + + // check if we need to go to the root scope + if(pos->id()=="::") + { + pos++; + cpp_typecheck.cpp_scopes.go_to_root_scope(); + recursive=false; + } + + base_name=""; + template_args.make_nil(); + + while(pos!=cpp_name.get_sub().end()) + { + if(pos->id()==ID_name) + base_name+=pos->get_string(ID_identifier); + else if(pos->id()==ID_template_args) + template_args=to_cpp_template_args_non_tc(*pos); + else if(pos->id()=="::") + { + cpp_scopest::id_sett id_set; + + if(template_args.is_not_nil()) + { + cpp_typecheck.cpp_scopes.current_scope().lookup( + base_name, + recursive?cpp_scopet::RECURSIVE:cpp_scopet::QUALIFIED, + cpp_idt::TEMPLATE, + id_set); + + const symbolt &symb_tmpl= + disambiguate_template_classes(base_name, id_set, template_args); + + cpp_typecheck.cpp_scopes.go_to( + cpp_typecheck.cpp_scopes.get_scope(symb_tmpl.name)); + + template_args.make_nil(); + } + else + { + cpp_typecheck.cpp_scopes.current_scope().lookup( + base_name, + recursive?cpp_scopet::RECURSIVE:cpp_scopet::QUALIFIED, + id_set); + + filter_for_named_scopes(id_set); + + if(id_set.empty()) + { + cpp_typecheck.show_instantiation_stack(cpp_typecheck.str); + cpp_typecheck.err_location(location); + cpp_typecheck.str << "scope `" << base_name << "' not found"; + throw 0; + } + else if(id_set.size()==1) + { + cpp_typecheck.cpp_scopes.go_to(**id_set.begin()); + } + else + { + cpp_typecheck.show_instantiation_stack(cpp_typecheck.str); + cpp_typecheck.err_location(location); + cpp_typecheck.str << "scope `" + << base_name << "' is ambiguous"; + throw 0; + } + } + + // we start from fresh + base_name.clear(); + } + else if(pos->id()==ID_operator) + { + base_name+="operator"; + + irept::subt::const_iterator next=pos+1; + assert(next != cpp_name.get_sub().end()); + + if(next->id() == ID_cpp_name || + next->id() == ID_pointer || + next->id() == ID_int || + next->id() == ID_char || + next->id() == ID_bool || + next->id() == ID_merged_type) + { + // it's a cast operator + irept next_ir = *next; + typet op_name; + op_name.swap(next_ir); + cpp_typecheck.typecheck_type(op_name); + base_name+="("+cpp_type2name(op_name)+")"; + pos++; + } + + } + else + base_name+=pos->id_string(); + + pos++; + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::disambiguate_template_classes + +Inputs: + +Outputs: + +Purpose: disambiguate partial specialization + +\*******************************************************************/ + +const symbolt &cpp_typecheck_resolvet::disambiguate_template_classes( + const irep_idt &base_name, + const cpp_scopest::id_sett &id_set, + const cpp_template_args_non_tct &full_template_args) +{ + if(id_set.empty()) + { + cpp_typecheck.show_instantiation_stack(cpp_typecheck.str); + cpp_typecheck.err_location(location); + cpp_typecheck.str << "template scope `" << base_name << "' not found"; + throw 0; + } + + std::set primary_templates; + + for(cpp_scopest::id_sett::const_iterator + it=id_set.begin(); + it!=id_set.end(); + it++) + { + const irep_idt id=(*it)->identifier; + const symbolt &s=cpp_typecheck.lookup(id); + if(!s.type.get_bool(ID_is_template)) continue; + const cpp_declarationt &cpp_declaration=to_cpp_declaration(s.type); + if(!cpp_declaration.is_template_class()) continue; + irep_idt specialization_of=cpp_declaration.get_specialization_of(); + if(specialization_of!="") + primary_templates.insert(specialization_of); + else + primary_templates.insert(id); + } + + assert(primary_templates.size()!=0); + + if(primary_templates.size()>=2) + { + cpp_typecheck.show_instantiation_stack(cpp_typecheck.str); + cpp_typecheck.err_location(location); + cpp_typecheck.str << "template scope `" + << base_name << "' is ambiguous"; + throw 0; + } + + assert(primary_templates.size()==1); + + const symbolt &primary_template_symbol= + cpp_typecheck.lookup(*primary_templates.begin()); + + // We typecheck the template arguments in the context + // of the original scope! + cpp_template_args_tct full_template_args_tc; + + { + cpp_save_scopet save_scope(cpp_typecheck.cpp_scopes); + + cpp_typecheck.cpp_scopes.go_to(*original_scope); + + // use template type of 'primary template' + full_template_args_tc= + cpp_typecheck.typecheck_template_args( + location, + primary_template_symbol, + full_template_args); + // go back to where we used to be + } + + // find any matches + + std::vector matches; + + // the baseline + matches.push_back( + matcht(full_template_args_tc, full_template_args_tc, + primary_template_symbol.name)); + + for(cpp_scopest::id_sett::const_iterator + it=id_set.begin(); + it!=id_set.end(); + it++) + { + const irep_idt id=(*it)->identifier; + const symbolt &s=cpp_typecheck.lookup(id); + + irep_idt specialization_of=s.type.get("specialization_of"); + if(specialization_of=="") continue; + + const cpp_declarationt &cpp_declaration= + to_cpp_declaration(s.type); + + const cpp_template_args_non_tct &partial_specialization_args= + cpp_declaration.partial_specialization_args(); + + // alright, set up template arguments as 'unassigned' + + cpp_saved_template_mapt saved_map(cpp_typecheck.template_map); + cpp_save_scopet save_scope(cpp_typecheck.cpp_scopes); + + cpp_typecheck.template_map.build_unassigned( + cpp_declaration.template_type()); + + // iterate over template instance + assert(full_template_args_tc.arguments().size()== + partial_specialization_args.arguments().size()); + + // we need to do this in the right scope + + cpp_scopet *template_scope= + static_cast( + cpp_typecheck.cpp_scopes.id_map[id]); + + if(template_scope==NULL) + { + cpp_typecheck.err_location(location); + cpp_typecheck.str << "template identifier: " << id << std::endl; + throw "template class instantiation error"; + } + + // enter the scope of the template + cpp_typecheck.cpp_scopes.go_to(*template_scope); + + for(unsigned i=0; i::const_iterator + m_it=matches.begin(); + m_it!=matches.end(); + m_it++) + { + std::cout << "M: " << m_it->cost + << " " << m_it->id << std::endl; + } + + std::cout << std::endl; + #endif + + const matcht &match=*matches.begin(); + + const symbolt &choice= + cpp_typecheck.lookup(match.id); + + // build instance + const symbolt &instance= + cpp_typecheck.instantiate_template( + location, + choice, + match.specialization_args, + match.full_args); + + if(instance.type.id()!=ID_struct && + instance.type.id()!=ID_incomplete_struct) + { + cpp_typecheck.err_location(location); + cpp_typecheck.str << "template `" + << base_name << "' is not a class"; + throw 0; + } + + return instance; +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::resolve_namespace + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +cpp_scopet &cpp_typecheck_resolvet::resolve_namespace( + const cpp_namet &cpp_name) +{ + std::string base_name; + cpp_template_args_non_tct template_args; + template_args.make_nil(); + + cpp_save_scopet save_scope(cpp_typecheck.cpp_scopes); + resolve_scope(cpp_name, base_name, template_args); + + const locationt &location=cpp_name.location(); + bool qualified=cpp_name.is_qualified(); + + cpp_scopest::id_sett id_set; + + cpp_typecheck.cpp_scopes.current_scope().lookup( + base_name, + qualified?cpp_scopet::QUALIFIED:cpp_scopet::RECURSIVE, + id_set); + + filter_for_namespaces(id_set); + + if(id_set.empty()) + { + cpp_typecheck.err_location(location); + cpp_typecheck.str + << "namespace `" + << base_name << "' not found"; + throw 0; + } + else if(id_set.size()==1) + { + cpp_idt &id=**id_set.begin(); + return (cpp_scopet &)id; + } + else + { + cpp_typecheck.err_location(location); + cpp_typecheck.str + << "namespace `" + << base_name << "' is ambigous"; + throw 0; + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::show_identifiers + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::show_identifiers( + const std::string &base_name, + const resolve_identifierst &identifiers, + std::ostream &out) +{ + for(resolve_identifierst::const_iterator + it=identifiers.begin(); + it!=identifiers.end(); + it++) + { + const exprt &id_expr=*it; + + out << " "; + + if(id_expr.id()==ID_type) + { + out << "type " << cpp_typecheck.to_string(id_expr.type()); + } + else + { + irep_idt id; + + if(id_expr.type().get_bool(ID_is_template)) + out << "template "; + + if(id_expr.id()==ID_member) + { + out << "member "; + id="."+base_name; + } + else if(id_expr.id()=="pod_constructor") + { + out << "constructor "; + id=""; + } + else if(id_expr.id()=="function_template_instance") + { + out << "symbol "; + } + else + { + out << "symbol "; + id=cpp_typecheck.to_string(id_expr); + } + + if(id_expr.type().get_bool(ID_is_template)) + { + } + else if(id_expr.type().id()==ID_code) + { + const code_typet &code_type=to_code_type(id_expr.type()); + const typet &return_type=code_type.return_type(); + const code_typet::argumentst &arguments=code_type.arguments(); + out << cpp_typecheck.to_string(return_type); + out << " " << id << "("; + + for(code_typet::argumentst::const_iterator + it=arguments.begin(); it!=arguments.end(); it++) + { + const typet &argument_type=it->type(); + + if(it!=arguments.begin()) + out << ", "; + + out << cpp_typecheck.to_string(argument_type); + } + + if(code_type.has_ellipsis()) + { + if(!arguments.empty()) out << ", "; + out << "..."; + } + + out << ")"; + } + else + out << id << ": " << cpp_typecheck.to_string(id_expr.type()); + + if(id_expr.id()==ID_symbol) + { + const symbolt &symbol=cpp_typecheck.lookup(to_symbol_expr(id_expr)); + out << " (" << symbol.location << ")"; + } + else if(id_expr.id()=="function_template_instance") + { + const symbolt &symbol=cpp_typecheck.lookup(id_expr.type().get(ID_C_template)); + out << " (" << symbol.location << ")"; + } + } + + out << std::endl; + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::resolve + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +exprt cpp_typecheck_resolvet::resolve( + const cpp_namet &cpp_name, + const wantt want, + const cpp_typecheck_fargst &fargs, + bool fail_with_exception) +{ + std::string base_name; + cpp_template_args_non_tct template_args; + template_args.make_nil(); + + // save 'this_expr' before resolving the scopes + this_expr=cpp_typecheck.cpp_scopes.current_scope().this_expr; + + original_scope=&cpp_typecheck.cpp_scopes.current_scope(); + cpp_save_scopet save_scope(cpp_typecheck.cpp_scopes); + + // this changes the scope + resolve_scope(cpp_name, base_name, template_args); + + #ifdef CPP_SYSTEMC_EXTENSION + // SystemC extension + if(base_name == "sc_uint" || base_name == "sc_bv") + return do_builtin_sc_uint_extension(cpp_name, template_args); + else if(base_name == "sc_int") + return do_builtin_sc_int_extension(cpp_name, template_args); + else if(base_name == "sc_logic") + return do_builtin_sc_logic_extension(cpp_name, template_args); + else if(base_name == "sc_lv") + return do_builtin_sc_lv_extension(cpp_name, template_args); + else if (base_name == "SC_LOGIC_0" || base_name == "SC_LOGIC_1" || + base_name == "SC_LOGIC_Z" || base_name == "SC_LOGIC_X") + { + exprt constant(ID_constant, typet("verilogbv")); + constant.type().set(ID_width, ID_1); + if(base_name == "SC_LOGIC_0") + constant.set(ID_value, ID_0); + else if(base_name == "SC_LOGIC_1") + constant.set(ID_value, ID_1); + else if(base_name == "SC_LOGIC_Z") + constant.set(ID_value, "z"); + else if(base_name == "SC_LOGIC_X") + constant.set(ID_value, "x"); + else + assert(0); + + if(want==TYPE) + { + if(!fail_with_exception) return nil_exprt(); + + cpp_typecheck.err_location(cpp_name); + cpp_typecheck.str + << "error: expected type, but got expression `" + << cpp_typecheck.to_string(constant) << "'"; + throw 0; + } + + return constant; + } + #endif + + const locationt &location=cpp_name.location(); + bool qualified=cpp_name.is_qualified(); + + // do __CPROVER scope + if(qualified) + { + if(cpp_typecheck.cpp_scopes.current_scope().identifier== + "c::__CPROVER") + return do_builtin(base_name, template_args); + } + else + { + if(base_name=="true") + { + exprt result; + result.make_true(); + result.location()=location; + return result; + } + else if(base_name=="false") + { + exprt result; + result.make_false(); + result.location()=location; + return result; + } + else if(base_name=="__nullptr") // this is c++0x + { + constant_exprt result; + result.set_value("NULL"); + result.type()=pointer_typet(); + result.type().subtype()=empty_typet(); + result.location()=location; + return result; + } + else if(base_name=="__func__" || + base_name=="__FUNCTION__" || + base_name=="__PRETTY_FUNCTION__") + { + // __func__ is an ANSI-C standard compliant hack to get the function name + // __FUNCTION__ and __PRETTY_FUNCTION__ are GCC-specific + string_constantt s; + s.set_value(location.get_function()); + s.location()=location; + return s; + } + } + + cpp_scopest::id_sett id_set; + + cpp_scopet::lookup_kindt lookup_kind= + qualified?cpp_scopet::QUALIFIED:cpp_scopet::RECURSIVE; + + if(template_args.is_nil()) + cpp_typecheck.cpp_scopes.current_scope().lookup(base_name, lookup_kind, id_set); + else + cpp_typecheck.cpp_scopes.current_scope().lookup(base_name, lookup_kind, cpp_idt::TEMPLATE, id_set); + + // Argument-dependent name lookup + #if 0 + // not clear what this is good for + if(!qualified && !fargs.has_object) + resolve_with_arguments(id_set, base_name, fargs); + #endif + + if(id_set.empty()) + { + if(!fail_with_exception) return nil_exprt(); + + cpp_typecheck.err_location(location); + cpp_typecheck.str + << "symbol `" + << base_name << "' not found"; + + if(qualified) + { + if(cpp_typecheck.cpp_scopes.current_scope().is_root_scope()) + cpp_typecheck.str << " in root scope"; + else + cpp_typecheck.str << " in scope `" + << + cpp_typecheck.cpp_scopes.current_scope().prefix + << "'"; + } + + //cpp_typecheck.cpp_scopes.get_root_scope().print(std::cout); + throw 0; + } + + resolve_identifierst identifiers; + + if(template_args.is_not_nil()) + { + // first figure out if we are doing functions/methods or + // classes + bool have_classes=false, have_methods=false; + + for(cpp_scopest::id_sett::const_iterator + it=id_set.begin(); + it!=id_set.end(); + it++) + { + const irep_idt id=(*it)->identifier; + const symbolt &s=cpp_typecheck.lookup(id); + assert(s.type.get_bool(ID_is_template)); + if(to_cpp_declaration(s.type).is_template_class()) + have_classes=true; + else + have_methods=true; + } + + if(want==BOTH && have_classes && have_methods) + { + if(!fail_with_exception) return nil_exprt(); + + cpp_typecheck.show_instantiation_stack(cpp_typecheck.str); + cpp_typecheck.err_location(location); + cpp_typecheck.str + << "template symbol `" + << base_name << "' is ambiguous"; + throw 0; + } + + if(want==TYPE || have_classes) + { + const symbolt &s= + disambiguate_template_classes(base_name, id_set, template_args); + + identifiers.push_back(exprt(ID_type, symbol_typet(s.name))); + } + else + { + // methods and functions + convert_identifiers( + id_set, want, fargs, identifiers); + + apply_template_args( + identifiers, template_args, fargs); + } + } + else + { + convert_identifiers( + id_set, want, fargs, identifiers); + } + + // change types into constructors if we want a constructor + if(want==VAR) + make_constructors(identifiers); + + filter(identifiers, want); + + #if 0 + std::cout << "P0 " << base_name << " " << identifiers.size() << "\n"; + show_identifiers(base_name, identifiers, std::cout); + std::cout << "\n"; + #endif + + exprt result; + + // We may need to disambiguate functions, + // but don't want templates yet + resolve_identifierst new_identifiers=identifiers; + remove_templates(new_identifiers); + + #if 0 + std::cout << "P1 " << base_name << " " << new_identifiers.size() << "\n"; + show_identifiers(base_name, new_identifiers, std::cout); + std::cout << "\n"; + #endif + + disambiguate_functions(new_identifiers, fargs); + + // no matches? Try again with function template guessing. + if(new_identifiers.empty() && template_args.is_nil()) + { + new_identifiers=identifiers; + guess_function_template_args(new_identifiers, fargs); + + #if 0 + std::cout << "P2 " << base_name << " " << new_identifiers.size() << "\n"; + show_identifiers(base_name, new_identifiers, std::cout); + std::cout << "\n"; + #endif + } + + remove_duplicates(new_identifiers); + + #if 0 + std::cout << "P3 " << base_name << " " << new_identifiers.size() << "\n"; + show_identifiers(base_name, new_identifiers, std::cout); + std::cout << "\n"; + #endif + + if(new_identifiers.size()==1) + { + result=*new_identifiers.begin(); + } + else + { + // nothing or too many + if(!fail_with_exception) return nil_exprt(); + + if(new_identifiers.empty()) + { + cpp_typecheck.err_location(location); + cpp_typecheck.str + << "found no match for symbol `" << base_name + << "', candidates are:" << std::endl; + show_identifiers(base_name, identifiers, cpp_typecheck.str); + } + else + { + cpp_typecheck.err_location(location); + cpp_typecheck.str + << "symbol `" << base_name + << "' does not uniquely resolve:" << std::endl; + show_identifiers(base_name, new_identifiers, cpp_typecheck.str); + + #if 0 + exprt e1=*new_identifiers.begin(); + exprt e2=*(++new_identifiers.begin()); + cpp_typecheck.str << "e1==e2: " << (e1==e2) << std::endl; + cpp_typecheck.str << "e1.type==e2.type: " << (e1.type()==e2.type()) << std::endl; + cpp_typecheck.str << "e1.id()==e2.id(): " << (e1.id()==e2.id()) << std::endl; + cpp_typecheck.str << "e1.iden==e2.iden: " << (e1.get(ID_identifier)==e2.get(ID_identifier)) << std::endl; + cpp_typecheck.str << "e1.iden:: " << e1.get(ID_identifier) << std::endl; + cpp_typecheck.str << "e2.iden:: " << e2.get(ID_identifier) << std::endl; + #endif + } + + if(fargs.in_use) + { + cpp_typecheck.str << std::endl; + cpp_typecheck.str << "argument types:" << std::endl; + + for(exprt::operandst::const_iterator + it=fargs.operands.begin(); + it!=fargs.operands.end(); + it++) + { + cpp_typecheck.str << " " + << cpp_typecheck.to_string(it->type()) << std::endl; + } + } + + if(!cpp_typecheck.instantiation_stack.empty()) + { + cpp_typecheck.str << std::endl; + cpp_typecheck.show_instantiation_stack(cpp_typecheck.str); + } + + throw 0; + } + + // we do some checks before we return + if(result.get_bool("#not_accessible")) + { + #if 0 + if(!fail_with_exception) return nil_exprt(); + + cpp_typecheck.err_location(result.location()); + cpp_typecheck.str + << "error: member `" << result.get("component_name").c_str() + << "' is not accessible"; + throw 0; + #endif + } + + switch(want) + { + case VAR: + if(result.id()==ID_type && !cpp_typecheck.cpp_is_pod(result.type())) + { + if(!fail_with_exception) return nil_exprt(); + + cpp_typecheck.err_location(location); + + cpp_typecheck.str + << "error: expected expression, but got type `" + << cpp_typecheck.to_string(result.type()) << "'"; + + throw 0; + } + break; + + case TYPE: + if(result.id()!=ID_type) + { + if(!fail_with_exception) return nil_exprt(); + + cpp_typecheck.err_location(location); + + cpp_typecheck.str + << "error: expected type, but got expression `" + << cpp_typecheck.to_string(result) << "'"; + + throw 0; + } + break; + + default:; + } + + return result; +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::guess_template_args + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::guess_template_args( + const exprt &template_expr, + const exprt &desired_expr) +{ + if(template_expr.id()==ID_cpp_name) + { + const cpp_namet &cpp_name= + to_cpp_name(template_expr); + + if(!cpp_name.is_qualified()) + { + cpp_save_scopet save_scope(cpp_typecheck.cpp_scopes); + + cpp_template_args_non_tct template_args; + std::string base_name; + resolve_scope(cpp_name, base_name, template_args); + + cpp_scopest::id_sett id_set; + cpp_typecheck.cpp_scopes.current_scope().lookup( + base_name, cpp_scopet::RECURSIVE, id_set); + + // alright, rummage through these + for(cpp_scopest::id_sett::const_iterator it=id_set.begin(); + it!=id_set.end(); + it++) + { + const cpp_idt &id=**it; + // template argument? + if(id.id_class==cpp_idt::TEMPLATE_ARGUMENT) + { + // see if unassigned + exprt &e=cpp_typecheck.template_map.expr_map[id.identifier]; + if(e.id()==ID_unassigned) + { + typet old_type=e.type(); + e=desired_expr; + if(e.type()!=old_type) + e.make_typecast(old_type); + } + } + } + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::guess_template_args + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::guess_template_args( + const typet &template_type, + const typet &desired_type) +{ + // look at + // http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/topic/com.ibm.xlcpp8a.doc/language/ref/template_argument_deduction.htm + + // T + // const T + // volatile T + // T& + // T* + // T[10] + // A + // C(*)(T) + // T(*)() + // T(*)(U) + // T C::* + // C T::* + // T U::* + // T (C::*)() + // C (T::*)() + // D (C::*)(T) + // C (T::*)(U) + // T (C::*)(U) + // T (U::*)() + // T (U::*)(V) + // E[10][i] + // B + // TT + // TT + // TT + + #if 0 + std::cout << "TT: " << template_type.pretty() << std::endl; + std::cout << "DT: " << desired_type.pretty() << std::endl; + #endif + + if(template_type.id()==ID_cpp_name) + { + // we only care about cpp_names that are template parameters! + const cpp_namet &cpp_name=to_cpp_name(template_type); + + cpp_save_scopet save_scope(cpp_typecheck.cpp_scopes); + + if(cpp_name.has_template_args()) + { + // this could be s.th. like my_template, and we need + // to match 'T'. Then 'desired_type' has to be a template instance. + + // TODO + } + else + { + // template parameters aren't qualified + if(!cpp_name.is_qualified()) + { + std::string base_name; + cpp_template_args_non_tct template_args; + resolve_scope(cpp_name, base_name, template_args); + + cpp_scopest::id_sett id_set; + cpp_typecheck.cpp_scopes.current_scope().lookup( + base_name, cpp_scopet::RECURSIVE, id_set); + + // alright, rummage through these + for(cpp_scopest::id_sett::const_iterator + it=id_set.begin(); + it!=id_set.end(); + it++) + { + const cpp_idt &id=**it; + + // template argument? + if(id.id_class==cpp_idt::TEMPLATE_ARGUMENT) + { + // see if unassigned + typet &t=cpp_typecheck.template_map.type_map[id.identifier]; + if(t.id()==ID_unassigned) + { + t=desired_type; + + // remove const, volatile (these can be added in the call) + t.remove(ID_C_constant); + t.remove(ID_C_volatile); + #if 0 + std::cout << "ASSIGN " << id.identifier << " := " + << cpp_typecheck.to_string(desired_type) << std::endl; + #endif + } + } + } + } + } + } + else if(template_type.id()==ID_merged_type) + { + // look at subtypes + for(typet::subtypest::const_iterator + it=template_type.subtypes().begin(); + it!=template_type.subtypes().end(); + it++) + { + guess_template_args(*it, desired_type); + } + } + else if(is_reference(template_type) || + is_rvalue_reference(template_type)) + { + guess_template_args(template_type.subtype(), desired_type); + } + else if(template_type.id()==ID_pointer) + { + const typet &desired_type_followed= + cpp_typecheck.follow(desired_type); + + if(desired_type_followed.id()==ID_pointer) + guess_template_args(template_type.subtype(), desired_type_followed.subtype()); + } + else if(template_type.id()==ID_array) + { + const typet &desired_type_followed= + cpp_typecheck.follow(desired_type); + + if(desired_type_followed.id()==ID_array) + { + // look at subtype first + guess_template_args( + template_type.subtype(), + desired_type_followed.subtype()); + + // size (e.g., buffer size guessing) + guess_template_args( + to_array_type(template_type).size(), + to_array_type(desired_type_followed).size()); + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::guess_function_template_args + +Inputs: + +Outputs: + +Purpose: Guess template arguments for function templates + +\*******************************************************************/ + +exprt cpp_typecheck_resolvet::guess_function_template_args( + const exprt &expr, + const cpp_typecheck_fargst &fargs) +{ + typet tmp=expr.type(); + cpp_typecheck.follow_symbol(tmp); + + if(!tmp.get_bool(ID_is_template)) + return nil_exprt(); // not a template + + assert(expr.id()==ID_symbol); + + // a template is always a declaration + const cpp_declarationt &cpp_declaration= + to_cpp_declaration(tmp); + + // Template classes require explicit template arguments, + // no guessing! + if(cpp_declaration.is_template_class()) + return nil_exprt(); + + // we need function arguments for guessing + if(fargs.operands.empty()) + return nil_exprt(); // give up + + // We need to guess in the case of function templates! + + irep_idt template_identifier= + to_symbol_expr(expr).get_identifier(); + + const symbolt &template_symbol= + cpp_typecheck.lookup(template_identifier); + + // alright, set up template arguments as 'unassigned' + + cpp_saved_template_mapt saved_map(cpp_typecheck.template_map); + + cpp_typecheck.template_map.build_unassigned( + cpp_declaration.template_type()); + + // there should be exactly one declarator + assert(cpp_declaration.declarators().size()==1); + + const cpp_declaratort &function_declarator= + cpp_declaration.declarators().front(); + + // and that needs to have function type + if(function_declarator.type().id()!="function_type") + { + cpp_typecheck.err_location(location); + throw "expected function type for function template"; + } + + cpp_save_scopet cpp_saved_scope(cpp_typecheck.cpp_scopes); + + // we need the template scope + cpp_scopet *template_scope= + static_cast( + cpp_typecheck.cpp_scopes.id_map[template_identifier]); + + if(template_scope==NULL) + { + cpp_typecheck.err_location(location); + cpp_typecheck.str << "template identifier: " << template_identifier << std::endl; + throw "function template instantiation error"; + } + + // enter the scope of the template + cpp_typecheck.cpp_scopes.go_to(*template_scope); + + // walk through the function arguments + const irept::subt &arguments= + function_declarator.type().find(ID_arguments).get_sub(); + + for(unsigned i=0; itype().get(ID_identifier)); + + assert(type_symb.type.id()==ID_struct); + + const struct_typet &struct_type= + to_struct_type(type_symb.type); + + assert(struct_type.has_component(new_symbol.name)); + member_exprt member(code_type); + member.set_component_name(new_symbol.name); + member.struct_op()=*fargs.operands.begin(); + member.location()=location; + expr.swap(member); + return; + } + + } + + expr=cpp_symbol_expr(new_symbol); + expr.location()=location; + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::disambiguate_functions + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +bool cpp_typecheck_resolvet::disambiguate_functions( + const exprt &expr, + unsigned &args_distance, + const cpp_typecheck_fargst &fargs) +{ + args_distance=0; + + if(expr.type().id()!=ID_code || !fargs.in_use) + return true; + + const code_typet &type=to_code_type(expr.type()); + + if(expr.id()==ID_member || + type.return_type().id() == ID_constructor) + { + // if it's a member, but does not have an object yet, + // we add one + if(!fargs.has_object) + { + const code_typet::argumentst &arguments=type.arguments(); + const code_typet::argumentt &argument = arguments.front(); + + assert(argument.get("#base_name")==ID_this); + + if(type.return_type().id() == ID_constructor) + { + // it's a constructor + const typet &object_type = argument.type().subtype(); + exprt object(ID_symbol, object_type); + object.set(ID_C_lvalue, true); + + cpp_typecheck_fargst new_fargs(fargs); + new_fargs.add_object(object); + return new_fargs.match(type, args_distance, cpp_typecheck); + } + else + { + if(expr.type().get_bool("#is_operator") && + fargs.operands.size() == arguments.size()) + { + return fargs.match(type, args_distance, cpp_typecheck); + } + + cpp_typecheck_fargst new_fargs(fargs); + new_fargs.add_object(expr.op0()); + + return new_fargs.match(type, args_distance, cpp_typecheck); + } + } + } + else if(fargs.has_object) + { + // if it's not a member then we shall remove the object + cpp_typecheck_fargst new_fargs(fargs); + new_fargs.remove_object(); + + return new_fargs.match(type, args_distance, cpp_typecheck); + } + + return fargs.match(type, args_distance, cpp_typecheck); +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::filter_for_named_scopes + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::filter_for_named_scopes( + cpp_scopest::id_sett &id_set) +{ + cpp_scopest::id_sett new_set; + + // We only want scopes! + for(cpp_scopest::id_sett::const_iterator + it=id_set.begin(); + it!=id_set.end(); + it++) + { + cpp_idt &id=**it; + + if(id.is_class() || id.is_enum() || id.is_namespace()) + { + assert(id.is_scope); + new_set.insert(&id); + } + else if(id.is_typedef()) + { + irep_idt identifier=id.identifier; + + if(id.is_member) + { + struct_typet struct_type = + static_cast(cpp_typecheck.lookup(id.class_identifier).type); + const exprt pcomp=struct_type.get_component(identifier); + assert(pcomp.is_not_nil()); + assert(pcomp.get_bool(ID_is_type)); + const typet &type=pcomp.type(); + assert(type.id()!=ID_struct); + if(type.id()==ID_symbol) + identifier = type.get(ID_identifier); + else + continue; + } + + while(true) + { + const symbolt &symbol=cpp_typecheck.lookup(identifier); + assert(symbol.is_type); + + // todo? maybe do enum here, too? + if(symbol.type.id()==ID_struct) + { + // this is a scope, too! + cpp_idt &class_id= + cpp_typecheck.cpp_scopes.get_id(identifier); + + assert(class_id.is_scope); + new_set.insert(&class_id); + break; + } + else if(symbol.type.id()==ID_symbol) + identifier=symbol.type.get(ID_identifier); + else + break; + } + } + else if(id.id_class==cpp_scopet::TEMPLATE) + { + #if 0 + const symbolt &symbol= + cpp_typecheck.lookup(id.identifier); + + // Template struct? Really needs arguments to be a scope! + if(symbol.type.get(ID_type)==ID_struct) + { + id.print(std::cout); + assert(id.is_scope); + new_set.insert(&id); + } + #endif + } + else if(id.id_class==cpp_scopet::TEMPLATE_ARGUMENT) + { + // maybe it is a scope + exprt e = cpp_typecheck.template_map.lookup(id.identifier); + + if(e.id() != ID_type) + continue; // it's definitively not a scope + + if(e.type().id() == ID_symbol) + { + irep_idt identifier=e.type().get(ID_identifier); + + while(true) + { + const symbolt &symbol=cpp_typecheck.lookup(identifier); + assert(symbol.is_type); + + if(symbol.type.id()==ID_struct) + { + // this is a scope, too! + cpp_idt &class_id= + cpp_typecheck.cpp_scopes.get_id(identifier); + + assert(class_id.is_scope); + new_set.insert(&class_id); + break; + } + else if(symbol.type.id()==ID_symbol) + identifier=symbol.type.get(ID_identifier); + else + break; + } + } + } + } + + id_set.swap(new_set); +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::filter_for_namespaces + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::filter_for_namespaces( + cpp_scopest::id_sett &id_set) +{ + // we only want namespaces + for(cpp_scopest::id_sett::iterator + it=id_set.begin(); + it!=id_set.end(); + ) // no it++ + { + if((*it)->is_namespace()) + it++; + else + { + cpp_scopest::id_sett::iterator old(it); + it++; + id_set.erase(old); + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheck_resolvet::resolve_with_arguments + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +void cpp_typecheck_resolvet::resolve_with_arguments( + cpp_scopest::id_sett &id_set, + const std::string &base_name, + const cpp_typecheck_fargst &fargs) +{ + // not clear what this is good for + for(unsigned i=0; i resolve_identifierst; + + void convert_identifiers( + const cpp_scopest::id_sett &id_set, + const wantt want, + const cpp_typecheck_fargst &fargs, + resolve_identifierst &identifiers); + + exprt convert_template_argument( + const cpp_idt &id); + + exprt convert_identifier( + const cpp_idt &id, + const wantt want, + const cpp_typecheck_fargst &fargs); + + void disambiguate_functions( + resolve_identifierst &identifiers, + const cpp_typecheck_fargst &fargs); + + void filter( + resolve_identifierst &identifiers, + const wantt want); + + const symbolt &disambiguate_template_classes( + const irep_idt &base_name, + const cpp_scopest::id_sett &id_set, + const cpp_template_args_non_tct &template_args); + + void make_constructors( + resolve_identifierst &identifiers); + + void apply_template_args( + resolve_identifierst &identifiers, + const cpp_template_args_non_tct &template_args, + const cpp_typecheck_fargst &fargs); + + void apply_template_args( + exprt &expr, + const cpp_template_args_non_tct &template_args, + const cpp_typecheck_fargst &fargs); + + void guess_function_template_args( + resolve_identifierst &identifiers, + const cpp_typecheck_fargst &fargs); + + void remove_templates( + resolve_identifierst &identifiers); + + void remove_duplicates( + resolve_identifierst &identifiers); + + exprt guess_function_template_args( + const exprt &expr, + const cpp_typecheck_fargst &fargs); + + void guess_template_args( + const typet &template_parameter, + const typet &desired_type); + + void guess_template_args( + const exprt &template_parameter, + const exprt &desired_expr); + + bool disambiguate_functions( + const exprt &expr, + unsigned &args_distance, + const cpp_typecheck_fargst &fargs); + + exprt do_builtin( + const irep_idt &base_name, + const cpp_template_args_non_tct &template_args); + + void show_identifiers( + const std::string &base_name, + const resolve_identifierst &identifiers, + std::ostream &out); + + void resolve_with_arguments( + cpp_scopest::id_sett &id_set, + const std::string &base_name, + const cpp_typecheck_fargst &fargs); + + void filter_for_named_scopes(cpp_scopest::id_sett &id_set); + void filter_for_namespaces(cpp_scopest::id_sett &id_set); + + #ifdef CPP_SYSTEMC_EXTENSION + exprt do_builtin_sc_uint_extension( + const cpp_namet &cpp_name, + const cpp_template_args_non_tct &template_args); + + exprt do_builtin_sc_int_extension( + const cpp_namet &cpp_name, + const cpp_template_args_non_tct &template_args); + + exprt do_builtin_sc_logic_extension( + const cpp_namet &cpp_name, + const cpp_template_args_non_tct &template_args); + + exprt do_builtin_sc_lv_extension( + const cpp_namet &cpp_name, + const cpp_template_args_non_tct &template_args); + #endif + + struct matcht + { + unsigned cost; + cpp_template_args_tct specialization_args; + cpp_template_args_tct full_args; + irep_idt id; + matcht(cpp_template_args_tct _s_args, + cpp_template_args_tct _f_args, + irep_idt _id): + cost(_s_args.arguments().size()), + specialization_args(_s_args), + full_args(_f_args), + id(_id) + { + } + }; + + inline friend bool operator < (const matcht &m1, const matcht &m2) + { + return m1.cost +#include +#include +#include + +#include "cpp_type2name.h" +#include "cpp_typecheck.h" +#include "cpp_declarator_converter.h" +#include "cpp_template_type.h" +#include "cpp_convert_type.h" +#include "cpp_template_args.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::check_template_restrictions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::check_template_restrictions( + const irept &cpp_name, + const irep_idt &final_identifier, + const typet &final_type) +{ + if(final_type.id()==ID_template) + { + // subtype must be class or function + + if(final_type.subtype().id()!=ID_struct && + final_type.subtype().id()!=ID_code) + { + err_location(cpp_name); + str << "template only allowed with classes or functions," + " but got `" << to_string(final_type.subtype()) << "'"; + throw 0; + } + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_template_class + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_template_class( + cpp_declarationt &declaration) +{ + // Do template arguments. This also sets up the template scope. + cpp_scopet &template_scope= + typecheck_template_parameters(declaration.template_type()); + + typet &type=declaration.type(); + template_typet &template_type=declaration.template_type(); + + bool has_body=type.find(ID_body).is_not_nil(); + + const cpp_namet &cpp_name= + static_cast(type.find(ID_tag)); + + std::string identifier, base_name; + cpp_name.convert(identifier, base_name); + + if(identifier!=base_name) + { + err_location(cpp_name.location()); + throw "no namespaces allowed here"; + } + + if(base_name.empty()) + { + err_location(type.location()); + throw "template classes must not be anonymous"; + } + + const cpp_template_args_non_tct &partial_specialization_args= + declaration.partial_specialization_args(); + + const irep_idt symbol_name= + template_class_identifier( + base_name, template_type, partial_specialization_args); + + #if 0 + // Check if the name is already used by a different template + // in the same scope. + { + cpp_scopet::id_sett id_set; + cpp_scopes.current_scope().lookup( + base_name, + cpp_scopet::SCOPE_ONLY, + cpp_scopet::TEMPLATE, + id_set); + + if(!id_set.empty()) + { + const symbolt &previous=lookup((*id_set.begin())->identifier); + if(previous.name!=symbol_name || id_set.size()>1) + { + err_location(cpp_name.location()); + str << "template declaration of `" << base_name.c_str() + << " does not match previous declaration\n"; + str << "location of previous definition: " << previous.location; + throw 0; + } + } + } + #endif + + // check if we have it already + + contextt::symbolst::iterator previous_symbol= + context.symbols.find(symbol_name); + + if(previous_symbol!=context.symbols.end()) + { + // there already + + bool previous_has_body= + previous_symbol->second.type.find(ID_type).find(ID_body).is_not_nil(); + + if(has_body && previous_has_body) + { + err_location(cpp_name.location()); + str << "template struct `" << base_name + << "' defined previously" << std::endl; + str << "location of previous definition: " + << previous_symbol->second.location; + throw 0; + } + + if(has_body) + { + // we replace the template! + previous_symbol->second.type.swap(declaration); + + // we also replace the template scope (the old one could be deleted) + cpp_scopes.id_map[symbol_name] = &template_scope; + } + + assert(cpp_scopes.id_map[symbol_name]->id_class == cpp_idt::TEMPLATE_SCOPE); + return; + } + + // it's not there yet + + symbolt symbol; + + symbol.name=symbol_name; + symbol.base_name=base_name; + symbol.location=cpp_name.location(); + symbol.mode=current_mode; + symbol.module=module; + symbol.type.swap(declaration); + symbol.is_macro=false; + symbol.value=exprt("template_decls"); + + symbol.pretty_name= + cpp_scopes.current_scope().prefix+id2string(symbol.base_name); + + symbolt *new_symbol; + if(context.move(symbol, new_symbol)) + throw "cpp_typecheckt::typecheck_compound_type: context.move() failed"; + + // put into current scope + cpp_idt &id=cpp_scopes.put_into_scope(*new_symbol); + id.id_class=cpp_idt::TEMPLATE; + id.prefix=cpp_scopes.current_scope().prefix+ + id2string(new_symbol->base_name); + + // link the template symbol with the template scope + cpp_scopes.id_map[symbol_name]=&template_scope; + assert(cpp_scopes.id_map[symbol_name]->id_class==cpp_idt::TEMPLATE_SCOPE); +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_template_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_template_function( + cpp_declarationt &declaration) +{ + assert(declaration.declarators().size()==1); + + cpp_declaratort &declarator=declaration.declarators()[0]; + + const cpp_namet &cpp_name = to_cpp_name(declarator.add(ID_name)); + std::string identifier, base_name; + + if(cpp_name.is_qualified() || + cpp_name.has_template_args()) + { + // must be of the form: name1::name2; + if(cpp_name.get_sub().size() != 4 || + cpp_name.get_sub()[0].id() != ID_name || + cpp_name.get_sub()[1].id() != ID_template_args || + cpp_name.get_sub()[2].id() != "::" || + cpp_name.get_sub()[3].id() != ID_name) + { + if(cpp_name.get_sub().size() != 5 || + cpp_name.get_sub()[0].id() != ID_name || + cpp_name.get_sub()[1].id() != ID_template_args || + cpp_name.get_sub()[2].id() != "::" || + cpp_name.get_sub()[3].id() != ID_operator) + { + err_location(cpp_name); + str << "bad template name"; + throw 0; + } + } + + // let's find the template class this function template belongs to. + cpp_scopet::id_sett id_set; + + cpp_scopes.current_scope().lookup( + cpp_name.get_sub().front().get(ID_identifier), + cpp_scopet::SCOPE_ONLY, + id_set); + + if(id_set.empty()) + { + str << cpp_scopes.current_scope(); + err_location(cpp_name); + str << "identifier `" << cpp_name.get_sub().front().get(ID_identifier) + << "' not found"; + throw 0; + } + else if(id_set.size()>1) + { + err_location(cpp_name); + str << "identifier `" << cpp_name.get_sub().front().get(ID_identifier) + << "' is ambigious"; + throw 0; + } + else if((*(id_set.begin()))->id_class != cpp_idt::TEMPLATE) + { + std::cerr << *(*id_set.begin()) << std::endl; + err_location(cpp_name); + str << "identifier `" << cpp_name.get_sub().front().get(ID_identifier) + << "' is not a template"; + throw 0; + } + + const cpp_idt &cpp_id=**(id_set.begin()); + symbolt &template_symbol= + context.symbols.find(cpp_id.identifier)->second; + + exprt &template_methods = static_cast( + template_symbol.value.add("template_methods")); + + template_methods.copy_to_operands(declaration); + + // save current scope + cpp_save_scopet cpp_saved_scope(cpp_scopes); + + const irept &instantiated_with = + template_symbol.value.add("instantiated_with"); + + for(unsigned i=0; i(instantiated_with.get_sub()[i]); + + cpp_declarationt decl_tmp=declaration; + + // do template arguments + // this also sets up the template scope of the method + cpp_scopet &method_scope= + typecheck_template_parameters(decl_tmp.template_type()); + + cpp_scopes.go_to(method_scope); + + // mapping from template arguments to values/types + template_map.build(decl_tmp.template_type(), tc_template_args); + + decl_tmp.remove(ID_template_type); + decl_tmp.remove(ID_is_template); + + convert(decl_tmp); + cpp_saved_scope.restore(); + } + + return; + } + + // do template arguments + // this also sets up the template scope + cpp_scopet &template_scope= + typecheck_template_parameters(declaration.template_type()); + + cpp_name.convert(identifier, base_name); + if(identifier!=base_name) + { + err_location(declaration); + str << "namespaces not supported in template declaration"; + throw 0; + } + + template_typet &template_type=declaration.template_type(); + + typet function_type= + declarator.merge_type(declaration.type()); + + cpp_convert_plain_type(function_type); + + irep_idt symbol_name= + template_function_identifier( + base_name, + template_type, + function_type); + + bool has_value=declarator.find(ID_value).is_not_nil(); + + // check if we have it already + + contextt::symbolst::iterator previous_symbol= + context.symbols.find(symbol_name); + + if(previous_symbol!=context.symbols.end()) + { + bool previous_has_value = + to_cpp_declaration(previous_symbol->second.type). + declarators()[0].find(ID_value).is_not_nil(); + + if(has_value && previous_has_value) + { + err_location(cpp_name.location()); + str << "function template symbol `" << base_name + << "' declared previously" << std::endl; + str << "location of previous definition: " + << previous_symbol->second.location; + throw 0; + } + + if(has_value) + { + previous_symbol->second.type.swap(declaration); + cpp_scopes.id_map[symbol_name]=&template_scope; + } + + // todo: the old template scope now is useless, + // and thus, we could delete it + return; + } + + symbolt symbol; + symbol.name=symbol_name; + symbol.base_name=base_name; + symbol.location=cpp_name.location(); + symbol.mode=current_mode; + symbol.module=module; + symbol.value.make_nil(); + + symbol.type.swap(declaration); + symbol.pretty_name= + cpp_scopes.current_scope().prefix+id2string(symbol.base_name); + + symbolt *new_symbol; + if(context.move(symbol, new_symbol)) + throw "cpp_typecheckt::typecheck_compound_type: context.move() failed"; + + // put into scope + cpp_idt &id=cpp_scopes.put_into_scope(*new_symbol); + id.id_class=cpp_idt::TEMPLATE; + id.prefix=cpp_scopes.current_scope().prefix+ + id2string(new_symbol->base_name); + + // link the template symbol with the template scope + assert(template_scope.id_class==cpp_idt::TEMPLATE_SCOPE); + cpp_scopes.id_map[symbol_name] = &template_scope; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::template_class_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_typecheckt::template_class_identifier( + const irep_idt &base_name, + const template_typet &template_type, + const cpp_template_args_non_tct &partial_specialization_args) +{ + std::string identifier= + cpp_identifier_prefix(current_mode)+"::"+ + cpp_scopes.current_scope().prefix+ + "template."+id2string(base_name) + "<"; + + int counter=0; + + // these are probably not needed -- templates + // should be unique in a namespace + forall_expr(it, template_type.parameters()) + { + if(counter!=0) identifier+=","; + + if(it->id()==ID_type) + identifier+="Type"+i2string(counter); + else + identifier+="Non_Type"+i2string(counter); + + counter++; + } + + identifier += ">"; + + if(!partial_specialization_args.arguments().empty()) + { + identifier+="_specialized_to_<"; + + counter=0; + for(cpp_template_args_non_tct::argumentst::const_iterator + it=partial_specialization_args.arguments().begin(); + it!=partial_specialization_args.arguments().end(); + it++, counter++) + { + if(counter!=0) identifier+=","; + + if(it->id()==ID_type || it->id()=="ambiguous") + identifier+=cpp_type2name(it->type()); + else + identifier+=cpp_expr2name(*it); + } + + identifier+=">"; + } + + return identifier; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::template_function_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cpp_typecheckt::template_function_identifier( + const irep_idt &base_name, + const template_typet &template_type, + const typet &function_type) +{ + // we first build something without function arguments + cpp_template_args_non_tct partial_specialization_args; + std::string identifier= + template_class_identifier(base_name, template_type, + partial_specialization_args); + + // we must also add the signature of the function to the identifier + identifier+=cpp_type2name(function_type); + + return identifier; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_template_class_specialization + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_template_class_specialization( + cpp_declarationt &declaration) +{ + cpp_save_scopet saved_scope(cpp_scopes); + + typet &type=declaration.type(); + + assert(type.id()==ID_struct); + + cpp_namet &cpp_name= + static_cast(type.add(ID_tag)); + + if(cpp_name.is_qualified()) + { + err_location(cpp_name.location()); + str << "qualifiers not excpected here"; + throw 0; + } + + if(cpp_name.get_sub().size()!=2 || + cpp_name.get_sub()[0].id()!=ID_name || + cpp_name.get_sub()[1].id()!=ID_template_args) + { + // currently we are more restrictive + // than the standard + err_location(cpp_name.location()); + str << "bad template-class-sepcialization name"; + throw 0; + } + + irep_idt base_name= + cpp_name.get_sub()[0].get(ID_identifier); + + // copy the template arguments + const cpp_template_args_non_tct template_args_non_tc= + to_cpp_template_args_non_tc(cpp_name.get_sub()[1]); + + // Remove the template arguments from the name. + cpp_name.get_sub().pop_back(); + + // get the template symbol + + cpp_scopest::id_sett id_set; + cpp_scopes.current_scope().lookup( + base_name, cpp_scopet::SCOPE_ONLY, cpp_idt::TEMPLATE, id_set); + + // remove any specializations + for(cpp_scopest::id_sett::iterator + it=id_set.begin(); + it!=id_set.end(); + ) // no it++ + { + cpp_scopest::id_sett::iterator next=it; + next++; + + if(lookup((*it)->identifier).type. + find("specialization_of").is_not_nil()) + id_set.erase(it); + + it=next; + } + + // only one should be left + if(id_set.empty()) + { + err_location(type.location()); + str << "template `" << base_name << "' not found"; + throw 0; + } + else if(id_set.size()>1) + { + err_location(type); + str << "template `" << base_name << "' is ambiguous"; + throw 0; + } + + contextt::symbolst::iterator s_it= + context.symbols.find((*id_set.begin())->identifier); + + assert(s_it!=context.symbols.end()); + + symbolt &template_symbol=s_it->second; + + if(!template_symbol.type.get_bool(ID_is_template)) + { + err_location(type); + str << "expected a template"; + } + + #if 0 + // is this partial specialization? + if(declaration.template_type().parameters().empty()) + { + // typecheck arguments -- these are for the 'primary' template! + cpp_template_args_tct template_args_tc= + typecheck_template_args( + declaration.location(), + to_cpp_declaration(template_symbol.type).template_type(), + template_args_non_tc); + + // Full specialization, i.e., template<>. + // We instantiate. + instantiate_template( + cpp_name.location(), + template_symbol, + template_args_tc, + type); + } + else + #endif + + { + // partial -- we typecheck + declaration.partial_specialization_args()=template_args_non_tc; + declaration.set_specialization_of(template_symbol.name); + + // We can't typecheck arguments yet, they are used + // for guessing later. But we can check the number. + if(template_args_non_tc.arguments().size()!= + to_cpp_declaration(template_symbol.type).template_type().parameters().size()) + { + err_location(cpp_name.location()); + throw "template specialization with wrong number of arguments"; + } + + typecheck_template_class(declaration); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_template_function_or_member_specialization + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_template_function_or_member_specialization( + cpp_declarationt &declaration) +{ + cpp_save_scopet saved_scope(cpp_scopes); + + if(declaration.declarators().size()!=1 || + declaration.declarators().front().type().id()!="function_type") + { + err_location(declaration.type()); + str << "expected function template specialization"; + throw 0; + } + + assert(declaration.declarators().size()==1); + cpp_declaratort declarator=declaration.declarators().front(); + cpp_namet &cpp_name=declarator.name(); + + if(cpp_name.is_qualified()) + { + err_location(cpp_name.location()); + str << "qualifiers not excpected here"; + throw 0; + } + + // There is specialization (instantiation with template arguments) + // but also function overloading (no template arguments) + + assert(!cpp_name.get_sub().empty()); + + if(cpp_name.get_sub().back().id()==ID_template_args) + { + // proper specialization with arguments + if(cpp_name.get_sub().size() != 2 || + cpp_name.get_sub()[0].id() != ID_name || + cpp_name.get_sub()[1].id() != ID_template_args) + { + // currently we are more restrictive + // than the standard + err_location(cpp_name.location()); + str << "bad template-function-specialization name"; + throw 0; + } + + std::string base_name= + cpp_name.get_sub()[0].get(ID_identifier).c_str(); + + cpp_scopest::id_sett id_set; + cpp_scopes.current_scope().lookup( + base_name, cpp_scopet::SCOPE_ONLY, id_set); + + if(id_set.empty()) + { + err_location(cpp_name.location()); + str << "template `" << base_name << "' not found"; + throw 0; + } + else if(id_set.size()>1) + { + err_location(cpp_name.location()); + str << "template `" << base_name << "' is ambiguous"; + } + + const symbolt &template_symbol= + lookup((*id_set.begin())->identifier); + + cpp_template_args_tct template_args= + typecheck_template_args( + declaration.location(), + template_symbol, + to_cpp_template_args_non_tc(cpp_name.get_sub()[1])); + + cpp_name.get_sub().pop_back(); + + typet specialization; + specialization.swap(declarator); + + instantiate_template( + cpp_name.location(), + template_symbol, + template_args, + template_args, + specialization); + } + else + { + // Just overloading, but this is still a template + // for disambiguation purposes! + // http://www.gotw.ca/publications/mill17.htm + cpp_declarationt new_declaration=declaration; + + new_declaration.remove(ID_template_type); + new_declaration.remove(ID_is_template); + new_declaration.set(ID_C_template, ""); // todo, get identifier + + convert_non_template_declaration(new_declaration); + } +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_template_parameters + + Inputs: + + Outputs: + + Purpose:/ + +\*******************************************************************/ + +cpp_scopet &cpp_typecheckt::typecheck_template_parameters( + template_typet &type) +{ + cpp_save_scopet cpp_saved_scope(cpp_scopes); + + assert(type.id()==ID_template); + + std::string id_suffix="template::"+i2string(template_counter++); + + // produce a new scope for the template parameters + cpp_scopet &template_scope= + cpp_scopes.current_scope().new_scope( + cpp_scopes.current_scope().prefix+id_suffix); + + template_scope.prefix=template_scope.get_parent().prefix+id_suffix; + template_scope.id_class=cpp_idt::TEMPLATE_SCOPE; + + cpp_scopes.go_to(template_scope); + + // put template parameters into this scope + template_typet::parameterst ¶meters=type.parameters(); + + unsigned anon_count=0; + + Forall_expr(it, parameters) + { + exprt ¶meter=*it; + + cpp_declarationt declaration; + declaration.swap(static_cast(parameter)); + + cpp_declarator_convertert cpp_declarator_converter(*this); + + // there must be _one_ declarator + assert(declaration.declarators().size()==1); + + cpp_declaratort &declarator=declaration.declarators().front(); + + // there might be a default type or value + exprt default_expr=declarator.value(); + typet default_type=(const typet &)(declarator.value()); + + // it may be anonymous + if(declarator.name().is_nil()) + { + irept name(ID_name); + name.set(ID_identifier, "anon#"+i2string(++anon_count)); + declarator.name()=cpp_namet(); + declarator.name().get_sub().push_back(name); + } + + cpp_declarator_converter.is_typedef=declaration.get_bool(ID_is_type); + cpp_declarator_converter.is_template_argument=true; + + const symbolt &symbol= + cpp_declarator_converter.convert(declaration, declarator); + + if(cpp_declarator_converter.is_typedef) + { + parameter=exprt(ID_type, typet(ID_symbol)); + parameter.type().set(ID_identifier, symbol.name); + + parameter.type().location()=declaration.find_location(); + + if(default_type.is_not_nil()) + { + parameter.add("#default") = default_type; + } + } + else + { + parameter=symbol_expr(symbol); + + if(default_expr.is_not_nil()) + { + typecheck_expr(default_expr); + parameter.add("#default") = default_expr; + } + } + + parameter.location()=declaration.find_location(); + } + + // continue without adding to the prefix + template_scope.prefix=template_scope.get_parent().prefix; + + return template_scope; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_template_args + + Inputs: location, non-typechecked template arguments + + Outputs: typechecked template arguments + + Purpose: + +\*******************************************************************/ + +cpp_template_args_tct cpp_typecheckt::typecheck_template_args( + const locationt &location, + const symbolt &template_symbol, + const cpp_template_args_non_tct &template_args) +{ + // old stuff + assert(template_args.id()!="already_typechecked"); + + assert(template_symbol.type.get_bool(ID_is_template)); + + const template_typet &template_type= + to_cpp_declaration(template_symbol.type).template_type(); + + // bad re-cast + cpp_template_args_tct result= + (const cpp_template_args_tct &)(template_args); + + cpp_template_args_tct::argumentst &args= + result.arguments(); + + const template_typet::parameterst ¶meters= + template_type.parameters(); + + if(parameters.size()=args.size()) + { + // Check for default parameter. + // These may depend on previous arguments. + exprt default_value= + static_cast(parameter.find("#default")); + + if(default_value.is_nil()) + { + err_location(location); + throw "not enough template arguments"; + } + + args.push_back(default_value); + + // we need to enter the template scope to typecheck these + } + + exprt &arg=args[i]; + + if(parameter.id()==ID_type) + { + if(arg.id()==ID_type) + { + typecheck_type(arg.type()); + } + else if(arg.id()=="ambiguous") + { + typecheck_type(arg.type()); + typet t=arg.type(); + arg=exprt(ID_type, t); + } + else + { + err_location(arg); + str << "expected type, but got expression"; + throw 0; + } + } + else // expression + { + if(arg.id()==ID_type) + { + err_location(arg); + str << "expected expression, but got type"; + throw 0; + } + else if(arg.id()=="ambiguous") + { + exprt e; + e.swap(arg.type()); + arg.swap(e); + } + + typecheck_expr(arg); + simplify(arg, *this); + implicit_typecast(arg, parameter.type()); + } + } + + assert(args.size()==parameters.size()); + + return result; +} + +/*******************************************************************\ + +Function: cpp_typecheckt::convert_template_declaration + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert_template_declaration( + cpp_declarationt &declaration) +{ + assert(declaration.is_template()); + + if(declaration.member_spec().is_virtual()) + { + err_location(declaration); + str << "invalid use of 'virtual' in template declaration"; + throw 0; + } + + if(convert_typedef(declaration.type())) + { + err_location(declaration); + str << "template declaration for typedef"; + throw 0; + } + + typet &type=declaration.type(); + + // there are 1) function templates and 2) template classes + + if(declaration.is_template_class()) + { + // there should not be declarators + if(!declaration.declarators().empty()) + { + err_location(declaration); + throw "template class not expected to have declarators"; + } + + // it needs to be a template class + if(type.id()!=ID_struct) + { + err_location(declaration); + throw "expected template class"; + } + + // Is it class template specialization? + // We can tell if there are template arguments in the class name, + // like template<...> class tag ... + if((static_cast( + type.find(ID_tag))).has_template_args()) + { + convert_template_class_specialization(declaration); + return; + } + + typecheck_template_class(declaration); + return; + } + else // maybe function template, maybe template class member + { + // there should be declarators + if(declaration.declarators().empty()) + { + err_location(declaration); + throw "function template or member expected to have declarator"; + } + + // Is it function template specialization? + // Only full specialization is allowed! + if(declaration.template_type().parameters().empty()) + { + convert_template_function_or_member_specialization(declaration); + return; + } + + typecheck_template_function(declaration); + return; + } +} + diff --git a/src/cpp/cpp_typecheck_type.cpp b/src/cpp/cpp_typecheck_type.cpp new file mode 100644 index 00000000000..5f1dba9ea56 --- /dev/null +++ b/src/cpp/cpp_typecheck_type.cpp @@ -0,0 +1,257 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include +#include + +#include "cpp_typecheck.h" +#include "cpp_convert_type.h" +#include "expr2cpp.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::typecheck_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::typecheck_type(typet &type) +{ + assert(type.id()!=irep_idt()); + assert(type.is_not_nil()); + + try + { + cpp_convert_plain_type(type); + } + + catch(const char *error) + { + err_location(type); + str << error; + throw 0; + } + + catch(const std::string &error) + { + err_location(type); + str << error; + throw 0; + } + + if(type.id()==ID_cpp_name) + { + c_qualifierst qualifiers(type); + + cpp_namet cpp_name; + cpp_name.swap(type); + + exprt symbol_expr=resolve( + cpp_name, + cpp_typecheck_resolvet::TYPE, + cpp_typecheck_fargst()); + + if(symbol_expr.id()!=ID_type) + { + err_location(type); + str << "error: expected type"; + throw 0; + } + + type=symbol_expr.type(); + assert(type.is_not_nil()); + + if(type.get_bool(ID_C_constant)) + qualifiers.is_constant = true; + + qualifiers.write(type); + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + typecheck_compound_type(type); + } + else if(type.id()==ID_pointer) + { + // the pointer might have a qualifier, but do subtype first + typecheck_type(type.subtype()); + + // Check if it is a pointer-to-member + if(type.find("to-member").is_not_nil()) + { + // these can point either to data members or member functions + // of a class + + typet &class_object=static_cast(type.add("to-member")); + + if(class_object.id()==ID_cpp_name) + { + assert(class_object.get_sub().back().id()=="::"); + class_object.get_sub().pop_back(); + } + + typecheck_type(class_object); + + // there may be arguments if this is a pointer to member function + if(type.subtype().id()==ID_code) + { + irept::subt &args=type.subtype().add(ID_arguments).get_sub(); + + if(args.empty() || + args.front().get(ID_C_base_name)!=ID_this) + { + // Add 'this' to the arguments + exprt a0(ID_argument); + a0.set(ID_C_base_name, ID_this); + a0.type().id(ID_pointer); + a0.type().subtype() = class_object; + args.insert(args.begin(),a0); + } + } + } + + // now do qualifier + if(type.find("#qualifier").is_not_nil()) + { + typet &t=static_cast(type.add("#qualifier")); + cpp_convert_plain_type(t); + c_qualifierst q(t); + q.write(type); + } + + type.remove("#qualifier"); + } + else if(type.id()==ID_array) + { + exprt &size_expr=static_cast(type.add(ID_size)); + + if(size_expr.is_nil()) + type.id(ID_incomplete_array); + else + { + typecheck_expr(size_expr); + + simplify_exprt expr_simplifier(*this); + expr_simplifier.simplify(size_expr); + + if(size_expr.id()!=ID_unassigned && + size_expr.id()!=ID_constant && + size_expr.id()!=ID_infinity) + { + err_location(type); + str << "failed to determine size of array: " << + expr2cpp(size_expr, context); + throw 0; + } + } + + typecheck_type(type.subtype()); + + if(type.subtype().get_bool(ID_C_constant)) + type.set(ID_C_constant, true); + + if(type.subtype().get_bool(ID_C_volatile)) + type.set(ID_C_volatile, true); + } + else if(type.id()==ID_code) + { + code_typet &code_type=to_code_type(type); + typecheck_type(code_type.return_type()); + + code_typet::argumentst &arguments=code_type.arguments(); + + for(code_typet::argumentst::iterator it=arguments.begin(); + it!=arguments.end(); + it++) + { + typecheck_type(it->type()); + + // see if there is a default value + if(it->has_default_value()) + { + typecheck_expr(it->default_value()); + implicit_typecast(it->default_value(), it->type()); + } + } + } + else if(type.id()==ID_template) + { + typecheck_type(type.subtype()); + } + else if(type.id()==ID_c_enum) + { + typecheck_enum_type(type); + } + else if(type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bool || + type.id()==ID_floatbv || + type.id()==ID_fixedbv || + type.id()==ID_empty) + { + } + else if(type.id()==ID_symbol) + { + } + else if(type.id()==ID_constructor || + type.id()==ID_destructor) + { + } + else if(type.id()=="cpp-cast-operator") + { + } + else if(type.id()=="cpp-template-type") + { + } + else if(type.id()==ID_typeof) + { + exprt e=static_cast(type.find(ID_expr_arg)); + + if(e.is_nil()) + { + typet tmp_type= + static_cast(type.find(ID_type_arg)); + typecheck_type(tmp_type); + type=tmp_type; + } + else + { + typecheck_expr(e); + type=e.type(); + } + } + else if(type.id()==ID_decltype) + { + exprt e=static_cast(type.find(ID_expr_arg)); + typecheck_expr(e); + type=e.type(); + } + #ifdef CPP_SYSTEMC_EXTENSION + else if(type.id()==ID_verilogbv) + { + } + #endif + else if(type.id()==ID_unassigned) + { + // ignore, for template argument guessing + } + else + { + err_location(type); + str << "unexpected type: " << type.pretty(); + throw 0; + } + + assert(type.is_not_nil()); +} diff --git a/src/cpp/cpp_typecheck_using.cpp b/src/cpp/cpp_typecheck_using.cpp new file mode 100644 index 00000000000..84ba26c96bb --- /dev/null +++ b/src/cpp/cpp_typecheck_using.cpp @@ -0,0 +1,81 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "cpp_typecheck.h" + +/*******************************************************************\ + +Function: cpp_typecheckt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cpp_typecheckt::convert(cpp_usingt &cpp_using) +{ + // there are two forms of using clauses: + // a) using namespace SCOPE; ("using directive") + // b) using SCOPE::id; ("using declaration") + + cpp_typecheck_resolvet resolver(*this); + cpp_save_scopet save_scope(this->cpp_scopes); + + std::string base_name; + cpp_template_args_non_tct template_args; + resolver.resolve_scope(cpp_using.name(), base_name, template_args); + + bool qualified=cpp_using.name().is_qualified(); + cpp_scopest::id_sett id_set; + + cpp_scopes.current_scope().lookup( + base_name, qualified?cpp_scopet::QUALIFIED:cpp_scopet::RECURSIVE, id_set); + + bool using_directive=cpp_using.get_namespace(); + + if(id_set.empty()) + { + err_location(cpp_using.name().location()); + str << "using " + << (using_directive?"namespace":"identifier") + << " `" + << base_name << "' not found"; + throw 0; + } + + // go back to where we used to be + save_scope.restore(); + + for(cpp_scopest::id_sett::iterator + it=id_set.begin(); + it!=id_set.end(); + it++) + { + if(using_directive) + { + if((*it)->id_class==cpp_idt::NAMESPACE) + cpp_scopes.current_scope().add_using_scope(static_cast(**it)); + else + { + // we should likely complain about this + } + } + else // declaration + { + // we copy all 'normal' identifiers into the current scope + if((*it)->id_class!=cpp_idt::TEMPLATE_ARGUMENT && + (*it)->id_class!=cpp_idt::NAMESPACE) + cpp_scopes.current_scope().insert(**it); + } + } +} diff --git a/src/cpp/cpp_typecheck_virtual_table.cpp b/src/cpp/cpp_typecheck_virtual_table.cpp new file mode 100644 index 00000000000..5a1137ccc7e --- /dev/null +++ b/src/cpp/cpp_typecheck_virtual_table.cpp @@ -0,0 +1,96 @@ +/*******************************************************************\ + +Function: cpp_typecheckt::do_virtual_table + +Inputs: + +Outputs: + +Purpose: + +\*******************************************************************/ + +#include +#include +#include +#include "cpp_typecheck.h" + +void cpp_typecheckt::do_virtual_table(const symbolt& symbol) +{ + + // builds virtual-table value maps: (class x virtual_name x value) + std::map > vt_value_maps; + + const struct_typet& struct_type = to_struct_type(symbol.type); + for(unsigned i = 0; i < struct_type.components().size(); i++) + { + const struct_typet::componentt& compo = struct_type.components()[i]; + if(!compo.get_bool("is_virtual")) + continue; + + const code_typet& code_type = to_code_type(compo.type()); + assert(code_type.arguments().size() > 0); + + const pointer_typet& pointer_type = + static_cast(code_type.arguments()[0].type()); + + irep_idt class_id = pointer_type.subtype().get("identifier"); + + std::map& value_map = + vt_value_maps[class_id]; + + + exprt e = symbol_exprt(compo.get_name(),code_type); + + if(compo.get_bool("is_pure_virtual")) + { + pointer_typet pointer_type(code_type); + e = gen_zero(pointer_type); + assert(e.is_not_nil()); + value_map[compo.get("virtual_name")] = e; + } + else + { + address_of_exprt address(e); + value_map[compo.get("virtual_name")] = address; + } + } + + // create virtual-table symbol variables + for(std::map >::const_iterator cit = + vt_value_maps.begin(); cit != vt_value_maps.end(); cit++) + { + const std::map& value_map = cit->second; + + const symbolt& late_cast_symb = namespacet(context).lookup(cit->first); + const symbolt& vt_symb_type = namespacet(context).lookup("virtual_table::"+late_cast_symb.name.as_string()); + + symbolt vt_symb_var; + vt_symb_var.name= vt_symb_type.name.as_string() + "@"+ symbol.name.as_string(); + vt_symb_var.base_name= vt_symb_type.base_name.as_string() + "@" + symbol.base_name.as_string(); + vt_symb_var.mode=current_mode; + vt_symb_var.module=module; + vt_symb_var.location=vt_symb_type.location; + vt_symb_var.type = symbol_typet(vt_symb_type.name); + vt_symb_var.lvalue = true; + vt_symb_var.static_lifetime = true; + + // do the values + const struct_typet& vt_type = to_struct_type(vt_symb_type.type); + exprt values("struct",symbol_typet(vt_symb_type.name)); + for(unsigned i=0; i < vt_type.components().size(); i++) + { + const struct_typet::componentt& compo = vt_type.components()[i]; + std::map::const_iterator cit2 = + value_map.find( compo.get("base_name")); + assert(cit2 != value_map.end()); + const exprt& value = cit2->second; + assert(value.type() == compo.type()); + values.operands().push_back(value); + } + vt_symb_var.value = values; + + bool failed = context.move(vt_symb_var); + assert(!failed); + } +} diff --git a/src/cpp/cpp_using.h b/src/cpp/cpp_using.h new file mode 100644 index 00000000000..d0ca8929373 --- /dev/null +++ b/src/cpp/cpp_using.h @@ -0,0 +1,42 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_CPP_USING_H +#define CPROVER_CPP_USING_H + +#include "cpp_name.h" + +class cpp_usingt:public irept +{ +public: + cpp_usingt():irept(ID_cpp_using) + { + } + + cpp_namet &name() + { + return (cpp_namet &)add(ID_name); + } + + const cpp_namet &name() const + { + return (cpp_namet &)find(ID_name); + } + + bool get_namespace() const + { + return get_bool(ID_namespace); + } + + void set_namespace(bool value) + { + set(ID_namespace, value); + } +}; + +#endif diff --git a/src/cpp/cpp_util.cpp b/src/cpp/cpp_util.cpp new file mode 100644 index 00000000000..2ed4fd6dda1 --- /dev/null +++ b/src/cpp/cpp_util.cpp @@ -0,0 +1,36 @@ +/*******************************************************************\ + +Module: + +Author: + +\*******************************************************************/ + +#include +#include + +#include "cpp_util.h" + +/*******************************************************************\ + +Function: cpp_symbol_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt cpp_symbol_expr(const symbolt &symbol) +{ + exprt tmp(ID_symbol, symbol.type); + tmp.set(ID_identifier, symbol.name); + + if(symbol.lvalue) + tmp.set(ID_C_lvalue, true); + + return tmp; +} + diff --git a/src/cpp/cpp_util.h b/src/cpp/cpp_util.h new file mode 100644 index 00000000000..8f9f3fbb562 --- /dev/null +++ b/src/cpp/cpp_util.h @@ -0,0 +1,48 @@ +/*******************************************************************\ + +Module: + +Author: + +\*******************************************************************/ + +#ifndef CPROVER_CPP_UTIL_H +#define CPROVER_CPP_UTIL_H + +#include +#include + +/*******************************************************************\ + +Function: cpp_symbol_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt cpp_symbol_expr(const symbolt &symbol); + +/*******************************************************************\ + +Function: already_typechecked + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +extern inline void already_typechecked(irept &irep) +{ + exprt tmp("already_typechecked"); + tmp.copy_to_operands(static_cast(irep)); + irep.swap(tmp); +} + +#endif diff --git a/src/cpp/expr2cpp.cpp b/src/cpp/expr2cpp.cpp new file mode 100644 index 00000000000..a2f93214b44 --- /dev/null +++ b/src/cpp/expr2cpp.cpp @@ -0,0 +1,570 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include + +#include + +#include "expr2cpp.h" + +class expr2cppt:public expr2ct +{ +public: + expr2cppt(const namespacet &_ns):expr2ct(_ns) { } + + virtual std::string convert(const exprt &src) + { + return expr2ct::convert(src); + } + + virtual std::string convert(const typet &src) + { + return expr2ct::convert(src); + } + +protected: + virtual std::string convert(const exprt &src, unsigned &precedence); + virtual std::string convert_cpp_this(const exprt &src, unsigned precedence); + virtual std::string convert_cpp_new(const exprt &src, unsigned precedence); + virtual std::string convert_extractbit(const exprt &src, unsigned precedence); + virtual std::string convert_extractbits(const exprt &src, unsigned precedence); + virtual std::string convert_code_cpp_delete(const exprt &src, unsigned precedence); + virtual std::string convert_struct(const exprt &src, unsigned &precedence); + virtual std::string convert_code(const codet &src, unsigned indent); + virtual std::string convert_constant(const exprt &src, unsigned &precedence); + + virtual std::string convert_rec( + const typet &src, + const c_qualifierst &qualifiers); + + typedef hash_set_cont id_sett; +}; + +/*******************************************************************\ + +Function: expr2cppt::convert_struct + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_struct( + const exprt &src, + unsigned &precedence) +{ + const typet &full_type=ns.follow(src.type()); + + if(full_type.id()!=ID_struct) + return convert_norep(src, precedence); + + const struct_typet &struct_type=to_struct_type(full_type); + + std::string dest="{ "; + + const struct_typet::componentst &components= + struct_type.components(); + + assert(components.size()==src.operands().size()); + + exprt::operandst::const_iterator o_it=src.operands().begin(); + + bool first=true; + unsigned last_size=0; + + for(struct_typet::componentst::const_iterator + c_it=components.begin(); + c_it!=components.end(); + c_it++) + { + if(c_it->type().id()==ID_code) + { + } + else + { + std::string tmp=convert(*o_it); + std::string sep; + + if(first) + first=false; + else + { + if(last_size+40get_string(ID_pretty_name); + dest+="="; + dest+=tmp; + } + + o_it++; + } + + dest+=" }"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2cppt::convert_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_constant( + const exprt &src, + unsigned &precedence) +{ + if(src.type().id()==ID_bool) + { + // C++ has built-in Boolean constants, in contrast to C + if(src.is_true()) + return "true"; + else if(src.is_false()) + return "false"; + } + + return expr2ct::convert_constant(src, precedence); +} + +/*******************************************************************\ + +Function: expr2cppt::convert_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_rec( + const typet &src, + const c_qualifierst &qualifiers) +{ + c_qualifierst new_qualifiers(qualifiers); + new_qualifiers.read(src); + + if(is_reference(src)) + { + return new_qualifiers.as_string()+convert(src.subtype())+" &"; + } + else if(is_rvalue_reference(src)) + { + return new_qualifiers.as_string()+convert(src.subtype())+" &&"; + } + else if(src.get(ID_C_cpp_type)!=irep_idt()) + { + const irep_idt cpp_type=src.get(ID_C_cpp_type); + + if(cpp_type==ID_signed_char) + return new_qualifiers.as_string()+"signed char"; + else if(cpp_type==ID_unsigned_char) + return new_qualifiers.as_string()+"unsigned char"; + else if(cpp_type==ID_char) + return new_qualifiers.as_string()+"char"; + else if(cpp_type==ID_signed_short_int) + return new_qualifiers.as_string()+"short"; + else if(cpp_type==ID_unsigned_short_int) + return new_qualifiers.as_string()+"unsigned short"; + else if(cpp_type==ID_signed_int) + return new_qualifiers.as_string()+"int"; + else if(cpp_type==ID_unsigned_int) + return new_qualifiers.as_string()+"unsigned"; + else if(cpp_type==ID_signed_long_int) + return new_qualifiers.as_string()+"long"; + else if(cpp_type==ID_unsigned_long_int) + return new_qualifiers.as_string()+"unsigned long"; + else if(cpp_type==ID_signed_long_long_int) + return new_qualifiers.as_string()+"long long"; + else if(cpp_type==ID_unsigned_long_long_int) + return new_qualifiers.as_string()+"unsigned long long"; + else if(cpp_type==ID_wchar_t) + return new_qualifiers.as_string()+"wchar_t"; + else if(cpp_type==ID_float) + return new_qualifiers.as_string()+"float"; + else if(cpp_type==ID_double) + return new_qualifiers.as_string()+"double"; + else if(cpp_type==ID_long_double) + return new_qualifiers.as_string()+"long double"; + else + return expr2ct::convert_rec(src, qualifiers); + } + else if(src.id()==ID_symbol) + { + const irep_idt &identifier=src.get(ID_identifier); + + const symbolt &symbol=ns.lookup(identifier); + + if(symbol.type.id()==ID_struct || + symbol.type.id()==ID_incomplete_struct) + { + std::string dest=new_qualifiers.as_string(); + + if(symbol.type.get_bool(ID_C_class)) + dest+="class"; + else + dest+="struct"; + + if(symbol.pretty_name!=irep_idt()) + dest+=" "+id2string(symbol.pretty_name); + + return dest; + } + else if(symbol.type.id()==ID_c_enum) + { + std::string dest=new_qualifiers.as_string(); + + dest+="enum"; + + if(symbol.pretty_name!=irep_idt()) + dest+=" "+id2string(symbol.pretty_name); + + return dest; + } + else + return expr2ct::convert_rec(src, qualifiers); + } + else if(src.id()==ID_struct || + src.id()==ID_incomplete_struct) + { + std::string dest=new_qualifiers.as_string(); + + if(src.get_bool(ID_C_class)) + dest+="class"; + else + dest+="struct"; + + return dest; + } + else if(src.id()==ID_constructor) + { + return "constructor "; + } + else if(src.id()==ID_destructor) + { + return "destructor "; + } + else if(src.id()=="cpp-template-type") + { + return "typename"; + } + else if(src.id()==ID_template) + { + std::string dest="template<"; + + const irept::subt &arguments=src.find(ID_arguments).get_sub(); + + forall_irep(it, arguments) + { + if(it!=arguments.begin()) dest+=", "; + + const exprt &argument=(const exprt &)*it; + + if(argument.id()==ID_symbol) + { + dest+=convert(argument.type())+" "; + dest+=convert(argument); + } + else if(argument.id()==ID_type) + dest+=convert(argument.type()); + else + dest+=argument.to_string(); + } + + dest+="> "+convert(src.subtype()); + return dest; + } + else if(src.id()==ID_pointer && src.find("to-member").is_not_nil()) + { + typet tmp=src; + typet member; + member.swap(tmp.add("to-member")); + + std::string dest = "(" + convert_rec(member, c_qualifierst()) + ":: *)"; + + if(src.subtype().id()==ID_code) + { + const code_typet& code_type = to_code_type(src.subtype()); + const typet& return_type = code_type.return_type(); + dest = convert_rec(return_type, c_qualifierst()) +" " + dest; + + const code_typet::argumentst& args = code_type.arguments(); + dest += "("; + + if(args.size() > 0) + dest += convert_rec(args[0].type(), c_qualifierst()); + + for(unsigned i = 1; i < args.size();i++) + dest += ", " + convert_rec(args[i].type(), c_qualifierst()); + dest += ")"; + } + else + { + dest = convert_rec(src.subtype(),c_qualifierst()) + " " + dest; + } + return dest; + } + else if(src.id()==ID_verilogbv) + return "sc_lv["+id2string(src.get(ID_width))+"]"; + else if(src.id()==ID_unassigned) + return "?"; + else + return expr2ct::convert_rec(src, qualifiers); +} + +/*******************************************************************\ + +Function: expr2cppt::convert_cpp_this + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_cpp_this( + const exprt &src, + unsigned precedence) +{ + return "this"; +} + +/*******************************************************************\ + +Function: expr2cppt::convert_cpp_new + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_cpp_new( + const exprt &src, + unsigned precedence) +{ + std::string dest; + + if(src.get(ID_statement)==ID_cpp_new_array) + { + dest="new"; + + std::string tmp_size= + convert(static_cast(src.find(ID_size))); + + dest+=" "; + dest+=convert(src.type().subtype()); + dest+="["; + dest+=tmp_size; + dest+="]"; + } + else + dest="new "+convert(src.type().subtype()); + + return dest; +} + +/*******************************************************************\ + +Function: expr2cppt::convert_code_cpp_delete + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_code_cpp_delete( + const exprt &src, + unsigned indent) +{ + std::string dest=indent_str(indent)+"delete "; + + if(src.operands().size()!=1) + { + unsigned precedence; + return convert_norep(src, precedence); + } + + std::string tmp=convert(src.op0()); + + dest+=tmp+";\n"; + + return dest; +} + +/*******************************************************************\ + +Function: expr2cppt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert( + const exprt &src, + unsigned &precedence) +{ + if(src.id()=="cpp-this") + return convert_cpp_this(src, precedence=15); + if(src.id()==ID_extractbit) + return convert_extractbit(src, precedence=15); + else if(src.id()==ID_extractbits) + return convert_extractbits(src, precedence=15); + else if(src.id()==ID_sideeffect && + (src.get(ID_statement)==ID_cpp_new || + src.get(ID_statement)==ID_cpp_new_array)) + return convert_cpp_new(src, precedence=15); + else if(src.is_constant() && src.type().id() == ID_verilogbv) + return "'" + id2string(src.get(ID_value)) + "'"; + else if(src.id()==ID_unassigned) + return "?"; + else if(src.id()=="pod_constructor") + return "pod_constructor"; + else + return expr2ct::convert(src, precedence); +} + +/*******************************************************************\ + +Function: expr2cppt::convert_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_code( + const codet &src, + unsigned indent) +{ + const irep_idt &statement=src.get(ID_statement); + + if(statement==ID_cpp_delete || + statement==ID_cpp_delete_array) + return convert_code_cpp_delete(src, indent); + + if(statement==ID_cpp_new || + statement==ID_cpp_new_array) + return convert_cpp_new(src,indent); + + return expr2ct::convert_code(src, indent); +} + + +/*******************************************************************\ + +Function: expr2cppt::extractbit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_extractbit( + const exprt &src, + unsigned precedence) +{ + assert(src.operands().size() == 2); + return convert(src.op0()) + "[" + convert(src.op1()) + "]"; +} + +/*******************************************************************\ + +Function: expr2cppt::extractbit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cppt::convert_extractbits( + const exprt &src, + unsigned precedence) +{ + assert(src.operands().size() == 3); + return convert(src.op0()) + ".range(" + convert(src.op1()) + "," + + convert(src.op2()) + ")"; +} + +/*******************************************************************\ + +Function: expr2cpp + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string expr2cpp(const exprt &expr, const namespacet &ns) +{ + expr2cppt expr2cpp(ns); + expr2cpp.get_shorthands(expr); + return expr2cpp.convert(expr); +} + +/*******************************************************************\ + +Function: type2cpp + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string type2cpp(const typet &type, const namespacet &ns) +{ + expr2cppt expr2cpp(ns); + return expr2cpp.convert(type); +} diff --git a/src/cpp/expr2cpp.h b/src/cpp/expr2cpp.h new file mode 100644 index 00000000000..0d221310f06 --- /dev/null +++ b/src/cpp/expr2cpp.h @@ -0,0 +1,18 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_EXPR2CPP_H +#define CPROVER_EXPR2CPP_H + +#include +#include + +std::string expr2cpp(const exprt &expr, const namespacet &ns); +std::string type2cpp(const typet &type, const namespacet &ns); + +#endif diff --git a/src/cpp/internal_additions.cpp b/src/cpp/internal_additions.cpp new file mode 100644 index 00000000000..cf6b4cd9a63 --- /dev/null +++ b/src/cpp/internal_additions.cpp @@ -0,0 +1,140 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include "internal_additions.h" + +/*******************************************************************\ + +Function: c2cpp + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string c2cpp(const std::string &s) +{ + std::string result; + + result.reserve(s.size()); + + for(unsigned i=0; i\"" << std::endl; + + // new and delete are in the root namespace! + out << "void operator delete(void *);" << std::endl; + out << "void *operator new(__typeof__(sizeof(int)));" << std::endl; + + // auxiliaries for new/delete + out << "extern \"C\" void *__new(__typeof__(sizeof(int)));" << std::endl; + out << "extern \"C\" void *__new_array(__typeof__(sizeof(int)), __typeof__(sizeof(int)));" << std::endl; + out << "extern \"C\" void __delete(void *);" << std::endl; + out << "extern \"C\" void __delete_array(void *);" << std::endl; + out << "extern \"C\" void *__placement_new(__typeof__(sizeof(int)), void *);" << std::endl; + + // __CPROVER namespace + out << "namespace __CPROVER { }" << std::endl; + + // types + out << "typedef __typeof__(sizeof(int)) __CPROVER::size_t;" << std::endl; + + // assume/assert + out << "extern \"C\" void assert(bool assertion);" << std::endl; + out << "extern \"C\" void __CPROVER_assume(bool assumption);" << std::endl; + out << "extern \"C\" void __CPROVER_assert(" + "bool assertion, const char *description);" << std::endl; + out << "extern \"C\" void __CPROVER::assume(bool assumption);" << std::endl; + out << "extern \"C\" void __CPROVER::assert(" + "bool assertion, const char *description);" << std::endl; + + // CPROVER extensions + out << "extern \"C\" const unsigned __CPROVER::constant_infinity_uint;" << std::endl; + out << "extern \"C\" void __CPROVER_initialize();" << std::endl; + out << "extern \"C\" void __CPROVER::input(const char *id, ...);" << std::endl; + out << "extern \"C\" void __CPROVER::output(const char *id, ...);" << std::endl; + out << "extern \"C\" void __CPROVER::cover(bool condition);" << std::endl; + + // pointers + out << "extern \"C\" unsigned __CPROVER_POINTER_OBJECT(const void *p);" << std::endl; + out << "extern \"C\" signed __CPROVER_POINTER_OFFSET(const void *p);" << std::endl; + out << "extern \"C\" bool __CPROVER_DYNAMIC_OBJECT(const void *p);" << std::endl; + out << "extern \"C\" extern unsigned char __CPROVER_memory[__CPROVER::constant_infinity_uint];" << std::endl; + + // malloc + out << "extern \"C\" void *__CPROVER_malloc(__CPROVER::size_t size);" << std::endl; + out << "extern \"C\" const void *__CPROVER_deallocated=0;" << std::endl; + out << "extern \"C\" const void *__CPROVER_malloc_object=0;" << std::endl; + out << "extern \"C\" __CPROVER::size_t __CPROVER_malloc_size;" << std::endl; + + // float + out << "extern \"C\" int __CPROVER_rounding_mode;" << std::endl; + + // arrays + out << "bool __CPROVER::array_equal(const void array1[], const void array2[]);" << std::endl; + out << "void __CPROVER::array_copy(const void dest[], const void src[]);" << std::endl; + out << "void __CPROVER::array_set(const void dest[], ...);" << std::endl; + + // GCC stuff + out << "extern \"C\" {" << std::endl; + out << c2cpp(GCC_BUILTIN_HEADERS); + out << "}" << std::endl; + + if(config.ansi_c.os==configt::ansi_ct::OS_WIN) + out << "extern \"C\" int __noop(...);\n"; // this is Visual C/C++ + + std::string architecture_strings; + ansi_c_architecture_strings(architecture_strings); + + out << "extern \"C\" {" << std::endl; + out << architecture_strings; + out << "}" << std::endl; + + // type_info infrastructure -- the standard wants this to be in the + // std:: namespace, but MS has it in the root namespace + if(config.ansi_c.mode==configt::ansi_ct::MODE_VISUAL_STUDIO) + out << "class type_info;" << std::endl; +} + diff --git a/src/cpp/internal_additions.h b/src/cpp/internal_additions.h new file mode 100644 index 00000000000..e6b6c1330bd --- /dev/null +++ b/src/cpp/internal_additions.h @@ -0,0 +1,11 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +void cpp_internal_additions(std::ostream &code); diff --git a/src/cpp/parse.cpp b/src/cpp/parse.cpp new file mode 100644 index 00000000000..4258b8c2094 --- /dev/null +++ b/src/cpp/parse.cpp @@ -0,0 +1,6209 @@ +#include + +#include +#include +#include +#include + +#include + +#include "tokens.h" +#include "cpp_token_buffer.h" +#include "cpp_parser.h" +#include "cpp_member_spec.h" +#include "cpp_enum_type.h" + +//#define DEBUG + +class Parser +{ +public: + cpp_token_buffert *lex; + cpp_parsert *parser; + + bool parse(); + +protected: + typedef irept Ptree; + + enum DeclKind { kDeclarator, kArgDeclarator, kCastDeclarator }; + enum TemplateDeclKind { tdk_unknown, tdk_decl, tdk_instantiation, + tdk_specialization, num_tdks }; + typedef cpp_tokent Token; + + // rules + bool rProgram(cpp_itemt &item); + + bool SyntaxError(); + void ShowMessageHead(char*); + + bool rDefinition(cpp_itemt &); + bool rNullDeclaration(cpp_declarationt &); + bool rTypedef(cpp_declarationt &); + bool rTypedefStatement(codet &); + bool rTypeSpecifier(typet &, bool); + bool isTypeSpecifier(); + bool rLinkageSpec(cpp_linkage_spect &); + bool rNamespaceSpec(cpp_namespace_spect &); + bool rUsing(cpp_usingt &); + bool rLinkageBody(cpp_linkage_spect::itemst &); + bool rTemplateDecl(cpp_declarationt &); + bool rTemplateDecl2(typet &, TemplateDeclKind &kind); + bool rTempArgList(irept &); + bool rTempArgDeclaration(cpp_declarationt &); + bool rExternTemplateDecl(Ptree &); + + bool rDeclaration(cpp_declarationt &); + bool rIntegralDeclaration(cpp_declarationt &, cpp_storage_spect &, cpp_member_spect &, typet &, typet &); + bool rConstDeclaration(cpp_declarationt &, cpp_storage_spect &, cpp_member_spect &, typet &); + bool rOtherDeclaration(cpp_declarationt &, cpp_storage_spect &, cpp_member_spect &, typet &); + bool rCondition(exprt &); + bool rSimpleDeclaration(cpp_declarationt &); + + bool isConstructorDecl(); + bool isPtrToMember(int); + bool optMemberSpec(cpp_member_spect &); + bool optStorageSpec(cpp_storage_spect &); + bool optCvQualify(typet &); + bool rAttribute(); + bool optIntegralTypeOrClassSpec(typet &); + bool rConstructorDecl(cpp_declaratort &, typet &); + bool optThrowDecl(Ptree &); + + bool rDeclarators(cpp_declarationt::declaratorst &, bool, bool=false); + bool rDeclaratorWithInit(cpp_declaratort &, bool, bool); + bool rDeclarator(cpp_declaratort &, DeclKind, bool, bool, bool=false); + bool rDeclaratorQualifier(); + bool optPtrOperator(typet &); + bool rMemberInitializers(Ptree &); + bool rMemberInit(exprt &); + + bool rName(Ptree &); + bool rOperatorName(irept &); + bool rCastOperatorName(Ptree &); + bool rPtrToMember(Ptree &); + bool rTemplateArgs(irept &); + + bool rArgDeclListOrInit(exprt &, bool&, bool); + bool rArgDeclList(Ptree &); + bool rArgDeclaration(cpp_declarationt &); + + bool rFunctionArguments(exprt &); + bool rInitializeExpr(exprt &); + + bool rEnumSpec(typet &); + bool rEnumBody(irept &); + bool rClassSpec(typet &); + bool rBaseSpecifiers(irept &); + bool rClassBody(exprt &); + bool rClassMember(cpp_itemt &); + bool rAccessDecl(Ptree &); + + bool rCommaExpression(exprt &); + + bool rExpression(exprt &); + bool rConditionalExpr(exprt &); + bool rLogicalOrExpr(exprt &, bool); + bool rLogicalAndExpr(exprt &, bool); + bool rInclusiveOrExpr(exprt &, bool); + bool rExclusiveOrExpr(exprt &, bool); + bool rAndExpr(exprt &, bool); + bool rEqualityExpr(exprt &, bool); + bool rRelationalExpr(exprt &, bool); + bool rShiftExpr(exprt &); + bool rAdditiveExpr(exprt &); + bool rMultiplyExpr(exprt &); + bool rPmExpr(exprt &); + bool rCastExpr(exprt &); + bool rTypeName(typet &); + bool rUnaryExpr(exprt &); + bool rThrowExpr(exprt &); + bool rSizeofExpr(exprt &); + bool rTypeidExpr(exprt &); + bool isAllocateExpr(int); + bool rAllocateExpr(exprt &); + bool rAllocateType(exprt &, typet &, exprt &); + bool rNewDeclarator(typet &); + bool rAllocateInitializer(exprt &); + bool rPostfixExpr(exprt &); + bool rPrimaryExpr(exprt &); + bool rMSCTypePredicate(exprt &); + bool rVarName(exprt &); + bool rVarNameCore(exprt &); + bool isTemplateArgs(); + + bool rFunctionBody(codet &); + bool rCompoundStatement(codet &); + bool rStatement(codet &); + bool rIfStatement(codet &); + bool rSwitchStatement(codet &); + bool rWhileStatement(codet &); + bool rDoStatement(codet &); + bool rForStatement(codet &); + bool rTryStatement(codet &); + bool rGCCAsmStatement(codet &); + bool rMSCAsmStatement(codet &); + + bool rExprStatement(codet &); + bool rDeclarationStatement(codet &); + bool rIntegralDeclStatement(codet &, cpp_storage_spect &, typet &, typet &); + bool rOtherDeclStatement(codet &, cpp_storage_spect &, typet &); + + bool MaybeTypeNameOrClassTemplate(Token&); + void SkipTo(int token); + bool moreVarName(); + + bool rStringL(Token &tk); + + unsigned number_of_errors; + + void merge_types(typet &src, typet &dest); + + void set_location(irept &dest, const cpp_tokent &token) + { + locationt &location=(locationt &)dest.add(ID_C_location); + location.set_file(token.filename); + location.set_line(token.line_no); + } + + void make_subtype(typet &src, typet &dest) + { + typet *p=&dest; + while(p->id()!=irep_idt() && p->is_not_nil()) + p=&p->subtype(); + p->swap(src); + } +}; + +const unsigned int MaxErrors=10; + +bool Parser::rStringL(Token &tk) +{ + if(lex->GetToken(tk)!=StringL) + return false; + + // merge with following string literals + if(lex->LookAhead(0)==StringL) + { + exprt &dest=tk.data; + + while(lex->LookAhead(0)==StringL) + { + Token tk2; + lex->GetToken(tk2); + concatenate_strings(dest, tk2.data); + } + } + + return true; +} + +void Parser::merge_types(typet &src, typet &dest) +{ + if(src.is_nil()) return; + + if(dest.is_nil()) + dest.swap(src); + else + { + if(dest.id()!=ID_merged_type) + { + typet tmp(ID_merged_type); + tmp.move_to_subtypes(dest); + dest.swap(tmp); + } + + dest.move_to_subtypes(src); + } +} + +bool Parser::SyntaxError() +{ + #define ERROR_TOKENS 4 + + Token t[ERROR_TOKENS]; + + for(unsigned i=0; iLookAhead(i, t[i]); + + if(t[0].kind!='\0') + { + locationt location; + location.set_file(t[0].filename); + location.set_line(i2string(t[0].line_no)); + + std::string message="parse error before `"; + + for(unsigned i=0; iprint(1, message, -1, location); + } + + return bool(++number_of_errors < MaxErrors); +} + +bool Parser::rProgram(cpp_itemt &item) +{ + while(lex->LookAhead(0)!='\0') + if(rDefinition(item)) + return true; + else + { + Token tk; + + if(!SyntaxError()) + return false; // too many errors + + SkipTo(';'); + lex->GetToken(tk); // ignore ';' + } + + return false; +} + +/* + definition + : null.declaration + | typedef + | template.decl + | linkage.spec + | namespace.spec + | using.declaration + | extern.template.decl + | declaration +*/ +bool Parser::rDefinition(cpp_itemt &item) +{ + bool res; + + int t=lex->LookAhead(0); + + if(t==';') + res=rNullDeclaration(item.make_declaration()); + else if(t==TYPEDEF) + res=rTypedef(item.make_declaration()); + else if(t==TEMPLATE) + res=rTemplateDecl(item.make_declaration()); + else if(t==EXTERN && lex->LookAhead(1)==StringL) + res=rLinkageSpec(item.make_linkage_spec()); + else if(t==EXTERN && lex->LookAhead(1)==TEMPLATE) + res=rExternTemplateDecl(item.make_declaration()); + else if(t==NAMESPACE) + res=rNamespaceSpec(item.make_namespace_spec()); + else if(t==USING) + res=rUsing(item.make_using()); + else + res=rDeclaration(item.make_declaration()); + + return res; +} + +bool Parser::rNullDeclaration(cpp_declarationt &decl) +{ + Token tk; + + if(lex->GetToken(tk)!=';') + return false; + + set_location(decl, tk); + + return true; +} + +/* + typedef + : TYPEDEF type.specifier declarators ';' +*/ +bool Parser::rTypedef(cpp_declarationt &declaration) +{ + Token tk; + typet type_name; + + if(lex->GetToken(tk)!=TYPEDEF) + return false; + + #ifdef DEBUG + std::cout << "Parser::rTypedef 1\n"; + #endif + + declaration=cpp_declarationt(); + set_location(declaration, tk); + + declaration.type()=typet(ID_typedef); + + if(!rTypeSpecifier(type_name, false)) + return false; + + merge_types(type_name, declaration.type()); + + #ifdef DEBUG + std::cout << "Parser::rTypedef 2\n"; + #endif + + if(!rDeclarators(declaration.declarators(), true)) + return false; + + if(lex->GetToken(tk)!=';') + return false; + + #ifdef DEBUG + std::cout << "Parser::rTypedef 3\n"; + #endif + + return true; +} + +bool Parser::rTypedefStatement(codet &statement) +{ + statement=codet(ID_decl); + statement.operands().resize(1); + return rTypedef((cpp_declarationt &)statement.op0()); +} + +/* + type.specifier + : {cv.qualify} (integral.or.class.spec | name) {cv.qualify} +*/ +bool Parser::rTypeSpecifier(typet &tspec, bool check) +{ + typet cv_q; + + cv_q.make_nil(); + + if(!optCvQualify(cv_q)) + return false; + + if(!optIntegralTypeOrClassSpec(tspec)) + return false; + + if(tspec.is_nil()) + { + Token tk; + lex->LookAhead(0, tk); + + if(check) + if(!MaybeTypeNameOrClassTemplate(tk)) + return false; + + if(!rName(tspec)) + return false; + } + + if(!optCvQualify(cv_q)) + return false; + + merge_types(cv_q, tspec); + + return true; +} + +// isTypeSpecifier() returns true if the next is probably a type specifier. + +bool Parser::isTypeSpecifier() +{ + int t=lex->LookAhead(0); + + if(t==Identifier || t==Scope + || t==CONST || t==VOLATILE + || t==CHAR || t==INT || t==SHORT || t==LONG + || t==WCHAR || t==COMPLEX // new !!! + || t==SIGNED || t==UNSIGNED || t==FLOAT || t==DOUBLE + || t==WCHAR_T + || t==INT8 || t==INT16 || t==INT32 || t==INT64 || t==PTR32 || t==PTR64 + || t==VOID || t==BOOLEAN + || t==CLASS || t==STRUCT || t==UNION || t==ENUM + || t==TYPENAME + || t==INT64 + || t==TYPEOF + || t==DECLTYPE + ) + return true; + + return false; +} + +/* + linkage.spec + : EXTERN StringL definition + | EXTERN StringL linkage.body +*/ +bool Parser::rLinkageSpec(cpp_linkage_spect &linkage_spec) +{ + Token tk1, tk2; + + if(lex->GetToken(tk1)!=EXTERN) + return false; + + if(!rStringL(tk2)) + return false; + + linkage_spec=cpp_linkage_spect(); + set_location(linkage_spec, tk1); + linkage_spec.linkage().swap(tk2.data); + set_location(linkage_spec.linkage(), tk2); + + if(lex->LookAhead(0)=='{') + { + if(!rLinkageBody(linkage_spec.items())) + return false; + } + else + { + cpp_itemt item; + + if(!rDefinition(item)) + return false; + + linkage_spec.items().push_back(item); + } + + return true; +} + +/* + namespace.spec + : NAMESPACE Identifier definition + | NAMESPACE { Identifier } linkage.body +*/ + +bool Parser::rNamespaceSpec(cpp_namespace_spect &namespace_spec) +{ + Token tk1, tk2; + std::string name; + if(lex->GetToken(tk1)!=NAMESPACE) + return false; + + if(lex->LookAhead(0)=='{') + name=""; + else + if(lex->GetToken(tk2)==Identifier) + name.swap(tk2.text); + else + return false; + + namespace_spec=cpp_namespace_spect(); + set_location(namespace_spec, tk1); + namespace_spec.set_namespace(name); + + if(lex->LookAhead(0)=='{') + { + if(!rLinkageBody(namespace_spec.items())) + return false; + } + else + { + cpp_itemt item; + + if(!rDefinition(item)) + return false; + + namespace_spec.items().push_back(item); + } + + return true; +} + +/* + using.declaration : USING { NAMESPACE } name ';' +*/ +bool Parser::rUsing(cpp_usingt &cpp_using) +{ + Token tk; + + if(lex->GetToken(tk)!=USING) + return false; + + cpp_using=cpp_usingt(); + set_location(cpp_using, tk); + + if(lex->LookAhead(0)==NAMESPACE) + { + lex->GetToken(tk); + cpp_using.set_namespace(true); + } + + if(!rName(cpp_using.name())) + return false; + + if(lex->GetToken(tk)!=';') + return false; + + return true; +} + +/* + linkage.body : '{' (definition)* '}' + + Note: this is also used to construct namespace.spec +*/ +bool Parser::rLinkageBody(cpp_linkage_spect::itemst &items) +{ + Token op, cp; + + if(lex->GetToken(op)!='{') + return false; + + items.clear(); + while(lex->LookAhead(0)!='}') + { + cpp_itemt item; + + if(!rDefinition(item)) + { + if(!SyntaxError()) + return false; // too many errors + + SkipTo('}'); + lex->GetToken(cp); + items.push_back(item); + return true; // error recovery + } + + items.push_back(item); + } + + lex->GetToken(cp); + return true; +} + +/* + template.decl + : TEMPLATE '<' temp.arg.list '>' declaration + | TEMPLATE declaration + | TEMPLATE '<' '>' declaration + + The second case is an explicit template instantiation. declaration must + be a class declaration. For example, + + template class Foo; + + explicitly instantiates the template Foo with int and char. + + The third case is a specialization of a function template. declaration + must be a function template. For example, + + template <> int count(String x) { return x.length; } +*/ +bool Parser::rTemplateDecl(cpp_declarationt &decl) +{ + TemplateDeclKind kind=tdk_unknown; + + typet template_type; + if(!rTemplateDecl2(template_type, kind)) + return false; + + cpp_declarationt body; + if(!rDeclaration(body)) + return false; + + // Repackage the decl and body depending upon what kind of template + // declaration was observed. + switch(kind) + { + case tdk_decl: + #ifdef DEBUG + std::cout << "BODY: " << body << std::endl; + std::cout << "TEMPLATE_TYPE: " << template_type << std::endl; + #endif + + body.add(ID_template_type).swap(template_type); + body.set(ID_is_template, true); + decl.swap(body); + break; + + case tdk_instantiation: + // Repackage the decl as a PtreeTemplateInstantiation + decl=body; + assert(0); + // assumes that decl has the form: [nil [class ...] ;] + #if 0 + if(Ptree::Length(decl)!=3) + return false; + + if(Ptree::First(decl).is_not_nil()) + return false; + + if(Ptree::Second(decl)->What()!=ntClassSpec) + return false; + + if(!Ptree::Eq(Ptree::Third(decl), ';')) + return false; + #endif + + //decl=new PtreeTemplateInstantiation(Ptree::Second(decl)); + break; + + case tdk_specialization: + body.add(ID_template_type).swap(template_type); + body.set(ID_is_template, true); + decl.swap(body); + break; + + default: + assert(0); + break; + } + + return true; +} + +bool Parser::rTemplateDecl2(typet &decl, TemplateDeclKind &kind) +{ + Token tk; + + if(lex->GetToken(tk)!=TEMPLATE) + return false; + + decl=typet(ID_template); + set_location(decl, tk); + + if(lex->LookAhead(0)!='<') + { + // template instantiation + kind=tdk_instantiation; + return true; // ignore TEMPLATE + } + + if(lex->GetToken(tk)!='<') + return false; + + irept &args=decl.add(ID_arguments); + + if(!rTempArgList(args)) + return false; + + if(lex->GetToken(tk)!='>') + return false; + + // ignore nested TEMPLATE + while (lex->LookAhead(0)==TEMPLATE) + { + lex->GetToken(tk); + if(lex->LookAhead(0)!='<') + break; + + lex->GetToken(tk); + irept dummy_args; + if(!rTempArgList(dummy_args)) + return false; + + if(lex->GetToken(tk)!='>') + return false; + } + + if(args.get_sub().empty()) + // template < > declaration + kind=tdk_specialization; + else + // template < ... > declaration + kind=tdk_decl; + + return true; +} + +/* + temp.arg.list + : empty + | temp.arg.declaration (',' temp.arg.declaration)* +*/ +bool Parser::rTempArgList(irept &args) +{ + if(lex->LookAhead(0)=='>') + return true; + + cpp_declarationt a; + if(!rTempArgDeclaration(a)) + return false; + + args.get_sub().push_back(get_nil_irep()); + args.get_sub().back().swap(a); + + while(lex->LookAhead(0)==',') + { + Token tk; + + lex->GetToken(tk); + if(!rTempArgDeclaration(a)) + return false; + + args.get_sub().push_back(get_nil_irep()); + args.get_sub().back().swap(a); + } + + return true; +} + +/* + temp.arg.declaration + : CLASS [Identifier] {'=' type.name} + | type.specifier arg.declarator {'=' additive.expr} + | template.decl2 CLASS Identifier {'=' type.name} +*/ +bool Parser::rTempArgDeclaration(cpp_declarationt &declaration) +{ + int t0=lex->LookAhead(0); + + if((t0==CLASS || t0==TYPENAME)) + { + Token tk1; + lex->GetToken(tk1); + + declaration=cpp_declarationt(); + set_location(declaration, tk1); + + declaration.set(ID_is_type, true); + declaration.type()=typet("cpp-template-type"); + + declaration.declarators().resize(1); + cpp_declaratort &declarator=declaration.declarators().front(); + + declarator=cpp_declaratort(); + declarator.name().make_nil(); + declarator.type().make_nil(); + set_location(declarator, tk1); + + if(lex->LookAhead(0) == Identifier) + { + cpp_namet cpp_name; + Token tk2; + lex->GetToken(tk2); + + exprt name(ID_name); + set_location(name, tk2); + name.set(ID_identifier, tk2.text); + set_location(name,tk1); + cpp_name.get_sub().push_back(name); + declarator.name().swap(cpp_name); + } + + if(lex->LookAhead(0)=='=') + { + typet default_type; + + lex->GetToken(tk1); + if(!rTypeName(default_type)) + return false; + + declarator.value()=exprt(ID_type); + declarator.value().type().swap(default_type); + } + } + else if(t0==TEMPLATE) + { + TemplateDeclKind kind; + + typet template_type; + + if(!rTemplateDecl2(template_type, kind)) + return false; + + // TODO! + + Token tk1, tk2; + + if(lex->GetToken(tk1)!=CLASS || lex->GetToken(tk2)!=Identifier) + return false; + + //Ptree cspec=new PtreeClassSpec(new LeafReserved(tk1), + // Ptree::Cons(new Leaf(tk2),nil), + // nil); + //decl=Ptree::Snoc(decl, cspec); + if(lex->LookAhead(0)=='=') + { + typet default_type; + lex->GetToken(tk1); + if(!rTypeName(default_type)) + return false; + + //decl=Ptree::Nconc(decl, Ptree::List(new Leaf(tk1), + // default_type)); + } + } + else + { + declaration=cpp_declarationt(); + declaration.set(ID_is_type, false); + + if(!rTypeSpecifier(declaration.type(), true)) + return false; + + declaration.declarators().resize(1); + cpp_declaratort &declarator=declaration.declarators().front(); + + if(!rDeclarator(declarator, kArgDeclarator, false, true)) + return false; + + exprt &value=declarator.value(); + + if(lex->LookAhead(0)=='=') + { + Token tk; + + lex->GetToken(tk); + if(!rAdditiveExpr(value)) + return false; + } + else + value.make_nil(); + } + + return true; +} + +/* + extern.template.decl + : EXTERN TEMPLATE declaration +*/ +bool Parser::rExternTemplateDecl(Ptree &decl) +{ + Token tk1, tk2; + + if(lex->GetToken(tk1)!=EXTERN) + return false; + + if(lex->GetToken(tk2)!=TEMPLATE) + return false; + + cpp_declarationt body; + if(!rDeclaration(body)) + return false; + + //decl=new PtreeExternTemplate(new Leaf(tk1), + // Ptree::List(new Leaf(tk2), body)); + return true; +} + +/* + declaration + : integral.declaration + | const.declaration + | other.declaration + + decl.head + : {member.spec} {storage.spec} {member.spec} {cv.qualify} + + integral.declaration + : integral.decl.head declarators (';' | function.body) + | integral.decl.head ';' + | integral.decl.head ':' expression ';' + + integral.decl.head + : decl.head integral.or.class.spec {cv.qualify} + + other.declaration + : decl.head name {cv.qualify} declarators (';' | function.body) + | decl.head name constructor.decl (';' | function.body) + | FRIEND name ';' + + const.declaration + : cv.qualify {'*'} Identifier '=' expression {',' declarators} ';' + + Note: if you modify this function, look at declaration.statement, too. + Note: this regards a statement like "T (a);" as a constructor + declaration. See isConstructorDecl(). +*/ + +bool Parser::rDeclaration(cpp_declarationt &declaration) +{ + #ifdef DEBUG + std::cout << "Parser::rDeclaration 0.1 token: " << lex->LookAhead(0) << std::endl; + #endif + + cpp_member_spect member_spec; + if(!optMemberSpec(member_spec)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rDeclaration 0.2\n"; + #endif + + cpp_storage_spect storage_spec; + if(!optStorageSpec(storage_spec)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rDeclaration 1\n"; + #endif + + if(member_spec.is_empty()) + if(!optMemberSpec(member_spec)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rDeclaration 3\n"; + #endif + + typet cv_q, integral; + cv_q.make_nil(); + + if(!optCvQualify(cv_q)) + return false; + + // added these two to do "const static volatile int i=1;" + if(!optStorageSpec(storage_spec)) + return false; + + if(!optCvQualify(cv_q)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rDeclaration 4\n"; + #endif + + if(!optIntegralTypeOrClassSpec(integral)) + return false; + + // added this one to do "void inline foo();" + if(member_spec.is_empty()) + if(!optMemberSpec(member_spec)) + return false; + + if(integral.is_not_nil()) + { + #ifdef DEBUG + std::cout << "Parser::rDeclaration 5\n"; + #endif + return rIntegralDeclaration(declaration, storage_spec, member_spec, integral, cv_q); + } + else + { + int t=lex->LookAhead(0); + + #ifdef DEBUG + std::cout << "Parser::rDeclaration 6 " << t << "\n"; + #endif + + if(cv_q.is_not_nil() && + ((t==Identifier && lex->LookAhead(1)=='=') || t=='*')) + return rConstDeclaration(declaration, storage_spec, member_spec, cv_q); + else + return rOtherDeclaration(declaration, storage_spec, member_spec, cv_q); + } +} + +/* single declaration, for use in a condition (controlling + expression of switch/while/if) */ +bool Parser::rSimpleDeclaration(cpp_declarationt &declaration) +{ + typet cv_q, integral; + + /* no member specification permitted here, and no + storage specifier: + type-specifier ::= + simple-type-specifier + class-specifier + enum-specifier + elaborated-type-specifier + cv-qualifier */ + + cv_q.make_nil(); + + if(!optCvQualify(cv_q)) + return false; + + if(!optIntegralTypeOrClassSpec(integral)) + return false; + + if(integral.is_nil() && + !rName(integral)) + return false; + + if(cv_q.is_not_nil() && integral.is_not_nil()) + merge_types(cv_q, integral); + else if(cv_q.is_not_nil() && integral.is_nil()) + integral.swap(cv_q); + + /* no type-specifier so far -> can't be a declaration */ + if(integral.is_nil()) + return false; + + cpp_declaratort declarator; + if(!rDeclarator(declarator, kDeclarator, false, true, true)) + return false; + + if(lex->LookAhead(0)!='=') + return false; + + Token eqs; + lex->GetToken(eqs); + + //int t=lex->LookAhead(0); + + exprt e; + if(!rExpression(e)) + return false; + + //Ptree::Nconc(d, Ptree::List(new Leaf(eqs), e)); + + //statement=new PtreeDeclaration(0, Ptree::List(integral, + // Ptree::List(d))); + // TODO + return true; +} + +bool Parser::rIntegralDeclaration( + cpp_declarationt &declaration, + cpp_storage_spect &storage_spec, + cpp_member_spect &member_spec, + typet &integral, + typet &cv_q) +{ + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 1 token: " + << (char) lex->LookAhead(0) << "\n"; + #endif + + if(!optCvQualify(cv_q)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 2\n"; + #endif + + merge_types(cv_q, integral); + + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 3\n"; + #endif + + declaration.type().swap(integral); + declaration.storage_spec().swap(storage_spec); + declaration.member_spec().swap(member_spec); + + Token tk; + + switch(lex->LookAhead(0)) + { + case ';': + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 4\n"; + #endif + + lex->GetToken(tk); + return true; + + case ':': // bit field + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 5\n"; + #endif + + lex->GetToken(tk); + + { + exprt width; + + if(!rExpression(width)) + return false; + + if(lex->GetToken(tk)!=';') + return false; + + // TODO + } + return true; + + default: + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 6 " + << lex->LookAhead(0) << "\n"; + #endif + + if(!rDeclarators(declaration.declarators(), true)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 7\n"; + #endif + + if(lex->LookAhead(0)==';') + { + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 8 " + << declaration << "\n"; + #endif + + lex->GetToken(tk); + return true; + } + else + { + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 9\n"; + #endif + + if(declaration.declarators().size()!=1) + return false; + + codet body; + if(!rFunctionBody(body)) + return false; + + if(declaration.declarators().size()!=1) + return false; + + declaration.declarators().front().value().swap(body); + + #ifdef DEBUG + std::cout << "Parser::rIntegralDeclaration 10\n"; + #endif + + return true; + } + } +} + +bool Parser::rConstDeclaration( + cpp_declarationt &declaration, + cpp_storage_spect &storage_spec, + cpp_member_spect &member_spec, + typet &cv_q) +{ + #ifdef DEBUG + std::cout << "Parser::rConstDeclaration\n"; + #endif + + cpp_declarationt::declaratorst declarators; + + if(!rDeclarators(declarators, false)) + return false; + + if(lex->LookAhead(0)!=';') + return false; + + Token tk; + lex->GetToken(tk); + + return true; +} + +bool Parser::rOtherDeclaration( + cpp_declarationt &declaration, + cpp_storage_spect &storage_spec, + cpp_member_spect &member_spec, + typet &cv_q) +{ + typet type_name; + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 1\n"; + #endif + + if(!rName(type_name)) + return false; + + merge_types(cv_q, type_name); + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 2\n"; + #endif + + // added this one to do "typename inline foo();" + if(member_spec.is_empty()) + if(!optMemberSpec(member_spec)) + return false; + + // this allows "typename static foo();" + if(storage_spec.is_empty()) + if(!optStorageSpec(storage_spec)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 3\n"; + #endif + + bool is_constructor = isConstructorDecl(); + bool is_operator = false; + + if(is_constructor) + { + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 4\n"; + #endif + + assert(!type_name.get_sub().empty()); + + for(unsigned i=0; i < type_name.get_sub().size(); i++) + { + if(type_name.get_sub()[i].id() == ID_operator) + { + is_operator = true; + break; + } + } + } + + if(is_operator && is_constructor) + { + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 5\n"; + #endif + + // it's a conversion operator + typet type = type_name; + type.get_sub().erase(type.get_sub().begin()); + + cpp_declaratort conv_operator_declarator; + if(!rConstructorDecl(conv_operator_declarator, type_name)) + return false; + + type_name=typet("cpp-cast-operator"); + + declaration.declarators().push_back(conv_operator_declarator); + } + else if(cv_q.is_nil() && is_constructor) + { + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 6\n"; + #endif + + assert(!type_name.get_sub().empty()); + + bool is_destructor=false; + forall_irep(it, type_name.get_sub()) + if(it->id()=="~") { is_destructor=true; break; } + + cpp_declaratort constructor_declarator; + if(!rConstructorDecl(constructor_declarator, type_name)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 7\n"; + #endif + + // it's the name (declarator), not the return type + + type_name=typet(is_destructor?ID_destructor:ID_constructor); + declaration.declarators().push_back(constructor_declarator); + } + else if(!member_spec.is_empty() && lex->LookAhead(0)==';') + { + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 8\n"; + #endif + + // FRIEND name ';' + //if(Ptree::Length(member_spec)==1 && member_spec->Car()->What()==FRIEND) + { + Token tk; + lex->GetToken(tk); + //statement=new PtreeDeclaration(head, Ptree::List(type_name, + // new Leaf(tk))); + return true; + } + //else + // return false; + } + else + { + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 9\n"; + #endif + + if(!optCvQualify(cv_q)) + return false; + + merge_types(cv_q, type_name); + + if(!rDeclarators(declaration.declarators(), false)) + return false; + } + + declaration.type().swap(type_name); + declaration.storage_spec().swap(storage_spec); + declaration.member_spec().swap(member_spec); + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 10\n"; + #endif + + if(lex->LookAhead(0)==';') + { + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 11\n"; + #endif + + Token tk; + lex->GetToken(tk); + } + else + { + #ifdef DEBUG + std::cout << "Parser::rOtherDeclaration 12;\n"; + #endif + + if(declaration.declarators().size()!=1) + return false; + + if(!rFunctionBody((codet &)declaration.declarators().front().value())) + return false; + } + + return true; +} + +/* + This returns true for an declaration like: + T (a); + even if a is not a type name. This is a bug according to the ANSI + specification, but I believe none says "T (a);" for a variable + declaration. +*/ +bool Parser::isConstructorDecl() +{ + #ifdef DEBUG + std::cout << "Parser::isConstructorDecl "<< lex->LookAhead(0) + << " "<< lex->LookAhead(1) << "\n"; + #endif + + if(lex->LookAhead(0)!='(') + return false; + else + { + int t=lex->LookAhead(1); + if(t=='*' || t=='&' || t=='(') + return false; // it's a declarator + else if(t==STDCALL || t==FASTCALL || t==CLRCALL || t==CDECL) + return false; // it's a declarator + else if(isPtrToMember(1)) + return false; // declarator (::*) + + // maybe constructor + return true; + } +} + +/* + ptr.to.member + : {'::'} (identifier {'<' any* '>'} '::')+ '*' +*/ +bool Parser::isPtrToMember(int i) +{ + int t0=lex->LookAhead(i++); + + if(t0==Scope) + t0=lex->LookAhead(i++); + + while(t0==Identifier){ + int t=lex->LookAhead(i++); + if(t=='<'){ + int n=1; + while(n > 0){ + int u=lex->LookAhead(i++); + if(u=='<') + ++n; + else if(u=='>') + --n; + else if(u=='('){ + int m=1; + while(m > 0){ + int v=lex->LookAhead(i++); + if(v=='(') + ++m; + else if(v==')') + --m; + else if(v=='\0' || v==';' || v=='}') + return false; + } + } + else if(u=='\0' || u==';' || u=='}') + return false; + } + + t=lex->LookAhead(i++); + } + + if(t!=Scope) + return false; + + t0=lex->LookAhead(i++); + if(t0=='*') + return true; + } + + return false; +} + +/* + member.spec + : (FRIEND | INLINE | VIRTUAL | EXPLICIT)+ +*/ +bool Parser::optMemberSpec(cpp_member_spect &member_spec) +{ + member_spec.clear(); + + int t=lex->LookAhead(0); + + while(t==FRIEND || t==INLINE || t==VIRTUAL || t==EXPLICIT) + { + Token tk; + lex->GetToken(tk); + + switch(t) + { + case INLINE: member_spec.set_inline(true); break; + case VIRTUAL: member_spec.set_virtual(true); break; + case FRIEND: member_spec.set_friend(true); break; + case EXPLICIT: member_spec.set_explicit(true); break; + default: assert(false); + } + + t=lex->LookAhead(0); + } + + return true; +} + +/* + storage.spec : STATIC | EXTERN | AUTO | REGISTER | MUTABLE +*/ +bool Parser::optStorageSpec(cpp_storage_spect &storage_spec) +{ + int t=lex->LookAhead(0); + + if(t==STATIC || + t==EXTERN || + t==AUTO || + t==REGISTER || + t==MUTABLE) + { + Token tk; + lex->GetToken(tk); + + switch(t) + { + case STATIC: storage_spec.set_static(); break; + case EXTERN: storage_spec.set_extern(); break; + case AUTO: storage_spec.set_auto(); break; + case REGISTER: storage_spec.set_register(); break; + case MUTABLE: storage_spec.set_mutable(); break; + default: assert(false); + } + + set_location(storage_spec, tk); + } + + return true; +} + +/* + cv.qualify : (CONST | VOLATILE)+ +*/ +bool Parser::optCvQualify(typet &cv) +{ + for(;;) + { + int t=lex->LookAhead(0); + if(t==CONST || t==VOLATILE || + t==PTR32 || t==PTR64 || + t==ATTRIBUTE) + { + Token tk; + lex->GetToken(tk); + typet p; + + switch(t) + { + case CONST: + p=typet(ID_const); + set_location(p, tk); + merge_types(p, cv); + break; + + case VOLATILE: + p=typet(ID_volatile); + set_location(p, tk); + merge_types(p, cv); + break; + + case PTR32: + p=typet(ID_ptr32); + set_location(p, tk); + merge_types(p, cv); + break; + + case PTR64: + p=typet(ID_ptr64); + set_location(p, tk); + merge_types(p, cv); + break; + + case ATTRIBUTE: + if(!rAttribute()) + return false; + break; + + default: + assert(false); + break; + } + } + else + break; + } + + return true; +} + +bool Parser::rAttribute() +{ + Token tk; + lex->GetToken(tk); + + switch(tk.kind) + { + case '(': + rAttribute(); + if(lex->LookAhead(0)!=')') return false; + lex->GetToken(tk); + break; + + case Identifier: + break; + + default: + return false; + } + + return true; +} + +/* + + !!! added WCHAR + + integral.or.class.spec + : (CHAR | WCHAR | INT | SHORT | LONG | SIGNED | UNSIGNED | FLOAT | DOUBLE + | VOID | BOOLEAN | COMPLEX)+ + | class.spec + | enum.spec + + Note: if editing this, see also isTypeSpecifier(). +*/ +bool Parser::optIntegralTypeOrClassSpec(typet &p) +{ + bool is_integral; + int t; + + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 0\n"; + #endif // DEBUG + + is_integral=false; + p.make_nil(); + + for(;;) + { + t=lex->LookAhead(0); + + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 1\n"; + #endif // DEBUG + + if(t==CHAR || t==INT || t==SHORT || t==LONG || t==SIGNED + || t==WCHAR || t==COMPLEX // new !!! + || t==UNSIGNED || t==FLOAT || t==DOUBLE || t==VOID + || t==WCHAR_T + || t==INT8 || t==INT16 || t==INT32 || t==INT64 + || t==BOOLEAN + ) + { + Token tk; + typet kw; + lex->GetToken(tk); + kw=typet(tk.text); + set_location(kw, tk); + + merge_types(kw, p); + + is_integral=true; + } + else + break; + } + + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 2\n"; + #endif // DEBUG + + if(is_integral) + return true; + + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 3\n"; + #endif // DEBUG + + if(t==CLASS || t==STRUCT || t==UNION) + return rClassSpec(p); + else if(t==ENUM) + return rEnumSpec(p); + else if(t==TYPEOF) + { + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 4\n"; + #endif // DEBUG + + Token typeof_tk; + lex->GetToken(typeof_tk); + + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 5\n"; + #endif // DEBUG + + p=typet(ID_typeof); + set_location(p, typeof_tk); + + Token tk; + if(lex->GetToken(tk)!='(') return false; + + // the argument can be a type or an expression + + { + typet tname; + cpp_token_buffert::post pos=lex->Save(); + + if(rTypeName(tname)) + if(lex->GetToken(tk)==')') + { + p.add(ID_type_arg).swap(tname); + return true; + } + + lex->Restore(pos); + } + + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 6\n"; + #endif // DEBUG + + exprt expr; + if(!rCommaExpression(expr)) return false; + + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 7\n"; + #endif // DEBUG + + if(lex->GetToken(tk)!=')') return false; + + #ifdef DEBUG + std::cout << "Parser::optIntegralTypeOrClassSpec 8\n"; + #endif // DEBUG + + p.add(ID_expr_arg).swap(expr); + + return true; + } + else if(t==DECLTYPE) + { + Token decltype_tk; + lex->GetToken(decltype_tk); + + p=typet(ID_decltype); + set_location(p, decltype_tk); + + Token tk; + if(lex->GetToken(tk)!='(') return false; + + // the argument is always an expression + + exprt expr; + if(!rCommaExpression(expr)) return false; + + if(lex->GetToken(tk)!=')') return false; + + p.add(ID_expr_arg).swap(expr); + + return true; + } + else + { + p.make_nil(); + return true; + } +} + +/* + constructor.decl + : '(' {arg.decl.list} ')' {cv.qualify} {throw.decl} + {member.initializers} {'=' Constant} +*/ +bool Parser::rConstructorDecl( + cpp_declaratort &constructor, + typet &type_name) +{ + #ifdef DEBUG + std::cout << "Parser::rConstructorDecl 0\n"; + #endif + + constructor=cpp_declaratort(typet("function_type")); + constructor.type().subtype().make_nil(); + constructor.name().swap(type_name); + + Token op; + if(lex->GetToken(op)!='(') + return false; + + irept &arguments=constructor.type().add(ID_arguments); + + if(lex->LookAhead(0)!=')') + if(!rArgDeclList(arguments)) + return false; + + Token cp; + lex->GetToken(cp); + + typet &cv=(typet &)constructor.add(ID_method_qualifier); + cv.make_nil(); + optCvQualify(cv); + + optThrowDecl(constructor.throw_decl()); + + if(lex->LookAhead(0)==':') + { + Ptree mi; + + if(rMemberInitializers(mi)) + constructor.member_initializers().swap(mi); + else + return false; + } + + if(lex->LookAhead(0)=='=') + { + Token eq, zero; + lex->GetToken(eq); + + if(lex->GetToken(zero)!=Constant) + return false; + + exprt pure_virtual(ID_code); + pure_virtual.set(ID_statement, "cpp-pure-virtual"); + + constructor.add(ID_value).swap(pure_virtual); + } + else + constructor.add(ID_value).make_nil(); + + return true; +} + +/* + throw.decl : THROW '(' (name {','})* {name} ')' + : THROW '(' '...' ')' +*/ +bool Parser::optThrowDecl(Ptree &throw_decl) +{ + Token tk; + int t; + Ptree p=get_nil_irep(); + + if(lex->LookAhead(0)==THROW) + { + lex->GetToken(tk); + //p=Ptree::Snoc(p, new LeafReserved(tk)); + + if(lex->GetToken(tk)!='(') + return false; + + //p=Ptree::Snoc(p, new Leaf(tk)); + + for(;;) + { + Ptree q; + t=lex->LookAhead(0); + if(t=='\0') + return false; + else if(t==')') + break; + else if(t==Ellipsis) + { + lex->GetToken(tk); + } + else if(rName(q)) + { + // p=Ptree::Snoc(p, q); + } + else + return false; + + if(lex->LookAhead(0)==',') + { + lex->GetToken(tk); + //p=Ptree::Snoc(p, new Leaf(tk)); + } + else + break; + } + + if(lex->GetToken(tk)!=')') + return false; + + //p=Ptree::Snoc(p, new Leaf(tk)); + } + + throw_decl=p; + return true; +} + +/* + declarators : declarator.with.init (',' declarator.with.init)* + + is_statement changes the behavior of rArgDeclListOrInit(). +*/ +bool Parser::rDeclarators( + cpp_declarationt::declaratorst &declarators, + bool should_be_declarator, + bool is_statement) +{ + Token tk; + + for(;;) + { + cpp_declaratort declarator; + if(!rDeclaratorWithInit(declarator, should_be_declarator, is_statement)) + return false; + + declarators.push_back(declarator); + + if(lex->LookAhead(0)==',') + lex->GetToken(tk); + else + return true; + } +} + +/* + declarator.with.init + : ':' expression + | declarator {'=' initialize.expr | ':' expression} +*/ +bool Parser::rDeclaratorWithInit( + cpp_declaratort &dw, + bool should_be_declarator, + bool is_statement) +{ + if(lex->LookAhead(0)==':') // bit field + { + Token tk; + lex->GetToken(tk); + + exprt e; + if(!rExpression(e)) + return false; + + //dw=Ptree::List(new Leaf(tk), e); + return true; + } + else + { + cpp_declaratort declarator; + + if(!rDeclarator(declarator, kDeclarator, false, + should_be_declarator, is_statement)) + return false; + + // asm post-declarator + if(lex->LookAhead(0)==GCC_ASM) + { + // this is stuff like + // int x __asm("asd")=1, y; + Token tk; + lex->GetToken(tk); // GCC_ASM + + if(lex->GetToken(tk)!='(') return false; + if(!rStringL(tk)) return false; + if(lex->GetToken(tk)!=')') return false; + } + + int t=lex->LookAhead(0); + if(t=='=') + { + // initializer + Token tk; + lex->GetToken(tk); + if(!rInitializeExpr(declarator.value())) + return false; + + dw.swap(declarator); + return true; + } + else if(t==':') + { + // bit field + exprt e; + + Token tk; + lex->GetToken(tk); + if(!rExpression(e)) + return false; + + //dw=Ptree::Nconc(d, Ptree::List(new Leaf(tk), e)); + // TODO + dw.swap(declarator); + return true; + } + else + { + dw.swap(declarator); + return true; + } + } +} + +/* __stdcall, __fastcall, __clrcall, __cdecl + + These are Visual-Studio specific. + +*/ + +bool Parser::rDeclaratorQualifier() +{ + int t=lex->LookAhead(0); + + // we just eat these + + while(t==STDCALL || t==FASTCALL || t==CLRCALL || t==CDECL) + { + Token op; + lex->GetToken(op); + t=lex->LookAhead(0); + } + + return true; +} + +/* + declarator + : (ptr.operator)* (name | '(' declarator ')') + ('[' comma.expression ']')* {func.args.or.init} + + func.args.or.init + : '(' arg.decl.list.or.init ')' {cv.qualify} {throw.decl} + {member.initializers} + + Note: We assume that '(' declarator ')' is followed by '(' or '['. + This is to avoid accepting a function call F(x) as a pair of + a type F and a declarator x. This assumption is ignored + if should_be_declarator is true. + + Note: is_statement changes the behavior of rArgDeclListOrInit(). +*/ + +bool Parser::rDeclarator( + cpp_declaratort &declarator, + DeclKind kind, + bool recursive, + bool should_be_declarator, + bool is_statement) +{ + int t; + bool recursive_decl=false; + + #ifdef DEBUG + std::cout << "Parser::rDeclarator2 1\n"; + #endif + + // we can have one or more declatator qualifiers + if(!rDeclaratorQualifier()) + return false; + + typet d_outer, d_inner; + irept name; + + name.make_nil(); + d_outer.make_nil(); + d_inner.make_nil(); + + if(!optPtrOperator(d_outer)) + return false; + + // we can have another sequence of declatator qualifiers + if(!rDeclaratorQualifier()) + return false; + + #ifdef DEBUG + std::cout << "Parser::rDeclarator2 2\n"; + #endif + + t=lex->LookAhead(0); + + if(t=='(') + { + #ifdef DEBUG + std::cout << "Parser::rDeclarator2 3\n"; + #endif + + Token op; + lex->GetToken(op); + + recursive_decl=true; + + cpp_declaratort declarator2; + if(!rDeclarator(declarator2, kind, true, true, false)) + return false; + + Token cp; + + if(lex->GetToken(cp)!=')') + return false; + + if(!should_be_declarator) + if(kind==kDeclarator && d_outer.is_nil()) + { + t=lex->LookAhead(0); + if(t!='[' && t!='(') + return false; + } + + d_inner.swap(declarator2.type()); + name.swap(declarator2.name()); + } + else if(kind!=kCastDeclarator && + (kind==kDeclarator || t==Identifier || t==Scope)) + { + #ifdef DEBUG + std::cout << "Parser::rDeclarator2 4\n"; + #endif + + // if this is an argument declarator, "int (*)()" is valid. + if(!rName(name)) + return false; + } + + #ifdef DEBUG + std::cout << "Parser::rDeclarator2 5\n"; + #endif + + exprt init_args(static_cast(get_nil_irep())); + typet method_qualifier(static_cast(get_nil_irep())); // const... + + for(;;) + { + t=lex->LookAhead(0); + if(t=='(') // function + { + Token op, cp; + exprt args; + bool is_args=true; + + lex->GetToken(op); + + if(lex->LookAhead(0)==')') + args.clear(); + else + if(!rArgDeclListOrInit(args, is_args, is_statement)) + return false; + + if(lex->GetToken(cp)!=')') + return false; + + if(is_args) + { + typet function_type("function_type"); + function_type.subtype().swap(d_outer); + function_type.add(ID_arguments).swap(args); + + // make this subtype of d_inner + make_subtype(function_type, d_inner); + d_outer.swap(d_inner); + + optCvQualify(method_qualifier); + } + else + { + init_args.swap(args); + // loop should end here + } + + Ptree throw_decl; + optThrowDecl(throw_decl); // ignore in this version + + if(lex->LookAhead(0)==':') + { + Ptree mi; + if(rMemberInitializers(mi)) + { + // TODO + } + else + return false; + } + + break; // "T f(int)(char)" is invalid. + } + else if(t=='[') // array + { + Token ob, cb; + exprt expr; + lex->GetToken(ob); + if(lex->LookAhead(0)==']') + expr.make_nil(); + else + if(!rCommaExpression(expr)) + return false; + + if(lex->GetToken(cb)!=']') + return false; + + std::list tl; + tl.push_back(d_outer); + while(tl.back().id() == ID_array) + { + tl.push_back(tl.back().subtype()); + } + + typet array_type(ID_array); + array_type.add(ID_size).swap(expr); + array_type.subtype().swap(tl.back()); + tl.pop_back(); + d_outer.swap(array_type); + while(!tl.empty()) + { + tl.back().subtype().swap(d_outer); + d_outer.swap(tl.back()); + tl.pop_back(); + } + } + else + break; + } + + declarator=cpp_declaratort(); + + declarator.name().swap(name); + + if(init_args.is_not_nil()) + declarator.init_args().swap(init_args); + + if(method_qualifier.is_not_nil()) + declarator.method_qualifier().swap(method_qualifier); + + declarator.type().swap(d_outer); + + return true; +} + +/* + ptr.operator + : (('*' | ptr.to.member)['&'] {cv.qualify})+ +*/ +bool Parser::optPtrOperator(typet &ptrs) +{ + #ifdef DEBUG + std::cout << "Parser::optPtrOperator 1\n"; + #endif // DEBUG + + std::list t_list; + + for(;;) + { + int t=lex->LookAhead(0); + + #ifdef DEBUG + std::cout << "Parser::optPtrOperator 2 " << t << "\n"; + #endif + + if(t!='*' && !isPtrToMember(0)) + break; + else + { + typet op; + if(t=='*') + { + Token tk; + lex->GetToken(tk); + op.id(ID_pointer); + set_location(op, tk); + } + else + if(!rPtrToMember(op)) + return false; + + typet cv; + cv.make_nil(); + optCvQualify(cv); + op.add(ID_C_qualifier).swap(cv); + + t_list.push_back(typet()); + t_list.back().swap(op); + } + } + + { + int t=lex->LookAhead(0); + + if(t=='&') + { + Token tk; + lex->GetToken(tk); + typet op(ID_pointer); + op.set(ID_C_reference, true); + set_location(op, tk); + t_list.push_front(op); + } + else if(t==ANDAND) // &&, these are C++0x rvalue refs + { + Token tk; + lex->GetToken(tk); + typet op(ID_pointer); + op.set(ID_C_rvalue_reference, true); + set_location(op, tk); + t_list.push_front(op); + } + } + + for(std::list::reverse_iterator + it=t_list.rbegin(); + it!=t_list.rend(); + it++) + { + it->subtype().swap(ptrs); + ptrs.swap(*it); + } + + return true; +} + +/* + member.initializers + : ':' member.init (',' member.init)* +*/ +bool Parser::rMemberInitializers(Ptree &init) +{ + Token tk; + + if(lex->GetToken(tk)!=':') + return false; + + init=irept(ID_member_initializers); + set_location(init, tk); + + exprt m; + if(!rMemberInit(m)) + return false; + + init.move_to_sub(m); + + while(lex->LookAhead(0)==',') + { + lex->GetToken(tk); + if(!rMemberInit(m)) + return false; + + init.move_to_sub(m); + } + + return true; +} + +/* + member.init + : name '(' function.arguments ')' +*/ +bool Parser::rMemberInit(exprt &init) +{ + Ptree name; + exprt args; + Token tk1, tk2; + + if(!rName(name)) + return false; + + init=codet(ID_member_initializer); + init.add(ID_member).swap(name); + + if(lex->GetToken(tk1)!='(') + return false; + + set_location(init, tk1); + + if(!rFunctionArguments(args)) + return false; + + if(lex->GetToken(tk2)!=')') + return false; + + init.operands().swap(args.operands()); + + return true; +} + +/* + name : {'::'} name2 ('::' name2)* + + name2 + : Identifier {template.args} + | '~' Identifier + | OPERATOR operator.name {template.args} + + Don't use this function for parsing an expression + It always regards '<' as the beginning of template arguments. +*/ +bool Parser::rName(Ptree &name) +{ + #ifdef DEBUG + std::cout << "Parser::rName 0\n"; + #endif + + name=irept(ID_cpp_name); + irept::subt &components=name.get_sub(); + + if(lex->LookAhead(0)==TYPENAME) + { + Token tk; + lex->GetToken(tk); + name.set(ID_typename, true); + } + + { + Token tk; + lex->LookAhead(0, tk); + set_location(name, tk); + } + + #ifdef DEBUG + std::cout << "Parser::rName 1\n"; + #endif + + for(;;) + { + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rName 1.1 " << "\n"; + #endif + + switch(lex->LookAhead(0)) + { + case TEMPLATE: + lex->GetToken(tk); + // Skip template token, next will be identifier + if(lex->LookAhead(0)!=Identifier) return false; + break; + + case '<': + { + irept args; + if(!rTemplateArgs(args)) + return false; + + components.push_back(irept(ID_template_args)); + components.back().add(ID_arguments).swap(args); + + // done unless scope is next + if(lex->LookAhead(0)!=Scope) return true; + } + break; + + case Identifier: + lex->GetToken(tk); + components.push_back(irept(ID_name)); + components.back().set(ID_identifier, tk.text); + set_location(components.back(), tk); + + { + int t=lex->LookAhead(0); + // done unless scope or template args is next + if(t!=Scope && t!='<') return true; + } + break; + + case Scope: + lex->GetToken(tk); + components.push_back(irept("::")); + set_location(components.back(), tk); + break; + + case '~': + lex->GetToken(tk); + + // identifier must be next + if(lex->LookAhead(0)!=Identifier) + return false; + + components.push_back(irept("~")); + set_location(components.back(), tk); + break; + + case OPERATOR: + lex->GetToken(tk); + { + components.push_back(irept(ID_operator)); + set_location(components.back(), tk); + + components.push_back(irept()); + + if(!rOperatorName(components.back())) + return false; + } + + // done unless template args are next + if(lex->LookAhead(0)!='<') return true; + break; + + default: + return false; + } +} +} + +/* + operator.name + : '+' | '-' | '*' | '/' | '%' | '^' | '&' | '|' | '~' + | '!' | '=' | '<' | '>' | AssignOp | ShiftOp | EqualOp + | RelOp | LogAndOp | LogOrOp | IncOp | ',' | PmOp | ArrowOp + | NEW {'[' ']'} + | DELETE {'[' ']'} + | '(' ')' + | '[' ']' + | cast.operator.name +*/ + +bool Parser::rOperatorName(irept &name) +{ + Token tk; + + int t=lex->LookAhead(0); + if(t=='+' || t=='-' || t=='*' || t=='/' || t=='%' || t=='^' || + t=='&' || t=='|' || t=='~' || t=='!' || t=='=' || t=='<' || + t=='>' || t==AssignOp || t==ShiftOp || t==EqualOp || + t==RelOp || t==ANDAND || t==LogOrOp || t==IncOp || + t==',' || t==PmOp || t==ArrowOp) + { + lex->GetToken(tk); + name=irept(tk.text); + set_location(name, tk); + } + else if(t==NEW || t==DELETE) + { + lex->GetToken(tk); + + if(lex->LookAhead(0)!='[') + { + name=irept(t==NEW?ID_cpp_new:ID_cpp_delete); + set_location(name, tk); + } + else + { + name=irept(t==NEW?ID_cpp_new_array:ID_cpp_delete_array); + set_location(name, tk); + + lex->GetToken(tk); + + if(lex->GetToken(tk)!=']') + return false; + } + } + else if(t=='(') + { + lex->GetToken(tk); + name=irept("()"); + set_location(name, tk); + + if(lex->GetToken(tk)!=')') + return false; + } + else if(t=='[') + { + lex->GetToken(tk); + name=irept("[]"); + set_location(name, tk); + + if(lex->GetToken(tk)!=']') + return false; + } + else + return rCastOperatorName(name); + + return true; +} + +/* + cast.operator.name + : {cv.qualify} (integral.or.class.spec | name) {cv.qualify} + {(ptr.operator)*} +*/ + +bool Parser::rCastOperatorName(Ptree &name) +{ + typet cv1, cv2, type_name, ptr; + + cv1.make_nil(); + cv2.make_nil(); + type_name.make_nil(); + ptr.make_nil(); + + if(!optCvQualify(cv1)) + return false; + + if(!optIntegralTypeOrClassSpec(type_name)) + return false; + + if(type_name.is_nil()) + { + if(!rName(type_name)) + return false; + } + + merge_types(cv1,type_name); + + if(!optCvQualify(cv2)) + return false; + + if(!optPtrOperator(ptr)) + return false; + + if(ptr.is_nil()) + { + name=type_name; + return true; + } + else + { + std::list t_list; + do + { + t_list.push_back(ptr); + typet tmp = ptr.subtype(); + ptr = tmp; + }while(ptr.is_not_nil()); + + ptr = type_name; + while(!t_list.empty()) + { + t_list.back().subtype() = ptr; + ptr = t_list.back(); + t_list.pop_back(); + } + merge_types(cv2,ptr); + name = ptr; + return true; + } +} + +/* + ptr.to.member + : {'::'} (identifier {template.args} '::')+ '*' +*/ +bool Parser::rPtrToMember(Ptree &ptr_to_mem) +{ + #ifdef DEBUG + std::cout << "Parser::rPtrToMember 0\n"; + #endif + + irept ptm(ID_pointer); + irept& name = ptm.add("to-member"); + name.id(ID_cpp_name); + + irept::subt &components=name.get_sub(); + + { + Token tk; + lex->LookAhead(0, tk); + set_location(name, tk); + } + + bool loop_cond = true; + while(loop_cond) + { + Token tk; + + switch(lex->LookAhead(0)) + { + case TEMPLATE: + lex->GetToken(tk); + // Skip template token, next will be identifier + if(lex->LookAhead(0)!=Identifier) return false; + break; + + case '<': + { + irept args; + if(!rTemplateArgs(args)) + return false; + + components.push_back(irept(ID_template_args)); + components.back().add(ID_arguments).swap(args); + + if(lex->LookAhead(0)!=Scope) return false; + } + break; + + case Identifier: + lex->GetToken(tk); + components.push_back(irept(ID_name)); + components.back().set(ID_identifier, tk.text); + set_location(components.back(), tk); + + { + int t=lex->LookAhead(0); + if(t!=Scope && t!='<') return false; + } + break; + + case Scope: + lex->GetToken(tk); + components.push_back(irept("::")); + set_location(components.back(), tk); + + // done if next token is '*' + if(lex->LookAhead(0) == '*') + { + lex->GetToken(tk); + ptr_to_mem.swap(ptm); + + + #ifdef DEBUG + std::cout << "Parser::rPtrToMember 1\n"; + #endif + + return true; + } + + if(lex->LookAhead(0) != Identifier) + return false; + + break; + + default: + return false; + } + } + return false; +} + +/* + template.args + : '<' '>' + | '<' template.argument {',' template.argument} '>' + + template.argument + : type.name + | logical.or.expr +*/ +bool Parser::rTemplateArgs(irept &template_args) +{ + #ifdef DEBUG + std::cout << "Parser::rTemplateArgs 0\n"; + #endif + + Token tk1; + + if(lex->GetToken(tk1)!='<') + return false; + + set_location(template_args, tk1); + + #ifdef DEBUG + std::cout << "Parser::rTemplateArgs 1\n"; + #endif + + // in case of Foo<> + if(lex->LookAhead(0)=='>') + { + Token tk2; + lex->GetToken(tk2); + return true; + } + + #ifdef DEBUG + std::cout << "Parser::rTemplateArgs 2\n"; + #endif + + for(;;) + { + exprt exp; + cpp_token_buffert::post pos=lex->Save(); + + #ifdef DEBUG + std::cout << "Parser::rTemplateArgs 3\n"; + #endif + + typet a; + + // try type name first + if(rTypeName(a) && + (lex->LookAhead(0) == '>' || lex->LookAhead(0) == ',')) + { + #ifdef DEBUG + std::cout << "Parser::rTemplateArgs 4\n"; + #endif + + // ok + exp=exprt(ID_type); + exp.location()=a.location(); + exp.type().swap(a); + + // but could also be an expr + lex->Restore(pos); + exprt tmp; + if(rLogicalOrExpr(tmp, true)) + exp.id("ambiguous"); + lex->Restore(pos); + rTypeName(a); + + } + else + { + // parsing failed, try expression + #ifdef DEBUG + std::cout << "Parser::rTemplateArgs 5\n"; + #endif + + lex->Restore(pos); + + if(!rLogicalOrExpr(exp, true)) + return false; + } + + #ifdef DEBUG + std::cout << "Parser::rTemplateArgs 6\n"; + #endif + + template_args.get_sub().push_back(irept(irep_idt())); + template_args.get_sub().back().swap(exp); + + Token tk2; + switch(lex->GetToken(tk2)) + { + case '>': + return true; + + case ',': + break; + + case ShiftOp: + if(tk2.text[0]=='>') + { + // turn >> into > > // TODO + //lex->GetOnlyClosingBracket(tk2); + //temp_args=Ptree::List(new Leaf(tk1), args, + // new Leaf(tk2.ptr, 1)); + return false; + } + + default: + return false; + } + } +} + +/* + arg.decl.list.or.init + : arg.decl.list + | function.arguments + + This rule accepts function.arguments to parse declarations like: + Point p(1, 3); + "(1, 3)" is arg.decl.list.or.init. + + If maybe_init is true, we first examine whether tokens construct + function.arguments. This ordering is significant if tokens are + Point p(s, t); + s and t can be type names or variable names. +*/ +bool Parser::rArgDeclListOrInit( + exprt &arglist, + bool &is_args, + bool maybe_init) +{ + cpp_token_buffert::post pos=lex->Save(); + if(maybe_init) + { + if(rFunctionArguments(arglist)) + if(lex->LookAhead(0)==')') + { + is_args=false; + //encode.Clear(); + return true; + } + + lex->Restore(pos); + return(is_args=rArgDeclList(arglist)); + } + else + { + if((is_args=rArgDeclList(arglist))) + return true; + else + { + lex->Restore(pos); + //encode.Clear(); + return rFunctionArguments(arglist); + } + } +} + +/* + arg.decl.list + : empty + | arg.declaration ( ',' arg.declaration )* {{ ',' } Ellipses} +*/ +bool Parser::rArgDeclList(Ptree &arglist) +{ + Ptree list; + + list.clear(); + for(;;) + { + cpp_declarationt declaration; + + int t=lex->LookAhead(0); + if(t==')') + { + arglist.swap(list); + break; + } + else if(t==Ellipsis) + { + Token tk; + lex->GetToken(tk); + arglist.swap(list); + arglist.get_sub().push_back(irept(ID_ellipsis)); + break; + } + else if(rArgDeclaration(declaration)) + { + Token tk; + + list.get_sub().push_back(irept(irep_idt())); + list.get_sub().back().swap(declaration); + t=lex->LookAhead(0); + if(t==',') + lex->GetToken(tk); + else if(t!=')' && t!=Ellipsis) + return false; + } + else + { + arglist.clear(); + return false; + } + } + + return true; +} + +/* + arg.declaration + : {userdef.keyword | REGISTER} type.specifier arg.declarator + {'=' expression} +*/ +bool Parser::rArgDeclaration(cpp_declarationt &declaration) +{ + typet header; + Token tk; + + switch(lex->LookAhead(0)) + { + case REGISTER: + lex->GetToken(tk); + header=typet(ID_register); + break; + + default: + header.make_nil(); + break; + } + + if(!rTypeSpecifier(declaration.type(), true)) + return false; + + cpp_declaratort arg_declarator; + + if(!rDeclarator(arg_declarator, kArgDeclarator, false, true)) + return false; + + declaration.declarators().push_back(arg_declarator); + + int t=lex->LookAhead(0); + if(t=='=') + { + lex->GetToken(tk); + if(!rInitializeExpr(declaration.declarators().back().value())) + return false; + } + + return true; +} + +/* + initialize.expr + : expression + | '{' initialize.expr (',' initialize.expr)* {','} '}' +*/ +bool Parser::rInitializeExpr(exprt &expr) +{ + if(lex->LookAhead(0)!='{') + return rExpression(expr); + + // we want { initialize_expr, ... } + + Token tk; + lex->GetToken(tk); + + exprt e; + + expr.id(ID_initializer_list); + expr.type().id(ID_incomplete_array); + set_location(expr, tk); + + int t=lex->LookAhead(0); + + while(t!='}') + { + exprt tmp; + + if(!rInitializeExpr(tmp)) + { + if(!SyntaxError()) + return false; // too many errors + + SkipTo('}'); + lex->GetToken(tk); + return true; // error recovery + } + + expr.move_to_operands(tmp); + + t=lex->LookAhead(0); + if(t=='}') + { + // done! + } + else if(t==',') + { + lex->GetToken(tk); + t=lex->LookAhead(0); + } + else + { + if(!SyntaxError()) + return false; // too many errors + + SkipTo('}'); + lex->GetToken(tk); + return true; // error recovery + } + } + + lex->GetToken(tk); + + return true; +} + +/* + function.arguments + : empty + | expression (',' expression)* + + This assumes that the next token following function.arguments is ')'. +*/ +bool Parser::rFunctionArguments(exprt &args) +{ + exprt exp; + Token tk; + + args=exprt(irep_idt()); + if(lex->LookAhead(0)==')') + return true; + + for(;;) + { + if(!rExpression(exp)) + return false; + + args.move_to_operands(exp); + + if(lex->LookAhead(0)!=',') + return true; + else + lex->GetToken(tk); + } +} + +/* + enum.spec + : ENUM Identifier + | ENUM {Identifier} '{' {enum.body} '}' +*/ +bool Parser::rEnumSpec(typet &spec) +{ + Token tk; + + if(lex->GetToken(tk)!=ENUM) + return false; + + spec=cpp_enum_typet(); + set_location(spec, tk); + + int t=lex->GetToken(tk); + + if(t==Identifier) + { + spec.set(ID_name, tk.text); + + if(lex->LookAhead(0)=='{') + t=lex->GetToken(tk); + else + return true; + } + else + spec.set(ID_name, irep_idt()); + + if(t!='{') + return false; + + if(lex->LookAhead(0)=='}') + { + // there is still a body, just an empty one! + spec.add(ID_body); + } + else + if(!rEnumBody(spec.add(ID_body))) + return false; + + if(lex->GetToken(tk)!='}') + return false; + + return true; +} + +/* + enum.body + : Identifier {'=' expression} (',' Identifier {'=' expression})* {','} +*/ +bool Parser::rEnumBody(irept &body) +{ + body.clear(); + + for(;;) + { + Token tk, tk2; + + if(lex->LookAhead(0)=='}') + return true; + + if(lex->GetToken(tk)!=Identifier) + return false; + + body.get_sub().push_back(irept()); + irept &n=body.get_sub().back(); + set_location(n, tk); + n.set(ID_name, tk.text); + + if(lex->LookAhead(0, tk2)=='=') // set the constant + { + lex->GetToken(tk2); // read the '=' + + exprt exp; + + if(!rExpression(exp)) + { + if(!SyntaxError()) + return false; // too many errors + + SkipTo('}'); + body.clear(); // empty + return true; // error recovery + } + + n.add(ID_value).swap(exp); + } + else + n.add(ID_value).make_nil(); + + if(lex->LookAhead(0)!=',') + return true; + + lex->GetToken(tk); + } +} + +/* + class.spec + : {userdef.keyword} class.key class.body + | {userdef.keyword} class.key name {class.body} + | {userdef.keyword} class.key name ':' base.specifiers class.body + + class.key + : CLASS | STRUCT | UNION +*/ +bool Parser::rClassSpec(typet &spec) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rClassSpec 1\n"; + #endif + + int t=lex->GetToken(tk); + if(t!=CLASS && t!=STRUCT && t!=UNION) + return false; + + #ifdef DEBUG + std::cout << "Parser::rClassSpec 2\n"; + #endif + + if(t==CLASS) + { + spec=typet(ID_struct); + spec.set(ID_C_class, true); + } + else if(t==STRUCT) + { + spec=typet(ID_struct); + spec.set(ID_C_class, false); + } + else if(t==UNION) + spec=typet(ID_union); + else + assert(false); + + set_location(spec, tk); + + #if 0 + Ptree comments=lex->GetComments(); + spec=new PtreeClassSpec(new LeafReserved(tk), nil, comments); + if(head.is_not_nil()) + spec=new PtreeClassSpec(head, spec, comments); + #endif + + #ifdef DEBUG + std::cout << "Parser::rClassSpec 3\n"; + #endif + + if(lex->LookAhead(0)=='{') + { + //encode.NoName(); + //spec=Ptree::Snoc(spec, Ptree::List(nil, nil)); + #ifdef DEBUG + std::cout << "Parser::rClassSpec 4\n"; + #endif + } + else + { + irept name; + + if(!rName(name)) + return false; + + spec.add(ID_tag).swap(name); + + #ifdef DEBUG + std::cout << "Parser::rClassSpec 5\n"; + #endif + + //spec=Ptree::Snoc(spec, name); + t=lex->LookAhead(0); + if(t==':') + { + if(!rBaseSpecifiers(spec.add(ID_bases))) + return false; + } + else if(t=='{') + { + //spec=Ptree::Snoc(spec, nil); + } + else + { + //((PtreeClassSpec*)spec)->encoded_name=encode.Get(); + return true; // class.key Identifier + } + } + + #ifdef DEBUG + std::cout << "Parser::rClassSpec 6\n"; + #endif + + //((PtreeClassSpec*)spec)->encoded_name=encode.Get(); + exprt body; + + if(!rClassBody(body)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rClassSpec 7\n"; + #endif + + //spec=Ptree::Snoc(spec, body); + ((exprt&)spec.add(ID_body)).operands().swap(body.operands()); + return true; +} + +/* + base.specifiers + : ':' base.specifier (',' base.specifier)* + + base.specifier + : {{VIRTUAL} (PUBLIC | PROTECTED | PRIVATE) {VIRTUAL}} name +*/ +bool Parser::rBaseSpecifiers(irept &bases) +{ + Token tk; + + if(lex->GetToken(tk)!=':') + return false; + + for(;;) + { + int t=lex->LookAhead(0); + irept base(ID_base); + + if(t==VIRTUAL) + { + lex->GetToken(tk); + base.set(ID_virtual, true); + t=lex->LookAhead(0); + } + + if(t==PUBLIC || t==PROTECTED || t==PRIVATE) + { + switch(lex->GetToken(tk)) + { + case PUBLIC: + base.set(ID_protection, ID_public); + break; + + case PROTECTED: + base.set(ID_protection, ID_protected); + break; + + case PRIVATE: + base.set(ID_protection, ID_private); + break; + + default: + assert(0); + } + + t=lex->LookAhead(0); + } + + if(t==VIRTUAL) + { + lex->GetToken(tk); + base.set(ID_virtual, true); + } + + if(!rName(base.add(ID_name))) + return false; + + bases.get_sub().push_back(irept()); + bases.get_sub().back().swap(base); + + if(lex->LookAhead(0)!=',') + return true; + else + lex->GetToken(tk); + } +} + +/* + class.body : '{' (class.members)* '}' +*/ +bool Parser::rClassBody(exprt &body) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rClassBody 0\n"; + #endif + + if(lex->GetToken(tk)!='{') + return false; + + //Ptree ob=new Leaf(tk); + + exprt members=exprt("cpp-class-body"); + + set_location(members, tk); + + while(lex->LookAhead(0)!='}') + { + cpp_itemt member; + + if(!rClassMember(member)) + { + if(!SyntaxError()) + return false; // too many errors + + SkipTo('}'); + lex->GetToken(tk); + //body=Ptree::List(ob, nil, new Leaf(tk)); + return true; // error recovery + } + + //lex->GetComments(); + //mems=Ptree::Snoc(mems, m); + + #ifdef DEBUG + std::cout << "Parser::rClassBody " << member << std::endl; + #endif + + members.move_to_operands(static_cast(static_cast(member))); + } + + lex->GetToken(tk); + body.swap(members); + return true; +} + +/* + class.member + : (PUBLIC | PROTECTED | PRIVATE) ':' + | user.access.spec + | ';' + | type.def + | template.decl + | using.declaration + | metaclass.decl + | declaration + | access.decl + + Note: if you modify this function, see ClassWalker::TranslateClassSpec() + as well. +*/ +bool Parser::rClassMember(cpp_itemt &member) +{ + Token tk1, tk2; + + int t=lex->LookAhead(0); + + #ifdef DEBUG + std::cout << "Parser::rClassMember 0 " << t << std::endl; + #endif // DEBUG + + if(t==PUBLIC || t==PROTECTED || t==PRIVATE) + { + switch(lex->GetToken(tk1)) + { + case PUBLIC: + member.id("cpp-public"); + break; + + case PROTECTED: + member.id("cpp-protected"); + break; + + case PRIVATE: + member.id("cpp-private"); + break; + + default: + assert(0); + } + + set_location(member, tk1); + + if(lex->GetToken(tk2)!=':') + return false; + + return true; + } + else if(t==';') + return rNullDeclaration(member.make_declaration()); + else if(t==TYPEDEF) + return rTypedef(member.make_declaration()); + else if(t==TEMPLATE) + return rTemplateDecl(member.make_declaration()); + else if(t==USING) + return rUsing(member.make_using()); + else + { + cpp_token_buffert::post pos=lex->Save(); + if(rDeclaration(member.make_declaration())) + return true; + + lex->Restore(pos); + return rAccessDecl(member); + } +} + +/* + access.decl + : name ';' e.g. ::; +*/ +bool Parser::rAccessDecl(Ptree &mem) +{ + Ptree name; + Token tk; + + if(!rName(name)) + return false; + + if(lex->GetToken(tk)!=';') + return false; + + //mem=new PtreeAccessDecl(new PtreeName(name, encode), + // Ptree::List(new Leaf(tk))); + return true; +} + +/* + comma.expression + : expression + | comma.expression ',' expression (left-to-right) +*/ +bool Parser::rCommaExpression(exprt &exp) +{ + #ifdef DEBUG + std::cout << "Parser::rCommaExpression 0\n"; + #endif + + if(!rExpression(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rCommaExpression 1\n"; + #endif + + while(lex->LookAhead(0)==',') + { + Token tk; + + lex->GetToken(tk); + + exprt right; + if(!rExpression(right)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(ID_comma); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + #ifdef DEBUG + std::cout << "Parser::rCommaExpression 2\n"; + #endif + + return true; +} + +/* + expression + : conditional.expr {(AssignOp | '=') expression} right-to-left +*/ +bool Parser::rExpression(exprt &exp) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rExpression 0\n"; + #endif + + if(!rConditionalExpr(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rExpression 1\n"; + #endif + + int t=lex->LookAhead(0); + + if(t=='=' || t==AssignOp) + { + lex->GetToken(tk); + + #ifdef DEBUG + std::cout << "Parser::rExpression 2\n"; + #endif + + exprt right; + if(!rExpression(right)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rExpression 3\n"; + #endif + + exprt left; + left.swap(exp); + + exp=exprt(ID_sideeffect); + + if(t=='=') + exp.set(ID_statement, ID_assign); + else if(tk.text=="+=") + exp.set(ID_statement, ID_assign_plus); + else if(tk.text=="-=") + exp.set(ID_statement, ID_assign_minus); + else if(tk.text=="*=") + exp.set(ID_statement, ID_assign_mult); + else if(tk.text=="/=") + exp.set(ID_statement, ID_assign_div); + else if(tk.text=="%=") + exp.set(ID_statement, ID_assign_mod); + else if(tk.text=="<<=") + exp.set(ID_statement, ID_assign_shl); + else if(tk.text==">>=") + exp.set(ID_statement, ID_assign_shr); + else if(tk.text=="&=") + exp.set(ID_statement, ID_assign_bitand); + else if(tk.text=="^=") + exp.set(ID_statement, ID_assign_bitxor); + else if(tk.text=="|=") + exp.set(ID_statement, ID_assign_bitor); + + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + #ifdef DEBUG + std::cout << "Parser::rExpression 4\n"; + #endif + + return true; +} + +/* + conditional.expr + : logical.or.expr {'?' comma.expression ':' conditional.expr} right-to-left +*/ +bool Parser::rConditionalExpr(exprt &exp) +{ + #ifdef DEBUG + std::cout << "Parser::rConditionalExpr 0\n"; + #endif + + if(!rLogicalOrExpr(exp, false)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rConditionalExpr 1\n"; + #endif + + if(lex->LookAhead(0)=='?') + { + Token tk1, tk2; + exprt then, otherwise; + + lex->GetToken(tk1); + if(!rCommaExpression(then)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rConditionalExpr 2\n"; + #endif + + if(lex->GetToken(tk2)!=':') + return false; + + if(!rExpression(otherwise)) + return false; + + exprt cond; + cond.swap(exp); + + exp=exprt(ID_if); + exp.move_to_operands(cond, then, otherwise); + set_location(exp, tk1); + } + + return true; +} + +/* + logical.or.expr + : logical.and.expr + | logical.or.expr LogOrOp logical.and.expr left-to-right +*/ +bool Parser::rLogicalOrExpr(exprt &exp, bool temp_args) +{ + #ifdef DEBUG + std::cout << "Parser::rLogicalOrExpr 0\n"; + #endif + + if(!rLogicalAndExpr(exp, temp_args)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rLogicalOrExpr 1\n"; + #endif + + while(lex->LookAhead(0)==LogOrOp) + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rLogicalAndExpr(right, temp_args)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(ID_or); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + logical.and.expr + : inclusive.or.expr + | logical.and.expr LogAndOp inclusive.or.expr +*/ +bool Parser::rLogicalAndExpr(exprt &exp, bool temp_args) +{ + #ifdef DEBUG + std::cout << "Parser::rLogicalAndExpr 1\n"; + #endif + + if(!rInclusiveOrExpr(exp, temp_args)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rLogicalAndExpr 1\n"; + #endif + + while(lex->LookAhead(0)==ANDAND) + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rInclusiveOrExpr(right, temp_args)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(ID_and); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + inclusive.or.expr + : exclusive.or.expr + | inclusive.or.expr '|' exclusive.or.expr +*/ +bool Parser::rInclusiveOrExpr(exprt &exp, bool temp_args) +{ + #ifdef DEBUG + std::cout << "Parser::rInclusiveOrExpr 0\n"; + #endif + + if(!rExclusiveOrExpr(exp, temp_args)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rInclusiveOrExpr 1\n"; + #endif + + while(lex->LookAhead(0)=='|') + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rExclusiveOrExpr(right, temp_args)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(ID_bitor); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + exclusive.or.expr + : and.expr + | exclusive.or.expr '^' and.expr +*/ +bool Parser::rExclusiveOrExpr(exprt &exp, bool temp_args) +{ + #ifdef DEBUG + std::cout << "Parser::rExclusiveOrExpr 0\n"; + #endif + + if(!rAndExpr(exp, temp_args)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rExclusiveOrExpr 1\n"; + #endif + + while(lex->LookAhead(0)=='^') + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rAndExpr(right, temp_args)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(ID_bitxor); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + and.expr + : equality.expr + | and.expr '&' equality.expr +*/ +bool Parser::rAndExpr(exprt &exp, bool temp_args) +{ + #ifdef DEBUG + std::cout << "Parser::rAndExpr 0\n"; + #endif + + if(!rEqualityExpr(exp, temp_args)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rAndExpr 1\n"; + #endif + + while(lex->LookAhead(0)=='&') + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rEqualityExpr(right, temp_args)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(ID_bitand); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + equality.expr + : relational.expr + | equality.expr EqualOp relational.expr +*/ +bool Parser::rEqualityExpr(exprt &exp, bool temp_args) +{ + #ifdef DEBUG + std::cout << "Parser::rEqualityExpr 0\n"; + #endif + + if(!rRelationalExpr(exp, temp_args)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rEqualityExpr 1\n"; + #endif + + while(lex->LookAhead(0)==EqualOp) + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rRelationalExpr(right, temp_args)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(tk.text=="=="?ID_equal:ID_notequal); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + relational.expr + : shift.expr + | relational.expr (RelOp | '<' | '>') shift.expr +*/ +bool Parser::rRelationalExpr(exprt &exp, bool temp_args) +{ + #ifdef DEBUG + std::cout << "Parser::rRelationalExpr 0\n"; + #endif + + if(!rShiftExpr(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rRelationalExpr 1\n"; + #endif + + int t; + + while(t=lex->LookAhead(0), + (t==RelOp || t=='<' || (t=='>' && !temp_args))) + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rShiftExpr(right)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(tk.text); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + shift.expr + : additive.expr + | shift.expr ShiftOp additive.expr +*/ +bool Parser::rShiftExpr(exprt &exp) +{ + #ifdef DEBUG + std::cout << "Parser::rShiftExpr 0\n"; + #endif + + if(!rAdditiveExpr(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rShiftExpr 1\n"; + #endif + + while(lex->LookAhead(0)==ShiftOp) + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rAdditiveExpr(right)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt((tk.text==">>")?ID_shr:ID_shl); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + additive.expr + : multiply.expr + | additive.expr ('+' | '-') multiply.expr +*/ +bool Parser::rAdditiveExpr(exprt &exp) +{ + #ifdef DEBUG + std::cout << "Parser::rAdditiveExpr 0\n"; + #endif + + if(!rMultiplyExpr(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rAdditiveExpr 1\n"; + #endif + + int t; + while(t=lex->LookAhead(0), (t=='+' || t=='-')) + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rMultiplyExpr(right)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt(tk.text); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + return true; +} + +/* + multiply.expr + : pm.expr + | multiply.expr ('*' | '/' | '%') pm.expr +*/ +bool Parser::rMultiplyExpr(exprt &exp) +{ + Token tk; + Ptree right; + + #ifdef DEBUG + std::cout << "Parser::rMultiplyExpr 0\n"; + #endif + + if(!rPmExpr(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rMultiplyExpr 1\n"; + #endif + + int t; + while(t=lex->LookAhead(0), (t=='*' || t=='/' || t=='%')) + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rPmExpr(right)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt((tk.text=="%")?ID_mod:tk.text); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + #ifdef DEBUG + std::cout << "Parser::rMultiplyExpr 2\n"; + #endif + + return true; +} + +/* + pm.expr (pointer to member .*, ->*) + : cast.expr + | pm.expr PmOp cast.expr +*/ +bool Parser::rPmExpr(exprt &exp) +{ + #ifdef DEBUG + std::cout << "Parser::rPmExpr 0\n"; + #endif + + if(!rCastExpr(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rPmExpr 1\n"; + #endif + + while(lex->LookAhead(0)==PmOp) + { + Token tk; + lex->GetToken(tk); + + exprt right; + if(!rCastExpr(right)) + return false; + + exprt left; + left.swap(exp); + + exp=exprt("pointer-to-member"); + exp.move_to_operands(left, right); + set_location(exp, tk); + } + + #ifdef DEBUG + std::cout << "Parser::rPmExpr 2\n"; + #endif + + return true; +} + +/* + cast.expr + : unary.expr + | '(' type.name ')' cast.expr +*/ +bool Parser::rCastExpr(exprt &exp) +{ + #ifdef DEBUG + std::cout << "Parser::rCastExpr 0\n"; + #endif + + if(lex->LookAhead(0)!='(') + return rUnaryExpr(exp); + else + { + Token tk1, tk2; + + #ifdef DEBUG + std::cout << "Parser::rCastExpr 1\n"; + #endif + + cpp_token_buffert::post pos=lex->Save(); + lex->GetToken(tk1); + + typet tname; + + if(rTypeName(tname)) + if(lex->GetToken(tk2)==')') + if(rCastExpr(exp)) + { + exprt op; + op.swap(exp); + + exp=exprt("explicit-typecast"); + exp.type().swap(tname); + exp.move_to_operands(op); + set_location(exp, tk1); + return true; + } + + lex->Restore(pos); + return rUnaryExpr(exp); + } +} + +/* + type.name + : type.specifier cast.declarator +*/ +bool Parser::rTypeName(typet &tname) +{ + typet type_name; + + if(!rTypeSpecifier(type_name, true)) + return false; + + cpp_declaratort declarator; + + if(!rDeclarator(declarator, kCastDeclarator, false, false)) + return false; + + tname.swap(declarator.type()); + + // make type_name subtype of arg + make_subtype(type_name, tname); + + return true; +} + +/* + unary.expr + : postfix.expr + | ('*' | '&' | '+' | '-' | '!' | '~' | IncOp) cast.expr + | sizeof.expr + | allocate.expr + | throw.expression +*/ + +bool Parser::rUnaryExpr(exprt &exp) +{ + int t=lex->LookAhead(0); + + #ifdef DEBUG + std::cout << "Parser::rUnaryExpr 0\n"; + #endif + + if(t=='*' || t=='&' || t=='+' || + t=='-' || t=='!' || t=='~' || t==IncOp) + { + Token tk; + lex->GetToken(tk); + + #ifdef DEBUG + std::cout << "Parser::rUnaryExpr 1\n"; + #endif + + exprt right; + if(!rCastExpr(right)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rUnaryExpr 2\n"; + #endif + + switch(t) + { + case '*': + exp=exprt(ID_dereference); + break; + + case '&': + exp=exprt(ID_address_of); + break; + + case '+': + exp=exprt(ID_unary_plus); + break; + + case '-': + exp=exprt(ID_unary_minus); + break; + + case '!': + exp=exprt(ID_not); + break; + + case '~': + exp=exprt(ID_bitnot); + break; + + case IncOp: + exp=exprt(ID_sideeffect); + exp.set(ID_statement, + tk.text=="++"?ID_preincrement:ID_predecrement); + break; + + default: + assert(0); + } + + exp.move_to_operands(right); + set_location(exp, tk); + + return true; + } + else if(t==SIZEOF) + return rSizeofExpr(exp); + else if(t==THROW) + return rThrowExpr(exp); + else if(t==REAL || t==IMAG) + { + // a GCC extension for complex floating-point arithmetic + Token tk; + lex->GetToken(tk); + + exprt unary; + + if(!rUnaryExpr(unary)) + return false; + + exp=exprt(t==REAL?ID_complex_real:ID_complex_imag); + exp.move_to_operands(unary); + set_location(exp, tk); + return true; + } + else if(isAllocateExpr(t)) + return rAllocateExpr(exp); + else + return rPostfixExpr(exp); +} + +/* + throw.expression + : THROW {expression} +*/ +bool Parser::rThrowExpr(exprt &exp) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rThrowExpr 0\n"; + #endif + + if(lex->GetToken(tk)!=THROW) + return false; + + int t=lex->LookAhead(0); + + exp=exprt(ID_sideeffect); + exp.set(ID_statement, ID_throw); + set_location(exp, tk); + + if(t==':' || t==';') + { + // done + } + else + { + exprt e; + + if(!rExpression(e)) + return false; + + exp.move_to_operands(e); + } + + return true; +} + +/* + typeid.expr + : TYPEID '(' expression ')' + | TYPEID '(' type.name ')' +*/ +bool Parser::rTypeidExpr(exprt &exp) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rTypeidExpr 0\n"; + #endif + + if(lex->GetToken(tk)!=TYPEID) + return false; + + if(lex->LookAhead(0)=='(') + { + typet tname; + exprt subexp; + Token op, cp; + + cpp_token_buffert::post pos=lex->Save(); + lex->GetToken(op); + if(rTypeName(tname)) + if(lex->GetToken(cp)==')') + { + //exp=new PtreeTypeidExpr(new Leaf(tk), + // Ptree::List(new Leaf(op), tname, + // new Leaf(cp))); + + exp=exprt("typeid"); + set_location(exp, tk); + return true; + } + + lex->Restore(pos); + lex->GetToken(op); + + if(rExpression(subexp)) + if(lex->GetToken(cp)==')') + { + // exp=new PtreeTypeidExpr(new Leaf(tk), + // Ptree::List( + // Ptree::List(new Leaf(op), subexp, new Leaf(cp)) + // )); + + exp=exprt("typeid"); + set_location(exp, tk); + return true; + } + + lex->Restore(pos); + } + + return false; +} + +/* + sizeof.expr + : SIZEOF unary.expr + | SIZEOF '(' type.name ')' +*/ + +bool Parser::rSizeofExpr(exprt &exp) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rSizeofExpr 0\n"; + #endif + + if(lex->GetToken(tk)!=SIZEOF) + return false; + + if(lex->LookAhead(0)=='(') + { + typet tname; + Token op, cp; + + cpp_token_buffert::post pos=lex->Save(); + lex->GetToken(op); + + if(rTypeName(tname)) + if(lex->GetToken(cp)==')') + { + exp=exprt(ID_sizeof); + exp.add(ID_type_arg).swap(tname); + set_location(exp, tk); + return true; + } + + lex->Restore(pos); + } + + exprt unary; + + if(!rUnaryExpr(unary)) + return false; + + exp=exprt(ID_sizeof); + exp.move_to_operands(unary); + set_location(exp, tk); + return true; +} + +bool Parser::isAllocateExpr(int t) +{ + if(t==Scope) + t=lex->LookAhead(1); + + return t==NEW || t==DELETE; +} + +/* + allocate.expr + : {Scope | userdef.keyword} NEW allocate.type + | {Scope} DELETE {'[' ']'} cast.expr +*/ +bool Parser::rAllocateExpr(exprt &exp) +{ + Token tk; + Ptree head=get_nil_irep(); + + #ifdef DEBUG + std::cout << "Parser::rAllocateExpr 0\n"; + #endif + + int t=lex->LookAhead(0); + if(t==Scope) + { + lex->GetToken(tk); + // TODO, one can put 'new'/'delete' into a namespace! + } + + #ifdef DEBUG + std::cout << "Parser::rAllocateExpr 1\n"; + #endif + + t=lex->GetToken(tk); + + #ifdef DEBUG + std::cout << "Parser::rAllocateExpr 2\n"; + #endif + + if(t==DELETE) + { + exprt obj; + + if(lex->LookAhead(0)=='[') + { + lex->GetToken(tk); + + if(lex->GetToken(tk)!=']') + return false; + + exp=exprt(ID_sideeffect); + exp.set(ID_statement, ID_cpp_delete_array); + } + else + { + exp=exprt(ID_sideeffect); + exp.set(ID_statement, ID_cpp_delete); + } + + set_location(exp, tk); + + if(!rCastExpr(obj)) + return false; + + exp.move_to_operands(obj); + + return true; + } + else if(t==NEW) + { + #ifdef DEBUG + std::cout << "Parser::rAllocateExpr 3\n"; + #endif + + exp=exprt(ID_sideeffect); + exp.set(ID_statement, ID_cpp_new); + set_location(exp, tk); + + exprt arguments, initializer; + + if(!rAllocateType(arguments, exp.type(), initializer)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rAllocateExpr 4\n"; + #endif + + exp.add(ID_initializer).swap(initializer); + exp.operands().swap(arguments.operands()); + return true; + } + else + return false; +} + +/* + allocate.type + : {'(' function.arguments ')'} type.specifier new.declarator + {allocate.initializer} + | {'(' function.arguments ')'} '(' type.name ')' {allocate.initializer} +*/ + +bool Parser::rAllocateType( + exprt &arguments, + typet &atype, + exprt &initializer) +{ + if(lex->LookAhead(0)!='(') + { + atype.make_nil(); + } + else + { + // reads the '(' + lex->GetToken(); + + // we may need to backtrack + cpp_token_buffert::post pos=lex->Save(); + + if(rTypeName(atype)) + { + if(lex->GetToken()==')') + { + // we have "( type.name )" + + if(lex->LookAhead(0)!='(') + { + if(!isTypeSpecifier()) + return true; + } + else if(rAllocateInitializer(initializer)) + { + // the next token cannot be '(' + if(lex->LookAhead(0)!='(') + return true; + } + } + } + + // if we reach here, it's not '(' type.name ')', + // and we have to process '(' function.arguments ')'. + + lex->Restore(pos); + if(!rFunctionArguments(arguments)) + return false; + + if(lex->GetToken()!=')') + return false; + } + + if(lex->LookAhead(0)=='(') + { + lex->GetToken(); + + typet tname; + + if(!rTypeName(tname)) + return false; + + if(lex->GetToken()!=')') + return false; + + atype.swap(tname); + } + else + { + typet tname; + + if(!rTypeSpecifier(tname, false)) + return false; + + if(!rNewDeclarator(tname)) + return false; + + atype.swap(tname); + } + + if(lex->LookAhead(0)=='(') + { + if(!rAllocateInitializer(initializer)) + return false; + } + + return true; +} + +/* + new.declarator + : empty + | ptr.operator + | {ptr.operator} ('[' comma.expression ']')+ +*/ +bool Parser::rNewDeclarator(typet &decl) +{ + if(lex->LookAhead(0)!='[') + if(!optPtrOperator(decl)) + return false; + + while(lex->LookAhead(0)=='[') + { + Token ob, cb; + exprt expr; + + lex->GetToken(ob); + if(!rCommaExpression(expr)) + return false; + + if(lex->GetToken(cb)!=']') + return false; + + array_typet array_type; + array_type.size().swap(expr); + array_type.subtype().swap(decl); + set_location(array_type, ob); + + decl.swap(array_type); + } + + return true; +} + +/* + allocate.initializer + : '(' {initialize.expr (',' initialize.expr)* } ')' +*/ +bool Parser::rAllocateInitializer(exprt &init) +{ + { + if(lex->GetToken()!='(') + return false; + } + + init.clear(); + + if(lex->LookAhead(0)==')') + { + lex->GetToken(); + return true; + } + + for(;;) + { + exprt exp; + if(!rInitializeExpr(exp)) + return false; + + init.move_to_operands(exp); + + if(lex->LookAhead(0)==',') + lex->GetToken(); + else if(lex->LookAhead(0)==')') + { + lex->GetToken(); + break; + } + else + return false; + } + + return true; +} + +/* + postfix.exp + : primary.exp + | postfix.expr '[' comma.expression ']' + | postfix.expr '(' function.arguments ')' + | postfix.expr '.' var.name + | postfix.expr ArrowOp var.name + | postfix.expr IncOp + | openc++.postfix.expr + + openc++.postfix.expr + : postfix.expr '.' userdef.statement + | postfix.expr ArrowOp userdef.statement + + Note: function-style casts are accepted as function calls. +*/ +bool Parser::rPostfixExpr(exprt &exp) +{ + #ifdef DEBUG + std::cout << "Parser::rPostfixExpr 0\n"; + #endif + + if(!rPrimaryExpr(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rPostfixExpr 1\n"; + #endif + + exprt e; + Token cp, op; + int t, t2; + + for(;;) + { + switch(lex->LookAhead(0)) + { + case '[': + lex->GetToken(op); + if(!rCommaExpression(e)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rPostfixExpr 2\n"; + #endif + + if(lex->GetToken(cp)!=']') + return false; + + { + exprt left; + left.swap(exp); + + exp=exprt(ID_index); + exp.move_to_operands(left, e); + set_location(exp, op); + } + break; + + case '(': + #ifdef DEBUG + std::cout << "Parser::rPostfixExpr 3\n"; + #endif + + lex->GetToken(op); + if(!rFunctionArguments(e)) + return false; + + if(lex->GetToken(cp)!=')') + return false; + + #ifdef DEBUG + std::cout << "Parser::rPostfixExpr 4\n"; + #endif + + { + side_effect_expr_function_callt fc; + fc.function().swap(exp); + fc.arguments().reserve(e.operands().size()); + set_location(fc, op); + + Forall_operands(it, e) + fc.arguments().push_back(*it); + e.operands().clear(); // save some + exp.swap(fc); + } + break; + + case IncOp: + lex->GetToken(op); + + { + exprt tmp(ID_sideeffect); + tmp.move_to_operands(exp); + tmp.set(ID_statement, + op.text=="++"?ID_postincrement:ID_postdecrement); + set_location(tmp, op); + + exp.swap(tmp); + } + break; + + case '.': + case ArrowOp: + t2=lex->GetToken(op); + t=lex->LookAhead(0); + + #ifdef DEBUG + std::cout << "Parser::rPostfixExpr 5\n"; + #endif + + if(!rVarName(e)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rPostfixExpr 6\n"; + #endif + + { + exprt left; + left.swap(exp); + + if(t2=='.') + exp=exprt(ID_member); + else // ArrowOp + exp=exprt(ID_ptrmember); + + exp.move_to_operands(left); + set_location(exp, op); + } + + exp.add("component_cpp_name").swap(e); + + break; + + default: + return true; + } + } +} + +/* + __is_base_of ( base, derived ) + __is_convertible_to ( from, to ) + __is_class ( t ) + __is_... (t) +*/ + +bool Parser::rMSCTypePredicate(exprt &expr) +{ + Token tk; + + lex->GetToken(tk); + + expr.id(irep_idt(tk.text)); + set_location(expr, tk); + + typet tname1, tname2; + + switch(tk.kind) + { + case MSC_TYPE_PREDICATE: + if(lex->GetToken(tk)!='(') return false; + if(!rTypeName(tname1)) return false; + if(lex->GetToken(tk)!=')') return false; + expr.add(ID_type_arg).swap(tname1); + break; + + case MSC_BINARY_TYPE_PREDICATE: + if(lex->GetToken(tk)!='(') return false; + if(!rTypeName(tname1)) return false; + if(lex->GetToken(tk)!=',') return false; + if(!rTypeName(tname2)) return false; + if(lex->GetToken(tk)!=')') return false; + expr.add("type_arg1").swap(tname1); + expr.add("type_arg2").swap(tname2); + break; + + default: + assert(false); + } + + return true; +} + +/* + primary.exp + : Constant + | CharConst + | WideCharConst !!! new + | StringL + | WideStringL !!! new + | THIS + | var.name + | '(' comma.expression ')' + | integral.or.class.spec '(' function.arguments ')' + | typeid.expr + | openc++.primary.exp + + openc++.primary.exp + : var.name '::' userdef.statement +*/ +bool Parser::rPrimaryExpr(exprt &exp) +{ + Token tk, tk2; + + #ifdef DEBUG + std::cout << "Parser::rPrimaryExpr 0 " << lex->LookAhead(0) << " " << lex->current_token().text <<"\n"; + #endif + + switch(lex->LookAhead(0)) + { + case Constant: + case CharConst: + case WideCharConst: + lex->GetToken(tk); + exp.swap(tk.data); + set_location(exp, tk); + return true; + + case WideStringL: + case StringL: + rStringL(tk); + exp.swap(tk.data); + set_location(exp, tk); + return true; + + case THIS: + lex->GetToken(tk); + exp=exprt("cpp-this"); + set_location(exp, tk); + return true; + + case '(': + lex->GetToken(tk); + + if(lex->LookAhead(0)=='{') // GCC extension + { + codet code; + + if(!rCompoundStatement(code)) + return false; + + exp=exprt(ID_sideeffect); + exp.set(ID_statement, ID_statement_expression); + set_location(exp, tk); + exp.move_to_operands(code); + + if(lex->GetToken(tk2)!=')') + return false; + } + else + { + exprt exp2; + + if(!rCommaExpression(exp2)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rPrimaryExpr 1\n"; + #endif + + if(lex->GetToken(tk2)!=')') + return false; + + exp.swap(exp2); + } + + return true; + + case TYPEID: + return rTypeidExpr(exp); + + case MSC_TYPE_PREDICATE: + case MSC_BINARY_TYPE_PREDICATE: + return rMSCTypePredicate(exp); + + default: + { + typet type; + + if(!optIntegralTypeOrClassSpec(type)) + return false; + + if(type.is_not_nil()) + { + if(lex->GetToken(tk)!='(') + return false; + + exprt exp2; + if(!rFunctionArguments(exp2)) + return false; + + if(lex->GetToken(tk2)!=')') + return false; + + exp=exprt("explicit-constructor-call"); + exp.type().swap(type); + exp.operands().swap(exp2.operands()); + set_location(exp, tk); + } + else + { + if(!rVarName(exp)) + return false; + + if(lex->LookAhead(0)==Scope) + { + lex->GetToken(tk); + + //exp=new PtreeStaticUserStatementExpr(exp, + // Ptree::Cons(new Leaf(tk), exp2)); + // TODO + } + } + } + + return true; + } +} + +/* + var.name : {'::'} name2 ('::' name2)* + + name2 + : Identifier {template.args} + | '~' Identifier + | OPERATOR operator.name + + if var.name ends with a template type, the next token must be '(' +*/ +bool Parser::rVarName(exprt &name) +{ + #ifdef DEBUG + std::cout << "Parser::rVarName 0\n"; + #endif + + if(rVarNameCore(name)) + return true; + else + return false; +} + +bool Parser::rVarNameCore(exprt &name) +{ + #ifdef DEBUG + std::cout << "Parser::rVarNameCore 0\n"; + #endif + + name=exprt(ID_cpp_name); + irept::subt &components=name.get_sub(); + + if(lex->LookAhead(0)==TYPENAME) + { + Token tk; + lex->GetToken(tk); + name.set("typename", true); + } + + { + Token tk; + lex->LookAhead(0, tk); + set_location(name, tk); + } + + #ifdef DEBUG + std::cout << "Parser::rVarNameCore 1\n"; + #endif + + for(;;) + { + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rVarNameCore 1.1 " << lex->LookAhead(0) + << std::endl; + #endif + + switch(lex->LookAhead(0)) + { + case Identifier: + #ifdef DEBUG + std::cout << "Parser::rVarNameCore 2\n"; + #endif + + lex->GetToken(tk); + components.push_back(irept(ID_name)); + components.back().set(ID_identifier, tk.text); + set_location(components.back(), tk); + + // may be followed by template arguments + if(isTemplateArgs()) + { + #ifdef DEBUG + std::cout << "Parser::rVarNameCore 3\n"; + #endif + + irept args; + if(!rTemplateArgs(args)) + return false; + + components.push_back(irept(ID_template_args)); + components.back().add(ID_arguments).swap(args); + } + + if(!moreVarName()) return true; + break; + + case Scope: + #ifdef DEBUG + std::cout << "Parser::rVarNameCore 4\n"; + #endif + + lex->GetToken(tk); + components.push_back(irept("::")); + set_location(components.back(), tk); + break; + + case '~': + #ifdef DEBUG + std::cout << "Parser::rVarNameCore 5\n"; + #endif + + lex->GetToken(tk); + + if(lex->LookAhead(0)!=Identifier) + return false; + + components.push_back(irept("~")); + set_location(components.back(), tk); + break; + + case OPERATOR: + #ifdef DEBUG + std::cout << "Parser::rVarNameCore 6\n"; + #endif + + lex->GetToken(tk); + + components.push_back(irept(ID_operator)); + set_location(components.back(), tk); + + { + irept op; + if(!rOperatorName(op)) + return false; + + components.push_back(op); + } + return true; + + default: + return false; + } + } +} + +bool Parser::moreVarName() +{ + if(lex->LookAhead(0)==Scope) + { + int t=lex->LookAhead(1); + if(t==Identifier || t=='~' || t==OPERATOR) + return true; + } + + return false; +} + +/* + template.args : '<' any* '>' + + template.args must be followed by '(' or '::' +*/ +bool Parser::isTemplateArgs() +{ + int i=0; + int t=lex->LookAhead(i++); + + #ifdef DEBUG + std::cout << "Parser::isTemplateArgs 0\n"; + #endif + + if(t=='<') + { + int n=1; + + while(n>0) + { + #ifdef DEBUG + std::cout << "Parser::isTemplateArgs 1\n"; + #endif + + int u=lex->LookAhead(i++); + + #ifdef DEBUG + std::cout << "Parser::isTemplateArgs 2\n"; + #endif + + if(u=='<') + ++n; + else if(u=='>') + --n; + else if(u=='(') + { + int m=1; + while(m>0) + { + int v=lex->LookAhead(i++); + + #ifdef DEBUG + std::cout << "Parser::isTemplateArgs 3\n"; + #endif + + if(v=='(') + ++m; + else if(v==')') + --m; + else if(v=='\0' || v==';' || v=='}') + return false; + } + } + else if(u=='\0' || u==';' || u=='}') + return false; + + #ifdef DEBUG + std::cout << "Parser::isTemplateArgs 4\n"; + #endif + } + + #ifdef DEBUG + std::cout << "Parser::isTemplateArgs 5\n"; + #endif + + t=lex->LookAhead(i); + + #ifdef DEBUG + std::cout << "Parser::isTemplateArgs 6\n"; + #endif + + return t==Scope || t=='('; + } + + #ifdef DEBUG + std::cout << "Parser::isTemplateArgs 7\n"; + #endif + + return false; +} + +/* + function.body : compound.statement +*/ +bool Parser::rFunctionBody(codet &body) +{ + return rCompoundStatement(body); +} + +/* + compound.statement + : '{' (statement)* '}' +*/ +bool Parser::rCompoundStatement(codet &statement) +{ + Token ob, cb; + + #ifdef DEBUG + std::cout << "Parser::rCompoundStatement 1\n"; + #endif + + if(lex->GetToken(ob)!='{') + return false; + + #ifdef DEBUG + std::cout << "Parser::rCompoundStatement 2\n"; + #endif + + statement=code_blockt(); + + while(lex->LookAhead(0)!='}') + { + codet statement2; + + if(!rStatement(statement2)) + { + if(!SyntaxError()) + return false; // too many errors + + SkipTo('}'); + lex->GetToken(cb); + return true; // error recovery + } + + statement.move_to_operands(statement2); + } + + if(lex->GetToken(cb)!='}') + return false; + + return true; +} + +/* + statement + : compound.statement + | typedef + | if.statement + | switch.statement + | while.statement + | do.statement + | for.statement + | try.statement + | BREAK ';' + | CONTINUE ';' + | RETURN { comma.expression } ';' + | GOTO Identifier ';' + | CASE expression ':' statement + | DEFAULT ':' statement + | Identifier ':' statement + | expr.statement + | USING { NAMESPACE } identifier ';' +*/ +bool Parser::rStatement(codet &statement) +{ + Token tk1, tk2, tk3; + int k; + + #ifdef DEBUG + std::cout << "Parser::rStatement 0 " << lex->LookAhead(0) << "\n"; + #endif + + switch(k=lex->LookAhead(0)) + { + case '{': + return rCompoundStatement(statement); + + case TYPEDEF: + return rTypedefStatement(statement); + + case IF: + return rIfStatement(statement); + + case SWITCH: + return rSwitchStatement(statement); + + case WHILE: + return rWhileStatement(statement); + + case DO: + return rDoStatement(statement); + + case FOR: + return rForStatement(statement); + + case TRY: + return rTryStatement(statement); + + case BREAK: + case CONTINUE: + lex->GetToken(tk1); + + if(k==BREAK) + statement=codet(ID_break); + else // CONTINUE + statement=codet(ID_continue); + + set_location(statement, tk1); + + if(lex->GetToken(tk2)!=';') + return false; + + return true; + + case RETURN: + #ifdef DEBUG + std::cout << "Parser::rStatement RETURN 0\n"; + #endif + + lex->GetToken(tk1); + + statement=codet(ID_return); + set_location(statement, tk1); + + if(lex->LookAhead(0)==';') + { + #ifdef DEBUG + std::cout << "Parser::rStatement RETURN 1\n"; + #endif + lex->GetToken(tk2); + } + else + { + #ifdef DEBUG + std::cout << "Parser::rStatement RETURN 2\n"; + #endif + + exprt exp; + + if(!rCommaExpression(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rStatement RETURN 3\n"; + #endif + + if(lex->GetToken(tk2)!=';') + return false; + + statement.move_to_operands(exp); + } + + return true; + + case GOTO: + lex->GetToken(tk1); + + statement=codet(ID_goto); + set_location(statement, tk1); + + if(lex->GetToken(tk2)!=Identifier) + return false; + + if(lex->GetToken(tk3)!=';') + return false; + + statement.set(ID_destination, tk2.text); + + return true; + + case CASE: + { + lex->GetToken(tk1); + + statement=codet(ID_code); + set_location(statement, tk1); + statement.set(ID_statement, ID_label); + + exprt exp; + if(!rExpression(exp)) + return false; + + exprt &case_ops=statement.add_expr(ID_case); + case_ops.copy_to_operands(exp); + + if(lex->GetToken(tk2)!=':') + return false; + + codet statement2; + if(!rStatement(statement2)) + return false; + + statement.move_to_operands(statement2); + } + return true; + + case DEFAULT: + { + lex->GetToken(tk1); + + statement=codet(ID_code); + set_location(statement, tk1); + statement.set(ID_statement, ID_label); + statement.set(ID_default, true); + + if(lex->GetToken(tk2)!=':') + return false; + + codet statement2; + if(!rStatement(statement2)) + return false; + + statement.move_to_operands(statement2); + + } + return true; + + case GCC_ASM: + return rGCCAsmStatement(statement); + + case MSC_ASM: + return rMSCAsmStatement(statement); + + case Identifier: + if(lex->LookAhead(1)==':') // label statement + { + lex->GetToken(tk1); + + statement=codet(ID_label); + set_location(statement, tk1); + statement.set(ID_label, tk1.text); + + lex->GetToken(tk2); + + codet statement2; + if(!rStatement(statement2)) + return false; + + statement.move_to_operands(statement2); + return true; + } + + return rExprStatement(statement); + + case USING: + { + cpp_usingt cpp_using; + + if(!rUsing(cpp_using)) + return false; + + // TODO + + return true; + } + + default: + return rExprStatement(statement); + } +} + +/* + if.statement + : IF '(' comma.expression ')' statement { ELSE statement } +*/ +bool Parser::rIfStatement(codet &statement) +{ + Token tk1, tk2, tk3, tk4; + + if(lex->GetToken(tk1)!=IF) + return false; + + statement=codet(ID_ifthenelse); + set_location(statement, tk1); + + if(lex->GetToken(tk2)!='(') + return false; + + exprt exp; + if(!rCondition(exp)) + return false; + + if(lex->GetToken(tk3)!=')') + return false; + + codet then; + if(!rStatement(then)) + return false; + + statement.reserve_operands(3); + statement.move_to_operands(exp); + statement.move_to_operands(then); + + if(lex->LookAhead(0)==ELSE) + { + lex->GetToken(tk4); + + codet otherwise; + if(!rStatement(otherwise)) + return false; + + statement.move_to_operands(otherwise); + } + + return true; +} + +/* + switch.statement + : SWITCH '(' comma.expression ')' statement +*/ +bool Parser::rSwitchStatement(codet &statement) +{ + Token tk1, tk2, tk3; + + if(lex->GetToken(tk1)!=SWITCH) + return false; + + statement=codet(ID_switch); + set_location(statement, tk1); + + if(lex->GetToken(tk2)!='(') + return false; + + exprt exp; + if(!rCondition(exp)) + return false; + + if(lex->GetToken(tk3)!=')') + return false; + + codet body; + if(!rStatement(body)) + return false; + + statement.move_to_operands(exp, body); + + return true; +} + +/* + while.statement + : WHILE '(' comma.expression ')' statement +*/ +bool Parser::rWhileStatement(codet &statement) +{ + Token tk1, tk2, tk3; + + if(lex->GetToken(tk1)!=WHILE) + return false; + + statement=codet(ID_while); + set_location(statement, tk1); + + if(lex->GetToken(tk2)!='(') + return false; + + exprt exp; + if(!rCondition(exp)) + return false; + + if(lex->GetToken(tk3)!=')') + return false; + + codet body; + if(!rStatement(body)) + return false; + + statement.move_to_operands(exp, body); + + return true; +} + +/* + do.statement + : DO statement WHILE '(' comma.expression ')' ';' +*/ +bool Parser::rDoStatement(codet &statement) +{ + Token tk0, tk1, tk2, tk3, tk4; + + if(lex->GetToken(tk0)!=DO) + return false; + + statement=codet(ID_dowhile); + set_location(statement, tk0); + + codet body; + if(!rStatement(body)) + return false; + + if(lex->GetToken(tk1)!=WHILE) + return false; + + if(lex->GetToken(tk2)!='(') + return false; + + exprt exp; + if(!rCommaExpression(exp)) + return false; + + if(lex->GetToken(tk3)!=')') + return false; + + if(lex->GetToken(tk4)!=';') + return false; + + statement.move_to_operands(exp, body); + + return true; +} + +/* + for.statement + : FOR '(' expr.statement {comma.expression} ';' {comma.expression} ')' + statement +*/ +bool Parser::rForStatement(codet &statement) +{ + Token tk1, tk2, tk3, tk4; + + if(lex->GetToken(tk1)!=FOR) + return false; + + statement=codet(ID_for); + set_location(statement, tk1); + + if(lex->GetToken(tk2)!='(') + return false; + + codet exp1; + + if(!rExprStatement(exp1)) + return false; + + exprt exp2; + + if(lex->LookAhead(0)==';') + exp2.make_nil(); + else + if(!rCommaExpression(exp2)) + return false; + + if(lex->GetToken(tk3)!=';') + return false; + + exprt exp3; + + if(lex->LookAhead(0)==')') + exp3.make_nil(); + else + { + if(!rCommaExpression(exp3)) + return false; + } + + if(lex->GetToken(tk4)!=')') + return false; + + codet body; + + if(!rStatement(body)) + return false; + + statement.reserve_operands(4); + statement.move_to_operands(exp1); + statement.move_to_operands(exp2); + statement.move_to_operands(exp3); + statement.move_to_operands(body); + + return true; +} + +/* + try.statement + : TRY compound.statement (exception.handler)+ ';' + + exception.handler + : CATCH '(' (arg.declaration | Ellipsis) ')' compound.statement +*/ +bool Parser::rTryStatement(codet &statement) +{ + Token tk, op, cp; + + if(lex->GetToken(tk)!=TRY) + return false; + + statement=codet(ID_catch); + set_location(statement, tk); + + codet body; + cpp_declarationt handler; + + if(!rCompoundStatement(body)) + return false; + + statement.move_to_operands(body); + + do + { + if(lex->GetToken(tk)!=CATCH) + return false; + + if(lex->GetToken(op)!='(') + return false; + + if(lex->LookAhead(0)==Ellipsis) + { + lex->GetToken(cp); + //handler=new Leaf(cp); + } + else + { + if(!rArgDeclaration(handler)) + return false; + } + + if(lex->GetToken(cp)!=')') + return false; + + if(!rCompoundStatement(body)) + return false; + + // TODO + //st=Ptree::Snoc(st, Ptree::List(new LeafReserved(tk), + // new Leaf(op), handler, new Leaf(cp), + // body)); + } + while(lex->LookAhead(0)==CATCH); + + return true; +} + +bool Parser::rGCCAsmStatement(codet &statement) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rGCCAsmStatement 1\n"; + #endif // DEBUG + + // asm [volatile] ("stuff" [ : ["=S" [(__res)], ... ]]) ; + + if(lex->GetToken(tk)!=GCC_ASM) return false; + + statement=codet(ID_asm); + statement.set(ID_flavor, ID_gcc); + set_location(statement, tk); + + if(lex->LookAhead(0)==VOLATILE) + lex->GetToken(tk); + + #ifdef DEBUG + std::cout << "Parser::rGCCAsmStatement 3\n"; + #endif // DEBUG + + if(lex->GetToken(tk)!='(') return false; + if(!rStringL(tk)) return false; + + statement.move_to_operands(tk.data); + + #ifdef DEBUG + std::cout << "Parser::rGCCAsmStatement 3\n"; + #endif // DEBUG + + while(lex->LookAhead(0)!=')') + { + #ifdef DEBUG + std::cout << "Parser::rGCCAsmStatement 4\n"; + #endif // DEBUG + + // get ':' + if(lex->GetToken(tk)!=':') return false; + + for(;;) + { + if(lex->LookAhead(0)!=StringL) break; + + // get StringL + rStringL(tk); + + if(lex->LookAhead(0)=='(') + { + // get '(' + lex->GetToken(tk); + + #ifdef DEBUG + std::cout << "Parser::rGCCAsmStatement 5\n"; + #endif // DEBUG + + exprt expr; + if(!rCommaExpression(expr)) return false; + + #ifdef DEBUG + std::cout << "Parser::rGCCAsmStatement 6\n"; + #endif // DEBUG + + if(lex->GetToken(tk)!=')') return false; + } + + // more? + if(lex->LookAhead(0)!=',') break; + lex->GetToken(tk); + } + } + + #ifdef DEBUG + std::cout << "Parser::rGCCAsmStatement 7\n"; + #endif // DEBUG + + if(lex->GetToken(tk)!=')') return false; + if(lex->GetToken(tk)!=';') return false; + + #ifdef DEBUG + std::cout << "Parser::rGCCAsmStatement 8\n"; + #endif // DEBUG + + return true; +} + +bool Parser::rMSCAsmStatement(codet &statement) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rMSCAsmStatement 1\n"; + #endif // DEBUG + + // asm "STUFF" + // asm { "STUFF" } + + if(lex->GetToken(tk)!=MSC_ASM) return false; + + statement=codet(ID_asm); + statement.set(ID_flavor, ID_msc); + set_location(statement, tk); + + #ifdef DEBUG + std::cout << "Parser::rMSCAsmStatement 2\n"; + #endif // DEBUG + + if(lex->LookAhead(0)=='{') + { + lex->GetToken(tk); // eat the '{' + + #ifdef DEBUG + std::cout << "Parser::rMSCAsmStatement 3\n"; + #endif // DEBUG + + if(!rStringL(tk)) return false; + statement.move_to_operands(tk.data); + if(lex->GetToken(tk)!='}') return false; + + #ifdef DEBUG + std::cout << "Parser::rMSCAsmStatement 4\n"; + #endif // DEBUG + } + else + { + #ifdef DEBUG + std::cout << "Parser::rMSCAsmStatement 5\n"; + #endif // DEBUG + + if(!rStringL(tk)) return false; + statement.move_to_operands(tk.data); + + #ifdef DEBUG + std::cout << "Parser::rMSCAsmStatement 6\n"; + #endif // DEBUG + } + + #ifdef DEBUG + std::cout << "Parser::rMSCAsmStatement 7\n"; + #endif // DEBUG + + return true; +} + +/* + expr.statement + : ';' + | declaration.statement + | comma.expression ';' + | openc++.postfix.expr + | openc++.primary.exp +*/ +bool Parser::rExprStatement(codet &statement) +{ + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rExprStatement 0\n"; + #endif + + if(lex->LookAhead(0)==';') + { + #ifdef DEBUG + std::cout << "Parser::rExprStatement 1\n"; + #endif + + lex->GetToken(tk); + statement=codet(ID_skip); + set_location(statement, tk); + return true; + } + else + { + #ifdef DEBUG + std::cout << "Parser::rExprStatement 2\n"; + #endif + + cpp_token_buffert::post pos=lex->Save(); + + if(rDeclarationStatement(statement)) + { + #ifdef DEBUG + std::cout << "rDe: " << statement << std::endl; + #endif + return true; + } + else + { + exprt exp; + + lex->Restore(pos); + + #ifdef DEBUG + std::cout << "Parser::rExprStatement 3\n"; + #endif + + if(!rCommaExpression(exp)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rExprStatement 4\n"; + #endif + + #ifdef DEBUG + std::cout << "Parser::rExprStatement 5 " << lex->LookAhead(0) << "\n"; + #endif + + if(lex->GetToken(tk)!=';') + return false; + + #ifdef DEBUG + std::cout << "Parser::rExprStatement 6\n"; + #endif + + statement=codet(ID_expression); + statement.location()=exp.location(); + statement.move_to_operands(exp); + return true; + } + } +} + +bool Parser::rCondition(exprt &statement) +{ + cpp_token_buffert::post pos=lex->Save(); + + cpp_declarationt declaration; + + if(rSimpleDeclaration(declaration)) + { + statement=codet(ID_decl); + statement.move_to_operands(declaration); + return true; + } + else + { + lex->Restore(pos); + + if(!rCommaExpression(statement)) + return false; + + return true; + } +} + +/* + declaration.statement + : decl.head integral.or.class.spec {cv.qualify} {declarators} ';' + | decl.head name {cv.qualify} declarators ';' + | const.declaration + + decl.head + : {storage.spec} {cv.qualify} + + const.declaration + : cv.qualify {'*'} Identifier '=' expression {',' declarators} ';' + + Note: if you modify this function, take a look at rDeclaration(), too. +*/ +bool Parser::rDeclarationStatement(codet &statement) +{ + cpp_storage_spect storage_spec; + typet cv_q, integral; + cpp_member_spect member_spec; + + #ifdef DEBUG + std::cout << "Parser::rDeclarationStatement 1\n"; + #endif + + if(!optStorageSpec(storage_spec)) + return false; + + cv_q.make_nil(); + + if(!optCvQualify(cv_q)) + return false; + + // added for junk like const volatile static ... + if(!optStorageSpec(storage_spec)) + return false; + + if(!optCvQualify(cv_q)) + return false; + + if(!optIntegralTypeOrClassSpec(integral)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rDeclarationStatement 2\n"; + #endif + + if(integral.is_not_nil()) + return rIntegralDeclStatement(statement, storage_spec, integral, cv_q); + else + { + int t=lex->LookAhead(0); + + #ifdef DEBUG + std::cout << "Parser::rDeclarationStatement 3 " << t << "\n"; + #endif + + if(cv_q.is_not_nil() && + ((t==Identifier && lex->LookAhead(1)=='=') || t=='*')) + { + #ifdef DEBUG + std::cout << "Parser::rDeclarationStatement 4\n"; + #endif + + statement=codet(ID_decl); + statement.operands().resize(1); + cpp_declarationt &declaration=(cpp_declarationt &)(statement.op0()); + return rConstDeclaration(declaration, storage_spec, member_spec, cv_q); + } + else + return rOtherDeclStatement(statement, storage_spec, cv_q); + } +} + +/* + integral.decl.statement + : decl.head integral.or.class.spec {cv.qualify} {declarators} ';' +*/ +bool Parser::rIntegralDeclStatement( + codet &statement, + cpp_storage_spect &storage_spec, + typet &integral, + typet &cv_q) +{ + Token tk; + + if(!optCvQualify(cv_q)) + return false; + + merge_types(cv_q, integral); + + cpp_declarationt declaration; + declaration.type().swap(integral); + declaration.storage_spec().swap(storage_spec); + + if(lex->LookAhead(0)==';') + { + lex->GetToken(tk); + statement=codet(ID_decl); + set_location(statement, tk); + statement.move_to_operands(declaration); + } + else + { + if(!rDeclarators(declaration.declarators(), false, true)) + return false; + + if(lex->GetToken(tk)!=';') + return false; + + statement=codet(ID_decl); + set_location(statement, tk); + + statement.move_to_operands(declaration); + } + + return true; +} + +/* + other.decl.statement + :decl.head name {cv.qualify} declarators ';' +*/ +bool Parser::rOtherDeclStatement( + codet &statement, + cpp_storage_spect &storage_spec, + typet &cv_q) +{ + typet type_name; + Token tk; + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclStatement 1\n"; + #endif // DEBUG + + if(!rName(type_name)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclStatement 2\n"; + #endif // DEBUG + + if(!optCvQualify(cv_q)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclStatement 3\n"; + #endif // DEBUG + + merge_types(cv_q, type_name); + + cpp_declarationt declaration; + declaration.type().swap(type_name); + declaration.storage_spec().swap(storage_spec); + + if(!rDeclarators(declaration.declarators(), false, true)) + return false; + + #ifdef DEBUG + std::cout << "Parser::rOtherDeclStatement 4\n"; + #endif // DEBUG + + if(lex->GetToken(tk)!=';') + return false; + + statement=codet(ID_decl); + set_location(statement, tk); + statement.move_to_operands(declaration); + + return true; +} + +bool Parser::MaybeTypeNameOrClassTemplate(Token&) +{ + return true; +} + +void Parser::SkipTo(int token) +{ + Token tk; + + for(;;) + { + int t=lex->LookAhead(0); + if(t==token || t=='\0') + break; + else + lex->GetToken(tk); + } +} + +/*******************************************************************\ + +Function: Parser::parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool Parser::parse() +{ + number_of_errors=0; + + #if 1 + cpp_itemt item; + + while(rProgram(item)) + { + parser->parse_tree.items.push_back(cpp_itemt()); + parser->parse_tree.items.back().swap(item); + } + + #else + Token tk; + + while(lex->GetToken(tk)) + std::cout << tk.text << ' '; + + std::cout << std::endl; + #endif + + return number_of_errors!=0; +} + +/*******************************************************************\ + +Function: cpp_parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cpp_parse() +{ + Parser parser; + parser.lex=&cpp_parser.token_buffer; + parser.parser=&cpp_parser; + return parser.parse(); +} diff --git a/src/cpp/recursion_counter.h b/src/cpp/recursion_counter.h new file mode 100644 index 00000000000..9113fd2712d --- /dev/null +++ b/src/cpp/recursion_counter.h @@ -0,0 +1,24 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +class recursion_countert +{ +public: + recursion_countert(unsigned &_cnt):cnt(_cnt) + { + cnt++; + } + + ~recursion_countert() + { + cnt--; + } + +protected: + unsigned &cnt; +}; diff --git a/src/cpp/scanner.l b/src/cpp/scanner.l new file mode 100644 index 00000000000..3dd24d22ad1 --- /dev/null +++ b/src/cpp/scanner.l @@ -0,0 +1,410 @@ +%option nounput + +%{ + +/*************** Includes and Defines *****************************/ + +#ifdef _WIN32 +#define YY_NO_UNISTD_H +static int isatty(int) { return 0; } +#endif + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define PARSER cpp_parser +#define YYSTYPE unsigned +#undef ECHO +#define ECHO + +#include "cpp_parser.h" +#include "tokens.h" + +void set_line_no(); + +%} + +%x COMMENT1 +%x COMMENT2 +%x GCC_ATTRIBUTE +%x MSC_ASM1 +%x MSC_ASM2 +%x MSC_DECLSPEC +%x MSC_PRAGMA +%x MSC_ANNOTATION + +%{ +void cpp_scanner_init() +{ + YY_FLUSH_BUFFER; + BEGIN(0); // this is INITIAL, hopefully +} +%} + +identifier [a-zA-Z_][0-9a-zA-Z_$]* + +exponent_part [eE][-+]?[0-9]+ +fractional_constant ([0-9]*"."[0-9]+)|([0-9]+".") +floating_constant (({fractional_constant}{exponent_part}?)|([0-9]+{exponent_part}))[FfLl]? + +integer_suffix [uUlL] +integer_suffix_opt {integer_suffix}* +decimal_constant [1-9][0-9]*{integer_suffix_opt} +octal_constant "0"[0-7]*{integer_suffix_opt} +hex_constant "0"[xX][0-9a-fA-F]+{integer_suffix_opt} + +simple_escape [abfnrtv'"?\\] +octal_escape [0-7]{1,3} +hex_escape "x"[0-9a-fA-F]+ + +escape_sequence [\\]({simple_escape}|{octal_escape}|{hex_escape}) +c_char [^'\\\n]|{escape_sequence} +s_char [^"\\\n]|{escape_sequence} + +h_tab [\011] +form_feed [\014] +v_tab [\013] +c_return [\015] + +horizontal_white [ ]|{h_tab} +ws {horizontal_white}* + +integer [0-9]+ +cppfile "\""[ !#-~]*"\"" +cpplineno "#"{horizontal_white}*"line"*{horizontal_white}*{integer}.*"\n" +pragma "#"{horizontal_white}*pragma{horizontal_white}.*"\n" + +%% + +%{ +#define loc() (PARSER.current_token().text=yytext,PARSER.set_location()) +#define MSC_Keyword(x) (PARSER.mode==cpp_parsert::MSC?x:Identifier) +%} + +"/*" { BEGIN(COMMENT1); } /* begin C comment state */ + +{ + "*/" { BEGIN(INITIAL); } /* end comment state, back to INITIAL */ + "/*" { yycpperror("Probably nested comments"); } + <> { yycpperror("Unterminated comment"); } + [^*/\n]* { /* ignore every char except '*' and NL (performance!) */ } + . { } /* all single characters within comments are ignored */ + \n { } + } + +"//" { BEGIN(COMMENT2); } /* begin C++ comment state */ + +{ + \n { BEGIN(INITIAL); } /* end comment state, back to INITIAL */ + .* { } /* all characters within comments are ignored */ + } + +{ +")" { PARSER.parenthesis_counter--; + if(PARSER.parenthesis_counter==0) + BEGIN(INITIAL); } +"(" { PARSER.parenthesis_counter++; } +. { /* Throw away */ } +} + +"[repeatable" { BEGIN(MSC_ANNOTATION); } +"[source_annotation_attribute" { BEGIN(MSC_ANNOTATION); } +"[returnvalue" { BEGIN(MSC_ANNOTATION); } +"[SA_Pre" { BEGIN(MSC_ANNOTATION); } +"[SA_Post" { BEGIN(MSC_ANNOTATION); } +"[SA_FormatString" { BEGIN(MSC_ANNOTATION); } + +"__pragma" { BEGIN(MSC_PRAGMA); PARSER.parenthesis_counter=0; } + +"]" { BEGIN(INITIAL); } +. { /* ignore */ } + +{ws}"{" { BEGIN(MSC_ASM2); loc(); return '{'; } +[^{^}]* { loc(); + PARSER.current_token().data=exprt(yytext); + BEGIN(INITIAL); + return StringL; } + +[^}]* { loc(); + PARSER.current_token().data=exprt(yytext); + return StringL; } +"}" { BEGIN(INITIAL); loc(); return '}'; } + +")" { PARSER.parenthesis_counter--; + if(PARSER.parenthesis_counter==0) + BEGIN(INITIAL); } +"(" { PARSER.parenthesis_counter++; } +. { /* Throw away */ } + +")" { PARSER.parenthesis_counter--; + if(PARSER.parenthesis_counter==0) + BEGIN(INITIAL); } +"(" { PARSER.parenthesis_counter++; } +. { /* Throw away */ } + +{ +{horizontal_white}+ +({v_tab}|{c_return}|{form_feed})+ +({horizontal_white}|{v_tab}|{c_return}|{form_feed})*"\n" + +__alignof__ { loc(); return SIZEOF; } +__asm__|asm|__asm { if(PARSER.mode==cpp_parsert::MSC) + { + BEGIN(MSC_ASM1); + return MSC_ASM; + } + else + { + loc(); + return GCC_ASM; + } + } +__attribute { BEGIN(GCC_ATTRIBUTE); PARSER.parenthesis_counter=0; } +__attribute__ { BEGIN(GCC_ATTRIBUTE); PARSER.parenthesis_counter=0; } +_cdecl|__cdecl { loc(); return MSC_Keyword(CDECL); } +__const { loc(); return CONST; } +__declspec { BEGIN(MSC_DECLSPEC); PARSER.parenthesis_counter=0; } +__extension__ { loc(); return Ignore; } +_inline { loc(); return INLINE; } +__inline { loc(); return INLINE; } +__inline__ { loc(); return INLINE; } +__forceinline { loc(); return MSC_Keyword(INLINE); } +__noreturn__ { loc(); return Ignore; } +restrict { loc(); return Ignore; } +__restrict__ { loc(); return Ignore; } +__restrict { loc(); return Ignore; } +__stdcall { loc(); return MSC_Keyword(STDCALL); } +__fastcall { loc(); return MSC_Keyword(FASTCALL); } +__clrcall { loc(); return MSC_Keyword(CLRCALL); } +__signed { loc(); return SIGNED; } +__signed__ { loc(); return SIGNED; } +__vector { loc(); return Ignore; } +__volatile__ { loc(); return VOLATILE; } +typeof { loc(); return TYPEOF; } +__typeof { loc(); return TYPEOF; } +__typeof__ { loc(); return TYPEOF; } +__int8 { loc(); return MSC_Keyword(INT8); } +__int16 { loc(); return MSC_Keyword(INT16); } +__int32 { loc(); return MSC_Keyword(INT32); } +__int64 { loc(); return MSC_Keyword(INT64); } +__ptr32 { loc(); return MSC_Keyword(PTR32); } +__ptr64 { loc(); return MSC_Keyword(PTR64); } +__thiscall { loc(); return Ignore; } +__w64 { loc(); return Ignore; } +_Complex { loc(); return COMPLEX; } +__complex__ { loc(); return COMPLEX; } +__real__ { loc(); return REAL; } +__imag__ { loc(); return IMAG; } +auto { loc(); return AUTO; } +bool { loc(); return BOOLEAN; } +break { loc(); return BREAK; } +case { loc(); return CASE; } +catch { loc(); return CATCH; } +char { loc(); return CHAR; } +class { loc(); return CLASS; } +const { loc(); return CONST; } +continue { loc(); return CONTINUE; } +default { loc(); return DEFAULT; } +delete { loc(); return DELETE; } +decltype { loc(); return DECLTYPE; } +do { loc(); return DO; } +double { loc(); return DOUBLE; } +else { loc(); return ELSE; } +enum { loc(); return ENUM; } +explicit { loc(); return EXPLICIT; } +extern { loc(); return EXTERN; } +float { loc(); return FLOAT; } +for { loc(); return FOR; } +friend { loc(); return FRIEND; } +goto { loc(); return GOTO; } +if { loc(); return IF; } +inline { loc(); return INLINE; } +int { loc(); return INT; } +long { loc(); return LONG; } +mutable { loc(); return MUTABLE; } +namespace { loc(); return NAMESPACE; } +new { loc(); return NEW; } +noreturn { loc(); return Ignore; } +operator { loc(); return OPERATOR; } +private { loc(); return PRIVATE; } +protected { loc(); return PROTECTED; } +public { loc(); return PUBLIC; } +register { loc(); return REGISTER; } +return { loc(); return RETURN; } +short { loc(); return SHORT; } +signed { loc(); return SIGNED; } +sizeof { loc(); return SIZEOF; } +static { loc(); return STATIC; } +static_assert { loc(); return STATIC_ASSERT; } +struct { loc(); return STRUCT; } +switch { loc(); return SWITCH; } +template { loc(); return TEMPLATE; } +this { loc(); return THIS; } +throw { loc(); return THROW; } +try { loc(); return TRY; } +typedef { loc(); return TYPEDEF; } +typeid { loc(); return TYPEID; } +typename { loc(); return TYPENAME; } +union { loc(); return UNION; } +unsigned { loc(); return UNSIGNED; } +using { loc(); return USING; } +virtual { loc(); return VIRTUAL; } +void { loc(); return VOID; } +volatile { loc(); return VOLATILE; } +wchar_t { loc(); return WCHAR_T; } +while { loc(); return WHILE; } +__CPROVER_thread_local { loc(); return THREAD_LOCAL; } + +%{ +/* a huge batch of MS extensions + http://msdn.microsoft.com/en-us/library/ms177194(v=vs.80).aspx */ +%} + +"__has_assign" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_copy" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_finalizer" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_nothrow_assign" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_nothrow_constructor" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_nothrow_copy" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_trivial_assign" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_trivial_constructor" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_trivial_copy" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_trivial_destructor" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_user_destructor" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__has_virtual_destructor" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_abstract" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_base_of" { loc(); return MSC_Keyword(MSC_BINARY_TYPE_PREDICATE); } +"__is_class" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_convertible_to" { loc(); return MSC_Keyword(MSC_BINARY_TYPE_PREDICATE); } +"__is_delegate" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_empty" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_enum" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_interface_class" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_pod" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_polymorphic" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_ref_array" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_ref_class" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_sealed" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_simple_value_class" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_union" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } +"__is_value_class" { loc(); return MSC_Keyword(MSC_TYPE_PREDICATE); } + +{identifier} { loc(); + return Identifier; // or TYPDEFname + } + +{decimal_constant} { loc(); + PARSER.current_token().data=convert_integer_literal(yytext, 10); + return Constant; + } + +{octal_constant} { loc(); + PARSER.current_token().data=convert_integer_literal(yytext, 8); + return Constant; + } + +{hex_constant} { loc(); + PARSER.current_token().data=convert_integer_literal(yytext, 16); + return Constant; + } + +{floating_constant} { + loc(); + PARSER.current_token().data=convert_float_literal(yytext); + return Constant; + } + +"L"?[']{c_char}+['] { + loc(); + PARSER.current_token().data=convert_character_literal(yytext, false); + return Constant; + } + +"L"?["]{s_char}*["] { + loc(); + PARSER.current_token().data=convert_string_literal(yytext); + return StringL; + } + +"(" { loc(); return yytext[0]; } +")" { loc(); return yytext[0]; } +"," { loc(); return yytext[0]; } + +{cpplineno} { set_line_no(); } +{pragma} { /* ignore it */ } + +"#" + +"#".*"\n" { yycpperror("unknown preprocessor directive"); } + +"##" { } + +"{" { loc(); return '{'; } +"}" { loc(); return '}'; } +"[" { loc(); return '['; } +"]" { loc(); return ']'; } +"." { loc(); return '.'; } +"&" { loc(); return '&'; } +"*" { loc(); return '*'; } +"+" { loc(); return '+'; } +"-" { loc(); return '-'; } +"~" { loc(); return '~'; } +"!" { loc(); return '!'; } +"/" { loc(); return '/'; } +"%" { loc(); return '%'; } +"<" { loc(); return '<'; } +">" { loc(); return '>'; } +"^" { loc(); return '^'; } +"|" { loc(); return '|'; } +"?" { loc(); return '?'; } +":" { loc(); return ':'; } +";" { loc(); return ';'; } +"=" { loc(); return '='; } + +".*" { loc(); return PmOp; } +"->*" { loc(); return PmOp; } +"::" { loc(); return Scope; } +"->" { loc(); return ArrowOp; } +"++" { loc(); return IncOp; } +"--" { loc(); return IncOp; } +"<<" { loc(); return ShiftOp; } +">>" { loc(); return ShiftOp; } +"<=" { loc(); return RelOp; } +">=" { loc(); return RelOp; } +"==" { loc(); return EqualOp; } +"!=" { loc(); return EqualOp; } +"&&" { loc(); return ANDAND; } +"||" { loc(); return LogOrOp; } +"*=" { loc(); return AssignOp; } +"/=" { loc(); return AssignOp; } +"%=" { loc(); return AssignOp; } +"+=" { loc(); return AssignOp; } +"-=" { loc(); return AssignOp; } +"<<=" { loc(); return AssignOp; } +">>=" { loc(); return AssignOp; } +"&=" { loc(); return AssignOp; } +"^=" { loc(); return AssignOp; } +"|=" { loc(); return AssignOp; } +"..." { loc(); return Ellipsis; } + +. { yycpperror("unknown character"); } +} + +%% + +int yywrap() { return 1; } + +void set_line_no() +{ + preprocessor_line(yytext, PARSER.line_no, PARSER.filename); +} diff --git a/src/cpp/template_map.cpp b/src/cpp/template_map.cpp new file mode 100644 index 00000000000..243af634396 --- /dev/null +++ b/src/cpp/template_map.cpp @@ -0,0 +1,377 @@ +/*******************************************************************\ + +Module: C++ Language Type Checking + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "template_map.h" + +/*******************************************************************\ + +Function: template_mapt::apply + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_mapt::apply(typet &type) const +{ + if(type.id()==ID_array) + { + apply(type.subtype()); + apply(static_cast(type.add(ID_size))); + } + else if(type.id()==ID_pointer) + { + apply(type.subtype()); + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + irept::subt &components=type.add(ID_components).get_sub(); + + Forall_irep(it, components) + { + typet &subtype=static_cast(it->add(ID_type)); + apply(subtype); + } + } + else if(type.id()==ID_symbol) + { + type_mapt::const_iterator m_it= + type_map.find(type.get(ID_identifier)); + + if(m_it!=type_map.end()) + { + type=m_it->second; + return; + } + } + else if(type.id()==ID_code) + { + apply(static_cast(type.add(ID_return_type))); + + irept::subt &arguments=type.add(ID_arguments).get_sub(); + + Forall_irep(it, arguments) + { + if(it->id()==ID_argument) + apply(static_cast(it->add(ID_type))); + } + } + else if(type.id()==ID_merged_type) + { + Forall_subtypes(it, type) + apply(*it); + } +} + +/*******************************************************************\ + +Function: template_mapt::apply + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_mapt::apply(exprt &expr) const +{ + apply(expr.type()); + + if(expr.id()==ID_symbol) + { + expr_mapt::const_iterator m_it= + expr_map.find(expr.get(ID_identifier)); + + if(m_it!=expr_map.end()) + { + expr=m_it->second; + return; + } + } + + Forall_operands(it, expr) + apply(*it); +} + +/*******************************************************************\ + +Function: template_mapt::lookup + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt template_mapt::lookup(const irep_idt &identifier) const +{ + type_mapt::const_iterator t_it= + type_map.find(identifier); + + if(t_it!=type_map.end()) + { + exprt e(ID_type); + e.type()=t_it->second; + return e; + } + + expr_mapt::const_iterator e_it= + expr_map.find(identifier); + + if(e_it!=expr_map.end()) + return e_it->second; + + return static_cast(get_nil_irep()); +} + +/*******************************************************************\ + +Function: template_mapt::lookup_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet template_mapt::lookup_type(const irep_idt &identifier) const +{ + type_mapt::const_iterator t_it= + type_map.find(identifier); + + if(t_it!=type_map.end()) + return t_it->second; + + return static_cast(get_nil_irep()); +} + +/*******************************************************************\ + +Function: template_mapt::lookup_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt template_mapt::lookup_expr(const irep_idt &identifier) const +{ + expr_mapt::const_iterator e_it= + expr_map.find(identifier); + + if(e_it!=expr_map.end()) + return e_it->second; + + return static_cast(get_nil_irep()); +} + +/*******************************************************************\ + +Function: template_mapt::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_mapt::print(std::ostream &out) const +{ + for(type_mapt::const_iterator it=type_map.begin(); + it!=type_map.end(); + it++) + out << it->first << " = " << it->second.pretty() << std::endl; + + for(expr_mapt::const_iterator it=expr_map.begin(); + it!=expr_map.end(); + it++) + out << it->first << " = " << it->second.pretty() << std::endl; +} + +/*******************************************************************\ + +Function: template_mapt::build + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_mapt::build( + const template_typet &template_type, + const cpp_template_args_tct &template_args) +{ + const template_typet::parameterst &template_parameters= + template_type.parameters(); + + cpp_template_args_tct::argumentst instance= + template_args.arguments(); + + template_typet::parameterst::const_iterator t_it= + template_parameters.begin(); + + if(instance.size()(param.find("#default")); + + if(value.is_not_nil()) + instance.push_back(value); + else + break; + } + } + + // these should have been typechecked before + assert(instance.size()==template_parameters.size()); + + for(cpp_template_args_tct::argumentst::const_iterator + i_it=instance.begin(); + i_it!=instance.end(); + i_it++, t_it++) + { + assert(t_it!=template_parameters.end()); + + const exprt &t=*t_it; + const exprt &i=*i_it; + + if(t.id()==ID_type) + { + if(i.id()!=ID_type) + assert(false); // typechecked before! + + typet tmp=i.type(); + + irep_idt identifier=t.type().get(ID_identifier); + type_map[identifier] = tmp; + + #if 0 + std::cout << "T-arg " << identifier << ":=" << to_string(tmp) << std::endl; + #endif + } + else + { + if(i.id()==ID_type) + assert(false); // typechecked before! + + irep_idt identifier=t.get(ID_identifier); + expr_map[identifier]=i; + + #if 0 + std::cout << "T-arg " << identifier << ":=" << to_string(i) << std::endl; + #endif + } + } +} + +/*******************************************************************\ + +Function: template_mapt::build_unassigned + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_mapt::build_unassigned( + const template_typet &template_type) +{ + const template_typet::parameterst &template_parameters= + template_type.parameters(); + + for(template_typet::parameterst::const_iterator + t_it=template_parameters.begin(); + t_it!=template_parameters.end(); + t_it++) + { + const exprt &t=*t_it; + + if(t.id()==ID_type) + { + typet tmp(ID_unassigned); + tmp.set(ID_identifier, t.type().get(ID_identifier)); + tmp.location()=t.location(); + type_map[t.type().get(ID_identifier)]=tmp; + } + else + { + exprt tmp(ID_unassigned, t.type()); + tmp.set(ID_identifier, t.get(ID_identifier)); + tmp.location()=t.location(); + expr_map[t.get(ID_identifier)]=tmp; + } + } +} + +/*******************************************************************\ + +Function: template_mapt::build_template_args + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cpp_template_args_tct template_mapt::build_template_args( + const template_typet &template_type) const +{ + const template_typet::parameterst &template_parameters= + template_type.parameters(); + + cpp_template_args_tct template_args; + template_args.arguments().resize(template_parameters.size()); + + for(unsigned i=0; i +#include + +#include + +#include "cpp_template_type.h" +#include "cpp_template_args.h" + +class template_mapt +{ +public: + // this maps template parameters to their instantiated value + typedef std::map type_mapt; + typedef std::map expr_mapt; + type_mapt type_map; + expr_mapt expr_map; + + void apply(exprt &dest) const; + void apply(typet &dest) const; + + void swap(template_mapt &template_map) + { + type_map.swap(template_map.type_map); + expr_map.swap(template_map.expr_map); + } + + exprt lookup(const irep_idt &identifier) const; + typet lookup_type(const irep_idt &identifier) const; + exprt lookup_expr(const irep_idt &identifier) const; + + void print(std::ostream &out) const; + + void clear() + { + type_map.clear(); + expr_map.clear(); + } + + void build( + const template_typet &template_type, + const cpp_template_args_tct &template_args); + + void build_unassigned( + const template_typet &template_type); + + cpp_template_args_tct build_template_args( + const template_typet &template_type) const; +}; + +class cpp_saved_template_mapt +{ +public: + cpp_saved_template_mapt(template_mapt &map): + old_map(map), map(map) + { + } + + ~cpp_saved_template_mapt() + { + #if 0 + std::cout << "RESTORING TEMPLATE MAP\n"; + #endif + map.swap(old_map); + } + +private: + template_mapt old_map; + template_mapt ↦ +}; + +#endif diff --git a/src/cpp/tokens.h b/src/cpp/tokens.h new file mode 100644 index 00000000000..92b68335fae --- /dev/null +++ b/src/cpp/tokens.h @@ -0,0 +1,114 @@ +// tokens + +enum { + +Identifier = 258, +Constant = 262, +CharConst = 263, +StringL = 264, +AssignOp = 267, +EqualOp = 268, +RelOp = 269, +ShiftOp = 270, +LogOrOp = 271, +ANDAND = 272, +IncOp = 273, +Scope = 274, +Ellipsis = 275, +PmOp = 276, +ArrowOp = 277, +BadToken = 278, +AUTO = 281, +CHAR = 282, +CLASS = 283, +CONST = 284, +DELETE = 285, +DOUBLE = 286, +ENUM = 287, +EXTERN = 288, +FLOAT = 289, +FRIEND = 290, +INLINE = 291, +INT = 292, +LONG = 293, +NEW = 294, +OPERATOR = 295, +PRIVATE = 296, +PROTECTED = 297, +PUBLIC = 298, +REGISTER = 299, +SHORT = 300, +SIGNED = 301, +STATIC = 302, +STRUCT = 303, +TYPEDEF = 304, +UNION = 305, +UNSIGNED = 306, +VIRTUAL = 307, +VOID = 308, +VOLATILE = 309, +TEMPLATE = 310, +MUTABLE = 311, +BREAK = 312, +CASE = 313, +CONTINUE = 314, +DEFAULT = 315, +DO = 316, +ELSE = 317, +FOR = 318, +GOTO = 319, +IF = 320, +RETURN = 321, +SIZEOF = 322, +SWITCH = 323, +THIS = 324, +WHILE = 325, +ATTRIBUTE = 326, //=g++, +//METACLASS = 327, +UserKeyword = 328, +UserKeyword2 = 329, +UserKeyword3 = 330, +UserKeyword4 = 331, +BOOLEAN = 332, +EXTENSION = 333, //=g++, +TRY = 334, +CATCH = 335, +THROW = 336, +UserKeyword5 = 337, +NAMESPACE = 338, +USING = 339, +TYPEID = 340, +WideStringL = 341, +WideCharConst= 342, +WCHAR = 343, +EXPLICIT = 344, +TYPENAME = 345, +WCHAR_T = 346, +INT8 = 347, +INT16 = 348, +INT32 = 349, +INT64 = 350, +PTR32 = 351, +PTR64 = 352, +COMPLEX = 353, +REAL = 354, +IMAG = 355, + +Ignore = 500, +GCC_ASM = 501, +DECLSPEC = 502, +TYPEOF = 504, +MSC_ASM = 505, +THREAD_LOCAL = 506, +DECLTYPE = 507, +CDECL = 508, +STDCALL = 509, +FASTCALL = 510, +CLRCALL = 511, +STATIC_ASSERT= 512, + +// MSC-specific +MSC_TYPE_PREDICATE = 513, +MSC_BINARY_TYPE_PREDICATE = 514 +}; + diff --git a/src/goto-cc/Makefile b/src/goto-cc/Makefile new file mode 100644 index 00000000000..36c0e18723d --- /dev/null +++ b/src/goto-cc/Makefile @@ -0,0 +1,44 @@ +SRC = goto-cc.cpp \ + cmdline_options.cpp get_base_name.cpp \ + gcc_cmdline.cpp ms_cl_cmdline.cpp \ + compile.cpp armcc_cmdline.cpp \ + languages.cpp goto_cc_cmdline.cpp \ + xml_binaries/xml_irep_hashing.cpp \ + xml_binaries/xml_symbol_hashing.cpp \ + xml_binaries/xml_symbol.cpp \ + xml_binaries/xml_goto_function.cpp \ + xml_binaries/xml_goto_function_hashing.cpp \ + xml_binaries/xml_goto_program.cpp \ + xml_binaries/xml_goto_program_hashing.cpp \ + xml_binaries/read_goto_object.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) \ + ../big-int/bigint$(OBJEXT) \ + ../goto-programs/goto-programs$(LIBEXT) \ + ../pointer-analysis/pointer-analysis$(LIBEXT) \ + ../util/util$(LIBEXT) \ + ../ansi-c/ansi-c$(LIBEXT) \ + ../xmllang/xmllang$(LIBEXT) \ + ../langapi/langapi$(LIBEXT) + +INCLUDES= -I .. -I ../util + +LIBS = + +include ../config.inc +include ../common + +all: goto-cc$(EXEEXT) + +ifdef MODULE_CPP + OBJ += ../cpp/cpp$(LIBEXT) + CXXFLAGS += -D HAVE_CPP +endif + +############################################################################### + +goto-cc$(EXEEXT): $(OBJ) + $(CXX) $(LINKFLAGS) -o $@ $(OBJ) $(LIBS) + +clean: + rm -f $(SRC:.cpp=$(OBJEXT)) $(SRC:.cpp=.d) goto-cc$(EXEEXT) diff --git a/src/goto-cc/armcc_cmdline.cpp b/src/goto-cc/armcc_cmdline.cpp new file mode 100644 index 00000000000..c0a63bb46fb --- /dev/null +++ b/src/goto-cc/armcc_cmdline.cpp @@ -0,0 +1,326 @@ +/*******************************************************************\ + +Module: A special command line object to mimick ARM's armcc + +Author: Daniel Kroening + +\*******************************************************************/ + +#include + +#include + +#include "armcc_cmdline.h" + +/*******************************************************************\ + +Function: armcc_cmdlinet::parse + + Inputs: argument count, argument strings + + Outputs: none + + Purpose: parses the commandline options into a cmdlinet + +\*******************************************************************/ + +// see +// http://infocenter.arm.com/help/topic/com.arm.doc.dui0472c/Cchbggjb.html + +static const char *options_no_arg[]= +{ + // goto-cc-specific + "--dot", + "--xml", + "--show-symbol-table", + "--show-function-table", + "--16", + "--32", + "--64", + "--little-endian", + "--big-endian", + "--unsigned-char", + "--ppc-macos", + "--i386-macos", + "--i386-linux", + "--i386-win32", + "--no-arch", + "--no-library", + "--string-abstraction", + + // armcc + "--help", + "--show_cmdline", + "--version_number", + "--vsn", + "--c90", + "--c99", + "--compile_all_input", + "--no_compile_all_input", + "--cpp", + "--gnu", + "--strict", + "--no_strict", + "--strict_warnings", + "--kandr_include", + "--reduce_paths", + "--no_reduce_paths", + "--sys_include", + "--no-project", + "--reinitialize_workdir", + "--pch", + "--pch_messages", + "--no_pch_messages", + "--pch_verbose", + "--no_pch_verbose", + "-C", + "--code_gen", + "--no_code_gen", + "-E", + "-M", + "--anachronisms", + "--no_anachronisms", + "--dep_name", + "--no_dep_name", + "--export_all_vtbl", + "--no_export_all_vtbl", + "--force_new_nothrow", + "--no_force_new_nothrow", + "--friend_injection", + "--no_friend_injection", + "--guiding_decls", + "--no_guiding_decls", + "--implicit_include", + "--no_implicit_include", + "--implicit_include_searches", + "--no_implicit_include_searches", + "--implicit_typename", + "--no_implicit_typename", + "--nonstd_qualifider_deduction", + "--no_nonstd_qualifider_deduction", + "--old_specializations", + "--no_old_specializations", + "--parse_templates", + "--no_parse_templates", + "--rtti", + "--no_rtti", + "--using_std", + "--no_using_std", + "--vfe", + "--no_vf", + "--asm", + "-c", + "--depend_system_headers", + "--no_depend_system_headers", + "--interleave", + "--list", + "--md", + "-S", + "--split_sections", + "--arm", + "--thumb", + "--debug", + "--no_debug", + "--debug_macros", + "--no_debug_macros", + "--dwarf2", + "--dwarf3", + "-g", + "--remove_unneeded_entities", + "--no_remove_unneeded_entities", + "--alternative_tokens", + "--no_alternative_tokens", + "--bigend", + "--dllexpot_all", + "--no_dllexpot_all", + "--dollar", + "--no_dollar", + "--enum_is_int", + "--exceptions", + "--no_exceptions", + "--exceptions_unwind", + "--no_exceptions_unwind", + "--export_all_vtbl", + "--no_export_all_vtbl", + "--export_defs_implicitly", + "--no_export_defs_implicitly", + "--extend_initializers", + "--no_extend_initializers", + "--hide_all", + "--no_hide_all", + "--littleend", + "--loose_implicit_cast", + "--multibyte_chars", + "--no_multibyte_chars", + "--narrow_volatile_bitfields", + "--restrict", + "--no_restrict", + "--signed_bitfields", + "--unsigned_bitfields", + "--signed_chars", + "--unsigned_chars", + "--split_ldm", + "--unaligned_access", + "--no_unaligned_access", + "--vectorize", + "--no_vectorize", + "--vla", + "--no_vla", + "--wchar16", + "--wchar32", + "--autoinline", + "--no_autoinline", + "--data_reorder", + "--no_data_reorder", + "--forceinline", + "--inline", + "--no_inline", + "--lower_ropi", + "--no_lower_ropi", + "--lower_rwpi", + "--no_lower_rwpi", + "--ltcg", + "--multifile", + "--no_multifile", + "-Ospace", + "-Otime", + "--brief_diagnostics", + "--no_brief_diagnostics", + "--remarks", + "--wrap_diagnostics", + "--no_wrap_diagnostics", + "--arm_linux", + "--arm_linux_configure", + "--arm_linux_paths", + "--shared", + "--translate_g++", + "--translate_gcc", + "--translate_gld", + NULL +}; + +static const char *options_with_prefix[]= +{ + "--project=", + "--workdir=", + "--create_pch=", + "--pch_dir=", + "--use_pch=", + "--pending_instantiations=", + "--errors=", + "--default_extension=", + "--depend=", + "--depend_format=", + "--info=", + "--compatible=", + "--cpu=", + "--fpu=", + "--fp16_format=", + "--fpmode=", + "--fpu=", + "--bss_threshold=", + "--locale=", + "--message_locale=", + "--min_array_alignment=", + "--pointer_alignment=", + "--fpmode=", + "--library_interface=", + "--library_type=", + "--retain=", + "--diag_error=", + "--diag_remark=", + "--diag_style=", + "--diag_suppress=", + "--diag_warning=", + "--preinclude=", + "--via=", + "--feedback=", + "--profile=", + "--apcs=", + "--arm_linux_config_file=", + "--configure_gcc=", + "--configure_gld=", + "--configure_sysroot=", + "--configure_cpp_headers=", + "--configure_extra_includes=", + "--configure_extra_libraries=", + "Warmscc," + "-W", + "-O", + NULL +}; + +static const char *options_with_arg[]= +{ + // goto-cc specific + "--verbosity", + + // armcc-specific + "-D", + "-U", + "-A", + "-L", + "-I", + "-J", + "-o", + NULL +}; + +bool armcc_cmdlinet::parse(int argc, const char **argv) +{ + for(int i=1; iprefix.size()) // concatenated? + options[optnr].values.push_back( + std::string(argv[i], prefix.size(), std::string::npos)); + else + { + // Separated. + if(i!=argc-1) // Guard against end of command line. + { + options[optnr].values.push_back(argv[i+1]); + i++; + } + else + options[optnr].values.push_back(""); + } + } + else if(prefix_in_list(argv[i], options_with_prefix, prefix)) + { + // options that have a concatenated argument + int optnr=get_optnr(prefix); + options[optnr].isset=true; + options[optnr].values.push_back( + std::string(argv[i], prefix.size(), std::string::npos)); + } + else + { // unrecognized option + std::cout << "Warning: uninterpreted armcc option '" + << argv[i] << "'" << std::endl; + } + } + + return false; +} diff --git a/src/goto-cc/armcc_cmdline.h b/src/goto-cc/armcc_cmdline.h new file mode 100644 index 00000000000..7113af96bb8 --- /dev/null +++ b/src/goto-cc/armcc_cmdline.h @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: A special command line object to mimick ARM's armcc + +Author: Daniel Kroening + +Date: June 2006 + +\*******************************************************************/ + +#ifndef GOTO_CC_ARMCC_CMDLINE_H +#define GOTO_CC_ARMCC_CMDLINE_H + +#include "goto_cc_cmdline.h" + +class armcc_cmdlinet:public goto_cc_cmdlinet +{ +public: + virtual bool parse(int, const char**); + + armcc_cmdlinet() + { + mode=ARM; + } +}; + +#endif /* GOTO_CC_ARMCC_CMDLINE_H */ diff --git a/src/goto-cc/cmdline_options.cpp b/src/goto-cc/cmdline_options.cpp new file mode 100644 index 00000000000..23754cbee96 --- /dev/null +++ b/src/goto-cc/cmdline_options.cpp @@ -0,0 +1,577 @@ +/*******************************************************************\ + +Module: Command line option container + +Author: CM Wintersteiger, 2006 + +\*******************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef _WIN32 +#define EX_OK 0 +#define EX_USAGE 64 +#define EX_SOFTWARE 70 +#else +#include +#endif + +#include "cmdline_options.h" +#include "compile.h" +#include "version.h" + +/*******************************************************************\ + +Function: cmdline_optionst::cmdline_optionst + + Inputs: + + Outputs: + + Purpose: constructor + +\*******************************************************************/ + +cmdline_optionst::cmdline_optionst( + goto_cc_cmdlinet &_cmdline): + language_uit("goto-cc " GOTOCC_VERSION, _cmdline), + cmdline(_cmdline) +{ + register_languages(); +} + +/*******************************************************************\ + +Function: tokenize + + Inputs: a string, a vector of tokens and a string of delimiters + + Outputs: nothing + + Purpose: fills the token vector with tokens separated by delimiters + from the string + +\*******************************************************************/ + +void tokenize( + const std::string& str, + std::vector& tokens, + const std::string& delimiters = " ") +{ + std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); + std::string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (std::string::npos != pos || std::string::npos != lastPos) + { + tokens.push_back(str.substr(lastPos, pos - lastPos)); + lastPos = str.find_first_not_of(delimiters, pos); + pos = str.find_first_of(delimiters, lastPos); + } +} + +/*******************************************************************\ + +Function: cmdline_optionst::doit + + Inputs: + + Outputs: + + Purpose: does it. + +\*******************************************************************/ + +static bool is_directory(const std::string &s) +{ + if(s.size()<1) return false; + char last_char=s[s.size()-1]; + // Visual CL recognizes both + return last_char=='\\' || last_char=='/'; +} + +bool cmdline_optionst::doit() +{ + int verbosity=1; + + compilet compiler(cmdline); + + transform(my_name.begin(), my_name.end(), my_name.begin(), tolower); + + if(has_prefix(my_name, "ld") || + has_prefix(my_name, "goto-ld") || + has_prefix(my_name, "link") || + has_prefix(my_name, "goto-link")) + compiler.act_as_ld=true; + + if(cmdline.mode==goto_cc_cmdlinet::GCC) + { + if(cmdline.isset('v')) + { + if(compiler.act_as_ld) + print("GNU ld version 2.16.91 20050610 (goto-cc " GOTOCC_VERSION ")"); + else + print("gcc version 3.4.4 (goto-cc " GOTOCC_VERSION ")"); + return false; + } + + if(cmdline.isset("version")) + { + if(compiler.act_as_ld) + { + print("GNU ld version 2.16.91 20050610 (goto-cc " GOTOCC_VERSION ")"); + } + else + { + print("gcc (GCC) 3.4.4 (goto-cc " GOTOCC_VERSION ")\n"); + } + + print("Copyright (C) 2006-2010 Daniel Kroening, Christoph Wintersteiger\n"); + + return false; + } + + if(cmdline.isset("dumpversion")) + { + std::cout << "3.4.4" << std::endl; + return false; + } + + if(cmdline.isset("Wall")) + verbosity=2; + } + + if(cmdline.isset("verbosity")) + verbosity=atoi(cmdline.getval("verbosity")); + + compiler.set_verbosity(verbosity); + set_verbosity(verbosity); + + switch(cmdline.mode) + { + case goto_cc_cmdlinet::GCC: + debug("GCC mode"); + break; + + case goto_cc_cmdlinet::VISUAL_STUDIO: + debug("Visual Studio mode"); + break; + + case goto_cc_cmdlinet::CODEWARRIOR: + debug("CodeWarrior mode"); + break; + + case goto_cc_cmdlinet::ARM: + debug("ARM mode"); + break; + + default:; + assert(false); + } + + // get configuration + config.set(cmdline); + + if(cmdline.mode==goto_cc_cmdlinet::GCC || + cmdline.mode==goto_cc_cmdlinet::CODEWARRIOR) + { + if(cmdline.mode==goto_cc_cmdlinet::GCC) + config.ansi_c.mode=configt::ansi_ct::MODE_GCC; + else if(cmdline.mode==goto_cc_cmdlinet::CODEWARRIOR) + config.ansi_c.mode=configt::ansi_ct::MODE_CODEWARRIOR; + + compiler.object_file_extension="o"; + + if(cmdline.isset('E')) + compiler.only_preprocess=true; + + if(cmdline.isset('U')) + config.ansi_c.undefines=cmdline.get_values('U'); + + if(cmdline.isset("undef")) + config.ansi_c.preprocessor_options.push_back("-undef"); + + if(cmdline.isset("nostdinc")) + config.ansi_c.preprocessor_options.push_back("-nostdinc"); + + if(cmdline.isset('L')) + compiler.library_paths=cmdline.get_values('L'); + // Don't add the system paths! + + if(cmdline.isset('l')) + compiler.libraries=cmdline.get_values('l'); + + compiler.doLink=!(cmdline.isset('c') || cmdline.isset('S')); + + if(cmdline.isset('o')) + { + // given gcc -o file1 -o file2, + // gcc will output to file2, not file1 + compiler.output_file_object=cmdline.get_values('o').back(); + compiler.output_file_executable=cmdline.get_values('o').back(); + } + else + { + compiler.output_file_object=""; + compiler.output_file_executable="a.out"; + } + + if(cmdline.isset("Wp,")) + { + const std::list &values= + cmdline.get_values("Wp,"); + + for(std::list::const_iterator + it=values.begin(); + it!=values.end(); + it++) + config.ansi_c.preprocessor_options.push_back("-Wp,"+*it); + } + + if(cmdline.isset("isystem")) + { + const std::list &values= + cmdline.get_values("isystem"); + + for(std::list::const_iterator + it=values.begin(); + it!=values.end(); + it++) + config.ansi_c.preprocessor_options.push_back("-isystem "+*it); + } + } + else if(cmdline.mode==goto_cc_cmdlinet::ARM) + { + config.ansi_c.mode=configt::ansi_ct::MODE_ARM; + + if(cmdline.isset("")) + compiler.only_preprocess=true; + + if(cmdline.isset('E')) + compiler.only_preprocess=true; + + if(cmdline.isset('U')) + config.ansi_c.undefines=cmdline.get_values('U'); + + if(cmdline.isset('L')) + compiler.library_paths=cmdline.get_values('L'); + // Don't add the system paths! + + compiler.doLink=!(cmdline.isset('c') || cmdline.isset('S')); + + // these take precedence over -I + if(cmdline.isset('J')) + { + const std::list &values= + cmdline.get_values('J'); + + for(std::list::const_iterator + it=values.begin(); + it!=values.end(); + it++) + config.ansi_c.preprocessor_options.push_back("-J"+*it); + } + + if(cmdline.isset("preinclude=")) + { + const std::list &values= + cmdline.get_values("preinclude="); + + for(std::list::const_iterator + it=values.begin(); + it!=values.end(); + it++) + config.ansi_c.preprocessor_options.push_back("--preinclude="+*it); + } + + // armcc's default is .o + if(cmdline.isset("default_extension=")) + compiler.object_file_extension= + cmdline.getval("default_extension="); + else + compiler.object_file_extension="o"; + + // note that ARM's default is "unsigned_chars", + // in contrast to gcc's default! + if(cmdline.isset("signed_chars")) + config.ansi_c.char_is_unsigned=false; + else + config.ansi_c.char_is_unsigned=true; + + // ARM's default is 16 bits for wchar_t + if(cmdline.isset("wchar32")) + config.ansi_c.wchar_t_width=32; + else + config.ansi_c.wchar_t_width=16; + + if(cmdline.isset('o')) + { + // given goto-armcc -o file1 -o file2, we output to file2, not file1 + compiler.output_file_object=cmdline.get_values('o').back(); + compiler.output_file_executable=cmdline.get_values('o').back(); + } + else + { + compiler.output_file_object=""; + compiler.output_file_executable="a.out"; + } + } + else if(cmdline.mode==goto_cc_cmdlinet::VISUAL_STUDIO) + { + config.ansi_c.mode=configt::ansi_ct::MODE_VISUAL_STUDIO; + compiler.object_file_extension="obj"; + + if(cmdline.isset('E') || cmdline.isset('P')) + compiler.only_preprocess=true; + + compiler.doLink=!( cmdline.isset('E') || cmdline.isset('P') || + cmdline.isset('c') ); + + if(cmdline.isset("Fo")) + { + compiler.output_file_object=cmdline.getval("Fo"); + + // this could be a directory + if(is_directory(compiler.output_file_object)) + { + if(cmdline.args.size()>=1) + compiler.output_file_object+=get_base_name(cmdline.args[0])+".obj"; + } + } + + if(cmdline.isset("Fe")) + { + compiler.output_file_executable=cmdline.getval("Fe"); + + // this could be a directory + if(is_directory(compiler.output_file_executable)) + { + if(cmdline.args.size()>=1) + compiler.output_file_executable+=get_base_name(cmdline.args[0])+".exe"; + } + } + else + { + // We need at least one argument. + // CL uses the first file name it gets! + if(cmdline.args.size()>=1) + compiler.output_file_executable=get_base_name(cmdline.args[0])+".exe"; + } + } + + if(verbosity>8) + { + std::list::iterator it; + + std::cout << "Defines:\n"; + for(it=config.ansi_c.defines.begin(); + it!=config.ansi_c.defines.end(); + it++) + { + std::cout << " " << (*it) << std::endl; + } + + std::cout << "Undefines:\n"; + for(it=config.ansi_c.undefines.begin(); + it!=config.ansi_c.undefines.end(); + it++) + { + std::cout << " " << (*it) << std::endl; + } + + std::cout << "Preprocessor Options:\n"; + for(it=config.ansi_c.preprocessor_options.begin(); + it!=config.ansi_c.preprocessor_options.end(); + it++) + { + std::cout << " " << (*it) << std::endl; + } + + std::cout << "Include Paths:\n"; + for(it=config.ansi_c.include_paths.begin(); + it!=config.ansi_c.include_paths.end(); + it++) + { + std::cout << " " << (*it) << std::endl; + } + + std::cout << "Library Paths:\n"; + for(it=compiler.library_paths.begin(); + it!=compiler.library_paths.end(); + it++) + { + std::cout << " " << (*it) << std::endl; + } + + std::cout << "Output file (object): " << compiler.output_file_object << std::endl; + std::cout << "Output file (executable): " << compiler.output_file_executable << std::endl; + } + + // Parse input program, convert to goto program, write output + return compiler.doit(); +} + +/*******************************************************************\ + +Function: cmdline_optionst::help + + Inputs: + + Outputs: + + Purpose: display command line help + +\*******************************************************************/ + +void cmdline_optionst::help() +{ + std::cout << + "\n" + "* * goto-cc " + GOTOCC_VERSION + " - Copyright (C) 2006-2011 * *\n" + "* * Daniel Kroening, Christoph Wintersteiger * *\n" + "* * kroening@kroening.com * *\n" + "\n"; + + switch(cmdline.mode) + { + case goto_cc_cmdlinet::VISUAL_STUDIO: + std::cout << "goto-cl understands the options of CL plus the following.\n\n"; + break; + + case goto_cc_cmdlinet::GCC: + std::cout << "goto-cc understands the options of gcc plus the following.\n\n"; + break; + + case goto_cc_cmdlinet::CODEWARRIOR: + std::cout << "goto-cw understands the options of gcc (mwcc mode) plus the following.\n\n"; + break; + + case goto_cc_cmdlinet::ARM: + std::cout << "goto-armcc understands the options of armcc plus the following.\n\n"; + break; + + default: + assert(false); + } + + std::cout << + "Usage: Purpose:\n" + "\n" + " --dot outputs a dot graph for every output file\n" + " --verbosity # verbosity level\n" + " --xml use the old XML binary format\n" + " --show-symbol-table outputs the symbol table after linking\n" + " --show-function-table outputs the function table after linking\n" + "\nArchitecture options:\n" + " --16, --32, --64 set width of machine word\n" + " --little-endian allow little-endian word-byte conversions\n" + " --big-endian allow big-endian word-byte conversions\n" + " --unsigned-char make \"char\" unsigned by default\n" + " --ppc-macos set MACOS/PPC architecture\n" + #ifdef _WIN32 + " --i386-macos set MACOS/I386 architecture\n" + " --i386-linux set Linux/I386 architecture\n" + " --i386-win32 set Windows/I386 architecture (default)\n" + #else + #ifdef __APPLE__ + " --i386-macos set MACOS/I386 architecture (default)\n" + " --i386-linux set Linux/I386 architecture\n" + " --i386-win32 set Windows/I386 architecture\n" + #else + " --i386-macos set MACOS/I386 architecture\n" + " --i386-linux set Linux/I386 architecture (default)\n" + " --i386-win32 set Windows/I386 architecture\n" + #endif + #endif + " --no-arch don't set up an architecture\n" + "\nLinker options:\n" + " --no-library do not add definitions for library functions\n" + " --string-abstraction abstract strings in library functions\n" + "\n"; +} + +/*******************************************************************\ + +Function: cmdline_optionst::main + + Inputs: none + + Outputs: true on error, false otherwise + + Purpose: starts the compiler + +\*******************************************************************/ + +int cmdline_optionst::main(int argc, const char **argv) +{ + if(cmdline.parse(argc, argv)) + { + usage_error(); + return EX_USAGE; + } + + if(cmdline.isset('?') || cmdline.isset('h') || cmdline.isset("help")) + { + help(); + return EX_OK; + } + + try + { + if(doit()) + return EX_USAGE; + else + return EX_OK; + } + + catch(const char *e) + { + error(e); + return EX_SOFTWARE; + } + + catch(const std::string e) + { + error(e); + return EX_SOFTWARE; + } + + catch(int e) + { + error("Exception: " + i2string(e)); + return EX_SOFTWARE; + } + + catch(std::bad_alloc) + { + error("Out of memory"); + return EX_SOFTWARE; + } +} + +/*******************************************************************\ + +Function: cmdline_optionst::usage_error + + Inputs: none + + Outputs: none + + Purpose: prints a message informing the user about incorrect options + +\*******************************************************************/ + +void cmdline_optionst::usage_error() +{ + std::cerr << "Usage error!\n\n"; + help(); +} diff --git a/src/goto-cc/cmdline_options.h b/src/goto-cc/cmdline_options.h new file mode 100644 index 00000000000..949bceeb18b --- /dev/null +++ b/src/goto-cc/cmdline_options.h @@ -0,0 +1,36 @@ +/*******************************************************************\ + +Module: Command line interpretation for goto-cc. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifndef GOTO_CC_CMDLINE_OPTIONS_H +#define GOTO_CC_CMDLINE_OPTIONS_H + +#include +#include + +#include "goto_cc_cmdline.h" + +class cmdline_optionst:public language_uit +{ +public: + goto_cc_cmdlinet &cmdline; + std::string my_name; + + virtual int main(int argc, const char **argv); + virtual bool doit(); + virtual void help(); + virtual void usage_error(); + + cmdline_optionst(goto_cc_cmdlinet &_cmdline); + +private: + void register_languages(); +}; + +#endif /*CMDLINE_H_*/ diff --git a/src/goto-cc/compile.cpp b/src/goto-cc/compile.cpp new file mode 100644 index 00000000000..e20ac8695a6 --- /dev/null +++ b/src/goto-cc/compile.cpp @@ -0,0 +1,1716 @@ +/*******************************************************************\ + +Module: Compile and link source and object files. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifdef __linux__ +#include +#include +#include +#endif + +#ifdef __MACH__ +#include +#include +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "xml_binaries/xml_irep_hashing.h" +#include "xml_binaries/xml_symbol_hashing.h" +#include "xml_binaries/xml_goto_function_hashing.h" +#include "xml_binaries/read_goto_object.h" + +#include "get_base_name.h" +#include "compile.h" +#include "version.h" + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#define chdir _chdir +#define popen _popen +#define pclose _pclose +#endif + +#define DOTGRAPHSETTINGS "color=black;" \ + "orientation=portrait;" \ + "fontsize=20;"\ + "compound=true;"\ + "size=\"30,40\";"\ + "ratio=compress;" + +unsigned compilet::subgraphscount; + +std::string to_string(unsigned long l) { + std::string res = ""; + char x[20]; + sprintf(x, "%ld", l); + res = x; + return res; +} + +std::string to_string(long l) { + std::string res = ""; + char x[20]; + sprintf(x, "%ld", l); + res = x; + return res; +} + +/*******************************************************************\ + +Function: compilet::doit + + Inputs: none + + Outputs: true on error, false otherwise + + Purpose: reads and source and object files, compiles and links them + into goto program objects. + +\*******************************************************************/ + +bool compilet::doit() +{ + std::string error_label; + + options.set_option("bounds-check", !cmdline.isset("no-bounds-check")); + options.set_option("div-by-zero-check", !cmdline.isset("no-div-by-zero-check")); + options.set_option("pointer-check", !cmdline.isset("no-pointer-check")); + options.set_option("assertions", !cmdline.isset("no-assertions")); + options.set_option("assumptions", !cmdline.isset("no-assumptions")); + options.set_option("simplify", !cmdline.isset("no-simplify")); + options.set_option("overflow-check", cmdline.isset("overflow-check")); + + // we do want the assertions + optionst options; + options.set_option("assertions", true); + options.set_option("assumptions", true); + options.set_option("error-label", error_label); + + compiled_functions.clear(); + + add_compiler_specific_defines(config); + + // Parse commandline for source and object filenames + for(unsigned i=0; i<_cmdline.args.size(); i++) + if(add_input_file(_cmdline.args[i])) + return true; + + for(std::list::const_iterator it = libraries.begin(); + it!=libraries.end(); + it++) + { + if(!find_library(*it)) + warning(std::string("Library not found: ") + *it + " (ignoring)"); + } + + // Work through the given source files + print(8, "No. of source files: " + + to_string((unsigned long) source_files.size())); + + print(8, "No. of object files: " + + to_string((unsigned long) object_files.size())); + + if(source_files.size()==0 && object_files.size()==0) + { + error("no input files"); + return true; + } + + if(act_as_ld && source_files.size()>0) + { + error("ld cannot link source files"); + return true; + } + + if(only_preprocess && object_files.size()>0) + { + error("cannot preprocess object files"); + return true; + } + + if(source_files.size()>0) + if(compile()) return true; + + if(only_preprocess) return false; + + if(doLink) + { + if(link()) return true; + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::add_input_file + + Inputs: none + + Outputs: false on success, true on error. + + Purpose: puts input filenames into a list and does preprocessing for + libraries. + +\*******************************************************************/ + +bool compilet::add_input_file(const std::string &filename) +{ + size_t r=filename.rfind('.', filename.length()-1); + + if (r!=std::string::npos) + { + std::string ext = filename.substr(r+1, filename.length()); + + if(ext=="a") + { + #ifdef _WIN32 + char td[MAX_PATH]; + #else + char td[] = "goto-cc.XXXXXX"; + #endif + + std::string tstr = get_temporary_directory(td); + + if(tstr=="") + { + error("Cannot create temporary directory."); + return true; + } + + tmp_dirs.push_back(tstr); + std::stringstream cmd(""); + if(chdir(tmp_dirs.back().c_str())!=0) + { + error("Cannot switch to temporary directory."); + return true; + } + + #ifdef _WIN32 + if (filename[0]!='/' && filename[1]!=':') + #else + if (filename[0]!='/') + #endif + { + cmd << "ar t " << + #ifdef _WIN32 + working_directory << "\\" << filename; + #else + working_directory << "/" << filename; + #endif + } + else + { + cmd << "ar t " << filename; + } + + FILE *stream = popen(cmd.str().c_str(), "r"); + if (stream!=NULL) + { + std::string line; + char ch; + while((ch=fgetc(stream))!=EOF) + { + if(ch!='\n' && ch!=EOF) + { + line += ch; + } + else + { + std::string t; + #ifdef _WIN32 + t = tmp_dirs.back() + '\\' + line; + #else + t = tmp_dirs.back() + '/' + line; + #endif + object_files.push_back(t); + line = ""; + } + } + } + pclose(stream); + cmd.str(""); + #ifdef _WIN32 + if(filename[0]!='/' && filename[1]!=':') + #else + if(filename[0]!='/') + #endif + { + cmd << "ar x " << + #ifdef _WIN32 + working_directory << "\\" << filename; + #else + working_directory << "/" << filename; + #endif + } + else + { + cmd << "ar x " << filename; + } + + stream = popen(cmd.str().c_str(), "r"); + pclose(stream); + + if(chdir(working_directory.c_str())!=0) + error("Could not change back to working directory."); + } + else if(ext=="so") + { + if(cmdline.isset("xml")) + { + if(is_xml_file(filename)) + object_files.push_back(filename); + } + else + { + if(is_binary_file(filename)) + object_files.push_back(filename); + } + } + else if(ext==object_file_extension || + ext=="la" || ext=="lo") // Object file recognized + object_files.push_back(filename); + else // assume source file + source_files.push_back(filename); + } + else + { + // don't care about no extensions + source_files.push_back(filename); + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::find_library + + Inputs: library name + + Outputs: true if found, false otherwise + + Purpose: tries to find a library object file that matches the given + library name. + +\*******************************************************************/ + +bool compilet::find_library(const std::string &name) +{ + std::string tmp; + for(std::list::const_iterator + it=library_paths.begin(); + it!=library_paths.end(); + it++) + { + #ifdef _WIN32 + tmp = *it + "\\lib"; + #else + tmp = *it + "/lib"; + #endif + std::ifstream in((tmp+name+".a").c_str()); + if (in.is_open()) + add_input_file(tmp+name+".a"); + else + { + std::string libname = tmp+name+".so"; + if (is_elf_file(libname)) + std::cout << "Warning: Cannot read ELF library " << libname << "." + << std::endl; + + if (cmdline.isset("xml")) + { + if (is_xml_file(libname)) + add_input_file(libname); + else + return false; + } + else + { + if (is_binary_file(libname)) + add_input_file(libname); + else + return false; + } + } + } + + return true; +} + +/*******************************************************************\ + +Function: compilet::is_xml_file + + Inputs: file name + + Outputs: true if the given file name exists and is an xml file, + false otherwise + + Purpose: checking if we can load an object file + +\*******************************************************************/ + +bool compilet::is_xml_file(const std::string &filename) +{ + std::fstream in; + in.open(filename.c_str(), std::ios::in); + + if(in.is_open()) + { + char buf[5]; + for (unsigned i=0; i<5; i++) + buf[i] = in.get(); + if (buf[0]=='<' && buf[1]=='?' && + buf[2]=='x' && buf[3]=='m' && buf[4]=='l') + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::is_binary_file + + Inputs: file name + + Outputs: true if the given file name exists and is a (goto-)binary file, + false otherwise + + Purpose: checking if we can load an object file + +\*******************************************************************/ + +bool compilet::is_binary_file(const std::string &filename) +{ + std::fstream in; + in.open(filename.c_str(), std::ios::in); + + if(in.is_open()) + { + char buf[3]; + for (unsigned i=0; i<3; i++) + buf[i] = in.get(); + if (buf[0]=='G' && buf[1]=='B' && buf[2]=='F') + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::is_elf_file + + Inputs: file name + + Outputs: true if the given file name exists and is an ELF file, + false otherwise + + Purpose: checking if we can load an object file + +\*******************************************************************/ + +bool compilet::is_elf_file(const std::string &filename) +{ + std::fstream in; + + in.open(filename.c_str(), std::ios::in); + if(in.is_open()) + { + char buf[4]; + for (unsigned i=0; i<4; i++) + buf[i] = in.get(); + if (buf[0]==0x7f && buf[1]=='E' && + buf[2]=='L' && buf[3]=='F') + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::link + + Inputs: none + + Outputs: true on error, false otherwise + + Purpose: parses object files and links them + +\*******************************************************************/ + +bool compilet::link() +{ + // "compile" hitherto uncompiled functions + print(8, "Compiling functions"); + convert_symbols(compiled_functions); + + if(cmdline.isset("partial-inlining")) + { + // inline those functions marked as "inlined" + // we no longer do partial inlining by default -- can just as + // well be done in the backend + print(8, "Partial inlining"); + goto_partial_inline( + compiled_functions, + ns, + get_message_handler()); + } + + // parse object files + while(object_files.size()>0) + { + std::string filename=object_files.front(); + object_files.pop_front(); + + if(parse_object(filename, compiled_functions)) + return true; + } + + if(cmdline.isset("show-symbol-table")) + { + show_symbol_table(); + return true; + } + + if(cmdline.isset("show-function-table")) + { + show_function_table(); + return true; + } + + // finalize + contextt::symbolst::iterator it_main= + context.symbols.find("c::main"); + + if((!act_as_ld || it_main!=context.symbols.end()) && + !cmdline.isset("static") && !cmdline.isset("shared")) + { + print(8, "Finalizing"); + + forall_symbols(it, context.symbols) + { + const symbolt &symbol = it->second; + if (symbol.mode!="" && + find(seen_modes.begin(), seen_modes.end(), symbol.mode) + == seen_modes.end()) + { + seen_modes.push_back(symbol.mode); + } + } + + if(seen_modes.empty()) + { + if(!source_files.empty()) + { + error("no modes found!"); + return true; + } + } + else + { + // Hackfix for C++ + std::list::iterator cpp = + find(seen_modes.begin(), seen_modes.end(), "cpp"); + + std::list::iterator c = + find(seen_modes.begin(), seen_modes.end(), "C"); + + if (c!=seen_modes.end() && cpp!=seen_modes.end()) + seen_modes.erase(c); + + std::list::iterator it = seen_modes.begin(); + for (; it!=seen_modes.end(); it++) + { + languaget *language=get_language_from_mode(*it); + if (language) + { + if (language->final(context, ui_message_handler)) + return true; + } + else + { + error("unknown language mode '" +id2string(*it)+ "'"); + return true; + } + } + } + + // check for main + contextt::symbolst::iterator i= + context.symbols.find("main"); + + if(i==context.symbols.end()) + { + error("'main' symbol not found"); + return true; + } + + // final may add some more functions. + convert_symbols(compiled_functions); + } + + if(write_object_file(output_file_executable, context, compiled_functions)) + return true; + + return false; +} + +/*******************************************************************\ + +Function: compilet::compile + + Inputs: none + + Outputs: true on error, false otherwise + + Purpose: parses source files and writes object files, or keeps the + symbols in the context depending on the doLink flag. + +\*******************************************************************/ + +bool compilet::compile() +{ + while(!source_files.empty()) + { + std::string filename=source_files.front(); + source_files.pop_front(); + + bool r=parse_source(filename); // don't break the program! + + if(!r && !doLink && !only_preprocess) + { + // output an object file for every source file + + // "compile" functions + convert_symbols(compiled_functions); + + if(cmdline.isset("show-symbol-table")) + { + show_symbol_table(); + return true; + } + + if(cmdline.isset("show-function-table")) + { + show_function_table(); + return true; + } + + std::string cfn; + + if(output_file_object=="") + { + if(cmdline.isset('S')) // compile, but don't assemble + cfn=get_base_name(filename) + ".s"; + else + cfn=get_base_name(filename) + "." + object_file_extension; + } + else + { + cfn=output_file_object; + } + + if(write_object_file(cfn, context, compiled_functions)) + return true; + + context.clear(); // clean symbol table for next source file. + compiled_functions.clear(); + } + + if(r) return true; // parser/typecheck error + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::parse + + Inputs: filename + + Outputs: true on error, false otherwise + + Purpose: parses a source file (low-level parsing) + +\*******************************************************************/ + +bool compilet::parse(const std::string &filename) +{ + if(filename=="-") return parse_stdin(); + + std::ifstream infile(filename.c_str()); + + if(!infile) + { + error("failed to open input file", filename); + return true; + } + + languaget *languagep=get_language_from_filename(filename); + + if(languagep==NULL) + { + error("failed to figure out type of file", filename); + return true; + } + + languaget &language=*languagep; + language_filet language_file; + + std::pair + res=language_files.filemap.insert( + std::pair(filename, language_file)); + + language_filet &lf=res.first->second; + lf.filename=filename; + lf.language=languagep; + + print(8, "Parsing: "+filename); + + if(only_preprocess) + { + std::ostream *os = &std::cout; + std::ofstream ofs; + + if(cmdline.isset('o')) + { + ofs.open(cmdline.getval('o')); + os = &ofs; + + if(!ofs.is_open()) + { + error(std::string("failed to open output file `")+ + cmdline.getval('o')+"'"); + return true; + } + } + + language.preprocess(infile, filename, *os, get_message_handler()); + } + else + { + if(language.parse(infile, filename, get_message_handler())) + { + if(get_ui()==ui_message_handlert::PLAIN) + error("PARSING ERROR"); + return true; + } + } + + lf.get_modules(); + return false; +} + +/*******************************************************************\ + +Function: compilet::parse_stdin + + Inputs: filename + + Outputs: true on error, false otherwise + + Purpose: parses a source file (low-level parsing) + +\*******************************************************************/ + +bool compilet::parse_stdin() +{ + ansi_c_languaget language; + + print(8, "Parsing: (stdin)"); + + if(only_preprocess) + { + std::ostream *os = &std::cout; + std::ofstream ofs; + + if(cmdline.isset('o')) + { + ofs.open(cmdline.getval('o')); + os = &ofs; + + if(!ofs.is_open()) + { + error(std::string("failed to open output file `")+ + cmdline.getval('o')); + return true; + } + } + + language.preprocess(std::cin, "", *os, get_message_handler()); + } + else + { + if(language.parse(std::cin, "", get_message_handler())) + { + if(get_ui()==ui_message_handlert::PLAIN) + error("PARSING ERROR"); + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::write_object_file + + Inputs: filename, functions table + + Outputs: true on error, false otherwise + + Purpose: writes the goto functions in the function table to a + binary format object file. + +\*******************************************************************/ + +bool compilet::write_object_file( + const std::string &filename, + const contextt &lcontext, + goto_functionst &functions) +{ + if(cmdline.isset("xml")) + return write_xml_object_file(filename, lcontext, functions); + else + return write_bin_object_file(filename, lcontext, functions); +} + +/*******************************************************************\ + +Function: compilet::write_bin_object_file + + Inputs: filename, functions table + + Outputs: true on error, false otherwise + + Purpose: writes the goto functions in the function table to a + binary format object file. + +\*******************************************************************/ + +bool compilet::write_bin_object_file( + const std::string &filename, + const contextt &lcontext, + goto_functionst &functions) +{ + print(8, "Writing binary format object " + filename); + + // symbols + print(8, "Symbols in table: " + + to_string((unsigned long) lcontext.symbols.size())); + + std::ofstream f(filename.c_str(), std::ios::binary); + if (!f.is_open()) + { + error("Error opening file " + filename); + return true; + } + + if(write_goto_binary(f, lcontext, functions)) + return true; + + unsigned cnt = function_body_count(functions); + if (verbosity>=9) + { + std::cout << "Functions: " << functions.function_map.size() << "; "; + std::cout << cnt << " have a body." << std::endl; + } + + f.close(); + + if(cmdline.isset("dot")) + { + std::ofstream dgf; + write_dot_header(filename, dgf); + + for (goto_functionst::function_mapt::iterator it= + functions.function_map.begin(); + it!=functions.function_map.end(); + it++) + { + if (it->second.body_available) + write_dot_subgraph(dgf, id2string(it->first), it->second.body); + } + + do_dot_function_calls(dgf); + dgf << "}" << std::endl; + dgf.close(); + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::write_xml_object_file + + Inputs: filename, functions table + + Outputs: true on error, false otherwise + + Purpose: writes the goto functions in the function table to an xml + object file + +\*******************************************************************/ + +bool compilet::write_xml_object_file( + const std::string &filename, + const contextt &lcontext, + goto_functionst &functions) +{ + print(8, "Writing xml format object " + filename); + + std::ofstream f(filename.c_str()); + if(!f.is_open()) + { + error("Error opening file " + filename); + return true; + } + + f << "" << std::endl; + f << "" << std::endl; + f << " " << std::endl; + + xml_irep_convertt::ireps_containert irepc; + xml_irep_convertt irepconverter(irepc); + xml_symbol_convertt symbolconverter(irepc); + xml_goto_function_convertt gfconverter(irepc); + + xmlt syms("symbols"); + print(8, "Symbols in table: " + to_string((unsigned long) lcontext.symbols.size())); + forall_symbols(it, lcontext.symbols) + { + const symbolt &sym = it->second; + symbolconverter.convert(sym, syms); + } + + xmlt funs("functions"); + if (verbosity>=9) + { + std::cout << "Functions: " << functions.function_map.size() << "; "; + std::cout << function_body_count(functions) << " have a body." << std::endl; + } + + std::ofstream dgf; + if (cmdline.isset("dot")) + write_dot_header(filename, dgf); + for ( goto_functionst::function_mapt::iterator it=functions.function_map.begin(); + it != functions.function_map.end(); + it++) + { + if (it->second.body_available) + { + xmlt &fun = funs.new_element("function"); + fun.set_attribute("name", id2string(it->first)); + gfconverter.convert(it->second, fun); + if (dgf.is_open()) + write_dot_subgraph(dgf, id2string(it->first), it->second.body); + } + } + + if (dgf.is_open()) + { + do_dot_function_calls(dgf); + dgf << "}" << std::endl; + dgf.close(); + } + + irepconverter.output_map(f, 2); + f << " " << std::endl; + syms.output(f, 1); + funs.output(f, 1); + f << ""; + f.close(); + return false; +} + +/*******************************************************************\ + +Function: compilet::write_dot_subgraph + + Inputs: output stream, name and goto program + + Outputs: true on error, false otherwise + + Purpose: writes the dot graph that corresponds to the goto program + to the output stream. + +\*******************************************************************/ + +void compilet::write_dot_subgraph( + std::ostream &out, + const std::string &name, + goto_programt &goto_program) +{ + clusters.push_back(exprt("cluster")); + clusters.back().set("name", name); + clusters.back().set("nr", subgraphscount); + + out << "subgraph \"cluster_" << name << "\" {" << std::endl; + out << "label=\"" << name << "\";" << std::endl; + + const goto_programt::instructionst& instructions = + goto_program.instructions; + + if (instructions.size()==0) + { + out << "Node_" << subgraphscount << "_0 " << + "[shape=Mrecord,fontsize=22,label=\"?\"];" << std::endl; + } + else + { + std::set seen; + goto_programt::const_targetst worklist; + worklist.push_back(instructions.begin()); + + while (!worklist.empty()) + { + goto_programt::const_targett it=worklist.front(); + worklist.pop_front(); + + if(it==instructions.end() || + seen.find(it)!=seen.end()) continue; + + std::stringstream tmp(""); + if(it->is_goto()) + { + if (it->guard.is_true()) + tmp.str("Goto"); + else + { + std::string t = from_expr(ns, "", it->guard); + while (t[ t.size()-1 ]=='\n') + t = t.substr(0,t.size()-1); + tmp << escape(t) << "?"; + } + } + else if (it->is_assume()) + { + std::string t = from_expr(ns, "", it->guard); + while (t[ t.size()-1 ]=='\n') + t = t.substr(0,t.size()-1); + tmp << "Assume\\n(" << escape(t) << ")"; + } + else if (it->is_assert()) + { + std::string t = from_expr(ns, "", it->guard); + while (t[ t.size()-1 ]=='\n') + t = t.substr(0,t.size()-1); + tmp << "Assert\\n(" << escape(t) << ")"; + } + else if (it->is_skip()) + tmp.str("Skip"); + else if (it->is_end_function()) + tmp.str("End of Function"); + else if (it->is_location()) + tmp.str("Location"); + else if (it->is_dead()) + tmp.str("Dead"); + else if (it->is_atomic_begin()) + tmp.str("Atomic Begin"); + else if (it->is_atomic_end()) + tmp.str("Atomic End"); + else if (it->is_function_call()) + { + std::string t = from_expr(ns, "", it->code); + while (t[ t.size()-1 ]=='\n') + t = t.substr(0,t.size()-1); + tmp.str(escape(t)); + + exprt fc; + std::stringstream ss; + ss << "Node_" << subgraphscount << "_" << it->location_number; + fc.operands().push_back(exprt(ss.str())); + fc.operands().push_back(it->code.op1()); + function_calls.push_back(fc); + } + else if (it->is_assign() || + it->is_return() || + it->is_other()) + { + std::string t = from_expr(ns, "", it->code); + while (t[ t.size()-1 ]=='\n') + t = t.substr(0,t.size()-1); + tmp.str(escape(t)); + } + else if (it->is_start_thread()) + tmp.str("Start of Thread"); + else if (it->is_end_thread()) + tmp.str("End of Thread"); + else if (it->is_throw()) + tmp.str("THROW"); + else if (it->is_catch()) + tmp.str("CATCH"); + else + tmp.str("UNKNOWN"); + + out << "Node_" << subgraphscount << "_" << it->location_number; + out << " [shape="; + if (it->is_goto() && !it->guard.is_true() && !it->guard.is_false()) + out << "diamond"; + else + out <<"Mrecord"; + out << ",fontsize=22,label=\""; + out << tmp.str(); + out << "\"];" << std::endl; + + std::set tres; + std::set fres; + find_next(instructions, it, tres, fres); + + std::string tlabel="true"; + std::string flabel="false"; + if (fres.size()==0 || tres.size()==0) + { + tlabel=""; + flabel=""; + } + + typedef std::set t; + + for (t::iterator trit=tres.begin(); + trit!=tres.end(); + trit++) + write_edge(out, *it, **trit, tlabel); + for (t::iterator frit=fres.begin(); + frit!=fres.end(); + frit++) + write_edge(out, *it, **frit, flabel); + + seen.insert(it); + goto_programt::const_targetst temp; + goto_program.get_successors(it, temp); + worklist.insert(worklist.end(), temp.begin(), temp.end()); + } + } + + out << "}" << std::endl; + subgraphscount++; +} + +/*******************************************************************\ + +Function: compilet::do_dot_function_calls + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void compilet::do_dot_function_calls(std::ostream &out) +{ + for (std::list::const_iterator it=function_calls.begin(); + it!=function_calls.end(); + it++) + { + std::list::const_iterator cit=clusters.begin(); + for (;cit!=clusters.end();cit++) + if (cit->get("name")==it->op1().get(ID_identifier)) + break; + + if (cit!=clusters.end()) + { + out << it->op0().id() << + " -> " "Node_" << cit->get("nr") << "_0" << + " [lhead=\"cluster_" << it->op1().get(ID_identifier) << "\"," << + "color=blue];" << std::endl; + } + else + { + out << "subgraph \"cluster_" << it->op1().get(ID_identifier) << + "\" {" << std::endl; + out << "rank=sink;"<op1().get(ID_identifier) << "\";" << std::endl; + out << "Node_" << subgraphscount << "_0 " << + "[shape=Mrecord,fontsize=22,label=\"?\"];" + << std::endl; + out << "}" << std::endl; + clusters.push_back(exprt("cluster")); + clusters.back().set("name", it->op1().get(ID_identifier)); + clusters.back().set("nr", subgraphscount); + out << it->op0().id() << + " -> " "Node_" << subgraphscount << "_0" << + " [lhead=\"cluster_" << it->op1().get("identifier") << "\"," << + "color=blue];" << std::endl; + subgraphscount++; + } + } +} + +/*******************************************************************\ + +Function: compilet::find_next + + Inputs: instructions, instruction iterator, true results and + false results + + Outputs: none + + Purpose: finds an instructions successors (for goto graphs) + +\*******************************************************************/ + +void compilet::find_next( + const goto_programt::instructionst &instructions, + const goto_programt::const_targett &it, + std::set &tres, + std::set &fres) +{ + if (it->is_goto() && !it->guard.is_false()) + { + goto_programt::targetst::const_iterator gtit = it->targets.begin(); + for (; gtit!=it->targets.end(); gtit++) + tres.insert((*gtit)); + } + + if (it->is_goto() && it->guard.is_true()) + return; + + goto_programt::const_targett next = it; next++; + if (next!=instructions.end()) + fres.insert(next); +} + +/*******************************************************************\ + +Function: compilet::write_edge + + Inputs: output stream, from, to and a label + + Outputs: none + + Purpose: writes an edge from the from node to the to node and with + the given label to the output stream (dot format) + +\*******************************************************************/ + +void compilet::write_edge( + std::ostream &out, + const goto_programt::instructiont &from, + const goto_programt::instructiont &to, + const std::string &label) +{ + out << "Node_" << subgraphscount << "_" << from.location_number; + out << " -> "; + out << "Node_" << subgraphscount << "_" << to.location_number << " "; + if (label!="") + { + out << "[fontsize=20,label=\"" << label << "\""; + if (from.is_backwards_goto() && + from.location_number > to.location_number) + out << ",color=red"; + out << "]"; + } + out << ";" << std::endl; +} + +/*******************************************************************\ + +Function: compilet::escape + + Inputs: a string + + Outputs: the escaped string + + Purpose: escapes a string. beware, this might not work for all + kinds of strings. + +\*******************************************************************/ + +std::string &compilet::escape(std::string &str) +{ + char last = '\0'; + + for(unsigned i=0; i' || + str[i]=='<' || + str[i]=='{' || + str[i]=='}') + { + str.insert(i, "\\"); + i++; + } + last = str[i]; + } + + return str; +} + +/*******************************************************************\ + +Function: compilet::parse_source + + Inputs: filename + + Outputs: true on error, false otherwise + + Purpose: parses a source file + +\*******************************************************************/ + +bool compilet::parse_source(const std::string &filename) +{ + if(parse(filename)) + return true; + + if(typecheck()) // we just want to typecheck this one file here + return true; + + // so we remove it from the list afterwards + language_files.filemap.erase(filename); + return false; +} + +/*******************************************************************\ + +Function: compilet::parse_object + + Inputs: a filename + + Outputs: true on error, false otherwise + + Purpose: parses an object file + +\*******************************************************************/ + +bool compilet::parse_object( + const std::string &filename, + goto_functionst &functions) +{ + std::ifstream infile; + if(cmdline.isset("xml")) + infile.open(filename.c_str()); + else + infile.open(filename.c_str(), std::ios::binary); + + if(!infile) + { + error("failed to open input file", filename); + return true; + } + + print(8, "Parsing: " + filename); + + // we parse to a temporary context + contextt temp_context; + goto_functionst temp_functions; + std::list seen_modes; + + if(cmdline.isset("xml")) + { + if(read_goto_object(infile, filename, temp_context, + temp_functions, *message_handler)) + return true; + } + else + { + if(read_bin_goto_object(infile, filename, temp_context, + temp_functions, *message_handler)) + return true; + } + + for(contextt::symbolst::const_iterator + it=temp_context.symbols.begin(); + it!=temp_context.symbols.end(); + it++) + { + if ( it->second.mode!="" && + find( seen_modes.begin(), + seen_modes.end(), + it->second.mode) + == seen_modes.end()) + { + seen_modes.push_back(it->second.mode); + } + } + + std::list::const_iterator cpp = + find(seen_modes.begin(), seen_modes.end(), "cpp"); + std::list::iterator c = + find(seen_modes.begin(), seen_modes.end(), "C"); + + if(c!=seen_modes.end() && cpp!=seen_modes.end()) + { + seen_modes.erase(c); + } + + if(seen_modes.size()!=1) + { + std::cerr << "Multilanguage linking not supported." << std::endl; + return true; + } + + // hardwired to C linking + + c_linkt c_link(context, temp_context, ui_message_handler); + + if(c_link.typecheck_main()) + return true; + + if(link_functions(context, functions, + temp_context, temp_functions, + c_link.replace_symbol)) + return true; + + return false; +} + +/*******************************************************************\ + +Function: compilet::show_function_table + + Inputs: none + + Outputs: nothing + + Purpose: prints the function table to stdout + +\*******************************************************************/ + +void compilet::show_function_table() +{ + for(goto_functionst::function_mapt::const_iterator + i=compiled_functions.function_map.begin(); + i!=compiled_functions.function_map.end(); + i++) + { + std::cout << i->first << std::endl; + } +} + +/*******************************************************************\ + +Function: compilet::compilet + + Inputs: none + + Outputs: nothing + + Purpose: constructor + +\*******************************************************************/ + +compilet::compilet(cmdlinet &_cmdline): + language_uit("goto-cc " GOTOCC_VERSION, _cmdline), + ns(context), + cmdline(_cmdline) +{ + doLink = false; + act_as_ld = false; + only_preprocess = false; + subgraphscount = 0; + + unsigned bsize=50; + char *buf = (char*) malloc(sizeof(char)*bsize); + errno=0; + while(buf && getcwd(buf, bsize-1)==NULL && errno==ERANGE) + { + bsize*=2; + buf = (char*) realloc(buf, sizeof(char)*bsize); + } + working_directory = buf; + if(buf) free(buf); +} + +/*******************************************************************\ + +Function: compilet::~compilet + + Inputs: none + + Outputs: nothing + + Purpose: cleans up temporary files + +\*******************************************************************/ + +compilet::~compilet() +{ + // clean up temp dirs + + for (std::list::const_iterator it = tmp_dirs.begin(); + it!=tmp_dirs.end(); + it++) + { + DIR *dir=opendir(it->c_str()); + + if(dir!=NULL) + { + struct dirent *ent; + + while((ent=readdir(dir))!=NULL) + remove((*it + "/" + ent->d_name).c_str()); + + closedir(dir); + } + + rmdir(it->c_str()); + } +} + +/*******************************************************************\ + +Function: compilet::write_dot_file + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool compilet::write_dot_header( + const std::string &filename, + std::ofstream &dgf) +{ + std::string dgfilename = filename + ".dot"; + if (verbosity>=9) + status("Writing dot graph to " + dgfilename); + dgf.open(dgfilename.c_str()); + if (!dgf.is_open()) + { + error("Error opening file: " + dgfilename); + return true; + } + dgf << "digraph G {" << std::endl; + dgf << DOTGRAPHSETTINGS << std::endl; + return false; +} + +/*******************************************************************\ + +Function: compilet::function_body_count + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned compilet::function_body_count(const goto_functionst &functions) +{ + int fbs = 0; + for ( goto_functionst::function_mapt::const_iterator it= + functions.function_map.begin(); + it != functions.function_map.end(); + it++) + if (it->second.body_available) + fbs++; + + return fbs; +} + +/*******************************************************************\ + +Function: compilet::link_functions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool compilet::link_functions( + contextt &context, + goto_functionst &functions, + contextt &temp_context, + goto_functionst &temp_functions, + replace_symbolt &replace_symbol) +{ + // merge functions + Forall_goto_functions(it, temp_functions) + { + goto_functionst::function_mapt::iterator fit= + functions.function_map.find(it->first); + + if(fit==functions.function_map.end()) // fine + { + replace_symbols_in_function(it, replace_symbol); + goto_functionst::goto_functiont &in_context= + functions.function_map[it->first]; + + in_context.body.swap(it->second.body); + in_context.body_available=it->second.body_available; + in_context.type=it->second.type; + } + else // collision! + { + goto_functionst::goto_functiont &in_context= + functions.function_map[it->first]; + goto_functionst::goto_functiont &new_func=it->second; + + if(in_context.body.instructions.size()==0) + { + // the one with body wins! + replace_symbols_in_function(it, replace_symbol); + in_context.body.swap(new_func.body); + in_context.body_available=new_func.body_available; + in_context.type=new_func.type; + } + else if(new_func.body.instructions.size()==0) + { + // keep the old one + } + else if(in_context.type.get_bool("#inlined")) + { + // ok + } + else if(base_type_eq(in_context.type, new_func.type, ns)) + { + // keep the one in in_context -- libraries come last! + std::stringstream str; + str << "warning: function `" << it->first << "' in module `" << + temp_context.symbols.begin()->second.module << + "' is shadowed by a definition in module `" << + context.symbols.begin()->second.module << "'"; + warning(str.str()); + } + else + { + std::stringstream str; + str << "error: duplicate definition of function `" + << it->first + << "'" << std::endl; + str << "In module `" << + context.symbols.begin()->second.module + << "' and module `" << + temp_context.symbols.begin()->second.module << "'"; + error(str.str()); + return true; + } + } + } + + return false; +} + +/*******************************************************************\ + +Function: compilet::replace_symbols_in_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void compilet::replace_symbols_in_function( + goto_functionst::function_mapt::iterator it, + replace_symbolt &replace_symbol) const +{ + goto_programt &program = it->second.body; + replace_symbol.replace(it->second.type); + + Forall_goto_program_instructions(iit, program) + { + replace_symbol.replace(iit->code); + replace_symbol.replace(iit->guard); + } +} + +/*******************************************************************\ + +Function: compilet::add_compiler_specific_defines + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void compilet::add_compiler_specific_defines(configt &config) const +{ + config.ansi_c.defines.push_back("__GOTO_CC_VERSION__=" GOTOCC_VERSION); +} + +/*******************************************************************\ + +Function: compilet::convert_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void compilet::convert_symbols(goto_functionst &dest) +{ + goto_convert_functionst converter(context, options, dest, + ui_message_handler); + + Forall_symbols(it, context.symbols) + { + if(it->second.type.id()==ID_code && + it->second.value.id()!="compiled" && + it->second.value.is_not_nil()) + { + print(9, "Compiling "+id2string(it->first)); + converter.convert_function(it->first); + it->second.value=exprt("compiled"); + } + } +} diff --git a/src/goto-cc/compile.h b/src/goto-cc/compile.h new file mode 100644 index 00000000000..c8af02cb591 --- /dev/null +++ b/src/goto-cc/compile.h @@ -0,0 +1,112 @@ +/*******************************************************************\ + +Module: Compile and link source and object files. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifndef GOTO_CC_COMPILE_H +#define GOTO_CC_COMPILE_H + +#include +#include +#include + +#include +#include +#include + +class compilet: public language_uit +{ +public: + optionst options; + namespacet ns; + goto_functionst compiled_functions; + bool act_as_ld, only_preprocess; + std::string working_directory; + + std::list library_paths; + std::list source_files; + std::list object_files; + std::list libraries; + std::list tmp_dirs; + std::list seen_modes; + + std::string object_file_extension; + std::string output_file_object, output_file_executable; + bool doLink; + + compilet(cmdlinet &_cmdline); + + ~compilet(); + + bool add_input_file( const std::string& ); + bool find_library( const std::string& ); + bool is_xml_file( const std::string& ); + bool is_binary_file( const std::string& ); + bool is_elf_file( const std::string& ); + + bool parse(const std::string &filename); + bool parse_stdin(); + bool doit(); + bool compile(); + bool link(); + + bool parse_source( const std::string& ); + bool parse_object( const std::string&, goto_functionst& ); + + bool write_object_file( const std::string &, const contextt &, + goto_functionst &); + bool write_xml_object_file( const std::string&, const contextt &, + goto_functionst& ); + bool write_bin_object_file( const std::string&, const contextt &, + goto_functionst& ); + +protected: + cmdlinet &cmdline; + std::string& escape(std::string&); + static unsigned subgraphscount; + + void write_edge( std::ostream&, + const goto_programt::instructiont&, + const goto_programt::instructiont&, + const std::string&); + + void find_next( const goto_programt::instructionst&, + const goto_programt::const_targett&, + std::set&, + std::set&); + + void show_function_table(); + + std::list function_calls; + std::list clusters; + bool write_dot_header( const std::string&, std::ofstream& ); + void write_dot_subgraph( std::ostream&, + const std::string&, + goto_programt&); + void do_dot_function_calls( std::ostream & ); + unsigned function_body_count( const goto_functionst &functions ); + + void add_compiler_specific_defines(configt &config) const; + + bool link_functions( + contextt &context, + goto_functionst &functions, + contextt &temp_context, + goto_functionst &temp_functions, + replace_symbolt &replace_symbol); + + void replace_symbols_in_function( + goto_functionst::function_mapt::iterator it, + replace_symbolt &replace_symbol) const; + + void convert_symbols(goto_functionst &dest); +}; + +std::string get_base_name(const std::string &); + +#endif /*COMPILE_H_*/ diff --git a/src/goto-cc/dist-linux b/src/goto-cc/dist-linux new file mode 100644 index 00000000000..dcd02f45809 --- /dev/null +++ b/src/goto-cc/dist-linux @@ -0,0 +1,25 @@ +#!/bin/bash +make +strip goto-cc + +VERSION=`./goto-cc --version` +VERSION_FILE=`echo $VERSION | sed "y/./-/"` + +echo $VERSION_FILE + +(cd ../goto-cc; make; strip goto-cc) +(cd ../goto-instrument; make; strip goto-instrument) + +mkdir /tmp/goto-cc-dist +cp goto-cc /tmp/goto-cc-dist/ +cp ../goto-instrument/goto-instrument /tmp/goto-cc-dist/ +cp ../../LICENSE /tmp/goto-cc-dist/ +cd /tmp/goto-cc-dist +tar cfz goto-cc-${VERSION_FILE}-linux.tgz goto-cc \ + goto-instrument LICENSE + +echo Copying. +scp goto-cc-${VERSION_FILE}-linux.tgz kroening@dkr0.inf.ethz.ch:/home/www/cprover.org/goto-cc/download/ + +cd /tmp +rm -R /tmp/goto-cc-dist diff --git a/src/goto-cc/dist-win b/src/goto-cc/dist-win new file mode 100644 index 00000000000..b105d8419ba --- /dev/null +++ b/src/goto-cc/dist-win @@ -0,0 +1,26 @@ +#!/bin/bash + +make +strip goto-cc.exe + +VERSION=`./goto-cc.exe --version` +VERSION_FILE=`echo $VERSION | sed "y/./-/"` + +(cd ../goto-cc; make; strip goto-cc.exe) +(cd ../goto-instrument; make; strip goto-instrument.exe) + +echo $VERSION_FILE + +mkdir /tmp/goto-cc-dist +cp goto-cc.exe /tmp/goto-cc-dist/ +cp ../goto-instrument/goto-instrument.exe /tmp/goto-cc-dist/ +cp ../../LICENSE /tmp/goto-cc-dist/ +cd /tmp/goto-cc-dist +zip -9 goto-cc-${VERSION_FILE}-win.zip goto-cc.exe \ + goto-instrument.exe LICENSE + +echo Copying. +scp goto-cc-${VERSION_FILE}-win.zip kroening@dkr0.inf.ethz.ch:/home/www/cprover.org/goto-cc/download/ + +cd /tmp +rm -R /tmp/goto-cc-dist diff --git a/src/goto-cc/gcc_cmdline.cpp b/src/goto-cc/gcc_cmdline.cpp new file mode 100644 index 00000000000..0fc5279d826 --- /dev/null +++ b/src/goto-cc/gcc_cmdline.cpp @@ -0,0 +1,308 @@ +/*******************************************************************\ + +Module: A special command line object for the gcc-like options + +Author: CM Wintersteiger, 2006 + +\*******************************************************************/ + +#include + +#include + +#include "gcc_cmdline.h" + +/*******************************************************************\ + +Function: gcc_cmdlinet::parse + + Inputs: argument count, argument strings + + Outputs: none + + Purpose: parses the commandline options into a cmdlinet + +\*******************************************************************/ + +bool gcc_cmdlinet::parse(int argc, const char **argv) +{ + for(int i=1; i2) + { + options[optnr].hasval = true; + options[optnr].values.push_back(argv[i]+2); + } + } + else if( // options that have an = argument + strncmp(argv[i], "-std", 4)==0 || + strncmp(argv[i], "-print-file-name", 16)==0 || + strncmp(argv[i], "-print-prog-name", 16)==0 || + strncmp(argv[i], "-specs", 6)==0 || + strncmp(argv[i], "--sysroot", 9)==0 + ) + { + const char *inx=strchr(argv[i], '='); + if(inx!=NULL) + { + option = options[optnr]; + option.optstring = + option.optstring.substr(0, option.optstring.find("=",0)-1); + optnr=getoptnr(option.optstring); + if(optnr==-1) + { + options.push_back(option); + optnr = options.size()-1; + } + options[optnr].isset=true; + + options[optnr].hasval = true; + options[optnr].values.push_back(inx+1); + } + } + else + { // unrecognized option + std::cout << "Warning: uninterpreted gcc option '" << argv[i] << "'" << std::endl; + } + } + + return false; +} diff --git a/src/goto-cc/gcc_cmdline.h b/src/goto-cc/gcc_cmdline.h new file mode 100644 index 00000000000..1251045d513 --- /dev/null +++ b/src/goto-cc/gcc_cmdline.h @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: A special command line object for the gcc-like options + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifndef GOTO_CC_GCC_CMDLINE_H +#define GOTO_CC_GCC_CMDLINE_H + +#include "goto_cc_cmdline.h" + +class gcc_cmdlinet:public goto_cc_cmdlinet +{ +public: + virtual bool parse(int, const char**); + + gcc_cmdlinet() + { + mode=GCC; + } +}; + +#endif /* GOTO_CC_GCC_CMDLINE_H */ diff --git a/src/goto-cc/get_base_name.cpp b/src/goto-cc/get_base_name.cpp new file mode 100644 index 00000000000..3e497af445b --- /dev/null +++ b/src/goto-cc/get_base_name.cpp @@ -0,0 +1,40 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +Date: + +\*******************************************************************/ + +#include "get_base_name.h" + +/*******************************************************************\ + +Function: get_base_name + + Inputs: a string + + Outputs: a new string + + Purpose: cleans a filename from paths and extensions + +\*******************************************************************/ + +std::string get_base_name(const std::string &in) +{ + size_t r=in.rfind('.', in.length()-1); + if(r==std::string::npos) r=in.length(); + + size_t f=in.rfind('/', in.length()-1); + if(f==std::string::npos) f=0; + + size_t fw=in.rfind('\\', in.length()-1); + if(fw==std::string::npos) fw=0; + + f = (fw>f)?fw:f; + + if(in[f]=='/' || in[f]=='\\') f++; + return in.substr(f, r-f); +} diff --git a/src/goto-cc/get_base_name.h b/src/goto-cc/get_base_name.h new file mode 100644 index 00000000000..c38500a1c54 --- /dev/null +++ b/src/goto-cc/get_base_name.h @@ -0,0 +1,13 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +Date: + +\*******************************************************************/ + +#include + +std::string get_base_name(const std::string &in); diff --git a/src/goto-cc/goto-cc.cpp b/src/goto-cc/goto-cc.cpp new file mode 100644 index 00000000000..db868d3cdd5 --- /dev/null +++ b/src/goto-cc/goto-cc.cpp @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: GOTO-CC Main Module + +Authors: Daniel Kroening, kroening@kroening.com + +Date: May 2006 + +\*******************************************************************/ + +#include "cmdline_options.h" +#include "get_base_name.h" +#include "gcc_cmdline.h" +#include "armcc_cmdline.h" +#include "ms_cl_cmdline.h" + +/*******************************************************************\ + +Function: main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int main(int argc, const char **argv) +{ + if(argv==NULL || argc<1) + { + std::cerr << "failed to determine base name" << std::endl; + return 1; + } + + std::string base_name=get_base_name(argv[0]); + + if(base_name=="goto-link" || base_name=="link" || + base_name=="goto-cl" || base_name=="cl") + { + // this is the Visual Studio personality + ms_cl_cmdlinet cmdline; + cmdline_optionst cmdline_options(cmdline); + cmdline_options.my_name=base_name; + return cmdline_options.main(argc, argv); + } + else if(base_name=="goto-cw" || + base_name=="goto-cw-link") + { + // this is the CodeWarrior personality, + // but we use the gcc command line interface + gcc_cmdlinet cmdline; + cmdline.mode=goto_cc_cmdlinet::CODEWARRIOR; + cmdline_optionst cmdline_options(cmdline); + cmdline_options.my_name=base_name; + return cmdline_options.main(argc, argv); + } + else if(base_name=="goto-armcc" || + base_name=="goto-armlink") + { + // this is the armcc personality + armcc_cmdlinet cmdline; + cmdline_optionst cmdline_options(cmdline); + cmdline_options.my_name=base_name; + return cmdline_options.main(argc, argv); + } + else + { + // the default personality is GCC + gcc_cmdlinet cmdline; + cmdline_optionst cmdline_options(cmdline); + cmdline_options.my_name=base_name; + return cmdline_options.main(argc, argv); + } +} diff --git a/src/goto-cc/goto-cc.h b/src/goto-cc/goto-cc.h new file mode 100644 index 00000000000..38e15723c57 --- /dev/null +++ b/src/goto-cc/goto-cc.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: C to Goto Program Converter + +Author: Daniel Kroening + Karen Yorav + +Date: June 2006 + +Purpose: Preprocess the C program and convert it into a GOTO + program. The resulting program includes only + (conditional) gotos and basic blocks. + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_CC_H +#define CPROVER_GOTO_CC_H + +#endif diff --git a/src/goto-cc/goto_cc_cmdline.cpp b/src/goto-cc/goto_cc_cmdline.cpp new file mode 100644 index 00000000000..6fe8cf2f3f5 --- /dev/null +++ b/src/goto-cc/goto_cc_cmdline.cpp @@ -0,0 +1,131 @@ +/*******************************************************************\ + +Module: Command line interpretation for goto-cc + +Author: Daniel Kroening + +Date: April 2010 + +\*******************************************************************/ + +#include + +#include "goto_cc_cmdline.h" + +/*******************************************************************\ + +Function: goto_cc_cmdlinet::in_list + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_cc_cmdlinet::in_list(const char *option, const char **list) +{ + for(unsigned i=0; list[i]!=NULL; i++) + { + if(strcmp(option, list[i])==0) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: goto_cc_cmdlinet::prefix_in_list + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_cc_cmdlinet::prefix_in_list( + const char *option, + const char **list, + std::string &prefix) +{ + for(unsigned i=0; list[i]!=NULL; i++) + { + if(strncmp(option, list[i], strlen(list[i]))==0) + { + prefix=std::string(list[i]); + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: goto_cc_cmdlinet::get_optnr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int goto_cc_cmdlinet::get_optnr(const std::string &opt_string) +{ + int optnr; + cmdlinet::optiont option; + + if(std::string(opt_string, 0, 2)=="--") // starts with -- ? + { + if(opt_string.size()==3) // still "short" + { + option.islong=false; + option.optchar=opt_string[2]; + optnr=getoptnr(option.optchar); + } + else + { + option.islong=true; + option.optstring=std::string(opt_string, 2, std::string::npos); + option.optchar=0; + optnr=getoptnr(option.optstring); + } + } + else if(std::string(opt_string, 0, 1)=="-") + { + if(opt_string.size()==2) + { + option.islong=false; + option.optchar=opt_string[1]; + optnr=getoptnr(option.optchar); + } + else + { + option.islong=true; + option.optstring=std::string(opt_string, 1, std::string::npos); + option.optchar=0; + optnr=getoptnr(option.optstring); + } + } + else + { + assert(false); + return -1; + } + + if(optnr==-1) + { + // new + options.push_back(option); + return options.size()-1; + } + + return optnr; +} + diff --git a/src/goto-cc/goto_cc_cmdline.h b/src/goto-cc/goto_cc_cmdline.h new file mode 100644 index 00000000000..b3fb450c671 --- /dev/null +++ b/src/goto-cc/goto_cc_cmdline.h @@ -0,0 +1,34 @@ +/*******************************************************************\ + +Module: Command line interpretation for goto-cc + +Author: Daniel Kroening + +Date: April 2010 + +\*******************************************************************/ + +#ifndef GOTO_CC_CMDLINE_H +#define GOTO_CC_CMDLINE_H + +#include + +class goto_cc_cmdlinet:public cmdlinet +{ +public: + typedef enum { VISUAL_STUDIO, GCC, CODEWARRIOR, ARM } modet; + modet mode; + + virtual bool parse(int argc, const char **argv)=0; + + static bool in_list(const char *option, const char **list); + + static bool prefix_in_list( + const char *option, + const char **list, + std::string &prefix); + + int get_optnr(const std::string &option); +}; + +#endif /*CMDLINE_H_*/ diff --git a/src/goto-cc/languages.cpp b/src/goto-cc/languages.cpp new file mode 100644 index 00000000000..0a6c8b000e1 --- /dev/null +++ b/src/goto-cc/languages.cpp @@ -0,0 +1,46 @@ +/*******************************************************************\ + +Module: Language Registration + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include + +#include + +#ifdef HAVE_CPP +#include +#endif + +#ifdef HAVE_SPECC +#include +#endif + +#include "cmdline_options.h" + +/*******************************************************************\ + +Function: cmdline_optionst::register_languages + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cmdline_optionst::register_languages() +{ + register_language(new_ansi_c_language); + + #ifdef HAVE_CPP + register_language(new_cpp_language); + #endif + + #ifdef HAVE_SPECC + register_language(new_specc_language); + #endif +} diff --git a/src/goto-cc/ms_cl_cmdline.cpp b/src/goto-cc/ms_cl_cmdline.cpp new file mode 100644 index 00000000000..91b0b8a8148 --- /dev/null +++ b/src/goto-cc/ms_cl_cmdline.cpp @@ -0,0 +1,390 @@ +/*******************************************************************\ + +Module: A special command line object for the CL options + +Author: Daniel Kroening + +\*******************************************************************/ + +#include +#include +#include + +#include +#include + +#include "ms_cl_cmdline.h" + +/*******************************************************************\ + +Function: ms_cl_cmdlinet::parse + + Inputs: argument count, argument strings + + Outputs: none + + Purpose: parses the commandline options into a cmdlinet + +\*******************************************************************/ + +const char *non_ms_cl_options[]= +{ + "--dot", + "--show-symbol-table", + "--show-function-table", + "--ppc-macos", + "--i386-linux", + "--i386-win32", + "--i386-macos", + "--string-abstraction", + "--no-library", + "--16", + "--32", + "--64", + "--little-endian", + "--big-endian", + "--unsigned-char", + "--no-arch", + "--help", + "--xml", + "--partial-inlining", + "--verbosity", + NULL +}; + +bool ms_cl_cmdlinet::parse(int argc, const char **argv) +{ + // first do environment + const char *CL_env=getenv("CL"); + + if(CL_env!=NULL) + { + const char *ptr=CL_env; + while(*ptr!=0) + { + while(*ptr==' ') ptr++; + + unsigned cnt=0; + while(ptr[cnt]!=0 && ptr[cnt]!=' ') cnt++; + + std::string tmp=std::string(ptr, 0, cnt); + process_cl_option(tmp); + } + } + + for(int i=1; i inline expansion (default n=0) + "Od", // disable optimizations (default) + "Og", // enable global optimization + "Oi", // [-] enable intrinsic functions + "Os", // favor code space + "Ot", // favor code speed + "Ox", // maximum optimizations + "Oy", // [-] enable frame pointer omission + "GF", // enable read-only string pooling + "Gm", // [-] enable minimal rebuild + "Gy", // [-] separate functions for linker + "GS", // [-] enable security checks + "GR", // [-] enable C++ RTTI + "GX", // [-] enable C++ EH (same as /EHsc) + "EHs", // enable C++ EH (no SEH exceptions) + "EHa", // enable C++ EH (w/ SEH exceptions) + "EHc", // extern "C" defaults to nothrow + "fp", // floating-point model + "GL", // [-] enable link-time code generation + "GA", // optimize for Windows Application + "Ge", // force stack checking for all funcs + "Gs", // [num] control stack checking calls + "Gh", // enable _penter function call + "GH", // enable _pexit function call + "GT", // generate fiber-safe TLS accesses + "RTC1", // Enable fast checks (/RTCsu) + "RTCc", // Convert to smaller type checks + "RTCs", // Stack Frame runtime checking + "RTCu", // Uninitialized local usage checks + "clr", // compile for common language runtime + "Gd", // __cdecl calling convention + "Gr", // __fastcall calling convention + "Gz", // __stdcall calling convention + "GZ", // Enable stack checks (/RTCs) + "QIfist", // [-] use FIST instead of ftol() + "hotpatch", // ensure function padding for hotpatchable images + "arch:", // minimum CPU architecture requirements + "Fa", // [file] name assembly listing file + "FA", // [scu] configure assembly listing + "Fd", // [file] name .PDB file + "Fe", // name executable file + "Fm", // [file] name map file + "Fo", // name object file + "Fp", // name precompiled header file + "Fr", // [file] name source browser file + "FR", // [file] name extended .SBR file + "doc", // [file] process XML documentation comments + "AI", // add to assembly search path + "FU", // forced using assembly/module + "C", // don't strip comments + "D", // {=|#} define macro + "E", // preprocess to stdout + "EP", // preprocess to stdout, no #line + "P", // preprocess to file + "Fx", // merge injected code to file + "FI", // name forced include file + "U", // remove predefined macro + "u", // remove all predefined macros + "I", // add to include search path + "X", // ignore "standard places" + "Zi", // enable debugging information + "Z7", // enable old-style debug info + "Zp", // [n] pack structs on n-byte boundary + "Za", // disable extensions + "Ze", // enable extensions (default) + "Zl", // omit default library name in .OBJ + "Zg", // generate function prototypes + "Zs", // syntax check only + "vd", // {0|1|2} disable/enable vtordisp + "vm", // type of pointers to members + "Zc:", // arg1[,arg2] C++ language conformance, where arguments can be: + "ZI", // enable Edit and Continue debug info + "openmp", // enable OpenMP 2.0 language extensions + "?", + "help", // print this help message + "FC", // use full pathnames in diagnostics /H max external name length + "J", // default char type is unsigned + "nologo", // suppress copyright message + "show", // Includes show include file names + "Tc", // compile file as .c + "Tp", // compile file as .cpp + "TC", // compile all files as .c + "TP", // compile all files as .cpp + "V", // set version string + "w", // disable all warnings + "wd", // disable warning n + "we", // treat warning n as an error + "wo", // issue warning n once + "w", // set warning level 1-4 for n + "W", // set warning level (default n=1) + "Wall", // enable all warnings + "WL", // enable one line diagnostics + "WX", // treat warnings as errors + "Yc", // [file] create .PCH file + "Yd", // put debug info in every .OBJ + "Yl", // [sym] inject .PCH ref for debug lib + "Yu", // [file] use .PCH file + "Y", // - disable all PCH options + "Zm", // max memory alloc (% of default) + "Wp64", // enable 64 bit porting warnings + "LD", // Create .DLL + "LDd", // Create .DLL debug library + "LN", // Create a .netmodule + "F", // set stack size + "link", // [linker options and libraries] + "MD", // link with MSVCRT.LIB + "MT", // link with LIBCMT.LIB + "MDd", // link with MSVCRTD.LIB debug lib + "MTd", // link with LIBCMTD.LIB debug lib + NULL +}; + +void ms_cl_cmdlinet::process_cl_option(const std::string &s) +{ + if(s=="") return; + + if(s[0]!='/' && s[0]!='-') + { + args.push_back(s); + return; + } + + for(unsigned j=0; ms_cl_flags[j]!=NULL; j++) + { + if(std::string(s, 1, std::string::npos)==ms_cl_flags[j]) + { + cmdlinet::optiont option; + + if(s.size()==2) + { + option.islong=false; + option.optstring=""; + option.optchar=s[1]; + } + else + { + option.islong=true; + option.optstring=std::string(s, 1, std::string::npos); + option.optchar=0; + } + + int optnr=getoptnr(option.optstring); + + if(optnr==-1) + { + options.push_back(option); + optnr=options.size()-1; + } + + options[optnr].isset=true; + return; + } + } + + for(unsigned j=0; ms_cl_prefixes[j]!=NULL; j++) + { + unsigned length=strlen(ms_cl_prefixes[j]); + if(std::string(s, 1, length)==ms_cl_prefixes[j]) + { + cmdlinet::optiont option; + + int optnr; + + if(length==1) + { + option.islong=false; + option.optstring=""; + option.optchar=ms_cl_prefixes[j][0]; + optnr=getoptnr(option.optchar); + } + else + { + option.islong=true; + option.optstring=std::string(s, 1, length); + option.optchar=0; + optnr=getoptnr(option.optstring); + } + + if(optnr==-1) + { + options.push_back(option); + optnr=options.size()-1; + } + + options[optnr].isset=true; + options[optnr].values.push_back(std::string(s, length+1, std::string::npos)); + return; + } + } + + // unrecognized option + std::cout << "Warning: uninterpreted CL option `" + << s << "'" << std::endl; +} diff --git a/src/goto-cc/ms_cl_cmdline.h b/src/goto-cc/ms_cl_cmdline.h new file mode 100644 index 00000000000..b234683f731 --- /dev/null +++ b/src/goto-cc/ms_cl_cmdline.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: A special command line object for the gcc-like options + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifndef GOTO_CC_MS_CL_CMDLINE_H +#define GOTO_CC_MS_CL_CMDLINE_H + +#include "goto_cc_cmdline.h" + +class ms_cl_cmdlinet:public goto_cc_cmdlinet +{ +public: + virtual bool parse(int, const char **); + + ms_cl_cmdlinet() + { + mode=VISUAL_STUDIO; + } + +protected: + void process_non_cl_option(const std::string &s); + void process_cl_option(const std::string &s); + void process_response_file(const std::string &file); +}; + +#endif /*MS_CL_CMDLINE_H_*/ diff --git a/src/goto-cc/version.h b/src/goto-cc/version.h new file mode 100644 index 00000000000..802eab735e4 --- /dev/null +++ b/src/goto-cc/version.h @@ -0,0 +1,7 @@ +#ifndef GOTO_CC_VERSION_H +#define GOTO_CC_VERSION_H + +#define XML_VERSION "1.4" +#define GOTOCC_VERSION "3.9" + +#endif /* GOTO_CC_VERSION_H */ diff --git a/src/goto-cc/xml_binaries/read_goto_object.cpp b/src/goto-cc/xml_binaries/read_goto_object.cpp new file mode 100644 index 00000000000..2503a0922be --- /dev/null +++ b/src/goto-cc/xml_binaries/read_goto_object.cpp @@ -0,0 +1,144 @@ +/*******************************************************************\ + +Module: Read goto object files. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#include +#include +#include +#include + +#define XML_VERSION "1.4" + +#include + +#include "read_goto_object.h" +#include "xml_goto_function_hashing.h" +#include "xml_irep_hashing.h" +#include "xml_symbol_hashing.h" + +/*******************************************************************\ + +Function: read_goto_object + + Inputs: input stream, context, functions + + Outputs: true on error, false otherwise + + Purpose: reads a goto object xml file back into a symbol and a + function table + +\*******************************************************************/ + +bool read_goto_object( + std::istream &in, + const std::string &filename, + contextt &context, + goto_functionst &functions, + message_handlert &message_handler) +{ + message_streamt message_stream(message_handler); + + xml_parser.clear(); + xml_parser.filename = filename; + xml_parser.in = ∈ + xml_parser.set_message_handler(message_handler); + + if (xml_parser.parse()) + return true; + + xmlt &top = xml_parser.parse_tree.element; + + if (top.get_attribute("version")!=XML_VERSION) + { + message_stream.str << + "The input was compiled with a different version of " + "goto-cc, please recompile."; + message_stream.error(); + return true; + } + + xml_irep_convertt::ireps_containert ic; + xml_irep_convertt irepconverter(ic); + xml_symbol_convertt symbolconverter(ic); + xml_goto_function_convertt gfconverter(ic); + + if(top.name.substr(0, 11)=="goto-object") + { + for(xmlt::elementst::const_iterator + sec_it=top.elements.begin(); + sec_it != top.elements.end(); + sec_it++) + { + xmlt sec = *sec_it; + if (sec.name=="irep_hash_map") + { + for(xmlt::elementst::const_iterator + irep_it = sec.elements.begin(); + irep_it != sec.elements.end(); + irep_it++) + { + irept i; + irepconverter.convert(*irep_it, i); + irepconverter.insert(irep_it->get_attribute("id"), i); + } + } + else if (sec.name=="symbols") + { + for(xmlt::elementst::const_iterator + sym_it = sec.elements.begin(); + sym_it != sec.elements.end(); + sym_it++) + { + symbolt symbol; + symbolconverter.convert(*sym_it, symbol); + // std::cout << "Adding Symbol: " << symbol.name << std::endl; + if(!symbol.is_type && + symbol.type.id()=="code") + { + // makes sure there is an empty function + // for this symbol. if we got code for it, + // it will be added lateron. + functions.function_map[symbol.name].type= + to_code_type(symbol.type); + } + context.add(symbol); + } + } + else if (sec.name=="functions") + { + for(xmlt::elementst::const_iterator + fun_it = sec.elements.begin(); + fun_it != sec.elements.end(); + fun_it++) + { + std::string fname = fun_it->get_attribute("name"); + //std::cout << "Adding function body: " << fname << std::endl; + goto_functionst::goto_functiont &f = functions.function_map[fname]; + gfconverter.convert(*fun_it, f); + } + } + else + { + message_stream.str << "Unknown Section '" + << sec.name << "' in object file."; + message_stream.error(); + return true; + } + + } + } + else + { + message_stream.error("no goto-object"); + return true; + } + + xml_parser.clear(); + return false; +} diff --git a/src/goto-cc/xml_binaries/read_goto_object.h b/src/goto-cc/xml_binaries/read_goto_object.h new file mode 100644 index 00000000000..e65be9f334b --- /dev/null +++ b/src/goto-cc/xml_binaries/read_goto_object.h @@ -0,0 +1,25 @@ +/*******************************************************************\ + +Module: Read goto object files. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifndef READ_GOTO_OBJECT_H_ +#define READ_GOTO_OBJECT_H_ + +#include +#include +#include + +bool read_goto_object( + std::istream &in, + const std::string &filename, + contextt &context, + goto_functionst &functions, + message_handlert &msg_hndlr); + +#endif /*READ_GOTO_OBJECT_H_*/ diff --git a/src/goto-cc/xml_binaries/xml_goto_function.cpp b/src/goto-cc/xml_binaries/xml_goto_function.cpp new file mode 100644 index 00000000000..02974508aa7 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_goto_function.cpp @@ -0,0 +1,53 @@ +/*******************************************************************\ + +Module: Convert goto functions to xml structures and back. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#include + +#include "xml_goto_function.h" +#include "xml_goto_program.h" + +/*******************************************************************\ + +Function: convert + + Inputs: goto_function and an xml node + + Outputs: none + + Purpose: takes a goto_function and creates an according xml structure + +\*******************************************************************/ + +void convert( const goto_functionst::goto_functiont& function, xmlt& xml) +{ + if (function.body_available) + convert(function.body, xml); +} + +/*******************************************************************\ + +Function: convert + + Inputs: xml structure and a goto_function to fill + + Outputs: none + + Purpose: constructs the goto_function according to the information + in the xml structure. + +\*******************************************************************/ + +void convert( const xmlt& xml, goto_functionst::goto_functiont& function) +{ + function.body.clear(); + convert(xml, function.body); + function.body_available = function.body.instructions.size()>0; + // don't forget to fix the functions type via the symbol table! +} diff --git a/src/goto-cc/xml_binaries/xml_goto_function.h b/src/goto-cc/xml_binaries/xml_goto_function.h new file mode 100644 index 00000000000..0f351bfb8b2 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_goto_function.h @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Convert goto functions into xml structures and back + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifndef XML_GOTO_FUNCTION_H_ +#define XML_GOTO_FUNCTION_H_ + +#include +#include + +void convert( const xmlt&, goto_functionst::goto_functiont& ); +void convert( const goto_functionst::goto_functiont&, xmlt& ); + +#endif /*XML_GOTO_FUNCTION_H_*/ diff --git a/src/goto-cc/xml_binaries/xml_goto_function_hashing.cpp b/src/goto-cc/xml_binaries/xml_goto_function_hashing.cpp new file mode 100644 index 00000000000..1315e8b4b80 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_goto_function_hashing.cpp @@ -0,0 +1,56 @@ +/*******************************************************************\ + +Module: Convert goto functions to xml structures and back (with irep + hashing) + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#include "xml_goto_function_hashing.h" +#include "xml_goto_program_hashing.h" + +/*******************************************************************\ + +Function: xml_goto_function_convertt::convert + + Inputs: goto_function and an xml node + + Outputs: none + + Purpose: takes a goto_function and creates an according xml structure + +\*******************************************************************/ + +void +xml_goto_function_convertt::convert( const goto_functionst::goto_functiont& function, xmlt& xml) +{ + xml_goto_program_convertt gpconverter(ireps_container); + if (function.body_available) + gpconverter.convert(function.body, xml); +} + +/*******************************************************************\ + +Function: xml_goto_function_convertt::convert + + Inputs: xml structure and a goto_function to fill + + Outputs: none + + Purpose: constructs the goto_function according to the information + in the xml structure. + +\*******************************************************************/ + +void +xml_goto_function_convertt::convert( const xmlt& xml, goto_functionst::goto_functiont& function) +{ + xml_goto_program_convertt gpconverter(ireps_container); + function.body.clear(); + gpconverter.convert(xml, function.body); + function.body_available = function.body.instructions.size()>0; + // don't forget to fix the functions type via the symbol table! +} diff --git a/src/goto-cc/xml_binaries/xml_goto_function_hashing.h b/src/goto-cc/xml_binaries/xml_goto_function_hashing.h new file mode 100644 index 00000000000..b0e13170b8a --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_goto_function_hashing.h @@ -0,0 +1,31 @@ +/*******************************************************************\ + +Module: Convert goto functions into xml structures and back (with irep + hashing). + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#ifndef XML_GOTO_FUNCTION_H_ +#define XML_GOTO_FUNCTION_H_ + +#include +#include + +#include "xml_irep_hashing.h" + +class xml_goto_function_convertt { + private: + xml_irep_convertt::ireps_containert &ireps_container; + public: + xml_goto_function_convertt(xml_irep_convertt::ireps_containert &ic) : + ireps_container(ic) {}; + + void convert( const xmlt&, goto_functionst::goto_functiont& ); + void convert( const goto_functionst::goto_functiont&, xmlt& ); +}; + +#endif /*XML_GOTO_FUNCTION_H_*/ diff --git a/src/goto-cc/xml_binaries/xml_goto_program.cpp b/src/goto-cc/xml_binaries/xml_goto_program.cpp new file mode 100644 index 00000000000..7cc1966e95b --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_goto_program.cpp @@ -0,0 +1,431 @@ +/*******************************************************************\ + +Module: Convert goto programs to xml structures and back. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#include +#include + +#include "xml_goto_program.h" + +/*******************************************************************\ + +Function: convert + + Inputs: goto program, namespace and an xml structure to fill + + Outputs: none + + Purpose: constructs the xml structure according to the goto program + and the namespace into the given xml object. + +\*******************************************************************/ + +void convert(const goto_programt &goto_program, + xmlt &xml) +{ + std::stringstream tmp; + // std::cout << "TNO: " << goto_program.target_numbers.size() << std::endl; + + const goto_programt::instructionst &instructions = + goto_program.instructions; + goto_programt::instructionst::const_iterator ins_it = + instructions.begin(); + for (;ins_it!=instructions.end();ins_it++) + { + xmlt &ins = xml.new_element("instruction"); + + if (!ins_it->location.is_nil()) + { + convert(ins_it->location, ins.new_element("location")); + } + + if(!ins_it->labels.empty()) + { + xmlt &lbl = ins.new_element("labels"); + for(goto_programt::instructiont::labelst::const_iterator + l_it=ins_it->labels.begin(); + l_it!=ins_it->labels.end(); + l_it++) + { + lbl.new_element("label").set_attribute("name", id2string(*l_it)); + } + } + + + if(ins_it->target_number!=0) + { + // std::cout << "Targetlabel found!" << std::endl; + tmp.str(""); + tmp << ins_it->target_number; + ins.set_attribute("targetlabel",tmp.str()); + } + + switch(ins_it->type) + { + case GOTO: + { + ins.name = "goto"; + if (!ins_it->guard.is_true()) + { + xmlt &g = ins.new_element("guard"); + convert(ins_it->guard, g); + } + xmlt &tgt = ins.new_element("targets"); + for(goto_programt::instructiont::targetst::const_iterator + gt_it=ins_it->targets.begin(); + gt_it!=ins_it->targets.end(); + gt_it++) + { + tmp.str(""); + tmp << (*gt_it)->target_number; + tgt.new_element("target").data = tmp.str(); + } + break; + } + + case ASSUME: + { + ins.name = "assume"; + xmlt &g = ins.new_element("guard"); + convert(ins_it->guard, g); + + const irep_idt &comment=ins_it->location.get("comment"); + + if(comment!="") + ins.new_element("comment").data=id2string(comment); + + break; + } + + case ASSERT: + { + ins.name = "assert"; + xmlt &g = ins.new_element("guard"); + convert(ins_it->guard, g); + const irep_idt &comment=ins_it->location.get("comment"); + + if(comment!="") + ins.new_element("comment").data=id2string(comment); + + break; + } + + case SKIP: + ins.name = "skip"; + break; + + case END_FUNCTION: + ins.name = "end_function"; + break; + + case LOCATION: + ins.name = "location"; + break; + + case DEAD: + ins.name = "dead"; + break; + + case ATOMIC_BEGIN: + ins.name = "atomic_begin"; + break; + + case ATOMIC_END: + ins.name = "atomic_end"; + break; + + case RETURN: + { + ins.name = "return"; + xmlt &c = ins.new_element("code"); + convert(ins_it->code, c); + break; + } + + case OTHER: + { + ins.name = "instruction"; + xmlt &c = ins.new_element("code"); + convert(ins_it->code, c); + break; + } + + case ASSIGN: + { + ins.name = "assign"; + xmlt &c = ins.new_element("code"); + convert(ins_it->code, c); + break; + } + + case FUNCTION_CALL: + { + ins.name = "functioncall"; + xmlt &c = ins.new_element("code"); + convert(ins_it->code, c); + break; + } + + case START_THREAD: + { + ins.name = "thread_start"; + xmlt &tgt = ins.new_element("targets"); + if(ins_it->targets.size()==1) + { + tmp.str(""); + tmp << ins_it->targets.front()->target_number; + tgt.new_element("target").data = tmp.str(); + } + break; + } + + case END_THREAD: + ins.name = "thread_end"; + break; + + default: + ins.name = "unknown"; + break; + } + + if (ins_it->function!="") + { + xmlt &fnc = ins.new_element("function"); + fnc.data = ins_it->function.as_string(); + } + } +} + +/*******************************************************************\ + +Function: convert + + Inputs: an xml structure, namespace, function symbol + and a goto program to fill + + Outputs: none + + Purpose: constructs the goto program according to the xml structure + and the namespace into the given goto program object. + +\*******************************************************************/ +void convert( const xmlt& xml, + goto_programt& goto_program) +{ + goto_program.clear(); + goto_programt::instructionst &instructions = goto_program.instructions; + + xmlt::elementst::const_iterator it = xml.elements.begin(); + for (; it != xml.elements.end(); it++) + { + goto_programt::targett inst = goto_program.add_instruction(); + inst->targets.clear(); + + if (it->name=="goto") + { + inst->type = GOTO; + } + else if (it->name=="assume") + { + inst->type = ASSUME; + } + else if (it->name=="assert") + { + inst->type = ASSERT; + } + else if (it->name=="skip") + { + inst->type = SKIP; + } + else if (it->name=="end_function") + { + inst->type = END_FUNCTION; + } + else if (it->name=="location") + { + inst->type = LOCATION; + } + else if (it->name=="dead") + { + inst->type = DEAD; + } + else if (it->name=="atomic_begin") + { + inst->type = ATOMIC_BEGIN; + } + else if (it->name=="atomic_end") + { + inst->type = ATOMIC_END; + } + else if (it->name=="return") + { + inst->make_return(); + } + else if (it->name=="instruction") // OTHER + { + inst->make_other(); + } + else if (it->name=="assign") + { + inst->make_other(); + inst->type=ASSIGN; + } + else if (it->name=="functioncall") + { + inst->make_other(); + inst->type=FUNCTION_CALL; + } + else if (it->name=="thread_start") + { + inst->type = START_THREAD; + } + else if (it->name=="thread_end") + { + inst->type = END_THREAD; + } + else + { + std::cout << "Unknown instruction type encountered (" << it->name << ")"; + std::cout << std::endl; + return; + } + + xmlt::elementst::const_iterator eit = it->elements.begin(); + for (; eit != it->elements.end(); eit++) + { + if (eit->name=="location") + { + convert(*eit, inst->location); + } + else if (eit->name=="variables") + { + } + else if (eit->name=="labels") + { + xmlt::elementst::const_iterator lit = eit->elements.begin(); + for (; lit != eit->elements.end(); lit++) + { + if (lit->name=="label") + { + std::string ls = lit->get_attribute("name"); + inst->labels.push_back(ls); + } + else + { + std::cout << "Unknown node in labels section." << std::endl; + return; + } + } + } + else if (eit->name=="guard") + { + inst->guard.remove("value"); + convert(*eit, inst->guard); + } + else if (eit->name=="code") + { + convert(*eit, inst->code); + } + else if (eit->name=="targets") + { + // Don't do anything here, we'll need a second run for that + } + else if (eit->name=="comment") + { + inst->location.set("comment", eit->data); + } + else if (eit->name=="function") + { + inst->function = eit->data; + } + } + } + + // assign line numbers + goto_program.compute_location_numbers(); + + // second run, for targets + goto_programt::targett ins_it = instructions.begin(); + it = xml.elements.begin(); + for (; it != xml.elements.end() && ins_it!=instructions.end(); it++) + { + xmlt::elementst::const_iterator eit = it->elements.begin(); + for (; eit != it->elements.end(); eit++) + { + if (eit->name=="targets") + { + xmlt::elementst::const_iterator tit = eit->elements.begin(); + for (; tit != eit->elements.end(); tit++) + { + if (tit->name=="target") + { + goto_programt::targett tins = + find_instruction(xml, instructions, tit->data); + if (tins != instructions.end()) + { + // Here we insert the iterators that somehow seem + // to be strange afterwards (see line 87) + ins_it->targets.push_back(tins); + } + else + { + std::cout << "Warning: instruction not found when " + "resolving target links." << std::endl; + } + } + else + { + std::cout << "Unknown node in targets section." << std::endl; + return; + } + } + } + } + ins_it++; + } + + // resolve links + goto_program.update(); + + //std::cout << "TNI: " << goto_program.target_numbers.size() << std::endl; +} + +/*******************************************************************\ + +Function: find_instruction + + Inputs: a target label string, the instructions list and an xml program + + Outputs: iterator to the found instruction or .end() + + Purpose: finds the index of the instruction labelled with the given + target label in the given xml-program + +\*******************************************************************/ + +goto_programt::targett +find_instruction( + const xmlt &xml, + goto_programt::instructionst &instructions, + const irep_idt &label) +{ + goto_programt::targett ins_it=instructions.begin(); + xmlt::elementst::const_iterator it=xml.elements.begin(); + + for (; it != xml.elements.end() && ins_it!=instructions.end(); it++) + { + if (label==it->get_attribute("targetlabel")) + return ins_it; + + ins_it++; + } + + return instructions.end(); +} + diff --git a/src/goto-cc/xml_binaries/xml_goto_program.h b/src/goto-cc/xml_binaries/xml_goto_program.h new file mode 100644 index 00000000000..db2bea95605 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_goto_program.h @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: Convert goto programs into xml structures and back + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifndef XML_GOTO_PROGRAM_H_ +#define XML_GOTO_PROGRAM_H_ + +#include +#include + +void convert( + const goto_programt&, + xmlt&); + +void convert( + const xmlt&, + goto_programt&); + +goto_programt::targett find_instruction( + const xmlt &, + goto_programt::instructionst &, + const irep_idt &); + +#endif /*XML_GOTO_PROGRAM_H_*/ diff --git a/src/goto-cc/xml_binaries/xml_goto_program_hashing.cpp b/src/goto-cc/xml_binaries/xml_goto_program_hashing.cpp new file mode 100644 index 00000000000..04ad5ad62eb --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_goto_program_hashing.cpp @@ -0,0 +1,423 @@ +/*******************************************************************\ + +Module: Convert goto programs to xml structures and back (with irep + hashing) + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#include + +#include "xml_irep_hashing.h" +#include "xml_goto_program_hashing.h" + +/*******************************************************************\ + +Function: xml_goto_program_convertt::convert + + Inputs: goto program and an xml structure to fill + + Outputs: none + + Purpose: constructs the xml structure according to the goto program + and the namespace into the given xml object. + +\*******************************************************************/ + +void xml_goto_program_convertt::convert(const goto_programt &goto_program, + xmlt &xml) +{ + std::stringstream tmp; + // std::cout << "TNO: " << goto_program.target_numbers.size() << std::endl; + + const goto_programt::instructionst &instructions = + goto_program.instructions; + goto_programt::instructionst::const_iterator ins_it = + instructions.begin(); + for (;ins_it!=instructions.end();ins_it++) + { + xmlt &ins = xml.new_element("instruction"); + if (!ins_it->location.is_nil()) + { + irepconverter.reference_convert(ins_it->location, ins.new_element("location")); + } + + if(!ins_it->labels.empty()) + { + xmlt &lbl = ins.new_element("labels"); + for(goto_programt::instructiont::labelst::const_iterator + l_it=ins_it->labels.begin(); + l_it!=ins_it->labels.end(); + l_it++) + { + lbl.new_element("label").set_attribute("name", id2string(*l_it)); + } + } + + + if(ins_it->target_number!=0) + { + // std::cout << "Targetlabel found!" << std::endl; + tmp.str(""); + tmp << ins_it->target_number; + ins.set_attribute("targetlabel",tmp.str()); + } + + switch(ins_it->type) + { + case GOTO: + { + ins.name = "goto"; + if (!ins_it->guard.is_true()) + { + xmlt &g = ins.new_element("guard"); + irepconverter.reference_convert(ins_it->guard, g); + } + xmlt &tgt = ins.new_element("targets"); + for(goto_programt::instructiont::targetst::const_iterator + gt_it=ins_it->targets.begin(); + gt_it!=ins_it->targets.end(); + gt_it++) + { + tmp.str(""); + tmp << (*gt_it)->target_number; + tgt.new_element("target").data = tmp.str(); + } + break; + } + + case ASSUME: + { + ins.name = "assume"; + xmlt &g = ins.new_element("guard"); + irepconverter.reference_convert(ins_it->guard, g); + const irep_idt &comment=ins_it->location.get("comment"); + if(comment!="") + ins.new_element("comment").data=id2string(comment); + break; + } + + case ASSERT: + { + ins.name = "assert"; + xmlt &g = ins.new_element("guard"); + irepconverter.reference_convert(ins_it->guard, g); + const irep_idt &comment=ins_it->location.get("comment"); + if(comment!="") + ins.new_element("comment").data=id2string(comment); + break; + } + + case SKIP: + ins.name = "skip"; + break; + + case END_FUNCTION: + ins.name = "end_function"; + break; + + case LOCATION: + ins.name = "location"; + break; + + case DEAD: + ins.name = "dead"; + break; + + case ATOMIC_BEGIN: + ins.name = "atomic_begin"; + break; + + case ATOMIC_END: + ins.name = "atomic_end"; + break; + + case RETURN: + { + ins.name = "return"; + xmlt &c = ins.new_element("code"); + irepconverter.reference_convert(ins_it->code, c); + break; + } + + case OTHER: + { + ins.name = "instruction"; + xmlt &c = ins.new_element("code"); + irepconverter.reference_convert(ins_it->code, c); + break; + } + + case ASSIGN: + { + ins.name = "assign"; + xmlt &c = ins.new_element("code"); + irepconverter.reference_convert(ins_it->code, c); + break; + } + + case FUNCTION_CALL: + { + ins.name = "functioncall"; + xmlt &c = ins.new_element("code"); + irepconverter.reference_convert(ins_it->code, c); + break; + } + + case START_THREAD: + { + ins.name = "thread_start"; + xmlt &tgt = ins.new_element("targets"); + if(ins_it->targets.size()==1) + { + tmp.str(""); + tmp << ins_it->targets.front()->target_number; + tgt.new_element("target").data = tmp.str(); + } + break; + } + + case END_THREAD: + ins.name = "thread_end"; + break; + + default: + ins.name = "unknown"; + break; + } + + if(ins_it->function!="") + { + xmlt &fnc=ins.new_element("function"); + fnc.data=ins_it->function.as_string(); + } + } +} + +/*******************************************************************\ + +Function: xml_goto_program_convertt::convert + + Inputs: an xml structure and a goto program to fill + + Outputs: none + + Purpose: constructs the goto program according to the xml structure + and the namespace into the given goto program object. + +\*******************************************************************/ +void xml_goto_program_convertt::convert( const xmlt& xml, + goto_programt& goto_program) +{ + goto_program.clear(); + goto_programt::instructionst &instructions = goto_program.instructions; + + xmlt::elementst::const_iterator it = xml.elements.begin(); + for (; it != xml.elements.end(); it++) + { + goto_programt::targett inst = goto_program.add_instruction(); + inst->targets.clear(); + + if (it->name=="goto") + { + inst->type = GOTO; + } + else if (it->name=="assume") + { + inst->type = ASSUME; + } + else if (it->name=="assert") + { + inst->type = ASSERT; + } + else if (it->name=="skip") + { + inst->type = SKIP; + } + else if (it->name=="end_function") + { + inst->type = END_FUNCTION; + } + else if (it->name=="location") + { + inst->type = LOCATION; + } + else if (it->name=="dead") + { + inst->type = DEAD; + } + else if (it->name=="atomic_begin") + { + inst->type = ATOMIC_BEGIN; + } + else if (it->name=="atomic_end") + { + inst->type = ATOMIC_END; + } + else if (it->name=="return") + { + inst->make_return(); + } + else if (it->name=="instruction") // OTHER + { + inst->make_other(); + } + else if (it->name=="assign") // OTHER + { + inst->make_other(); + inst->type = ASSIGN; + } + else if (it->name=="functioncall") // OTHER + { + inst->make_other(); + inst->type = FUNCTION_CALL; + } + else if (it->name=="thread_start") + { + inst->type = START_THREAD; + } + else if (it->name=="thread_end") + { + inst->type = END_THREAD; + } + else + { + std::cout << "Unknown instruction type encountered (" << it->name << ")"; + std::cout << std::endl; + return; + } + + xmlt::elementst::const_iterator eit = it->elements.begin(); + for (; eit != it->elements.end(); eit++) + { + if (eit->name=="location") + { + irepconverter.convert(*eit, inst->location); + irepconverter.resolve_references(inst->location); + } + else if (eit->name=="variables") + { + } + else if (eit->name=="labels") + { + xmlt::elementst::const_iterator lit = eit->elements.begin(); + for (; lit != eit->elements.end(); lit++) + { + if (lit->name=="label") + { + std::string ls = lit->get_attribute("name"); + inst->labels.push_back(ls); + } + else + { + std::cout << "Unknown node in labels section." << std::endl; + return; + } + } + } + else if (eit->name=="guard") + { + inst->guard.remove("value"); + irepconverter.convert(*eit, inst->guard); + irepconverter.resolve_references(inst->guard); + } + else if (eit->name=="code") + { + irepconverter.convert(*eit, inst->code); + irepconverter.resolve_references(inst->code); + } + else if (eit->name=="targets") + { + // Don't do anything here, we'll need a second run for that + } + else if (eit->name=="comment") + { + inst->location.set("comment", eit->data); + } + else if (eit->name=="function") + { + inst->function=eit->data; + } + } + } + + // assign line numbers + goto_program.compute_location_numbers(); + + // second run, for targets + goto_programt::targett ins_it = instructions.begin(); + it = xml.elements.begin(); + for (; it != xml.elements.end() && ins_it!=instructions.end(); it++) + { + xmlt::elementst::const_iterator eit = it->elements.begin(); + for (; eit != it->elements.end(); eit++) + { + if (eit->name=="targets") + { + xmlt::elementst::const_iterator tit = eit->elements.begin(); + for (; tit != eit->elements.end(); tit++) + { + if (tit->name=="target") + { + goto_programt::targett tins = + find_instruction(xml, instructions, tit->data); + if (tins != instructions.end()) + { + // Here we insert the iterators that somehow seem + // to be strange afterwards (see line 87) + ins_it->targets.push_back(tins); + } + else + { + std::cout << "Warning: instruction not found when " + "resolving target links." << std::endl; + } + } + else + { + std::cout << "Unknown node in targets section." << std::endl; + return; + } + } + } + } + ins_it++; + } + + // resolve links + goto_program.update(); + + //std::cout << "TNI: " << goto_program.target_numbers.size() << std::endl; +} + +/*******************************************************************\ + +Function: xml_goto_program_convertt::find_instruction + + Inputs: a target label string, the instructions list and an xml program + + Outputs: iterator to the found instruction or .end() + + Purpose: finds the index of the instruction labelled with the given + target label in the given xml-program + +\*******************************************************************/ +goto_programt::targett +xml_goto_program_convertt::find_instruction( const xmlt &xml, + goto_programt::instructionst &instructions, + const std::string &label) +{ + goto_programt::targett ins_it = instructions.begin(); + xmlt::elementst::const_iterator it = xml.elements.begin(); + for (; it != xml.elements.end() && ins_it!=instructions.end(); it++) + { + if (label==it->get_attribute("targetlabel")) + return ins_it; + ins_it++; + } + return instructions.end(); +} + diff --git a/src/goto-cc/xml_binaries/xml_goto_program_hashing.h b/src/goto-cc/xml_binaries/xml_goto_program_hashing.h new file mode 100644 index 00000000000..d2857ee0cc1 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_goto_program_hashing.h @@ -0,0 +1,39 @@ +/*******************************************************************\ + +Module: Convert goto programs into xml structures and back (with + irep hashing) + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#ifndef XML_GOTO_PROGRAM_H_ +#define XML_GOTO_PROGRAM_H_ + +#include +#include + +#include "xml_irep_hashing.h" + +class xml_goto_program_convertt { + private: + xml_irep_convertt irepconverter; + public: + xml_goto_program_convertt(xml_irep_convertt::ireps_containert &ic) : + irepconverter(ic) {}; + + void convert(const goto_programt&, xmlt&); + void convert(const xmlt&, goto_programt&); + + goto_programt::targett + find_instruction( const xmlt &, + goto_programt::instructionst &, + const std::string &); +}; + + + + +#endif /*XML_GOTO_PROGRAM_H_*/ diff --git a/src/goto-cc/xml_binaries/xml_irep_hashing.cpp b/src/goto-cc/xml_binaries/xml_irep_hashing.cpp new file mode 100644 index 00000000000..4ebd8973338 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_irep_hashing.cpp @@ -0,0 +1,396 @@ +/*******************************************************************\ + +Module: XML-irep conversions with hashing + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#include + +#include "xml_irep_hashing.h" +#include "string_hash.h" + +/*******************************************************************\ + +Function: xml_irep_convertt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void xml_irep_convertt::convert( + const irept &irep, + xmlt &xml) +{ + if(irep.id()!="nil") + xml.new_element("id").data=irep.id_string(); + + forall_irep(it, irep.get_sub()) + { + xmlt &x_sub=xml.new_element("s"); + reference_convert(*it, x_sub); + } + + forall_named_irep(it, irep.get_named_sub()) + { + xmlt &x_nsub=xml.new_element("ns"); + x_nsub.set_attribute("n", name2string(it->first)); + reference_convert(it->second, x_nsub); + } + + forall_named_irep(it, irep.get_comments()) + { + xmlt &x_com = xml.new_element("c"); + x_com.set_attribute("n", name2string(it->first)); + reference_convert(it->second, x_com); + } +} + +/*******************************************************************\ + +Function: xml_irep_convertt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void xml_irep_convertt::convert( + const xmlt &xml, + irept &irep) +{ + irep.id("nil"); + xmlt::elementst::const_iterator it = xml.elements.begin(); + for (; it != xml.elements.end(); it++) + { + if (it->name=="R") { + irep.id("__REFERENCE__"); + irep.set("REF", it->data); + } + else if (it->name=="id") + { + irep.id(it->data); + } + else if (it->name=="ns") + { + irept r; + convert(*it, r); + std::string named_name = it->get_attribute("n"); + irep.move_to_named_sub(named_name, r); + } + else if (it->name=="s") + { + irept r; + convert(*it, r); + irep.move_to_sub(r); + } + else if (it->name=="c") + { + irept r; + convert(*it, r); + std::string named_name = it->get_attribute("n"); + irep.move_to_named_sub(named_name, r); + } + else + { + // Should not happen + std::cout << "Unknown sub found (" << it->name << "); malformed xml?"; + std::cout << std::endl; + } + } +} + +/*******************************************************************\ + +Function: xml_irep_convertt::reference_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void xml_irep_convertt::reference_convert( + const irept &irep, + xmlt &xml) +{ + xmlt &ir = xml.new_element("R"); + + ireps_containert::content_containert::const_iterator fi = + find_irep_by_content(irep); + if (fi==ireps_container.content_container.end()) + { + unsigned id = ireps_container.id_replace_map[add_with_childs(irep)]; + ir.data = long_to_string(id); + } else { + ir.data = long_to_string( + ireps_container.id_replace_map[fi->second]); + } +} + +/*******************************************************************\ + +Function: xml_irep_convertt::add_with_childs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +unsigned long xml_irep_convertt::add_with_childs(const irept &iwi) +{ + unsigned long id = insert((unsigned long)&iwi, iwi); + if (id!=(unsigned long)&iwi) return id; + + forall_irep(it, iwi.get_sub()) + { + ireps_containert::content_containert::const_iterator fi = + find_irep_by_content(*it); + if (fi==ireps_container.content_container.end()) + { + add_with_childs(*it); + } + } + forall_named_irep(it, iwi.get_named_sub()) + { + ireps_containert::content_containert::const_iterator fi = + find_irep_by_content(it->second); + if (fi==ireps_container.content_container.end()) + { + add_with_childs(it->second); + } + } + forall_named_irep(it, iwi.get_comments()) + { + ireps_containert::content_containert::const_iterator fi = + find_irep_by_content(it->second); + if (fi==ireps_container.content_container.end()) + { + add_with_childs(it->second); + } + } + return id; +} + +/*******************************************************************\ + +Function: xml_irep_convertt::resolve_references + + Inputs: none + + Outputs: none + + Purpose: resolves references to ireps from an irep after reading + an irep hash map into memory. + +\*******************************************************************/ + +void xml_irep_convertt::resolve_references( const irept &cur ) +{ + if (cur.id() == "__REFERENCE__") + { + unsigned long id = string_to_long(cur.get_string("REF")); + ireps_containert::id_containert::const_iterator itr = find_irep_by_id(id); + if (itr==ireps_container.id_container.end()) + { + std::cout << "Warning: can't resolve irep reference (sub " + << cur.get("REF") << ")" << std::endl; + } + else + { + irept &curX = const_cast(cur); + curX = itr->second; + } + } + + forall_irep(iti, cur.get_sub()) + resolve_references(*iti); + + forall_named_irep(iti, cur.get_named_sub()) + resolve_references(iti->second); + + forall_named_irep(iti, cur.get_comments()) + resolve_references(iti->second); + +} + +/*******************************************************************\ + +Function: xml_irep_convertt::long_to_string + + Inputs: an irep pointer + + Outputs: a new string + + Purpose: converts the hash value to a readable string + +\*******************************************************************/ +std::string xml_irep_convertt::long_to_string(const unsigned long l) { + std::stringstream s; + s << std::hex << l; + return s.str(); +} + +/*******************************************************************\ + +Function: xml_irep_convertt::string_to_long + + Inputs: a string + + Outputs: an unsigned long + + Purpose: converts the string to an unsigned long that used to give + a pointer to an irep in an old compilation + +\*******************************************************************/ +unsigned long xml_irep_convertt::string_to_long(const std::string &s) { + std::stringstream ss(s); + unsigned long res=0; + ss >> std::hex >> res; + return res; +} + +/*******************************************************************\ + +Function: xml_irep_convertt::find_irep_by_id + + Inputs: an id + + Outputs: an iterator into the ireps hash set + + Purpose: finds an irep in the ireps hash set by its id + +\*******************************************************************/ +xml_irep_convertt::ireps_containert::id_containert::const_iterator +xml_irep_convertt::find_irep_by_id(const unsigned int id) { + return ireps_container.id_container.find(id); +} + +/*******************************************************************\ + +Function: xml_irep_convertt::find_irep_by_content + + Inputs: an irep + + Outputs: an iterator into the ireps hash set + + Purpose: finds an irep in the ireps hash set by checking contents + +\*******************************************************************/ +xml_irep_convertt::ireps_containert::content_containert::const_iterator +xml_irep_convertt::find_irep_by_content(const irept &irep) { + return ireps_container.content_container.find(irep); +} + +/*******************************************************************\ + +Function: xml_irep_convertt::insert + + Inputs: an unsigned long and an irep + + Outputs: true on success, false otherwise + + Purpose: inserts an irep into the hashtable + +\*******************************************************************/ +unsigned long xml_irep_convertt::insert( + unsigned long id, + const irept& i) +{ + ireps_containert::content_containert::const_iterator sit; + sit = find_irep_by_content(i); + if (sit==ireps_container.content_container.end()) { + ireps_container.content_container.insert( + std::pair(i, id)); + + if( ireps_container.id_container.insert( + std::pair(id, i) + ).second ) { + ireps_container.id_replace_map[id] = + ireps_container.id_container.size(); + } + + return id; + } else { + return sit->second; + } +} + +/*******************************************************************\ + +Function: xml_irep_convertt::insert + + Inputs: a string and an irep + + Outputs: true on success, false otherwise + + Purpose: inserts an irep into the hashtable + +\*******************************************************************/ +unsigned long xml_irep_convertt::insert( + const std::string &id, + const irept& i) +{ + return insert(string_to_long(id), i); +} + +/*******************************************************************\ + +Function: xml_irep_convertt::convert_map + + Inputs: an xml node + + Outputs: nothing + + Purpose: converts the current hash map of ireps into the given xml + structure + +\*******************************************************************/ +void xml_irep_convertt::convert_map(xmlt &xml) { + ireps_containert::id_containert::iterator hit = + ireps_container.id_container.begin(); + for (; hit!=ireps_container.id_container.end(); hit++) { + xmlt &xmlhe = xml.new_element("irep"); + xmlhe.set_attribute("id", long_to_string( + ireps_container.id_replace_map[hit->first])); + convert(hit->second, xmlhe); + } +} + +/*******************************************************************\ + +Function: xml_irep_convertt::output_map + + Inputs: an output stream + + Outputs: nothing + + Purpose: converts the current hash map of ireps into xml nodes and + outputs them to the stream + +\*******************************************************************/ +void xml_irep_convertt::output_map(std::ostream &out, unsigned indent) { + ireps_containert::id_containert::iterator hit = + ireps_container.id_container.begin(); + for (; hit!=ireps_container.id_container.end(); hit++) { + xmlt xmlhe("irep"); + xmlhe.set_attribute("id", long_to_string( + ireps_container.id_replace_map[hit->first])); + convert(hit->second, xmlhe); + xmlhe.output(out, indent); + } +} diff --git a/src/goto-cc/xml_binaries/xml_irep_hashing.h b/src/goto-cc/xml_binaries/xml_irep_hashing.h new file mode 100644 index 00000000000..a1573cfdc02 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_irep_hashing.h @@ -0,0 +1,95 @@ +/*******************************************************************\ + +Module: XML-irep conversions with hashing + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#ifndef XML_IREP_HASHING_H_ +#define XML_IREP_HASHING_H_ + +#include +#include +#include + +class xml_irep_convertt { + private: + + struct ul_hash + { + unsigned short operator()(const unsigned long l) const + { + return (l & 0xFFFF); + } + }; + struct ul_eq + { + bool operator()(const unsigned long l, const unsigned long r) const + { + return (l==r); + } + }; + struct irep_full_hash + { + size_t operator()(const irept &i) const + { + return i.full_hash(); + } + }; + struct irep_content_eq + { + bool operator()(const irept &l, const irept &r) const + { + return full_eq(l,r); + } + }; + + public: + class ireps_containert { + public: + typedef hash_map_cont id_containert; + id_containert id_container; + typedef hash_map_cont content_containert; + content_containert content_container; + typedef std::map id_replace_mapt; + id_replace_mapt id_replace_map; + + void clear( void ) { + id_container.clear(); + content_container.clear(); + id_replace_map.clear(); + } + }; + + xml_irep_convertt(ireps_containert& ic) : ireps_container(ic) {}; + + unsigned long insert(unsigned long, const irept&); + unsigned long insert(const std::string&, const irept&); + + void convert(const irept &irep, xmlt &xml); + void convert(const xmlt &xml, irept &irep); + void reference_convert(const irept &irep, xmlt &xml); + void resolve_references( const irept &cur ); + + void convert_map(xmlt &xml); + void output_map(std::ostream &out, unsigned indent); + + void clear( void ) { ireps_container.clear(); } + private: + ireps_containert& ireps_container; + + ireps_containert::id_containert::const_iterator + find_irep_by_id(const unsigned int); + ireps_containert::content_containert::const_iterator + find_irep_by_content(const irept &irep); + + std::string long_to_string(const unsigned long); + unsigned long string_to_long(const std::string &); + + unsigned long add_with_childs(const irept&); +}; + +#endif /*XML_IREP_HASHING_H_*/ diff --git a/src/goto-cc/xml_binaries/xml_symbol.cpp b/src/goto-cc/xml_binaries/xml_symbol.cpp new file mode 100644 index 00000000000..9655c075d38 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_symbol.cpp @@ -0,0 +1,142 @@ +/*******************************************************************\ + +Module: Compile and link source and object files. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#include "xml_irep.h" +#include "xml_symbol.h" + +/*******************************************************************\ + +Function: convert + + Inputs: a symbol and an xml node + + Outputs: none + + Purpose: converts a symbol to an xml symbol node + +\*******************************************************************/ + +void convert(const symbolt& sym, xmlt &root) +{ + xmlt &xmlsym = root.new_element("symbol"); + xmlsym.set_attribute("name", id2string(sym.name)); + + xmlt &xmltype = xmlsym.new_element("type"); + convert(sym.type, xmltype); + + xmlt &xmlval = xmlsym.new_element("value"); + if(!sym.is_type && sym.type.id() == "code" && !sym.value.is_nil()) + xmlval.data = "compiled"; // only for implemented functions + else + convert(sym.value, xmlval); + + xmlt &flags = xmlsym.new_element("flags"); + + flags.set_attribute_bool("lvalue", sym.lvalue); + flags.set_attribute_bool("static_lifetime", sym.static_lifetime); + flags.set_attribute_bool("file_local", sym.file_local); + flags.set_attribute_bool("theorem", sym.theorem); + flags.set_attribute_bool("thread_local", sym.thread_local); + flags.set_attribute_bool("type", sym.is_type); + flags.set_attribute_bool("extern", sym.is_extern); + flags.set_attribute_bool("input", sym.is_input); + flags.set_attribute_bool("output", sym.is_output); + flags.set_attribute_bool("macro", sym.is_macro); + flags.set_attribute_bool("actual", sym.is_actual); + flags.set_attribute_bool("binding", sym.binding); + flags.set_attribute_bool("free_var", sym.free_var); + flags.set_attribute_bool("statevar", sym.is_statevar); + + xmlt &mode = flags.new_element("mode"); + mode.data = id2string(sym.mode); + + flags.new_element("base_name").data=id2string(sym.base_name); + flags.new_element("module").data=id2string(sym.module); + + if (sym.pretty_name.size()>0) + flags.new_element("pretty_name").data=id2string(sym.pretty_name); + + xmlt &xmlloc = xmlsym.new_element("location"); + convert(sym.location, xmlloc); + xmlloc.name = "location"; // convert overwrote this +} + +/*******************************************************************\ + +Function: convert + + Inputs: an xml node and a symbol + + Outputs: none + + Purpose: converts an xml symbol node to a symbol + +\*******************************************************************/ + +void convert(const xmlt &xmlsym, symbolt& symbol) +{ + symbol.name=xmlsym.get_attribute("name"); + + for(xmlt::elementst::const_iterator + it=xmlsym.elements.begin(); + it!=xmlsym.elements.end(); + it++) + { + if (it->name=="type") + { + convert(*it, symbol.type); + } + else if (it->name=="value") + { + if (it->data=="compiled") + { + symbol.value.id("code"); + } + else + { + convert(*it, symbol.value); + } + } + else if (it->name=="flags") + { + symbol.lvalue = it->get_attribute_bool("lvalue"); + symbol.static_lifetime = it->get_attribute_bool("static_lifetime"); + symbol.file_local = it->get_attribute_bool("file_local"); + symbol.theorem = it->get_attribute_bool("theorem"); + symbol.thread_local = it->get_attribute_bool("thread_local"); + symbol.is_type = it->get_attribute_bool("type"); + symbol.is_extern = it->get_attribute_bool("extern"); + symbol.is_input = it->get_attribute_bool("input"); + symbol.is_output = it->get_attribute_bool("output"); + symbol.is_macro = it->get_attribute_bool("macro"); + symbol.is_actual = it->get_attribute_bool("actual"); + symbol.binding = it->get_attribute_bool("binding"); + symbol.free_var = it->get_attribute_bool("free_var"); + symbol.is_statevar = it->get_attribute_bool("statevar"); + + for(xmlt::elementst::const_iterator + fit=it->elements.begin(); + fit!=it->elements.end(); + fit++) + { + if(fit->name=="mode") + symbol.mode=fit->data; + else if(fit->name=="base_name") + symbol.base_name=fit->data; + else if(fit->name=="module") + symbol.module=fit->data; + } + } + else if(it->name=="location") + { + convert(*it, symbol.location); + } + } +} diff --git a/src/goto-cc/xml_binaries/xml_symbol.h b/src/goto-cc/xml_binaries/xml_symbol.h new file mode 100644 index 00000000000..8076e94405a --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_symbol.h @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Converts symbols to xml structures and back. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#ifndef XML_SYMBOL_H_ +#define XML_SYMBOL_H_ + +#include +#include + +void convert(const symbolt &, xmlt &); +void convert(const xmlt &, symbolt &); + +#endif /*XML_SYMBOL_H_*/ diff --git a/src/goto-cc/xml_binaries/xml_symbol_hashing.cpp b/src/goto-cc/xml_binaries/xml_symbol_hashing.cpp new file mode 100644 index 00000000000..b733957d4c2 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_symbol_hashing.cpp @@ -0,0 +1,53 @@ +/*******************************************************************\ + +Module: XML-symbol conversions with irep hashing + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#include "xml_symbol_hashing.h" +#include "xml_irep_hashing.h" + +/*******************************************************************\ + +Function: xml_symbol_convertt::convert + + Inputs: a symbol and an xml node + + Outputs: none + + Purpose: converts a symbol to an xml symbol node + +\*******************************************************************/ + +void xml_symbol_convertt::convert(const symbolt& sym, xmlt &root) +{ + xmlt &xmlsym = root.new_element("symbol"); + irepcache.push_back(irept()); + sym.to_irep(irepcache.back()); + irepconverter.reference_convert(irepcache.back(), xmlsym); +} + +/*******************************************************************\ + +Function: xml_symbol_convertt::convert + + Inputs: an xml node and a symbol + + Outputs: none + + Purpose: converts an xml symbol node to a symbol + +\*******************************************************************/ + +void xml_symbol_convertt::convert(const xmlt &xmlsym, symbolt& symbol) +{ + irept t; + + irepconverter.convert(xmlsym, t); + irepconverter.resolve_references(t); + symbol.from_irep(t); +} diff --git a/src/goto-cc/xml_binaries/xml_symbol_hashing.h b/src/goto-cc/xml_binaries/xml_symbol_hashing.h new file mode 100644 index 00000000000..83d24e3f9b8 --- /dev/null +++ b/src/goto-cc/xml_binaries/xml_symbol_hashing.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: XML-symbol conversions with irep hashing + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#ifndef XML_SYMBOL_HASHING_H_ +#define XML_SYMBOL_HASHING_H_ + +#include +#include + +#include "xml_irep_hashing.h" + +class xml_symbol_convertt { + private: + xml_irep_convertt irepconverter; + std::list irepcache; + + public: + xml_symbol_convertt(xml_irep_convertt::ireps_containert &ic) : + irepconverter(ic) {}; + + void convert(const symbolt &, xmlt &); + void convert(const xmlt &, symbolt &); +}; + +#endif /*XML_SYMBOL_HASHING_H_*/ diff --git a/src/goto-instrument/Makefile b/src/goto-instrument/Makefile new file mode 100644 index 00000000000..2c611ae9169 --- /dev/null +++ b/src/goto-instrument/Makefile @@ -0,0 +1,48 @@ +SRC = main.cpp parseoptions.cpp document_claims.cpp languages.cpp \ + uninitialized.cpp uninitialized_domain.cpp full_slicer.cpp \ + object_id.cpp show_locations.cpp points_to.cpp +OBJ = $(SRC:.cpp=$(OBJEXT)) \ + ../ansi-c/ansi-c$(LIBEXT) \ + ../big-int/bigint$(OBJEXT) \ + ../goto-programs/goto-programs$(LIBEXT) \ + ../goto-symex/goto-symex$(LIBEXT) \ + ../pointer-analysis/pointer-analysis$(LIBEXT) \ + ../langapi/langapi$(LIBEXT) \ + ../util/util$(LIBEXT) + +INCLUDES= -I .. -I ../util + +LIBS = + +include ../config.inc +include ../common + +all: goto-instrument$(EXEEXT) + +ifdef MODULE_CPP + OBJ += ../cpp/cpp$(LIBEXT) + CXXFLAGS += -DHAVE_CPP +endif + +ifdef MODULE_JAVA + OBJ += ../java/java$(LIBEXT) + CXXFLAGS += -DHAVE_JAVA +endif + +ifdef MODULE_SPECC + OBJ += ../specc/specc$(LIBEXT) + CXXFLAGS += -DHAVE_SPECC +endif + +ifdef MODULE_PHP + OBJ += ../php/php$(LIBEXT) + CXXFLAGS += -DHAVE_PHP +endif + +############################################################################### + +goto-instrument$(EXEEXT): $(OBJ) + $(CXX) $(LINKFLAGS) -o $@ $(OBJ) $(LIBS) + +clean: + rm -f $(SRC:.cpp=$(OBJEXT)) goto-instrument$(EXEEXT) diff --git a/src/goto-instrument/document_claims.cpp b/src/goto-instrument/document_claims.cpp new file mode 100644 index 00000000000..01c7fa79bf7 --- /dev/null +++ b/src/goto-instrument/document_claims.cpp @@ -0,0 +1,348 @@ +/*******************************************************************\ + +Module: Subgoal Documentation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include "document_claims.h" + +#define MAXWIDTH 62 + +struct linet +{ + std::string text; + int line_number; +}; + +/*******************************************************************\ + +Function: strip_space + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strip_space(std::list &lines) +{ + unsigned strip=50; + + for(std::list::const_iterator it=lines.begin(); + it!=lines.end(); it++) + { + for(unsigned j=0; jtext.size(); j++) + if(it->text[j]!=' ') + { + strip=j; + break; + } + } + + if(strip!=0) + { + for(std::list::iterator it=lines.begin(); + it!=lines.end(); it++) + { + if(it->text.size()>=strip) + it->text=std::string(it->text, strip, std::string::npos); + + if(it->text.size()>=MAXWIDTH) + it->text=std::string(it->text, 0, MAXWIDTH); + } + } +} + +/*******************************************************************\ + +Function: escape_latex + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string escape_latex(const std::string &s, bool alltt) +{ + std::string dest; + + for(unsigned i=0; i lines; + + for(int l=line_start; l<=line_end && in; l++) + { + lines.push_back(linet()); + + std::string &line=lines.back().text; + std::getline(in, line); + + if(!line.empty() && line[line.size()-1]=='\r') + line.resize(line.size()-1); + + lines.back().line_number=l; + } + + // remove empty lines at the end and at the beginning + + for(std::list::iterator it=lines.begin(); + it!=lines.end();) + { + if(is_empty(it->text)) + it=lines.erase(it); + else + break; + } + + for(std::list::iterator it=lines.end(); + it!=lines.begin();) + { + it--; + + if(is_empty(it->text)) + it=lines.erase(it); + else + break; + } + + // strip space + strip_space(lines); + + // build dest + + for(std::list::iterator it=lines.begin(); + it!=lines.end(); it++) + { + std::string line_no=i2string(it->line_number); + + while(line_no.size()<4) + line_no=" "+line_no; + + std::string tmp=line_no+" "+escape_latex(it->text, true); + + if(it->line_number==line_int) + tmp=emphasize(tmp); + + dest+=tmp+"\n"; + } +} + +/*******************************************************************\ + +Function: document_claims + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +struct doc_claimt +{ + std::set comment_set; +}; + +void document_claims( + const namespacet &ns, + const goto_functionst &goto_functions, + std::ostream &out) +{ + typedef std::map claim_sett; + claim_sett claim_set; + + forall_goto_functions(f_it, goto_functions) + { + const goto_programt &goto_program=f_it->second.body; + + forall_goto_program_instructions(i_it, goto_program) + { + if(i_it->is_assert()) + { + locationt new_location; + + new_location.set("file", i_it->location.get("file")); + new_location.set("line", i_it->location.get("line")); + new_location.set("function", i_it->location.get("function")); + + claim_set[new_location].comment_set. + insert(i_it->location.get("comment")); + } + } + } + + for(claim_sett::const_iterator it=claim_set.begin(); + it!=claim_set.end(); it++) + { + std::string code; + const irept &location=it->first; + + get_code(location, code); + + out << "\\claimlocation{File " + << escape_latex(location.get_string("file"), false) + << " function " + << escape_latex(location.get_string("function"), false) + << "}" << std::endl; + + out << std::endl; + + for(std::set::const_iterator + s_it=it->second.comment_set.begin(); + s_it!=it->second.comment_set.end(); + s_it++) + out << "\\claim{" << escape_latex(id2string(*s_it), false) + << "}" << std::endl; + + out << std::endl; + + out << "\\begin{alltt}\\claimcode\n" + << code + << "\\end{alltt}\n"; + + out << std::endl; + out << std::endl; + } +} diff --git a/src/goto-instrument/document_claims.h b/src/goto-instrument/document_claims.h new file mode 100644 index 00000000000..97d76184abb --- /dev/null +++ b/src/goto-instrument/document_claims.h @@ -0,0 +1,16 @@ +/*******************************************************************\ + +Module: Claim Documentation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +void document_claims( + const namespacet &ns, + const goto_functionst &goto_functions, + std::ostream &out); diff --git a/src/goto-instrument/full_slicer.cpp b/src/goto-instrument/full_slicer.cpp new file mode 100644 index 00000000000..55fdeef9220 --- /dev/null +++ b/src/goto-instrument/full_slicer.cpp @@ -0,0 +1,218 @@ +/*******************************************************************\ + +Module: Slicing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "full_slicer.h" +#include "full_slicer_class.h" +#include "object_id.h" + +/*******************************************************************\ + +Function: full_slicert::fixedpoint + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void full_slicert::fixedpoint() +{ + queuet queue; + + for(cfgt::entry_mapt::iterator + e_it=cfg.entry_map.begin(); + e_it!=cfg.entry_map.end(); + e_it++) + if(e_it->first->is_assert()) + { + get_objects(e_it->first->guard, e_it->second.required_objects); + queue.push(&e_it->second); + } + + while(!queue.empty()) + { + cfgt::iterator e=queue.top(); + queue.pop(); + + // look at required_objects + object_id_sett required_objects=transform(e); + + for(cfgt::entriest::const_iterator + p_it=e->predecessors.begin(); + p_it!=e->predecessors.end(); + p_it++) + { + (*p_it)->required_objects.insert( + required_objects.begin(), required_objects.end()); + + queue.push(*p_it); + } + } +} + +/*******************************************************************\ + +Function: full_slicert::slice + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void full_slicert::slice(goto_functionst &goto_functions) +{ + // build the CFG data structure + cfg(goto_functions); + + // compute the fixedpoint + fixedpoint(); + + // now replace those instructions that are not needed + // by skips + + Forall_goto_functions(f_it, goto_functions) + if(f_it->second.body_available) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + const cfgt::entryt &e=cfg.entry_map[i_it]; + if(!e.node_required && + !i_it->is_end_function()) + i_it->make_skip(); + } + } + + // remove the skips + remove_skip(goto_functions); + goto_functions.update(); +} + +/*******************************************************************\ + +Function: full_slicert::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +object_id_sett full_slicert::transform(cfgt::iterator e) +{ + object_id_sett result=e->required_objects; + + const goto_programt::instructiont &instruction=*(e->PC); + + switch(instruction.type) + { + // these introduce control-flow dependencies + case ASSUME: + { + object_id_sett w; + get_objects(instruction.guard, w); + + bool found=false; + + // does it write something we need? + for(object_id_sett::const_iterator + o_it1=w.begin(); o_it1!=w.end(); o_it1++) + if(result.find(*o_it1)!=result.end()) + found=true; + + if(found) + { + e->node_required=true; + result.insert(w.begin(), w.end()); + } + } + break; + + case OTHER: + break; + + case RETURN: + // TODO + break; + + case ASSIGN: + { + const code_assignt &code_assign=to_code_assign(instruction.code); + + object_id_sett w; + get_objects_w(code_assign, w); + + bool found=false; + + // does it write something we need? + for(object_id_sett::const_iterator + o_it1=w.begin(); o_it1!=w.end(); o_it1++) + { + object_id_sett::iterator o_it2=result.find(*o_it1); + if(o_it2!=result.end()) + { + found=true; + // remove what is written + result.erase(o_it2); + } + } + + if(found) + { + e->node_required=true; + // add what it reads + get_objects_r(code_assign, result); + } + } + break; + + case DECL: // this cuts off dependencies + { + object_idt object_id(to_symbol_expr(instruction.code.op0())); + if(result.find(object_id)!=result.end()) + { + e->node_required=true; + result.erase(object_id); + } + } + break; + + case FUNCTION_CALL: + // these are like assignments for the arguments + break; + + default:; + } + + return result; +} + +/*******************************************************************\ + +Function: slicer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void full_slicer(goto_functionst &goto_functions) +{ + full_slicert()(goto_functions); +} diff --git a/src/goto-instrument/full_slicer.h b/src/goto-instrument/full_slicer.h new file mode 100644 index 00000000000..3318d7b15f7 --- /dev/null +++ b/src/goto-instrument/full_slicer.h @@ -0,0 +1,16 @@ +/*******************************************************************\ + +Module: Slicing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_FULL_SLICER_H +#define CPROVER_GOTO_PROGRAMS_FULL_SLICER_H + +#include + +void full_slicer(goto_functionst &goto_functions); + +#endif diff --git a/src/goto-instrument/full_slicer_class.h b/src/goto-instrument/full_slicer_class.h new file mode 100644 index 00000000000..0bb9c29d347 --- /dev/null +++ b/src/goto-instrument/full_slicer_class.h @@ -0,0 +1,56 @@ +/*******************************************************************\ + +Module: Goto Program Slicing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAM_FULL_SLICER_CLASS_H +#define CPROVER_GOTO_PROGRAM_FULL_SLICER_CLASS_H + +#include + +#include +#include + +#include "object_id.h" + +/*******************************************************************\ + + Class: full_slicert + + Purpose: + +\*******************************************************************/ + +class full_slicert +{ +public: + void operator()(goto_functionst &goto_functions) + { + slice(goto_functions); + } + +protected: + struct cfg_nodet + { + cfg_nodet():node_required(false) + { + } + + bool node_required; + object_id_sett required_objects; + }; + + typedef cfg_baset cfgt; + cfgt cfg; + + typedef std::stack queuet; + + void fixedpoint(); + void slice(goto_functionst &goto_functions); + object_id_sett transform(cfgt::iterator); +}; + +#endif diff --git a/src/goto-instrument/languages.cpp b/src/goto-instrument/languages.cpp new file mode 100644 index 00000000000..90d6f5c7507 --- /dev/null +++ b/src/goto-instrument/languages.cpp @@ -0,0 +1,47 @@ +/*******************************************************************\ + +Module: Language Registration + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#ifdef HAVE_CPP +#include +#endif + +#ifdef HAVE_SPECC +#include +#endif + +#include "parseoptions.h" + +/*******************************************************************\ + +Function: goto_instrument_parseoptionst::register_languages + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_instrument_parseoptionst::register_languages() +{ + register_language(new_ansi_c_language); + + #ifdef HAVE_CPP + register_language(new_cpp_language); + #endif + + #ifdef HAVE_SPECC + register_language(new_specc_language); + #endif +} + diff --git a/src/goto-instrument/main.cpp b/src/goto-instrument/main.cpp new file mode 100644 index 00000000000..f311e8dd3b9 --- /dev/null +++ b/src/goto-instrument/main.cpp @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: Main Module + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "parseoptions.h" + +/*******************************************************************\ + +Function: main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int main(int argc, const char **argv) +{ + goto_instrument_parseoptionst parseoptions(argc, argv); + return parseoptions.main(); +} diff --git a/src/goto-instrument/object_id.cpp b/src/goto-instrument/object_id.cpp new file mode 100644 index 00000000000..1e369e4940b --- /dev/null +++ b/src/goto-instrument/object_id.cpp @@ -0,0 +1,132 @@ +/*******************************************************************\ + +Module: Object Identifiers + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "object_id.h" + +/*******************************************************************\ + +Function: get_objects_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typedef enum { LHS_R, LHS_W, READ } get_modet; + +void get_objects_rec( + get_modet mode, + const exprt &expr, + object_id_sett &dest, + const std::string &suffix) +{ + if(expr.id()==ID_symbol) + { + if(mode==LHS_W || mode==READ) + dest.insert(object_idt(to_symbol_expr(expr))); + } + else if(expr.id()==ID_index) + { + const index_exprt &index_expr=to_index_expr(expr); + + if(mode==LHS_R || mode==READ) + get_objects_rec(READ, index_expr.index(), dest, ""); + + if(mode==LHS_R) + get_objects_rec(READ, index_expr.array(), dest, "[]"+suffix); + else + get_objects_rec(mode, index_expr.array(), dest, "[]"+suffix); + } + else if(expr.id()==ID_if) + { + const if_exprt &if_expr=to_if_expr(expr); + + if(mode==LHS_R || mode==READ) + get_objects_rec(READ, if_expr.cond(), dest, ""); + + get_objects_rec(mode, if_expr.true_case(), dest, suffix); + get_objects_rec(mode, if_expr.false_case(), dest, suffix); + } + else if(expr.id()==ID_member) + { + const member_exprt &member_expr=to_member_expr(expr); + + get_objects_rec(mode, member_expr.struct_op(), dest, + "."+id2string(member_expr.get_component_name())+suffix); + } + else if(expr.id()==ID_dereference) + { + const dereference_exprt &dereference_expr= + to_dereference_expr(expr); + + if(mode==LHS_R || mode==READ) + get_objects_rec(READ, dereference_expr.pointer(), dest, ""); + } + else + { + if(mode==LHS_R || mode==READ) + forall_operands(it, expr) + get_objects_rec(READ, *it, dest, ""); + } +} + +/*******************************************************************\ + +Function: get_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void get_objects(const exprt &expr, object_id_sett &dest) +{ + get_objects_rec(READ, expr, dest, ""); +} + +/*******************************************************************\ + +Function: get_objects_r + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void get_objects_r(const code_assignt &assign, object_id_sett &dest) +{ + get_objects_rec(LHS_R, assign.lhs(), dest, ""); + get_objects_rec(READ, assign.rhs(), dest, ""); +} + +/*******************************************************************\ + +Function: get_objects_w + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void get_objects_w(const code_assignt &assign, object_id_sett &dest) +{ + get_objects_rec(LHS_W, assign.lhs(), dest, ""); +} + diff --git a/src/goto-instrument/object_id.h b/src/goto-instrument/object_id.h new file mode 100644 index 00000000000..64e6cc580c5 --- /dev/null +++ b/src/goto-instrument/object_id.h @@ -0,0 +1,51 @@ +/*******************************************************************\ + +Module: Object Identifiers + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_OBJECT_ID_H +#define CPROVER_OBJECT_ID_H + +#include +#include + +#include +#include + +class object_idt +{ +public: + object_idt() { } + + explicit object_idt(const symbol_exprt &symbol_expr) + { + id=symbol_expr.get_identifier(); + } + + friend std::ostream &operator << (std::ostream &out, const object_idt &x) + { + return out << x.id; + } + + friend inline bool operator < (const object_idt &a, const object_idt &b) + { + return a.id < b.id; + } + +protected: + irep_idt id; +}; + +inline std::ostream &operator << (std::ostream &, const object_idt &); +inline bool operator < (const object_idt &a, const object_idt &b); + +typedef std::set object_id_sett; + +void get_objects(const exprt &expr, object_id_sett &dest); +void get_objects_r(const code_assignt &assign, object_id_sett &); +void get_objects_w(const code_assignt &assign, object_id_sett &); + +#endif diff --git a/src/goto-instrument/parseoptions.cpp b/src/goto-instrument/parseoptions.cpp new file mode 100644 index 00000000000..59776e2df91 --- /dev/null +++ b/src/goto-instrument/parseoptions.cpp @@ -0,0 +1,431 @@ +/*******************************************************************\ + +Module: Main Module + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "parseoptions.h" +#include "version.h" +#include "document_claims.h" +#include "uninitialized.h" +#include "full_slicer.h" +#include "show_locations.h" +#include "points_to.h" + +/*******************************************************************\ + +Function: goto_instrument_parseoptionst::set_verbosity + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_instrument_parseoptionst::set_verbosity(messaget &message) +{ + int v=8; + + if(cmdline.isset("verbosity")) + { + v=atoi(cmdline.getval("verbosity")); + if(v<0) + v=0; + else if(v>9) + v=9; + } + + message.set_verbosity(v); +} + +/*******************************************************************\ + +Function: goto_instrument_parseoptionst::doit + + Inputs: + + Outputs: + + Purpose: invoke main modules + +\*******************************************************************/ + +int goto_instrument_parseoptionst::doit() +{ + if(cmdline.isset("version")) + { + std::cout << GOTO_INSTRUMENT_VERSION << std::endl; + return 0; + } + + if(cmdline.args.size()!=1 && cmdline.args.size()!=2) + { + help(); + return 0; + } + + set_verbosity(*this); + + try + { + register_languages(); + + goto_functionst goto_functions; + + get_goto_program(goto_functions); + instrument_goto_program(goto_functions); + + if(cmdline.isset("show-value-sets")) + { + namespacet ns(context); + + status("Function Pointer Removal"); + remove_function_pointers(ns, goto_functions); + + status("Partial Inlining"); + goto_partial_inline(goto_functions, ns, ui_message_handler); + + status("Pointer Analysis"); + value_set_analysist value_set_analysis(ns); + value_set_analysis(goto_functions); + + show_value_sets(get_ui(), goto_functions, value_set_analysis); + return 0; + } + + if(cmdline.isset("show-points-to")) + { + namespacet ns(context); + + status("Function Pointer Removal"); + remove_function_pointers(ns, goto_functions); + + status("Partial Inlining"); + goto_partial_inline(goto_functions, ns, ui_message_handler); + + status("Pointer Analysis"); + points_tot points_to; + points_to(goto_functions); + points_to.output(std::cout); + return 0; + } + + if(cmdline.isset("show-symbol-table")) + { + show_symbol_table(); + return 0; + } + + if(cmdline.isset("show-uninitialized")) + { + show_uninitialized(context, goto_functions, std::cout); + return 0; + } + + if(cmdline.isset("interpreter")) + { + status("Starting interpeter"); + interpreter(context, goto_functions); + return 0; + } + + if(cmdline.isset("show-claims")) + { + const namespacet ns(context); + show_claims(ns, get_ui(), goto_functions); + return 0; + } + + if(cmdline.isset("document-claims")) + { + const namespacet ns(context); + document_claims(ns, goto_functions, std::cout); + return 0; + } + + if(cmdline.isset("show-loops")) + { + show_loop_numbers(get_ui(), goto_functions); + return 0; + } + + if(cmdline.isset("show-goto-functions")) + { + namespacet ns(context); + goto_functions.output(ns, std::cout); + return 0; + } + + if(cmdline.isset("show-locations")) + { + show_locations(get_ui(), goto_functions); + return 0; + } + + if(cmdline.isset("dump-c")) + { + namespacet ns(context); + dump_c(goto_functions, ns, std::cout); + return 0; + } + + // write new binary? + if(cmdline.args.size()==2) + { + status("Writing GOTO program to "+cmdline.args[1]); + + if(write_goto_binary( + cmdline.args[1], context, goto_functions, get_message_handler())) + return 1; + else + return 0; + } + + help(); + return 0; + } + + catch(const char *e) + { + error(e); + return 11; + } + + catch(const std::string e) + { + error(e); + return 11; + } + + catch(int) + { + return 11; + } + + catch(std::bad_alloc) + { + error("Out of memory"); + return 11; + } +} + +/*******************************************************************\ + +Function: goto_instrument_parseoptionst::get_goto_program + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_instrument_parseoptionst::get_goto_program( + goto_functionst &goto_functions) +{ + status("Reading GOTO program from "+cmdline.args[0]); + + if(read_goto_binary(cmdline.args[0], + context, goto_functions, get_message_handler())) + throw 0; + + config.ansi_c.set_from_context(context); +} + +/*******************************************************************\ + +Function: goto_instrument_parseoptionst::instrument_goto_program + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_instrument_parseoptionst::instrument_goto_program( + goto_functionst &goto_functions) +{ + optionst options; + + // check array bounds + if(cmdline.isset("bounds-check")) + options.set_option("bounds-check", true); + else + options.set_option("bounds-check", false); + + // check division by zero + if(cmdline.isset("div-by-zero-check")) + options.set_option("div-by-zero-check", true); + else + options.set_option("div-by-zero-check", false); + + // check overflow/underflow + if(cmdline.isset("overflow-check")) + options.set_option("overflow-check", true); + else + options.set_option("overflow-check", false); + + // check for NaN (not a number) + if(cmdline.isset("nan-check")) + options.set_option("nan-check", true); + else + options.set_option("nan-check", false); + + // check pointers + if(cmdline.isset("pointer-check")) + options.set_option("pointer-check", true); + else + options.set_option("pointer-check", false); + + // check assertions + if(cmdline.isset("no-assertions")) + options.set_option("assertions", false); + else + options.set_option("assertions", true); + + // use assumptions + if(cmdline.isset("no-assumptions")) + options.set_option("assumptions", false); + else + options.set_option("assumptions", true); + + // magic error label + if(cmdline.isset("error-label")) + options.set_option("error-label", cmdline.getval("error-label")); + + namespacet ns(context); + + // add generic checks, if needed + goto_check(ns, options, goto_functions); + + // check for uninitalized local varibles + if(cmdline.isset("uninitialized-check")) + { + status("Adding checks for uninitialized local variables"); + add_uninitialized_locals_assertions(context, goto_functions); + } + + if(cmdline.isset("string-abstraction")) + { + status("String Abstraction"); + string_abstraction(context, + get_message_handler(), goto_functions); + } + + if(cmdline.isset("pointer-check")) + { + status("Function Pointer Removal"); + remove_function_pointers(ns, goto_functions); + + // do partial inlining + status("Partial Inlining"); + goto_partial_inline(goto_functions, ns, ui_message_handler); + + status("Pointer Analysis"); + value_set_analysist value_set_analysis(ns); + value_set_analysis(goto_functions); + + // add pointer checks + status("Adding Pointer Checks"); + pointer_checks( + goto_functions, ns, options, value_set_analysis); + } + + // add failed symbols + add_failed_symbols(context); + + // recalculate numbers, etc. + goto_functions.update(); + + // add loop ids + goto_functions.compute_loop_numbers(); + + // full slice? + if(cmdline.isset("full-slice")) + { + status("Performing a full slice"); + full_slicer(goto_functions); + } +} + +/*******************************************************************\ + +Function: goto_instrument_parseoptionst::help + + Inputs: + + Outputs: + + Purpose: display command line help + +\*******************************************************************/ + +void goto_instrument_parseoptionst::help() +{ + std::cout << + "\n" + "* * Goto-Instrument " GOTO_INSTRUMENT_VERSION " - Copyright (C) 2008-2009 * *\n" + "* * Daniel Kroening * *\n" + "* * kroening@kroening.com * *\n" + "\n" + "Usage: Purpose:\n" + "\n" + " goto-instrument [-?] [-h] [--help] show help\n" + " goto-instrument in out perform instrumentation\n" + "\n" + "Main options:\n" + " --show-loops show the loops in the program\n" + " --show-claims show claims\n" + " --document-claims generate claim documentation\n" + " --show-symbol-table show symbol table\n" + " --show-goto-functions show goto program\n" + " --dump-c generate C source\n" + " --interpreter do concrete execution\n" + "\n" + "Instrumentation options:\n" + " --no-assertions ignore user assertions\n" + " --bounds-check add array bounds checks\n" + " --div-by-zero-check add division by zero checks\n" + " --pointer-check add pointer checks\n" + " --overflow-check add arithmetic over- and underflow checks\n" + " --nan-check add floating-point NaN checks\n" + " --uninitialized-check add checks for uninitialized locals (experimental)\n" + " --error-label label check that label is unreachable\n" + "\n" + "Other options:\n" + " --version show version and exit\n" + " --xml-ui use XML-formatted output\n" + "\n"; +} diff --git a/src/goto-instrument/parseoptions.h b/src/goto-instrument/parseoptions.h new file mode 100644 index 00000000000..139ad401649 --- /dev/null +++ b/src/goto-instrument/parseoptions.h @@ -0,0 +1,60 @@ +/*******************************************************************\ + +Module: Command Line Parsing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_INSTRUMENT_PARSEOPTIONS_H +#define CPROVER_GOTO_INSTRUMENT_PARSEOPTIONS_H + +#include +#include + +#include +#include + +#define GOTO_INSTRUMENT_OPTIONS \ + "(all)(document-claims)(dump-c)" \ + "(bounds-check)(no-bounds-check)" \ + "(pointer-check)(no-pointer-check)" \ + "(leak-check)" \ + "(div-by-zero-check)(no-div-by-zero-check)" \ + "(no-assertions)(no-assumptions)(uninitialized-check)" \ + "(nan-check)(no-nan-check)" \ + "(overflow-check)(no-overflow-check)" \ + "(show-goto-functions)(show-value-sets)" \ + "(show-uninitialized)(show-locations)" \ + "(full-slice)" \ + "(show-symbol-table)(show-claims)(show-points-to)" \ + "(error-label):(string-abstraction)" \ + "(verbosity):(version)(xml-ui)" + +class goto_instrument_parseoptionst: + public parseoptions_baset, + public language_uit +{ +public: + virtual int doit(); + virtual void help(); + + goto_instrument_parseoptionst(int argc, const char **argv): + parseoptions_baset(GOTO_INSTRUMENT_OPTIONS, argc, argv), + language_uit("goto-instrument", cmdline) + { + } + +protected: + virtual void register_languages(); + + void get_goto_program( + goto_functionst &goto_functions); + + void instrument_goto_program( + goto_functionst &goto_functions); + + void set_verbosity(messaget &message); +}; + +#endif diff --git a/src/goto-instrument/points_to.cpp b/src/goto-instrument/points_to.cpp new file mode 100644 index 00000000000..e90b8fe847b --- /dev/null +++ b/src/goto-instrument/points_to.cpp @@ -0,0 +1,116 @@ +/*******************************************************************\ + +Module: Field-sensitive, location-insensitive points-to analysis + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "points_to.h" + +/*******************************************************************\ + +Function: points_tot::fixedpoint + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void points_tot::fixedpoint() +{ + // this loop iterates until fixed-point + + bool added; + + do + { + added=false; + + for(cfgt::entry_mapt::iterator + e_it=cfg.entry_map.begin(); + e_it!=cfg.entry_map.end(); + e_it++) + { + if(transform(&e_it->second)) + added=true; + } + } + while(added); +} + +/*******************************************************************\ + +Function: points_tot::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void points_tot::output(std::ostream &out) const +{ + for(value_mapt::const_iterator + v_it=value_map.begin(); + v_it!=value_map.end(); + v_it++) + { + out << v_it->first << ":"; + + for(object_id_sett::const_iterator + o_it=v_it->second.begin(); + o_it!=v_it->second.end(); + o_it++) + { + out << " " << *o_it; + } + + out << std::endl; + } +} + +/*******************************************************************\ + +Function: points_tot::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool points_tot::transform(cfgt::iterator e) +{ + bool result=false; + const goto_programt::instructiont &instruction=*(e->PC); + + switch(instruction.type) + { + case RETURN: + // TODO + break; + + case ASSIGN: + { + const code_assignt &code_assign=to_code_assign(instruction.code); + + } + break; + + case FUNCTION_CALL: + // these are like assignments for the arguments + break; + + default:; + } + + return result; +} diff --git a/src/goto-instrument/points_to.h b/src/goto-instrument/points_to.h new file mode 100644 index 00000000000..ac7397c95f2 --- /dev/null +++ b/src/goto-instrument/points_to.h @@ -0,0 +1,81 @@ +/*******************************************************************\ + +Module: Field-sensitive, location-insensitive points-to analysis + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_INSTRUMENT_POINTS_TO_H +#define CPROVER_GOTO_INSTRUMENT_POINTS_TO_H + +#include + +#include +#include + +#include "object_id.h" + +/*******************************************************************\ + + Class: points_tot + + Purpose: + +\*******************************************************************/ + +class points_tot +{ +public: + points_tot() + { + } + + inline void operator()(goto_functionst &goto_functions) + { + // build the CFG data structure + cfg(goto_functions); + + // iterate + fixedpoint(); + } + + inline const object_id_sett &operator[](const object_idt &object_id) + { + value_mapt::const_iterator it=value_map.find(object_id); + if(it!=value_map.end()) return it->second; + return empty_set; + } + + void output(std::ostream &out) const; + + inline friend std::ostream &operator << ( + std::ostream &out, const points_tot &points_to) + { + points_to.output(out); + return out; + } + +protected: + struct cfg_nodet + { + cfg_nodet() + { + } + }; + + typedef cfg_baset cfgt; + cfgt cfg; + + typedef std::map value_mapt; + value_mapt value_map; + + void fixedpoint(); + bool transform(cfgt::iterator); + + const object_id_sett empty_set; +}; + +std::ostream &operator << (std::ostream &, const points_tot &); + +#endif diff --git a/src/goto-instrument/show_locations.cpp b/src/goto-instrument/show_locations.cpp new file mode 100644 index 00000000000..31f5a757b73 --- /dev/null +++ b/src/goto-instrument/show_locations.cpp @@ -0,0 +1,93 @@ +/*******************************************************************\ + +Module: Show program locations + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include "show_locations.h" + +/*******************************************************************\ + +Function: show_locations + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_locations( + ui_message_handlert::uit ui, + const irep_idt function_id, + const goto_programt &goto_program) +{ + for(goto_programt::instructionst::const_iterator + it=goto_program.instructions.begin(); + it!=goto_program.instructions.end(); + it++) + { + const locationt &location=it->location; + + switch(ui) + { + case ui_message_handlert::XML_UI: + { + xmlt xml("program_location"); + xml.new_element("function").data=id2string(function_id); + xml.new_element("id").data=i2string(it->location_number); + + xmlt &l=xml.new_element(); + l.name="location"; + + l.new_element("line").data=id2string(location.get_line()); + l.new_element("file").data=id2string(location.get_file()); + l.new_element("function").data=id2string(location.get_function()); + + std::cout << xml << std::endl; + } + break; + + case ui_message_handlert::PLAIN: + std::cout << function_id << " " + << it->location_number << " " + << it->location << std::endl; + break; + + default: + assert(false); + } + } +} + +/*******************************************************************\ + +Function: show_locations + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_locations( + ui_message_handlert::uit ui, + const goto_functionst &goto_functions) +{ + for(goto_functionst::function_mapt::const_iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + show_locations(ui, it->first, it->second.body); +} diff --git a/src/goto-instrument/show_locations.h b/src/goto-instrument/show_locations.h new file mode 100644 index 00000000000..0138477eef6 --- /dev/null +++ b/src/goto-instrument/show_locations.h @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Show program locations + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_SHOW_LOCATIONS_H +#define CPROVER_GOTO_PROGRAMS_SHOW_LOCATIONS_H + +#include + +#include + +void show_locations( + ui_message_handlert::uit ui, + const goto_functionst &goto_functions); + +#endif diff --git a/src/goto-instrument/uninitialized.cpp b/src/goto-instrument/uninitialized.cpp new file mode 100644 index 00000000000..f6cdd12471d --- /dev/null +++ b/src/goto-instrument/uninitialized.cpp @@ -0,0 +1,279 @@ +/*******************************************************************\ + +Module: Detection for Uninitialized Local Variables + +Author: Daniel Kroening + +Date: January 2010 + +\*******************************************************************/ + +#include +#include + +#include "uninitialized_domain.h" + +/*******************************************************************\ + + Class: uninitializedt + + Purpose: + +\*******************************************************************/ + +class uninitializedt +{ +public: + uninitializedt(contextt &_context): + context(_context), + ns(_context), + uninitialized_analysis(ns) + { + } + + void add_assertions(goto_programt &goto_program); + +protected: + contextt &context; + namespacet ns; + uninitialized_analysist uninitialized_analysis; + + // The variables that need tracking, + // i.e., are uninitialized and may be read? + std::set tracking; + + void get_tracking(goto_programt::const_targett i_it); +}; + +/*******************************************************************\ + +Function: uninitializedt::get_tracking + + Inputs: + + Outputs: + + Purpose: which variables need tracking, + i.e., are uninitialized and may be read? + +\*******************************************************************/ + +void uninitializedt::get_tracking(goto_programt::const_targett i_it) +{ + std::list objects=objects_read(*i_it); + + forall_expr_list(o_it, objects) + { + if(o_it->id()=="symbol") + { + const irep_idt &identifier=to_symbol_expr(*o_it).get_identifier(); + const std::set &uninitialized= + uninitialized_analysis[i_it].uninitialized; + if(uninitialized.find(identifier)!=uninitialized.end()) + tracking.insert(identifier); + } + else if(o_it->id()=="dereference") + { + } + } +} + +/*******************************************************************\ + +Function: uninitializedt::add_assertions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void uninitializedt::add_assertions(goto_programt &goto_program) +{ + uninitialized_analysis(goto_program); + + // find out which variables need tracking + + tracking.clear(); + forall_goto_program_instructions(i_it, goto_program) + get_tracking(i_it); + + // add tracking symbols to symbol table + for(std::set::const_iterator + it=tracking.begin(); + it!=tracking.end(); + it++) + { + const symbolt &symbol=ns.lookup(*it); + + symbolt new_symbol; + new_symbol.name=id2string(symbol.name)+"#initialized"; + new_symbol.type=bool_typet(); + new_symbol.base_name=id2string(symbol.base_name)+"#initialized"; + new_symbol.location=symbol.location; + new_symbol.mode=symbol.mode; + new_symbol.module=symbol.module; + new_symbol.thread_local=true; + new_symbol.static_lifetime=false; + new_symbol.file_local=true; + new_symbol.lvalue=true; + + context.move(new_symbol); + } + + Forall_goto_program_instructions(i_it, goto_program) + { + goto_programt::instructiont &instruction=*i_it; + + if(instruction.is_decl()) + { + // if we track it, add declaration and assignment + // for tracking variable! + + const irep_idt &identifier= + to_code_decl(instruction.code).get_identifier(); + + if(tracking.find(identifier)!=tracking.end()) + { + goto_programt::targett i1=goto_program.insert_after(i_it); + goto_programt::targett i2=goto_program.insert_after(i1); + i_it++, i_it++; + + const irep_idt new_identifier= + id2string(identifier)+"#initialized"; + + symbol_exprt symbol_expr; + symbol_expr.set_identifier(new_identifier); + symbol_expr.type()=bool_typet(); + i1->type=DECL; + i1->location=instruction.location; + i1->code=code_declt(symbol_expr); + + i2->type=ASSIGN; + i2->location=instruction.location; + i2->code=code_assignt(symbol_expr, false_exprt()); + } + } + else + { + std::list read=objects_read(instruction); + std::list written=objects_written(instruction); + + // if(instruction.is_function_call()) + //const code_function_callt &code_function_call= + // to_code_function_call(instruction.code); + + // check tracking variables + forall_expr_list(it, read) + { + if(it->id()=="symbol") + { + const irep_idt &identifier=to_symbol_expr(*it).get_identifier(); + const std::set &uninitialized= + uninitialized_analysis[i_it].uninitialized; + + if(uninitialized.find(identifier)!=uninitialized.end()) + { + assert(tracking.find(identifier)!=tracking.end()); + const irep_idt new_identifier=id2string(identifier)+"#initialized"; + + // insert assertion + goto_programt::instructiont assertion; + assertion.type=ASSERT; + assertion.guard=symbol_exprt(new_identifier, bool_typet()); + assertion.location=instruction.location; + assertion.location.set_comment("use of uninitialized local variable"); + assertion.location.set_property("uninitialized local"); + + goto_program.insert_before_swap(i_it, assertion); + i_it++; + } + } + } + + // set tracking variables + forall_expr_list(it, written) + { + if(it->id()=="symbol") + { + const irep_idt &identifier=to_symbol_expr(*it).get_identifier(); + + if(tracking.find(identifier)!=tracking.end()) + { + const irep_idt new_identifier=id2string(identifier)+"#initialized"; + + goto_programt::instructiont assignment; + assignment.type=ASSIGN; + assignment.code=code_assignt( + symbol_exprt(new_identifier, bool_typet()), true_exprt()); + assignment.location=instruction.location; + + goto_program.insert_before_swap(i_it, assignment); + i_it++; + } + } + } + } + } +} + +/*******************************************************************\ + +Function: add_uninitialized_locals_assertions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void add_uninitialized_locals_assertions( + contextt &context, + goto_functionst &goto_functions) +{ + Forall_goto_functions(f_it, goto_functions) + { + uninitializedt uninitialized(context); + + uninitialized.add_assertions(f_it->second.body); + } +} + +/*******************************************************************\ + +Function: show_uninitialized + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_uninitialized( + const class contextt &context, + const goto_functionst &goto_functions, + std::ostream &out) +{ + const namespacet ns(context); + + forall_goto_functions(f_it, goto_functions) + { + if(f_it->second.body_available) + { + out << "////" << std::endl; + out << "//// Function: " << f_it->first << std::endl; + out << "////" << std::endl; + out << std::endl; + uninitialized_analysist uninitialized_analysis(ns); + uninitialized_analysis(f_it->second.body); + uninitialized_analysis.output(f_it->second.body, out); + } + } + +} diff --git a/src/goto-instrument/uninitialized.h b/src/goto-instrument/uninitialized.h new file mode 100644 index 00000000000..094dfb3dace --- /dev/null +++ b/src/goto-instrument/uninitialized.h @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: Detection for Uninitialized Local Variables + +Author: Daniel Kroening + +Date: January 2010 + +\*******************************************************************/ + +#ifndef CPROVER_UNINITALIZED_H +#define CPROVER_UNINITALIZED_H + +#include + +#include + +void add_uninitialized_locals_assertions( + class contextt &context, + goto_functionst &goto_functions); + +void show_uninitialized( + const class contextt &context, + const goto_functionst &goto_functions, + std::ostream &out); + +#endif diff --git a/src/goto-instrument/uninitialized_domain.cpp b/src/goto-instrument/uninitialized_domain.cpp new file mode 100644 index 00000000000..2e9648195f3 --- /dev/null +++ b/src/goto-instrument/uninitialized_domain.cpp @@ -0,0 +1,170 @@ +/*******************************************************************\ + +Module: Detection for Uninitialized Local Variables + +Author: Daniel Kroening + +Date: January 2010 + +\*******************************************************************/ + +#include +#include + +#include "uninitialized_domain.h" + +/*******************************************************************\ + +Function: uninitialized_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void uninitialized_domaint::transform( + const namespacet &ns, + locationt from, + locationt to) +{ + switch(from->type) + { + case DECL: + { + const irep_idt &identifier= + to_code_decl(from->code).get_identifier(); + const symbolt &symbol=ns.lookup(identifier); + + if(!symbol.static_lifetime) + uninitialized.insert(identifier); + } + break; + + default: + { + std::list read=expressions_read(*from); + std::list written=expressions_written(*from); + + forall_expr_list(it, read) find_dirty(ns, *it); + forall_expr_list(it, written) assign(*it); + + // we only care about the *first* uninitalized use + forall_expr_list(it, read) assign(*it); + } + } +} + +/*******************************************************************\ + +Function: uninitialized_domaint::find_dirty + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void uninitialized_domaint::find_dirty( + const namespacet &ns, + const exprt &expr) +{ + if(expr.id()=="address_of") + { + const address_of_exprt &address_of_expr=to_address_of_expr(expr); + if(address_of_expr.object().id()=="symbol") + { + const irep_idt &identifier= + to_symbol_expr(address_of_expr.object()).get_identifier(); + const symbolt &symbol=ns.lookup(identifier); + if(!symbol.static_lifetime) dirty.insert(identifier); + } + } + + forall_operands(it, expr) + find_dirty(ns, *it); +} + +/*******************************************************************\ + +Function: uninitialized_domaint::assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void uninitialized_domaint::assign(const exprt &lhs) +{ + if(lhs.id()=="index") + assign(to_index_expr(lhs).array()); + else if(lhs.id()=="member") + assign(to_member_expr(lhs).struct_op()); + else if(lhs.id()=="symbol") + uninitialized.erase(to_symbol_expr(lhs).get_identifier()); +} + +/*******************************************************************\ + +Function: uninitialized_domaint::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void uninitialized_domaint::output( + const namespacet &ns, + std::ostream &out) const +{ + out << "Uninitialized:" << std::endl; + for(uninitializedt::const_iterator + it=uninitialized.begin(); + it!=uninitialized.end(); + it++) + out << " " << *it << std::endl; + + out << "Dirty:" << std::endl; + for(dirtyt::const_iterator + it=dirty.begin(); + it!=dirty.end(); + it++) + out << " " << *it << std::endl; +} + +/*******************************************************************\ + +Function: uninitialized_domaint::merge + + Inputs: + + Outputs: returns true iff there is s.th. new + + Purpose: + +\*******************************************************************/ + +bool uninitialized_domaint::merge(const uninitialized_domaint &other) +{ + unsigned old_uninitialized=uninitialized.size(); + unsigned old_dirty=dirty.size(); + + uninitialized.insert( + other.uninitialized.begin(), + other.uninitialized.end()); + + dirty.insert(other.dirty.begin(), other.dirty.end()); + + return old_uninitialized!=uninitialized.size() || + old_dirty!=dirty.size(); +} diff --git a/src/goto-instrument/uninitialized_domain.h b/src/goto-instrument/uninitialized_domain.h new file mode 100644 index 00000000000..5d2df4aa1a7 --- /dev/null +++ b/src/goto-instrument/uninitialized_domain.h @@ -0,0 +1,46 @@ +/*******************************************************************\ + +Module: Detection for Uninitialized Local Variables + +Author: Daniel Kroening + +Date: January 2010 + +\*******************************************************************/ + +#include + +class uninitialized_domaint:public domain_baset +{ +public: + // locals that are not initialized + typedef std::set uninitializedt; + uninitializedt uninitialized; + + // locals that might be accessed via a pointer + typedef std::set dirtyt; + dirtyt dirty; + + virtual void transform( + const namespacet &ns, + locationt from, + locationt to); + + virtual void output( + const namespacet &ns, + std::ostream &out) const; + + // returns true iff there is s.th. new + bool merge(const uninitialized_domaint &other); + +protected: + void find_dirty( + const namespacet &ns, + const exprt &expr); + + void assign(const exprt &lhs); +}; + +typedef static_analysist + uninitialized_analysist; + diff --git a/src/goto-instrument/version.h b/src/goto-instrument/version.h new file mode 100644 index 00000000000..ad5055231c9 --- /dev/null +++ b/src/goto-instrument/version.h @@ -0,0 +1 @@ +#define GOTO_INSTRUMENT_VERSION "1.0" diff --git a/src/goto-programs/Makefile b/src/goto-programs/Makefile new file mode 100644 index 00000000000..43ca46262f3 --- /dev/null +++ b/src/goto-programs/Makefile @@ -0,0 +1,42 @@ +SRC = goto_convert.cpp goto_function.cpp goto_main.cpp goto_sideeffects.cpp \ + goto_program.cpp basic_blocks.cpp goto_threads.cpp goto_check.cpp \ + goto_function_pointers.cpp goto_functions.cpp goto_inline.cpp \ + remove_skip.cpp goto_convert_functions.cpp string_instrumentation.cpp \ + builtin_functions.cpp show_claims.cpp destructor.cpp set_claims.cpp \ + slicer.cpp invariant_set.cpp invariant_propagation.cpp \ + add_race_assertions.cpp rw_set.cpp read_goto_binary.cpp \ + invariant_set_domain.cpp static_analysis.cpp string_abstraction.cpp \ + goto_program_serialization.cpp goto_function_serialization.cpp \ + read_bin_goto_object.cpp goto_program_irep.cpp interpreter.cpp \ + interpreter_evaluate.cpp flow_insensitive_analysis.cpp \ + format_strings.cpp loop_numbers.cpp pointer_arithmetic.cpp \ + goto_program_template.cpp write_goto_binary.cpp remove_unreachable.cpp \ + remove_unused_functions.cpp dynamic_memory.cpp dump_c.cpp \ + wp.cpp goto_rw.cpp goto_clean_expr.cpp safety_checker.cpp \ + control_flow_graph.cpp depth_first_spanning_tree.cpp dominator_info.cpp \ + loops.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) + +INCLUDES= -I .. -I ../util + +include ../config.inc +include ../common + +all: goto-programs$(LIBEXT) + +############################################################################### + +goto-programs$(LIBEXT): $(OBJ) + $(LINKLIB) + +test_wp$(EXEEXT): test_wp$(OBJEXT) goto-programs$(LIBEXT) + $(CXX) $(LINKFLAGS) test_wp$(OBJEXT) -o test_wp$(EXEEXT) \ + ../ansi-c/ansi-c$(LIBEXT) ../util/util$(LIBEXT) \ + ../big-int/bigint$(OBJEXT) goto-programs$(LIBEXT) \ + ../pointer-analysis/pointer-analysis$(LIBEXT) \ + ../langapi/langapi$(LIBEXT) + +clean: + rm -f $(OBJ) goto-programs$(LIBEXT) test_wp$(EXEEXT) + diff --git a/src/goto-programs/add_race_assertions.cpp b/src/goto-programs/add_race_assertions.cpp new file mode 100644 index 00000000000..33754058a25 --- /dev/null +++ b/src/goto-programs/add_race_assertions.cpp @@ -0,0 +1,272 @@ +/*******************************************************************\ + +Module: Race Detection for Threaded Goto Programs + +Author: Daniel Kroening + +Date: February 2006 + +\*******************************************************************/ + +#include +#include +#include +#include + +#include + +#include "remove_skip.h" +#include "add_race_assertions.h" +#include "rw_set.h" + +class w_guardst +{ +public: + w_guardst(contextt &_context):context(_context) + { + } + + std::list w_guards; + + const symbolt &get_guard_symbol(const irep_idt &object); + + const exprt get_guard_symbol_expr(const irep_idt &object) + { + return symbol_expr(get_guard_symbol(object)); + } + + const exprt get_w_guard_expr(const rw_sett::entryt &entry) + { + assert(entry.w); + return get_guard_symbol_expr(entry.object); + } + + const exprt get_assertion(const rw_sett::entryt &entry) + { + return gen_not(get_guard_symbol_expr(entry.object)); + } + + void add_initialization(goto_programt &goto_program) const; + +protected: + contextt &context; +}; + +/*******************************************************************\ + +Function: w_guardst::get_guard_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const symbolt &w_guardst::get_guard_symbol(const irep_idt &object) +{ + const irep_idt identifier=id2string(object)+"$w_guard"; + + const contextt::symbolst::const_iterator it= + context.symbols.find(identifier); + + if(it!=context.symbols.end()) + return it->second; + + w_guards.push_back(identifier); + + symbolt new_symbol; + new_symbol.name=identifier; + new_symbol.base_name=identifier; + new_symbol.type=typet("bool"); + new_symbol.static_lifetime=true; + new_symbol.value.make_false(); + + symbolt *symbol_ptr; + context.move(new_symbol, symbol_ptr); + return *symbol_ptr; +} + +/*******************************************************************\ + +Function: w_guardst::add_initialization + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void w_guardst::add_initialization(goto_programt &goto_program) const +{ + goto_programt::targett t=goto_program.instructions.begin(); + const namespacet ns(context); + + for(std::list::const_iterator + it=w_guards.begin(); + it!=w_guards.end(); + it++) + { + exprt symbol=symbol_expr(ns.lookup(*it)); + + t=goto_program.insert_before(t); + t->type=ASSIGN; + t->code=code_assignt(symbol, false_exprt()); + + t++; + } +} + +/*******************************************************************\ + +Function: add_race_assertions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void add_race_assertions( + value_setst &value_sets, + contextt &context, + goto_programt &goto_program, + w_guardst &w_guards) +{ + namespacet ns(context); + + Forall_goto_program_instructions(i_it, goto_program) + { + goto_programt::instructiont &instruction=*i_it; + + if(instruction.is_assign()) + { + rw_sett rw_set(ns, value_sets, i_it, instruction.code); + + if(rw_set.entries.empty()) continue; + + goto_programt::instructiont original_instruction; + original_instruction.swap(instruction); + + instruction.make_skip(); + i_it++; + + // now add assignments for what is written -- set + forall_rw_set_entries(e_it, rw_set) + if(e_it->second.w) + { + goto_programt::targett t=goto_program.insert_before(i_it); + + t->type=ASSIGN; + t->code=code_assignt( + w_guards.get_w_guard_expr(e_it->second), + e_it->second.get_guard()); + + t->location=original_instruction.location; + i_it=++t; + } + + // insert original statement here + { + goto_programt::targett t=goto_program.insert_before(i_it); + *t=original_instruction; + i_it=++t; + } + + // now add assignments for what is written -- reset + forall_rw_set_entries(e_it, rw_set) + if(e_it->second.w) + { + goto_programt::targett t=goto_program.insert_before(i_it); + + t->type=ASSIGN; + t->code=code_assignt( + w_guards.get_w_guard_expr(e_it->second), + false_exprt()); + + t->location=original_instruction.location; + i_it=++t; + } + + // now add assertion for what is read and written + forall_rw_set_entries(e_it, rw_set) + { + goto_programt::targett t=goto_program.insert_before(i_it); + + t->make_assertion(w_guards.get_assertion(e_it->second)); + t->location=original_instruction.location; + t->location.set("comment", e_it->second.get_comment()); + i_it=++t; + } + + i_it--; // the for loop already counts us up + } + } + + remove_skip(goto_program); +} + +/*******************************************************************\ + +Function: add_race_assertions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void add_race_assertions( + value_setst &value_sets, + contextt &context, + goto_programt &goto_program) +{ + w_guardst w_guards(context); + + add_race_assertions(value_sets, context, goto_program, w_guards); + + w_guards.add_initialization(goto_program); + goto_program.update(); +} + +/*******************************************************************\ + +Function: add_race_assertions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void add_race_assertions( + value_setst &value_sets, + contextt &context, + goto_functionst &goto_functions) +{ + w_guardst w_guards(context); + + Forall_goto_functions(f_it, goto_functions) + add_race_assertions(value_sets, context, f_it->second.body, w_guards); + + // get "main" + goto_functionst::function_mapt::iterator + m_it=goto_functions.function_map.find(goto_functions.main_id()); + + if(m_it!=goto_functions.function_map.end()) + { + goto_programt &main=m_it->second.body; + w_guards.add_initialization(main); + } + + goto_functions.update(); +} diff --git a/src/goto-programs/add_race_assertions.h b/src/goto-programs/add_race_assertions.h new file mode 100644 index 00000000000..f1937fb171b --- /dev/null +++ b/src/goto-programs/add_race_assertions.h @@ -0,0 +1,29 @@ +/*******************************************************************\ + +Module: Race Detection for Threaded Goto Programs + +Author: Daniel Kroening + +Date: February 2006 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_RACE_DETECTION_H +#define CPROVER_GOTO_PROGRAMS_RACE_DETECTION_H + +#include + +#include "goto_program.h" +#include "goto_functions.h" + +void add_race_assertions( + value_setst &value_sets, + class contextt &context, + goto_programt &goto_program); + +void add_race_assertions( + value_setst &value_sets, + class contextt &context, + goto_functionst &goto_functions); + +#endif diff --git a/src/goto-programs/basic_blocks.cpp b/src/goto-programs/basic_blocks.cpp new file mode 100644 index 00000000000..b7b4584c2d2 --- /dev/null +++ b/src/goto-programs/basic_blocks.cpp @@ -0,0 +1,105 @@ +/*******************************************************************\ + +Module: Group Basic Blocks in Goto Program + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "basic_blocks.h" + +/*******************************************************************\ + +Function: basic_blocks + + Inputs: + + Outputs: + + Purpose: convert basic blocks into single expressions of type "block" + +\*******************************************************************/ + +void basic_blocks(goto_programt &goto_program, + unsigned max_block_size) +{ + // get the targets + typedef std::set targetst; + + targetst targets; + + for(goto_programt::instructionst::iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + if(i_it->is_goto()) + for(goto_programt::instructiont::targetst::iterator + t_it=i_it->targets.begin(); + t_it!=i_it->targets.end(); + t_it++) + targets.insert(*t_it); + + // Scan program + for(goto_programt::instructionst::iterator + it=goto_program.instructions.begin(); + it!=goto_program.instructions.end(); + ) // intentionally no it++ + { + // goto's and empty code are left unchanged + if(it->is_goto() || it->is_dead() || + it->is_assert() || it->is_assume() || + it->is_atomic_begin() || it->is_atomic_end() || + it->is_end_thread() || it->is_start_thread() || + it->is_end_function() || it->is_location() || + it->is_skip() || it->is_function_call() || + it->is_decl()) + it++; + else if(it->is_other() || it->is_assign()) + { + if(it->code.is_nil()) + it++; + else + { + unsigned count=0; + + // this is the start of a new block + targetst::iterator t_it; // iterator for searching targets + + // find the end of the block + goto_programt::instructionst::iterator end_block = it; + do + { + end_block++; + count++; + if(end_block!=goto_program.instructions.end()) + t_it=targets.find(end_block); + + if(max_block_size!=0 && count>=max_block_size) + break; + } + while(end_block!=goto_program.instructions.end() && + (end_block->is_other() || end_block->is_assign()) && + t_it==targets.end()); + + // replace it with the code of the block + + { + code_blockt new_block; + + for(goto_programt::instructionst::iterator stmt = it; + stmt != end_block; + stmt++) + if(!stmt->code.is_nil()) + new_block.move_to_operands(stmt->code); + + it->code.swap(new_block); + it++; + if(it!=goto_program.instructions.end()) + it=goto_program.instructions.erase(it, end_block); + } + } + } + else + assert(false); + } +} diff --git a/src/goto-programs/basic_blocks.h b/src/goto-programs/basic_blocks.h new file mode 100644 index 00000000000..342ba4acb5a --- /dev/null +++ b/src/goto-programs/basic_blocks.h @@ -0,0 +1,17 @@ +/*******************************************************************\ + +Module: Group Basic Blocks in Goto Program + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAM_BASIC_BLOCKS_H +#define CPROVER_GOTO_PROGRAM_BASIC_BLOCKS_H + +#include "goto_program.h" + +void basic_blocks(goto_programt &goto_program, + unsigned max_block_size=0); + +#endif diff --git a/src/goto-programs/builtin_functions.cpp b/src/goto-programs/builtin_functions.cpp new file mode 100644 index 00000000000..b10d643cc51 --- /dev/null +++ b/src/goto-programs/builtin_functions.cpp @@ -0,0 +1,1094 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "goto_convert_class.h" +#include "dynamic_memory.h" + +/*******************************************************************\ + +Function: get_alloc_type_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void get_alloc_type_rec( + const exprt &src, + typet &type, + exprt &size) +{ + const irept &sizeof_type=src.find("#c_sizeof_type"); + + if(!sizeof_type.is_nil()) + { + type=static_cast(sizeof_type); + } + else if(src.id()==ID_mult) + { + forall_operands(it, src) + get_alloc_type_rec(*it, type, size); + } + else + { + size.copy_to_operands(src); + } +} + +/*******************************************************************\ + +Function: get_alloc_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +#if 0 +static void get_alloc_type( + const exprt &src, + typet &type, + exprt &size) +{ + type.make_nil(); + size.make_nil(); + + get_alloc_type_rec(src, type, size); + + if(type.is_nil()) + type=char_type(); + + if(size.has_operands()) + { + if(size.operands().size()==1) + { + exprt tmp; + tmp.swap(size.op0()); + size.swap(tmp); + } + else + { + size.id(ID_mult); + size.type()=size.op0().type(); + } + + if(size.type()!=size_type()) + size.make_typecast(size_type()); + } +} +#endif + +/*******************************************************************\ + +Function: goto_convertt::do_prob_uniform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_prob_uniform( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + const irep_idt &identifier=function.get(ID_identifier); + + // make it a side effect if there is an LHS + if(arguments.size()!=2) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have two arguments"; + } + + if(lhs.is_nil()) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have LHS"; + } + + exprt rhs=exprt(ID_sideeffect, lhs.type()); + rhs.set(ID_statement, "prob_uniform"); + rhs.location()=function.location(); + + if(lhs.type().id()!=ID_unsignedbv && + lhs.type().id()!=ID_signedbv) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected other type"; + } + + if(arguments[0].type().id()!=lhs.type().id() || + arguments[1].type().id()!=lhs.type().id()) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected operands to be of same type as LHS"; + } + + if(!arguments[0].is_constant() || + !arguments[1].is_constant()) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected operands to be constant literals"; + } + + mp_integer lb, ub; + + if(to_integer(arguments[0], lb) || + to_integer(arguments[1], ub)) + { + err_location(function); + throw "error converting operands"; + } + + if(lb > ub) + { + err_location(function); + throw "expected lower bound to be smaller or equal to the upper bound"; + } + + rhs.copy_to_operands(arguments[0], arguments[1]); + + code_assignt assignment(lhs, rhs); + assignment.location()=function.location(); + copy(assignment, ASSIGN, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::do_prob_coin + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_prob_coin( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + const irep_idt &identifier=function.get(ID_identifier); + + // make it a side effect if there is an LHS + if(arguments.size()!=2) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have two arguments"; + } + + if(lhs.is_nil()) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have LHS"; + } + + exprt rhs=exprt(ID_sideeffect, lhs.type()); + rhs.set(ID_statement, "prob_coin"); + rhs.location()=function.location(); + + if(lhs.type()!=bool_typet()) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected bool"; + } + + if(arguments[0].type().id()!=ID_unsignedbv || + arguments[0].id()!=ID_constant) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected first " + "operand to be a constant literal of type unsigned long"; + } + + mp_integer num, den; + + if(to_integer(arguments[0], num) || + to_integer(arguments[1], den)) + { + err_location(function); + throw "error converting operands"; + } + + if(num-den > mp_integer(0)) + { + err_location(function); + throw "probability has to be smaller than 1"; + } + + if(den == mp_integer(0)) + { + err_location(function); + throw "denominator may not be zero"; + } + + rationalt numerator(num), denominator(den); + rationalt prob = numerator / denominator; + + rhs.copy_to_operands(from_rational(prob)); + + code_assignt assignment(lhs, rhs); + assignment.location()=function.location(); + copy(assignment, ASSIGN, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::do_printf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_printf( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + const irep_idt &f_id=function.get(ID_identifier); + + if(f_id==CPROVER_PREFIX "printf" || + f_id=="c::printf") + { + exprt printf_code(ID_sideeffect, + static_cast(function.type().find(ID_return_type))); + + printf_code.set(ID_statement, ID_printf); + + printf_code.operands()=arguments; + printf_code.location()=function.location(); + + if(lhs.is_not_nil()) + { + code_assignt assignment(lhs, printf_code); + assignment.location()=function.location(); + copy(assignment, ASSIGN, dest); + } + else + { + printf_code.id(ID_code); + printf_code.type()=typet(ID_code); + copy(to_code(printf_code), OTHER, dest); + } + } +} + +/*******************************************************************\ + +Function: goto_convertt::do_input + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_input( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + codet input_code; + input_code.set_statement(ID_input); + input_code.operands()=arguments; + input_code.location()=function.location(); + + if(arguments.size()<2) + { + err_location(function); + throw "input takes at least two arguments"; + } + + copy(input_code, OTHER, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::do_cover + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_cover( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + codet output_code; + output_code.set_statement(ID_output); + output_code.location()=function.location(); + + if(arguments.size()!=1) + { + err_location(function); + throw "cover takes one argument"; + } + + // build string constant + exprt string_constant(ID_string_constant); + string_constant.type()=array_typet(); + string_constant.type().subtype()=char_type(); + string_constant.set(ID_value, ID_cover); + + index_exprt index_expr; + index_expr.type()=char_type(); + index_expr.array()=string_constant; + index_expr.index()=gen_zero(index_type()); + + output_code.copy_to_operands(address_of_exprt(index_expr)); + + forall_expr(it, arguments) + output_code.copy_to_operands(*it); + + copy(output_code, OTHER, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::do_output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_output( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + codet output_code; + output_code.set_statement(ID_output); + output_code.operands()=arguments; + output_code.location()=function.location(); + + if(arguments.size()<2) + { + err_location(function); + throw "output takes at least two arguments"; + } + + copy(output_code, OTHER, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::do_atomic_begin + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_atomic_begin( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + if(lhs.is_not_nil()) + { + err_location(lhs); + throw "atomic_begin does not expect an LHS"; + } + + if(!arguments.empty()) + { + err_location(function); + throw "atomic_begin takes no arguments"; + } + + goto_programt::targett t=dest.add_instruction(ATOMIC_BEGIN); + t->location=function.location(); +} + +/*******************************************************************\ + +Function: goto_convertt::do_atomic_end + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_atomic_end( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + if(lhs.is_not_nil()) + { + err_location(lhs); + throw "atomic_end does not expect an LHS"; + } + + if(!arguments.empty()) + { + err_location(function); + throw "atomic_end takes no arguments"; + } + + goto_programt::targett t=dest.add_instruction(ATOMIC_END); + t->location=function.location(); +} + +/*******************************************************************\ + +Function: goto_convertt::do_cpp_new + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_cpp_new( + const exprt &lhs, + const side_effect_exprt &rhs, + goto_programt &dest) +{ + if(lhs.is_nil()) + throw "do_cpp_new without lhs is yet to be implemented"; + + // build size expression + exprt object_size= + static_cast(rhs.find(ID_sizeof)); + + exprt alloc_size; + + if(rhs.get(ID_statement)==ID_cpp_new_array) + { + exprt count=static_cast(rhs.find(ID_size)); + + if(count.type()!=object_size.type()) + count.make_typecast(object_size.type()); + + // might have side-effect + clean_expr(count, dest); + + alloc_size=mult_exprt(object_size, count); + } + else + alloc_size=object_size; + + exprt tmp_symbol_expr; + + // is this a placement new? + if(rhs.operands().size()==0) + { + // call __new + exprt new_symbol=symbol_expr(ns.lookup("c::__new")); + + const typet &return_type= + to_code_type(new_symbol.type()).return_type(); + + const symbolt &tmp_symbol= + new_tmp_symbol(return_type, "new", dest, rhs.location()); + + tmp_symbol_expr=symbol_expr(tmp_symbol); + + code_function_callt new_call; + new_call.function()=new_symbol; + new_call.arguments().push_back(alloc_size); + new_call.set("#type", lhs.type().subtype()); + new_call.lhs()=tmp_symbol_expr; + new_call.location()=rhs.location(); + + convert(new_call, dest); + } + else if(rhs.operands().size()==1) + { + // call __placement_new + exprt new_symbol=symbol_expr(ns.lookup("c::__placement_new")); + + const code_typet &code_type= + to_code_type(new_symbol.type()); + + const typet &return_type=code_type.return_type(); + + assert(code_type.arguments().size()==2); + + const symbolt &tmp_symbol= + new_tmp_symbol(return_type, "new", dest, rhs.location()); + + tmp_symbol_expr=symbol_expr(tmp_symbol); + + code_function_callt new_call; + new_call.function()=new_symbol; + new_call.arguments().resize(2); + new_call.arguments()[0]=alloc_size; + new_call.arguments()[1]=rhs.op0(); + new_call.set("#type", lhs.type().subtype()); + new_call.lhs()=tmp_symbol_expr; + new_call.location()=rhs.location(); + + if(new_call.arguments()[0].type()!=code_type.arguments()[0].type()) + new_call.arguments()[0].make_typecast(code_type.arguments()[0].type()); + + if(new_call.arguments()[1].type()!=code_type.arguments()[1].type()) + new_call.arguments()[1].make_typecast(code_type.arguments()[1].type()); + + convert(new_call, dest); + } + else + throw "cpp_new expected to have 0 or 1 operands"; + + goto_programt::targett t_n=dest.add_instruction(ASSIGN); + t_n->code=code_assignt( + lhs, typecast_exprt(tmp_symbol_expr, lhs.type())); + t_n->location=rhs.find_location(); + + // grab initializer + goto_programt tmp_initializer; + cpp_new_initializer(lhs, rhs, tmp_initializer); + + dest.destructive_append(tmp_initializer); +} + +/*******************************************************************\ + +Function: goto_convertt::cpp_new_initializer + + Inputs: + + Outputs: + + Purpose: builds a goto program for object initialization + after new + +\*******************************************************************/ + +void goto_convertt::cpp_new_initializer( + const exprt &lhs, + const side_effect_exprt &rhs, + goto_programt &dest) +{ + exprt initializer= + static_cast(rhs.find(ID_initializer)); + + if(initializer.is_not_nil()) + { + if(rhs.get_statement()=="cpp_new[]") + { + // build loop + } + else if(rhs.get_statement()==ID_cpp_new) + { + // just one object + exprt deref_lhs(ID_dereference, rhs.type().subtype()); + deref_lhs.copy_to_operands(lhs); + + replace_new_object(deref_lhs, initializer); + convert(to_code(initializer), dest); + } + else + assert(false); + } +} + +/*******************************************************************\ + +Function: goto_convertt::get_array_argument + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt goto_convertt::get_array_argument(const exprt &src) +{ + if(src.id()==ID_typecast) + { + assert(src.operands().size()==1); + return get_array_argument(src.op0()); + } + + if(src.id()!=ID_address_of) + { + err_location(src); + throw "expected array-pointer as argument"; + } + + assert(src.operands().size()==1); + + if(src.op0().id()!=ID_index) + { + err_location(src); + throw "expected array-element as argument"; + } + + assert(src.op0().operands().size()==2); + + if(ns.follow(src.op0().op0().type()).id()!=ID_array) + { + err_location(src); + throw "expected array as argument"; + } + + return src.op0().op0(); +} + +/*******************************************************************\ + +Function: goto_convertt::do_array_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_array_set( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + if(arguments.size()!=2) + { + err_location(function); + throw "array_set expects two arguments"; + } + + codet array_set_statement; + array_set_statement.set_statement(ID_array_set); + array_set_statement.operands()=arguments; + + copy(array_set_statement, OTHER, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::do_array_copy + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_array_copy( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + if(arguments.size()!=2) + { + err_location(function); + throw "array_copy expects two arguments"; + } + + codet array_set_statement; + array_set_statement.set_statement(ID_array_copy); + array_set_statement.operands()=arguments; + + copy(array_set_statement, OTHER, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::do_array_equal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_array_equal( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + if(arguments.size()!=2) + { + err_location(function); + throw "array_equal expects two arguments"; + } + + const typet &arg0_type=ns.follow(arguments[0].type()); + const typet &arg1_type=ns.follow(arguments[1].type()); + + if(arg0_type.id()!=ID_pointer || + arg1_type.id()!=ID_pointer) + { + err_location(function); + throw "array_equal expects pointer arguments"; + } + + if(lhs.is_not_nil()) + { + code_assignt assignment; + + // add dereferencing here + dereference_exprt lhs_array, rhs_array; + lhs_array.op0()=arguments[0]; + rhs_array.op0()=arguments[1]; + lhs_array.type()=arg0_type.subtype(); + rhs_array.type()=arg1_type.subtype(); + + assignment.lhs()=lhs; + assignment.rhs()=binary_exprt( + lhs_array, ID_array_equal, rhs_array, lhs.type()); + + convert(assignment, dest); + } +} + +/*******************************************************************\ + +Function: goto_convertt::do_function_call_symbol + + Inputs: + + Outputs: + + Purpose: add function calls to function queue for later + processing + +\*******************************************************************/ + +void goto_convertt::do_function_call_symbol( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + if(function.get_bool("#invalid_object")) + return; // ignore + + // lookup symbol + const irep_idt &identifier=function.get(ID_identifier); + + const symbolt *symbol; + if(ns.lookup(identifier, symbol)) + { + err_location(function); + throw "error: function `"+id2string(identifier)+"' not found"; + } + + if(symbol->type.id()!=ID_code) + { + err_location(function); + throw "error: function `"+id2string(identifier)+"' type mismatch: expected code"; + } + + bool is_assume=identifier==CPROVER_PREFIX "assume" || + identifier=="specc::__CPROVER_assume"; + + bool is_assert=identifier=="c::assert" || + identifier=="specc::assert"; + + bool is_predicate=identifier==CPROVER_PREFIX "predicate" || + identifier=="specc::__CPROVER_predicate"; + + if(is_assume || is_assert || is_predicate) + { + if(arguments.size()!=1) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have one argument"; + } + + if(is_predicate) { + goto_programt::targett t = dest.add_instruction(OTHER); + t->guard = arguments.front(); + t->location = function.location(); + t->location.set("user-provided", true); + t->code = ID_user_specified_predicate; + t->code.set_statement(ID_user_specified_predicate); + return; + + } + + if(is_assume && !options.get_bool_option("assumptions")) + return; + + if(is_assert && !options.get_bool_option("assertions")) + return; + + goto_programt::targett t=dest.add_instruction( + is_assume?ASSUME:ASSERT); + t->guard=arguments.front(); + t->location=function.location(); + t->location.set("user-provided", true); + + if(is_assert) + t->location.set(ID_property, ID_assertion); + + if(lhs.is_not_nil()) + { + err_location(function); + throw id2string(identifier)+" expected not to have LHS"; + } + } + else if(identifier==CPROVER_PREFIX "assert") + { + if(arguments.size()!=2) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have two arguments"; + } + + const irep_idt description= + get_string_constant(arguments[1]); + + if(!options.get_bool_option("assertions")) + return; + + goto_programt::targett t=dest.add_instruction(ASSERT); + t->guard=arguments[0]; + t->location=function.location(); + t->location.set("user-provided", true); + t->location.set(ID_property, ID_assertion); + t->location.set(ID_comment, description); + + if(lhs.is_not_nil()) + { + err_location(function); + throw id2string(identifier)+" expected not to have LHS"; + } + } + else if(identifier==CPROVER_PREFIX "printf") + { + do_printf(lhs, function, arguments, dest); + } + else if(identifier==CPROVER_PREFIX "input") + { + do_input(lhs, function, arguments, dest); + } + else if(identifier==CPROVER_PREFIX "cover") + { + do_cover(lhs, function, arguments, dest); + } + else if(identifier==CPROVER_PREFIX "output") + { + do_output(lhs, function, arguments, dest); + } + else if(identifier==CPROVER_PREFIX "atomic_begin") + { + do_atomic_begin(lhs, function, arguments, dest); + } + else if(identifier==CPROVER_PREFIX "atomic_end") + { + do_atomic_end(lhs, function, arguments, dest); + } + else if(identifier==CPROVER_PREFIX "prob_biased_coin") + { + do_prob_coin(lhs, function, arguments, dest); + } + else if(has_prefix(id2string(identifier), CPROVER_PREFIX "prob_uniform_")) + { + do_prob_uniform(lhs, function, arguments, dest); + } + else if(has_prefix(id2string(identifier), "c::nondet_") || + has_prefix(id2string(identifier), "cpp::nondet_")) + { + // make it a side effect if there is an LHS + if(lhs.is_nil()) return; + + exprt rhs=side_effect_expr_nondett(lhs.type()); + rhs.location()=function.location(); + + code_assignt assignment(lhs, rhs); + assignment.location()=function.location(); + copy(assignment, ASSIGN, dest); + } + else if(has_prefix(id2string(identifier), CPROVER_PREFIX "uninterpreted_")) + { + // make it a side effect if there is an LHS + if(lhs.is_nil()) return; + + function_application_exprt rhs; + rhs.type()=lhs.type(); + rhs.location()=function.location(); + rhs.function()=function; + rhs.arguments()=arguments; + + code_assignt assignment(lhs, rhs); + assignment.location()=function.location(); + copy(assignment, ASSIGN, dest); + } + else if(has_prefix(id2string(identifier), CPROVER_PREFIX "array_set")) + { + do_array_set(lhs, function, arguments, dest); + } + else if(identifier==CPROVER_PREFIX "array_equal") + { + do_array_equal(lhs, function, arguments, dest); + } + else if(identifier==CPROVER_PREFIX "array_copy") + { + do_array_copy(lhs, function, arguments, dest); + } + else if(identifier=="c::printf" || + identifier=="c::fprintf" || + identifier=="c::sprintf" || + identifier=="c::snprintf") + { + do_printf(lhs, function, arguments, dest); + } + else if(identifier=="c::__assert_rtn" || + identifier=="c::__assert_fail") + { + // __assert_fail is Linux + + if(arguments.size()!=4) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have four arguments"; + } + + const irep_idt description= + "assertion "+id2string(get_string_constant(arguments[0])); + + if(!options.get_bool_option("assertions")) + return; + + goto_programt::targett t=dest.add_instruction(ASSERT); + t->guard=false_exprt(); + t->location=function.location(); + t->location.set("user-provided", true); + t->location.set(ID_property, ID_assertion); + t->location.set(ID_comment, description); + // we ignore any LHS + } + else if(identifier=="c::__assert_rtn") + { + // __assert_rtn is MACOS + + if(arguments.size()!=4) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have four arguments"; + } + + const irep_idt description= + "assertion "+id2string(get_string_constant(arguments[3])); + + if(!options.get_bool_option("assertions")) + return; + + goto_programt::targett t=dest.add_instruction(ASSERT); + t->guard=false_exprt(); + t->location=function.location(); + t->location.set("user-provided", true); + t->location.set(ID_property, ID_assertion); + t->location.set("comment", description); + // we ignore any LHS + } + else if(identifier=="c::_wassert") + { + // this is Windows + + if(arguments.size()!=3) + { + err_location(function); + throw "`"+id2string(identifier)+"' expected to have three arguments"; + } + + const irep_idt description= + "assertion "+id2string(get_string_constant(arguments[0])); + + if(!options.get_bool_option("assertions")) + return; + + goto_programt::targett t=dest.add_instruction(ASSERT); + t->guard=false_exprt(); + t->location=function.location(); + t->location.set("user-provided", true); + t->location.set(ID_property, ID_assertion); + t->location.set(ID_comment, description); + // we ignore any LHS + } + else if(identifier=="c::__builtin_prefetch") + { + // does nothing + } + else + { + do_function_call_symbol(*symbol); + + // insert function call + code_function_callt function_call; + function_call.lhs()=lhs; + function_call.function()=function; + function_call.arguments()=arguments; + function_call.location()=function.location(); + + copy(function_call, FUNCTION_CALL, dest); + } +} diff --git a/src/goto-programs/cfg.h b/src/goto-programs/cfg.h new file mode 100644 index 00000000000..37557849cdd --- /dev/null +++ b/src/goto-programs/cfg.h @@ -0,0 +1,242 @@ +/*******************************************************************\ + +Module: Control Flow Graph + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_CFG_H +#define CPROVER_GOTO_PROGRAMS_CFG_H + +#include + +#include "goto_functions.h" + +/*******************************************************************\ + + Class: cfg_baset + + Purpose: + +\*******************************************************************/ + +template +class cfg_baset +{ +public: + struct entryt: public T + { + typedef std::list entriest; + + // we have edges both ways + entriest successors, predecessors; + goto_programt::const_targett PC; + }; + + typedef entryt * iterator; + typedef const entryt * const_iterator; + + typedef std::list entriest; + + typedef std::map entry_mapt; + entry_mapt entry_map; + +protected: + void compute_edges( + const goto_functionst &goto_functions, + const goto_programt &goto_program, + goto_programt::const_targett PC); + + void compute_edges( + const goto_functionst &goto_functions, + const goto_programt &goto_program); + + void compute_edges( + const goto_functionst &goto_functions); + +public: + void operator()( + const goto_functionst &goto_functions) + { + compute_edges(goto_functions); + } + + void operator()( + const goto_programt &goto_program) + { + goto_functionst goto_functions; + compute_edges(goto_functions, goto_program); + } + +}; + +/*******************************************************************\ + +Function: cfg_baset::compute_edges + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void cfg_baset::compute_edges( + const goto_functionst &goto_functions, + const goto_programt &goto_program, + goto_programt::const_targett PC) +{ + const goto_programt::instructiont &instruction=*PC; + entryt &entry=entry_map[PC]; + entry.PC=PC; + goto_programt::const_targett next_PC(PC); + next_PC++; + + // compute forward edges first + if(instruction.is_goto()) + { + if(next_PC!=goto_program.instructions.end() && + !instruction.guard.is_true()) + entry.successors.push_back(&entry_map[next_PC]); + + for(goto_programt::instructiont::targetst::const_iterator + t_it=instruction.targets.begin(); + t_it!=instruction.targets.end(); + t_it++) + { + goto_programt::const_targett t=*t_it; + if(t!=goto_program.instructions.end()) + entry.successors.push_back(&entry_map[t]); + } + } + else if(instruction.is_start_thread()) + { + if(next_PC!=goto_program.instructions.end()) + entry.successors.push_back(&entry_map[PC]); + + for(goto_programt::instructiont::targetst::const_iterator + t_it=instruction.targets.begin(); + t_it!=instruction.targets.end(); + t_it++) + { + goto_programt::const_targett t=*t_it; + if(t!=goto_program.instructions.end()) + entry.successors.push_back(&entry_map[t]); + } + } + else if(instruction.is_function_call()) + { + const exprt &function= + to_code_function_call(instruction.code).function(); + + if(function.id()==ID_symbol) + { + const irep_idt &identifier= + to_symbol_expr(function).get_identifier(); + + goto_functionst::function_mapt::const_iterator f_it= + goto_functions.function_map.find(identifier); + + if(f_it!=goto_functions.function_map.end() && + f_it->second.body_available) + { + // get the first instruction + goto_programt::const_targett i_it= + f_it->second.body.instructions.begin(); + + goto_programt::const_targett e_it= + f_it->second.body.instructions.end(); + + goto_programt::const_targett last_it=e_it; last_it--; + + if(i_it!=e_it) + { + // nonempty function + entry.successors.push_back(&entry_map[i_it]); + + // add the last instruction as predecessor of the return location + if(next_PC!=goto_program.instructions.end()) + { + entry_map[last_it].successors.push_back(&entry_map[next_PC]); + entry_map[next_PC].predecessors.push_back(&entry_map[last_it]); + } + } + else if(next_PC!=goto_program.instructions.end()) + { + // empty function + entry.successors.push_back(&entry_map[next_PC]); + } + } + else if(next_PC!=goto_program.instructions.end()) + entry.successors.push_back(&entry_map[next_PC]); + } + } + else if(instruction.is_return()) + { + // the successor of return is the last instruction of the function, + // normally END_FUNCTION + if(next_PC!=goto_program.instructions.end()) + entry.successors.push_back(&entry_map[--(goto_program.instructions.end())]); + } + else + { + if(next_PC!=goto_program.instructions.end()) + entry.successors.push_back(&entry_map[next_PC]); + } + + // now do backward edges + for(typename entriest::const_iterator + s_it=entry.successors.begin(); + s_it!=entry.successors.end(); + s_it++) + { + (*s_it)->predecessors.push_back(&entry); + } +} + +/*******************************************************************\ + +Function: cfg_baset::compute_edges + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void cfg_baset::compute_edges( + const goto_functionst &goto_functions, + const goto_programt &goto_program) +{ + forall_goto_program_instructions(it, goto_program) + compute_edges(goto_functions, goto_program, it); +} + +/*******************************************************************\ + +Function: cfg_baset::compute_edges + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void cfg_baset::compute_edges( + const goto_functionst &goto_functions) +{ + forall_goto_functions(it, goto_functions) + if(it->second.body_available) + compute_edges(goto_functions, it->second.body); +} + +#endif diff --git a/src/goto-programs/control_flow_graph.cpp b/src/goto-programs/control_flow_graph.cpp new file mode 100644 index 00000000000..21151691b74 --- /dev/null +++ b/src/goto-programs/control_flow_graph.cpp @@ -0,0 +1,1135 @@ +/* + * control_flow_graph.cpp + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + +#include +#include +#include +#include +#include + + + +#include "control_flow_graph.h" +#include "dominator_info.h" +#include "loops.h" + +/* Control flow graphs */ + +static void output_single_node(const CFG_nodet& node, + std::ostream& out, const namespacet& ns, const CFGt::const_nodes_vectort* ordering, bool show_predecessors); + + +void redirect_pointer(CFG_nodet*& pointer_to_redirect, CFGt::nodes_mappingt& original_to_cloned) +{ + if(NULL != pointer_to_redirect) + { + if(original_to_cloned.find(pointer_to_redirect) != original_to_cloned.end()) + { + pointer_to_redirect = original_to_cloned[pointer_to_redirect]; + } + } + +} + +void patchup_pointers_in_nodes(CFGt::nodest& nodes, std::map& patchup_map) +{ + for(CFGt::nodest::iterator + it = nodes.begin(); + it != nodes.end(); + it++) + { + redirect_pointer((*it)->successor_next, patchup_map); + redirect_pointer((*it)->successor_jump, patchup_map); + } +} + +void patchup_pointers_in_nodes(CFGt::nodes_sett& nodes, std::map& patchup_map) +{ + for(CFGt::nodes_sett::iterator + it = nodes.begin(); + it != nodes.end(); + it++) + { + redirect_pointer((*it)->successor_next, patchup_map); + redirect_pointer((*it)->successor_jump, patchup_map); + } +} + + +CFGt::CFGt(const CFGt& other) : context(other.context) +{ + nodes.clear(); + entry_node = NULL; + last_node_added = NULL; + + nodes_mappingt patchup_map; + + for(nodes_sett::const_iterator it = other.nodes.begin(); it != other.nodes.end(); it++) + { + CFG_nodet* new_node = new CFG_nodet(**it); + patchup_map[*it] = new_node; + nodes.insert(new_node); + if(other.entry_node == *it) + { + assert(NULL == entry_node); + entry_node = new_node; + } + if(other.last_node_added == *it) + { + assert(NULL == last_node_added); + last_node_added = new_node; + } + } + + patchup_pointers(patchup_map); + update(); + +} + + +void CFGt::patchup_pointers(std::map& patchup_map) +{ + patchup_pointers_in_nodes(nodes, patchup_map); +} + + +void CFGt::fix_jumps() +{ + nodes_mappingt patchup_map; + for(CFGt::nodes_sett::iterator it = nodes.begin(); it != nodes.end(); it++) + { + if((GOTO == (*it)->type) && ((*it)->jump_condition != true_exprt())) + { + if(NULL == (*it)->successor_next) + { + assert(NULL != (*it)->successor_jump); + CFG_nodet new_node(code_assumet(), (*it)->function, (*it)->location, ASSUME); + new_node.reasoning_guard = (*it)->jump_condition; + new_node.successor_next = (*it)->successor_jump; + CFG_nodet& added_node = add_node(new_node); + patchup_map[*it] = &added_node; + } else if(NULL == (*it)->successor_jump) { + assert(NULL != (*it)->successor_next); + CFG_nodet new_node(code_assumet(), (*it)->function, (*it)->location, ASSUME); + new_node.reasoning_guard = not_exprt((*it)->jump_condition); + new_node.successor_next = (*it)->successor_next; + CFG_nodet& added_node = add_node(new_node); + patchup_map[*it] = &added_node; + } + } + } + patchup_pointers(patchup_map); +} + +void CFG_nodet::output_special_info(std::ostream&) const +{ + return; +} + +CFG_nodet::CFG_nodet(goto_programt::instructiont& goto_program_instruction) +{ + this->code = goto_program_instruction.code; + this->function = goto_program_instruction.function; + this->location = goto_program_instruction.location; + this->type = goto_program_instruction.type; + + if(this->type == ASSERT || this->type == ASSUME) { + this->reasoning_guard = goto_program_instruction.guard; + } else { + this->reasoning_guard = true_exprt(); + } + + if(this->type == GOTO) { + contextt ctx; + /* We only deal with deterministic gotos */ + assert(goto_program_instruction.targets.size() == 1); + this->jump_condition = goto_program_instruction.guard; + } else { + this->jump_condition = false_exprt(); + } + + this->successor_next = NULL; + this->successor_jump = NULL; +} + + +bool CFG_nodet::operator == (const CFG_nodet& other) const +{ + return code == other.code && function == other.function && location == other.location && type == other.type && + reasoning_guard == other.reasoning_guard && jump_condition == other.jump_condition && + successor_jump == other.successor_jump && successor_next == other.successor_next; +} + + +static goto_programt::targett get_following_instruction(goto_programt::targett current) { + goto_programt::targett result = current; + result++; + return result; +} + +void CFGt::initialize(goto_programt& program) { + + assert(nodes.empty()); + assert(NULL == entry_node); + assert(NULL == last_node_added); + + namespacet ns(context); + + /* First, for every instruction in the function, + * add a node to the CFG. Keep a mapping from instructions + * to nodes, so that later we can add edges to the CFG + */ + std::map instructions_to_nodes; + for(goto_programt::targett + it = program.instructions.begin(); + it != program.instructions.end(); + it++) + { + if(it->type == END_FUNCTION) + { + CFG_nodet return_node(code_returnt(), it->function, it->location, RETURN); + CFG_nodet& new_node = add_node(return_node); + instructions_to_nodes[it] = &new_node; + if(NULL == entry_node) + { + entry_node = &new_node; + } + } else { + CFG_nodet& new_node = add_node(CFG_nodet(*it)); + instructions_to_nodes[it] = &new_node; + if(NULL == entry_node) + { + entry_node = &new_node; + } + } + + } + + /* Add edges to the CFG nodes according to the + * successor relationships between instructions + */ + for(goto_programt::targett + instructions_it = program.instructions.begin(); + instructions_it != program.instructions.end(); + instructions_it++) + { + if((instructions_it->type == END_FUNCTION) || (instructions_it->type == RETURN)) + { + /* A RETURN instruction has no CFG successors */ + assert(NULL == instructions_to_nodes[instructions_it]->successor_next && NULL == instructions_to_nodes[instructions_it]->successor_jump); + continue; + } + + /* Get the next instruction */ + goto_programt::targett next_instruction = get_following_instruction(instructions_it); + assert(next_instruction != program.instructions.end()); + + CFG_nodet* node = instructions_to_nodes[instructions_it]; + + if(!(((node->type) == GOTO) && ((node->jump_condition) == true_exprt()))) + { + /* We populate the "successor_next" field, unless the node is an unconditional jump */ + node->successor_next = instructions_to_nodes[next_instruction]; + } + + if((instructions_it->type == GOTO) && (node->jump_condition != false_exprt())) { + /* We add a 'jump' successor to the current CFG node provided that the node + * is a GOTO, and the guard for the GOTO is not false + */ + assert(1 == instructions_it->targets.size()); + + goto_programt::targett instruction_to_jump_to = instructions_it->targets.front(); + + node->successor_jump = instructions_to_nodes[instructions_it->targets.front()]; + } + + } + + /* Remove unreachable parts of the CFG, and add predecessor information */ + update(); + +} + + +static int index_in_vector(const CFG_nodet* n, const CFGt::const_nodes_vectort* ordering) +{ + for(unsigned int i = 0; i < ordering->size(); i++) + { + if((*ordering)[i] == n) + { + return i; + } + } + assert(false); + return -1; +} + +static void output_single_node(const CFG_nodet& node, + std::ostream& out, const namespacet& ns, const CFGt::const_nodes_vectort* ordering, bool show_predecessors) +{ + if(NULL == ordering) + { + out << (&node); + } else { + out << index_in_vector(&node, ordering); + } + + out << ": " << node.type << " "; + + if(node.type == ASSERT || node.type == ASSUME) + { + out << from_expr(ns, "", node.reasoning_guard) << std::endl; + } else if(node.type != GOTO && node.type != SKIP && node.type != END_FUNCTION) { + out << from_expr(ns, "", node.code); + } else { + out << std::endl; + } + + out << " "; + + if(NULL != node.successor_jump) + { + out << "if(" << from_expr(ns, "", node.jump_condition) << ") GOTO "; + + if(NULL == ordering) + { + out << node.successor_jump; + } else { + out << index_in_vector(node.successor_jump, ordering); + } + + out << " ELSE "; + } + + if(NULL == node.successor_next) { + out << "EXIT"; + } else { + out << "GOTO "; + if(NULL == ordering) + { + out << node.successor_next; + } else { + out << index_in_vector(node.successor_next, ordering); + } + } + + out << std::endl; + + node.output_special_info(out); + + if(show_predecessors) + { + out << "Predecessors:"; + for(std::set::const_iterator + predecessor_it = node.predecessors.begin(); + predecessor_it != node.predecessors.end(); + predecessor_it++) + { + output_single_node(**predecessor_it, out, ns, ordering, false); + } + out << std::endl; + } +} + +void CFGt::output_node( + const CFG_nodet& node, + std::ostream& out, bool show_predecessors) const +{ + namespacet ns(context); + output_single_node(node, out, ns, &ordered_nodes, show_predecessors); +} + + +static void output_nodes(const CFGt::const_nodes_vectort& ordered_nodes, + const class namespacet &ns, + std::ostream& out, bool show_predecessors) +{ + for(CFGt::const_nodes_vectort::const_iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); it++) + { + output_single_node(**it, out, ns, &ordered_nodes, show_predecessors); + } + +} + + +void CFGt::output( + std::ostream& out, bool show_predecessors) const +{ + output_nodes(ordered_nodes, namespacet(context), out, show_predecessors); +} + + +void CFGt::to_goto_program( + goto_programt& program, + std::map >* loop_header_instruction_points, + std::map* node_to_target) const +{ + program.instructions.clear(); + + if(NULL != loop_header_instruction_points) + { + loop_header_instruction_points->clear(); + } + + std::map instructions_to_nodes; + std::map nodes_to_instructions; + + for(const_nodes_vectort::const_iterator nodes_it = ordered_nodes.begin(); nodes_it != ordered_nodes.end(); nodes_it++) + { + + goto_programt::targett inst = program.add_instruction(); + + check_cfg_point(inst, *nodes_it, loop_header_instruction_points); + + instructions_to_nodes[inst] = *nodes_it; + nodes_to_instructions[*nodes_it] = inst; + + inst->code = (*nodes_it)->code; + inst->function = (*nodes_it)->function; + inst->location = (*nodes_it)->location; + inst->type = (*nodes_it)->type; + + if(inst->type == ASSERT || inst->type == ASSUME) + { + inst->guard = (*nodes_it)->reasoning_guard; + } + + if(inst->type == GOTO) + { + inst->guard = (*nodes_it)->jump_condition; + } + + // If we find a CFG node that has no successor, its instruction must be followed by a return instruction + if((NULL == (*nodes_it)->successor_next) && (NULL == (*nodes_it)->successor_jump)) + { + assert(RETURN == (*nodes_it)->type); + } + + } + + + for(const_nodes_vectort::const_iterator nodes_it = ordered_nodes.begin(); nodes_it != ordered_nodes.end(); nodes_it++) + { + const CFG_nodet* node = *nodes_it; + goto_programt::targett inst = nodes_to_instructions[node]; + + if(node->type == GOTO) { + assert(inst->targets.empty()); + assert(node->successor_jump != NULL); + inst->targets.push_back( nodes_to_instructions[node->successor_jump] ); + } + + goto_programt::targett next_inst = inst; + next_inst++; + + if(next_inst == program.instructions.end() && NULL != node->successor_next) { + goto_programt::targett goto_instruction = program.add_instruction(); + goto_instruction->make_goto(); + goto_instruction->targets.push_back(nodes_to_instructions[node->successor_next]); + } else if(next_inst != program.instructions.end() && instructions_to_nodes[next_inst] != node->successor_next + && NULL != node->successor_next) { + goto_programt temp_program; + temp_program.instructions.clear(); + goto_programt::targett goto_instruction = temp_program.add_instruction(); + goto_instruction->make_goto(); + goto_instruction->targets.push_back(nodes_to_instructions[node->successor_next]); + program.instructions.splice(next_inst, temp_program.instructions); + } + + } + + program.add_instruction(END_FUNCTION); + + program.update(); + + if(node_to_target != NULL) + { + for(std::map::const_iterator + it = nodes_to_instructions.begin(); + it != nodes_to_instructions.end(); ++it) + { + (*node_to_target)[it->first] = it->second; + } + + } + +} + + +CFG_nodet& CFGt::append_node(CFG_nodet node) +{ + return append_node_ptr(new CFG_nodet(node)); +} + + +CFG_nodet& CFGt::add_node(CFG_nodet node) +{ + return add_node_ptr(new CFG_nodet(node)); +} + + +CFG_nodet& CFGt::append_node_ptr(CFG_nodet* node) +{ + if(NULL == last_node_added) + { + assert(NULL == entry_node); + entry_node = node; + } else { + assert(NULL != entry_node); + last_node_added->successor_next = node; + } + last_node_added = node; + nodes.insert(node); + return *node; +} + + +CFG_nodet& CFGt::add_node_ptr(CFG_nodet* node) +{ + if(NULL == entry_node) + { + assert(NULL == last_node_added); + entry_node = node; + } + last_node_added = node; + nodes.insert(node); + return *node; +} + + +void CFGt::hoist_declarations() +{ + + CFG_nodet* first_non_decl = entry_node; + while(DECL == first_non_decl->type) + { + first_non_decl = first_non_decl->successor_next; + } + + nodes_vectort processed_declarations; + + bool changed = true; + while(changed) + { + changed = false; + for(nodes_sett::iterator it = nodes.begin(); it != nodes.end(); it++) + { + if((DECL == (*it)->type) && (processed_declarations.end() == std::find(processed_declarations.begin(), processed_declarations.end(), *it))) + { + changed = true; + processed_declarations.push_back(*it); + nodes_mappingt patchup_map; + patchup_map[*it] = (*it)->successor_next; + patchup_pointers(patchup_map); + break; + } + } + } + + for(unsigned int i = 0; i < processed_declarations.size(); i++) + { + if(0 == i) + { + entry_node = processed_declarations[i]; + } else { + processed_declarations[i-1]->successor_next = processed_declarations[i]; + } + if(processed_declarations.size() - 1 == i) + { + processed_declarations[i]->successor_next = first_non_decl; + } + } + + update(); + +} + + + +void CFGt::remove_self_loops() +{ + /* Loop analysis techniques can be made simpler if we are able to assume no + * self-looping CFG nodes. These can be removed by adding intermediate "SKIP" + * nodes -- this method does such removal + */ + + for(nodes_sett::iterator + it = nodes.begin(); + it != nodes.end(); + it++) { + if((*it)->successor_next == *it) { + CFG_nodet& skip_node = add_node(CFG_nodet(code_skipt(), (*it)->function, (*it)->location, SKIP)); + skip_node.successor_next = *it; + (*it)->successor_next = &skip_node; + } + if((*it)->successor_jump == *it) { + CFG_nodet& skip_node = add_node(CFG_nodet(code_skipt(), (*it)->function, (*it)->location, SKIP)); + skip_node.successor_next = *it; + (*it)->successor_jump = &skip_node; + } + } +} + + + + +void CFGt::add_returns() +{ + for(nodes_sett::iterator + it = nodes.begin(); + it != nodes.end(); + it++) { + if(RETURN != (*it)->type && NULL == (*it)->successor_next && NULL == (*it)->successor_jump) { + CFG_nodet& return_node = add_node(CFG_nodet(code_returnt(), (*it)->function, (*it)->location, RETURN)); + if(GOTO == (*it)->type) + { + (*it)->successor_jump = &return_node; + } else { + (*it)->successor_next = &return_node; + } + } + } +} + + + + +bool CFGt::all_non_back_edge_predecessors_ordered( + const CFG_nodet* dest, + const const_nodes_vectort& ordered, + const dominator_infot& dominator_info) const { + + for(nodes_sett::const_iterator source = dest->predecessors.begin(); source != dest->predecessors.end(); source++) + { + if(dominator_info.is_back_edge(**source, *dest)) + { + continue; + } + if(ordered.end() == std::find(ordered.begin(), ordered.end(), *source)) + { + return false; + } + } + + return true; + +} + +void CFGt::order_nodes() +{ + + dominator_infot dominator_info(*this); + + ordered_nodes.clear(); + + assert(dominator_info.cfg_is_reducible(DFST_numberingt(*this))); + + nodest todo; + todo.push_front(entry_node); + + unsigned int iterations_with_no_progress = 0; + + while(!todo.empty()) + { + assert(iterations_with_no_progress < nodes.size()); + iterations_with_no_progress++; + + CFG_nodet* current = todo.front(); + todo.pop_front(); + + if(!all_non_back_edge_predecessors_ordered(current, ordered_nodes, dominator_info)) + { + todo.push_back(current); + } else { + // This node is next in the ordered_nodes, so plug it in + current->id = ordered_nodes.size(); + ordered_nodes.push_back(current); + iterations_with_no_progress = 0; + + // Add any successors if they are not already mapped or queued + if((NULL != current->successor_next) && (ordered_nodes.end() == std::find(ordered_nodes.begin(), ordered_nodes.end(), current->successor_next)) && (todo.end() == find(todo.begin(), todo.end(), current->successor_next))) + { + todo.push_front(current->successor_next); + } + if((NULL != current->successor_jump) && (ordered_nodes.end() == std::find(ordered_nodes.begin(), ordered_nodes.end(), current->successor_jump)) && (todo.end() == find(todo.begin(), todo.end(), current->successor_jump))) + { + todo.push_front(current->successor_jump); + } + + } + } + +} + +void CFGt::order_nodes_raw() +{ + ordered_nodes.clear(); + + ordered_nodes.push_back(entry_node); + + for(nodes_sett::iterator it = nodes.begin(); it != nodes.end(); it++) + { + if((*it) != entry_node) + { + ordered_nodes.push_back(*it); + } + } +} + + + + +bool CFGt::contains(const CFG_nodet& n) const +{ + /* Note that this does *not* take reachability into account */ + return nodes.end() != std::find(nodes.begin(), nodes.end(), (CFG_nodet*)&n); +} + + + +void CFGt::calculate_prececessors() +{ + for(nodes_sett::iterator + it = nodes.begin(); + it != nodes.end(); + it++) + { + (*it)->predecessors.clear(); + } + + for(nodes_sett::iterator + it = nodes.begin(); + it != nodes.end(); + it++) + { + if(NULL != (*it)->successor_next) + { + (*it)->successor_next->predecessors.insert(*it); + } + + if(NULL != (*it)->successor_jump) + { + (*it)->successor_jump->predecessors.insert(*it); + } + + } + +} + + +void CFGt::sanity_check() const { + // Entry node has no predecessors + // Every node's successors and predecessors must be in the CFG + // A node's successors must be distinct + // A node may not have more predecessors than the size of the CFG + + assert(entry_node->predecessors.size() == 0); + + for(CFGt::nodes_sett::const_iterator it = nodes.begin(); it != nodes.end(); it++) + { + if(NULL != (*it)->successor_next) + { + nodes.end() != std::find(nodes.begin(), nodes.end(), (*it)->successor_next); + } + + if(NULL != (*it)->successor_jump) + { + nodes.end() != std::find(nodes.begin(), nodes.end(), (*it)->successor_jump); + } + + if(NULL != (*it)->successor_next && NULL != (*it)->successor_jump) + { + assert((*it)->successor_next != (*it)->successor_jump); + } + + assert((*it)->predecessors.size() <= size()); + + } + +} + +void CFGt::update() { + add_returns(); + remove_self_loops(); + collect_garbage(); + calculate_prececessors(); + order_nodes(); + assert(ordered_nodes.size() == nodes.size()); + sanity_check(); +} + +CFG_nodet& CFGt::get_initial() +{ + if(size() == 0) + throw "get_initial called on empty CFG"; + return *entry_node; +} + +const CFG_nodet& CFGt::get_initial() const +{ + if(size() == 0) + throw "get_initial called on empty CFG"; + return *entry_node; +} + + + + +void CFGt::transform_to_monolithic_loop() +{ + loop_infot existing_loops(*this); + + if(existing_loops.num_loops() <= 1) + { + // No need to do transformation if there is only one, or even zero, loops + return; + } + + std::vector headers; + for(std::list::iterator it = existing_loops.loops.begin(); it != existing_loops.loops.end(); it++) { + headers.push_back((CFG_nodet*)(it->header)); + } + + symbolt loop_to_jump_to; + { + // Add new declaration for variable + loop_to_jump_to.base_name = "loop_to_jump_to"; + loop_to_jump_to.name = "__k_induction::" + loop_to_jump_to.base_name.as_string(); + loop_to_jump_to.type = uint_type(); + context.add(loop_to_jump_to); + exprt loop_to_jump_to_expr = symbol_expr(loop_to_jump_to); + CFG_nodet& loop_to_jump_to_decl = add_node(CFG_nodet(code_declt(loop_to_jump_to_expr), + entry_node->function, + entry_node->location, + DECL)); + + loop_to_jump_to_decl.successor_next = entry_node; + entry_node = &loop_to_jump_to_decl; + } + + CFG_nodet& monolithic_header = add_node(CFG_nodet(code_assumet(), entry_node->function, entry_node->location, ASSUME)); + monolithic_header.reasoning_guard = binary_relation_exprt(symbol_expr(loop_to_jump_to), "<", from_integer(headers.size(), uint_type())); + + std::vector replacement_headers; + for(unsigned int i = 0; i < headers.size(); i++) + { + codet nil_code; + nil_code.make_nil(); + replacement_headers.push_back(&(add_node(CFG_nodet( + nil_code, + existing_loops.outer_loops[0]->header->function, + existing_loops.outer_loops[0]->header->location, + GOTO)))); + if(0 == i) + { + monolithic_header.successor_next = replacement_headers[i]; + } else { + replacement_headers[i-1]->successor_next = replacement_headers[i]; + } + + if(i == headers.size() - 1) + { + replacement_headers[i]->jump_condition = true_exprt(); + } else { + replacement_headers[i]->jump_condition = equality_exprt(symbol_expr(loop_to_jump_to), from_integer(i, uint_type())); + } + } + + // Go through every node in the CFG. If the node would have pointed to header i, then add a new node that + // assigns i to loop_to_jump_to, and then jumps to the monolithic header. + // Note that we *cannot* just make a single node for each loop that assigns to loop_to_jump_to: this would + // result in multiple loops. + for(nodes_sett::iterator it = nodes.begin(); it != nodes.end(); it++) + { + for(unsigned int i = 0; i < headers.size(); i++) + { + if((*it)->successor_next == headers[i]) + { + CFG_nodet& assign_to_loop_to_jump_to = add_node(CFG_nodet(code_assignt(symbol_expr(loop_to_jump_to), from_integer(i, uint_type())), headers[i]->function, headers[i]->location, ASSIGN)); + assign_to_loop_to_jump_to.successor_next = &monolithic_header; + (*it)->successor_next = &assign_to_loop_to_jump_to; + } + + if((*it)->successor_jump == headers[i]) + { + CFG_nodet& assign_to_loop_to_jump_to = add_node(CFG_nodet(code_assignt(symbol_expr(loop_to_jump_to), from_integer(i, uint_type())), headers[i]->function, headers[i]->location, ASSIGN)); + assign_to_loop_to_jump_to.successor_next = &monolithic_header; + (*it)->successor_jump = &assign_to_loop_to_jump_to; + } + } + } + + for(unsigned int i = 0; i < headers.size(); i++) + { + replacement_headers[i]->successor_jump = headers[i]; + } + + update(); + + // Sanity check + assert(1 == loop_infot(*this).num_loops()); + +} + + + + +goto_programt& find_main(goto_functionst& goto_functions, contextt& context) +{ + namespacet ns(context); + + for(goto_functionst::function_mapt::iterator + f_it = goto_functions.function_map.begin(); + ; + f_it++) + { + + if(f_it == goto_functions.function_map.end()) + { + throw "main symbol not found; please set an entry point"; + } + + /* It seems that the scheme for naming functions + * internally has recently changed within CPROVER, + * this this code may no longer be the most suitable + */ + if(ns.lookup(f_it->first).base_name == config.main) + { + assert(f_it->second.body_available); + return f_it->second.body; + } + } + assert(false); + +} + +goto_programt& find_enclosing_main(goto_functionst& goto_functions, contextt& context) +{ + namespacet ns(context); + + for(goto_functionst::function_mapt::iterator + f_it = goto_functions.function_map.begin(); + ; + f_it++) + { + + if(f_it == goto_functions.function_map.end()) + { + throw "main symbol not found; please set an entry point"; + } + + /* It seems that the scheme for naming functions + * internally has recently changed within CPROVER, + * this this code may no longer be the most suitable + */ + if(ns.lookup(f_it->first).base_name == "") + { + assert(f_it->second.body_available); + return f_it->second.body; + } + } + assert(false); + +} + + + +unsigned int CFGt::size() const +{ + return nodes.size(); +} + + + +CFGt::~CFGt() +{ + for(nodes_sett::iterator it = nodes.begin(); it != nodes.end(); it++) + { + delete *it; + } +} + + +const CFGt::const_nodes_vectort& CFGt::get_ordered_nodes() const +{ + return ordered_nodes; +} + +CFGt::nodes_sett::const_iterator CFGt::begin() const +{ + return nodes.begin(); +} + +CFGt::nodes_sett::const_iterator CFGt::end() const +{ + return nodes.end(); +} + +const CFG_nodet& CFGt::get_last_added_node() const +{ + assert(NULL != last_node_added); + return *last_node_added; +} + +CFG_nodet& CFGt::get_last_added_node() +{ + assert(NULL != last_node_added); + return *last_node_added; +} + + +void CFGt::collect_garbage() +{ + nodes_sett reachable; + + nodest todo; + todo.push_back(entry_node); + + while(!todo.empty()) + { + CFG_nodet* current = todo.front(); + todo.pop_front(); + reachable.insert(current); + if(NULL != current->successor_next && reachable.end() == reachable.find(current->successor_next) && + todo.end() == std::find(todo.begin(), todo.end(), current->successor_next)) + { + todo.push_back(current->successor_next); + } + if(NULL != current->successor_jump && reachable.end() == reachable.find(current->successor_jump) && + todo.end() == std::find(todo.begin(), todo.end(), current->successor_jump)) + { + todo.push_back(current->successor_jump); + } + } + + nodes_sett to_be_deleted; + + for(nodes_sett::iterator it = nodes.begin(); it != nodes.end(); it++) + { + if(reachable.end() == reachable.find(*it)) + { + delete *it; + to_be_deleted.insert(*it); + } + } + + for(nodes_sett::iterator it = to_be_deleted.begin(); it != to_be_deleted.end(); it++) + { + nodes.erase(std::find(nodes.begin(), nodes.end(), *it)); + } + +} + + + +void CFGt::unwind_loop(const CFG_nodet* header, const loop_infot& loop_info, unsigned int unwind_count) +{ + if(0 == unwind_count) + { + return; + } + const loop_infot::loopt* the_loop = NULL; + for(std::list::const_iterator it = loop_info.loops.begin(); it != loop_info.loops.end(); it++) + { + if(header == it->header) + { + the_loop = &(*it); + break; + } + } + assert(NULL != the_loop); + + nodes_sett non_loop_nodes; + for(nodes_sett::iterator it = nodes.begin(); it != nodes.end(); it++) + { + if(!the_loop->contains(**it)) + { + non_loop_nodes.insert(*it); + } + } + + nodes_vectort new_headers; + std::vector new_nodes; + for(unsigned int i = 0; i < unwind_count; i++) + { + assert(new_nodes.size() == i); + new_nodes.push_back(nodes_sett()); + nodes_mappingt patchup_map; + for(nodes_const_sett::iterator it = the_loop->begin(); it != the_loop->end(); it++) + { + CFG_nodet& new_node = add_node(CFG_nodet(**it)); + new_nodes[i].insert(&new_node); + if(*it == header) + { + assert(new_headers.size() == i); + new_headers.push_back(&new_node); + } else { + patchup_map[*it] = &new_node; + } + } + patchup_pointers_in_nodes(new_nodes[i], patchup_map); + if(i > 0) + { + nodes_mappingt header_patchup_map; + header_patchup_map[header] = new_headers[i]; + patchup_pointers_in_nodes(new_nodes[i-1], header_patchup_map); + } + } + + nodes_mappingt final_patchup_map; + final_patchup_map[header] = new_headers[0]; + patchup_pointers_in_nodes(non_loop_nodes, final_patchup_map); + + update(); + +} + + + + +void CFGt::check_cfg_point(goto_programt::const_targett inst, const CFG_nodet* node, std::map >* loop_header_instruction_points) const +{ + return; +} + +void CFGt::instrument_with_guards( + const std::map& instruction_to_invariant, + bool use_assert) +{ + for(const_nodes_vectort::const_iterator it = get_ordered_nodes().begin(); + it != get_ordered_nodes().end(); + it++) + { + std::map::const_iterator + find_it = instruction_to_invariant.find(*it); + + if(find_it == instruction_to_invariant.end()) + continue; + + CFG_nodet& instr_node = + use_assert ? + add_node( + CFG_nodet( + code_assertt(), + (*it)->function, + (*it)->location, ASSERT)) + : add_node( + CFG_nodet( + code_assumet(), + (*it)->function, + (*it)->location, ASSUME)); + + instr_node.reasoning_guard = find_it->second; + CFGt::nodes_mappingt patchup_map; + patchup_map[*it] = &instr_node; + patchup_pointers(patchup_map); + instr_node.successor_next = (CFG_nodet*)*it; + } + + update(); + +} diff --git a/src/goto-programs/control_flow_graph.h b/src/goto-programs/control_flow_graph.h new file mode 100644 index 00000000000..3ea9ff8a5d7 --- /dev/null +++ b/src/goto-programs/control_flow_graph.h @@ -0,0 +1,187 @@ +/* + * control_flow_graph.h + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + +#ifndef CONTROL_FLOW_GRAPH_H_ +#define CONTROL_FLOW_GRAPH_H_ + +#include +#include + +#include + +#define forall_cfg_nodes(it, cfg) \ + for(CFGt::nodes_sett::const_iterator it = (cfg).begin(); \ + it != (cfg).end(); it++) + +/* Control Flow Graphs */ + +class CFG_nodet +{ +public: + + /* code, function, location and type determine the instruction represented by the CFG node */ + codet code; + irep_idt function; + locationt location; + goto_program_instruction_typet type; + + exprt reasoning_guard; /* Used if instruction is assert/assume, otherwise meaningless */ + exprt jump_condition; /* Used if successor_jump is not NULL. In this case, if jump_condition is true then the successor is successor_jump */ + CFG_nodet* successor_next; /* Default successor, or successor if jump_condition is false */ + CFG_nodet* successor_jump; /* Successor if jump condition is true */ + + std::set predecessors; /* Nodes that point to this node via successor_next or successor_jump fields. Set when parent CFG is updated */ + int id; /* Id for node, which is set when the parent CFG is updated */ + + CFG_nodet(goto_programt::instructiont& goto_program_instruction); + + CFG_nodet(codet code, irep_idt function, locationt location, goto_program_instruction_typet type) + : code(code), function(function), location(location), type(type) + { + reasoning_guard = false_exprt(); + jump_condition = false_exprt(); + successor_next = NULL; + successor_jump = NULL; + } + + virtual void output_special_info(std::ostream&) const; + + bool operator == (const CFG_nodet& other) const; + +}; + +class dominator_infot; + +class loop_infot; + +class CFGt +{ + +public: + + typedef std::list nodest; + typedef std::list const_nodest; + + typedef std::set nodes_sett; + typedef std::set nodes_const_sett; + typedef std::map nodes_mappingt; + + typedef std::vector nodes_vectort; + typedef std::vector const_nodes_vectort; + + contextt& context; + +private: + CFG_nodet* entry_node; + CFG_nodet* last_node_added; + nodes_sett nodes; + const_nodes_vectort ordered_nodes; + +public: + + CFGt(const CFGt& other); + + CFGt(contextt& context) : context(context) + { + nodes.clear(); + entry_node = NULL; + last_node_added = NULL; + } + + virtual ~CFGt(); + + void initialize(goto_programt& program); + + const const_nodes_vectort& get_ordered_nodes() const; + + nodes_sett::const_iterator begin() const; + + nodes_sett::const_iterator end() const; + + void output(std::ostream& out, bool show_predecessors=false) const; + + void output_node(const CFG_nodet& node, std::ostream& out, bool show_predecessors = false) const; + + void to_goto_program( + goto_programt& program, + std::map >* loop_header_instruction_points = NULL, + std::map* node_to_target = NULL) const; + + virtual CFG_nodet& append_node(CFG_nodet node); + + virtual CFG_nodet& add_node(CFG_nodet node); + + void patchup_pointers(std::map& patchup_map); + + // Transformations may lead to CFG nodes with a non-trivial jump condition, but only one + // successor. This method changes such nodes to 'assume's + void fix_jumps(); + + void update(); + + void transform_to_monolithic_loop(); + + // Re-arranges CFG so that all declarations appear at start + void hoist_declarations(); + + + const CFG_nodet& get_initial() const; + + CFG_nodet& get_initial(); + + unsigned int size() const; + + bool contains(const CFG_nodet& n) const; + + const CFG_nodet& get_last_added_node() const; + + CFG_nodet& get_last_added_node(); + + void order_nodes_raw(); // Should only be called for debugging purposes + + void unwind_loop(const CFG_nodet* header, const loop_infot& loop_info, unsigned int unwind_count); + + //instrument the control flow graph with assume or assert statements + void instrument_with_guards( + const std::map& instruction_to_invariant, + bool assert); + +private: + void add_returns(); + void remove_self_loops(); + void calculate_prececessors(); + void collect_garbage(); + void sanity_check() const; + bool all_non_back_edge_predecessors_ordered( + const CFG_nodet* dest, + const const_nodes_vectort& ordered, + const dominator_infot& dominator_info) const; + void order_nodes(); + +protected: + CFG_nodet& append_node_ptr(CFG_nodet* node); + CFG_nodet& add_node_ptr(CFG_nodet* node); + + virtual void check_cfg_point(goto_programt::const_targett inst, const CFG_nodet* node, std::map >* loop_header_instruction_points) const; + + +}; + + +/* Some utility functions */ + +void patchup_pointers_in_nodes(CFGt::nodest& nodes, std::map& patchup_map); + +void patchup_pointers_in_nodes(CFGt::nodes_sett& nodes, std::map& patchup_map); + +goto_programt& find_main(goto_functionst& goto_functions, contextt& context); + +goto_programt& find_enclosing_main(goto_functionst& goto_functions, contextt& context); + +void redirect_pointer(CFG_nodet*& pointer_to_redirect, CFGt::nodes_mappingt& original_to_cloned); + +#endif diff --git a/src/goto-programs/depth_first_spanning_tree.cpp b/src/goto-programs/depth_first_spanning_tree.cpp new file mode 100644 index 00000000000..f86bae1f211 --- /dev/null +++ b/src/goto-programs/depth_first_spanning_tree.cpp @@ -0,0 +1,85 @@ +/* + * depth_first_spanning_tree.cpp + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + +#include "depth_first_spanning_tree.h" + +int DFST_numberingt::search(const CFG_nodet& n, CFGt::nodes_const_sett& visited, int c) +{ + assert(c >= 0); + if(n == the_cfg.get_initial()) { + assert(c == (int)the_cfg.size()); + } else { + assert(c <= (int)the_cfg.size()); + } + + /* The node should not yet have been visited */ + assert(visited.end() == visited.find(&n)); + + visited.insert(&n); + + if((NULL != n.successor_next) && (visited.end() == visited.find(n.successor_next))) + { + c = search(*(n.successor_next), visited, c); + } + + if((NULL != n.successor_jump) && (visited.end() == visited.find(n.successor_jump))) + { + c = search(*(n.successor_jump), visited, c); + } + + c--; + dfn[&n] = c; + + /* c should remain non-negative, and should be zero only on leaving + * the root node + */ + if(n == the_cfg.get_initial()) { + assert(0 == c); + } else { + assert(c > 0); + } + + return c; + +} + +DFST_numberingt::DFST_numberingt(const CFGt& cfg) : the_cfg(cfg) +{ + CFGt::nodes_const_sett visited; + search(the_cfg.get_initial(), visited, the_cfg.size()); +} + +std::ostream& operator<<(std::ostream& os, const DFST_numberingt& dfst) +{ + for(CFGt::nodes_sett::const_iterator it = dfst.the_cfg.begin(); it != dfst.the_cfg.end(); it++) + { + dfst.the_cfg.output_node(**it, os, false); + os << " DFST number: " << ((DFST_numberingt&)dfst).dfn[*it]<< "\n"; + } + + return os; + +} + +bool DFST_numberingt::is_retreating(const CFG_nodet& d, const CFG_nodet& n) const +{ + std::map::const_iterator it_n = dfn.find(&n); + assert(it_n != dfn.end()); + + std::map::const_iterator it_d = dfn.find(&d); + assert(it_d != dfn.end()); + + return it_n->second <= it_d->second; +} + + +unsigned int DFST_numberingt::get_number(const CFG_nodet* n) const +{ + std::map::const_iterator it_n = dfn.find(n); + assert(it_n != dfn.end()); + return it_n->second; +} diff --git a/src/goto-programs/depth_first_spanning_tree.h b/src/goto-programs/depth_first_spanning_tree.h new file mode 100644 index 00000000000..0488708c8dd --- /dev/null +++ b/src/goto-programs/depth_first_spanning_tree.h @@ -0,0 +1,34 @@ +/* + * depth_first_spanning_tree.h + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + +#ifndef DEPTH_FIRST_SPANNING_TREE_H_ +#define DEPTH_FIRST_SPANNING_TREE_H_ + +#include "control_flow_graph.h" + +class DFST_numberingt { + +public: + DFST_numberingt(const CFGt& cfg); + + bool is_retreating(const CFG_nodet& d, const CFG_nodet& n) const; + + unsigned int get_number(const CFG_nodet* n) const; + + friend std::ostream& operator<<(std::ostream& os, const DFST_numberingt& dom); + +private: + + int search(const CFG_nodet& n, CFGt::nodes_const_sett& visited, int c); + + std::map dfn; + + const CFGt& the_cfg; + +}; + +#endif diff --git a/src/goto-programs/destructor.cpp b/src/goto-programs/destructor.cpp new file mode 100644 index 00000000000..823497df99a --- /dev/null +++ b/src/goto-programs/destructor.cpp @@ -0,0 +1,72 @@ +/*******************************************************************\ + +Module: Destructor Calls + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "destructor.h" + +/*******************************************************************\ + +Function: get_destructor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +code_function_callt get_destructor( + const namespacet &ns, + const typet &type) +{ + if(type.id()==ID_symbol) + { + return get_destructor(ns, ns.follow(type)); + } + else if(type.id()==ID_struct) + { + const struct_typet &struct_type=to_struct_type(type); + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it->type().id()==ID_code) + { + const code_typet &code_type=to_code_type(it->type()); + + if(code_type.return_type().id()==ID_destructor && + code_type.arguments().size()==1) + { + const typet &arg_type=code_type.arguments().front().type(); + + if(arg_type.id()==ID_pointer && + ns.follow(arg_type.subtype())==type) + { + exprt symbol_expr(ID_symbol, it->type()); + symbol_expr.set(ID_identifier, it->get(ID_name)); + + code_function_callt function_call; + function_call.function()=symbol_expr; + + return function_call; + } + } + } + } + } + + return static_cast(get_nil_irep()); +} diff --git a/src/goto-programs/destructor.h b/src/goto-programs/destructor.h new file mode 100644 index 00000000000..4afce1110de --- /dev/null +++ b/src/goto-programs/destructor.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: Destructor Calls + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_DESTRUCTOR_H +#define CPROVER_GOTO_PROGRAMS_DESTRUCTOR_H + +#include +#include + +code_function_callt get_destructor( + const namespacet &ns, + const typet &type); + +#endif diff --git a/src/goto-programs/dominator_info.cpp b/src/goto-programs/dominator_info.cpp new file mode 100644 index 00000000000..aeaf8672acc --- /dev/null +++ b/src/goto-programs/dominator_info.cpp @@ -0,0 +1,184 @@ +/* + * dominator_info.cpp + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + + +#include "dominator_info.h" + + +/* This is the algorithm in Fig. 10.52 of the Dragon book. + * + */ +dominator_infot::dominator_infot(const CFGt& cfg) : the_cfg(cfg) +{ + // Visiting nodes in a depth-first order *drastically* speeds up + // convergence speed for dominator detection + + DFST_numberingt numbering(the_cfg); + + std::vector depth_first_ordered_nodes(the_cfg.size()); + for(unsigned int i = 0; i < depth_first_ordered_nodes.size(); i++) + { + depth_first_ordered_nodes[i] = NULL; + } + for(CFGt::nodes_sett::const_iterator it = the_cfg.begin(); it != the_cfg.end(); it++) + { + depth_first_ordered_nodes[numbering.get_number(*it)] = *it; + } + for(unsigned int i = 0; i < depth_first_ordered_nodes.size(); i++) + { + assert(depth_first_ordered_nodes[i] != NULL); + } + + dominators_of.clear(); + + CFGt::nodes_const_sett N; + for(CFGt::nodes_sett::const_iterator it = the_cfg.begin(); it != the_cfg.end(); it++) + { + N.insert(*it); + } + + const CFG_nodet& n_0 = cfg.get_initial(); + + dominators_of[&n_0] = CFGt::nodes_const_sett(); + dominators_of[&n_0].insert(&n_0); + + for(CFGt::nodes_sett::const_iterator + it = cfg.begin(); + it != cfg.end(); + it++) + { + + if(&n_0 != *it) + { + dominators_of[*it] = N; + } + } + + bool changed = true; + while(changed) + { + + changed = false; + + for(unsigned int i = 0; i < depth_first_ordered_nodes.size(); i++) + { + const CFG_nodet* node = depth_first_ordered_nodes[i]; + + if(&n_0 != node) + { + + CFGt::nodes_const_sett intersection_of_incoming_dominators; + + for(std::set::const_iterator + it2 = node->predecessors.begin(); + it2 != node->predecessors.end(); + it2++) + { + if(node->predecessors.begin() == it2) + { + intersection_of_incoming_dominators = dominators_of[*it2]; + } else { + CFGt::nodes_const_sett intersection; + set_intersection( + intersection_of_incoming_dominators.begin(), + intersection_of_incoming_dominators.end(), + dominators_of[*it2].begin(), + dominators_of[*it2].end(), + std::insert_iterator(intersection, intersection.begin())); + + intersection_of_incoming_dominators = intersection; + } + + } + + intersection_of_incoming_dominators.insert(node); + + if(dominators_of[node] != intersection_of_incoming_dominators) { + dominators_of[node] = intersection_of_incoming_dominators; + changed = true; + } + + } + + } + + } + +} + + + +std::ostream& operator<<(std::ostream& os, const dominator_infot& dom) +{ + for(std::map::const_iterator i = dom.dominators_of.begin(); i != dom.dominators_of.end(); i++) + { + dom.the_cfg.output_node(*(i->first), os); + + os << " dominated by {\n "; + for(CFGt::nodes_const_sett::const_iterator j = i->second.begin(); j != i->second.end(); j++) + { + if(j != i->second.begin()) { + os << ", "; + } + + dom.the_cfg.output_node(**j, os); + + } + + os << " }\n\n"; + + } + + + return os; +} + + +bool dominator_infot::dominates(const CFG_nodet& first, const CFG_nodet& second) const +{ + dominator_mapt::const_iterator dominators_of_second = dominators_of.find(&second); + assert(dominators_of_second != dominators_of.end()); + return dominators_of_second->second.find(&first) != dominators_of_second->second.end(); +} + +bool dominator_infot::is_back_edge(const CFG_nodet& source, const CFG_nodet& dest) const +{ + return dominates(dest, source); +} + +bool dominator_infot::cfg_is_reducible(const DFST_numberingt& dfst) +{ + for(CFGt::nodes_sett::const_iterator + cfg_it = the_cfg.begin(); + cfg_it != the_cfg.end(); + cfg_it++) + { + const CFG_nodet& d = **cfg_it; + + for(std::set::const_iterator + predecessor_it = d.predecessors.begin(); + predecessor_it != d.predecessors.end(); + predecessor_it++) + { + const CFG_nodet& n = **predecessor_it; + + /* We have an edge of the form n -> d + * Need to check whether the edge is retreating + */ + + if(dfst.is_retreating(n, d)) + { + if(!is_back_edge(n, d)) + { + return false; + } + } + } + + } + return true; +} diff --git a/src/goto-programs/dominator_info.h b/src/goto-programs/dominator_info.h new file mode 100644 index 00000000000..faf62273dbd --- /dev/null +++ b/src/goto-programs/dominator_info.h @@ -0,0 +1,40 @@ +/* + * dominator_info.h + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + +#ifndef DOMINATOR_INFO_H_ +#define DOMINATOR_INFO_H_ + +#include "control_flow_graph.h" +#include "depth_first_spanning_tree.h" + +class dominator_infot { + +public: + dominator_infot(const CFGt& cfg); + + bool dominates(const CFG_nodet& first, const CFG_nodet& second) const; + + /* Determines whether instruction source dominates instruction dest */ + bool is_back_edge(const CFG_nodet& source, const CFG_nodet& dest) const; + + /* Determines whether the CFG is reducible */ + bool cfg_is_reducible(const DFST_numberingt& dfst); + + friend std::ostream& operator<<(std::ostream& os, const dominator_infot& dom); + +private: + + typedef std::map dominator_mapt; + + /* dominators_of[n] is all the nodes that dominate n. See page 670 of Dragon Book, first edition */ + dominator_mapt dominators_of; + + const CFGt& the_cfg; + +}; + +#endif diff --git a/src/goto-programs/dump_c.cpp b/src/goto-programs/dump_c.cpp new file mode 100644 index 00000000000..02117bc3573 --- /dev/null +++ b/src/goto-programs/dump_c.cpp @@ -0,0 +1,218 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "dump_c.h" + +/*******************************************************************\ + +Function: convert_id + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string convert_id(const irep_idt &id) +{ + std::string result=id2string(id); + + if(has_prefix(result, "c::")) + result=std::string(result, 3, std::string::npos); + + return result; +} + +/*******************************************************************\ + +Function: gen_indent + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void gen_indent(unsigned indent, std::ostream &out) +{ + for(unsigned i=0; itype) + { + case GOTO: + break; + + case ASSUME: + break; + + case ASSERT: + break; + + case OTHER: + break; + + case DECL: + break; + + case SKIP: + case LOCATION: + case END_FUNCTION: + // ignore + break; + + case DEAD: + out << "/* ignoring DEAD */" << std::endl << std::endl; + break; + + case START_THREAD: + out << "/* ignoring START_THREAD */" << std::endl << std::endl; + break; + + case END_THREAD: + out << "/* ignoring END_THREAD */" << std::endl << std::endl; + break; + + case ATOMIC_BEGIN: + out << "/* ignoring ATOMIC_BEGIN */" << std::endl << std::endl; + break; + + case ATOMIC_END: + out << "/* ignoring ATOMIC_END */" << std::endl << std::endl; + break; + + case RETURN: + gen_indent(indent, out); + out << "return"; + out << ";" << std::endl; + break; + + case ASSIGN: + break; + + case FUNCTION_CALL: + break; + + default: + throw "unexpected goto instruction"; + } +} + +/*******************************************************************\ + +Function: dump_c + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dump_c( + const goto_functionst::function_mapt::const_iterator f_it, + const namespacet &ns, + std::ostream &out) +{ + out << "/* " << f_it->first << " */" << std::endl; + + const symbolt &symbol=ns.lookup(f_it->first); + + out << "/* " << symbol.location << " */" << std::endl; + out << std::endl; + + const code_typet &type=to_code_type(ns.follow(symbol.type)); + + out << type2c(type.return_type(), ns); + out << " "; + out << convert_id(symbol.name); + + const code_typet::argumentst &arguments= + type.arguments(); + + if(arguments.empty()) + { + out << "(void)" << std::endl; + } + else + { + out << "(" << std::endl; + for(code_typet::argumentst::const_iterator + a_it=arguments.begin(); + a_it!=arguments.end(); + a_it++) + { + if(a_it!=arguments.begin()) out << ", " << std::endl; + out << " "; + out << type2c(a_it->type(), ns); + out << " " << convert_id(a_it->get_identifier()); + } + out << ")" << std::endl; + } + + out << "{" << std::endl; + + const goto_programt &body=f_it->second.body; + + forall_goto_program_instructions(it, body) + dump_c(1, body, it, ns, out); + + out << "} /* end of " << symbol.name << " */" << std::endl; + out << std::endl; +} + +/*******************************************************************\ + +Function: dump_c + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dump_c( + const goto_functionst &src, + const namespacet &ns, + std::ostream &out) +{ + forall_goto_functions(it, src) + if(it->second.body_available && + it->first!="main") + dump_c(it, ns, out); +} diff --git a/src/goto-programs/dump_c.h b/src/goto-programs/dump_c.h new file mode 100644 index 00000000000..b014dba5b89 --- /dev/null +++ b/src/goto-programs/dump_c.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: Dump C from Goto Program + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAM_DUMP_C_H +#define CPROVER_GOTO_PROGRAM_DUMP_C_H + +#include "goto_functions.h" + +void dump_c( + const goto_functionst &src, + const namespacet &ns, + std::ostream &out); + +#endif diff --git a/src/goto-programs/dynamic_memory.cpp b/src/goto-programs/dynamic_memory.cpp new file mode 100644 index 00000000000..2d7af4a6ce3 --- /dev/null +++ b/src/goto-programs/dynamic_memory.cpp @@ -0,0 +1,111 @@ +/*******************************************************************\ + +Module: Abstraction for malloc + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include "dynamic_memory.h" + +/*******************************************************************\ + +Function: valid_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt valid_object(const namespacet &ns, const exprt &pointer) +{ + // we check __CPROVER_deallocated! + const symbolt &deallocated_symbol=ns.lookup(CPROVER_PREFIX "deallocated"); + + exprt same_object_expr(ID_same_object, bool_typet()); + same_object_expr.copy_to_operands(pointer, symbol_expr(deallocated_symbol)); + + return not_exprt(same_object_expr); +} + +/*******************************************************************\ + +Function: dynamic_size + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt dynamic_size(const namespacet &ns, const exprt &pointer) +{ + // replace with CPROVER_alloc_size[POINTER_OBJECT(...)] + + exprt object_expr(ID_pointer_object, size_type()); + object_expr.copy_to_operands(pointer); + + exprt alloc_array=symbol_expr(ns.lookup(CPROVER_PREFIX "alloc_size")); + + #if 0 + std::cout << "AA: " << size_type().pretty() << std::endl; + std::cout << "AA: " << ns.follow(alloc_array.type()).pretty() << std::endl; + #endif + + assert(ns.follow(alloc_array.type()).subtype()==size_type()); + + index_exprt result; + result.array()=alloc_array; + result.index()=object_expr; + result.type()=size_type(); + + return result; +} + +/*******************************************************************\ + +Function: pointer_object_has_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt pointer_object_has_type(const exprt &pointer, const typet &type) +{ + return false_exprt(); +} + +/*******************************************************************\ + +Function: dynamic_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt dynamic_object(const exprt &pointer) +{ + exprt dynamic_expr(ID_dynamic_object, bool_typet()); + dynamic_expr.copy_to_operands(pointer); + return dynamic_expr; +} + diff --git a/src/goto-programs/dynamic_memory.h b/src/goto-programs/dynamic_memory.h new file mode 100644 index 00000000000..24262e60dd7 --- /dev/null +++ b/src/goto-programs/dynamic_memory.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: Abstraction for malloc + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_DYNAMIC_MEMORY_H +#define CPROVER_GOTO_PROGRAMS_DYNAMIC_MEMORY_H + +#include + +exprt valid_object(const namespacet &ns, const exprt &pointer); +exprt dynamic_size(const namespacet &ns, const exprt &pointer); +exprt pointer_object_has_type(const namespacet &ns, const exprt &pointer, const typet &type); +exprt dynamic_object(const exprt &pointer); + +#endif diff --git a/src/goto-programs/flow_insensitive_analysis.cpp b/src/goto-programs/flow_insensitive_analysis.cpp new file mode 100644 index 00000000000..08be226f3fb --- /dev/null +++ b/src/goto-programs/flow_insensitive_analysis.cpp @@ -0,0 +1,628 @@ +/*******************************************************************\ + +Module: Flow Insensitive Static Analysis + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "flow_insensitive_analysis.h" + +//#include +//#include + +/*******************************************************************\ + +Function: flow_insensitive_abstract_domain_baset::get_guard + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt flow_insensitive_abstract_domain_baset::get_guard( + locationt from, + locationt to) const +{ + if(!from->is_goto()) + return true_exprt(); + + locationt next=from; + next++; + + if(next==to) + { + exprt tmp(from->guard); + tmp.make_not(); + return tmp; + } + + return from->guard; +} + +/*******************************************************************\ + +Function: flow_insensitive_abstract_domain_baset::get_return_lhs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt flow_insensitive_abstract_domain_baset::get_return_lhs(locationt to) const +{ + // get predecessor of "to" + + to--; + + if(to->is_end_function()) + return static_cast(get_nil_irep()); + + // must be the function call + assert(to->is_function_call()); + + const code_function_callt &code= + to_code_function_call(to_code(to->code)); + + return code.lhs(); +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void flow_insensitive_analysis_baset::operator()( + const goto_functionst &goto_functions) +{ + initialize(goto_functions); + fixedpoint(goto_functions); +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void flow_insensitive_analysis_baset::operator()( + const goto_programt &goto_program) +{ + initialize(goto_program); + goto_functionst goto_functions; + fixedpoint(goto_program, goto_functions); +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void flow_insensitive_analysis_baset::output( + const goto_functionst &goto_functions, + std::ostream &out) const +{ + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + { + out << "////" << std::endl; + out << "//// Function: " << f_it->first << std::endl; + out << "////" << std::endl; + out << std::endl; + + output(f_it->second.body, f_it->first, out); + } +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void flow_insensitive_analysis_baset::output( + const goto_programt &goto_program, + const irep_idt &identifier, + std::ostream &out) const +{ + get_state().output(ns, out); +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::get_next + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +flow_insensitive_analysis_baset::locationt flow_insensitive_analysis_baset::get_next( + working_sett &working_set) +{ + assert(!working_set.empty()); + +// working_sett::iterator i=working_set.begin(); +// locationt l=i->second; +// working_set.erase(i); + +// pop_heap(working_set.begin(), working_set.end()); + locationt l=working_set.top(); + working_set.pop(); + + return l; +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::fixedpoint + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool flow_insensitive_analysis_baset::fixedpoint( + const goto_programt &goto_program, + const goto_functionst &goto_functions) +{ + if(goto_program.instructions.empty()) + return false; + + working_sett working_set; + +// make_heap(working_set.begin(), working_set.end()); + + put_in_working_set( + working_set, + goto_program.instructions.begin()); + + bool new_data=false; + + while(!working_set.empty()) + { + locationt l=get_next(working_set); + + if(visit(l, working_set, goto_program, goto_functions)) + new_data=true; + } + + return new_data; +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::visit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool flow_insensitive_analysis_baset::visit( + locationt l, + working_sett &working_set, + const goto_programt &goto_program, + const goto_functionst &goto_functions) +{ + bool new_data=false; + + #if 0 + std::cout << "Visiting: " << l->function << " " << + l->location_number << std::endl; + #endif + + goto_programt::const_targetst successors; + goto_program.get_successors(l, successors); + + seen_locations.insert(l); + if (statistics.find(l)==statistics.end()) + statistics[l]=1; + else + statistics[l]++; + + for(goto_programt::const_targetst::const_iterator + it=successors.begin(); + it!=successors.end(); + it++) + { + locationt to_l=*it; + + if(to_l==goto_program.instructions.end()) + continue; + + bool changed=false; + + if(l->is_function_call()) + { + // this is a big special case + const code_function_callt &code= + to_code_function_call(to_code(l->code)); + + changed= + do_function_call_rec( + l, + code.function(), + code.arguments(), + get_state(), + goto_functions); + } + else + changed = get_state().transform(ns, l, to_l); + + if(changed || !seen(to_l)) + { + new_data=true; + put_in_working_set(working_set, to_l); + } + } + +// if (l->function.as_string().find("debug")!=std::string::npos) +// std::cout << l->function << std::endl; //=="c::messages::debug") + +// { +// static unsigned state_cntr=0; +// std::string s("pastate"); s += i2string(state_cntr); +// std::ofstream f(s.c_str()); +// goto_program.output_instruction(ns, "", f, l); +// f << std::endl; +// get_state().output(ns, f); +// f.close(); +// state_cntr++; +// } + + return new_data; +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::do_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool flow_insensitive_analysis_baset::do_function_call( + locationt l_call, + const goto_functionst &goto_functions, + const goto_functionst::function_mapt::const_iterator f_it, + const exprt::operandst &arguments, + statet &state) +{ + const goto_functionst::goto_functiont &goto_function=f_it->second; + + if(!goto_function.body_available) + { + const code_function_callt &code = + to_code_function_call(to_code(l_call->code)); + + goto_programt temp; + + goto_programt::targett r=temp.add_instruction(); + r->make_return(); + r->code=code_returnt(); + r->function=f_it->first; + r->location_number=0; + + exprt rhs=side_effect_expr_nondett(code.lhs().type()); + r->code.move_to_operands(rhs); + + goto_programt::targett t=temp.add_instruction(END_FUNCTION); + t->code.set("identifier", code.function()); + t->function=f_it->first; + t->location_number=1; + + locationt l_next=l_call; l_next++; + bool new_data=state.transform(ns, l_call, r); + new_data = state.transform(ns, r, t) || new_data; + new_data = state.transform(ns, t, l_next) || new_data; + + return new_data; + } + + assert(!goto_function.body.instructions.empty()); + + bool new_data=false; + + { + // get the state at the beginning of the function + locationt l_begin=goto_function.body.instructions.begin(); + + // do the edge from the call site to the beginning of the function + new_data=state.transform(ns, l_call, l_begin); + + // do each function at least once + if(functions_done.find(f_it->first)== + functions_done.end()) + { + new_data=true; + functions_done.insert(f_it->first); + } + + // do we need to do the fixedpoint of the body? + if(new_data) + { + // recursive call + fixedpoint(goto_function.body, goto_functions); + new_data=true; // could be reset by fixedpoint + } + } + + { + // get location at end of procedure + locationt l_end=--goto_function.body.instructions.end(); + + assert(l_end->is_end_function()); + + // do edge from end of function to instruction after call + locationt l_next=l_call; + l_next++; + new_data = state.transform(ns, l_end, l_next) || new_data; + } + + return new_data; +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::do_function_call_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool flow_insensitive_analysis_baset::do_function_call_rec( + locationt l_call, + const exprt &function, + const exprt::operandst &arguments, + statet &state, + const goto_functionst &goto_functions) +{ + bool new_data = false; + + if(function.id()=="symbol") + { + const irep_idt &identifier=function.get("identifier"); + + if(recursion_set.find(identifier)!=recursion_set.end()) + { + // recursion detected! + return false; + } + else + recursion_set.insert(identifier); + + goto_functionst::function_mapt::const_iterator it= + goto_functions.function_map.find(identifier); + + if(it==goto_functions.function_map.end()) + { + std::cout << "failed to find function " << id2string(identifier) << std::endl; + throw "failed to find function "+id2string(identifier); + } + + new_data = + do_function_call( + l_call, + goto_functions, + it, + arguments, + state); + + recursion_set.erase(identifier); + } + else if(function.id()=="if") + { + if(function.operands().size()!=3) + throw "if takes three arguments"; + + new_data = + do_function_call_rec( + l_call, + function.op1(), + arguments, + state, + goto_functions); + + new_data = + do_function_call_rec( + l_call, + function.op2(), + arguments, + state, + goto_functions) || new_data; + } + else if(function.id()=="dereference") + { + // get value set + expr_sett values; + + get_reference_set(function, values); + + // now call all of these + for(expr_sett::const_iterator it=values.begin(); + it!=values.end(); + it++) + { + if(it->id()=="object_descriptor") + { + const object_descriptor_exprt &o=to_object_descriptor_expr(*it); + + // ... but only if they are actually functions. + goto_functionst::function_mapt::const_iterator it= + goto_functions.function_map.find(o.object().get("identifier")); + + if (it!=goto_functions.function_map.end()) + { + new_data = + do_function_call_rec( + l_call, + o.object(), + arguments, + state, + goto_functions) || new_data; + } + } + } + } + else if(function.id()=="NULL-object") + { + // ignore, can't be a function + } + else if(function.id()=="member" || function.id()=="index") + { + // ignore, can't be a function + } + else if(function.id()=="builtin-function") + { + // ignore + } + else + { + throw "unexpected function_call argument: "+ + function.id_string(); + } + return new_data; +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::fixedpoint + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void flow_insensitive_analysis_baset::fixedpoint( + const goto_functionst &goto_functions) +{ + // do each function at least once + + for(goto_functionst::function_mapt::const_iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + if(functions_done.find(it->first)== + functions_done.end()) + { + fixedpoint(it, goto_functions); + } +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::fixedpoint + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool flow_insensitive_analysis_baset::fixedpoint( + const goto_functionst::function_mapt::const_iterator it, + const goto_functionst &goto_functions) +{ + functions_done.insert(it->first); + return fixedpoint(it->second.body, goto_functions); +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::update + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void flow_insensitive_analysis_baset::update( + const goto_functionst &goto_functions) +{ + // no need to copy value sets around +} + +/*******************************************************************\ + +Function: flow_insensitive_analysis_baset::update + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void flow_insensitive_analysis_baset::update( + const goto_programt &goto_program) +{ + // no need to copy value sets around +} diff --git a/src/goto-programs/flow_insensitive_analysis.h b/src/goto-programs/flow_insensitive_analysis.h new file mode 100644 index 00000000000..51d58a3e1b7 --- /dev/null +++ b/src/goto-programs/flow_insensitive_analysis.h @@ -0,0 +1,263 @@ +/*******************************************************************\ + +Module: Flow Insensitive Static Analysis + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#ifndef FLOW_INSENSITIVE_ANALYSIS_H_ +#define FLOW_INSENSITIVE_ANALYSIS_H_ + +#include +#include +#include + +#include "goto_functions.h" + + +// don't use me -- I am just a base class +// please derive from me +class flow_insensitive_abstract_domain_baset +{ +public: + flow_insensitive_abstract_domain_baset() + { + } + + typedef goto_programt::const_targett locationt; + + virtual void initialize( const namespacet &ns )=0; + + virtual bool transform( + const namespacet &ns, + locationt from, + locationt to)=0; + + virtual ~flow_insensitive_abstract_domain_baset() + { + } + + virtual void output( + const namespacet &ns, + std::ostream &out) const + { + } + + typedef hash_set_cont expr_sett; + + virtual void get_reference_set( + const namespacet &ns, + const exprt &expr, + expr_sett &expr_set) + { + // dummy, overload me! + expr_set.clear(); + } + + virtual void clear(void)=0; + +protected: + friend class flow_insensitive_analysis_baset; + bool changed; + // utilities + + // get guard of a conditional edge + exprt get_guard(locationt from, locationt to) const; + + // get lhs that return value is assigned to + // for an edge that returns from a function + exprt get_return_lhs(locationt to) const; +}; + +class flow_insensitive_analysis_baset +{ +public: + typedef flow_insensitive_abstract_domain_baset statet; + typedef goto_programt::const_targett locationt; + + std::set seen_locations; + + std::map statistics; + + bool seen( const locationt& l ) + { + return (seen_locations.find(l)!=seen_locations.end()); + } + + flow_insensitive_analysis_baset(const namespacet &_ns): + ns(_ns), + initialized(false) + { + } + + virtual void initialize( + const goto_programt &goto_program) + { + if(!initialized) + { + initialized=true; + } + } + + virtual void initialize( + const goto_functionst &goto_functions) + { + if(!initialized) + { + initialized=true; + } + } + + virtual void update(const goto_programt &goto_program); + + virtual void update(const goto_functionst &goto_functions); + + virtual void operator()( + const goto_programt &goto_program); + + virtual void operator()( + const goto_functionst &goto_functions); + + virtual ~flow_insensitive_analysis_baset() + { + } + + virtual void clear() + { + initialized=false; + } + + virtual void output( + const goto_functionst &goto_functions, + std::ostream &out) const; + + void output( + const goto_programt &goto_program, + std::ostream &out) const + { + output(goto_program, "", out); + } + +protected: + const namespacet &ns; + + virtual void output( + const goto_programt &goto_program, + const irep_idt &identifier, + std::ostream &out) const; + + typedef std::priority_queue working_sett; + + locationt get_next(working_sett &working_set); + + void put_in_working_set( + working_sett &working_set, + locationt l) + { + working_set.push(l); + } + + // true = found s.th. new + bool fixedpoint( + const goto_programt &goto_program, + const goto_functionst &goto_functions); + + bool fixedpoint( + goto_functionst::function_mapt::const_iterator it, + const goto_functionst &goto_functions); + + void fixedpoint( + const goto_functionst &goto_functions); + + // true = found s.th. new + bool visit( + locationt l, + working_sett &working_set, + const goto_programt &goto_program, + const goto_functionst &goto_functions); + + static locationt successor(locationt l) + { + l++; + return l; + } + + typedef std::set functions_donet; + functions_donet functions_done; + + typedef std::set recursion_sett; + recursion_sett recursion_set; + + bool initialized; + + // function calls + bool do_function_call_rec( + locationt l_call, + const exprt &function, + const exprt::operandst &arguments, + statet &new_state, + const goto_functionst &goto_functions); + + bool do_function_call( + locationt l_call, + const goto_functionst &goto_functions, + const goto_functionst::function_mapt::const_iterator f_it, + const exprt::operandst &arguments, + statet &new_state); + + // abstract methods + + virtual statet &get_state()=0; + virtual const statet &get_state() const=0; + + typedef flow_insensitive_abstract_domain_baset::expr_sett expr_sett; + + virtual void get_reference_set( + const exprt &expr, + expr_sett &expr_set)=0; +}; + + +template +class flow_insensitive_analysist:public flow_insensitive_analysis_baset +{ +public: + // constructor + flow_insensitive_analysist(const namespacet &_ns): + flow_insensitive_analysis_baset(_ns) + { + } + + typedef goto_programt::const_targett locationt; + + virtual void clear() + { + state.clear(); + flow_insensitive_analysis_baset::clear(); + } + + inline T& get_data() { return state; } + inline const T& get_data() const { return state; } + +protected: + T state; // one global state + + virtual statet &get_state() { return state; } + + virtual const statet &get_state() const { return state; } + + void get_reference_set( + const exprt &expr, + expr_sett &expr_set) + { + state.get_reference_set(ns, expr, expr_set); + } + +private: + // to enforce that T is derived from abstract_domain_baset + void dummy(const T &s) { const statet &x=dummy1(s); } +}; + +#endif /*FLOW_INSENSITIVE_ANALYSIS_H_*/ diff --git a/src/goto-programs/format_strings.cpp b/src/goto-programs/format_strings.cpp new file mode 100644 index 00000000000..3623a62afbe --- /dev/null +++ b/src/goto-programs/format_strings.cpp @@ -0,0 +1,268 @@ +/*******************************************************************\ + +Module: Format String Parser + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include + +#include "format_strings.h" + +/*******************************************************************\ + +Function: parse_flags + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void parse_flags( + std::string::const_iterator &it, + format_tokent &curtok) +{ + while(*it=='#' || *it=='0' || + *it=='-' || *it==' ' || *it=='+') + { + switch(*it) + { + case '#': curtok.flags.push_back(format_tokent::ALTERNATE); break; + case '0': curtok.flags.push_back(format_tokent::ZERO_PAD); break; + case '-': curtok.flags.push_back(format_tokent::LEFT_ADJUST); break; + case ' ': curtok.flags.push_back(format_tokent::SIGNED_SPACE); break; + case '+': curtok.flags.push_back(format_tokent::SIGN); break; + default: throw 0; + } + it++; + } +} + +/*******************************************************************\ + +Function: parse_field_width + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void parse_field_width( + std::string::const_iterator &it, + format_tokent &curtok) +{ + if(*it=='*') + { + curtok.flags.push_back(format_tokent::ASTERISK); + it++; + } + + std::string tmp; + for(;isdigit(*it); it++) tmp+=*it; + curtok.field_width=string2integer(tmp); +} + +/*******************************************************************\ + +Function: parse_precision + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void parse_precision( + std::string::const_iterator &it, + format_tokent &curtok) +{ + if(*it=='.') + { + it++; + + if(*it=='*') + { + curtok.flags.push_back(format_tokent::ASTERISK); + it++; + } + else + { + std::string tmp; + for(;isdigit(*it); it++) tmp+=*it; + curtok.precision=string2integer(tmp); + } + } +} + +/*******************************************************************\ + +Function: parse_length_modifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void parse_length_modifier( + std::string::const_iterator &it, + format_tokent &curtok) +{ + if(*it=='h') + { + it++; + if(*it=='h') it++; + curtok.length_modifier = format_tokent::LEN_h; + } + else if(*it=='l') + { + it++; + if(*it=='l') it++; + curtok.length_modifier = format_tokent::LEN_l; + } + else if(*it=='L') + { + it++; + curtok.length_modifier = format_tokent::LEN_L; + } + else if(*it=='j') + { + it++; + curtok.length_modifier = format_tokent::LEN_j; + } + else if(*it=='t') + { + it++; + curtok.length_modifier = format_tokent::LEN_L; + } +} + +/*******************************************************************\ + +Function: parse_conversion_specifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void parse_conversion_specifier( + const std::string &arg_string, + std::string::const_iterator &it, + format_tokent &curtok) +{ + switch(*it) + { + case 'd': + case 'i': curtok.type=format_tokent::SIGNED_DEC; break; + case 'o': curtok.type=format_tokent::UNSIGNED_OCT; break; + case 'u': curtok.type=format_tokent::UNSIGNED_DEC; break; + case 'x': + case 'X': curtok.type=format_tokent::UNSIGNED_HEX; break; + case 'e': + case 'E': curtok.type=format_tokent::DOUBLE_ENG; break; + case 'f': + case 'F': curtok.type=format_tokent::DOUBLE; break; + case 'g': + case 'G': curtok.type=format_tokent::DOUBLE_G; break; + case 'a': + case 'A': curtok.type=format_tokent::DOUBLE_HEX; break; + case 'c': curtok.type=format_tokent::CHAR; break; + case 's': curtok.type=format_tokent::STRING; break; + case 'p': curtok.type=format_tokent::POINTER; break; + case '%': curtok.type=format_tokent::PERCENT; break; + case '[': // pattern matching in, e.g., fscanf. + { + std::string tmp; + it++; + if(*it=='^') // if it's there, it must be first + { + tmp+='^'; it++; + if(*it==']') // if it's there, it must be here + { + tmp+=']'; it++; + } + } + + for(;it!=arg_string.end() && *it!=']'; it++) + tmp+=*it; + + break; + } + + default: throw std::string("unsupported format conversion specifier: `") + + *it + "'"; + } + it++; +} + +/*******************************************************************\ + +Function: parse_format_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool parse_format_string( + const exprt &format_arg, + format_token_listt &token_list) +{ + token_list.clear(); + + if(format_arg.id()==ID_string_constant) + { + const std::string &arg_string = format_arg.get(ID_value).as_string(); + + std::string::const_iterator it=arg_string.begin(); + + while(it!=arg_string.end()) + { + if(*it=='%') + { + token_list.push_back(format_tokent()); + format_tokent &curtok=token_list.back(); + it++; + + parse_flags(it, curtok); + parse_field_width(it, curtok); + parse_precision(it, curtok); + parse_length_modifier(it, curtok); + parse_conversion_specifier(arg_string, it, curtok); + } + else + { + if(token_list.back().type!=format_tokent::TEXT) + token_list.push_back(format_tokent(format_tokent::TEXT)); + + std::string tmp; + for(;it!=arg_string.end() && *it!='%';it++) + tmp+=*it; + + token_list.back().value=tmp; + } + } + + return true; + } + + return false; // non-const format string +} diff --git a/src/goto-programs/format_strings.h b/src/goto-programs/format_strings.h new file mode 100644 index 00000000000..603ffd06ddb --- /dev/null +++ b/src/goto-programs/format_strings.h @@ -0,0 +1,59 @@ +/*******************************************************************\ + +Module: Format String Parser + +Author: CM Wintersteiger + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_FORMAT_STRINGS_H_ +#define CPROVER_GOTO_PROGRAMS_FORMAT_STRINGS_H_ + +#include +#include + +#include +#include + +class format_tokent +{ +public: + typedef enum { UNKNOWN, + TEXT, + SIGNED_DEC, // d, i + UNSIGNED_OCT, // o + UNSIGNED_DEC, // u + UNSIGNED_HEX, // x, X + DOUBLE_ENG, // e, E + DOUBLE, // f, F + DOUBLE_G, // g, G + DOUBLE_HEX, // a, A + CHAR, // c + STRING, // s + POINTER, // p + PERCENT // % + } token_typet; + + typedef enum { ALTERNATE, ZERO_PAD, LEFT_ADJUST, + SIGNED_SPACE, SIGN, ASTERISK } flag_typet; + + typedef enum { LEN_h, LEN_l, LEN_L, LEN_j, LEN_t } length_modifierst; + + format_tokent(token_typet _type) : type(_type) {} + format_tokent(void) : type(UNKNOWN) {} + + token_typet type; + std::list flags; + mp_integer field_width; + mp_integer precision; + length_modifierst length_modifier; + irep_idt value; // for text and pattern matching +}; + +class format_token_listt : public std::list {}; + +bool parse_format_string( + const exprt &format_arg, + format_token_listt &token_list); + +#endif /*CPROVER_GOTO_PROGRAMS_FORMAT_STRINGS_H_*/ diff --git a/src/goto-programs/goto_check.cpp b/src/goto-programs/goto_check.cpp new file mode 100644 index 00000000000..75791fbabd0 --- /dev/null +++ b/src/goto-programs/goto_check.cpp @@ -0,0 +1,733 @@ +/*******************************************************************\ + +Module: GOTO Programs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "goto_check.h" + +class goto_checkt +{ +public: + goto_checkt( + const namespacet &_ns, + const optionst &_options): + ns(_ns), + options(_options) { } + + void goto_check(goto_programt &goto_program); + +protected: + const namespacet &ns; + const optionst &options; + + void check_rec(const exprt &expr, guardt &guard, bool address); + void check(const exprt &expr); + + void bounds_check(const exprt &expr, const guardt &guard); + void div_by_zero_check(const exprt &expr, const guardt &guard); + void pointer_rel_check(const exprt &expr, const guardt &guard); + //void array_size_check(const exprt &expr); + void overflow_check(const exprt &expr, const guardt &guard); + void nan_check(const exprt &expr, const guardt &guard); + std::string array_name(const exprt &expr); + + void add_guarded_claim( + const exprt &expr, + const std::string &comment, + const std::string &property, + const locationt &location, + const guardt &guard); + + goto_programt new_code; + std::set assertions; +}; + +/*******************************************************************\ + +Function: goto_checkt::div_by_zero_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_checkt::div_by_zero_check( + const exprt &expr, + const guardt &guard) +{ + if(!options.get_bool_option("div-by-zero-check")) + return; + + if(expr.operands().size()!=2) + throw expr.id_string()+" takes two arguments"; + + // add divison by zero subgoal + + exprt zero=gen_zero(expr.op1().type()); + + if(zero.is_nil()) + throw "no zero of argument type of operator "+expr.id_string(); + + exprt inequality(ID_notequal, bool_typet()); + inequality.copy_to_operands(expr.op1(), zero); + + add_guarded_claim( + inequality, + "division by zero", + "division-by-zero", + expr.find_location(), + guard); +} + +/*******************************************************************\ + +Function: goto_checkt::overflow_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_checkt::overflow_check( + const exprt &expr, + const guardt &guard) +{ + if(!options.get_bool_option("overflow-check")) + return; + + // first, check type + if(expr.type().id()!=ID_signedbv) + return; + + // add overflow subgoal + + exprt overflow("overflow-"+expr.id_string(), bool_typet()); + overflow.operands()=expr.operands(); + + if(expr.id()==ID_typecast) + { + if(expr.operands().size()!=1) + throw "typecast takes one operand"; + + const typet &old_type=expr.op0().type(); + + unsigned new_width=atoi(expr.type().get(ID_width).c_str()); + unsigned old_width=atoi(old_type.get(ID_width).c_str()); + + if(old_type.id()==ID_unsignedbv) new_width--; + if(new_width>=old_width) return; + + overflow.id(overflow.id_string()+"-"+i2string(new_width)); + } + + overflow.make_not(); + + add_guarded_claim( + overflow, + "arithmetic overflow on "+expr.id_string(), + "overflow", + expr.find_location(), + guard); +} + +/*******************************************************************\ + +Function: goto_checkt::nan_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_checkt::nan_check( + const exprt &expr, + const guardt &guard) +{ + if(!options.get_bool_option("nan-check")) + return; + + // first, check type + if(expr.type().id()!=ID_floatbv) + return; + + if(expr.id()!=ID_plus && + expr.id()!=ID_mult && + expr.id()!=ID_div && + expr.id()!=ID_minus) + return; + + // add nan subgoal + + exprt isnan(ID_isnan, bool_typet()); + isnan.copy_to_operands(expr); + + isnan.make_not(); + + add_guarded_claim( + isnan, + "NaN on "+expr.id_string(), + "NaN", + expr.find_location(), + guard); +} + +/*******************************************************************\ + +Function: goto_checkt::pointer_rel_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_checkt::pointer_rel_check( + const exprt &expr, + const guardt &guard) +{ + if(expr.operands().size()!=2) + throw expr.id_string()+" takes one argument"; + + if(expr.op0().type().id()==ID_pointer && + expr.op1().type().id()==ID_pointer) + { + // add same-object subgoal + + if(options.get_bool_option("pointer-check")) + { + exprt same_object("same-object", bool_typet()); + same_object.copy_to_operands(expr.op0(), expr.op1()); + + add_guarded_claim( + same_object, + "same object violation", + "pointer", + expr.find_location(), + guard); + } + } +} + +/*******************************************************************\ + +Function: goto_checkt::array_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string goto_checkt::array_name(const exprt &expr) +{ + return ::array_name(ns, expr); +} + +/*******************************************************************\ + +Function: goto_checkt::bounds_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_checkt::bounds_check( + const exprt &expr, + const guardt &guard) +{ + if(!options.get_bool_option("bounds-check")) + return; + + if(expr.id()!=ID_index) + return; + + if(expr.find("bounds_check").is_not_nil() && + !expr.get_bool("bounds_check")) + return; + + if(expr.operands().size()!=2) + throw "index takes two operands"; + + typet array_type=ns.follow(expr.op0().type()); + + if(array_type.id()==ID_pointer) + return; // done by the pointer code + else if(array_type.id()==ID_incomplete_array) + { + std::cerr << expr.pretty() << std::endl; + throw "index got incomplete array"; + } + else if(array_type.id()!=ID_array) + throw "bounds check expected array type, got "+array_type.id_string(); + + std::string name=array_name(expr.op0()); + + const exprt &index=expr.op1(); + + if(index.type().id()!=ID_unsignedbv) + { + // we undo typecasts to signedbv + if(index.id()==ID_typecast && + index.operands().size()==1 && + index.op0().type().id()==ID_unsignedbv) + { + // ok + } + else + { + mp_integer i; + + if(!to_integer(index, i) && i>=0) + { + // ok + } + else + { + exprt zero=gen_zero(index.type()); + + if(zero.is_nil()) + throw "no zero constant of index type "+ + index.type().to_string(); + + exprt inequality(ID_ge, bool_typet()); + inequality.copy_to_operands(index, zero); + + add_guarded_claim( + inequality, + name+" lower bound", + "array bounds", + expr.find_location(), + guard); + } + } + } + + { + if(array_type.find(ID_size).is_nil()) + throw "index array operand of wrong type"; + + const exprt &size=(const exprt &)array_type.find(ID_size); + + if(size.id()!=ID_infinity) + { + exprt inequality(ID_lt, bool_typet()); + inequality.copy_to_operands(index, size); + + // typecast size + if(inequality.op1().type()!=inequality.op0().type()) + inequality.op1().make_typecast(inequality.op0().type()); + + add_guarded_claim( + inequality, + name+" upper bound", + "array bounds", + expr.find_location(), + guard); + } + } +} + +/*******************************************************************\ + +Function: goto_checkt::array_size_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +#if 0 +void goto_checkt::array_size_check( + const exprt &expr, + const irept &location) +{ + if(expr.type().id()==ID_array) + { + const exprt &size=(exprt &)expr.type().find(ID_size); + + if(size.type().id()==ID_unsignedbv) + { + // nothing to do + } + else + { + exprt zero=gen_zero(size.type()); + + if(zero.is_nil()) + throw "no zero constant of index type "+ + size.type().to_string(); + + exprt inequality(ID_ge, bool_typet()); + inequality.copy_to_operands(size, zero); + + std::string name=array_name(expr); + + guardt guard; + add_guarded_claim(inequality, name+" size", location, guard); + } + } +} +#endif + +/*******************************************************************\ + +Function: goto_checkt::add_guarded_claim + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_checkt::add_guarded_claim( + const exprt &_expr, + const std::string &comment, + const std::string &property, + const locationt &location, + const guardt &guard) +{ + bool all_claims=options.get_bool_option("all-claims"); + exprt expr(_expr); + + // first try simplifier on it + if(options.get_bool_option("simplify")) + simplify(expr, ns); + + if(!all_claims && expr.is_true()) + return; + + // add the guard + exprt guard_expr=guard.as_expr(); + + exprt new_expr; + + if(guard_expr.is_true()) + new_expr.swap(expr); + else + { + new_expr=exprt(ID_implies, bool_typet()); + new_expr.move_to_operands(guard_expr, expr); + } + + if(assertions.insert(new_expr).second) + { + goto_programt::targett t=new_code.add_instruction(ASSERT); + + t->guard.swap(new_expr); + t->location=location; + t->location.set_comment(comment); + t->location.set_property(property); + } +} + +/*******************************************************************\ + +Function: goto_checkt::check_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_checkt::check_rec( + const exprt &expr, + guardt &guard, + bool address) +{ + if(address) + { + if(expr.id()==ID_dereference) + { + assert(expr.operands().size()==1); + check_rec(expr.op0(), guard, false); + } + else if(expr.id()==ID_index) + { + assert(expr.operands().size()==2); + check_rec(expr.op0(), guard, true); + check_rec(expr.op1(), guard, false); + } + else + { + forall_operands(it, expr) + check_rec(*it, guard, true); + } + return; + } + + if(expr.id()==ID_address_of) + { + assert(expr.operands().size()==1); + check_rec(expr.op0(), guard, true); + return; + } + else if(expr.id()==ID_and || expr.id()==ID_or) + { + if(!expr.is_boolean()) + throw "`"+expr.id_string()+"' must be Boolean, but got "+ + expr.pretty(); + + unsigned old_guards=guard.size(); + + for(unsigned i=0; ilocation.is_nil()) i_it->location=it->location; + if(i_it->function==irep_idt()) i_it->function=it->function; + if(i_it->function==irep_idt()) i_it->function=it->function; + } + + // insert new instructions -- make sure targets are not moved + + while(!new_code.instructions.empty()) + { + goto_program.insert_before_swap(it, new_code.instructions.front()); + new_code.instructions.pop_front(); + it++; + } + } +} + +/*******************************************************************\ + +Function: goto_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_check( + const namespacet &ns, + const optionst &options, + goto_programt &goto_program) +{ + goto_checkt goto_check(ns, options); + goto_check.goto_check(goto_program); +} + +/*******************************************************************\ + +Function: goto_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_check( + const namespacet &ns, + const optionst &options, + goto_functionst &goto_functions) +{ + goto_checkt goto_check(ns, options); + + for(goto_functionst::function_mapt::iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + { + goto_check.goto_check(it->second.body); + } +} diff --git a/src/goto-programs/goto_check.h b/src/goto-programs/goto_check.h new file mode 100644 index 00000000000..733bc73a03a --- /dev/null +++ b/src/goto-programs/goto_check.h @@ -0,0 +1,28 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_GOTO_CHECK_H +#define CPROVER_GOTO_PROGRAMS_GOTO_CHECK_H + +#include +#include + +#include "goto_program.h" +#include "goto_functions.h" + +void goto_check( + const namespacet &ns, + const optionst &options, + goto_programt &goto_program); + +void goto_check( + const namespacet &ns, + const optionst &options, + goto_functionst &goto_functions); + +#endif diff --git a/src/goto-programs/goto_clean_expr.cpp b/src/goto-programs/goto_clean_expr.cpp new file mode 100644 index 00000000000..80cfe4e2a92 --- /dev/null +++ b/src/goto-programs/goto_clean_expr.cpp @@ -0,0 +1,333 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "goto_convert_class.h" + +/*******************************************************************\ + +Function: goto_convertt::make_temp_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::make_temp_symbol( + exprt &expr, + const std::string &suffix, + goto_programt &dest) +{ + const locationt location=expr.find_location(); + + symbolt &new_symbol= + new_tmp_symbol(expr.type(), suffix, dest, location); + + code_assignt assignment; + assignment.lhs()=symbol_expr(new_symbol); + assignment.rhs()=expr; + assignment.location()=location; + + convert(assignment, dest); + + expr=symbol_expr(new_symbol); +} + +/*******************************************************************\ + +Function: goto_convertt::needs_cleaning + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_convertt::needs_cleaning(const exprt &expr) +{ + if(expr.id()==ID_index || + expr.id()==ID_dereference || + expr.id()==ID_sideeffect || + expr.id()==ID_struct || + expr.id()==ID_array || + expr.id()==ID_union || + expr.id()==ID_comma) + return true; + + forall_operands(it, expr) + if(needs_cleaning(*it)) + return true; + + return false; +} + +/*******************************************************************\ + +Function: goto_convertt::rewrite_boolean + + Inputs: + + Outputs: + + Purpose: re-write boolean operators into ?: + +\*******************************************************************/ + +void goto_convertt::rewrite_boolean(exprt &expr) +{ + assert(expr.id()==ID_and || expr.id()==ID_or); + + if(!expr.is_boolean()) + throw "`"+expr.id_string()+"' " + "must be Boolean, but got "+expr.pretty(); + + // re-write "a && b" into nested a?b:0 + // re-write "a || b" into nested a?1:b + + exprt tmp; + + if(expr.id()==ID_and) + tmp=true_exprt(); + else // ID_or + tmp=false_exprt(); + + exprt::operandst &ops=expr.operands(); + + // start with last one + for(int i=int(ops.size())-1; i>=0; i--) + { + exprt &op=ops[i]; + + if(!op.is_boolean()) + throw "`"+expr.id_string()+"' takes Boolean " + "operands only, but got "+op.pretty(); + + if(expr.id()==ID_and) + { + if_exprt if_e(op, tmp, false_exprt()); + tmp.swap(if_e); + } + else // ID_or + { + if_exprt if_e(op, true_exprt(), tmp); + tmp.swap(if_e); + } + } + + expr.swap(tmp); +} + +/*******************************************************************\ + +Function: goto_convertt::clean_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::clean_expr( + exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + // this cleans: + // && || ?: comma (control-dependency) + // function calls + // object constructors like arrays, string constants, structs + // ++ -- + // compound assignments + + if(!needs_cleaning(expr)) return; + + if(expr.id()==ID_and || expr.id()==ID_or) + { + // rewrite into ?: + rewrite_boolean(expr); + + // recursive call + clean_expr(expr, dest, result_is_used); + return; + } + else if(expr.id()==ID_if) + { + if(expr.operands().size()!=3) + throw "if takes three arguments"; + + if(!expr.op0().is_boolean()) + throw "first argument of `if' must be boolean, but got " + +expr.op0().to_string(); + + // first pull out condition -- we need to prevent + // this getting destroyed by the side-effects in the other + // operands + make_temp_symbol(expr.op0(), "condition", dest); + + // now clean arguments + goto_programt tmp_true, tmp_false; + clean_expr(expr.op1(), tmp_true, result_is_used); + clean_expr(expr.op2(), tmp_false, result_is_used); + + // generate guard for argument side-effects + generate_ifthenelse( + expr.op0(), tmp_true, tmp_false, + expr.location(), dest); + + return; + } + else if(expr.id()==ID_comma) + { + exprt result; + + Forall_operands(it, expr) + { + bool last=(it==--expr.operands().end()); + + if(last) + { + result.swap(*it); + clean_expr(result, dest, result_is_used); + } + else + clean_expr(*it, dest, false); + } + + expr.swap(result); + + return; + } + else if(expr.id()==ID_typecast) + { + if(expr.operands().size()!=1) + throw "typecast takes one argument"; + + // preserve 'result_is_used' + clean_expr(expr.op0(), dest, result_is_used); + + return; + } + else if(expr.id()==ID_sideeffect) + { + // some of the side-effects need special treatment! + const irep_idt statement=expr.get(ID_statement); + + if(statement==ID_gcc_conditional_expression) + { + // need to do separately + remove_gcc_conditional_expression(expr, dest); + return; + } + else if(statement==ID_statement_expression) + { + // need to do separately to prevent that + // the operands of expr get 'cleaned' + remove_statement_expression(to_side_effect_expr(expr), dest, result_is_used); + return; + } + } + + // TODO: evaluation order + + Forall_operands(it, expr) + clean_expr(*it, dest); + + if(expr.id()==ID_sideeffect) + { + remove_side_effect(to_side_effect_expr(expr), dest, result_is_used); + } + else if(expr.id()==ID_address_of) + { + assert(expr.operands().size()==1); + address_of_replace_objects(expr.op0(), dest); + } +} + +/*******************************************************************\ + +Function: goto_convertt::address_of_replace_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::address_of_replace_objects( + exprt &expr, + goto_programt &dest) +{ + if(expr.id()==ID_struct) + make_temp_symbol(expr, "struct", dest); + else if(expr.id()==ID_union) + make_temp_symbol(expr, "union", dest); + else if(expr.id()==ID_array) + make_temp_symbol(expr, "array", dest); + else if(expr.id()==ID_string_constant) + { + } + else + Forall_operands(it, expr) + address_of_replace_objects(*it, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_gcc_conditional_expression + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_gcc_conditional_expression( + exprt &expr, + goto_programt &dest) +{ + if(expr.operands().size()!=2) + throw "conditional_expression takes two operands"; + + // first remove side-effects from condition + clean_expr(expr.op0(), dest); + + // now we can copy op0 safely + if_exprt if_expr; + + if_expr.cond()=expr.op0(); + if_expr.true_case()=expr.op0(); + if_expr.false_case()=expr.op1(); + if_expr.type()=expr.type(); + if_expr.location()=expr.location(); + + if(if_expr.cond().type()!=bool_typet()) + if_expr.cond().make_typecast(bool_typet()); + + expr.swap(if_expr); + + // there might still be junk in expr.op2() + clean_expr(expr, dest); +} diff --git a/src/goto-programs/goto_convert.cpp b/src/goto-programs/goto_convert.cpp new file mode 100644 index 00000000000..bd546428374 --- /dev/null +++ b/src/goto-programs/goto_convert.cpp @@ -0,0 +1,2196 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "goto_convert_class.h" +#include "destructor.h" + +/*******************************************************************\ + +Function: goto_convertt::finish_gotos + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::finish_gotos() +{ + for(gotost::const_iterator it=targets.gotos.begin(); + it!=targets.gotos.end(); + it++) + { + goto_programt::instructiont &i=**it; + + if(i.code.get(ID_statement)=="non-deterministic-goto") + { + const irept &destinations=i.code.find("destinations"); + + i.make_goto(); + + forall_irep(it, destinations.get_sub()) + { + labelst::const_iterator l_it= + targets.labels.find(it->id_string()); + + if(l_it==targets.labels.end()) + { + err_location(i.code); + str << "goto label " << it->id_string() << " not found"; + throw 0; + } + + i.targets.push_back(l_it->second); + } + } + else if(i.is_start_thread()) + { + const irep_idt &goto_label=i.code.get(ID_destination); + + labelst::const_iterator l_it= + targets.labels.find(goto_label); + + if(l_it==targets.labels.end()) + { + err_location(i.code); + str << "goto label " << goto_label << " not found"; + throw 0; + } + + i.targets.push_back(l_it->second); + } + else if(i.code.get(ID_statement)==ID_goto) + { + const irep_idt &goto_label=i.code.get(ID_destination); + + labelst::const_iterator l_it=targets.labels.find(goto_label); + + if(l_it==targets.labels.end()) + { + err_location(i.code); + str << "goto label " << goto_label << " not found"; + throw 0; + } + + i.targets.clear(); + i.targets.push_back(l_it->second); + } + else + { + err_location(i.code); + throw "finish_gotos: unexpected goto"; + } + } + + targets.gotos.clear(); +} + +/*******************************************************************\ + +Function: goto_convertt::finish_computed_gotos + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::finish_computed_gotos(goto_programt &goto_program) +{ + for(computed_gotost::const_iterator + g_it=targets.computed_gotos.begin(); + g_it!=targets.computed_gotos.end(); + g_it++) + { + goto_programt::instructiont &i=**g_it; + exprt destination=i.code.op0(); + + assert(destination.id()==ID_dereference); + assert(destination.operands().size()==1); + + exprt pointer=destination.op0(); + + // remember the expression for later checks + i.type=OTHER; + i.code=code_expressiont(pointer); + + // insert huge case-split + for(labelst::const_iterator + l_it=targets.labels.begin(); + l_it!=targets.labels.end(); + l_it++) + { + exprt label_expr(ID_label, empty_typet()); + label_expr.set(ID_identifier, l_it->first); + + equality_exprt guard; + + guard.lhs()=pointer; + guard.rhs()=address_of_exprt(label_expr); + + goto_programt::targett t= + goto_program.insert_after(*g_it); + + t->make_goto(l_it->second); + t->location=i.location; + t->guard=guard; + } + } + + targets.computed_gotos.clear(); +} + +/*******************************************************************\ + +Function: goto_convertt::goto_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::goto_convert(const codet &code, goto_programt &dest) +{ + goto_convert_rec(code, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::goto_convert_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::goto_convert_rec( + const codet &code, + goto_programt &dest) +{ + convert(code, dest); + + finish_gotos(); + finish_computed_gotos(dest); +} + +/*******************************************************************\ + +Function: goto_convertt::copy + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::copy( + const codet &code, + goto_program_instruction_typet type, + goto_programt &dest) +{ + goto_programt::targett t=dest.add_instruction(type); + t->code=code; + t->location=code.location(); +} + +/*******************************************************************\ + +Function: goto_convert::convert_label + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_label( + const code_labelt &code, + goto_programt &dest) +{ + if(code.operands().size()!=1) + { + err_location(code); + throw "label statement expected to have one operand"; + } + + // grab the label + const irep_idt &label=code.get_label(); + + goto_programt tmp; + + // magic thread creation label? + if(has_prefix(id2string(label), "__CPROVER_ASYNC_")) + { + // this is like a START_THREAD + codet tmp_code(ID_start_thread); + tmp_code.copy_to_operands(code.op0()); + tmp_code.location()=code.location(); + convert(tmp_code, tmp); + } + else + convert(to_code(code.op0()), tmp); + + // magic ERROR label? + + const std::string &error_label=options.get_option("error-label"); + + goto_programt::targett target; + + if(error_label!="" && label==error_label) + { + goto_programt::targett t=dest.add_instruction(ASSERT); + t->guard.make_false(); + t->location=code.location(); + t->location.set(ID_property, "error label"); + t->location.set(ID_comment, "error label"); + t->location.set("user-provided", true); + + target=t; + dest.destructive_append(tmp); + } + else + { + target=tmp.instructions.begin(); + dest.destructive_append(tmp); + } + + if(!label.empty()) + { + targets.labels.insert(std::pair + (label, target)); + target->labels.push_front(label); + } + + // cases? + + const exprt::operandst &case_op=code.case_op(); + + if(!case_op.empty()) + { + exprt::operandst &case_op_dest=targets.cases[target]; + + case_op_dest.reserve(case_op_dest.size()+case_op.size()); + + forall_expr(it, case_op) + case_op_dest.push_back(*it); + } + + // default? + + if(code.is_default()) + targets.set_default(target); +} + +/*******************************************************************\ + +Function: goto_convertt::convert + + Inputs: + + Outputs: + + Purpose: converts 'code' and appends the result to 'dest' + +\*******************************************************************/ + +void goto_convertt::convert( + const codet &code, + goto_programt &dest) +{ + const irep_idt &statement=code.get_statement(); + + if(statement==ID_block) + convert_block(code, dest); + else if(statement==ID_decl) + convert_decl(to_code_decl(code), dest); + else if(statement==ID_decl_type) + convert_decl_type(code, dest); + else if(statement==ID_expression) + convert_expression(to_code_expression(code), dest); + else if(statement==ID_assign) + convert_assign(to_code_assign(code), dest); + else if(statement==ID_init) + convert_init(code, dest); + else if(statement==ID_assert) + convert_assert(to_code_assert(code), dest); + else if(statement==ID_assume) + convert_assume(to_code_assume(code), dest); + else if(statement==ID_function_call) + convert_function_call(to_code_function_call(code), dest); + else if(statement==ID_label) + convert_label(to_code_label(code), dest); + else if(statement==ID_for) + convert_for(code, dest); + else if(statement==ID_while) + convert_while(code, dest); + else if(statement==ID_dowhile) + convert_dowhile(code, dest); + else if(statement==ID_switch) + convert_switch(code, dest); + else if(statement==ID_break) + convert_break(to_code_break(code), dest); + else if(statement==ID_return) + convert_return(to_code_return(code), dest); + else if(statement==ID_continue) + convert_continue(to_code_continue(code), dest); + else if(statement==ID_goto) + convert_goto(code, dest); + else if(statement=="computed-goto") + convert_computed_goto(code, dest); + else if(statement==ID_skip) + convert_skip(code, dest); + else if(statement=="non-deterministic-goto") + convert_non_deterministic_goto(code, dest); + else if(statement==ID_ifthenelse) + convert_ifthenelse(code, dest); + else if(statement==ID_specc_notify) + convert_specc_notify(code, dest); + else if(statement==ID_specc_wait) + convert_specc_wait(code, dest); + else if(statement==ID_specc_par) + convert_specc_par(code, dest); + else if(statement==ID_start_thread) + convert_start_thread(code, dest); + else if(statement==ID_end_thread) + convert_end_thread(code, dest); + else if(statement==ID_atomic_begin) + convert_atomic_begin(code, dest); + else if(statement==ID_atomic_end) + convert_atomic_end(code, dest); + else if(statement==ID_bp_enforce) + convert_bp_enforce(code, dest); + else if(statement==ID_bp_abortif) + convert_bp_abortif(code, dest); + else if(statement==ID_cpp_delete || + statement=="cpp_delete[]") + convert_cpp_delete(code, dest); + else + copy(code, OTHER, dest); + + if(dest.instructions.empty()) + { + dest.add_instruction(SKIP); + dest.instructions.back().code.make_nil(); + } +} + +/*******************************************************************\ + +Function: goto_convertt::convert_block + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_block( + const codet &code, + goto_programt &dest) +{ + // this needs to be ordered to obtain + // the correct ordering for the destructor calls + std::list locals; + + forall_operands(it, code) + { + const codet &code=to_code(*it); + + if(code.get_statement()==ID_decl) + { + const exprt &op0=code.op0(); + assert(op0.id()==ID_symbol); + const irep_idt &identifier=op0.get(ID_identifier); + const symbolt &symbol=lookup(identifier); + + if(!symbol.static_lifetime && + symbol.type.id()!=ID_code) + locals.push_back(identifier); + } + + convert(code, dest); + + for(tmp_symbolst::const_iterator + it=tmp_symbols.begin(); + it!=tmp_symbols.end(); + it++) + locals.push_back(*it); + + tmp_symbols.clear(); + } + + // see if we need to call any destructors + + while(!locals.empty()) + { + const symbolt &symbol=ns.lookup(locals.back()); + + code_function_callt destructor=get_destructor(ns, symbol.type); + + if(destructor.is_not_nil()) + { + // add "this" + exprt this_expr(ID_address_of, pointer_typet()); + this_expr.type().subtype()=symbol.type; + this_expr.copy_to_operands(symbol_expr(symbol)); + destructor.arguments().push_back(this_expr); + + convert(destructor, dest); + } + + locals.pop_back(); + } +} + +/*******************************************************************\ + +Function: goto_convertt::convert_expression + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_expression( + const code_expressiont &code, + goto_programt &dest) +{ + if(code.operands().size()!=1) + { + err_location(code); + throw "expression statement takes one operand"; + } + + exprt expr=code.op0(); + + if(expr.id()==ID_if) + { + // we do a special treatment for ?: + const if_exprt &if_expr=to_if_expr(expr); + code_ifthenelset tmp_code; + tmp_code.operands().resize(3); + tmp_code.location()=expr.location(); + tmp_code.cond()=if_expr.cond(); + tmp_code.then_case()=code_expressiont(if_expr.true_case()); + tmp_code.then_case().location()=expr.location(); + tmp_code.else_case()=code_expressiont(if_expr.false_case()); + tmp_code.else_case().location()=expr.location(); + convert_ifthenelse(tmp_code, dest); + } + else if(expr.id()==ID_statement_expression) + { + assert(expr.operands().size()==1); + convert(to_code(expr.op0()), dest); + } + else + { + clean_expr(expr, dest, false); // result _not_ used + + if(expr.is_not_nil()) + { + codet tmp=code; + tmp.op0()=expr; + tmp.location()=expr.location(); + copy(tmp, OTHER, dest); + } + } +} + +/*******************************************************************\ + +Function: goto_convertt::convert_decl + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_decl( + const code_declt &code, + goto_programt &dest) +{ + const exprt &op0=code.op0(); + + if(op0.id()!=ID_symbol) + { + err_location(op0); + throw "decl statement expects symbol as first operand"; + } + + const irep_idt &identifier=op0.get(ID_identifier); + + const symbolt &symbol=lookup(identifier); + + if(symbol.static_lifetime || + symbol.type.id()==ID_code) + return; // this is a SKIP! + + if(code.operands().size()==1) + { + copy(code, DECL, dest); + } + else + { + exprt initializer; + + codet tmp=code; + initializer=code.op1(); + tmp.operands().resize(1); + + goto_programt sideeffects; + clean_expr(initializer, sideeffects); + dest.destructive_append(sideeffects); + + // break up into decl and assignment + copy(tmp, DECL, dest); + + code_assignt assign(code.op0(), initializer); + assign.location()=tmp.location(); + + copy(assign, ASSIGN, dest); + } +} + +/*******************************************************************\ + +Function: goto_convertt::convert_decl_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_decl_type( + const codet &code, + goto_programt &dest) +{ + // we remove these +} + +/*******************************************************************\ + +Function: goto_convertt::convert_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_assign( + const code_assignt &code, + goto_programt &dest) +{ + exprt lhs=code.lhs(), + rhs=code.rhs(); + + clean_expr(lhs, dest); + + if(rhs.id()==ID_sideeffect && + rhs.get(ID_statement)==ID_function_call) + { + if(rhs.operands().size()!=2) + { + err_location(rhs); + throw "function_call sideeffect takes two operands"; + } + + Forall_operands(it, rhs) + clean_expr(*it, dest); + + do_function_call(lhs, rhs.op0(), rhs.op1().operands(), dest); + } + else if(rhs.id()==ID_sideeffect && + (rhs.get(ID_statement)==ID_cpp_new || + rhs.get(ID_statement)=="cpp_new[]")) + { + Forall_operands(it, rhs) + clean_expr(*it, dest); + + do_cpp_new(lhs, to_side_effect_expr(rhs), dest); + } + else if(rhs.id()==ID_sideeffect && + rhs.get(ID_statement)==ID_malloc) + { + // just preserve + Forall_operands(it, rhs) + clean_expr(*it, dest); + + code_assignt new_assign(code); + new_assign.lhs()=lhs; + new_assign.rhs()=rhs; + + copy(new_assign, ASSIGN, dest); + } + else + { + clean_expr(rhs, dest); + + if(lhs.id()==ID_typecast) + { + assert(lhs.operands().size()==1); + + // add a typecast to the rhs + exprt new_rhs=rhs; + rhs.make_typecast(lhs.op0().type()); + + // remove typecast from lhs + exprt tmp=lhs.op0(); + lhs.swap(tmp); + } + + code_assignt new_assign(code); + new_assign.lhs()=lhs; + new_assign.rhs()=rhs; + + copy(new_assign, ASSIGN, dest); + } +} + +/*******************************************************************\ + +Function: goto_convertt::convert_init + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_init( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=2) + { + err_location(code); + throw "init statement takes two operands"; + } + + // make it an assignment + codet assignment=code; + assignment.set_statement(ID_assign); + + convert(to_code_assign(assignment), dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_cpp_delete + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_cpp_delete( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=1) + { + err_location(code); + throw "cpp_delete statement takes one operand"; + } + + exprt tmp_op=code.op0(); + + clean_expr(tmp_op, dest); + + // we call the destructor, and then free + const exprt &destructor= + static_cast(code.find(ID_destructor)); + + if(destructor.is_not_nil()) + { + if(code.get_statement()=="cpp_delete[]") + { + // build loop + } + else if(code.get_statement()==ID_cpp_delete) + { + // just one object + exprt deref_op(ID_dereference, tmp_op.type().subtype()); + deref_op.copy_to_operands(tmp_op); + + codet tmp_code=to_code(destructor); + replace_new_object(deref_op, tmp_code); + convert(tmp_code, dest); + } + else + assert(false); + } + + // now do "free" + exprt delete_symbol=symbol_expr(ns.lookup("c::__delete")); + + assert(to_code_type(delete_symbol.type()).arguments().size()==1); + typet arg_type= + to_code_type(delete_symbol.type()).arguments().front().type(); + + code_function_callt delete_call; + delete_call.function()=delete_symbol; + delete_call.arguments().push_back(typecast_exprt(tmp_op, arg_type)); + delete_call.lhs().make_nil(); + delete_call.location()=code.location(); + + convert(delete_call, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_assert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_assert( + const code_assertt &code, + goto_programt &dest) +{ + exprt cond=code.assertion(); + + clean_expr(cond, dest); + + if(!options.get_bool_option("assertions")) + return; + + goto_programt::targett t=dest.add_instruction(ASSERT); + t->guard.swap(cond); + t->location=code.location(); + t->location.set(ID_property, ID_assertion); + t->location.set("user-provided", true); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_skip + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_skip( + const codet &code, + goto_programt &dest) +{ + goto_programt::targett t=dest.add_instruction(SKIP); + t->location=code.location(); + t->code=code; +} + +/*******************************************************************\ + +Function: goto_convertt::convert_assert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_assume( + const code_assumet &code, + goto_programt &dest) +{ + exprt op=code.assumption(); + + clean_expr(op, dest); + + goto_programt::targett t=dest.add_instruction(ASSUME); + t->guard.swap(op); + t->location=code.location(); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_for + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_for( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=4) + { + err_location(code); + throw "for takes four operands"; + } + + // turn for(A; c; B) { P } into + // A; while(c) { P; B; } + //----------------------------- + // A; + // u: sideeffects in c + // v: if(!c) goto z; + // w: P; + // x: B; <-- continue target + // y: goto u; + // z: ; <-- break target + + // A; + code_blockt block; + if(code.op0().is_not_nil()) + { + block.copy_to_operands(code.op0()); + convert(block, dest); + } + + exprt cond=code.op1(); + + goto_programt sideeffects; + clean_expr(cond, sideeffects); + + // save break/continue targets + break_continue_targetst old_targets(targets); + + // do the u label + goto_programt::targett u=sideeffects.instructions.begin(); + + // do the v label + goto_programt tmp_v; + goto_programt::targett v=tmp_v.add_instruction(); + + // do the z label + goto_programt tmp_z; + goto_programt::targett z=tmp_z.add_instruction(SKIP); + + // do the x label + goto_programt tmp_x; + + if(code.op2().is_nil()) + tmp_x.add_instruction(SKIP); + else + { + exprt tmp_B=code.op2(); + + clean_expr(tmp_B, tmp_x, false); + + if(tmp_x.instructions.empty()) + tmp_x.add_instruction(SKIP); + } + + // optimize the v label + if(sideeffects.instructions.empty()) + u=v; + + // set the targets + targets.set_break(z); + targets.set_continue(tmp_x.instructions.begin()); + + // v: if(!c) goto z; + v->make_goto(z); + v->guard=cond; + v->guard.make_not(); + v->location=cond.location(); + + // do the w label + goto_programt tmp_w; + convert(to_code(code.op3()), tmp_w); + + // y: goto u; + goto_programt tmp_y; + goto_programt::targett y=tmp_y.add_instruction(); + y->make_goto(u); + y->guard.make_true(); + y->location=code.location(); + + dest.destructive_append(sideeffects); + dest.destructive_append(tmp_v); + dest.destructive_append(tmp_w); + dest.destructive_append(tmp_x); + dest.destructive_append(tmp_y); + dest.destructive_append(tmp_z); + + // restore break/continue + targets.restore(old_targets); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_while + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_while( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=2) + { + err_location(code); + throw "while takes two operands"; + } + + const exprt &cond=code.op0(); + const locationt &location=code.location(); + + // while(c) P; + //-------------------- + // v: sideeffects in c + // if(!c) goto z; + // x: P; + // y: goto v; <-- continue target + // z: ; <-- break target + + // save break/continue targets + break_continue_targetst old_targets(targets); + + // do the z label + goto_programt tmp_z; + goto_programt::targett z=tmp_z.add_instruction(); + z->make_skip(); + + goto_programt tmp_branch; + generate_conditional_branch(gen_not(cond), z, location, tmp_branch); + + // do the v label + goto_programt::targett v=tmp_branch.instructions.begin(); + + // do the y label + goto_programt tmp_y; + goto_programt::targett y=tmp_y.add_instruction(); + + // set the targets + targets.set_break(z); + targets.set_continue(y); + + // do the x label + goto_programt tmp_x; + convert(to_code(code.op1()), tmp_x); + + // y: if(c) goto v; + y->make_goto(v); + y->guard.make_true(); + y->location=code.location(); + + dest.destructive_append(tmp_branch); + dest.destructive_append(tmp_x); + dest.destructive_append(tmp_y); + dest.destructive_append(tmp_z); + + // restore break/continue + targets.restore(old_targets); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_dowhile + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_dowhile( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=2) + { + err_location(code); + throw "dowhile takes two operands"; + } + + // save location + locationt condition_location=code.op0().find_location(); + + exprt cond=code.op0(); + + goto_programt sideeffects; + clean_expr(cond, sideeffects); + + // do P while(c); + //-------------------- + // w: P; + // x: sideeffects in c <-- continue target + // y: if(c) goto w; + // z: ; <-- break target + + // save break/continue targets + break_continue_targetst old_targets(targets); + + // do the y label + goto_programt tmp_y; + goto_programt::targett y=tmp_y.add_instruction(); + + // do the z label + goto_programt tmp_z; + goto_programt::targett z=tmp_z.add_instruction(); + z->make_skip(); + + // do the x label + goto_programt::targett x; + if(sideeffects.instructions.empty()) + x=y; + else + x=sideeffects.instructions.begin(); + + // set the targets + targets.set_break(z); + targets.set_continue(x); + + // do the w label + goto_programt tmp_w; + convert(to_code(code.op1()), tmp_w); + goto_programt::targett w=tmp_w.instructions.begin(); + + // y: if(c) goto w; + y->make_goto(w); + y->guard=cond; + y->location=condition_location; + + dest.destructive_append(tmp_w); + dest.destructive_append(sideeffects); + dest.destructive_append(tmp_y); + dest.destructive_append(tmp_z); + + // restore break/continue targets + targets.restore(old_targets); +} + +/*******************************************************************\ + +Function: goto_convertt::case_guard + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::case_guard( + const exprt &value, + const exprt::operandst &case_op, + exprt &dest) +{ + dest=exprt(ID_or, typet(ID_bool)); + dest.reserve_operands(case_op.size()); + + forall_expr(it, case_op) + { + equality_exprt eq_expr; + eq_expr.lhs()=value; + eq_expr.rhs()=*it; + dest.move_to_operands(eq_expr); + } + + assert(dest.operands().size()!=0); + + if(dest.operands().size()==1) + { + exprt tmp; + tmp.swap(dest.op0()); + dest.swap(tmp); + } +} + +/*******************************************************************\ + +Function: goto_convertt::convert_switch + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_switch( + const codet &code, + goto_programt &dest) +{ + // switch(v) { + // case x: Px; + // case y: Py; + // ... + // default: Pd; + // } + // -------------------- + // x: if(v==x) goto X; + // y: if(v==y) goto Y; + // goto d; + // X: Px; + // Y: Py; + // d: Pd; + // z: ; + + if(code.operands().size()<2) + { + err_location(code); + throw "switch takes at least two operands"; + } + + exprt argument=code.op0(); + + goto_programt sideeffects; + clean_expr(argument, sideeffects); + + // save break/continue/default/cases targets + break_continue_switch_targetst old_targets(targets); + + // do the z label + goto_programt tmp_z; + goto_programt::targett z=tmp_z.add_instruction(); + z->make_skip(); + + // set the new targets -- continue stays as is + targets.set_break(z); + targets.set_default(z); + targets.cases.clear(); + + goto_programt tmp; + + forall_operands(it, code) + if(it!=code.operands().begin()) + convert(to_code(*it), tmp); + + goto_programt tmp_cases; + + for(casest::iterator it=targets.cases.begin(); + it!=targets.cases.end(); + it++) + { + const caset &case_ops=it->second; + + assert(!case_ops.empty()); + + exprt guard_expr; + case_guard(argument, case_ops, guard_expr); + + goto_programt::targett x=tmp_cases.add_instruction(); + x->make_goto(it->first); + x->guard.swap(guard_expr); + x->location=case_ops.front().find_location(); + } + + { + goto_programt::targett d_jump=tmp_cases.add_instruction(); + d_jump->make_goto(targets.default_target); + d_jump->location=targets.default_target->location; + } + + dest.destructive_append(sideeffects); + dest.destructive_append(tmp_cases); + dest.destructive_append(tmp); + dest.destructive_append(tmp_z); + + // restore old targets + targets.restore(old_targets); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_break + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_break( + const code_breakt &code, + goto_programt &dest) +{ + if(!targets.break_set) + { + err_location(code); + throw "break without target"; + } + + goto_programt::targett t=dest.add_instruction(); + t->make_goto(targets.break_target); + t->location=code.location(); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_return + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_return( + const code_returnt &code, + goto_programt &dest) +{ + if(!targets.return_set) + { + err_location(code); + throw "return without target"; + } + + if(code.operands().size()!=0 && + code.operands().size()!=1) + { + err_location(code); + throw "return takes none or one operand"; + } + + code_returnt new_code(code); + + if(new_code.has_return_value()) + { + goto_programt sideeffects; + clean_expr(new_code.return_value(), sideeffects); + dest.destructive_append(sideeffects); + } + + if(targets.return_value) + { + if(!new_code.has_return_value()) + { + err_location(new_code); + throw "function must return value"; + } + } + else + { + if(new_code.has_return_value() && + new_code.return_value().type().id()!=ID_empty) + { + err_location(new_code); + throw "function must not return value"; + } + } + + goto_programt::targett t=dest.add_instruction(); + t->make_return(); + t->code=new_code; + t->location=new_code.location(); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_continue + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_continue( + const code_continuet &code, + goto_programt &dest) +{ + if(!targets.continue_set) + { + err_location(code); + throw "continue without target"; + } + + goto_programt::targett t=dest.add_instruction(); + t->make_goto(targets.continue_target); + t->location=code.location(); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_goto + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_goto( + const codet &code, + goto_programt &dest) +{ + goto_programt::targett t=dest.add_instruction(); + t->make_goto(); + t->location=code.location(); + t->code=code; + + // remember it to do target later + targets.gotos.push_back(t); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_computed_goto + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_computed_goto( + const codet &code, + goto_programt &dest) +{ + goto_programt::targett t=dest.add_instruction(); + t->make_skip(); + t->location=code.location(); + t->code=code; + + // remember it to do this later + targets.computed_gotos.push_back(t); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_non_deterministic_goto + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_non_deterministic_goto( + const codet &code, + goto_programt &dest) +{ + convert_goto(code, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_specc_notify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_specc_notify( + const codet &code, + goto_programt &dest) +{ + #if 0 + goto_programt::targett t=dest.add_instruction(EVENT); + + forall_operands(it, code) + convert_specc_event(*it, t->events); + + t->code.swap(code); + t->location=code.location(); + #endif + + copy(code, OTHER, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_specc_event + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_specc_event( + const exprt &op, + std::set &events) +{ + if(op.id()==ID_or || op.id()==ID_and) + { + forall_operands(it, op) + convert_specc_event(*it, events); + } + else if(op.id()==ID_specc_event) + { + irep_idt event=op.get(ID_identifier); + + if(has_prefix(id2string(event), "specc::")) + event=std::string(id2string(event), 7, std::string::npos); + + events.insert(event); + } + else + { + err_location(op); + throw "convert_convert_event got "+op.id_string(); + } +} + +/*******************************************************************\ + +Function: goto_convertt::convert_specc_wait + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_specc_wait( + const codet &code, + goto_programt &dest) +{ + #if 0 + goto_programt::targett t=dest.add_instruction(WAIT); + + if(code.operands().size()!=1) + { + err_location(code); + throw "specc_wait expects one operand"; + } + + const exprt &op=code.op0(); + + if(op.id()=="or") + t->or_semantics=true; + + convert_specc_event(op, t->events); + + t->code.swap(code); + t->location=code.location(); + #endif + + copy(code, OTHER, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_specc_par + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_specc_par( + const codet &code, + goto_programt &dest) +{ + copy(code, OTHER, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_start_thread + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_start_thread( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=1) + { + err_location(code); + throw "start_thread expects one operand"; + } + + goto_programt::targett start_thread= + dest.add_instruction(START_THREAD); + + start_thread->location=code.location(); + + // see if op0 is an unconditional goto + + if(code.op0().get(ID_statement)==ID_goto) + { + start_thread->code.set(ID_destination, + code.op0().get(ID_destination)); + + // remember it to do target later + targets.gotos.push_back(start_thread); + } + else + { + convert(to_code(code.op0()), dest); + + dest.add_instruction(END_THREAD); + + goto_programt tmp2; + start_thread->targets.push_back(tmp2.add_instruction(SKIP)); + dest.destructive_append(tmp2); + } +} + +/*******************************************************************\ + +Function: goto_convertt::convert_end_thread + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_end_thread( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=0) + { + err_location(code); + throw "end_thread expects no operands"; + } + + copy(code, END_THREAD, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_atomic_begin + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_atomic_begin( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=0) + { + err_location(code); + throw "atomic_begin expects no operands"; + } + + copy(code, ATOMIC_BEGIN, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_atomic_end + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_atomic_end( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=0) + { + err_location(code); + throw "atomic_end expects no operands"; + } + + copy(code, ATOMIC_END, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_bp_enforce + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_bp_enforce( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=2) + { + err_location(code); + error("bp_enfroce expects two arguments"); + throw 0; + } + + // do an assume + exprt op=code.op0(); + + clean_expr(op, dest); + + goto_programt::targett t=dest.add_instruction(ASSUME); + t->guard=op; + t->location=code.location(); + + // change the assignments + + goto_programt tmp; + convert(to_code(code.op1()), tmp); + + if(!op.is_true()) + { + exprt constraint(op); + make_next_state(constraint); + + Forall_goto_program_instructions(it, tmp) + { + if(it->is_assign()) + { + assert(it->code.get(ID_statement)==ID_assign); + + // add constrain + codet constrain(ID_bp_constrain); + constrain.reserve_operands(2); + constrain.move_to_operands(it->code); + constrain.copy_to_operands(constraint); + it->code.swap(constrain); + + it->type=OTHER; + } + else if(it->is_other() && + it->code.get(ID_statement)==ID_bp_constrain) + { + // add to constraint + assert(it->code.operands().size()==2); + it->code.op1()= + gen_and(it->code.op1(), constraint); + } + } + } + + dest.destructive_append(tmp); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_bp_abortif + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_bp_abortif( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=1) + { + err_location(code); + throw "bp_abortif expects one argument"; + } + + // do an assert + exprt op=code.op0(); + + clean_expr(op, dest); + + op.make_not(); + + goto_programt::targett t=dest.add_instruction(ASSERT); + t->guard.swap(op); + t->location=code.location(); +} + +/*******************************************************************\ + +Function: goto_convertt::convert_ifthenelse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_ifthenelse( + const codet &code, + goto_programt &dest) +{ + if(code.operands().size()!=2 && + code.operands().size()!=3) + { + err_location(code); + throw "ifthenelse takes two or three operands"; + } + + bool has_else= + code.operands().size()==3 && + !code.op2().is_nil(); + + const locationt &location=code.location(); + + // convert 'then'-branch + goto_programt tmp_op1; + convert(to_code(code.op1()), tmp_op1); + + goto_programt tmp_op2; + + if(has_else) + convert(to_code(code.op2()), tmp_op2); + + exprt tmp_guard=code.op0(); + clean_expr(tmp_guard, dest); + + generate_ifthenelse(tmp_guard, tmp_op1, tmp_op2, location, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::collect_operands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::collect_operands( + const exprt &expr, + const irep_idt &id, + std::list &dest) +{ + if(expr.id()!=id) + { + dest.push_back(expr); + } + else + { + // left-to-right is important + forall_operands(it, expr) + collect_operands(*it, id, dest); + } +} + +/*******************************************************************\ + +Function: goto_convertt::generate_ifthenelse + + Inputs: + + Outputs: + + Purpose: if(guard) goto target; + +\*******************************************************************/ + +void goto_convertt::generate_ifthenelse( + const exprt &guard, + goto_programt &true_case, + goto_programt &false_case, + const locationt &location, + goto_programt &dest) +{ + if(true_case.instructions.empty() && + false_case.instructions.empty()) + return; + + // do guarded gotos directly + if(false_case.instructions.empty() && + true_case.instructions.size()==1 && + true_case.instructions.back().is_goto() && + true_case.instructions.back().guard.is_true()) + { + true_case.instructions.back().guard=guard; + dest.destructive_append(true_case); + return; + } + + if(true_case.instructions.empty()) + return generate_ifthenelse( + gen_not(guard), false_case, true_case, location, dest); + + bool has_else=!false_case.instructions.empty(); + + // if(c) P; + //-------------------- + // v: if(!c) goto z; + // w: P; + // z: ; + + // if(c) P; else Q; + //-------------------- + // v: if(!c) goto y; + // w: P; + // x: goto z; + // y: Q; + // z: ; + + // do the x label + goto_programt tmp_x; + goto_programt::targett x=tmp_x.add_instruction(); + + // do the z label + goto_programt tmp_z; + goto_programt::targett z=tmp_z.add_instruction(); + z->make_skip(); + + // y: Q; + goto_programt tmp_y; + goto_programt::targett y; + if(has_else) + { + tmp_y.swap(false_case); + y=tmp_y.instructions.begin(); + } + + // v: if(!c) goto z/y; + goto_programt tmp_v; + generate_conditional_branch( + gen_not(guard), has_else?y:z, location, tmp_v); + + // w: P; + goto_programt tmp_w; + tmp_w.swap(true_case); + + // x: goto z; + x->make_goto(z); + + dest.destructive_append(tmp_v); + dest.destructive_append(tmp_w); + + if(has_else) + { + dest.destructive_append(tmp_x); + dest.destructive_append(tmp_y); + } + + dest.destructive_append(tmp_z); +} + +/*******************************************************************\ + +Function: goto_convertt::generate_conditional_branch + + Inputs: + + Outputs: + + Purpose: if(guard) goto target; + +\*******************************************************************/ + +static bool has_and_or(const exprt &expr) +{ + forall_operands(it, expr) + if(has_and_or(*it)) return true; + + if(expr.id()==ID_and || expr.id()==ID_or) + return true; + + return false; +} + +void goto_convertt::generate_conditional_branch( + const exprt &guard, + goto_programt::targett target_true, + const locationt &location, + goto_programt &dest) +{ + if(has_and_or(guard)) + { + // if(guard) goto target; + // becomes + // if(guard) goto target; else goto next; + // next: skip; + + goto_programt tmp; + goto_programt::targett target_false=tmp.add_instruction(); + target_false->make_skip(); + + generate_conditional_branch( + guard, target_true, target_false, location, dest); + + dest.destructive_append(tmp); + } + else + { + // simple branch + exprt cond=guard; + clean_expr(cond, dest); + + goto_programt tmp; + goto_programt::targett g=tmp.add_instruction(); + g->make_goto(target_true); + g->guard=cond; + g->location=location; + dest.destructive_append(tmp); + } +} + +/*******************************************************************\ + +Function: goto_convertt::generate_conditional_branch + + Inputs: + + Outputs: + + Purpose: if(guard) goto target_true; else goto target_false; + +\*******************************************************************/ + +void goto_convertt::generate_conditional_branch( + const exprt &guard, + goto_programt::targett target_true, + goto_programt::targett target_false, + const locationt &location, + goto_programt &dest) +{ + if(guard.id()==ID_not) + { + assert(guard.operands().size()==1); + // simply swap targets + generate_conditional_branch( + guard.op0(), target_false, target_true, location, dest); + return; + } + + if(guard.id()==ID_and) + { + // turn + // if(a && b) goto target_true; else goto target_false; + // into + // if(!a) goto target_false; + // if(!b) goto target_false; + // goto target_true; + + std::list op; + collect_operands(guard, guard.id(), op); + + forall_expr_list(it, op) + generate_conditional_branch( + gen_not(*it), target_false, location, dest); + + goto_programt::targett t_true=dest.add_instruction(); + t_true->make_goto(target_true); + t_true->guard=true_exprt(); + t_true->location=location; + + return; + } + else if(guard.id()==ID_or) + { + // turn + // if(a || b) goto target_true; else goto target_false; + // into + // if(a) goto target_true; + // if(b) goto target_true; + // goto target_false; + + std::list op; + collect_operands(guard, guard.id(), op); + + forall_expr_list(it, op) + generate_conditional_branch( + *it, target_true, location, dest); + + goto_programt::targett t_false=dest.add_instruction(); + t_false->make_goto(target_false); + t_false->guard=true_exprt(); + t_false->location=guard.location(); + + return; + } + + exprt cond=guard; + clean_expr(cond, dest); + + goto_programt::targett t_true=dest.add_instruction(); + t_true->make_goto(target_true); + t_true->guard=cond; + t_true->location=location; + + goto_programt::targett t_false=dest.add_instruction(); + t_false->make_goto(target_false); + t_false->guard=true_exprt(); + t_false->location=location; +} + +/*******************************************************************\ + +Function: goto_convertt::get_string_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const irep_idt goto_convertt::get_string_constant( + const exprt &expr) +{ + if(expr.id()==ID_typecast && + expr.operands().size()==1) + return get_string_constant(expr.op0()); + + if(expr.id()==ID_address_of && + expr.operands().size()==1 && + expr.op0().id()==ID_index && + expr.op0().operands().size()==2) + { + const exprt &index_op=expr.op0().op0(); + + if(index_op.id()==ID_string_constant) + return index_op.get(ID_value); + + if(index_op.id()==ID_symbol) + { + //const symbolt &symbol=ns.lookup(index_op.get(ID_identifier)); + return ""; + } + } + + err_location(expr); + str << "expected string constant, but got: " + << expr.pretty() << std::endl; + + throw 0; +} + +/*******************************************************************\ + +Function: goto_convertt::new_tmp_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +symbolt &goto_convertt::new_tmp_symbol( + const typet &type, + const std::string &suffix, + goto_programt &dest, + const locationt &location) +{ + symbolt new_symbol; + symbolt *symbol_ptr; + + do + { + new_symbol.base_name="tmp_"+suffix+"$"+i2string(++temporary_counter); + new_symbol.name=tmp_symbol_prefix+id2string(new_symbol.base_name); + new_symbol.lvalue=true; + new_symbol.thread_local=true; + new_symbol.type=type; + } while(context.move(new_symbol, symbol_ptr)); + + tmp_symbols.push_back(symbol_ptr->name); + + goto_programt::targett t=dest.add_instruction(DECL); + t->code=code_declt(symbol_expr(*symbol_ptr)); + t->location=location; + + return *symbol_ptr; +} + diff --git a/src/goto-programs/goto_convert.h b/src/goto-programs/goto_convert.h new file mode 100644 index 00000000000..71b0ef2d928 --- /dev/null +++ b/src/goto-programs/goto_convert.h @@ -0,0 +1,33 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_GOTO_CONVERT_H +#define CPROVER_GOTO_PROGRAMS_GOTO_CONVERT_H + +#include +#include +#include + +#include "goto_program.h" + +// start from given code +void goto_convert( + const codet &code, + contextt &context, + const optionst &options, + goto_programt &dest, + message_handlert &message_handler); + +// start from "main" +void goto_convert( + contextt &context, + const optionst &options, + goto_programt &dest, + message_handlert &message_handler); + +#endif diff --git a/src/goto-programs/goto_convert_class.h b/src/goto-programs/goto_convert_class.h new file mode 100644 index 00000000000..82893e1c342 --- /dev/null +++ b/src/goto-programs/goto_convert_class.h @@ -0,0 +1,351 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_GOTO_CONVERT_CLASS_H +#define CPROVER_GOTO_PROGRAMS_GOTO_CONVERT_CLASS_H + +#include + +#include +#include +#include +#include +#include +#include + +#include "goto_program.h" + +class goto_convertt:public message_streamt +{ +public: + void goto_convert(const codet &code, goto_programt &dest); + + goto_convertt( + contextt &_context, + const optionst &_options, + message_handlert &_message_handler): + message_streamt(_message_handler), + context(_context), + options(_options), + ns(_context), + temporary_counter(0), + tmp_symbol_prefix("goto_convertt::") + { + } + + virtual ~goto_convertt() + { + } + +protected: + contextt &context; + const optionst &options; + namespacet ns; + unsigned temporary_counter; + std::string tmp_symbol_prefix; + + void goto_convert_rec(const codet &code, goto_programt &dest); + + // + // tools for symbols + // + void new_name(symbolt &symbol); + const symbolt &lookup(const irep_idt &identifier) const; + + symbolt &new_tmp_symbol( + const typet &type, + const std::string &suffix, + goto_programt &dest, + const locationt &location); + + typedef std::list tmp_symbolst; + tmp_symbolst tmp_symbols; + + // + // translation of C expressions (with side effects) + // into the program logic + // + + void clean_expr( + exprt &expr, + goto_programt &dest, + bool result_is_used=true); + + static bool needs_cleaning(const exprt &expr); + + void make_temp_symbol( + exprt &expr, + const std::string &suffix, + goto_programt &dest); + + void address_of_replace_objects( + exprt &expr, + goto_programt &dest); + + void rewrite_boolean(exprt &dest); + + static bool has_sideeffect(const exprt &expr); + static bool has_function_call(const exprt &expr); + + void remove_side_effect(side_effect_exprt &expr, goto_programt &dest, bool result_is_used); + void remove_assignment(side_effect_exprt &expr, goto_programt &dest); + void remove_pre(side_effect_exprt &expr, goto_programt &dest); + void remove_post(side_effect_exprt &expr, goto_programt &dest, bool result_is_used); + void remove_function_call(side_effect_exprt &expr, goto_programt &dest, bool result_is_used); + void remove_cpp_new(side_effect_exprt &expr, goto_programt &dest, bool result_is_used); + void remove_cpp_delete(side_effect_exprt &expr, goto_programt &dest, bool result_is_used); + void remove_malloc(side_effect_exprt &expr, goto_programt &dest, bool result_is_used); + void remove_temporary_object(side_effect_exprt &expr, goto_programt &dest, bool result_is_used); + void remove_statement_expression(side_effect_exprt &expr, goto_programt &dest, bool result_is_used); + void remove_gcc_conditional_expression(exprt &expr, goto_programt &dest); + + virtual void do_cpp_new( + const exprt &lhs, + const side_effect_exprt &rhs, + goto_programt &dest); + + static void replace_new_object( + const exprt &object, + exprt &dest); + + void cpp_new_initializer( + const exprt &lhs, + const side_effect_exprt &rhs, + goto_programt &dest); + + // + // function calls + // + + virtual void do_function_call( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest); + + virtual void do_function_call_if( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest); + + virtual void do_function_call_symbol( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest); + + virtual void do_function_call_symbol(const symbolt &symbol) + { + } + + virtual void do_function_call_dereference( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest); + + // + // conversion + // + void convert_block(const codet &code, goto_programt &dest); + void convert_decl(const code_declt &code, goto_programt &dest); + void convert_decl_type(const codet &code, goto_programt &dest); + void convert_expression(const code_expressiont &code, goto_programt &dest); + void convert_assign(const code_assignt &code, goto_programt &dest); + void convert_cpp_delete(const codet &code, goto_programt &dest); + void convert_for(const codet &code, goto_programt &dest); + void convert_while(const codet &code, goto_programt &dest); + void convert_dowhile(const codet &code, goto_programt &dest); + void convert_assume(const code_assumet &code, goto_programt &dest); + void convert_assert(const code_assertt &code, goto_programt &dest); + void convert_switch(const codet &code, goto_programt &dest); + void convert_break(const code_breakt &code, goto_programt &dest); + void convert_return(const code_returnt &code, goto_programt &dest); + void convert_continue(const code_continuet &code, goto_programt &dest); + void convert_ifthenelse(const codet &code, goto_programt &dest); + void convert_init(const codet &code, goto_programt &dest); + void convert_goto(const codet &code, goto_programt &dest); + void convert_computed_goto(const codet &code, goto_programt &dest); + void convert_skip(const codet &code, goto_programt &dest); + void convert_non_deterministic_goto(const codet &code, goto_programt &dest); + void convert_label(const code_labelt &code, goto_programt &dest); + void convert_function_call(const code_function_callt &code, goto_programt &dest); + void convert_specc_notify(const codet &code, goto_programt &dest); + void convert_specc_wait(const codet &code, goto_programt &dest); + void convert_specc_par(const codet &code, goto_programt &dest); + void convert_specc_event(const exprt &op, + std::set &events); + void convert_start_thread(const codet &code, goto_programt &dest); + void convert_end_thread(const codet &code, goto_programt &dest); + void convert_atomic_begin(const codet &code, goto_programt &dest); + void convert_atomic_end(const codet &code, goto_programt &dest); + void convert_bp_enforce(const codet &code, goto_programt &dest); + void convert_bp_abortif(const codet &code, goto_programt &dest); + void convert(const codet &code, goto_programt &dest); + void copy(const codet &code, goto_program_instruction_typet type, goto_programt &dest); + + // + // gotos + // + + void finish_gotos(); + void finish_computed_gotos(goto_programt &dest); + + typedef std::map labelst; + typedef std::list gotost; + typedef std::list computed_gotost; + typedef exprt::operandst caset; + typedef std::map casest; + + struct break_continue_targetst + { + break_continue_targetst():break_set(false), continue_set(false) + { + } + + goto_programt::targett break_target; + bool break_set; + + goto_programt::targett continue_target; + bool continue_set; + + void restore(const break_continue_targetst &targets) + { + *this=targets; + } + + void set_break(goto_programt::targett _break_target) + { + break_set=true; + break_target=_break_target; + } + + void set_continue(goto_programt::targett _continue_target) + { + continue_set=true; + continue_target=_continue_target; + } + }; + + struct break_continue_switch_targetst:public break_continue_targetst + { + break_continue_switch_targetst(): + default_set(false) + { + } + + using break_continue_targetst::restore; + + void restore(const break_continue_switch_targetst &targets) + { + *this=targets; + } + + void set_default(goto_programt::targett _default_target) + { + default_set=true; + default_target=_default_target; + } + + goto_programt::targett default_target; + bool default_set; + casest cases; + }; + + struct targetst:public break_continue_switch_targetst + { + bool return_set; + bool return_value; + + labelst labels; + gotost gotos; + computed_gotost computed_gotos; + + targetst(): + return_set(false) + { + } + + void swap(targetst &targets) + { + std::swap(targets.break_target, break_target); + std::swap(targets.break_set, break_set); + + std::swap(targets.continue_target, continue_target); + std::swap(targets.continue_set, continue_set); + + std::swap(targets.return_value, return_value); + std::swap(targets.return_set, return_set); + + std::swap(targets.default_target, default_target); + std::swap(targets.default_set, default_set); + + targets.labels.swap(labels); + targets.gotos.swap(gotos); + targets.cases.swap(cases); + } + } targets; + + void case_guard( + const exprt &value, + const caset &case_op, + exprt &dest); + + // if(cond) { true_case } else { false_case } + void generate_ifthenelse( + const exprt &cond, + goto_programt &true_case, + goto_programt &false_case, + const locationt &location, + goto_programt &dest); + + // if(guard) goto target_true; else goto target_false; + void generate_conditional_branch( + const exprt &guard, + goto_programt::targett target_true, + goto_programt::targett target_false, + const locationt &location, + goto_programt &dest); + + // if(guard) goto target; + void generate_conditional_branch( + const exprt &guard, + goto_programt::targett target_true, + const locationt &location, + goto_programt &dest); + + // turn a OP b OP c into a list a, b, c + static void collect_operands( + const exprt &expr, + const irep_idt &id, + std::list &dest); + + // + // misc + // + const irep_idt get_string_constant(const exprt &expr); + + // some built-in functions + void do_atomic_begin (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_atomic_end (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_create_thread (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_array_set (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_array_equal (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_array_copy (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_printf (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_input (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_output (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_cover (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_prob_coin (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + void do_prob_uniform (const exprt &lhs, const exprt &rhs, const exprt::operandst &arguments, goto_programt &dest); + + exprt get_array_argument(const exprt &src); +}; + +#endif diff --git a/src/goto-programs/goto_convert_functions.cpp b/src/goto-programs/goto_convert_functions.cpp new file mode 100644 index 00000000000..d103e3cfd48 --- /dev/null +++ b/src/goto-programs/goto_convert_functions.cpp @@ -0,0 +1,341 @@ +/*******************************************************************\ + +Module: Goto Programs with Functions + +Author: Daniel Kroening + +Date: June 2003 + +\*******************************************************************/ + +#include + +#include +#include + +#include "goto_convert_functions.h" +#include "goto_inline.h" +#include "remove_skip.h" + +/*******************************************************************\ + +Function: goto_convert_functionst::goto_convert_functionst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +goto_convert_functionst::goto_convert_functionst( + contextt &_context, + const optionst &_options, + goto_functionst &_functions, + message_handlert &_message_handler): + goto_convertt(_context, _options, _message_handler), + functions(_functions) +{ +} + +/*******************************************************************\ + +Function: goto_convert_functionst::~goto_convert_functionst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +goto_convert_functionst::~goto_convert_functionst() +{ +} + +/*******************************************************************\ + +Function: goto_convert_functionst::goto_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convert_functionst::goto_convert() +{ + // warning! hash-table iterators are not stable + + typedef std::list symbol_listt; + symbol_listt symbol_list; + + forall_symbols(it, context.symbols) + { + if(!it->second.is_type && + it->second.type.id()==ID_code) + symbol_list.push_back(it->first); + } + + for(symbol_listt::const_iterator + it=symbol_list.begin(); + it!=symbol_list.end(); + it++) + { + convert_function(*it); + } + + functions.compute_location_numbers(); + + // this removes the parse tree of the bodies from memory + #if 0 + Forall_symbols(it, context.symbols) + { + if(!it->second.is_type && + it->second.type.id()==ID_code && + it->second.value.is_not_nil()) + it->second.value=codet(); + } + #endif +} + +/*******************************************************************\ + +Function: goto_convert_functionst::hide + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_convert_functionst::hide(const goto_programt &goto_program) +{ + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + for(goto_programt::instructiont::labelst::const_iterator + l_it=i_it->labels.begin(); + l_it!=i_it->labels.end(); + l_it++) + { + if(*l_it=="__CPROVER_HIDE") + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: goto_convert_functionst::add_return + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convert_functionst::add_return( + goto_functionst::goto_functiont &f, + const locationt &location) +{ + if(!f.body.instructions.empty() && + f.body.instructions.back().is_return()) + return; // not needed, we have one already + + // see if we have an unconditional goto at the end + if(!f.body.instructions.empty() && + f.body.instructions.back().is_goto() && + f.body.instructions.back().guard.is_true()) + return; + + goto_programt::targett t=f.body.add_instruction(); + t->make_return(); + t->code=code_returnt(); + t->location=location; + + exprt rhs=exprt(ID_sideeffect, f.type.return_type()); + rhs.set(ID_statement, ID_nondet); + t->code.move_to_operands(rhs); +} + +/*******************************************************************\ + +Function: goto_convert_functionst::convert_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convert_functionst::convert_function(const irep_idt &identifier) +{ + const symbolt &symbol=ns.lookup(identifier); + goto_functionst::goto_functiont &f=functions.function_map[identifier]; + + // make tmp variables local to function + tmp_symbol_prefix=id2string(symbol.name)+"::$tmp::"; + temporary_counter=0; + + f.type=to_code_type(symbol.type); + f.body_available=symbol.value.is_not_nil(); + + if(!f.body_available) return; + + if(symbol.value.id()!=ID_code) + { + err_location(symbol.value); + throw "got invalid code for function `"+id2string(identifier)+"'"; + } + + const codet &code=to_code(symbol.value); + + locationt end_location; + + if(code.get_statement()==ID_block) + end_location=static_cast( + code.find("#end_location")); + else + end_location.make_nil(); + + targets=targetst(); + targets.return_set=true; + targets.return_value= + f.type.return_type().id()!=ID_empty && + f.type.return_type().id()!=ID_constructor && + f.type.return_type().id()!=ID_destructor; + + goto_convert_rec(code, f.body); + + // add non-det return value, if needed + if(targets.return_value) + add_return(f, end_location); + + // add "end of function" + goto_programt::targett t=f.body.add_instruction(); + t->type=END_FUNCTION; + t->location=end_location; + t->code.set(ID_identifier, identifier); + + // do function tags + Forall_goto_program_instructions(i_it, f.body) + i_it->function=identifier; + + // remove_skip depends on the target numbers + f.body.compute_target_numbers(); + + remove_skip(f.body); + + f.body.update(); + + if(hide(f.body)) + f.type.set("#hide", true); +} + +/*******************************************************************\ + +Function: goto_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convert( + contextt &context, + const optionst &options, + goto_functionst &functions, + message_handlert &message_handler) +{ + goto_convert_functionst goto_convert_functions( + context, options, functions, message_handler); + + try + { + goto_convert_functions.goto_convert(); + } + + catch(int) + { + goto_convert_functions.error(); + } + + catch(const char *e) + { + goto_convert_functions.error(e); + } + + catch(const std::string &e) + { + goto_convert_functions.error(e); + } + + if(goto_convert_functions.get_error_found()) + throw 0; +} + +/*******************************************************************\ + +Function: goto_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convert( + const irep_idt &identifier, + contextt &context, + const optionst &options, + goto_functionst &functions, + message_handlert &message_handler) +{ + goto_convert_functionst goto_convert_functions( + context, options, functions, message_handler); + + try + { + goto_convert_functions.convert_function(identifier); + } + + catch(int) + { + goto_convert_functions.error(); + } + + catch(const char *e) + { + goto_convert_functions.error(e); + } + + catch(const std::string &e) + { + goto_convert_functions.error(e); + } + + if(goto_convert_functions.get_error_found()) + throw 0; +} + + diff --git a/src/goto-programs/goto_convert_functions.h b/src/goto-programs/goto_convert_functions.h new file mode 100644 index 00000000000..ea371d9bf48 --- /dev/null +++ b/src/goto-programs/goto_convert_functions.h @@ -0,0 +1,59 @@ +/*******************************************************************\ + +Module: Goto Programs with Functions + +Author: Daniel Kroening + +Date: June 2003 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_CONVERT_FUNCTIONS_H +#define CPROVER_GOTO_CONVERT_FUNCTIONS_H + +#include "goto_functions.h" +#include "goto_convert_class.h" + +// convert it all! +void goto_convert( + contextt &context, + const optionst &options, + goto_functionst &functions, + message_handlert &message_handler); + +// just convert a specific function +void goto_convert( + const irep_idt &identifier, + contextt &context, + const optionst &options, + goto_functionst &functions, + message_handlert &message_handler); + +class goto_convert_functionst:public goto_convertt +{ +public: + void goto_convert(); + void convert_function(const irep_idt &identifier); + + goto_convert_functionst( + contextt &_context, + const optionst &_options, + goto_functionst &_functions, + message_handlert &_message_handler); + + virtual ~goto_convert_functionst(); + +protected: + goto_functionst &functions; + + static bool hide(const goto_programt &goto_program); + + // + // function calls + // + void add_return( + goto_functionst::goto_functiont &f, + const locationt &location); +}; + +#endif diff --git a/src/goto-programs/goto_function.cpp b/src/goto-programs/goto_function.cpp new file mode 100644 index 00000000000..09b85f2ac11 --- /dev/null +++ b/src/goto-programs/goto_function.cpp @@ -0,0 +1,213 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "goto_convert_class.h" + +/*******************************************************************\ + +Function: goto_convertt::convert_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::convert_function_call( + const code_function_callt &function_call, + goto_programt &dest) +{ + do_function_call( + function_call.lhs(), + function_call.function(), + function_call.arguments(), + dest); +} + +/*******************************************************************\ + +Function: goto_convertt::do_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_function_call( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + // make it all side effect free + + exprt new_lhs=lhs, + new_function=function; + + exprt::operandst new_arguments=arguments; + + if(!new_lhs.is_nil()) + clean_expr(new_lhs, dest); + + clean_expr(new_function, dest); + + Forall_expr(it, new_arguments) + clean_expr(*it, dest); + + // split on the function + + if(new_function.id()==ID_dereference) + { + do_function_call_dereference(new_lhs, new_function, new_arguments, dest); + } + else if(new_function.id()==ID_if) + { + do_function_call_if(new_lhs, new_function, new_arguments, dest); + } + else if(new_function.id()==ID_symbol) + { + do_function_call_symbol(new_lhs, new_function, new_arguments, dest); + } + else if(new_function.id()=="NULL-object") + { + } + else + { + err_location(function); + throw "unexpected function argument: "+new_function.id_string(); + } +} + +/*******************************************************************\ + +Function: goto_convertt::do_function_call_if + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_function_call_if( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + if(function.operands().size()!=3) + { + err_location(function); + throw "if expects three operands"; + } + + // case split + + // c?f():g() + //-------------------- + // v: if(!c) goto y; + // w: f(); + // x: goto z; + // y: g(); + // z: ; + + // do the v label + goto_programt tmp_v; + goto_programt::targett v=tmp_v.add_instruction(); + + // do the x label + goto_programt tmp_x; + goto_programt::targett x=tmp_x.add_instruction(); + + // do the z label + goto_programt tmp_z; + goto_programt::targett z=tmp_z.add_instruction(); + z->make_skip(); + + // y: g(); + goto_programt tmp_y; + goto_programt::targett y; + + do_function_call(lhs, function.op2(), arguments, tmp_y); + + if(tmp_y.instructions.empty()) + y=tmp_y.add_instruction(SKIP); + else + y=tmp_y.instructions.begin(); + + // v: if(!c) goto y; + v->make_goto(y); + v->guard=function.op0(); + v->guard.make_not(); + v->location=function.op0().location(); + + // w: f(); + goto_programt tmp_w; + + do_function_call(lhs, function.op1(), arguments, tmp_w); + + if(tmp_w.instructions.empty()) + tmp_w.add_instruction(SKIP); + + // x: goto z; + x->make_goto(z); + + dest.destructive_append(tmp_v); + dest.destructive_append(tmp_w); + dest.destructive_append(tmp_x); + dest.destructive_append(tmp_y); + dest.destructive_append(tmp_z); +} + +/*******************************************************************\ + +Function: goto_convertt::do_function_call_dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::do_function_call_dereference( + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + goto_programt &dest) +{ + // don't know what to do with it + goto_programt::targett t=dest.add_instruction(FUNCTION_CALL); + + code_function_callt function_call; + function_call.location()=function.location(); + function_call.lhs()=lhs; + function_call.function()=function; + function_call.arguments()=arguments; + + t->location=function.location(); + t->code.swap(function_call); +} diff --git a/src/goto-programs/goto_function_pointers.cpp b/src/goto-programs/goto_function_pointers.cpp new file mode 100644 index 00000000000..5e3af7761e4 --- /dev/null +++ b/src/goto-programs/goto_function_pointers.cpp @@ -0,0 +1,400 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "remove_skip.h" +#include "goto_function_pointers.h" + +/*******************************************************************\ + + Class: remove_function_pointerst + + Purpose: + +\*******************************************************************/ + +class remove_function_pointerst +{ +public: + explicit remove_function_pointerst(const namespacet &_ns):ns(_ns) + { + } + + void operator()(goto_functionst &functions); + +protected: + const namespacet &ns; + + void remove_function_pointer( + goto_programt &goto_program, + goto_programt::targett target); + + bool remove_function_pointers(goto_programt &goto_program); + + std::set address_taken; + + void compute_address_taken(const exprt &src); + void compute_address_taken(const goto_programt &goto_program); + void compute_address_taken(const goto_functionst &goto_functions); + + typedef std::map type_mapt; + type_mapt type_map; + + bool is_type_compatible( + const code_typet &call_type, + const code_typet &function_type); +}; + +/*******************************************************************\ + +Function: remove_function_pointerst::compute_address_taken + + Inputs: + + Outputs: + + Purpose: get all functions whose address is taken + +\*******************************************************************/ + +void remove_function_pointerst::compute_address_taken( + const exprt &src) +{ + forall_operands(it, src) + compute_address_taken(*it); + + if(src.id()==ID_address_of && + src.type().id()==ID_pointer && + src.type().subtype().id()==ID_code) + { + assert(src.operands().size()==1); + const exprt &op=src.op0(); + if(op.id()==ID_symbol) + address_taken.insert(to_symbol_expr(op).get_identifier()); + } +} + +/*******************************************************************\ + +Function: compute_address_taken + + Inputs: + + Outputs: + + Purpose: get all functions whose address is taken + +\*******************************************************************/ + +void remove_function_pointerst::compute_address_taken( + const goto_programt &goto_program) +{ + forall_goto_program_instructions(it, goto_program) + { + compute_address_taken(it->guard); + compute_address_taken(it->code); + } +} + +/*******************************************************************\ + +Function: remove_function_pointerst::compute_address_taken + + Inputs: + + Outputs: + + Purpose: get all functions whose address is taken + +\*******************************************************************/ + +void remove_function_pointerst::compute_address_taken( + const goto_functionst &goto_functions) +{ + forall_goto_functions(it, goto_functions) + compute_address_taken(it->second.body); +} + +/*******************************************************************\ + +Function: remove_function_pointerst::is_type_compatible + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool remove_function_pointerst::is_type_compatible( + const code_typet &call_type, + const code_typet &function_type) +{ + // we are willing to ignore anything that's returned + // if we call with 'void' + if(type_eq(call_type.return_type(), empty_typet(), ns)) + { + // ok + } + else + { + if(!type_eq(call_type.return_type(), + function_type.return_type(), ns)) + return false; + } + + // let's look at the arguments + const code_typet::argumentst &call_arguments=call_type.arguments(); + const code_typet::argumentst &function_arguments=function_type.arguments(); + + if(function_type.has_ellipsis() && + function_arguments.empty()) + { + // always ok + } + else + { + // we are quite strict here, could be much more generous + if(call_arguments.size()!=function_arguments.size()) + return false; + + // the following is also quite strict + for(unsigned i=0; icode); + + const exprt &function=code.function(); + + // this better have the right type + const code_typet &call_type=to_code_type(function.type()); + + assert(function.id()==ID_dereference); + assert(function.operands().size()==1); + + const exprt &pointer=function.op0(); + + typedef std::list functionst; + functionst functions; + + // get all type-compatible functions + // whose address is ever taken + for(type_mapt::const_iterator f_it= + type_map.begin(); + f_it!=type_map.end(); + f_it++) + { + // address taken? + if(address_taken.find(f_it->first)==address_taken.end()) + continue; + + // type-compatible? + if(!is_type_compatible(call_type, f_it->second)) + continue; + + symbol_exprt expr; + expr.type()=f_it->second; + expr.set_identifier(f_it->first); + functions.push_back(expr); + } + + // the final target is a skip + + goto_programt final_skip; + goto_programt::targett t_final=final_skip.add_instruction(); + t_final->make_skip(); + + // build the calls and gotos + + goto_programt new_code_calls; + goto_programt new_code_gotos; + + for(functionst::const_iterator + it=functions.begin(); + it!=functions.end(); + it++) + { + // call + goto_programt::targett t1=new_code_calls.add_instruction(); + t1->make_function_call(code); + to_code_function_call(t1->code).function()=*it; + goto_programt::targett t2=new_code_calls.add_instruction(); + t2->make_goto(t_final, true_exprt()); + + // goto + address_of_exprt address_of; + address_of.object()=*it; + address_of.type()=pointer_typet(); + address_of.type().subtype()=it->type(); + + if(address_of.type()!=pointer.type()) + address_of.make_typecast(pointer.type()); + + goto_programt::targett t3=new_code_gotos.add_instruction(); + t3->make_goto(t1, equality_exprt(pointer, address_of)); + } + + goto_programt new_code; + + // patch them all together + new_code.destructive_append(new_code_gotos); + new_code.destructive_append(new_code_calls); + new_code.destructive_append(final_skip); + + // set locations + Forall_goto_program_instructions(it, new_code) + it->location=target->location; + + goto_programt::targett next_target=target; + next_target++; + + goto_program.instructions.splice(next_target, new_code.instructions); + + // we preserve the dereferencing to possibly catch + // pointer-related errors + code_expressiont code_expression; + code_expression.location()=function.location(); + code_expression.expression()=function; + target->code.swap(code_expression); + target->type=OTHER; +} + +/*******************************************************************\ + +Function: remove_function_pointerst::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool remove_function_pointerst::remove_function_pointers( + goto_programt &goto_program) +{ + bool did_something=false; + + Forall_goto_program_instructions(target, goto_program) + if(target->is_function_call()) + { + const code_function_callt &code= + to_code_function_call(target->code); + + if(code.function().id()==ID_dereference) + { + remove_function_pointer(goto_program, target); + did_something=true; + } + } + + if(did_something) + { + remove_skip(goto_program); + goto_program.update(); + } + + return did_something; +} + +/*******************************************************************\ + +Function: remove_function_pointerst::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void remove_function_pointerst::operator()(goto_functionst &functions) +{ + bool did_something=false; + + compute_address_taken(functions); + + // build type map + for(goto_functionst::function_mapt::iterator f_it= + functions.function_map.begin(); + f_it!=functions.function_map.end(); + f_it++) + type_map[f_it->first]=f_it->second.type; + + for(goto_functionst::function_mapt::iterator f_it= + functions.function_map.begin(); + f_it!=functions.function_map.end(); + f_it++) + { + goto_programt &goto_program=f_it->second.body; + + if(remove_function_pointers(goto_program)) + did_something=true; + } + + if(did_something) + functions.compute_location_numbers(); +} + +/*******************************************************************\ + +Function: remove_function_pointers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void remove_function_pointers( + const namespacet &ns, + goto_functionst &functions) +{ + remove_function_pointerst rfp(ns); + rfp(functions); +} diff --git a/src/goto-programs/goto_function_pointers.h b/src/goto-programs/goto_function_pointers.h new file mode 100644 index 00000000000..fc01d583f93 --- /dev/null +++ b/src/goto-programs/goto_function_pointers.h @@ -0,0 +1,22 @@ +/*******************************************************************\ + +Module: Remove Indirect Function Calls + +Author: Daniel Kroening + +Date: June 2003 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_FUNCTION_POINTERS_H +#define CPROVER_GOTO_FUNCTION_POINTERS_H + +#include "goto_functions.h" + +// remove indirect function calls +// and replace by case-split +void remove_function_pointers( + const namespacet &ns, + goto_functionst &functions); + +#endif diff --git a/src/goto-programs/goto_function_serialization.cpp b/src/goto-programs/goto_function_serialization.cpp new file mode 100644 index 00000000000..43f8375472d --- /dev/null +++ b/src/goto-programs/goto_function_serialization.cpp @@ -0,0 +1,52 @@ +/*******************************************************************\ + +Module: Convert goto functions to binary format and back (with irep + hashing) + +Author: CM Wintersteiger + +Date: May 2007 + +\*******************************************************************/ + +#include "goto_function_serialization.h" +#include "goto_program_serialization.h" + +/*******************************************************************\ + +Function: goto_function_serializationt::convert + + Inputs: goto_function and an xml node + + Outputs: none + + Purpose: takes a goto_function and outputs the according serialization + +\*******************************************************************/ + +void goto_function_serializationt::convert( + const goto_functionst::goto_functiont& function, + std::ostream &out) +{ + if (function.body_available) + gpconverter.convert(function.body, out); +} + +/*******************************************************************\ + +Function: goto_function_serializationt::convert + + Inputs: xml structure and a goto_function to fill + + Outputs: none + + Purpose: reconstructs a goto_function from a serialized stream + +\*******************************************************************/ +void goto_function_serializationt::convert( + std::istream &in, + irept &funsymb) +{ + gpconverter.convert(in, funsymb); + // don't forget to fix the functions type via the symbol table! +} diff --git a/src/goto-programs/goto_function_serialization.h b/src/goto-programs/goto_function_serialization.h new file mode 100644 index 00000000000..9c3dc5882ad --- /dev/null +++ b/src/goto-programs/goto_function_serialization.h @@ -0,0 +1,34 @@ +/*******************************************************************\ + +Module: Convert goto functions into binary format and back (with irep + hashing). + +Author: CM Wintersteiger + +Date: May 2007 + +\*******************************************************************/ + +#ifndef GOTO_FUNCTION_SERIALIZATION_H_ +#define GOTO_FUNCTION_SERIALIZATION_H_ + +#include + +#include +#include + +class goto_function_serializationt +{ + private: + irep_serializationt::ireps_containert &ireps_container; + goto_program_serializationt gpconverter; + + public: + goto_function_serializationt(irep_serializationt::ireps_containert &ic) : + ireps_container(ic), gpconverter(ic) {}; + + void convert(std::istream &, irept &); + void convert(const goto_functionst::goto_functiont &, std::ostream &); +}; + +#endif /*GOTO_FUNCTION_SERIALIZATION_H_*/ diff --git a/src/goto-programs/goto_functions.cpp b/src/goto-programs/goto_functions.cpp new file mode 100644 index 00000000000..f814803f5e9 --- /dev/null +++ b/src/goto-programs/goto_functions.cpp @@ -0,0 +1,43 @@ +/*******************************************************************\ + +Module: Goto Programs with Functions + +Author: Daniel Kroening + +Date: June 2003 + +\*******************************************************************/ + +#include "goto_functions.h" + +/*******************************************************************\ + +Function: get_local_identifiers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void get_local_identifiers( + const goto_function_templatet &goto_function, + std::set &dest) +{ + goto_function.body.get_decl_identifiers(dest); + + const code_typet::argumentst &arguments= + goto_function.type.arguments(); + + // add arguments + for(code_typet::argumentst::const_iterator + a_it=arguments.begin(); + a_it!=arguments.end(); + a_it++) + { + const irep_idt &identifier=a_it->get_identifier(); + if(identifier!="") dest.insert(identifier); + } +} diff --git a/src/goto-programs/goto_functions.h b/src/goto-programs/goto_functions.h new file mode 100644 index 00000000000..bd8968e0a41 --- /dev/null +++ b/src/goto-programs/goto_functions.h @@ -0,0 +1,38 @@ +/*******************************************************************\ + +Module: Goto Programs with Functions + +Author: Daniel Kroening + +Date: June 2003 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_FUNCTIONS_H +#define CPROVER_GOTO_FUNCTIONS_H + +#include + +#include + +#include "goto_program.h" +#include "goto_functions_template.h" + +class goto_functionst:public goto_functions_templatet +{ +public: +}; + +#define Forall_goto_functions(it, functions) \ + for(goto_functionst::function_mapt::iterator it=(functions).function_map.begin(); \ + it!=(functions).function_map.end(); it++) + +#define forall_goto_functions(it, functions) \ + for(goto_functionst::function_mapt::const_iterator it=(functions).function_map.begin(); \ + it!=(functions).function_map.end(); it++) + +void get_local_identifiers( + const goto_function_templatet &goto_function, + std::set &dest); + +#endif diff --git a/src/goto-programs/goto_functions_template.h b/src/goto-programs/goto_functions_template.h new file mode 100644 index 00000000000..f19a953e9bc --- /dev/null +++ b/src/goto-programs/goto_functions_template.h @@ -0,0 +1,245 @@ +/*******************************************************************\ + +Module: Goto Programs with Functions + +Author: Daniel Kroening + +Date: June 2003 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_FUNCTIONS_TEMPLATE_H +#define CPROVER_GOTO_FUNCTIONS_TEMPLATE_H + +#include + +#include + +template +class goto_function_templatet +{ +public: + bodyT body; + code_typet type; + bool body_available; + + bool is_inlined() const + { + return type.get_bool(ID_C_inlined); + } + + goto_function_templatet():body_available(false) + { + } + + void clear() + { + body.clear(); + type.clear(); + body_available=false; + } + + void swap(goto_function_templatet &other) + { + body.swap(other.body); + type.swap(other.type); + std::swap(body_available, other.body_available); + } + + void copy_from(const goto_function_templatet &other) + { + body.copy_from(other.body); + type=other.type; + body_available=other.body_available; + } + + goto_function_templatet(const goto_function_templatet &src): + type(src.type), + body_available(src.body_available) + { + body.copy_from(src.body); + } +}; + +template +class goto_functions_templatet +{ +public: + typedef goto_function_templatet goto_functiont; + typedef std::map function_mapt; + function_mapt function_map; + + goto_functions_templatet() + { + } + + void clear() + { + function_map.clear(); + } + + void output( + const namespacet &ns, + std::ostream &out) const; + + void compute_location_numbers(); + void compute_loop_numbers(); + void compute_target_numbers(); + void compute_incoming_edges(); + + void update() + { + compute_incoming_edges(); + compute_target_numbers(); + compute_location_numbers(); + } + + irep_idt main_id() const + { + return "main"; + } + + void swap(goto_functions_templatet &other) + { + function_map.swap(other.function_map); + } + + void copy_from(const goto_functions_templatet &other) + { + for(typename function_mapt::const_iterator + f_it=other.function_map.begin(); + f_it!=other.function_map.end(); + f_it++) + function_map[f_it->first].copy_from(f_it->second); + } + +private: + // copy constructor, deliberatly unavailable + goto_functions_templatet(const goto_functions_templatet &src); +}; + +/*******************************************************************\ + +Function: goto_functions_templatet::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void goto_functions_templatet::output( + const namespacet &ns, + std::ostream& out) const +{ + for(typename function_mapt::const_iterator + it=function_map.begin(); + it!=function_map.end(); + it++) + { + if(it->second.body_available) + { + out << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" << std::endl; + out << std::endl; + + const symbolt &symbol=ns.lookup(it->first); + out << symbol.display_name() << ":" << std::endl; + it->second.body.output(ns, symbol.name, out); + } + } +} + +/*******************************************************************\ + +Function: goto_functions_templatet::compute_location_numbers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void goto_functions_templatet::compute_location_numbers() +{ + unsigned nr=0; + + for(typename function_mapt::iterator + it=function_map.begin(); + it!=function_map.end(); + it++) + it->second.body.compute_location_numbers(nr); +} + +/*******************************************************************\ + +Function: goto_functions_templatet::compute_incoming_edges + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void goto_functions_templatet::compute_incoming_edges() +{ + for(typename function_mapt::iterator + it=function_map.begin(); + it!=function_map.end(); + it++) + it->second.body.compute_incoming_edges(); +} + +/*******************************************************************\ + +Function: goto_functions_templatet::compute_target_numbers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void goto_functions_templatet::compute_target_numbers() +{ + for(typename function_mapt::iterator + it=function_map.begin(); + it!=function_map.end(); + it++) + it->second.body.compute_target_numbers(); +} + +/*******************************************************************\ + +Function: goto_functions_templatet::compute_loop_numbers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void goto_functions_templatet::compute_loop_numbers() +{ + for(typename function_mapt::iterator + it=function_map.begin(); + it!=function_map.end(); + it++) + it->second.body.compute_loop_numbers(); +} + +#endif diff --git a/src/goto-programs/goto_inline.cpp b/src/goto-programs/goto_inline.cpp new file mode 100644 index 00000000000..10f75e1c16e --- /dev/null +++ b/src/goto-programs/goto_inline.cpp @@ -0,0 +1,702 @@ +/*******************************************************************\ + +Module: Function Inlining + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "remove_skip.h" +#include "goto_inline.h" +#include "goto_inline_class.h" + +/*******************************************************************\ + +Function: goto_inlinet::parameter_assignments + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_inlinet::parameter_assignments( + const locationt &location, + const code_typet &code_type, + const exprt::operandst &arguments, + goto_programt &dest) +{ + // iterates over the operands + exprt::operandst::const_iterator it1=arguments.begin(); + + const code_typet::argumentst &argument_types= + code_type.arguments(); + + // iterates over the types of the arguments + for(code_typet::argumentst::const_iterator + it2=argument_types.begin(); + it2!=argument_types.end(); + it2++) + { + // if you run out of actual arguments there was a mismatch + if(it1==arguments.end()) + { + err_location(location); + throw "function call: not enough arguments"; + } + + const code_typet::argumentt &argument=*it2; + + // this is the type the n-th argument should be + const typet &arg_type=ns.follow(argument.type()); + + const irep_idt &identifier=argument.get_identifier(); + + if(identifier==irep_idt()) + { + err_location(location); + throw "no identifier for function argument"; + } + + { + const symbolt &symbol=ns.lookup(identifier); + + goto_programt::targett decl=dest.add_instruction(); + decl->make_decl(); + decl->code=code_declt(symbol_expr(symbol)); + decl->code.location()=location; + decl->location=location; + decl->function=location.get_function(); + } + + // nil means "don't assign" + if(it1->is_nil()) + { + } + else + { + // this is the actual parameter + exprt actual(*it1); + + // it should be the same exact type, + // subject to some exceptions + if(!base_type_eq(arg_type, actual.type(), ns)) + { + const typet &f_argtype = ns.follow(arg_type); + const typet &f_acttype = ns.follow(actual.type()); + + // we are willing to do some conversion + if((f_argtype.id()==ID_pointer && + f_acttype.id()==ID_pointer) || + (f_argtype.id()==ID_array && + f_acttype.id()==ID_pointer && + f_argtype.subtype()==f_acttype.subtype())) + { + actual.make_typecast(arg_type); + } + else if((f_argtype.id()==ID_signedbv || + f_argtype.id()==ID_unsignedbv || + f_argtype.id()==ID_bool) && + (f_acttype.id()==ID_signedbv || + f_acttype.id()==ID_unsignedbv || + f_acttype.id()==ID_bool)) + { + actual.make_typecast(arg_type); + } + else + { + err_location(*it1); + + str << "function call: argument `" << identifier + << "' type mismatch: got `" + << from_type(ns, identifier, it1->type()) + << "', expected `" + << from_type(ns, identifier, arg_type) + << "'"; + throw 0; + } + } + + // adds an assignment of the actual parameter to the formal parameter + code_assignt assignment(symbol_exprt(identifier, arg_type), actual); + assignment.location()=location; + + dest.add_instruction(ASSIGN); + dest.instructions.back().location=location; + dest.instructions.back().code.swap(assignment); + dest.instructions.back().function=location.get_function(); + } + + it1++; + } + + if(it1!=arguments.end()) + { + // too many arguments -- we just ignore that, no harm done + } +} + +/*******************************************************************\ + +Function: goto_inlinet::replace_return + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_inlinet::replace_return( + goto_programt &dest, + const exprt &lhs, + const exprt &constrain) +{ + for(goto_programt::instructionst::iterator + it=dest.instructions.begin(); + it!=dest.instructions.end(); + it++) + { + if(it->is_return()) + { + if(lhs.is_not_nil()) + { + if(it->code.operands().size()!=1) + { + err_location(it->code); + str << "return expects one operand!" << std::endl; + warning(); + continue; + } + + goto_programt tmp; + goto_programt::targett assignment=tmp.add_instruction(ASSIGN); + + code_assignt code_assign(lhs, it->code.op0()); + + // this may happen if the declared return type at the call site + // differs from the defined return type + if(code_assign.lhs().type()!= + code_assign.rhs().type()) + code_assign.rhs().make_typecast(code_assign.lhs().type()); + + assignment->code=code_assign; + assignment->location=it->location; + assignment->function=it->location.get_function(); + + if(constrain.is_not_nil() && !constrain.is_true()) + { + codet constrain(ID_bp_constrain); + constrain.reserve_operands(2); + constrain.move_to_operands(assignment->code); + constrain.copy_to_operands(constrain); + } + + dest.insert_before_swap(it, *assignment); + it++; + } + else if(!it->code.operands().empty()) + { + goto_programt tmp; + goto_programt::targett expression=tmp.add_instruction(OTHER); + + expression->code=codet(ID_expression); + expression->code.move_to_operands(it->code.op0()); + expression->location=it->location; + expression->function=it->location.get_function(); + + dest.insert_before_swap(it, *expression); + it++; + } + + it->make_goto(--dest.instructions.end()); + } + } +} + +/*******************************************************************\ + +Function: replace_location + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void replace_location(locationt &dest, const locationt &new_location) +{ + // can't just copy, e.g., due to comments field + dest.id(irep_idt()); // not NIL + dest.set_file(new_location.get_file()); + dest.set_line(new_location.get_line()); + dest.set_column(new_location.get_column()); + dest.set_function(new_location.get_function()); +} + +/*******************************************************************\ + +Function: replace_location + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void replace_location(exprt &dest, const locationt &new_location) +{ + Forall_operands(it, dest) + replace_location(*it, new_location); + + if(dest.find(ID_C_location).is_not_nil()) + replace_location(dest.location(), new_location); +} + +/*******************************************************************\ + +Function: goto_inlinet::expand_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_inlinet::expand_function_call( + goto_programt &dest, + goto_programt::targett &target, + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + const exprt &constrain, + bool full) +{ + // look it up + if(function.id()!=ID_symbol) + { + err_location(function); + throw "function_call expects symbol as function operand, " + "but got `"+function.id_string()+"'"; + } + + const irep_idt &identifier=function.get(ID_identifier); + + // see if we are already expanding it + if(recursion_set.find(identifier)!=recursion_set.end()) + { + if(!full) + { + target++; + return; // simply ignore, we don't do full inlining, it's ok + } + + // it's really recursive, and we need full inlining. + // Uh. Buh. Give up. + err_location(function); + warning("recursion is ignored"); + target->make_skip(); + + target++; + return; + } + + goto_functionst::function_mapt::iterator m_it= + goto_functions.function_map.find(identifier); + + if(m_it==goto_functions.function_map.end()) + { + if(!full) + { + target++; + return; // simply ignore, we don't do full inlining, it's ok + } + + err_location(function); + str << "failed to find function `" << identifier + << "'"; + throw 0; + } + + const goto_functionst::goto_functiont &f=m_it->second; + + // see if we need to inline this + if(!full) + { + if(!f.body_available || + (!f.is_inlined() && f.body.instructions.size() > smallfunc_limit)) + { + target++; + return; + } + } + + if(f.body_available) + { + recursion_sett::iterator recursion_it= + recursion_set.insert(identifier).first; + + // first make sure that this one is already inlined + goto_inline_rec(m_it, full); + + goto_programt tmp2; + tmp2.copy_from(f.body); + + assert(tmp2.instructions.back().is_end_function()); + tmp2.instructions.back().type=LOCATION; + + replace_return(tmp2, lhs, constrain); + + goto_programt tmp; + parameter_assignments(tmp2.instructions.front().location, f.type, arguments, tmp); + tmp.destructive_append(tmp2); + + if(f.type.get_bool("#hide")) + { + const locationt &new_location=function.find_location(); + + if(new_location.is_not_nil()) + { + Forall_goto_program_instructions(it, tmp) + { + replace_location(it->location, new_location); + replace_location(it->guard, new_location); + replace_location(it->code, new_location); + it->function=new_location.get_function(); + } + } + } + + // set up location instruction for function call + target->type=LOCATION; + target->code.clear(); + + goto_programt::targett next_target(target); + next_target++; + + dest.instructions.splice(next_target, tmp.instructions); + target=next_target; + + recursion_set.erase(recursion_it); + } + else // no body available + { + if(no_body_set.insert(identifier).second) + { + err_location(function); + str << "no body for function `" << identifier + << "'"; + warning(); + } + + goto_programt tmp; + + // evaluate function arguments -- they might have + // pointer dereferencing or the like + forall_expr(it, arguments) + { + goto_programt::targett t=tmp.add_instruction(); + t->make_other(); + t->location=target->location; + t->function=target->location.get_function(); + t->code=codet(ID_expression); + t->code.copy_to_operands(*it); + } + + // return value + if(lhs.is_not_nil()) + { + exprt rhs=exprt(ID_sideeffect, lhs.type()); + rhs.set(ID_statement, ID_nondet); + rhs.location()=target->location; + + code_assignt code(lhs, rhs); + code.location()=target->location; + + goto_programt::targett t=tmp.add_instruction(ASSIGN); + t->location=target->location; + t->function=target->location.get_function(); + t->code.swap(code); + } + + // now just kill call + target->type=LOCATION; + target->code.clear(); + target++; + + // insert tmp + dest.instructions.splice(target, tmp.instructions); + } +} + +/*******************************************************************\ + +Function: goto_inlinet::goto_inline + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_inlinet::goto_inline(goto_programt &dest) +{ + goto_inline_rec(dest, true); + replace_return(dest, + static_cast(get_nil_irep()), + static_cast(get_nil_irep())); +} + +/*******************************************************************\ + +Function: goto_inlinet::goto_inline_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_inlinet::goto_inline_rec( + goto_functionst::function_mapt::iterator f_it, + bool full) +{ + // already done? + + if(finished_inlining_set.find(f_it->first)!= + finished_inlining_set.end()) + return; // yes + + // do it + + goto_inline_rec(f_it->second.body, full); + + // remember we did it + + finished_inlining_set.insert(f_it->first); +} + +/*******************************************************************\ + +Function: goto_inlinet::goto_inline_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_inlinet::goto_inline_rec(goto_programt &dest, bool full) +{ + bool changed=false; + + for(goto_programt::instructionst::iterator + it=dest.instructions.begin(); + it!=dest.instructions.end(); + ) // no it++, done by inline_instruction + { + if(inline_instruction(dest, full, it)) + changed=true; + } + + if(changed) + { + remove_skip(dest); + dest.update(); + } +} + +/*******************************************************************\ + +Function: goto_inlinet::inline_instruction + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_inlinet::inline_instruction( + goto_programt &dest, + bool full, + goto_programt::targett &it) +{ + if(it->is_function_call()) + { + const code_function_callt &call=to_code_function_call(it->code); + + if(call.function().id()==ID_symbol) + { + expand_function_call( + dest, it, call.lhs(), call.function(), call.arguments(), + static_cast(get_nil_irep()), full); + + return true; + } + } + else if(it->is_other()) + { + // these are for Boolean programs + if(it->code.get(ID_statement)==ID_bp_constrain && + it->code.operands().size()==2 && + it->code.op0().operands().size()==2 && + it->code.op0().op1().get(ID_statement)==ID_function_call) + { + expand_function_call( + dest, it, + it->code.op0().op0(), // lhs + it->code.op0().op1().op0(), // function + it->code.op0().op1().op1().operands(), // arguments + it->code.op1(), // constraint + full); + + return true; + } + } + + // advance iterator + it++; + + return false; +} + +/*******************************************************************\ + +Function: goto_inline + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_inline( + goto_functionst &goto_functions, + const namespacet &ns, + message_handlert &message_handler) +{ + goto_inlinet goto_inline(goto_functions, ns, message_handler); + + try + { + // find main + goto_functionst::function_mapt::iterator it= + goto_functions.function_map.find(ID_main); + + if(it==goto_functions.function_map.end()) + return; + + goto_inline.goto_inline(it->second.body); + } + + catch(int) + { + goto_inline.error(); + } + + catch(const char *e) + { + goto_inline.error(e); + } + + catch(const std::string &e) + { + goto_inline.error(e); + } + + if(goto_inline.get_error_found()) + throw 0; + + // clean up + for(goto_functionst::function_mapt::iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + if(it->first!=ID_main) + { + it->second.body_available=false; + it->second.body.clear(); + } +} + +/*******************************************************************\ + +Function: goto_partial_inline + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_partial_inline( + goto_functionst &goto_functions, + const namespacet &ns, + message_handlert &message_handler, + unsigned _smallfunc_limit) +{ + goto_inlinet goto_inline( + goto_functions, + ns, + message_handler); + + goto_inline.smallfunc_limit=_smallfunc_limit; + + try + { + for(goto_functionst::function_mapt::iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + if(it->second.body_available) + goto_inline.goto_inline_rec(it, false); + } + + catch(int) + { + goto_inline.error(); + } + + catch(const char *e) + { + goto_inline.error(e); + } + + catch(const std::string &e) + { + goto_inline.error(e); + } + + if(goto_inline.get_error_found()) + throw 0; +} diff --git a/src/goto-programs/goto_inline.h b/src/goto-programs/goto_inline.h new file mode 100644 index 00000000000..0ac08b4af54 --- /dev/null +++ b/src/goto-programs/goto_inline.h @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: Function Inlining + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_INLINE_H +#define CPROVER_GOTO_INLINE_H + +#include + +#include "goto_functions.h" + +// do a full inlining +void goto_inline( + goto_functionst &goto_functions, + const namespacet &ns, + message_handlert &message_handler); + +// inline those functions marked as "inlined" +// and functions with less than _smallfunc_limit instructions +void goto_partial_inline( + goto_functionst &goto_functions, + const namespacet &ns, + message_handlert &message_handler, + unsigned _smallfunc_limit = 0); + +#endif diff --git a/src/goto-programs/goto_inline_class.h b/src/goto-programs/goto_inline_class.h new file mode 100644 index 00000000000..6eb3cf769e6 --- /dev/null +++ b/src/goto-programs/goto_inline_class.h @@ -0,0 +1,82 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_GOTO_INLINE_CLASS +#define CPROVER_GOTO_PROGRAMS_GOTO_INLINE_CLASS + +#include + +#include "goto_functions.h" + +class goto_inlinet:public message_streamt +{ +public: + goto_inlinet( + goto_functionst &_goto_functions, + const namespacet &_ns, + message_handlert &_message_handler): + message_streamt(_message_handler), + smallfunc_limit(0), + goto_functions(_goto_functions), + ns(_ns) + { + } + + void goto_inline(goto_programt &dest); + + void goto_inline_rec( + goto_functionst::function_mapt::iterator, + bool full); + + void goto_inline_rec(goto_programt &dest, bool full); + + // inline single instruction at 'target' + // returns true in case a change was done + // set 'full' to perform this recursively + bool inline_instruction( + goto_programt &dest, + bool full, + goto_programt::targett &target); + + unsigned smallfunc_limit; + +protected: + goto_functionst &goto_functions; + const namespacet &ns; + + void expand_function_call( + goto_programt &dest, + goto_programt::targett &target, + const exprt &lhs, + const exprt &function, + const exprt::operandst &arguments, + const exprt &constrain, + bool recursive); + + void replace_return( + goto_programt &body, + const exprt &lhs, + const exprt &constrain); + + void parameter_assignments( + const locationt &location, + const code_typet &code_type, + const exprt::operandst &arguments, + goto_programt &dest); + + typedef hash_set_cont recursion_sett; + recursion_sett recursion_set; + + typedef hash_set_cont no_body_sett; + no_body_sett no_body_set; + + typedef hash_set_cont finished_inlining_sett; + finished_inlining_sett finished_inlining_set; +}; + +#endif diff --git a/src/goto-programs/goto_main.cpp b/src/goto-programs/goto_main.cpp new file mode 100644 index 00000000000..bf9eda75494 --- /dev/null +++ b/src/goto-programs/goto_main.cpp @@ -0,0 +1,127 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "goto_convert_class.h" + +/*******************************************************************\ + +Function: goto_convertt::new_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::new_name(symbolt &symbol) +{ + // rename it + get_new_name(symbol, ns); + + // store in context + context.add(symbol); +} + +/*******************************************************************\ + +Function: goto_convertt::lookup + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const symbolt &goto_convertt::lookup(const irep_idt &identifier) const +{ + const symbolt *symbol; + if(ns.lookup(identifier, symbol)) + throw "failed to find symbol "+id2string(identifier); + return *symbol; +} + +/*******************************************************************\ + +Function: goto_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convert( + const codet &code, + contextt &context, + const optionst &options, + goto_programt &dest, + message_handlert &message_handler) +{ + goto_convertt goto_convert(context, options, message_handler); + + try + { + goto_convert.goto_convert(code, dest); + } + + catch(int) + { + goto_convert.error(); + } + + catch(const char *e) + { + goto_convert.error(e); + } + + catch(const std::string &e) + { + goto_convert.error(e); + } + + if(goto_convert.get_error_found()) + throw 0; +} + +/*******************************************************************\ + +Function: goto_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convert( + contextt &context, + const optionst &options, + goto_programt &dest, + message_handlert &message_handler) +{ + // find main symbol + const contextt::symbolst::const_iterator s_it= + context.symbols.find("main"); + + if(s_it==context.symbols.end()) + throw "failed to find main symbol"; + + const symbolt &symbol=s_it->second; + + ::goto_convert(to_code(symbol.value), context, options, dest, message_handler); +} diff --git a/src/goto-programs/goto_program.cpp b/src/goto-programs/goto_program.cpp new file mode 100644 index 00000000000..1ae78f77940 --- /dev/null +++ b/src/goto-programs/goto_program.cpp @@ -0,0 +1,432 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "goto_program.h" + +/*******************************************************************\ + +Function: goto_programt::output_instruction + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::ostream& goto_programt::output_instruction( + const class namespacet &ns, + const irep_idt &identifier, + std::ostream& out, + instructionst::const_iterator it) const +{ + out << " // " << it->location_number << " "; + + if(!it->location.is_nil()) + out << it->location.as_string(); + else + out << "no location"; + + out << "\n"; + + if(!it->labels.empty()) + { + out << " // Labels:"; + for(instructiont::labelst::const_iterator + l_it=it->labels.begin(); + l_it!=it->labels.end(); + l_it++) + { + out << " " << *l_it; + } + + out << std::endl; + } + + if(it->is_target()) + out << std::setw(6) << it->target_number << ": "; + else + out << " "; + + switch(it->type) + { + case NO_INSTRUCTION_TYPE: + out << "NO INSTRUCTION TYPE SET" << std::endl; + break; + + case GOTO: + if(!it->guard.is_true()) + { + out << "IF " + << from_expr(ns, identifier, it->guard) + << " THEN "; + } + + out << "GOTO "; + + for(instructiont::targetst::const_iterator + gt_it=it->targets.begin(); + gt_it!=it->targets.end(); + gt_it++) + { + if(gt_it!=it->targets.begin()) out << ", "; + out << (*gt_it)->target_number; + } + + out << std::endl; + break; + + case RETURN: + case OTHER: + case DECL: + case DEAD: + case FUNCTION_CALL: + case ASSIGN: + out << from_expr(ns, identifier, it->code) << std::endl; + break; + + case ASSUME: + case ASSERT: + if(it->is_assume()) + out << "ASSUME "; + else + out << "ASSERT "; + + { + out << from_expr(ns, identifier, it->guard); + + const irep_idt &comment=it->location.get(ID_comment); + if(comment!="") out << " // " << comment; + } + + out << std::endl; + break; + + case SKIP: + out << "SKIP" << std::endl; + break; + + case END_FUNCTION: + out << "END_FUNCTION" << std::endl; + break; + + case LOCATION: + out << "LOCATION" << std::endl; + break; + + case THROW: + out << "THROW" << std::endl; + break; + + case CATCH: + out << "CATCH" << std::endl; + break; + + case ATOMIC_BEGIN: + out << "ATOMIC_BEGIN" << std::endl; + break; + + case ATOMIC_END: + out << "ATOMIC_END" << std::endl; + break; + + case START_THREAD: + out << "START THREAD "; + + if(it->targets.size()==1) + out << it->targets.front()->target_number; + + out << std::endl; + break; + + case END_THREAD: + out << "END THREAD" << std::endl; + break; + + default: + throw "unknown statement"; + } + + return out; +} + +/*******************************************************************\ + +Function: goto_programt::get_decl_identifiers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_programt::get_decl_identifiers( + decl_identifierst &decl_identifiers) const +{ + forall_goto_program_instructions(it, (*this)) + { + if(it->is_decl()) + { + assert(it->code.get_statement()==ID_decl); + assert(it->code.operands().size()==1); + const symbol_exprt &symbol_expr=to_symbol_expr(it->code.op0()); + decl_identifiers.insert(symbol_expr.get_identifier()); + } + } +} + +/*******************************************************************\ + +Function: parse_lhs_read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void parse_lhs_read(const exprt &src, std::list &dest) +{ + if(src.id()==ID_dereference) + { + assert(src.operands().size()==1); + dest.push_back(src.op0()); + } + else if(src.id()==ID_index) + { + assert(src.operands().size()==2); + dest.push_back(src.op1()); + parse_lhs_read(src.op0(), dest); + } + else if(src.id()==ID_member) + { + assert(src.operands().size()==1); + parse_lhs_read(src.op0(), dest); + } + else if(src.id()==ID_if) + { + assert(src.operands().size()==3); + dest.push_back(src.op0()); + parse_lhs_read(src.op1(), dest); + parse_lhs_read(src.op2(), dest); + } +} + +/*******************************************************************\ + +Function: expressions_read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::list expressions_read( + const goto_programt::instructiont &instruction) +{ + std::list dest; + + switch(instruction.type) + { + case ASSUME: + case ASSERT: + case GOTO: + dest.push_back(instruction.guard); + break; + + case RETURN: + if(to_code_return(instruction.code).return_value().is_not_nil()) + dest.push_back(to_code_return(instruction.code).return_value()); + break; + + case FUNCTION_CALL: + { + const code_function_callt &function_call= + to_code_function_call(instruction.code); + forall_expr(it, function_call.arguments()) + dest.push_back(*it); + if(function_call.lhs().is_not_nil()) + parse_lhs_read(function_call.lhs(), dest); + } + break; + + case ASSIGN: + { + const code_assignt &assignment=to_code_assign(instruction.code); + dest.push_back(assignment.rhs()); + parse_lhs_read(assignment.lhs(), dest); + } + break; + + default:; + } + + return dest; +} + +/*******************************************************************\ + +Function: expressions_written + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::list expressions_written( + const goto_programt::instructiont &instruction) +{ + std::list dest; + + switch(instruction.type) + { + case FUNCTION_CALL: + { + const code_function_callt &function_call= + to_code_function_call(instruction.code); + if(function_call.lhs().is_not_nil()) + dest.push_back(function_call.lhs()); + } + break; + + case ASSIGN: + dest.push_back(to_code_assign(instruction.code).lhs()); + break; + + default:; + } + + return dest; +} + +/*******************************************************************\ + +Function: get_objects_read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void objects_read( + const exprt &src, + std::list &dest) +{ + if(src.id()==ID_symbol) + dest.push_back(src); + else if(src.id()==ID_address_of) + { + // don't traverse! + } + else if(src.id()==ID_dereference) + { + // this reads what is pointed to plus the pointer + assert(src.operands().size()==1); + dest.push_back(src); + objects_read(src.op0(), dest); + } + else + { + forall_operands(it, src) + objects_read(*it, dest); + } +} + +/*******************************************************************\ + +Function: objects_read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::list objects_read( + const goto_programt::instructiont &instruction) +{ + std::list expressions=expressions_read(instruction); + + std::list dest; + + forall_expr_list(it, expressions) + objects_read(*it, dest); + + return dest; +} + +/*******************************************************************\ + +Function: objects_written + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void objects_written( + const exprt &src, + std::list &dest) +{ + if(src.id()==ID_if) + { + assert(src.operands().size()==3); + objects_written(src.op1(), dest); + objects_written(src.op2(), dest); + } + else + dest.push_back(src); +} + +/*******************************************************************\ + +Function: objects_written + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::list objects_written( + const goto_programt::instructiont &instruction) +{ + std::list expressions=expressions_written(instruction); + + std::list dest; + + forall_expr_list(it, expressions) + objects_written(*it, dest); + + return dest; +} diff --git a/src/goto-programs/goto_program.h b/src/goto-programs/goto_program.h new file mode 100644 index 00000000000..3cc1efefae9 --- /dev/null +++ b/src/goto-programs/goto_program.h @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: Concrete Goto Program + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAM_H +#define CPROVER_GOTO_PROGRAM_H + +#include + +#include + +#include "goto_program_template.h" + +class goto_programt:public goto_program_templatet +{ +public: + std::ostream& output_instruction( + const class namespacet &ns, + const irep_idt &identifier, + std::ostream& out, + instructionst::const_iterator it) const; + + goto_programt() { } + + // get the variables in decl statements + typedef std::set decl_identifierst; + void get_decl_identifiers(decl_identifierst &decl_identifiers) const; +}; + +#define forall_goto_program_instructions(it, program) \ + for(goto_programt::instructionst::const_iterator it=(program).instructions.begin(); \ + it!=(program).instructions.end(); it++) + +#define Forall_goto_program_instructions(it, program) \ + for(goto_programt::instructionst::iterator it=(program).instructions.begin(); \ + it!=(program).instructions.end(); it++) + +extern inline bool operator<(const goto_programt::const_targett i1, + const goto_programt::const_targett i2) +{ + return order_const_target(i1, i2); +} + +std::list objects_read(const goto_programt::instructiont &); +std::list objects_written(const goto_programt::instructiont &); + +std::list expressions_read(const goto_programt::instructiont &); +std::list expressions_written(const goto_programt::instructiont &); + +#endif diff --git a/src/goto-programs/goto_program_irep.cpp b/src/goto-programs/goto_program_irep.cpp new file mode 100644 index 00000000000..043b5733dee --- /dev/null +++ b/src/goto-programs/goto_program_irep.cpp @@ -0,0 +1,203 @@ +/*******************************************************************\ + +Module: goto_programt -> irep conversion + +Author: CM Wintersteiger + +Date: May 2007 + +\*******************************************************************/ + +#include + +#include "goto_program_irep.h" + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert(const goto_programt::instructiont &instruction, irept &irep) +{ + irep.set(ID_code, instruction.code); + + if(instruction.function!="") + irep.set(ID_function, instruction.function); + + if(instruction.location.is_not_nil()) + irep.set(ID_location, instruction.location); + + irep.set(ID_type, (long) instruction.type); + + irep.set(ID_guard, instruction.guard); + + if(!instruction.targets.empty()) + { + irept &tgts=irep.add(ID_targets); + for(goto_programt::targetst::const_iterator it= + instruction.targets.begin(); + it!=instruction.targets.end(); + it++) + { + irept t(i2string((*it)->location_number)); + tgts.move_to_sub(t); + } + } + + if(!instruction.labels.empty()) + { + irept &lbls = irep.add(ID_labels); + irept::subt &subs = lbls.get_sub(); + subs.reserve(instruction.labels.size()); + for(goto_programt::instructiont::labelst::const_iterator it= + instruction.labels.begin(); + it!=instruction.labels.end(); + it++) + { + subs.push_back(irept(*it)); + } + } +} + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert(const irept &irep, goto_programt::instructiont &instruction) +{ + instruction.code=static_cast(irep.find(ID_code)); + instruction.function = irep.find(ID_function).id(); + instruction.location = static_cast(irep.find(ID_location)); + instruction.type = static_cast( + atoi(irep.find(ID_type).id_string().c_str())); + instruction.guard = static_cast(irep.find(ID_guard)); + + // don't touch the targets, the goto_programt conversion does that + + const irept &lbls=irep.find(ID_labels); + const irept::subt &lsubs=lbls.get_sub(); + for (irept::subt::const_iterator it=lsubs.begin(); + it!=lsubs.end(); + it++) + { + instruction.labels.push_back(it->id()); + } +} + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert( const goto_programt &program, irept &irep ) +{ + irep.id("goto-program"); + irep.get_sub().reserve(program.instructions.size()); + for (goto_programt::instructionst::const_iterator it= + program.instructions.begin(); + it!=program.instructions.end(); + it++) + { + irep.get_sub().push_back(irept()); + convert(*it, irep.get_sub().back()); + } +} + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert( const irept &irep, goto_programt &program ) +{ + assert(irep.id()=="goto-program"); + + program.instructions.clear(); + + std::list< std::list > number_targets_list; + + // convert instructions back + const irept::subt &subs = irep.get_sub(); + for (irept::subt::const_iterator it=subs.begin(); + it!=subs.end(); + it++) + { + program.instructions.push_back(goto_programt::instructiont()); + convert(*it, program.instructions.back()); + + number_targets_list.push_back(std::list()); + const irept &targets=it->find(ID_targets); + const irept::subt &tsubs=targets.get_sub(); + for (irept::subt::const_iterator tit=tsubs.begin(); + tit!=tsubs.end(); + tit++) + { + number_targets_list.back().push_back( + atoi(tit->id_string().c_str())); + } + } + + program.compute_location_numbers(); + + // resolve targets + std::list< std::list >::iterator nit= + number_targets_list.begin(); + for(goto_programt::instructionst::iterator lit= + program.instructions.begin(); + lit!=program.instructions.end() && nit!=number_targets_list.end(); + lit++, nit++) + { + for (std::list::iterator tit=nit->begin(); + tit!=nit->end(); + tit++) + { + goto_programt::targett fit=program.instructions.begin(); + for(;fit!=program.instructions.end();fit++) + { + if (fit->location_number==*tit) + { + lit->targets.push_back(fit); + break; + } + } + + if (fit==program.instructions.end()) + { + std::cout << "Warning: could not resolve target link " << + "during irep->goto_program translation." << std::endl; + throw 0; + } + } + } + + program.update(); +} diff --git a/src/goto-programs/goto_program_irep.h b/src/goto-programs/goto_program_irep.h new file mode 100644 index 00000000000..8a98741df07 --- /dev/null +++ b/src/goto-programs/goto_program_irep.h @@ -0,0 +1,22 @@ +/*******************************************************************\ + +Module: goto_programt -> irep conversion + +Author: CM Wintersteiger + +Date: May 2007 + +\*******************************************************************/ + +#ifndef GOTO_PROGRAM_IREP_H_ +#define GOTO_PROGRAM_IREP_H_ + +#include + +void convert(const goto_programt::instructiont &instruction, irept &irep); +void convert(const irept &irep, goto_programt::instructiont &instruction); + +void convert(const goto_programt &program, irept &irep); +void convert(const irept &irep, goto_programt &program); + +#endif /*GOTO_PROGRAM_IREP_H_*/ diff --git a/src/goto-programs/goto_program_serialization.cpp b/src/goto-programs/goto_program_serialization.cpp new file mode 100644 index 00000000000..5ea3aa84240 --- /dev/null +++ b/src/goto-programs/goto_program_serialization.cpp @@ -0,0 +1,60 @@ +/*******************************************************************\ + +Module: Convert goto programs to binary format and back (with irep + hashing) + +Author: CM Wintersteiger + +Date: May 2007 + +\*******************************************************************/ + +#include +#include + +#include "goto_program_irep.h" +#include "goto_program_serialization.h" + +/*******************************************************************\ + +Function: goto_program_serializationt::convert + + Inputs: goto program and an xml structure to fill + + Outputs: none + + Purpose: constructs the xml structure according to the goto program + and the namespace into the given xml object. + +\*******************************************************************/ + +void goto_program_serializationt::convert( + const goto_programt &goto_program, + std::ostream &out) +{ + irepcache.push_back(irept()); + ::convert(goto_program, irepcache.back()); + irepconverter.reference_convert(irepcache.back(), out); +} + +/*******************************************************************\ + +Function: goto_program_serializationt::convert + + Inputs: an xml structure and a goto program to fill + + Outputs: none + + Purpose: constructs the goto program according to the xml structure + and the namespace into the given goto program object. + +\*******************************************************************/ + +void goto_program_serializationt::convert( + std::istream &in, + irept& gprep) +{ + irepconverter.reference_convert(in, gprep); + // reference is not resolved here! +} + diff --git a/src/goto-programs/goto_program_serialization.h b/src/goto-programs/goto_program_serialization.h new file mode 100644 index 00000000000..7be038bb01e --- /dev/null +++ b/src/goto-programs/goto_program_serialization.h @@ -0,0 +1,34 @@ +/*******************************************************************\ + +Module: Convert goto programs to binary format and back (with + irep hashing) + +Author: CM Wintersteiger + +Date: July 2006 + +\*******************************************************************/ + +#ifndef GOTO_PROGRAM_SERIALIZATION_H_ +#define GOTO_PROGRAM_SERIALIZATION_H_ + +#include +#include + +class goto_program_serializationt { + private: + irep_serializationt irepconverter; + std::list irepcache; + + public: + goto_program_serializationt(irep_serializationt::ireps_containert &ic) : + irepconverter(ic) { }; + + void convert(const goto_programt&, std::ostream &); + void convert(std::istream&, irept&); + + goto_programt::targett + find_instruction( goto_programt&, unsigned ); +}; + +#endif /*GOTO_PROGRAM_SERIALIZATION_H_*/ diff --git a/src/goto-programs/goto_program_template.cpp b/src/goto-programs/goto_program_template.cpp new file mode 100644 index 00000000000..8a6489ae594 --- /dev/null +++ b/src/goto-programs/goto_program_template.cpp @@ -0,0 +1,49 @@ +/*******************************************************************\ + +Module: Goto Program Template + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "goto_program_template.h" + +/*******************************************************************\ + +Function: operator<< + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::ostream &operator<<(std::ostream &out, goto_program_instruction_typet t) +{ + switch(t) + { + case NO_INSTRUCTION_TYPE: out << "NO_INSTRUCTION_TYPE"; break; + case GOTO: out << "GOTO"; break; + case ASSUME: out << "ASSUME"; break; + case ASSERT: out << "ASSERT"; break; + case OTHER: out << "OTHER"; break; + case DECL: out << "DECL"; break; + case DEAD: out << "DEAD"; break; + case SKIP: out << "SKIP"; break; + case START_THREAD: out << "START_THREAD"; break; + case END_THREAD: out << "END_THREAD"; break; + case LOCATION: out << "LOCATION"; break; + case END_FUNCTION: out << "END_FUNCTION"; break; + case ATOMIC_BEGIN: out << "ATOMIC_BEGIN"; break; + case ATOMIC_END: out << "ATOMIC_END"; break; + case RETURN: out << "RETURN"; break; + case ASSIGN: out << "ASSIGN"; break; + case FUNCTION_CALL: out << "FUNCTION_CALL"; break; + default: + out << "?"; + } + + return out; +} diff --git a/src/goto-programs/goto_program_template.h b/src/goto-programs/goto_program_template.h new file mode 100644 index 00000000000..c95b81d5969 --- /dev/null +++ b/src/goto-programs/goto_program_template.h @@ -0,0 +1,725 @@ +/*******************************************************************\ + +Module: Goto Program Template + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAM_TEMPLATE_H +#define CPROVER_GOTO_PROGRAM_TEMPLATE_H + +#include + +#include +#include + +#include + +typedef enum { NO_INSTRUCTION_TYPE=0, + GOTO=1, // branch, possibly guarded + ASSUME=2, // non-failing guarded self loop + ASSERT=3, // assertions + OTHER=4, // anything else + SKIP=5, // just advance the PC + START_THREAD=6, // spawns an asynchronous thread + END_THREAD=7, // end the current thread + LOCATION=8, // semantically like SKIP + END_FUNCTION=9, // exit point of a function + ATOMIC_BEGIN=10, // marks a block without interleavings + ATOMIC_END=11, // end of a block without interleavings + RETURN=12, // return from a function + ASSIGN=13, // assignment lhs:=rhs + DECL=14, // declare a local variable + DEAD=15, // marks the end-of-live of a local variable + FUNCTION_CALL=16,// call a function + THROW=17, // throw an exception + CATCH=18, // catch an exception + } + goto_program_instruction_typet; + +std::ostream &operator<<(std::ostream &, goto_program_instruction_typet); + +template +class goto_program_templatet +{ +public: + // DO NOT COPY ME! I HAVE POINTERS IN ME! + goto_program_templatet(const goto_program_templatet &src) + { + assert(src.instructions.empty()); + } + + // DO NOT COPY ME! I HAVE POINTERS IN ME! + goto_program_templatet &operator=(const goto_program_templatet &src) + { + assert(src.instructions.empty()); + instructions.clear(); + update(); + return *this; + } + + class instructiont + { + public: + codeT code; + + // function this belongs to + irep_idt function; + + // keep track of the location in the source file + locationt location; + + // what kind of instruction? + goto_program_instruction_typet type; + + // for gotos, assume, assert + guardT guard; + + // for gotos + typedef typename std::list::iterator targett; + typedef typename std::list::const_iterator const_targett; + typedef std::list targetst; + typedef std::list const_targetst; + + targetst targets; + + typedef std::list labelst; + labelst labels; + + std::set incoming_edges; + + bool is_target() const + { return target_number!=unsigned(-1); } + + void make_goto() + { + type=GOTO; + targets.clear(); + guard.make_true(); + code.make_nil(); + } + + void make_return() + { + type=RETURN; + targets.clear(); + guard.make_true(); + code.make_nil(); + } + + void make_function_call(const codeT &_code) + { + type=FUNCTION_CALL; + targets.clear(); + guard.make_true(); + code=_code; + } + + void make_skip() + { + type=SKIP; + targets.clear(); + guard.make_true(); + code.make_nil(); + } + + void make_throw() + { + type=THROW; + targets.clear(); + guard.make_true(); + code.make_nil(); + } + + void make_catch() + { + type=CATCH; + targets.clear(); + guard.make_true(); + code.make_nil(); + } + + void make_assertion(const guardT &g) + { + type=ASSERT; + targets.clear(); + guard=g; + code.make_nil(); + } + + void make_assumption(const guardT &g) + { + type=ASSUME; + targets.clear(); + guard=g; + code.make_nil(); + } + + void make_assignment() + { + type=ASSIGN; + targets.clear(); + guard.make_nil(); + code.make_nil(); + } + + void make_goto(typename std::list::iterator _target) + { + make_goto(); + targets.push_back(_target); + } + + void make_goto(typename std::list::iterator _target, const guardT &g) + { + make_goto(_target); + guard=g; + } + + void make_other() + { + type=OTHER; + targets.clear(); + guard.make_true(); + code.make_nil(); + } + + void make_decl() + { + type=DECL; + targets.clear(); + guard.make_true(); + code.make_nil(); + } + + void make_dead() + { + type=DEAD; + targets.clear(); + guard.make_true(); + code.make_nil(); + } + + inline bool is_goto () const { return type==GOTO; } + inline bool is_return () const { return type==RETURN; } + inline bool is_assign () const { return type==ASSIGN; } + inline bool is_function_call() const { return type==FUNCTION_CALL; } + inline bool is_throw () const { return type==THROW; } + inline bool is_catch () const { return type==CATCH; } + inline bool is_skip () const { return type==SKIP; } + inline bool is_location () const { return type==LOCATION; } + inline bool is_other () const { return type==OTHER; } + inline bool is_decl () const { return type==DECL; } + inline bool is_dead () const { return type==DEAD; } + inline bool is_assume () const { return type==ASSUME; } + inline bool is_assert () const { return type==ASSERT; } + inline bool is_atomic_begin () const { return type==ATOMIC_BEGIN; } + inline bool is_atomic_end () const { return type==ATOMIC_END; } + inline bool is_start_thread () const { return type==START_THREAD; } + inline bool is_end_thread () const { return type==END_THREAD; } + inline bool is_end_function () const { return type==END_FUNCTION; } + + instructiont(): + location(static_cast(get_nil_irep())), + type(NO_INSTRUCTION_TYPE), + location_number(0), + target_number(unsigned(-1)) + { + guard.make_true(); + } + + instructiont(goto_program_instruction_typet _type): + location(static_cast(get_nil_irep())), + type(_type), + location_number(0), + target_number(unsigned(-1)) + { + guard.make_true(); + } + + void swap(instructiont &instruction) + { + instruction.code.swap(code); + instruction.location.swap(location); + std::swap(instruction.type, type); + instruction.guard.swap(guard); + instruction.targets.swap(targets); + instruction.function.swap(function); + } + + // a globally unique number to identify a program location + // it's guaranteed to be ordered in program order within + // one goto_program + unsigned location_number; + + // number unique per function to identify loops + unsigned loop_number; + + // a number to identify branch targets + // this is -1 if it's not a target + unsigned target_number; + + bool is_backwards_goto() const + { + if(!is_goto()) return false; + + for(typename targetst::const_iterator + it=targets.begin(); + it!=targets.end(); + it++) + if((*it)->location_number<=location_number) + return true; + + return false; + } + }; + + typedef std::list instructionst; + + typedef typename instructionst::iterator targett; + typedef typename instructionst::const_iterator const_targett; + typedef typename std::list targetst; + typedef typename std::list const_targetst; + + instructionst instructions; + + void get_successors( + targett target, + targetst &successors) + { + successors.clear(); + if(target==instructions.end()) return; + + targett next=target; + next++; + + const instructiont &i=*target; + + if(i.is_goto()) + { + for(typename targetst::const_iterator + t_it=i.targets.begin(); + t_it!=i.targets.end(); + t_it++) + successors.push_back(*t_it); + + if(!i.guard.is_true()) + successors.push_back(next); + } + else if(i.is_start_thread()) + { + for(typename targetst::const_iterator + t_it=i.targets.begin(); + t_it!=i.targets.end(); + t_it++) + successors.push_back(*t_it); + + successors.push_back(next); + } + else if(i.is_end_thread()) + { + // no successors + } + else if(i.is_return()) + { + // the successor is the end_function at the end + successors.push_back(--instructions.end()); + } + else if(i.is_assume()) + { + if(!i.guard.is_false()) + successors.push_back(next); + } + else + successors.push_back(next); + } + + void get_successors( + const_targett target, + const_targetst &successors) const + { + successors.clear(); + if(target==instructions.end()) return; + + const_targett next=target; + next++; + + const instructiont &i=*target; + + if(i.is_goto()) + { + for(typename targetst::const_iterator + t_it=i.targets.begin(); + t_it!=i.targets.end(); + t_it++) + successors.push_back(*t_it); + + if(!i.guard.is_true()) + successors.push_back(next); + } + else if(i.is_start_thread()) + { + for(typename targetst::const_iterator + t_it=i.targets.begin(); + t_it!=i.targets.end(); + t_it++) + successors.push_back(*t_it); + + successors.push_back(next); + } + else if(i.is_end_thread()) + { + // no successors + } + else if(i.is_return()) + { + // the successor is the end_function at the end + successors.push_back(--instructions.end()); + } + else if(i.is_assume()) + { + if(!i.guard.is_false()) + successors.push_back(next); + } + else + successors.push_back(next); + } + + void compute_incoming_edges(); + + // insertion that preserves jumps to "target" + void insert_before_swap(targett target, instructiont &instruction) + { + targett next=target; + next++; + instructions.insert(next, instructiont())->swap(*target); + target->swap(instruction); + } + + // insertion that preserves jumps to "target" + void insert_before_swap(targett target, goto_program_templatet &p) + { + if(p.instructions.empty()) return; + insert_before_swap(target, p.instructions.front()); + targett next=target; + next++; + p.instructions.erase(p.instructions.begin()); + instructions.splice(next, p.instructions); + } + + // this does not preserve jumps to target -- these move + targett insert_before(targett target) + { + return instructions.insert(target, instructiont()); + } + + targett insert_after(targett target) + { + targett t=target; + t++; + return instructions.insert(t, instructiont()); + } + + inline void destructive_append(goto_program_templatet &p) + { + instructions.splice(instructions.end(), + p.instructions); + } + + inline void destructive_insert( + targett target, + goto_program_templatet &p) + { + instructions.splice(target, + p.instructions); + } + + targett add_instruction() + { + instructions.push_back(instructiont()); + return --instructions.end(); + } + + targett add_instruction(goto_program_instruction_typet type) + { + instructions.push_back(instructiont(type)); + return --instructions.end(); + } + + // these assume that the targets are computed and numbered + std::ostream &output( + const class namespacet &ns, + const irep_idt &identifier, + std::ostream &out) const; + + std::ostream& output(std::ostream &out) const + { + return output(namespacet(contextt()), "", out); + } + + virtual std::ostream& output_instruction( + const class namespacet &ns, + const irep_idt &identifier, + std::ostream &out, + typename instructionst::const_iterator it) const=0; + + // compute the target numbers + void compute_target_numbers(); + + // compute location numbers + void compute_location_numbers(unsigned &nr) + { + for(typename instructionst::iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + it->location_number=nr++; + } + + // compute location numbers + void compute_location_numbers() + { + unsigned nr=0; + compute_location_numbers(nr); + } + + // compute loop numbers + void compute_loop_numbers() + { + unsigned nr=0; + for(typename instructionst::iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + if(it->is_backwards_goto()) + it->loop_number=nr++; + } + + void update(); + + // empty program? + bool empty() const + { + return instructions.empty(); + } + + // constructor/destructor + goto_program_templatet() + { + } + + virtual ~goto_program_templatet() + { + } + + void swap(goto_program_templatet &program) + { + program.instructions.swap(instructions); + } + + void clear() + { + instructions.clear(); + } + + void copy_from(const goto_program_templatet &src); + + bool has_assertion() const; +}; + +#include +#include + +template +void goto_program_templatet::update() +{ + compute_incoming_edges(); + compute_target_numbers(); + compute_location_numbers(); +} + +template +std::ostream& goto_program_templatet::output( + const namespacet &ns, + const irep_idt &identifier, + std::ostream& out) const +{ + // output program + + for(typename instructionst::const_iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + output_instruction(ns, identifier, out, it); + + return out; +} + +template +void goto_program_templatet::compute_target_numbers() +{ + // reset marking + + for(typename instructionst::iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + it->target_number=-1; + + // mark the goto targets + + for(typename instructionst::const_iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + { + for(typename instructiont::targetst::const_iterator + t_it=it->targets.begin(); + t_it!=it->targets.end(); + t_it++) + { + targett t=*t_it; + if(t!=instructions.end()) + t->target_number=0; + } + } + + // number the targets properly + unsigned cnt=0; + + for(typename instructionst::iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + { + if(it->is_target()) + it->target_number=++cnt; + } + + // check the targets! + // (this is a consistency check only) + + for(typename instructionst::const_iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + { + for(typename instructiont::targetst::const_iterator + t_it=it->targets.begin(); + t_it!=it->targets.end(); + t_it++) + { + targett t=*t_it; + if(t!=instructions.end()) + assert(t->target_number!=0); + } + } + +} + +template +void goto_program_templatet::copy_from( + const goto_program_templatet &src) +{ + // Definitions for mapping between the two programs + typedef std::map targets_mappingt; + targets_mappingt targets_mapping; + + clear(); + + // Loop over program - 1st time collects targets and copy + + for(typename instructionst::const_iterator + it=src.instructions.begin(); + it!=src.instructions.end(); + it++) + { + targett new_instruction=add_instruction(); + targets_mapping[it]=new_instruction; + *new_instruction=*it; + } + + // Loop over program - 2nd time updates targets + + for(typename instructionst::iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + { + for(typename instructiont::targetst::iterator + t_it=it->targets.begin(); + t_it!=it->targets.end(); + t_it++) + { + typename targets_mappingt::iterator + m_target_it=targets_mapping.find(*t_it); + + if(m_target_it==targets_mapping.end()) + throw "copy_from: target not found"; + + *t_it=m_target_it->second; + } + } + + compute_incoming_edges(); + compute_target_numbers(); +} + +// number them +template +bool goto_program_templatet::has_assertion() const +{ + for(typename instructionst::const_iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + if(it->is_assert() && !it->guard.is_true()) + return true; + + return false; +} + +template +void goto_program_templatet::compute_incoming_edges() +{ + for(typename instructionst::iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + { + it->incoming_edges.clear(); + } + + for(typename instructionst::iterator + it=instructions.begin(); + it!=instructions.end(); + it++) + { + targetst successors; + + get_successors(it, successors); + + for(typename targetst::const_iterator + s_it=successors.begin(); + s_it!=successors.end(); + s_it++) + { + targett t=*s_it; + + if(t!=instructions.end()) + t->incoming_edges.insert(it); + } + } +} + +template +inline bool order_const_target( + const typename goto_program_templatet::const_targett i1, + const typename goto_program_templatet::const_targett i2) +{ + const typename goto_program_templatet::instructiont &_i1=*i1; + const typename goto_program_templatet::instructiont &_i2=*i2; + return &_i1<&_i2; +} + +#endif diff --git a/src/goto-programs/goto_rw.cpp b/src/goto-programs/goto_rw.cpp new file mode 100644 index 00000000000..df4e267d322 --- /dev/null +++ b/src/goto-programs/goto_rw.cpp @@ -0,0 +1,198 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening + +Date: April 2010 + +\*******************************************************************/ + +#include + +#include "goto_rw.h" + +/*******************************************************************\ + +Function: get_objects_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typedef enum { LHS_W, READ } get_modet; + +void get_objects_rec( + get_modet mode, + const exprt &expr, + std::list &read, + std::list &write) +{ + if(expr.id()==ID_symbol) + { + if(mode==LHS_W) + write.push_back(expr); + else + read.push_back(expr); + } + else if(expr.id()==ID_index) + { + const index_exprt &index_expr=to_index_expr(expr); + + if(mode==READ) + get_objects_rec(READ, index_expr.index(), read, write); + + get_objects_rec(mode, index_expr.array(), read, write); + } + else if(expr.id()==ID_if) + { + const if_exprt &if_expr=to_if_expr(expr); + + if(mode==READ) + get_objects_rec(READ, if_expr.cond(), read, write); + + get_objects_rec(mode, if_expr.true_case(), read, write); + get_objects_rec(mode, if_expr.false_case(), read, write); + } + else if(expr.id()==ID_member) + { + const member_exprt &member_expr=to_member_expr(expr); + + get_objects_rec(mode, member_expr.struct_op(), read, write); + } + else if(expr.id()==ID_dereference) + { + const dereference_exprt &dereference_expr= + to_dereference_expr(expr); + + if(mode==READ) + get_objects_rec(READ, dereference_expr.pointer(), read, write); + + if(mode==LHS_W) + write.push_back(expr); + else + read.push_back(expr); + } + else + { + if(mode==READ) + forall_operands(it, expr) + get_objects_rec(READ, *it, read, write); + } +} + +/*******************************************************************\ + +Function: goto_rw + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_rw(const code_assignt &assign, + std::list &read, std::list &write) +{ + get_objects_rec(LHS_W, assign.lhs(), read, write); + get_objects_rec(READ, assign.rhs(), read, write); +} + +/*******************************************************************\ + +Function: goto_rw + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_rw(const code_function_callt &function_call, + std::list &read, std::list &write) +{ + if(function_call.lhs().is_not_nil()) + get_objects_rec(LHS_W, function_call.lhs(), read, write); + + get_objects_rec(READ, function_call.function(), read, write); + + forall_expr(it, function_call.arguments()) + get_objects_rec(READ, *it, read, write); +} + +/*******************************************************************\ + +Function: goto_rw + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_rw(const goto_programt::instructiont &instruction, + std::list &read, + std::list &write) +{ + switch(instruction.type) + { + case NO_INSTRUCTION_TYPE: + assert(false); + break; + + case GOTO: + case ASSUME: + case ASSERT: + get_objects_rec(READ, instruction.guard, read, write); + break; + + case RETURN: + { + const code_returnt &code_return=to_code_return(instruction.code); + if(code_return.has_return_value()) + get_objects_rec(READ, code_return.return_value(), read, write); + } + break; + + case OTHER: + // don't know + break; + + case DEAD: + case SKIP: + case START_THREAD: + case END_THREAD: + case LOCATION: + case END_FUNCTION: + case ATOMIC_BEGIN: + case ATOMIC_END: + case THROW: + case CATCH: + // these don't read or write anything + break; + + case ASSIGN: + goto_rw(to_code_assign(instruction.code), read, write); + break; + + case DECL: + get_objects_rec(LHS_W, to_code_decl(instruction.code).symbol(), + read, write); + break; + + case FUNCTION_CALL: + goto_rw(to_code_function_call(instruction.code), read, write); + break; + } +} + diff --git a/src/goto-programs/goto_rw.h b/src/goto-programs/goto_rw.h new file mode 100644 index 00000000000..77108cd56a4 --- /dev/null +++ b/src/goto-programs/goto_rw.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening + +Date: April 2010 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_GOTO_RW_H +#define CPROVER_GOTO_PROGRAMS_GOTO_RW_H + +#include "goto_program.h" + +void goto_rw(const goto_programt::instructiont &, + std::list &read, std::list &write); + +#endif diff --git a/src/goto-programs/goto_sideeffects.cpp b/src/goto-programs/goto_sideeffects.cpp new file mode 100644 index 00000000000..309c345d57c --- /dev/null +++ b/src/goto-programs/goto_sideeffects.cpp @@ -0,0 +1,741 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "goto_convert_class.h" + +/*******************************************************************\ + +Function: goto_convertt::has_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_convertt::has_function_call(const exprt &expr) +{ + forall_operands(it, expr) + if(has_function_call(*it)) + return true; + + if(expr.id()==ID_sideeffect && + expr.get(ID_statement)==ID_function_call) + return true; + + return false; +} + +/*******************************************************************\ + +Function: goto_convertt::remove_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_assignment( + side_effect_exprt &expr, + goto_programt &dest) +{ + const irep_idt statement=expr.get_statement(); + + if(statement==ID_assign) + { + exprt tmp=expr; + tmp.id(ID_code); + // just interpret as code + convert(to_code(tmp), dest); + } + else if(statement==ID_assign_plus || + statement==ID_assign_minus || + statement==ID_assign_mult || + statement==ID_assign_div || + statement==ID_assign_mod || + statement==ID_assign_shl || + statement==ID_assign_ashr || + statement==ID_assign_lshr || + statement==ID_assign_bitand || + statement==ID_assign_bitxor || + statement==ID_assign_bitor) + { + if(expr.operands().size()!=2) + { + err_location(expr); + str << statement << " takes two arguments"; + throw 0; + } + + exprt rhs; + + if(statement==ID_assign_plus) + rhs.id(ID_plus); + else if(statement==ID_assign_minus) + rhs.id(ID_minus); + else if(statement==ID_assign_mult) + rhs.id(ID_mult); + else if(statement==ID_assign_div) + rhs.id(ID_div); + else if(statement==ID_assign_mod) + rhs.id(ID_mod); + else if(statement==ID_assign_shl) + rhs.id(ID_shl); + else if(statement==ID_assign_ashr) + rhs.id(ID_ashr); + else if(statement==ID_assign_lshr) + rhs.id(ID_lshr); + else if(statement==ID_assign_bitand) + rhs.id(ID_bitand); + else if(statement==ID_assign_bitxor) + rhs.id(ID_bitxor); + else if(statement==ID_assign_bitor) + rhs.id(ID_bitor); + else + { + err_location(expr); + str << "assignment `" << statement << "' not yet supproted"; + throw 0; + } + + rhs.copy_to_operands(expr.op0(), expr.op1()); + rhs.type()=expr.op0().type(); + + if(rhs.op0().type().id()==ID_bool) + { + rhs.op0().make_typecast(int_type()); + rhs.op1().make_typecast(int_type()); + rhs.type()=int_type(); + rhs.make_typecast(typet(ID_bool)); + } + + exprt lhs=expr.op0(); + + code_assignt assignment(lhs, rhs); + assignment.location()=expr.location(); + + convert(assignment, dest); + } + else + assert(false); + + // revert assignment in the expression to its LHS + exprt lhs; + lhs.swap(expr.op0()); + expr.swap(lhs); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_pre + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_pre( + side_effect_exprt &expr, + goto_programt &dest) +{ + if(expr.operands().size()!=1) + throw "preincrement/predecrement must have one operand"; + + const irep_idt statement=expr.get_statement(); + + assert(statement==ID_preincrement || + statement==ID_predecrement); + + exprt rhs; + + if(statement==ID_preincrement) + rhs.id(ID_plus); + else + rhs.id(ID_minus); + + const typet &op_type=ns.follow(expr.op0().type()); + + if(op_type.id()==ID_bool) + { + rhs.copy_to_operands(expr.op0(), gen_one(int_type())); + rhs.op0().make_typecast(int_type()); + rhs.type()=int_type(); + rhs.make_typecast(typet(ID_bool)); + } + else if(op_type.id()==ID_c_enum || + op_type.id()==ID_incomplete_c_enum) + { + rhs.copy_to_operands(expr.op0(), gen_one(int_type())); + rhs.op0().make_typecast(int_type()); + rhs.type()=int_type(); + rhs.make_typecast(op_type); + } + else + { + typet constant_type; + + if(op_type.id()==ID_pointer) + constant_type=index_type(); + else if(is_number(op_type)) + constant_type=op_type; + else + { + err_location(expr); + throw "no constant one of type "+op_type.to_string(); + } + + exprt constant=gen_one(constant_type); + + rhs.copy_to_operands(expr.op0()); + rhs.move_to_operands(constant); + rhs.type()=expr.op0().type(); + } + + code_assignt assignment(expr.op0(), rhs); + assignment.location()=expr.find_location(); + + convert(assignment, dest); + + // revert to argument of pre-inc/pre-dec + exprt op=expr.op0(); + expr.swap(op); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_post + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_post( + side_effect_exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + goto_programt tmp1, tmp2; + + // we have ...(op++)... + + if(expr.operands().size()!=1) + throw "postincrement/postdecrement must have one operand"; + + const irep_idt statement=expr.get_statement(); + + assert(statement==ID_postincrement || + statement==ID_postdecrement); + + exprt rhs; + + if(statement==ID_postincrement) + rhs.id(ID_plus); + else + rhs.id(ID_minus); + + const typet &op_type=ns.follow(expr.op0().type()); + + if(op_type.id()==ID_bool) + { + rhs.copy_to_operands(expr.op0(), gen_one(int_type())); + rhs.op0().make_typecast(int_type()); + rhs.type()=int_type(); + rhs.make_typecast(typet(ID_bool)); + } + else if(op_type.id()==ID_c_enum || + op_type.id()==ID_incomplete_c_enum) + { + rhs.copy_to_operands(expr.op0(), gen_one(int_type())); + rhs.op0().make_typecast(int_type()); + rhs.type()=int_type(); + rhs.make_typecast(op_type); + } + else + { + typet constant_type; + + if(op_type.id()==ID_pointer) + constant_type=index_type(); + else if(is_number(op_type)) + constant_type=op_type; + else + { + err_location(expr); + throw "no constant one of type "+op_type.to_string(); + } + + exprt constant=gen_one(constant_type); + + rhs.copy_to_operands(expr.op0()); + rhs.move_to_operands(constant); + rhs.type()=expr.op0().type(); + } + + code_assignt assignment(expr.op0(), rhs); + assignment.location()=expr.find_location(); + + convert(assignment, tmp2); + + // fix up the expression, if needed + + if(result_is_used) + { + exprt tmp=expr.op0(); + make_temp_symbol(tmp, "post", dest); + expr.swap(tmp); + } + else + expr.make_nil(); + + dest.destructive_append(tmp1); + dest.destructive_append(tmp2); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_function_call( + side_effect_exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + if(!result_is_used) + { + assert(expr.operands().size()==2); + code_function_callt call; + call.function()=expr.op0(); + call.arguments()=expr.op1().operands(); + call.location()=expr.location(); + call.lhs().make_nil(); + convert_function_call(call, dest); + expr.make_nil(); + return; + } + + symbolt new_symbol; + + new_symbol.base_name="return_value"; + new_symbol.lvalue=true; + new_symbol.is_statevar=true; + new_symbol.thread_local=true; + new_symbol.type=expr.type(); + new_symbol.location=expr.find_location(); + + // get name of function, if available + + if(expr.id()!=ID_sideeffect || + expr.get(ID_statement)!=ID_function_call) + throw "expected function call"; + + if(expr.operands().empty()) + throw "function_call expects at least one operand"; + + if(expr.op0().id()==ID_symbol) + { + const irep_idt &identifier=expr.op0().get(ID_identifier); + const symbolt &symbol=lookup(identifier); + + std::string new_base_name=id2string(new_symbol.base_name); + + new_base_name+="_"; + new_base_name+=id2string(symbol.base_name); + new_base_name+="$"+i2string(++temporary_counter); + + new_symbol.base_name=new_base_name; + new_symbol.mode=symbol.mode; + } + + new_symbol.name=tmp_symbol_prefix+id2string(new_symbol.base_name); + + new_name(new_symbol); + + tmp_symbols.push_back(new_symbol.name); + + { + code_declt decl; + decl.symbol()=symbol_expr(new_symbol); + decl.location()=new_symbol.location; + convert_decl(decl, dest); + } + + { + goto_programt tmp_program2; + code_function_callt call; + call.lhs()=symbol_expr(new_symbol); + call.function()=expr.op0(); + call.arguments()=expr.op1().operands(); + call.location()=new_symbol.location; + convert_function_call(call, dest); + } + + static_cast(expr)=symbol_expr(new_symbol); +} + +/*******************************************************************\ + +Function: goto_convertt::replace_new_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::replace_new_object( + const exprt &object, + exprt &dest) +{ + if(dest.id()=="new_object") + dest=object; + else + Forall_operands(it, dest) + replace_new_object(object, *it); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_cpp_new + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_cpp_new( + side_effect_exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + codet call; + + symbolt new_symbol; + + new_symbol.base_name="new_ptr$"+i2string(++temporary_counter); + new_symbol.lvalue=true; + new_symbol.type=expr.type(); + new_symbol.name=tmp_symbol_prefix+id2string(new_symbol.base_name); + + new_name(new_symbol); + tmp_symbols.push_back(new_symbol.name); + + call=code_assignt(symbol_expr(new_symbol), expr); + + if(result_is_used) + static_cast(expr)=symbol_expr(new_symbol); + else + expr.make_nil(); + + convert(call, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_cpp_delete + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_cpp_delete( + side_effect_exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + assert(expr.operands().size()==1); + + codet tmp; + + tmp.set_statement(ID_cpp_delete); + tmp.location()=expr.location(); + tmp.copy_to_operands(expr.op0()); + tmp.set(ID_destructor, expr.find(ID_destructor)); + + convert_cpp_delete(tmp, dest); + + expr.make_nil(); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_malloc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_malloc( + side_effect_exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + codet call; + + if(result_is_used) + { + symbolt new_symbol; + + new_symbol.base_name="new_value$"+i2string(++temporary_counter); + new_symbol.lvalue=true; + new_symbol.type=expr.type(); + new_symbol.name=tmp_symbol_prefix+id2string(new_symbol.base_name); + + new_name(new_symbol); + tmp_symbols.push_back(new_symbol.name); + + call=code_assignt(symbol_expr(new_symbol), expr); + + static_cast(expr)=symbol_expr(new_symbol); + } + else + { + call=codet(ID_expression); + call.move_to_operands(expr); + } + + convert(call, dest); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_temporary_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_temporary_object( + side_effect_exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + if(expr.operands().size()!=1 && + expr.operands().size()!=0) + throw "temporary_object takes 0 or 1 operands"; + + symbolt &new_symbol= + new_tmp_symbol(expr.type(), "obj", dest, expr.find_location()); + + new_symbol.mode=expr.get(ID_mode); + + if(expr.operands().size()==1) + { + codet assignment(ID_assign); + assignment.reserve_operands(2); + assignment.copy_to_operands(symbol_expr(new_symbol)); + assignment.move_to_operands(expr.op0()); + + convert(assignment, dest); + } + + if(expr.find(ID_initializer).is_not_nil()) + { + assert(expr.operands().empty()); + exprt initializer=static_cast(expr.find(ID_initializer)); + replace_new_object(symbol_expr(new_symbol), initializer); + + convert(to_code(initializer), dest); + } + + static_cast(expr)=symbol_expr(new_symbol); +} + +/*******************************************************************\ + +Function: goto_convertt::remove_statement_expression + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_statement_expression( + side_effect_exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + if(expr.operands().size()!=1) + throw "statement_expression takes 1 operand"; + + if(expr.op0().id()!=ID_code) + throw "statement_expression takes code as operand"; + + codet &code=to_code(expr.op0()); + + if(!result_is_used) + { + convert(code, dest); + return; + } + + // get last statement from block + codet *last=&code; + + while(true) + { + if(last->get_statement()==ID_block) + { + if(last->operands().empty()) + throw "statement_expression expects non-empty block"; + + last=&to_code(last->operands().back()); + } + else if(last->get_statement()==ID_label) + { + assert(last->operands().size()==1); + last=&to_code(last->op0()); + } + else + break; + } + + codet old_last=*last; + if(last->get(ID_statement)==ID_expression) + last->set_statement(ID_skip); + + convert(code, dest); + + { + clean_expr(old_last, dest, true); + + if((old_last.get(ID_statement)==ID_expression && + old_last.operands().size()==1) || (old_last.get(ID_statement)==ID_assign)) + static_cast(expr)=old_last.op0(); + else + { + std::stringstream str; + str << "statement_expression expects expression as " + "last statement, but got `"+ + id2string(old_last.get(ID_statement))+"'"; + if(dest.instructions.size() > 0) + str << " - error may be at: " << dest.instructions.back().location; + throw str.str(); + } + } +} + +/*******************************************************************\ + +Function: goto_convertt::remove_side_effect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convertt::remove_side_effect( + side_effect_exprt &expr, + goto_programt &dest, + bool result_is_used) +{ + const irep_idt &statement=expr.get_statement(); + + if(statement==ID_function_call) + remove_function_call(expr, dest, result_is_used); + else if(statement==ID_assign || + statement==ID_assign_plus || + statement==ID_assign_minus || + statement==ID_assign_mult || + statement==ID_assign_div || + statement==ID_assign_bitor || + statement==ID_assign_bitxor || + statement==ID_assign_bitand || + statement==ID_assign_lshr || + statement==ID_assign_ashr || + statement==ID_assign_shl || + statement==ID_assign_mod) + remove_assignment(expr, dest); + else if(statement==ID_postincrement || + statement==ID_postdecrement) + remove_post(expr, dest, result_is_used); + else if(statement==ID_preincrement || + statement==ID_predecrement) + remove_pre(expr, dest); + else if(statement==ID_cpp_new || + statement=="cpp_new[]") + remove_cpp_new(expr, dest, result_is_used); + else if(statement==ID_cpp_delete || + statement=="cpp_delete[]") + remove_cpp_delete(expr, dest, result_is_used); + else if(statement==ID_malloc) + remove_malloc(expr, dest, result_is_used); + else if(statement==ID_temporary_object) + remove_temporary_object(expr, dest, result_is_used); + else if(statement==ID_statement_expression) + remove_statement_expression(expr, dest, result_is_used); + else if(statement==ID_nondet) + { + // these are fine + } + else if(statement==ID_throw) + { + // the result can't be used, these are void + goto_programt::targett t=dest.add_instruction(THROW); + t->code=codet(ID_throw); + t->code.operands().swap(expr.operands()); + t->code.location()=expr.location(); + t->location=expr.location(); + } + else + { + str << "cannot remove side effect (" << statement << ")"; + throw 0; + } +} + diff --git a/src/goto-programs/goto_threads.cpp b/src/goto-programs/goto_threads.cpp new file mode 100644 index 00000000000..5962012cf4b --- /dev/null +++ b/src/goto-programs/goto_threads.cpp @@ -0,0 +1,204 @@ +/*******************************************************************\ + +Module: GOTO Threads + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "goto_convert_functions.h" +#include "goto_threads.h" +#include "remove_skip.h" +#include "goto_inline.h" +#include "goto_inline_class.h" + +/*******************************************************************\ + +Function: goto_threadst::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_threadst::output( + const namespacet &ns, + std::ostream &out) +{ + unsigned thread_nr=0; + + forall_goto_thread_list(it, thread_list) + { + out << "******** THREAD: " << thread_nr << std::endl; + it->goto_program.output(ns, "", out); + thread_nr++; + out << std::endl; + } +} + +/*******************************************************************\ + + Class: goto_thread_convertt + + Purpose: + +\*******************************************************************/ + +class goto_thread_convertt:public goto_inlinet +{ +public: + goto_thread_convertt( + goto_threadst &_threads, + goto_functionst &_goto_functions, + const namespacet &_ns, + message_handlert &_message_handler): + goto_inlinet(_goto_functions, _ns, _message_handler), + threads(_threads) + { + } + + void goto_convert(goto_programt &dest); + +protected: + goto_threadst &threads; +}; + +/*******************************************************************\ + +Function: goto_thread_convertt::goto_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_thread_convertt::goto_convert(goto_programt &dest) +{ + for(goto_programt::instructionst::iterator + it=dest.instructions.begin(); + it!=dest.instructions.end(); + ) // no it++ + { + if(it->is_other() || it->is_function_call()) + { + inline_instruction(dest, true, it); + } + else if(it->is_start_thread()) + { + #if 0 + std::string event= + "start_thread_"+i2string(threads.thread_list.size()); + + threads.thread_list.push_back(goto_threadt()); + goto_threadt &thread=threads.thread_list.back(); + + assert(it->targets.size()==1); + goto_programt::targett new_it=it->targets.front(); + + goto_programt::targett next_it=it; + next_it++; + + it->targets.clear(); + it->type=SYNC; + it->event=event; + + goto_programt tmp; + goto_programt::targett sync=tmp.add_instruction(SYNC); + sync->event=event; + sync->location=it->location; + thread.goto_program.destructive_append(tmp); + + thread.goto_program.instructions.splice( + thread.goto_program.instructions.end(), + dest.instructions, next_it, new_it); + + it=new_it; + + // do this recursively on the new thread + goto_convert(thread.goto_program); + #endif + } + else + it++; + } + + remove_skip(dest); + dest.update(); +} + +/*******************************************************************\ + +Function: goto_convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_convert( + contextt &context, + const optionst &options, + goto_threadst &goto_threads, + message_handlert &message_handler) +{ + goto_functionst goto_functions; + + goto_convert( + context, + options, + goto_functions, + message_handler); + + goto_threads.thread_list.push_back(goto_threadt()); + goto_threadt &thread=goto_threads.thread_list.back(); + + { + // find main + goto_functionst::function_mapt::iterator it= + goto_functions.function_map.find(ID_main); + + if(it==goto_functions.function_map.end()) + return; + + thread.goto_program.swap(it->second.body); + } + + const namespacet ns(context); + goto_thread_convertt goto_thread_convert(goto_threads, goto_functions, ns, message_handler); + + try + { + goto_thread_convert.goto_convert(thread.goto_program); + } + + catch(int) + { + goto_thread_convert.error(); + } + + catch(const char *e) + { + goto_thread_convert.error(e); + } + + catch(const std::string &e) + { + goto_thread_convert.error(e); + } + + if(goto_thread_convert.get_error_found()) + throw 0; +} diff --git a/src/goto-programs/goto_threads.h b/src/goto-programs/goto_threads.h new file mode 100644 index 00000000000..4cb1f637df0 --- /dev/null +++ b/src/goto-programs/goto_threads.h @@ -0,0 +1,73 @@ +/*******************************************************************\ + +Module: Threaded Goto Programs + +Author: Daniel Kroening + +Date: June 2003 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_THREADS_H +#define CPROVER_GOTO_THREADS_H + +#include + +#include +#include +#include + +#include "goto_program.h" + +class goto_threadt +{ +public: + goto_programt goto_program; + + void swap(goto_threadt &thread) + { + thread.goto_program.swap(goto_program); + } +}; + +// use list, the targets need to be stable +class goto_threadst +{ +public: + typedef std::list thread_listt; + thread_listt thread_list; + + void clear() + { + thread_list.clear(); + } + + void output( + const namespacet &ns, + std::ostream &out); +}; + +#define forall_goto_threads(it, threads) \ + for(goto_threadst::thread_listt::const_iterator it=(threads).thread_list.begin(); \ + it!=(threads).thread_list.end(); it++) + +#define Forall_goto_threads(it, threads) \ + for(goto_threadst::thread_listt::iterator it=(threads).thread_list.begin(); \ + it!=(threads).thread_list.end(); it++) + +#define forall_goto_thread_list(it, thread_list) \ + for(goto_threadst::thread_listt::const_iterator it=(thread_list).begin(); \ + it!=(thread_list).end(); it++) + +#define Forall_goto_thread_list(it, thread_list) \ + for(goto_threadst::thread_listt::iterator it=(threads_list).begin(); \ + it!=(thread_list).end(); it++) + +// convert everyting starting from "main" +void goto_convert( + contextt &context, + const optionst &options, + goto_threadst &goto_threads, + message_handlert &message_handler); + +#endif diff --git a/src/goto-programs/induction_variable.h b/src/goto-programs/induction_variable.h new file mode 100644 index 00000000000..1fb83c04675 --- /dev/null +++ b/src/goto-programs/induction_variable.h @@ -0,0 +1,27 @@ +/* + * induction_variable.h + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + +#ifndef INDUCTION_VARIABLE_H_ +#define INDUCTION_VARIABLE_H_ + +class induction_variablet /* Represents simple induction variables */ +{ + induction_variablet(symbolt induction_variable, exprt lower_bound, exprt upper_bound, exprt step) : + induction_variable(induction_variable), lower_bound(lower_bound), upper_bound(upper_bound), step(step) + { + + } + + +private: + symbolt induction_variable; + exprt lower_bound; + exprt upper_bound; + exprt step; +}; + +#endif diff --git a/src/goto-programs/interpreter.cpp b/src/goto-programs/interpreter.cpp new file mode 100644 index 00000000000..ed6a06a8ec3 --- /dev/null +++ b/src/goto-programs/interpreter.cpp @@ -0,0 +1,660 @@ +/*******************************************************************\ + +Module: Interpreter for GOTO Programs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include "interpreter.h" +#include "interpreter_class.h" + +/*******************************************************************\ + +Function: interpretert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::operator()() +{ + build_memory_map(); + + const goto_functionst::function_mapt::const_iterator + main_it=goto_functions.function_map.find("main"); + + if(main_it==goto_functions.function_map.end()) + throw "main not found"; + + const goto_functionst::goto_functiont &goto_function=main_it->second; + + if(!goto_function.body_available) + throw "main has no body"; + + PC=goto_function.body.instructions.begin(); + function=main_it; + + done=false; + + while(!done) + { + show_state(); + command(); + if(!done) step(); + } +} + +/*******************************************************************\ + +Function: interpretert::show_state + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::show_state() +{ + std::cout << std::endl; + std::cout << "----------------------------------------------------" + << std::endl; + + if(PC==function->second.body.instructions.end()) + { + std::cout << "End of function `" + << function->first << "'" << std::endl; + } + else + function->second.body.output_instruction(ns, function->first, std::cout, PC); + + std::cout << std::endl; +} + +/*******************************************************************\ + +Function: interpretert::command + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::command() +{ + #define BUFSIZE 100 + char command[BUFSIZE]; + if(fgets(command, BUFSIZE-1, stdin)==NULL) + { + done=true; + return; + } + + char ch=tolower(command[0]); + + if(ch=='q') + done=true; +} + +/*******************************************************************\ + +Function: interpretert::step + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::step() +{ + if(PC==function->second.body.instructions.end()) + { + if(call_stack.empty()) + done=true; + else + { + PC=call_stack.top().return_PC; + function=call_stack.top().return_function; + stack_pointer=call_stack.top().old_stack_pointer; + call_stack.pop(); + } + + return; + } + + next_PC=PC; + next_PC++; + + switch(PC->type) + { + case GOTO: + execute_goto(); + break; + + case ASSUME: + execute_assume(); + break; + + case ASSERT: + execute_assert(); + break; + + case OTHER: + execute_other(); + break; + + case DECL: + execute_decl(); + break; + + case SKIP: + case LOCATION: + case END_FUNCTION: + break; + + case RETURN: + if(call_stack.empty()) + throw "RETURN without call"; + + if(PC->code.operands().size()==1 && + call_stack.top().return_value_address!=0) + { + std::vector rhs; + evaluate(PC->code.op0(), rhs); + assign(call_stack.top().return_value_address, rhs); + } + + next_PC=function->second.body.instructions.end(); + break; + + case ASSIGN: + execute_assign(); + break; + + case FUNCTION_CALL: + execute_function_call(); + break; + + case START_THREAD: + throw "START_THREAD not yet implemented"; + + case END_THREAD: + throw "END_THREAD not yet implemented"; + break; + + case ATOMIC_BEGIN: + throw "ATOMIC_BEGIN not yet implemented"; + + case ATOMIC_END: + throw "ATOMIC_END not yet implemented"; + + case DEAD: + throw "DEAD not yet implemented"; + + default: + throw "encountered instruction with undefined instruction type"; + } + + PC=next_PC; +} + +/*******************************************************************\ + +Function: interpretert::execute_goto + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::execute_goto() +{ + if(evaluate_boolean(PC->guard)) + { + if(PC->targets.size()==0) + throw "taken goto without target"; + + if(PC->targets.size()>=2) + throw "non-deterministic goto encountered"; + + next_PC=PC->targets.front(); + } +} + +/*******************************************************************\ + +Function: interpretert::execute_other + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::execute_other() +{ + const irep_idt &statement=PC->code.get_statement(); + + if(statement=="expression") + { + assert(PC->code.operands().size()==1); + std::vector rhs; + evaluate(PC->code.op0(), rhs); + } + else + throw "unexpected OTHER statement: "+id2string(statement); +} + +/*******************************************************************\ + +Function: interpretert::execute_decl + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::execute_decl() +{ + assert(PC->code.get_statement()=="decl"); +} + +/*******************************************************************\ + +Function: interpretert::execute_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::execute_assign() +{ + const code_assignt &code_assign= + to_code_assign(PC->code); + + std::vector rhs; + evaluate(code_assign.rhs(), rhs); + + if(rhs.size()!=0) + { + mp_integer address=evaluate_address(code_assign.lhs()); + unsigned size=get_size(code_assign.lhs().type()); + + if(size!=rhs.size()) + std::cout << "!! failed to obtain rhs (" + << rhs.size() << " vs. " + << size << ")" << std::endl; + else + assign(address, rhs); + } +} + +/*******************************************************************\ + +Function: interpretert::assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::assign( + mp_integer address, + const std::vector &rhs) +{ + for(unsigned i=0; iguard)) + throw "assertion failed"; +} + +/*******************************************************************\ + +Function: interpretert::execute_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::execute_function_call() +{ + const code_function_callt &function_call= + to_code_function_call(PC->code); + + // function to be called + mp_integer a=evaluate_address(function_call.function()); + + if(a==0) + throw "function call to NULL"; + else if(a>=memory.size()) + throw "out-of-range function call"; + + const memory_cellt &cell=memory[integer2long(a)]; + const irep_idt &identifier=cell.identifier; + + const goto_functionst::function_mapt::const_iterator f_it= + goto_functions.function_map.find(identifier); + + if(f_it==goto_functions.function_map.end()) + throw "failed to find function "+id2string(identifier); + + // return value + mp_integer return_value_address; + + if(function_call.lhs().is_not_nil()) + return_value_address= + evaluate_address(function_call.lhs()); + else + return_value_address=0; + + // values of the arguments + std::vector > argument_values; + + argument_values.resize(function_call.arguments().size()); + + for(unsigned i=0; isecond.body_available) + { + call_stack.push(stack_framet()); + stack_framet &frame=call_stack.top(); + + frame.return_PC=next_PC; + frame.return_function=function; + frame.old_stack_pointer=stack_pointer; + frame.return_value_address=return_value_address; + + // local variables + std::set locals; + get_local_identifiers(f_it->second, locals); + + for(std::set::const_iterator + it=locals.begin(); + it!=locals.end(); + it++) + { + const irep_idt &id=*it; + const symbolt &symbol=ns.lookup(id); + unsigned size=get_size(symbol.type); + + if(size!=0) + { + frame.local_map[id]=stack_pointer; + + for(unsigned i=0; i=memory.size()) memory.resize(address+1); + memory[address].value=0; + memory[address].identifier=id; + memory[address].offset=i; + } + + stack_pointer+=size; + } + } + + // assign the arguments + const code_typet::argumentst &arguments= + to_code_type(f_it->second.type).arguments(); + + if(argument_values.size()second.body.instructions.begin(); + } + else + throw "no body for "+id2string(identifier); +} + +/*******************************************************************\ + +Function: interpretert::build_memory_map + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::build_memory_map() +{ + // put in a dummy for NULL + memory.resize(1); + memory[0].offset=0; + memory[0].identifier="NULL-OBJECT"; + + // now do regular static symbols + for(contextt::symbolst::const_iterator + it=context.symbols.begin(); + it!=context.symbols.end(); + it++) + build_memory_map(it->second); + + // for the locals + stack_pointer=memory.size(); +} + +/*******************************************************************\ + +Function: interpretert::build_memory_map + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::build_memory_map(const symbolt &symbol) +{ + unsigned size=0; + + if(symbol.type.id()=="code") + { + size=1; + } + else if(symbol.static_lifetime) + { + size=get_size(symbol.type); + } + + if(size!=0) + { + unsigned address=memory.size(); + memory.resize(address+size); + memory_map[symbol.name]=address; + + for(unsigned i=0; i(it->find("type")); + + if(sub_type.id()!="code") + sum+=get_size(sub_type); + } + + return sum; + } + else if(type.id()=="union") + { + const irept::subt &components= + type.find("components").get_sub(); + + unsigned max_size=0; + + forall_irep(it, components) + { + const typet &sub_type=static_cast(it->find("type")); + + if(sub_type.id()!="code") + max_size=std::max(max_size, get_size(sub_type)); + } + + return max_size; + } + else if(type.id()=="array") + { + const exprt &size_expr=static_cast(type.find("size")); + + unsigned subtype_size=get_size(type.subtype()); + + mp_integer i; + if(!to_integer(size_expr, i)) + return subtype_size*integer2long(i); + else + return subtype_size; + } + else if(type.id()=="symbol") + { + return get_size(ns.follow(type)); + } + else + return 1; +} + +/*******************************************************************\ + +Function: interpreter + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpreter( + const contextt &context, + const goto_functionst &goto_functions) +{ + interpretert interpreter(context, goto_functions); + interpreter(); +} diff --git a/src/goto-programs/interpreter.h b/src/goto-programs/interpreter.h new file mode 100644 index 00000000000..2df93f373a7 --- /dev/null +++ b/src/goto-programs/interpreter.h @@ -0,0 +1,18 @@ +/*******************************************************************\ + +Module: Interpreter for GOTO Programs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_INTERPRETER_H +#define CPROVER_INTERPRETER_H + +#include "goto_functions.h" + +void interpreter( + const contextt &context, + const goto_functionst &goto_functions); + +#endif diff --git a/src/goto-programs/interpreter_class.h b/src/goto-programs/interpreter_class.h new file mode 100644 index 00000000000..e6fdd62d383 --- /dev/null +++ b/src/goto-programs/interpreter_class.h @@ -0,0 +1,107 @@ +#include + +#include + +#include "goto_functions.h" + +/*******************************************************************\ + + Class: interpretert + + Purpose: interpreter for GOTO programs + +\*******************************************************************/ + +class interpretert +{ +public: + interpretert( + const contextt &_context, + const goto_functionst &_goto_functions): + context(_context), + ns(_context), + goto_functions(_goto_functions) + { + } + + void operator()(); + + friend class simplify_evaluatet; + +protected: + const contextt &context; + const namespacet ns; + const goto_functionst &goto_functions; + + typedef hash_map_cont memory_mapt; + memory_mapt memory_map; + + class memory_cellt + { + public: + irep_idt identifier; + unsigned offset; + mp_integer value; + }; + + typedef std::vector memoryt; + memoryt memory; + + unsigned stack_pointer; + + void build_memory_map(); + void build_memory_map(const symbolt &symbol); + unsigned get_size(const typet &type) const; + void step(); + + void execute_assert(); + void execute_assume(); + void execute_assign(); + void execute_goto(); + void execute_function_call(); + void execute_other(); + void execute_decl(); + + void assign( + mp_integer address, + const std::vector &rhs); + + void read( + mp_integer address, + std::vector &dest) const; + + void command(); + + class stack_framet + { + public: + goto_programt::const_targett return_PC; + goto_functionst::function_mapt::const_iterator return_function; + mp_integer return_value_address; + memory_mapt local_map; + unsigned old_stack_pointer; + }; + + typedef std::stack call_stackt; + call_stackt call_stack; + + goto_functionst::function_mapt::const_iterator function; + goto_programt::const_targett PC, next_PC; + bool done; + + bool evaluate_boolean(const exprt &expr) const + { + std::vector v; + evaluate(expr, v); + if(v.size()!=1) throw "invalid boolean value"; + return v.front()!=0; + } + + void evaluate( + const exprt &expr, + std::vector &dest) const; + + mp_integer evaluate_address(const exprt &expr) const; + + void show_state(); +}; diff --git a/src/goto-programs/interpreter_evaluate.cpp b/src/goto-programs/interpreter_evaluate.cpp new file mode 100644 index 00000000000..a958b05510c --- /dev/null +++ b/src/goto-programs/interpreter_evaluate.cpp @@ -0,0 +1,515 @@ +/*******************************************************************\ + +Module: Interpreter for GOTO Programs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include + +#include "interpreter_class.h" + +/*******************************************************************\ + +Function: interpretert::evaluate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void interpretert::read( + mp_integer address, + std::vector &dest) const +{ + // copy memory region + for(unsigned i=0; i &dest) const +{ + if(expr.id()=="constant") + { + if(expr.type().id()=="struct") + { + } + else if(expr.type().id()=="floatbv") + { + ieee_floatt f; + f.from_expr(expr); + dest.push_back(f.pack()); + return; + } + else if(expr.type().id()=="fixedbv") + { + fixedbvt f; + f.from_expr(expr); + dest.push_back(f.get_value()); + return; + } + else if(expr.type().id()=="bool") + { + dest.push_back(expr.is_true()); + return; + } + else + { + mp_integer i; + if(!to_integer(expr, i)) + { + dest.push_back(i); + return; + } + } + } + else if(expr.id()=="struct") + { + dest.reserve(get_size(expr.type())); + bool error=false; + + forall_operands(it, expr) + { + if(it->type().id()=="code") continue; + + unsigned sub_size=get_size(it->type()); + if(sub_size==0) continue; + + std::vector tmp; + evaluate(*it, tmp); + + if(tmp.size()==sub_size) + { + for(unsigned i=0; i=" || + expr.id()=="<" || + expr.id()==">") + { + if(expr.operands().size()!=2) + throw id2string(expr.id())+" expects two operands"; + + std::vector tmp0, tmp1; + evaluate(expr.op0(), tmp0); + evaluate(expr.op1(), tmp1); + + if(tmp0.size()==1 && tmp1.size()==1) + { + const mp_integer &op0=tmp0.front(); + const mp_integer &op1=tmp1.front(); + + if(expr.id()=="=") + dest.push_back(op0==op1); + else if(expr.id()=="notequal") + dest.push_back(op0!=op1); + else if(expr.id()=="<=") + dest.push_back(op0<=op1); + else if(expr.id()==">=") + dest.push_back(op0>=op1); + else if(expr.id()=="<") + dest.push_back(op0") + dest.push_back(op0>op1); + } + + return; + } + else if(expr.id()=="or") + { + if(expr.operands().size()<1) + throw id2string(expr.id())+" expects at least one operand"; + + bool result=false; + + forall_operands(it, expr) + { + std::vector tmp; + evaluate(*it, tmp); + + if(tmp.size()==1 && tmp.front()!=0) + { + result=true; + break; + } + } + + dest.push_back(result); + + return; + } + else if(expr.id()=="if") + { + if(expr.operands().size()!=3) + throw "if expects three operands"; + + std::vector tmp0, tmp1, tmp2; + evaluate(expr.op0(), tmp0); + evaluate(expr.op1(), tmp1); + evaluate(expr.op2(), tmp2); + + if(tmp0.size()==1 && tmp1.size()==1 && tmp2.size()==1) + { + const mp_integer &op0=tmp0.front(); + const mp_integer &op1=tmp1.front(); + const mp_integer &op2=tmp2.front(); + + dest.push_back(op0!=0?op1:op2); + } + + return; + } + else if(expr.id()=="and") + { + if(expr.operands().size()<1) + throw id2string(expr.id())+" expects at least one operand"; + + bool result=true; + + forall_operands(it, expr) + { + std::vector tmp; + evaluate(*it, tmp); + + if(tmp.size()==1 && tmp.front()==0) + { + result=false; + break; + } + } + + dest.push_back(result); + + return; + } + else if(expr.id()=="not") + { + if(expr.operands().size()!=1) + throw id2string(expr.id())+" expects one operand"; + + std::vector tmp; + evaluate(expr.op0(), tmp); + + if(tmp.size()==1) + dest.push_back(tmp.front()==0); + + return; + } + else if(expr.id()=="+") + { + mp_integer result=0; + + forall_operands(it, expr) + { + std::vector tmp; + evaluate(*it, tmp); + if(tmp.size()==1) + result+=tmp.front(); + } + + dest.push_back(result); + return; + } + else if(expr.id()=="*") + { + // type-dependent! + mp_integer result; + + if(expr.type().id()=="fixedbv") + { + fixedbvt f; + f.spec=to_fixedbv_type(expr.type()); + f.from_integer(1); + result=f.get_value(); + } + else if(expr.type().id()=="floatbv") + { + ieee_floatt f; + f.spec=to_floatbv_type(expr.type()); + f.from_integer(1); + result=f.pack(); + } + else + result=1; + + forall_operands(it, expr) + { + std::vector tmp; + evaluate(*it, tmp); + if(tmp.size()==1) + { + if(expr.type().id()=="fixedbv") + { + fixedbvt f1, f2; + f1.spec=to_fixedbv_type(expr.type()); + f2.spec=to_fixedbv_type(it->type()); + f1.set_value(result); + f2.set_value(tmp.front()); + f1*=f2; + result=f1.get_value(); + } + else if(expr.type().id()=="floatbv") + { + ieee_floatt f1, f2; + f1.spec=to_floatbv_type(expr.type()); + f2.spec=to_floatbv_type(it->type()); + f1.unpack(result); + f2.unpack(tmp.front()); + f1*=f2; + result=f2.pack(); + } + else + result*=tmp.front(); + } + } + + dest.push_back(result); + return; + } + else if(expr.id()=="-") + { + if(expr.operands().size()!=2) + throw "- expects two operands"; + + std::vector tmp0, tmp1; + evaluate(expr.op0(), tmp0); + evaluate(expr.op1(), tmp1); + + if(tmp0.size()==1 && tmp1.size()==1) + dest.push_back(tmp0.front()-tmp1.front()); + return; + } + else if(expr.id()=="/") + { + if(expr.operands().size()!=2) + throw "/ expects two operands"; + + std::vector tmp0, tmp1; + evaluate(expr.op0(), tmp0); + evaluate(expr.op1(), tmp1); + + if(tmp0.size()==1 && tmp1.size()==1) + dest.push_back(tmp0.front()/tmp1.front()); + return; + } + else if(expr.id()=="unary-") + { + if(expr.operands().size()!=1) + throw "unary- expects one operand"; + + std::vector tmp0; + evaluate(expr.op0(), tmp0); + + if(tmp0.size()==1) + dest.push_back(-tmp0.front()); + return; + } + else if(expr.id()=="address_of") + { + if(expr.operands().size()!=1) + throw "address_of expects one operand"; + + dest.push_back(evaluate_address(expr.op0())); + return; + } + else if(expr.id()=="dereference" || + expr.id()=="index" || + expr.id()=="symbol" || + expr.id()=="member") + { + mp_integer a=evaluate_address(expr); + dest.resize(get_size(expr.type())); + read(a, dest); + return; + } + else if(expr.id()=="typecast") + { + if(expr.operands().size()!=1) + throw "typecast expects one operand"; + + std::vector tmp; + evaluate(expr.op0(), tmp); + + if(tmp.size()==1) + { + const mp_integer &value=tmp.front(); + + if(expr.type().id()=="pointer") + { + dest.push_back(value); + return; + } + else if(expr.type().id()=="signedbv") + { + const std::string s=integer2binary(value, bv_width(expr.type())); + dest.push_back(binary2integer(s, true)); + return; + } + else if(expr.type().id()=="unsigedbv") + { + const std::string s=integer2binary(value, bv_width(expr.type())); + dest.push_back(binary2integer(s, false)); + return; + } + else if(expr.type().id()=="bool") + { + dest.push_back(value!=0); + return; + } + } + } + else if(expr.id()=="ashr") + { + if(expr.operands().size()!=2) + throw "ashr expects two operands"; + + std::vector tmp0, tmp1; + evaluate(expr.op0(), tmp0); + evaluate(expr.op1(), tmp1); + + if(tmp0.size()==1 && tmp1.size()==1) + dest.push_back(tmp0.front()/power(2, tmp1.front())); + + return; + } + + std::cout << "!! failed to evaluate expression: " + << from_expr(ns, function->first, expr) + << std::endl; +} + +/*******************************************************************\ + +Function: interpretert::evaluate_address + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer interpretert::evaluate_address(const exprt &expr) const +{ + if(expr.id()=="symbol") + { + const irep_idt &identifier=expr.get("identifier"); + + interpretert::memory_mapt::const_iterator m_it1= + memory_map.find(identifier); + + if(m_it1!=memory_map.end()) + return m_it1->second; + + if(!call_stack.empty()) + { + interpretert::memory_mapt::const_iterator m_it2= + call_stack.top().local_map.find(identifier); + + if(m_it2!=call_stack.top().local_map.end()) + return m_it2->second; + } + } + else if(expr.id()=="dereference") + { + if(expr.operands().size()!=1) + throw "dereference expects one operand"; + + std::vector tmp0; + evaluate(expr.op0(), tmp0); + + if(tmp0.size()==1) + return tmp0.front(); + } + else if(expr.id()=="index") + { + if(expr.operands().size()!=2) + throw "index expects two operands"; + + std::vector tmp1; + evaluate(expr.op1(), tmp1); + + if(tmp1.size()==1) + return evaluate_address(expr.op0())+tmp1.front(); + } + else if(expr.id()=="member") + { + if(expr.operands().size()!=1) + throw "member expects one operand"; + + const struct_typet &struct_type= + to_struct_type(ns.follow(expr.op0().type())); + + const irep_idt &component_name= + to_member_expr(expr).get_component_name(); + + unsigned offset=0; + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it->get_name()==component_name) + break; + + offset+=get_size(it->type()); + } + + return evaluate_address(expr.op0())+offset; + } + + std::cout << "!! failed to evaluate address: " + << from_expr(ns, function->first, expr) + << std::endl; + + return 0; +} diff --git a/src/goto-programs/invariant_propagation.cpp b/src/goto-programs/invariant_propagation.cpp new file mode 100644 index 00000000000..59f7a3fef11 --- /dev/null +++ b/src/goto-programs/invariant_propagation.cpp @@ -0,0 +1,429 @@ +/*******************************************************************\ + +Module: Invariant Propagation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#include "invariant_propagation.h" + +/*******************************************************************\ + +Function: invariant_propagationt::make_all_true + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::make_all_true() +{ + for(state_mapt::iterator it=state_map.begin(); + it!=state_map.end(); + it++) + it->second.invariant_set.make_true(); +} + +/*******************************************************************\ + +Function: invariant_propagationt::make_all_false + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::make_all_false() +{ + for(state_mapt::iterator it=state_map.begin(); + it!=state_map.end(); + it++) + it->second.invariant_set.make_false(); +} + +/*******************************************************************\ + +Function: invariant_propagationt::add_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::add_objects( + const goto_programt &goto_program) +{ + // get the globals + object_listt globals; + get_globals(globals); + + // get the locals + goto_programt::decl_identifierst locals; + goto_program.get_decl_identifiers(locals); + + // cache the list for the locals to speed things up + typedef hash_map_cont object_cachet; + object_cachet object_cache; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + #if 0 + invariant_sett &is=(*this)[i_it].invariant_set; + + is.add_objects(globals); + #endif + + for(goto_programt::decl_identifierst::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + // cache hit? + object_cachet::const_iterator e_it=object_cache.find(*l_it); + + if(e_it==object_cache.end()) + { + const symbolt &symbol=ns.lookup(*l_it); + + object_listt &objects=object_cache[*l_it]; + get_objects(symbol, objects); + #if 0 + is.add_objects(objects); + #endif + } + #if 0 + else + is.add_objects(e_it->second); + #endif + } + } +} + +/*******************************************************************\ + +Function: invariant_propagationt::get_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::get_objects( + const symbolt &symbol, + object_listt &dest) +{ + std::list object_list; + + get_objects_rec(symbol_expr(symbol), object_list); + + for(std::list::const_iterator + it=object_list.begin(); + it!=object_list.end(); + it++) + dest.push_back(object_store.add(*it)); +} + +/*******************************************************************\ + +Function: invariant_propagationt::get_objects_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::get_objects_rec( + const exprt &src, + std::list &dest) +{ + const typet &t=ns.follow(src.type()); + + if(t.id()=="struct" || + t.id()=="union") + { + const struct_typet &struct_type=to_struct_type(t); + + const struct_typet::componentst &c=struct_type.components(); + + exprt member_expr("member"); + member_expr.copy_to_operands(src); + + for(struct_typet::componentst::const_iterator + it=c.begin(); + it!=c.end(); + it++) + { + member_expr.set("component_name", it->get_string("name")); + member_expr.type()=it->type(); + // recursive call + get_objects_rec(member_expr, dest); + } + } + else if(t.id()=="array") + { + //get_objects_rec(identifier, suffix+"[]", t.subtype(), dest); + //we don't track these + } + else if(check_type(t)) + { + dest.push_back(src); + } +} + +/*******************************************************************\ + +Function: invariant_propagationt::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::add_objects( + const goto_functionst &goto_functions) +{ + // get the globals + object_listt globals; + get_globals(globals); + + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + { + // get the locals + std::set locals; + get_local_identifiers(f_it->second, locals); + + const goto_programt &goto_program=f_it->second.body; + + // cache the list for the locals to speed things up + typedef hash_map_cont object_cachet; + object_cachet object_cache; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + #if 0 + invariant_sett &is=(*this)[i_it].invariant_set; + + is.add_objects(globals); + #endif + + for(std::set::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + // cache hit? + object_cachet::const_iterator e_it=object_cache.find(*l_it); + + if(e_it==object_cache.end()) + { + const symbolt &symbol=ns.lookup(*l_it); + + object_listt &objects=object_cache[*l_it]; + get_objects(symbol, objects); + #if 0 + is.add_objects(objects); + #endif + } + #if 0 + else + is.add_objects(e_it->second); + #endif + } + } + } +} + +/*******************************************************************\ + +Function: invariant_propagationt::get_globals + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::get_globals( + object_listt &dest) +{ + // static ones + forall_symbols(it, ns.get_context().symbols) + if(it->second.lvalue && + it->second.static_lifetime) + get_objects(it->second, dest); +} + +/*******************************************************************\ + +Function: invariant_propagationt::check_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool invariant_propagationt::check_type(const typet &type) const +{ + if(type.id()=="pointer") + return true; + else if(type.id()=="struct" || + type.id()=="union") + return false; + else if(type.id()=="array") + return false; + else if(type.id()=="symbol") + return check_type(ns.follow(type)); + else if(type.id()=="unsignedbv" || + type.id()=="signedbv") + return true; + else if(type.id()=="bool") + return true; + + return false; +} + +/*******************************************************************\ + +Function: invariant_propagationt::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::initialize(const goto_programt &goto_program) +{ + baset::initialize(goto_program); + + forall_goto_program_instructions(it, goto_program) + { + invariant_sett &s=state_map[it].invariant_set; + + if(it==goto_program.instructions.begin()) + s.make_true(); + else + s.make_false(); + + s.set_value_sets(value_sets); + s.set_object_store(object_store); + s.set_namespace(ns); + } + + add_objects(goto_program); +} + +/*******************************************************************\ + +Function: invariant_propagationt::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::initialize(const goto_functionst &goto_functions) +{ + baset::initialize(goto_functions); + + for(goto_functionst::function_mapt::const_iterator f_it= + goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + initialize(f_it->second.body); +} + +/*******************************************************************\ + +Function: invariant_propagationt::simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::simplify(goto_functionst &goto_functions) +{ + Forall_goto_functions(it, goto_functions) + simplify(it->second.body); +} + +/*******************************************************************\ + +Function: invariant_propagationt::simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_propagationt::simplify(goto_programt &goto_program) +{ + Forall_goto_program_instructions(i_it, goto_program) + { + if(!i_it->is_assert()) continue; + + // find invariant set + state_mapt::const_iterator s_it=state_map.find(i_it); + + if(s_it==state_map.end()) continue; + + const invariant_sett &invariant_set=s_it->second.invariant_set; + + exprt simplified_guard(i_it->guard); + + invariant_set.simplify(simplified_guard); + ::simplify(simplified_guard, ns); + + if(invariant_set.implies(simplified_guard).is_true()) + i_it->guard.make_true(); + } +} diff --git a/src/goto-programs/invariant_propagation.h b/src/goto-programs/invariant_propagation.h new file mode 100644 index 00000000000..eac5455a167 --- /dev/null +++ b/src/goto-programs/invariant_propagation.h @@ -0,0 +1,69 @@ +/*******************************************************************\ + +Module: Invariant Propagation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CEGAR_INVARIANT_PROPAGATION_H +#define CPROVER_CEGAR_INVARIANT_PROPAGATION_H + +#include + +#include "static_analysis.h" +#include "invariant_set_domain.h" + +class invariant_propagationt:public + static_analysist +{ +public: + invariant_propagationt( + const namespacet &_ns, + value_setst &_value_sets): + static_analysist(_ns), + value_sets(_value_sets), + object_store(_ns) + { + } + + const invariant_sett &lookup(locationt l) const + { + return (*this)[l].invariant_set; + } + + virtual void initialize(const goto_programt &goto_program); + virtual void initialize(const goto_functionst &goto_functions); + + void make_all_true(); + void make_all_false(); + + void simplify(goto_programt &goto_program); + void simplify(goto_functionst &goto_functions); + + typedef static_analysist baset; + +protected: + value_setst &value_sets; + + inv_object_storet object_store; + + typedef std::list object_listt; + + void add_objects(const goto_programt &goto_program); + void add_objects(const goto_functionst &goto_functions); + + void get_objects( + const symbolt &symbol, + object_listt &dest); + + void get_objects_rec( + const exprt &src, + std::list &dest); + + void get_globals(object_listt &globals); + + bool check_type(const typet &type) const; +}; + +#endif diff --git a/src/goto-programs/invariant_set.cpp b/src/goto-programs/invariant_set.cpp new file mode 100644 index 00000000000..b9ec0f5598e --- /dev/null +++ b/src/goto-programs/invariant_set.cpp @@ -0,0 +1,1506 @@ +/*******************************************************************\ + +Module: Invariant Set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "invariant_set.h" + +/*******************************************************************\ + +Function: inv_object_storet::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void inv_object_storet::output(std::ostream &out) const +{ + for(unsigned i=0; i=entries.size()) + { + entries.resize(n+1); + entries[n].expr=expr; + entries[n].is_constant=true; + } + + return false; + } + + return map.get_number(s, n); +} + +/*******************************************************************\ + +Function: inv_object_storet::add + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned inv_object_storet::add(const exprt &expr) +{ + std::string s=build_string(expr); + + assert(s!=""); + + unsigned n=map.number(s); + + if(n>=entries.size()) + { + entries.resize(n+1); + entries[n].expr=expr; + entries[n].is_constant=is_constant(expr); + } + + return n; +} + +/*******************************************************************\ + +Function: inv_object_storet::is_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool inv_object_storet::is_constant(unsigned n) const +{ + assert(n=bv_width(expr.op0().type())) + return build_string(expr.op0()); + } + else if(expr.op0().type().id()=="bool") + { + return build_string(expr.op0()); + } + } + } + + // we always track constants, but make sure they are uniquely + // represented + if(expr.is_constant()) + { + // NULL? + if(expr.type().id()=="pointer") + if(expr.get("value")=="NULL") + return "0"; + + mp_integer i; + + if(!to_integer(expr, i)) + return integer2string(i); + } + + // we also like "address_of" and "reference_to" + // if the object is constant + if(is_constant_address(expr)) + return from_expr(ns, "", expr); + + if(expr.id()=="member") + { + assert(expr.operands().size()==1); + return build_string(expr.op0())+"."+expr.get_string("component_name"); + } + + if(expr.id()=="symbol") + return expr.get_string("identifier"); + + return ""; +} + +/*******************************************************************\ + +Function: invariant_sett::get_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool invariant_sett::get_object( + const exprt &expr, + unsigned &n) const +{ + assert(object_store!=NULL); + return object_store->get(expr, n); +} + +/*******************************************************************\ + +Function: inv_object_storet::is_constant_address + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool inv_object_storet::is_constant_address(const exprt &expr) +{ + if(expr.id()=="address_of" || + expr.id()=="implicit_address_of" || + expr.id()=="reference_to") + if(expr.operands().size()==1) + return is_constant_address_rec(expr.op0()); + + return false; +} + +/*******************************************************************\ + +Function: inv_object_storet::is_constant_address_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool inv_object_storet::is_constant_address_rec(const exprt &expr) +{ + if(expr.id()=="symbol") + return true; + else if(expr.id()=="member") + { + assert(expr.operands().size()==1); + return is_constant_address_rec(expr.op0()); + } + else if(expr.id()=="index") + { + assert(expr.operands().size()==2); + if(expr.op1().is_constant()) + return is_constant_address_rec(expr.op0()); + } + + return false; +} + +/*******************************************************************\ + +Function: invariant_sett::add + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::add( + const std::pair &p, + ineq_sett &dest) +{ + eq_set.check_index(p.first); + eq_set.check_index(p.second); + + // add all. Quadratic. + unsigned f_r=eq_set.find(p.first); + unsigned s_r=eq_set.find(p.second); + + for(unsigned f=0; f(f, s)); +} + +/*******************************************************************\ + +Function: invariant_sett::add_eq + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::add_eq(const std::pair &p) +{ + eq_set.make_union(p.first, p.second); + + // check if there is a contradiction with two constants + unsigned r=eq_set.find(p.first); + + bool constant_seen=false; + mp_integer c; + + for(unsigned i=0; iis_constant(i)) + { + if(constant_seen) + { + // contradiction + make_false(); + return; + } + else + constant_seen=true; + } + + // replicate <= and != constraints + + for(ineq_sett::const_iterator it=le_set.begin(); + it!=le_set.end(); + it++) + add_eq(le_set, p, *it); + + for(ineq_sett::const_iterator it=ne_set.begin(); + it!=ne_set.end(); + it++) + add_eq(ne_set, p, *it); +} + +/*******************************************************************\ + +Function: invariant_sett::add_eq + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::add_eq( + ineq_sett &dest, + const std::pair &eq, + const std::pair &ineq) +{ + std::pair n; + + // uhuh. Need to try all pairs + + if(eq.first==ineq.first) + { + n=ineq; + n.first=eq.second; + dest.insert(n); + } + + if(eq.first==ineq.second) + { + n=ineq; + n.second=eq.second; + dest.insert(n); + } + + if(eq.second==ineq.first) + { + n=ineq; + n.first=eq.first; + dest.insert(n); + } + + if(eq.second==ineq.second) + { + n=ineq; + n.second=eq.first; + dest.insert(n); + } +} + +/*******************************************************************\ + +Function: invariant_sett::is_eq + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt invariant_sett::is_eq(std::pair p) const +{ + std::pair s=p; + std::swap(s.first, s.second); + + if(has_eq(p)) + return tvt(true); + + if(has_ne(p) || has_ne(s)) + return tvt(false); + + return tvt(tvt::TV_UNKNOWN); +} + +/*******************************************************************\ + +Function: invariant_sett::is_le + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt invariant_sett::is_le(std::pair p) const +{ + std::pair s=p; + std::swap(s.first, s.second); + + if(has_eq(p)) + return tvt(true); + + if(has_le(p)) + return tvt(true); + + if(has_le(s)) + if(has_ne(s) || has_ne(p)) + return tvt(false); + + return tvt(tvt::TV_UNKNOWN); +} + +/*******************************************************************\ + +Function: invariant_sett::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::output( + const irep_idt &identifier, + std::ostream &out) const +{ + if(is_false) + { + out << "FALSE" << std::endl; + return; + } + + assert(object_store!=NULL); + + for(unsigned i=0; i=2) + { + bool first=true; + for(unsigned j=0; jsecond + << std::endl; + } + + for(ineq_sett::const_iterator it=le_set.begin(); + it!=le_set.end(); + it++) + { + out << to_string(it->first, identifier) + << " <= " << to_string(it->second, identifier) + << std::endl; + } + + for(ineq_sett::const_iterator it=ne_set.begin(); + it!=ne_set.end(); + it++) + { + out << to_string(it->first, identifier) + << " != " << to_string(it->second, identifier) + << std::endl; + } +} + +/*******************************************************************\ + +Function: invariant_sett::strengthen + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::add_type_bounds(const exprt &expr, const typet &type) +{ + if(expr.type()==type) return; + + if(type.id()=="unsignedbv") + { + unsigned op_width=bv_width(type); + + if(op_width<=8) + { + unsigned a; + if(get_object(expr, a)) return; + + add_bounds(a, boundst(0, power(2, op_width)-1)); + } + } +} + +/*******************************************************************\ + +Function: invariant_sett::strengthen + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::strengthen(const exprt &expr) +{ + exprt tmp(expr); + nnf(tmp); + strengthen_rec(tmp); +} + +/*******************************************************************\ + +Function: invariant_sett::strengthen_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::strengthen_rec(const exprt &expr) +{ + if(expr.type().id()!="bool") + throw "non-Boolean argument to strengthen()"; + + #if 0 + std::cout << "S: " << from_expr(*ns, "", expr) << std::endl; + #endif + + if(is_false) + { + // we can't get any stronger + return; + } + + if(expr.is_true()) + { + // do nothing, it's useless + } + else if(expr.is_false()) + { + // wow, that's strong + make_false(); + } + else if(expr.id()=="not") + { + // give up, we expect NNF + } + else if(expr.id()=="and") + { + forall_operands(it, expr) + strengthen_rec(*it); + } + else if(expr.id()=="<=" || + expr.id()=="<") + { + assert(expr.operands().size()==2); + + // special rule: x <= (a & b) + // implies: x<=a && x<=b + + if(expr.op1().id()=="bitand") + { + const exprt &bitand_op=expr.op1(); + + forall_operands(it, bitand_op) + { + exprt tmp(expr); + tmp.op1()=*it; + strengthen_rec(tmp); + } + + return; + } + + std::pair p; + + if(get_object(expr.op0(), p.first) || + get_object(expr.op1(), p.second)) + return; + + mp_integer i0, i1; + bool have_i0=!to_integer(expr.op0(), i0); + bool have_i1=!to_integer(expr.op1(), i1); + + if(expr.id()=="<=") + { + if(have_i0) + add_bounds(p.second, lower_interval(i0)); + else if(have_i1) + add_bounds(p.first, upper_interval(i1)); + else + add_le(p); + } + else if(expr.id()=="<") + { + if(have_i0) + add_bounds(p.second, lower_interval(i0+1)); + else if(have_i1) + add_bounds(p.first, upper_interval(i1-1)); + else + { + add_le(p); + add_ne(p); + } + } + else + assert(false); + } + else if(expr.id()=="=") + { + assert(expr.operands().size()==2); + + const typet &op_type=ns->follow(expr.op0().type()); + + if(op_type.id()=="struct") + { + const struct_typet &struct_type=to_struct_type(op_type); + + const struct_typet::componentst &c=struct_type.components(); + + exprt lhs_member_expr("member"); + exprt rhs_member_expr("member"); + lhs_member_expr.copy_to_operands(expr.op0()); + rhs_member_expr.copy_to_operands(expr.op1()); + + for(struct_typet::componentst::const_iterator + it=c.begin(); + it!=c.end(); + it++) + { + const irep_idt &component_name=it->get("name"); + + lhs_member_expr.set("component_name", component_name); + rhs_member_expr.set("component_name", component_name); + lhs_member_expr.type()=it->type(); + rhs_member_expr.type()=it->type(); + + equality_exprt equality; + equality.lhs()=lhs_member_expr; + equality.rhs()=rhs_member_expr; + + // recursive call + strengthen_rec(equality); + } + + return; + } + + // special rule: x = (a & b) + // implies: x<=a && x<=b + + if(expr.op1().id()=="bitand") + { + const exprt &bitand_op=expr.op1(); + + forall_operands(it, bitand_op) + { + exprt tmp(expr); + tmp.op1()=*it; + tmp.id("<="); + strengthen_rec(tmp); + } + + return; + } + else if(expr.op0().id()=="bitand") + { + exprt tmp(expr); + std::swap(tmp.op0(), tmp.op1()); + strengthen_rec(tmp); + return; + } + + // special rule: x = (type) y + if(expr.op1().id()=="typecast") + { + assert(expr.op1().operands().size()==1); + add_type_bounds(expr.op0(), expr.op1().op0().type()); + } + else if(expr.op0().id()=="typecast") + { + assert(expr.op0().operands().size()==1); + add_type_bounds(expr.op1(), expr.op0().op0().type()); + } + + std::pair p, s; + + if(get_object(expr.op0(), p.first) || + get_object(expr.op1(), p.second)) + return; + + mp_integer i; + + if(!to_integer(expr.op0(), i)) + add_bounds(p.second, boundst(i)); + else if(!to_integer(expr.op1(), i)) + add_bounds(p.first, boundst(i)); + + s=p; + std::swap(s.first, s.second); + + // contradiction? + if(has_ne(p) || has_ne(s)) + make_false(); + else if(!has_eq(p)) + add_eq(p); + } + else if(expr.id()=="notequal") + { + assert(expr.operands().size()==2); + + std::pair p; + + if(get_object(expr.op0(), p.first) || + get_object(expr.op1(), p.second)) + return; + + // check if this is a contradiction + if(has_eq(p)) + make_false(); + else + add_ne(p); + } +} + +/*******************************************************************\ + +Function: invariant_sett::implies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt invariant_sett::implies(const exprt &expr) const +{ + exprt tmp(expr); + nnf(tmp); + return implies_rec(tmp); +} + +/*******************************************************************\ + +Function: invariant_sett::implies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt invariant_sett::implies_rec(const exprt &expr) const +{ + if(expr.type().id()!="bool") + throw "implies: non-Boolean expression"; + + #if 0 + std::cout << "I: " << from_expr(*ns, "", expr) << std::endl; + #endif + + if(is_false) // can't get any stronger + return tvt(true); + + if(expr.is_true()) + return tvt(true); + else if(expr.id()=="not") + { + // give up, we expect NNF + } + else if(expr.id()=="and") + { + forall_operands(it, expr) + if(implies_rec(*it)!=tvt(true)) + return tvt(tvt::TV_UNKNOWN); + + return tvt(true); + } + else if(expr.id()=="or") + { + forall_operands(it, expr) + if(implies_rec(*it)==tvt(true)) + return tvt(true); + } + else if(expr.id()=="<=" || + expr.id()=="<" || + expr.id()=="=" || + expr.id()=="notequal") + { + assert(expr.operands().size()==2); + + std::pair p; + + bool ob0=get_object(expr.op0(), p.first); + bool ob1=get_object(expr.op1(), p.second); + + if(ob0 || ob1) return tvt(tvt::TV_UNKNOWN); + + tvt r; + + if(expr.id()=="<=") + { + r=is_le(p); + if(!r.is_unknown()) return r; + + boundst b0, b1; + get_bounds(p.first, b0); + get_bounds(p.second, b1); + + return b0<=b1; + } + else if(expr.id()=="<") + { + r=is_lt(p); + if(!r.is_unknown()) return r; + + boundst b0, b1; + get_bounds(p.first, b0); + get_bounds(p.second, b1); + + return b0get_expr(a); + mp_integer tmp; + if(!to_integer(e_a, tmp)) + { + bounds=boundst(tmp); + return; + } + + if(e_a.type().id()=="unsignedbv") + bounds=lower_interval(mp_integer(0)); + } + + bounds_mapt::const_iterator it=bounds_map.find(a); + + if(it!=bounds_map.end()) bounds=it->second; +} + +/*******************************************************************\ + +Function: invariant_sett::nnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::nnf(exprt &expr, bool negate) +{ + if(expr.type().id()!="bool") + throw "nnf: non-Boolean expression"; + + if(expr.is_true()) + { + if(negate) expr.make_false(); + } + else if(expr.is_false()) + { + if(negate) expr.make_true(); + } + else if(expr.id()=="not") + { + assert(expr.operands().size()==1); + nnf(expr.op0(), !negate); + exprt tmp; + tmp.swap(expr.op0()); + expr.swap(tmp); + } + else if(expr.id()=="and") + { + if(negate) expr.id("or"); + + Forall_operands(it, expr) + nnf(*it, negate); + } + else if(expr.id()=="or") + { + if(negate) expr.id("and"); + + Forall_operands(it, expr) + nnf(*it, negate); + } + else if(expr.id()=="typecast") + { + assert(expr.operands().size()==1); + + if(expr.op0().type().id()=="unsignedbv" || + expr.op0().type().id()=="signedbv") + { + equality_exprt tmp; + tmp.lhs()=expr.op0(); + tmp.rhs()=gen_zero(expr.op0().type()); + nnf(tmp, !negate); + expr.swap(tmp); + } + else + { + if(negate) expr.make_not(); + } + } + else if(expr.id()=="<=") + { + if(negate) + { + // !a<=b <-> !b=>a <-> b !b>a <-> b<=a + expr.id("<="); + std::swap(expr.op0(), expr.op1()); + } + } + else if(expr.id()==">=") + { + if(negate) + expr.id("<"); + else + { + expr.id("<="); + std::swap(expr.op0(), expr.op1()); + } + } + else if(expr.id()==">") + { + if(negate) + expr.id("<="); + else + { + expr.id("<"); + std::swap(expr.op0(), expr.op1()); + } + } + else if(expr.id()=="=") + { + if(negate) expr.id("notequal"); + } + else if(expr.id()=="notequal") + { + if(negate) expr.id("="); + } + else + { + if(negate) + expr.make_not(); + } +} + +/*******************************************************************\ + +Function: invariant_sett::simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::simplify( + exprt &expr) const +{ + if(expr.id()=="address_of" || + expr.id()=="implicit_address_of" || + expr.id()=="reference_to") + return; + + Forall_operands(it, expr) + simplify(*it); + + if(expr.id()=="symbol" || + expr.id()=="member") + { + exprt tmp=get_constant(expr); + if(tmp.is_not_nil()) + expr.swap(tmp); + } +} + +/*******************************************************************\ + +Function: invariant_sett::get_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt invariant_sett::get_constant(const exprt &expr) const +{ + unsigned a; + + if(!get_object(expr, a)) + { + // bounds? + bounds_mapt::const_iterator it=bounds_map.find(a); + + if(it!=bounds_map.end()) + { + if(it->second.singleton()) + return from_integer(it->second.get_lower(), expr.type()); + } + + unsigned r=eq_set.find(a); + + // is it a constant? + for(unsigned i=0; iget_expr(i); + + if(e.is_constant()) + { + mp_integer value; + assert(!to_integer(e, value)); + + if(expr.type().id()=="pointer") + { + if(value==0) + { + exprt tmp("constant", expr.type()); + tmp.set("value", "NULL"); + return tmp; + } + } + else + return from_integer(value, expr.type()); + } + else if(object_store->is_constant_address(e)) + { + if(e.type()==expr.type()) + return e; + + exprt tmp("typecast", expr.type()); + tmp.copy_to_operands(e); + return tmp; + } + } + } + + return static_cast(get_nil_irep()); +} + +/*******************************************************************\ + +Function: inv_object_storet::to_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string inv_object_storet::to_string( + unsigned a, + const irep_idt &identifier) const +{ + //return from_expr(ns, "", get_expr(a)); + return id2string(map[a]); +} + +/*******************************************************************\ + +Function: invariant_sett::to_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string invariant_sett::to_string( + unsigned a, + const irep_idt &identifier) const +{ + assert(object_store!=NULL); + return object_store->to_string(a, identifier); +} + +/*******************************************************************\ + +Function: invariant_sett::make_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool invariant_sett::make_union(const invariant_sett &other) +{ + if(other.threaded && + !threaded) + { + make_threaded(); + return true; // change + } + + if(threaded) + return false; // no change + + if(other.is_false) + return false; // no change + + if(is_false) + { + // copy + is_false=false; + eq_set=other.eq_set; + le_set=other.le_set; + ne_set=other.ne_set; + bounds_map=other.bounds_map; + + return true; // change + } + + // equalities first + unsigned old_eq_roots=eq_set.count_roots(); + + eq_set.intersection(other.eq_set); + + // inequalities + unsigned old_ne_set=ne_set.size(); + unsigned old_le_set=le_set.size(); + + intersection(ne_set, other.ne_set); + intersection(le_set, other.le_set); + + // bounds + if(make_union_bounds_map(other.bounds_map)) + return true; + + if(old_eq_roots!=eq_set.count_roots()) return true; + if(old_ne_set!=ne_set.size()) return true; + if(old_le_set!=le_set.size()) return true; + + return false; // no change +} + +/*******************************************************************\ + +Function: invariant_sett::make_union_bounds_map + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool invariant_sett::make_union_bounds_map(const bounds_mapt &other) +{ + bool changed=false; + + for(bounds_mapt::iterator + it=bounds_map.begin(); + it!=bounds_map.end(); + ) // no it++ + { + bounds_mapt::const_iterator o_it=other.find(it->first); + + if(o_it==other.end()) + { + bounds_mapt::iterator next(it); + next++; + bounds_map.erase(it); + it=next; + changed=true; + } + else + { + boundst old(it->second); + it->second.approx_union_with(o_it->second); + if(!(it->second==old).is_true()) changed=true; + it++; + } + } + + return changed; +} + +/*******************************************************************\ + +Function: invariant_sett::modifies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::modifies(unsigned a) +{ + eq_set.isolate(a); + remove(ne_set, a); + remove(le_set, a); + bounds_map.erase(a); +} + +/*******************************************************************\ + +Function: invariant_sett::modifies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::modifies(const exprt &lhs) +{ + if(lhs.id()=="symbol" || + lhs.id()=="member") + { + unsigned a; + if(!get_object(lhs, a)) modifies(a); + } + else if(lhs.id()=="index") + { + // we don't track arrays + } + else if(lhs.id()=="dereference") + { + // be very, very conservative for now + make_true(); + } + else if(lhs.id()=="object_value") + { + // be very, very conservative for now + make_true(); + } + else if(lhs.id()=="if") + { + // we just assume both are changed + assert(lhs.operands().size()==3); + modifies(lhs.op1()); + modifies(lhs.op2()); + } + else if(lhs.id()=="typecast") + { + // just go down + assert(lhs.operands().size()==1); + modifies(lhs.op0()); + } + else if(lhs.id()=="valid_object") + { + } + else if(lhs.id()=="dynamic_size") + { + } + else if(lhs.id()=="byte_extract_little_endian" || + lhs.id()=="byte_extract_big_endian") + { + // just go down + assert(lhs.operands().size()==2); + modifies(lhs.op0()); + } + else if(lhs.id()=="NULL-object" || + lhs.id()=="is_zero_string" || + lhs.id()=="zero_string" || + lhs.id()=="zero_string_length") + { + // ignore + } + else + throw "modifies: unexpected lhs: "+lhs.id_string(); +} + +/*******************************************************************\ + +Function: invariant_sett::assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::assignment( + const exprt &lhs, + const exprt &rhs) +{ + equality_exprt equality; + equality.lhs()=lhs; + equality.rhs()=rhs; + + // first evaluate RHS + simplify(equality.rhs()); + ::simplify(equality.rhs(), *ns); + + // now kill LHS + modifies(lhs); + + // and put it back + strengthen(equality); +} + +/*******************************************************************\ + +Function: invariant_sett::apply_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_sett::apply_code(const codet &code) +{ + const irep_idt &statement=code.get("statement"); + + if(statement=="block") + { + forall_operands(it, code) + apply_code(to_code(*it)); + } + else if(statement=="assign" || + statement=="init") + { + if(code.operands().size()!=2) + throw "assignment expected to have two operands"; + + assignment(code.op0(), code.op1()); + } + else if(statement=="decl") + { + if(code.operands().size()==2) + assignment(code.op0(), code.op1()); + else + modifies(code.op0()); + } + else if(statement=="expression") + { + // this never modifies anything + } + else if(statement=="function_call") + { + // may be a desaster + make_true(); + } + else if(statement=="cpp_delete" || + statement=="cpp_delete[]") + { + // does nothing + } + else if(statement=="free") + { + // does nothing + } + else if(statement=="printf") + { + // does nothing + } + else if(statement=="lock" || + statement=="unlock" || + statement=="asm") + { + // ignore for now + } + else + { + std::cerr << code.pretty() << std::endl; + throw "invariant_sett: unexpected statement: "+id2string(statement); + } +} diff --git a/src/goto-programs/invariant_set.h b/src/goto-programs/invariant_set.h new file mode 100644 index 00000000000..73ccf9aacdb --- /dev/null +++ b/src/goto-programs/invariant_set.h @@ -0,0 +1,293 @@ +/*******************************************************************\ + +Module: Value Set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CEGAR_INVARIANT_SET_H +#define CPROVER_CEGAR_INVARIANT_SET_H + +/* +#include + +*/ + +#include +#include +#include +#include +#include +#include + +#include + +class inv_object_storet +{ +public: + inv_object_storet(const namespacet &_ns):ns(_ns) + { + } + + bool get(const exprt &expr, unsigned &n); + + unsigned add(const exprt &expr); + + bool is_constant(unsigned n) const; + bool is_constant(const exprt &expr) const; + + static bool is_constant_address(const exprt &expr); + + const irep_idt &operator[](unsigned n) const + { + return map[n]; + } + + const exprt &get_expr(unsigned n) const + { + assert(n mapt; + mapt map; + + struct entryt + { + bool is_constant; + exprt expr; + }; + + std::vector entries; + + std::string build_string(const exprt &expr) const; + + static bool is_constant_address_rec(const exprt &expr); +}; + +class invariant_sett +{ +public: + // equalities == + unsigned_union_find eq_set; + + // <= + typedef std::set > ineq_sett; + ineq_sett le_set; + + // != + ineq_sett ne_set; + + // bounds + typedef interval boundst; + typedef std::map bounds_mapt; + bounds_mapt bounds_map; + + bool threaded; + bool is_false; + + invariant_sett(): + threaded(false), + is_false(false), + value_sets(NULL), + object_store(NULL), + ns(NULL) + { + } + + void output( + const irep_idt &identifier, + std::ostream &out) const; + + // true = added s.th. + bool make_union(const invariant_sett &other_invariants); + + void strengthen(const exprt &expr); + + void make_true() + { + eq_set.clear(); + le_set.clear(); + ne_set.clear(); + is_false=false; + } + + void make_false() + { + eq_set.clear(); + le_set.clear(); + ne_set.clear(); + is_false=true; + } + + void make_threaded() + { + make_true(); + threaded=true; + } + + void apply_code( + const codet &code); + + void modifies( + const exprt &lhs); + + void assignment( + const exprt &lhs, + const exprt &rhs); + + void set_value_sets(value_setst &_value_sets) + { + value_sets=&_value_sets; + } + + void set_object_store(inv_object_storet &_object_store) + { + object_store=&_object_store; + } + + void set_namespace(const namespacet &_ns) + { + ns=&_ns; + } + + static void intersection(ineq_sett &dest, const ineq_sett &other) + { + ineq_sett::iterator it_d=dest.begin(); + + while(it_d!=dest.end()) + { + ineq_sett::iterator next_d(it_d); + next_d++; + + if(other.find(*it_d)==other.end()) + dest.erase(it_d); + + it_d=next_d; + } + } + + static void remove(ineq_sett &dest, unsigned a) + { + for(ineq_sett::iterator it=dest.begin(); + it!=dest.end(); + ) // no it++ + { + ineq_sett::iterator next(it); + next++; + + if(it->first==a || it->second==a) + dest.erase(it); + + it=next; + } + } + + tvt implies(const exprt &expr) const; + + void simplify(exprt &expr) const; + +protected: + value_setst *value_sets; + inv_object_storet *object_store; + const namespacet *ns; + + tvt implies_rec(const exprt &expr) const; + static void nnf(exprt &expr, bool negate=false); + void strengthen_rec(const exprt &expr); + + void add_type_bounds(const exprt &expr, const typet &type); + + void add_bounds(unsigned a, const boundst &bound) + { + bounds_map[a].intersect_with(bound); + } + + void get_bounds(unsigned a, boundst &dest) const; + + // true = added s.th. + bool make_union_bounds_map(const bounds_mapt &other); + + void modifies(unsigned a); + + std::string to_string( + unsigned a, + const irep_idt &identifier) const; + + bool get_object( + const exprt &expr, + unsigned &n) const; + + exprt get_constant(const exprt &expr) const; + + // queries + bool has_eq(const std::pair &p) const + { + return eq_set.same_set(p.first, p.second); + } + + bool has_le(const std::pair &p) const + { + return le_set.find(p)!=le_set.end(); + } + + bool has_ne(const std::pair &p) const + { + return ne_set.find(p)!=ne_set.end(); + } + + tvt is_eq(std::pair p) const; + tvt is_le(std::pair p) const; + + tvt is_lt(std::pair p) const + { + return is_le(p) && !is_eq(p); + } + + tvt is_ge(std::pair p) const + { + std::swap(p.first, p.second); + return is_eq(p) || is_lt(p); + } + + tvt is_gt(std::pair p) const + { + return !is_le(p); + } + + tvt is_ne(std::pair p) const + { + return !is_eq(p); + } + + void add(const std::pair &p, ineq_sett &dest); + + void add_le(const std::pair &p) + { + add(p, le_set); + } + + void add_ne(const std::pair &p) + { + add(p, ne_set); + } + + void add_eq(const std::pair &eq); + + void add_eq( + ineq_sett &dest, + const std::pair &eq, + const std::pair &ineq); +}; + +#endif diff --git a/src/goto-programs/invariant_set_domain.cpp b/src/goto-programs/invariant_set_domain.cpp new file mode 100644 index 00000000000..195bf193848 --- /dev/null +++ b/src/goto-programs/invariant_set_domain.cpp @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: Invariant Set Domain + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "invariant_set_domain.h" + +/*******************************************************************\ + +Function: invariant_set_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void invariant_set_domaint::transform( + const namespacet &ns, + locationt from_l, + locationt to_l) +{ + switch(from_l->type) + { + case GOTO: + { + exprt tmp(static_analysis_baset::get_guard(from_l, to_l)); + simplify(tmp, ns); + invariant_set.strengthen(tmp); + } + break; + + case ASSERT: + case ASSUME: + { + exprt tmp(from_l->guard); + simplify(tmp, ns); + invariant_set.strengthen(tmp); + } + break; + + case RETURN: + // ignore + break; + + case ASSIGN: + { + const code_assignt &assignment=to_code_assign(from_l->code); + invariant_set.assignment(assignment.lhs(), assignment.rhs()); + } + break; + + case OTHER: + if(from_l->code.is_not_nil()) + invariant_set.apply_code(from_l->code); + break; + + case DECL: + invariant_set.apply_code(from_l->code); + break; + + case FUNCTION_CALL: + invariant_set.apply_code(from_l->code); + break; + + case START_THREAD: + invariant_set.make_threaded(); + break; + + default:; + // do nothing + } +} diff --git a/src/goto-programs/invariant_set_domain.h b/src/goto-programs/invariant_set_domain.h new file mode 100644 index 00000000000..57ff27ce4ee --- /dev/null +++ b/src/goto-programs/invariant_set_domain.h @@ -0,0 +1,47 @@ +/*******************************************************************\ + +Module: Value Set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_INVARIANT_SET_DOMAIN_H +#define CPROVER_POINTER_ANALYSIS_INVARIANT_SET_DOMAIN_H + +#include "static_analysis.h" +#include "invariant_set.h" + +class invariant_set_domaint:public domain_baset +{ +public: + invariant_sett invariant_set; + + // overloading + + virtual bool merge(const invariant_set_domaint &other) + { + return invariant_set.make_union(other.invariant_set); + } + + virtual void output( + const namespacet &ns, + std::ostream &out) const + { + invariant_set.output("", out); + } + + virtual void initialize( + const namespacet &ns, + locationt l) + { + invariant_set.make_true(); + } + + virtual void transform( + const namespacet &ns, + locationt from_l, + locationt to_l); +}; + +#endif diff --git a/src/goto-programs/loop_numbers.cpp b/src/goto-programs/loop_numbers.cpp new file mode 100644 index 00000000000..6502bc3da9a --- /dev/null +++ b/src/goto-programs/loop_numbers.cpp @@ -0,0 +1,86 @@ +/*******************************************************************\ + +Module: Loop IDs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#include "loop_numbers.h" + +/*******************************************************************\ + +Function: show_loop_numbers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_loop_numbers( + ui_message_handlert::uit ui, + const goto_programt &goto_program) +{ + for(goto_programt::instructionst::const_iterator + it=goto_program.instructions.begin(); + it!=goto_program.instructions.end(); + it++) + { + if(it->is_backwards_goto()) + { + unsigned loop_id=it->loop_number; + + if(ui==ui_message_handlert::XML_UI) + { + xmlt xml("loop"); + xml.new_element("loop-id").data=id2string(it->function)+"."+i2string(loop_id); + + xmlt &l=xml.new_element(); + convert(it->location, l); + l.name="location"; + + std::cout << xml << std::endl; + } + else if(ui==ui_message_handlert::PLAIN) + { + std::cout << "Loop " + << it->function << "." << loop_id << ":" << std::endl; + + std::cout << " " << it->location << std::endl; + std::cout << std::endl; + } + else + assert(false); + } + } +} + +/*******************************************************************\ + +Function: show_loop_numbers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_loop_numbers( + ui_message_handlert::uit ui, + const goto_functionst &goto_functions) +{ + for(goto_functionst::function_mapt::const_iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + show_loop_numbers(ui, it->second.body); +} diff --git a/src/goto-programs/loop_numbers.h b/src/goto-programs/loop_numbers.h new file mode 100644 index 00000000000..fdbe6d6d336 --- /dev/null +++ b/src/goto-programs/loop_numbers.h @@ -0,0 +1,24 @@ +/*******************************************************************\ + +Module: Loop IDs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CBMC_LOOP_IDS_H +#define CPROVER_CBMC_LOOP_IDS_H + +#include + +#include "goto_functions.h" + +void show_loop_numbers( + ui_message_handlert::uit ui, + const goto_functionst &goto_functions); + +void show_loop_numbers( + ui_message_handlert::uit ui, + const goto_programt &goto_program); + +#endif diff --git a/src/goto-programs/loops.cpp b/src/goto-programs/loops.cpp new file mode 100644 index 00000000000..eca66f6e68b --- /dev/null +++ b/src/goto-programs/loops.cpp @@ -0,0 +1,649 @@ +/* + * loops.cpp + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + +#include + +#include "loops.h" + + +static void insert_on_stack(const CFG_nodet& m, CFGt::nodes_const_sett & loop, + std::vector & stack) +{ + + if(loop.end() != loop.find(&m)) + { + return; + } + + loop.insert(&m); + + stack.push_back(&m); + +} + +/* This is the algorithm of Fig. 10.15 of the Dragon book */ +void loop_infot::compute_loop_nodes_for_back_edge( + const CFG_nodet& n, + const CFG_nodet& d, + CFGt::nodes_const_sett & loop) +{ + + std::vector stack; + + loop.insert(&d); + + insert_on_stack(n, loop, stack); + + while(!stack.empty()) + { + + const CFG_nodet& m = *(stack.back()); + stack.pop_back(); + + for(std::set::iterator + it = m.predecessors.begin(); + it != m.predecessors.end(); + it++) + { + insert_on_stack(**it, loop, stack); + } + } + +} + + +loop_infot::loop_infot(const CFGt& cfg) +{ + dominator_infot dominator_info(cfg); + + /* Our loop analysis infrastructure is only designed for reducible control-flow graphs, + * so check that we have one + */ + { + DFST_numberingt dfst_numbering(cfg); + assert(dominator_info.cfg_is_reducible(dfst_numbering)); + } + + for(CFGt::const_nodes_vectort::const_iterator cfg_it = cfg.get_ordered_nodes().begin(); cfg_it != cfg.get_ordered_nodes().end(); cfg_it++) + { + const CFG_nodet& d = **cfg_it; + + loop_infot::loopt* loop_headed_at_current_node = NULL; + + for(std::set::const_iterator + predecessor_it = d.predecessors.begin(); + predecessor_it != d.predecessors.end(); + predecessor_it++) + { + /* Getting into notation of Dragon book: */ + + const CFG_nodet& n = **predecessor_it; + + /* We have an edge of the form n -> d + * + * Need to check whether d dominates n + */ + + if(dominator_info.is_back_edge(n, d)) + { + CFGt::nodes_const_sett loop_nodes = CFGt::nodes_const_sett(); + + compute_loop_nodes_for_back_edge(n, d, loop_nodes); + + if(NULL == loop_headed_at_current_node) + { + // This is the first loop detected for this header + loopt new_loop(&d, loop_nodes, &cfg); + loops.push_back(new_loop); + loop_headed_at_current_node = &loops.back(); + + + } else { + // There are already loops at this header: merge the new loop with these loops + loop_headed_at_current_node->add_nodes(loop_nodes); + } + + + } + + } + + } + + organize_loops(cfg); + +} + + +loop_infot::loopt* loop_infot::get_closest_containing_loop(const CFG_nodet* n) const +{ + std::map::const_iterator it = closest_containing_loop.find(n); + + if(it == closest_containing_loop.end()) + { + return NULL; + } + + return it->second; + +} + +loop_infot::loopt* loop_infot::get_largest_containing_loop(const CFG_nodet* n) const +{ + for(unsigned int i = 0; i < outer_loops.size(); i++) + { + if(outer_loops[i]->contains(*n)) + { + return outer_loops[i]; + } + } + return NULL; +} + + + +void loop_infot::organize_loops(const CFGt& cfg) +{ + if(loops.empty()) + { + return; + } + + /* First, give each loop a parent if it has one */ + for(std::list::iterator + it1 = loops.begin(); + it1 != loops.end(); + it1++) + { + std::list::iterator + it2 = it1; + for(it2++; + it2 != loops.end(); + it2++) + { + loop_infot::loopt & loop1 = *it1; + loop_infot::loopt & loop2 = *it2; + + if( loop1.contains(loop2) ) + { + loop2.candidate_parent(loop1); + } else if( loop2.contains(loop1) ) { + loop1.candidate_parent(loop2); + } else { + assert ( loop_infot::loopt::disjoint (loop1, loop2 ) ); + } + + } + + } + + /* Next, assign immediate children to every loop, and work out the outer and inner loops */ + for(std::list::iterator + it1 = loops.begin(); + it1 != loops.end(); // Note that we don't use end() - 1 here as we need to consider all loops when working out inner and outer loops + it1++) + { + loop_infot::loopt & loop1 = *it1; + + if(NULL == loop1.parent) + { + outer_loops.push_back(&loop1); + } + + std::list::iterator it2 = it1; + + for(it2++; + it2 != loops.end(); + it2++) + { + loop_infot::loopt & loop2 = *it2; + + if( loop2.parent == &loop1 ) + { + loop1.children.push_back(&loop2); + } + } + + if(loop1.children.empty()) + { + inner_loops.push_back(&loop1); + } + + } + + /* Finally, compute mapping which assigns each instruction to its nearest loop */ + for(CFGt::const_nodes_vectort::const_iterator it = cfg.get_ordered_nodes().begin(); it != cfg.get_ordered_nodes().end(); it++) + { + + closest_containing_loop[*it] = NULL; + + for(std::vector::iterator + inner_loop_it = inner_loops.begin(); + inner_loop_it != inner_loops.end(); + inner_loop_it++) + { + loop_infot::loopt * current_loop = *inner_loop_it; + + while(NULL != current_loop) + { + if(current_loop->contains(**it)) + { + if((NULL == closest_containing_loop[*it]) || closest_containing_loop[*it]->contains(*current_loop)) + { + closest_containing_loop[*it] = current_loop; + } + break; + } + + current_loop = current_loop->parent; + } + + } + + } + +} + + +void loop_infot::loopt::candidate_parent( loopt& other ) +{ + /* Set 'other' as parent if we don't have one. Otherwise, + * we'd like 'other' as parent if our parent contains other, + * i.e. we have this <= other <= parent. + */ + if((NULL == parent) || (parent->contains(other))) + { + parent = &other; + } +} + + +bool loop_infot::loopt::contains( const loopt& other) const +{ + for(CFGt::nodes_const_sett::iterator + it = other.nodes.begin(); + it != other.nodes.end(); + it++) + { + bool found = false; + for(CFGt::nodes_const_sett::iterator + it2 = this->nodes.begin(); + it2 != this->nodes.end(); + it2++) + { + if( (*it) == (*it2) ) + { + found = true; + break; + } + } + + if(!found) + { + return false; + } + + } + + return true; +} + +bool loop_infot::loopt::contains( const CFG_nodet& inst) const +{ + return nodes.end() != nodes.find(&inst); +} + +bool loop_infot::loopt::disjoint ( loopt& loop1, loopt& loop2 ) +{ + for(CFGt::nodes_const_sett::iterator + it = loop1.nodes.begin(); + it != loop1.nodes.end(); + it++) + { + for(CFGt::nodes_const_sett::iterator + it2 = loop2.nodes.begin(); + it2 != loop2.nodes.end(); + it2++) + { + if( (*it) == (*it2) ) + { + return false; + } + } + + } + + return true; + +} + + + +void loop_infot::loopt::add_nodes(CFGt::nodes_const_sett nodes) +{ + for(CFGt::nodes_const_sett::iterator it = nodes.begin(); it != nodes.end(); it++) + { + this->nodes.insert(*it); + } +} + + +unsigned int loop_infot::loopt::num_nodes() const +{ + return nodes.size(); +} + + +bool loop_infot::loopt::is_new_post_node(std::vector& post_nodes, CFG_nodet* n) const +{ + return (NULL != n) && !(this->contains(*n)) && (post_nodes.end() == find(post_nodes.begin(), post_nodes.end(), n)); +} + + +void loop_infot::loopt::calculate_post_nodes(std::vector& result) const +{ + for(CFGt::nodes_const_sett::const_iterator it = nodes.begin(); it != nodes.end(); it++) + { + if(is_new_post_node(result, (*it)->successor_next)) + { + result.push_back((*it)->successor_next); + } + if(is_new_post_node(result, (*it)->successor_jump)) + { + result.push_back((*it)->successor_jump); + } + } +} + + + +loop_infot::loopt::loopt(const CFG_nodet* header, CFGt::nodes_const_sett nodes, const CFGt* cfg) : header(header), cfg(cfg) +{ + this->add_nodes(nodes); + this->parent = NULL; + +} + + +std::ostream& operator<<(std::ostream& os, const loop_infot& loop_info) +{ + + for(std::vector::const_iterator + outer_loop_it = loop_info.outer_loops.begin(); + outer_loop_it != loop_info.outer_loops.end(); + outer_loop_it++) + { + os << (**outer_loop_it); + } + + return os; + +} + + + +static void blanks(std::ostream& os, const int num) +{ + for(int i=0; ioutput_node(*(loop.header), os); + os << std::endl; + blanks(os, indent); + os << " nodes:\n"; + + for(CFGt::nodes_const_sett::const_iterator + it = loop.nodes.begin(); + it != loop.nodes.end(); + it++) + { + os << " "; + loop.cfg->output_node(**it, os); + } + os << std::endl; + + if(!loop.children.empty()) + { + blanks(os, indent); + os << " children:" << std::endl; + + indent += indent_size; + + for(std::vector::const_iterator + it = loop.children.begin(); + it != loop.children.end(); + it++) + { + os << (**it) << std::endl; + } + + indent -= indent_size; + + } + + blanks(os, indent); + os << "]" << std::endl; + + return os; + +} + + +unsigned int loop_infot::num_loops() const +{ + return this->loops.size(); +} + + +bool loop_infot::is_inner_loop(const loopt* loop) const +{ + if(NULL == loop) + { + return false; + } + + for(std::vector::const_iterator + it = inner_loops.begin(); + it != inner_loops.end(); + it++) + { + if((*it) == loop) + { + return true; + } + + } + + return false; + +} + + +static bool is_unexplored_descendent(CFG_nodet* node, const loop_infot::loopt& loop, std::set& visited, std::list& queue) +{ + return NULL != node && + loop.contains(*node) && + loop.header != node && + visited.end() == visited.find(node) && + queue.end() == std::find(queue.begin(), queue.end(), node); +} + +bool loop_infot::loopt::on_critical_path(const CFG_nodet& n) const +{ + std::set visited; + std::list queue; + + dominator_infot dominator_info(*cfg); + + queue.push_back(&n); + + while(!queue.empty()) + { + const CFG_nodet* current = queue.front(); + queue.pop_front(); + visited.insert(current); + + if(!dominator_info.dominates(n, *current)) + { + return false; + } + + if(is_unexplored_descendent(current->successor_next, *this, visited, queue)) + { + queue.push_back(current->successor_next); + } + + if(is_unexplored_descendent(current->successor_jump, *this, visited, queue)) + { + queue.push_back(current->successor_jump); + } + } + + return true; +} + +void loop_infot::loopt::find_simple_induction_variables(std::set& result) +{ + // First, find "critical path" through loop: nodes which *must* be executed on any loop iteration + CFGt::nodes_const_sett critical_path; + + for(CFGt::nodes_const_sett::const_iterator it = nodes.begin(); it != nodes.end(); it++) + { + if(on_critical_path(**it)) + { + critical_path.insert(*it); + } + } + + for(CFGt::nodes_const_sett::const_iterator it = nodes.begin(); it != nodes.end(); it++) + { + if(NULL != (*it)->successor_next && NULL != (*it)->successor_jump && (!contains(*((*it)->successor_next))) || !contains(*((*it)->successor_jump))) + { + if(critical_path.end() == critical_path.find(*it)) + { + std::cout << "Exit from loop not on critical path; not interested\n"; + return; + } + } + + } + + + + std::cout << "Loop is: " << (*this); + + std::map num_increments_on_critical_path; + std::map increment_value; + std::set symbols_modified_non_incrementally_on_critical_path; + std::set symbols_modified_off_critical_path; + + for(CFGt::nodes_const_sett::iterator it = critical_path.begin(); it != critical_path.end(); it++) + { + if(ASSIGN == (*it)->type) { + exprt lhs = (*it)->code.op0(); + exprt rhs = (*it)->code.op1(); + if(lhs.id() == ID_symbol) + { + symbol_exprt lhs_as_symbol = to_symbol_expr(lhs); + if(rhs.id() == ID_plus && rhs.op0() == lhs && rhs.op1().id() == ID_constant) { + if(num_increments_on_critical_path.end() == num_increments_on_critical_path.find(lhs_as_symbol)) + { + num_increments_on_critical_path[lhs_as_symbol] = 1; + increment_value[lhs_as_symbol] = rhs.op1(); + } else { + num_increments_on_critical_path[lhs_as_symbol] = num_increments_on_critical_path[lhs_as_symbol] + 1; + } + } else { + symbols_modified_non_incrementally_on_critical_path.insert(lhs_as_symbol); + } + } + } + + } + + for(CFGt::nodes_const_sett::iterator it = this->nodes.begin(); it != this->nodes.end(); it++) + { + if(critical_path.end() == critical_path.find(*it)) + { + if(ASSIGN == (*it)->type && (*it)->code.op0().id() == ID_symbol) + { + symbols_modified_off_critical_path.insert(to_symbol_expr((*it)->code.op0())); + } + } + } + + std::map initial_value; + + const CFG_nodet* n = NULL; + + for(CFGt::nodes_sett::iterator it = this->header->predecessors.begin(); it != this->header->predecessors.end(); it++) + { + if(!(this->contains(**it))) + { + if(NULL != n) + { + n = NULL; + break; + } else { + n = *it; + } + } + + } + + while(NULL != n && n->predecessors.size() == 1) + { + if(ASSIGN == n->type && n->code.op0().id() == ID_symbol) + { + if(initial_value.end() == initial_value.find(to_symbol_expr(n->code.op0()))) + { + initial_value[to_symbol_expr(n->code.op0())] = n->code.op1(); + } + } + n = *(n->predecessors.begin()); + } + + + + + for(std::map::iterator it = num_increments_on_critical_path.begin(); it != num_increments_on_critical_path.end(); it++) + { + if(1 == it->second && symbols_modified_non_incrementally_on_critical_path.end() == symbols_modified_non_incrementally_on_critical_path.find(it->first) + && symbols_modified_off_critical_path.end() == symbols_modified_off_critical_path.find(it->first) + /*&& initial_value.end() != initial_value.find(it->first)*/) + { + namespacet ns(this->cfg->context); + std::cout << "Found an induction variable: "; + std::cout << expr2c(it->first, ns); + std::cout << ", its initial value is " << expr2c(initial_value[it->first], ns); + std::cout << " and its increment is "; + std::cout << expr2c(increment_value[it->first], ns) << "\n"; + } + + } + + + + + + +} + diff --git a/src/goto-programs/loops.h b/src/goto-programs/loops.h new file mode 100644 index 00000000000..4f02cc6ea51 --- /dev/null +++ b/src/goto-programs/loops.h @@ -0,0 +1,115 @@ +/* + * loops.h + * + * Created on: August 18, 2010 + * Author: Alastair Donaldson + */ + +#ifndef LOOPS_H_ +#define LOOPS_H_ + +#include "dominator_info.h" +#include "induction_variable.h" + +class loop_infot /* Represents a nest of loops */ +{ +public: + + /* Uses Dragon book algorithm to compute all natural loops for goto program */ + loop_infot(const CFGt&); + + /* Returns number of loops in the nest */ + unsigned int num_loops() const; + + /* Display information about the loop nest */ + friend std::ostream& operator<<(std::ostream& os, const loop_infot& loop_info); + + class loopt; + typedef std::vector loopst; + typedef std::vector const_loopst; + + class loopt /* Represents a single loop */ + { + public: + + loopt(const CFG_nodet* header, CFGt::nodes_const_sett nodes, const CFGt* cfg); + + void add_nodes(CFGt::nodes_const_sett nodes); + + bool contains( const loopt& other) const; + + bool contains( const CFG_nodet& inst) const; + + static bool disjoint ( loopt& loop1, loopt& loop2 ); + + void candidate_parent( loopt& other ); + + friend std::ostream& operator<<(std::ostream& os, const loopt& loop); + + loopt* parent; // Pointer to loop containing this one + + loopst children; // Inner loops + + const CFG_nodet* header; + + const CFGt* cfg; + + void find_simple_induction_variables(std::set& result); + + unsigned int num_nodes() const; + + void calculate_post_nodes(std::vector& result) const; + + CFGt::nodes_const_sett::const_iterator begin() const + { + return nodes.begin(); + } + + CFGt::nodes_const_sett::const_iterator end() const + { + return nodes.end(); + } + + unsigned depth() const + { + return parent == NULL ? 0 : 1 + parent->depth(); + } + + private: + + bool on_critical_path(const CFG_nodet& n) const; + + bool is_new_post_node(std::vector& post_nodes, CFG_nodet* n) const; + + CFGt::nodes_const_sett nodes; + + unsigned id; + + }; + + bool is_inner_loop(const loopt* loop) const; + + std::list loops; /* All loops */ + + loopst outer_loops; /* Just the outer loops */ + + loopst inner_loops; /* Just the inner loops */ + + loopt* get_closest_containing_loop(const CFG_nodet* n) const; /* Returns the innermost loop that contains the given node, returns NULL if the node is not in any loop */ + + loopt* get_largest_containing_loop(const CFG_nodet* n) const; /* Returns the outermost loop that contains the given node, returns NULL if the node is not in any loop */ + +private: + + void compute_loop_nodes_for_back_edge(const CFG_nodet& n, + const CFG_nodet& d, + CFGt::nodes_const_sett & loop); + + /* Organizes loops into containment chains, and associates each instruction with the inner-most loop that contains it */ + void organize_loops(const CFGt& cfg); + + std::map closest_containing_loop; + +}; + +#endif diff --git a/src/goto-programs/pointer_arithmetic.cpp b/src/goto-programs/pointer_arithmetic.cpp new file mode 100644 index 00000000000..f935cac61f8 --- /dev/null +++ b/src/goto-programs/pointer_arithmetic.cpp @@ -0,0 +1,138 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "std_expr.h" +#include "expr_util.h" + +#include "pointer_arithmetic.h" + +/*******************************************************************\ + +Function: pointer_arithmetict::pointer_arithmetict + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +pointer_arithmetict::pointer_arithmetict(const exprt &src) +{ + pointer.make_nil(); + offset.make_nil(); + read(src); +} + +/*******************************************************************\ + +Function: pointer_arithmetict::read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void pointer_arithmetict::read(const exprt &src) +{ + if(src.id()=="+") + { + forall_operands(it, src) + { + if(it->type().id()==ID_pointer) + read(*it); + else + add_to_offset(*it); + } + } + else if(src.id()=="-") + { + assert(src.operands().size()==2); + read(src.op0()); + exprt o=unary_minus_exprt(src.op1(), src.op1().type()); + add_to_offset(o); + } + else if(src.id()==ID_address_of) + { + assert(src.operands().size()==1); + if(src.op0().id()==ID_index) + { + const index_exprt &index_expr= + to_index_expr(src.op0()); + + if(index_expr.index().is_zero()) + make_pointer(src); + else + { + add_to_offset(index_expr.index()); + // produce &x[0] + i instead of &x[i] + exprt new_src=src; + new_src.op0().op1()=gen_zero(index_expr.index().type()); + make_pointer(new_src); + } + } + else + make_pointer(src); + } + else + make_pointer(src); +} + +/*******************************************************************\ + +Function: pointer_arithmetict::add_to_offset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void pointer_arithmetict::add_to_offset(const exprt &src) +{ + if(offset.is_nil()) + offset=src; + else if(offset.id()=="+") + offset.copy_to_operands(src); + else + { + exprt new_offset=plus_exprt(offset, src); + + if(new_offset.op1().type()!=offset.type()) + new_offset.op1().make_typecast(offset.type()); + + offset=new_offset; + } +} + +/*******************************************************************\ + +Function: pointer_arithmetict::make_pointer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void pointer_arithmetict::make_pointer(const exprt &src) +{ + if(pointer.is_nil()) + pointer=src; + else + add_to_offset(src); +} + diff --git a/src/goto-programs/pointer_arithmetic.h b/src/goto-programs/pointer_arithmetic.h new file mode 100644 index 00000000000..62a7a6465b7 --- /dev/null +++ b/src/goto-programs/pointer_arithmetic.h @@ -0,0 +1,26 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ARITHMETIC_H +#define CPROVER_POINTER_ARITHMETIC_H + +#include "expr.h" + +struct pointer_arithmetict +{ + exprt pointer, offset; + + pointer_arithmetict(const exprt &src); + +protected: + void read(const exprt &src); + void add_to_offset(const exprt &src); + void make_pointer(const exprt &src); +}; + +#endif diff --git a/src/goto-programs/read_bin_goto_object.cpp b/src/goto-programs/read_bin_goto_object.cpp new file mode 100644 index 00000000000..a81434ca3df --- /dev/null +++ b/src/goto-programs/read_bin_goto_object.cpp @@ -0,0 +1,307 @@ +/*******************************************************************\ + +Module: Read goto object files. + +Author: CM Wintersteiger + +Date: June 2006 + +\*******************************************************************/ + +#include +#include + +#include "read_bin_goto_object.h" +#include "goto_function_serialization.h" +//#include "symbol_serialization.h" +#include "irep_serialization.h" +#include "goto_program_irep.h" + +/*******************************************************************\ + +Function: read_goto_object_v1 + + Inputs: input stream, context, functions + + Outputs: true on error, false otherwise + + Purpose: read goto binary format v1 + +\*******************************************************************/ + +#if 0 +OBSOLETE +bool read_bin_goto_object_v1( + std::istream &in, + const std::string &filename, + contextt &context, + goto_functionst &functions, + message_handlert &message_handler, + irep_serializationt &irepconverter, + symbol_serializationt &symbolconverter, + goto_function_serializationt &gfconverter) +{ + unsigned count = irepconverter.read_long(in); + + for (unsigned i=0; i0; + } + + return false; +} +#endif + +/*******************************************************************\ + +Function: read_goto_object_v2 + + Inputs: input stream, context, functions + + Outputs: true on error, false otherwise + + Purpose: read goto binary format v2 + +\*******************************************************************/ + +bool read_bin_goto_object_v2( + std::istream &in, + const std::string &filename, + contextt &context, + goto_functionst &functions, + message_handlert &message_handler, + irep_serializationt &irepconverter, + goto_function_serializationt &gfconverter) +{ + unsigned count = irepconverter.read_long(in); // # of symbols + + for(unsigned i=0; i > target_mapt; + target_mapt target_map; + + unsigned ins_count = irepconverter.read_long(in); // # of instructions + for (unsigned i=0; ifirst; + + for(std::list::iterator nit = tit->second.begin(); + nit!=tit->second.end(); + nit++) + { + unsigned n=*nit; + + Forall_goto_program_instructions(iit, f.body) + { + if(iit->target_number==n) + { + ins->targets.push_back(iit); + break; + } + } + } + } + + f.body.update(); + f.body_available=f.body.instructions.size()>0; + } + + return false; +} + +/*******************************************************************\ + +Function: read_goto_object + + Inputs: input stream, context, functions + + Outputs: true on error, false otherwise + + Purpose: reads a goto binary file back into a symbol and a function table + +\*******************************************************************/ + +bool read_bin_goto_object( + std::istream &in, + const std::string &filename, + contextt &context, + goto_functionst &functions, + message_handlert &message_handler) +{ + message_streamt message_stream(message_handler); + + { + char hdr[4]; + hdr[0]=in.get(); + hdr[1]=in.get(); + hdr[2]=in.get(); + + if(hdr[0]=='G' && hdr[1]=='B' && hdr[2]=='F') + ; + else + { + hdr[3]=in.get(); + if(hdr[0]==0x7f && hdr[1]=='G' && hdr[2]=='B' && hdr[3]=='F') + { + // OK! + } + else if(hdr[0]==0x7f && hdr[1]=='E' && hdr[2]=='L' && hdr[3]=='F') + { + if(filename!="") + message_stream.str << + "Sorry, but I can't read ELF binary `" << filename << "'"; + else + message_stream.str << "Sorry, but I can't read ELF binaries"; + + message_stream.error(); + return true; + } + else + { + message_stream.str << "`" << filename << "' is not a goto-binary"; + message_stream.error(); + return true; + } + } + } + + irep_serializationt::ireps_containert ic; + irep_serializationt irepconverter(ic); + //symbol_serializationt symbolconverter(ic); + goto_function_serializationt gfconverter(ic); + + { + unsigned version=irepconverter.read_long(in); + + switch(version) + { + case 1: + message_stream.warning( + "The input was compiled with an old version of " + "goto-cc; please recompile"); + return true; + + case 2: + return read_bin_goto_object_v2(in, filename, + context, functions, + message_handler, + irepconverter, + gfconverter); + break; + + default: + message_stream.warning( + "The input was compiled with an unsupported version of " + "goto-cc; please recompile"); + return true; + } + } + + return false; +} diff --git a/src/goto-programs/read_bin_goto_object.h b/src/goto-programs/read_bin_goto_object.h new file mode 100644 index 00000000000..abc9a211bfa --- /dev/null +++ b/src/goto-programs/read_bin_goto_object.h @@ -0,0 +1,25 @@ +/*******************************************************************\ + +Module: Read goto object files. + +Author: CM Wintersteiger + +Date: May 2007 + +\*******************************************************************/ + +#ifndef READ_BIN_GOTO_OBJECT_H_ +#define READ_BIN_GOTO_OBJECT_H_ + +#include +#include +#include + +bool read_bin_goto_object( + std::istream &in, + const std::string &filename, + contextt &context, + goto_functionst &functions, + message_handlert &message_handler); + +#endif /*READ_BIN_GOTO_OBJECT_H_*/ diff --git a/src/goto-programs/read_goto_binary.cpp b/src/goto-programs/read_goto_binary.cpp new file mode 100644 index 00000000000..8d0f07b29e3 --- /dev/null +++ b/src/goto-programs/read_goto_binary.cpp @@ -0,0 +1,73 @@ +/*******************************************************************\ + +Module: Read Goto Programs + +Author: + +\*******************************************************************/ + +#include + +#include "read_goto_binary.h" +#include "read_bin_goto_object.h" + +/*******************************************************************\ + +Function: read_goto_binary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool read_goto_binary( + const std::string &filename, + contextt &context, + goto_functionst &dest, + message_handlert &message_handler) +{ + std::ifstream in(filename.c_str(), std::ios::binary); + + if(!in) + { + messaget message(message_handler); + message.error( + std::string("Failed to open `")+filename+"'"); + return true; + } + + return read_bin_goto_object(in, filename, context, dest, message_handler); +} + +/*******************************************************************\ + +Function: is_goto_binary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool is_goto_binary(const std::string &filename) +{ + std::ifstream in(filename.c_str(), std::ios::binary); + + if(!in) return false; + + char hdr[4]; + hdr[0]=in.get(); + hdr[1]=in.get(); + hdr[2]=in.get(); + hdr[3]=in.get(); + + if(hdr[0]==0x7f && hdr[1]=='G' && hdr[2]=='B' && hdr[3]=='F') + return true; + + return false; +} diff --git a/src/goto-programs/read_goto_binary.h b/src/goto-programs/read_goto_binary.h new file mode 100644 index 00000000000..7d1ee8214a7 --- /dev/null +++ b/src/goto-programs/read_goto_binary.h @@ -0,0 +1,25 @@ +/*******************************************************************\ + +Module: Read Goto Programs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_READ_GOTO_BINARY_H +#define CPROVER_GOTO_PROGRAMS_READ_GOTO_BINARY_H + +#include +#include + +#include "goto_functions.h" + +bool read_goto_binary( + const std::string &filename, + contextt &context, + goto_functionst &dest, + message_handlert &message_handler); + +bool is_goto_binary(const std::string &filename); + +#endif diff --git a/src/goto-programs/remove_skip.cpp b/src/goto-programs/remove_skip.cpp new file mode 100644 index 00000000000..a61b668a58c --- /dev/null +++ b/src/goto-programs/remove_skip.cpp @@ -0,0 +1,151 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "remove_skip.h" + +/*******************************************************************\ + +Function: is_skip + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static bool is_skip(goto_programt::instructionst::iterator it) +{ + if(it->is_skip()) + return !it->code.get_bool("explicit"); + + if(it->is_goto()) + { + if(it->guard.is_false()) return true; + + if(it->targets.size()!=1) + return false; + + goto_programt::instructionst::iterator next_it=it; + next_it++; + + return it->targets.front()==next_it; + } + + if(it->is_other()) + { + return it->code.is_nil() || + it->code.get(ID_statement)==ID_skip; + } + + return false; +} + +/*******************************************************************\ + +Function: remove_skip + + Inputs: + + Outputs: + + Purpose: remove unnecessary skip statements + +\*******************************************************************/ + +void remove_skip(goto_programt &goto_program) +{ + typedef std::map new_targetst; + new_targetst new_targets; + + // remove skip statements + + for(goto_programt::instructionst::iterator + it=goto_program.instructions.begin(); + it!=goto_program.instructions.end();) + { + goto_programt::targett old_target=it; + + // for collecting labels + std::list labels; + + while(is_skip(it)) + { + // don't remove the last skip statement, + // it could be a target + if(it==--goto_program.instructions.end()) + break; + + // save labels + labels.splice(labels.end(), it->labels); + it++; + } + + goto_programt::targett new_target=it; + + // save labels + it->labels.splice(it->labels.begin(), labels); + + if(new_target!=old_target) + { + while(new_target!=old_target) + { + // remember the old targets + new_targets[old_target]=new_target; + old_target=goto_program.instructions.erase(old_target); + } + } + else + it++; + } + + // adjust gotos + + Forall_goto_program_instructions(i_it, goto_program) + if(i_it->is_goto() || i_it->is_start_thread()) + { + for(goto_programt::instructiont::targetst::iterator + t_it=i_it->targets.begin(); + t_it!=i_it->targets.end(); + t_it++) + { + new_targetst::const_iterator + result=new_targets.find(*t_it); + + if(result!=new_targets.end()) + *t_it=result->second; + } + } + + // remove the last skip statement unless it's a target + goto_program.compute_incoming_edges(); + + if(!goto_program.instructions.empty() && + is_skip(--goto_program.instructions.end()) && + !goto_program.instructions.back().is_target()) + goto_program.instructions.pop_back(); +} + +/*******************************************************************\ + +Function: remove_skip + + Inputs: + + Outputs: + + Purpose: remove unnecessary skip statements + +\*******************************************************************/ + +void remove_skip(goto_functionst &goto_functions) +{ + Forall_goto_functions(f_it, goto_functions) + remove_skip(f_it->second.body); +} diff --git a/src/goto-programs/remove_skip.h b/src/goto-programs/remove_skip.h new file mode 100644 index 00000000000..d5ffff78fb9 --- /dev/null +++ b/src/goto-programs/remove_skip.h @@ -0,0 +1,17 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_REMOVE_SKIP_H +#define CPROVER_GOTO_PROGRAMS_REMOVE_SKIP_H + +#include "goto_functions.h" + +void remove_skip(goto_programt &goto_program); +void remove_skip(goto_functionst &goto_functions); + +#endif diff --git a/src/goto-programs/remove_unreachable.cpp b/src/goto-programs/remove_unreachable.cpp new file mode 100644 index 00000000000..9f36259c83e --- /dev/null +++ b/src/goto-programs/remove_unreachable.cpp @@ -0,0 +1,63 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "remove_unreachable.h" + +/*******************************************************************\ + +Function: remove_unreachable + + Inputs: + + Outputs: + + Purpose: remove unreachable code + +\*******************************************************************/ + +void remove_unreachable(goto_programt &goto_program) +{ + std::set reachable; + std::stack working; + + working.push(goto_program.instructions.begin()); + + while(!working.empty()) + { + goto_programt::targett t=working.top(); + working.pop(); + + if(reachable.find(t)==reachable.end() && + t!=goto_program.instructions.end()) + { + reachable.insert(t); + goto_programt::targetst successors; + goto_program.get_successors(t, successors); + + for(goto_programt::targetst::const_iterator + s_it=successors.begin(); + s_it!=successors.end(); + s_it++) + working.push(*s_it); + } + } + + // make all unreachable code a skip + // unless it's an 'end_function' + + Forall_goto_program_instructions(it, goto_program) + { + if(reachable.find(it)==reachable.end() && + !it->is_end_function()) + it->make_skip(); + } +} + diff --git a/src/goto-programs/remove_unreachable.h b/src/goto-programs/remove_unreachable.h new file mode 100644 index 00000000000..c40b0ded93f --- /dev/null +++ b/src/goto-programs/remove_unreachable.h @@ -0,0 +1,16 @@ +/*******************************************************************\ + +Module: Program Transformation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_REMOVE_UNREACHABLE_H +#define CPROVER_GOTO_PROGRAMS_REMOVE_UNREACHABLE_H + +#include "goto_functions.h" + +void remove_unreachable(goto_programt &goto_program); + +#endif diff --git a/src/goto-programs/remove_unused_functions.cpp b/src/goto-programs/remove_unused_functions.cpp new file mode 100644 index 00000000000..10e158442c6 --- /dev/null +++ b/src/goto-programs/remove_unused_functions.cpp @@ -0,0 +1,106 @@ +/*******************************************************************\ + +Module: Unused function removal + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include + +#include "remove_unused_functions.h" + +/*******************************************************************\ + +Function: remove_unused_functions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void remove_unused_functions( + goto_functionst &functions, + message_handlert &message_handler) +{ + std::set used_functions; + std::list unused_functions; + find_used_functions("main", functions, used_functions); + + for(goto_functionst::function_mapt::iterator it= + functions.function_map.begin(); + it!=functions.function_map.end(); + it++) + { + if(used_functions.find(it->first)==used_functions.end()) + unused_functions.push_back(it); + } + + message_streamt message_stream(message_handler); + + if(unused_functions.size()>0) + { + message_stream.str + << "Dropping " << unused_functions.size() << " of " << + functions.function_map.size() << " functions (" << + used_functions.size() << " used)"; + } + + message_stream.statistics(); + + for(std::list::const_iterator + it=unused_functions.begin(); + it!=unused_functions.end(); + it++) + functions.function_map.erase(*it); +} + +/*******************************************************************\ + +Function: find_used_functions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_used_functions( + const irep_idt &start, + goto_functionst &functions, + std::set &seen) +{ + std::pair::const_iterator, bool> res = + seen.insert(start); + + if(!res.second) + return; + else + { + goto_functionst::function_mapt::const_iterator f_it = + functions.function_map.find(start); + + if(f_it!=functions.function_map.end()) + { + forall_goto_program_instructions(it, f_it->second.body){ + if(it->type==FUNCTION_CALL) + { + const code_function_callt &call = + to_code_function_call(to_code(it->code)); + + // check that this is actually a simple call + assert(call.function().id()=="symbol"); + + find_used_functions(call.function().get("identifier"), + functions, + seen); + } + } + } + } +} diff --git a/src/goto-programs/remove_unused_functions.h b/src/goto-programs/remove_unused_functions.h new file mode 100644 index 00000000000..f1efc44e8fa --- /dev/null +++ b/src/goto-programs/remove_unused_functions.h @@ -0,0 +1,25 @@ +/*******************************************************************\ + +Module: Unused function removal + +Author: CM Wintersteiger + +\*******************************************************************/ + +#ifndef _CPROVER_GOTO_PROGRAMS_REMOVE_FUNCTIONS_H_ +#define _CPROVER_GOTO_PROGRAMS_REMOVE_FUNCTIONS_H_ + +#include + +#include + +void remove_unused_functions( + goto_functionst &functions, + message_handlert &message_handler); + +void find_used_functions( + const irep_idt ¤t, + goto_functionst &functions, + std::set &seen); + +#endif /*_CPROVER_LOOPFROG_REMOVE_FUNCTIONS_H_*/ diff --git a/src/goto-programs/rw_set.cpp b/src/goto-programs/rw_set.cpp new file mode 100644 index 00000000000..957b799c5b5 --- /dev/null +++ b/src/goto-programs/rw_set.cpp @@ -0,0 +1,155 @@ +/*******************************************************************\ + +Module: Race Detection for Threaded Goto Programs + +Author: Daniel Kroening + +Date: February 2006 + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include "rw_set.h" + +/*******************************************************************\ + +Function: rw_sett::compute + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void rw_sett::compute(const codet &code) +{ + const irep_idt &statement=code.get_statement(); + + if(statement==ID_assign) + { + assert(code.operands().size()==2); + assign(code.op0(), code.op1()); + } +} + +/*******************************************************************\ + +Function: rw_sett::assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void rw_sett::assign(const exprt &lhs, const exprt &rhs) +{ + read(rhs); + read_write_rec(lhs, false, true, "", guardt()); +} + +/*******************************************************************\ + +Function: rw_sett::read_write_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void rw_sett::read_write_rec( + const exprt &expr, + bool r, bool w, + const std::string &suffix, + const guardt &guard) +{ + if(expr.id()==ID_symbol) + { + const symbol_exprt &symbol_expr=to_symbol_expr(expr); + + const symbolt *symbol; + if(!ns.lookup(symbol_expr.get_identifier(), symbol)) + { + if(!symbol->static_lifetime) + return; // ignore for now + + if(symbol->thread_local) + return; // must ignore + + if(symbol->name=="c::__CPROVER_alloc" || + symbol->name=="c::__CPROVER_alloc_size" || + symbol->name=="c::stdin" || + symbol->name=="c::stdout" || + symbol->name=="c::stderr" || + symbol->name=="c::sys_nerr") + return; // ignore for now + } + + irep_idt object=id2string(symbol_expr.get_identifier())+suffix; + + entryt &entry=entries[object]; + entry.object=object; + entry.r=entry.r || r; + entry.w=entry.w || w; + entry.guard=guard.as_expr(); + } + else if(expr.id()=="member") + { + assert(expr.operands().size()==1); + const std::string &component_name=expr.get_string("component_name"); + read_write_rec(expr.op0(), r, w, "."+component_name+suffix, guard); + } + else if(expr.id()=="index") + { + // we don't distinguish the array elements for now + assert(expr.operands().size()==2); + read_write_rec(expr.op0(), r, w, "[]"+suffix, guard); + read(expr.op1(), guard); + } + else if(expr.id()=="dereference") + { + assert(expr.operands().size()==1); + read(expr.op0(), guard); + + exprt tmp(expr.op0()); + dereference(target, tmp, ns, value_sets); + + read_write_rec(tmp, r, w, suffix, guard); + } + else if(expr.id()=="address_of" || + expr.id()=="implicit_address_of") + { + assert(expr.operands().size()==1); + + } + else if(expr.id()=="if") + { + assert(expr.operands().size()==3); + read(expr.op0(), guard); + + guardt true_guard(guard); + true_guard.add(expr.op0()); + read_write_rec(expr.op1(), r, w, suffix, true_guard); + + guardt false_guard(guard); + false_guard.add(gen_not(expr.op0())); + read_write_rec(expr.op2(), r, w, suffix, false_guard); + } + else + { + forall_operands(it, expr) + read_write_rec(*it, r, w, suffix, guard); + } +} diff --git a/src/goto-programs/rw_set.h b/src/goto-programs/rw_set.h new file mode 100644 index 00000000000..d93a3b5d348 --- /dev/null +++ b/src/goto-programs/rw_set.h @@ -0,0 +1,107 @@ +/*******************************************************************\ + +Module: Race Detection for Threaded Goto Programs + +Author: Daniel Kroening + +Date: February 2006 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_RW_SET +#define CPROVER_GOTO_PROGRAMS_RW_SET + +#include +#include +#include +#include +#include + +#include + +class rw_sett +{ +public: + struct entryt + { + irep_idt object; + bool r, w; + exprt guard; + + entryt():r(false), w(false), + guard(true_exprt()) + { + } + + const exprt &get_guard() const + { + return guard; + } + + std::string get_comment() const + { + std::string result; + if(w) + result="W/W"; + else + result="R/W"; + + result+=" data race on "; + result+=id2string(object); + return result; + } + }; + + typedef hash_map_cont entriest; + entriest entries; + + void compute(const codet &code); + + rw_sett(const namespacet &_ns, + value_setst &_value_sets, + goto_programt::const_targett _target): + ns(_ns), + value_sets(_value_sets), + target(_target) + { + } + + rw_sett(const namespacet &_ns, + value_setst &_value_sets, + goto_programt::const_targett _target, + const codet &code):ns(_ns), + value_sets(_value_sets), + target(_target) + { + compute(code); + } + + void read(const exprt &expr) + { + read_write_rec(expr, true, false, "", guardt()); + } + + void read(const exprt &expr, const guardt &guard) + { + read_write_rec(expr, true, false, "", guard); + } + +protected: + const namespacet &ns; + value_setst &value_sets; + const goto_programt::const_targett target; + + void assign(const exprt &lhs, const exprt &rhs); + + void read_write_rec( + const exprt &expr, + bool r, bool w, + const std::string &suffix, + const guardt &guard); +}; + +#define forall_rw_set_entries(it, rw_set) \ + for(rw_sett::entriest::const_iterator it=(rw_set).entries.begin(); \ + it!=(rw_set).entries.end(); it++) + +#endif diff --git a/src/goto-programs/safety_checker.cpp b/src/goto-programs/safety_checker.cpp new file mode 100644 index 00000000000..71ce29d729b --- /dev/null +++ b/src/goto-programs/safety_checker.cpp @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: Safety Checker Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "safety_checker.h" + +/*******************************************************************\ + +Function: safety_checkert::safety_checkert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +safety_checkert::safety_checkert(const namespacet &_ns): + ns(_ns) +{ +} + diff --git a/src/goto-programs/safety_checker.h b/src/goto-programs/safety_checker.h new file mode 100644 index 00000000000..db820675946 --- /dev/null +++ b/src/goto-programs/safety_checker.h @@ -0,0 +1,41 @@ +/*******************************************************************\ + +Module: Safety Checker Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SAFETY_CHECKER_H +#define CPROVER_SAFETY_CHECKER_H + +// this is just an interface -- it won't actually do any checking! + +#include + +#include + +#include "goto_functions.h" + +class safety_checkert:public messaget +{ +public: + explicit safety_checkert(const namespacet &_ns); + + typedef enum { SAFE, UNSAFE, ERROR } resultt; + + // check whether all assertions in goto_functions are safe + // if UNSAFE, then a trace is returned + + virtual resultt operator()( + const goto_functionst &goto_functions)=0; + + // this is the counterexample + goto_tracet error_trace; + +protected: + // the namespace + namespacet ns; +}; + +#endif diff --git a/src/goto-programs/set_claims.cpp b/src/goto-programs/set_claims.cpp new file mode 100644 index 00000000000..88dc91b74df --- /dev/null +++ b/src/goto-programs/set_claims.cpp @@ -0,0 +1,122 @@ +/*******************************************************************\ + +Module: Set Claims + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "set_claims.h" + +/*******************************************************************\ + +Function: set_claims + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void set_claims( + goto_programt &goto_program, + std::map &claim_counters, + hash_set_cont &claim_set) +{ + for(goto_programt::instructionst::iterator + it=goto_program.instructions.begin(); + it!=goto_program.instructions.end(); + it++) + { + if(!it->is_assert()) continue; + + irep_idt function=it->location.get_function(); + unsigned &count=claim_counters[function]; + + count++; + + std::string claim_name= + function==""?i2string(count): + id2string(function)+"."+i2string(count); + + hash_set_cont::iterator + c_it=claim_set.find(claim_name); + + if(c_it==claim_set.end()) + it->type=SKIP; + else + claim_set.erase(c_it); + } +} + +/*******************************************************************\ + +Function: set_claims + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void set_claims( + goto_programt &goto_program, + const std::list &claims) +{ + hash_set_cont claim_set; + std::map claim_counters; + + for(std::list::const_iterator + it=claims.begin(); + it!=claims.end(); + it++) + claim_set.insert(*it); + + set_claims(goto_program, claim_counters, claim_set); + + if(!claim_set.empty()) + throw "claim "+(*claim_set.begin())+" not found"; +} + +/*******************************************************************\ + +Function: set_claims + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void set_claims( + goto_functionst &goto_functions, + const std::list &claims) +{ + hash_set_cont claim_set; + std::map claim_counters; + + for(std::list::const_iterator + it=claims.begin(); + it!=claims.end(); + it++) + claim_set.insert(*it); + + for(goto_functionst::function_mapt::iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + if(!it->second.is_inlined()) + set_claims(it->second.body, claim_counters, claim_set); + + if(!claim_set.empty()) + throw "claim "+(*claim_set.begin())+" not found"; +} diff --git a/src/goto-programs/set_claims.h b/src/goto-programs/set_claims.h new file mode 100644 index 00000000000..29a35df81d9 --- /dev/null +++ b/src/goto-programs/set_claims.h @@ -0,0 +1,22 @@ +/*******************************************************************\ + +Module: Set claims + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_SET_CLAIMS_H +#define CPROVER_GOTO_PROGRAMS_SET_CLAIMS_H + +#include + +void set_claims( + goto_functionst &goto_functions, + const std::list &claims); + +void set_claims( + goto_programt &goto_program, + const std::list &claims); + +#endif diff --git a/src/goto-programs/show_claims.cpp b/src/goto-programs/show_claims.cpp new file mode 100644 index 00000000000..eff0fa92bad --- /dev/null +++ b/src/goto-programs/show_claims.cpp @@ -0,0 +1,146 @@ +/*******************************************************************\ + +Module: Show Claims + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include "show_claims.h" + +/*******************************************************************\ + +Function: cbmc_parseoptionst::show_claims + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_claims( + const namespacet &ns, + const irep_idt &identifier, + ui_message_handlert::uit ui, + const goto_programt &goto_program, + std::map &claim_counter) +{ + for(goto_programt::instructionst::const_iterator + it=goto_program.instructions.begin(); + it!=goto_program.instructions.end(); + it++) + { + if(!it->is_assert()) + continue; + + const locationt &location=it->location; + + const irep_idt &comment=location.get_comment(); + const irep_idt &function=location.get_function(); + const irep_idt &property=location.get_property(); + const irep_idt description= + (comment==""?"assertion":comment); + + unsigned &count=claim_counter[function]; + + count++; + + std::string claim_name= + function==""?i2string(count): + id2string(function)+"."+i2string(count); + + switch(ui) + { + case ui_message_handlert::XML_UI: + { + xmlt xml("claim"); + xml.new_element("number").data=claim_name; + xml.new_element("name").data=claim_name; + + xmlt &l=xml.new_element(); + convert(it->location, l); + l.name="location"; + + l.new_element("line").data=id2string(location.get_line()); + l.new_element("file").data=id2string(location.get_file()); + l.new_element("function").data=id2string(location.get_function()); + + xml.new_element("description").data=id2string(description); + xml.new_element("property").data=id2string(property); + xml.new_element("expression").data=from_expr(ns, identifier, it->guard); + + std::cout << xml << std::endl; + } + break; + + case ui_message_handlert::PLAIN: + std::cout << "Claim " << claim_name << ":" << std::endl; + + std::cout << " " << it->location << std::endl + << " " << description << std::endl + << " " << from_expr(ns, identifier, it->guard) + << std::endl + << std::endl; + break; + + default: + assert(false); + } + } +} + +/*******************************************************************\ + +Function: cbmc_parseoptionst::show_claims + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_claims( + const namespacet &ns, + ui_message_handlert::uit ui, + const goto_programt &goto_program) +{ + std::map count; + show_claims(ns, "", ui, goto_program, count); +} + +/*******************************************************************\ + +Function: show_claims + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_claims( + const namespacet &ns, + ui_message_handlert::uit ui, + const goto_functionst &goto_functions) +{ + std::map count; + + for(goto_functionst::function_mapt::const_iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + if(!it->second.is_inlined()) + show_claims(ns, it->first, ui, it->second.body, count); +} diff --git a/src/goto-programs/show_claims.h b/src/goto-programs/show_claims.h new file mode 100644 index 00000000000..52e0ce66b50 --- /dev/null +++ b/src/goto-programs/show_claims.h @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: Show claims + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_SHOW_CLAIMS_H +#define CPROVER_GOTO_PROGRAMS_SHOW_CLAIMS_H + +#include +#include + +#include + +void show_claims( + const namespacet &ns, + ui_message_handlert::uit ui, + const goto_functionst &goto_functions); + +void show_claims( + const namespacet &ns, + ui_message_handlert::uit ui, + const goto_programt &goto_program); + +#endif diff --git a/src/goto-programs/slicer.cpp b/src/goto-programs/slicer.cpp new file mode 100644 index 00000000000..36fbcf44a00 --- /dev/null +++ b/src/goto-programs/slicer.cpp @@ -0,0 +1,205 @@ +/*******************************************************************\ + +Module: Slicer + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "slicer.h" +#include "remove_skip.h" +#include "remove_unreachable.h" +#include "cfg.h" +#include "slicer_class.h" + +/*******************************************************************\ + +Function: slicert::fixedpoint_assertions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void slicert::fixedpoint_assertions() +{ + queuet queue; + + for(cfgt::entry_mapt::iterator + e_it=cfg.entry_map.begin(); + e_it!=cfg.entry_map.end(); + e_it++) + if(e_it->first->is_assert() || + e_it->second.threaded) + queue.push(&e_it->second); + + while(!queue.empty()) + { + cfgt::iterator e=queue.top(); + queue.pop(); + + if(e->reaches_assertion) continue; + + e->reaches_assertion=true; + + for(cfgt::entriest::const_iterator + p_it=e->predecessors.begin(); + p_it!=e->predecessors.end(); + p_it++) + { + queue.push(*p_it); + } + } +} + +/*******************************************************************\ + +Function: slicert::fixedpoint_threads + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void slicert::fixedpoint_threads() +{ + queuet queue; + + for(cfgt::entry_mapt::iterator + e_it=cfg.entry_map.begin(); + e_it!=cfg.entry_map.end(); + e_it++) + if(e_it->first->is_start_thread()) + queue.push(&e_it->second); + + while(!queue.empty()) + { + cfgt::iterator e=queue.top(); + queue.pop(); + + if(e->threaded) continue; + + e->threaded=true; + + for(cfgt::entriest::const_iterator + p_it=e->successors.begin(); + p_it!=e->successors.end(); + p_it++) + { + queue.push(*p_it); + } + } +} + +/*******************************************************************\ + +Function: slicert::slice + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void slicert::slice(goto_functionst &goto_functions) +{ + // now replace those instructions that do not reach any assertions + // by self-loops + + Forall_goto_functions(f_it, goto_functions) + if(f_it->second.body_available) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + const cfgt::entryt &e=cfg.entry_map[i_it]; + if(!e.reaches_assertion && + !i_it->is_end_function()) + i_it->make_goto(i_it); + } + + // replace unreachable code by skip + remove_unreachable(f_it->second.body); + } + + // remove the skips + remove_skip(goto_functions); + goto_functions.update(); +} + +/*******************************************************************\ + +Function: slicert::slice + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void slicert::slice(goto_programt &goto_program) +{ + // now replace those instructions that do not reach any assertions + // by assume(false) + + Forall_goto_program_instructions(it, goto_program) + { + const cfgt::entryt &e=cfg.entry_map[it]; + if(!e.reaches_assertion) + it->make_assumption(false_exprt()); + } + + // replace unreachable code by skip + remove_unreachable(goto_program); + + // remove the skips + remove_skip(goto_program); + goto_program.update(); +} + +/*******************************************************************\ + +Function: slicer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void slicer(goto_programt &goto_program) +{ + slicert()(goto_program); +} + +/*******************************************************************\ + +Function: slicer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void slicer(goto_functionst &goto_functions) +{ + slicert()(goto_functions); +} diff --git a/src/goto-programs/slicer.h b/src/goto-programs/slicer.h new file mode 100644 index 00000000000..ad7e958c478 --- /dev/null +++ b/src/goto-programs/slicer.h @@ -0,0 +1,17 @@ +/*******************************************************************\ + +Module: Slicing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_SLICER_H +#define CPROVER_GOTO_PROGRAMS_SLICER_H + +#include "goto_functions.h" + +void slicer(goto_functionst &goto_functions); +void slicer(goto_programt &goto_program); + +#endif diff --git a/src/goto-programs/slicer_class.h b/src/goto-programs/slicer_class.h new file mode 100644 index 00000000000..0b72dbc17ce --- /dev/null +++ b/src/goto-programs/slicer_class.h @@ -0,0 +1,69 @@ +/*******************************************************************\ + +Module: Goto Program Slicing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAM_SLICER_CLASS_H +#define CPROVER_GOTO_PROGRAM_SLICER_CLASS_H + +#include "goto_functions.h" +#include "cfg.h" + +/*******************************************************************\ + + Class: slicert + + Purpose: + +\*******************************************************************/ + +class slicert +{ +public: + void operator()(goto_functionst &goto_functions) + { + cfg(goto_functions); + fixedpoints(); + slice(goto_functions); + } + + void operator()(goto_programt &goto_program) + { + cfg(goto_program); + fixedpoints(); + slice(goto_program); + } + +protected: + struct slicer_entryt + { + slicer_entryt():reaches_assertion(false), threaded(false) + { + } + + bool reaches_assertion, threaded; + }; + + typedef cfg_baset cfgt; + cfgt cfg; + + typedef std::stack queuet; + + void fixedpoint_assertions(); + void fixedpoint_threads(); + + void fixedpoints() + { + // do threads first + fixedpoint_threads(); + fixedpoint_assertions(); + } + + void slice(goto_programt &goto_program); + void slice(goto_functionst &goto_functions); +}; + +#endif diff --git a/src/goto-programs/static_analysis.cpp b/src/goto-programs/static_analysis.cpp new file mode 100644 index 00000000000..5572fda329d --- /dev/null +++ b/src/goto-programs/static_analysis.cpp @@ -0,0 +1,651 @@ +/*******************************************************************\ + +Module: Value Set Propagation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include +#include +#include + +#include "static_analysis.h" + +/*******************************************************************\ + +Function: static_analysis_baset::get_guard + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt static_analysis_baset::get_guard( + locationt from, + locationt to) +{ + if(!from->is_goto()) + return true_exprt(); + + locationt next=from; + next++; + + if(next==to) + { + exprt tmp(from->guard); + tmp.make_not(); + return tmp; + } + + return from->guard; +} + +/*******************************************************************\ + +Function: static_analysis_baset::get_return_lhs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt static_analysis_baset::get_return_lhs(locationt to) +{ + // get predecessor of "to" + + to--; + + if(to->is_end_function()) + return static_cast(get_nil_irep()); + + // must be the function call + assert(to->is_function_call()); + + const code_function_callt &code= + to_code_function_call(to->code); + + return code.lhs(); +} + +/*******************************************************************\ + +Function: static_analysis_baset::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::operator()( + const goto_functionst &goto_functions) +{ + initialize(goto_functions); + fixedpoint(goto_functions); +} + +/*******************************************************************\ + +Function: static_analysis_baset::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::operator()( + const goto_programt &goto_program) +{ + initialize(goto_program); + goto_functionst goto_functions; + fixedpoint(goto_program, goto_functions); +} + +/*******************************************************************\ + +Function: static_analysis_baset::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::output( + const goto_functionst &goto_functions, + std::ostream &out) const +{ + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + { + if(f_it->second.body_available) + { + out << "////" << std::endl; + out << "//// Function: " << f_it->first << std::endl; + out << "////" << std::endl; + out << std::endl; + + output(f_it->second.body, f_it->first, out); + } + } +} + +/*******************************************************************\ + +Function: static_analysis_baset::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::output( + const goto_programt &goto_program, + const irep_idt &identifier, + std::ostream &out) const +{ + forall_goto_program_instructions(i_it, goto_program) + { + out << "**** " << i_it->location << std::endl; + + get_state(i_it).output(ns, out); + out << std::endl; + goto_program.output_instruction(ns, identifier, out, i_it); + out << std::endl; + } +} + +/*******************************************************************\ + +Function: static_analysis_baset::generate_states + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::generate_states( + const goto_functionst &goto_functions) +{ + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + generate_states(f_it->second.body); +} + +/*******************************************************************\ + +Function: static_analysis_baset::generate_states + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::generate_states( + const goto_programt &goto_program) +{ + forall_goto_program_instructions(i_it, goto_program) + generate_state(i_it); +} + +/*******************************************************************\ + +Function: static_analysis_baset::update + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::update( + const goto_functionst &goto_functions) +{ + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + update(f_it->second.body); +} + +/*******************************************************************\ + +Function: static_analysis_baset::generate_states + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::update( + const goto_programt &goto_program) +{ + locationt previous; + bool first=true; + + forall_goto_program_instructions(i_it, goto_program) + { + // do we have it already? + if(!has_location(i_it)) + { + generate_state(i_it); + + if(!first) + merge(get_state(i_it), get_state(previous)); + } + + first=false; + previous=i_it; + } +} + +/*******************************************************************\ + +Function: static_analysis_baset::get_next + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static_analysis_baset::locationt static_analysis_baset::get_next( + working_sett &working_set) +{ + assert(!working_set.empty()); + + working_sett::iterator i=working_set.begin(); + locationt l=i->second; + working_set.erase(i); + + return l; +} + +/*******************************************************************\ + +Function: static_analysis_baset::fixedpoint + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool static_analysis_baset::fixedpoint( + const goto_programt &goto_program, + const goto_functionst &goto_functions) +{ + if(goto_program.instructions.empty()) + return false; + + working_sett working_set; + + put_in_working_set( + working_set, + goto_program.instructions.begin()); + + bool new_data=false; + + while(!working_set.empty()) + { + locationt l=get_next(working_set); + + if(visit(l, working_set, goto_program, goto_functions)) + new_data=true; + } + + return new_data; +} + +/*******************************************************************\ + +Function: static_analysis_baset::visit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool static_analysis_baset::visit( + locationt l, + working_sett &working_set, + const goto_programt &goto_program, + const goto_functionst &goto_functions) +{ + bool new_data=false; + + statet ¤t=get_state(l); + + current.seen=true; + + goto_programt::const_targetst successors; + + goto_program.get_successors(l, successors); + + for(goto_programt::const_targetst::const_iterator + it=successors.begin(); + it!=successors.end(); + it++) + { + locationt to_l=*it; + + if(to_l==goto_program.instructions.end()) + continue; + + std::auto_ptr tmp_state( + make_temporary_state(current)); + + statet &new_values=*tmp_state; + + if(l->is_function_call()) + { + // this is a big special case + const code_function_callt &code= + to_code_function_call(l->code); + + do_function_call_rec( + l, + code.function(), + code.arguments(), + new_values, + goto_functions); + } + else + new_values.transform(ns, l, to_l); + + statet &other=get_state(to_l); + + bool have_new_values= + merge(other, new_values); + + if(have_new_values) + new_data=true; + + if(have_new_values || !other.seen) + put_in_working_set(working_set, to_l); + } + + return new_data; +} + +/*******************************************************************\ + +Function: static_analysis_baset::do_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::do_function_call( + locationt l_call, + const goto_functionst &goto_functions, + const goto_functionst::function_mapt::const_iterator f_it, + const exprt::operandst &arguments, + statet &new_state) +{ + const goto_functionst::goto_functiont &goto_function=f_it->second; + + if(!goto_function.body_available) + return; // do nothing + + assert(!goto_function.body.instructions.empty()); + + { + // get the state at the beginning of the function + locationt l_begin=goto_function.body.instructions.begin(); + + // do the edge from the call site to the beginning of the function + new_state.transform(ns, l_call, l_begin); + + statet &begin_state=get_state(l_begin); + + bool new_data=false; + + // merge the new stuff + if(merge(begin_state, new_state)) + new_data=true; + + // do each function at least once + if(functions_done.find(f_it->first)== + functions_done.end()) + { + new_data=true; + functions_done.insert(f_it->first); + } + + // do we need to do the fixedpoint of the body? + if(new_data) + { + // recursive call + fixedpoint(goto_function.body, goto_functions); + } + } + + { + // get location at end of procedure + locationt l_end=--goto_function.body.instructions.end(); + + assert(l_end->is_end_function()); + + statet &end_of_function=get_state(l_end); + + // do edge from end of function to instruction after call + locationt l_next=l_call; + l_next++; + end_of_function.transform(ns, l_end, l_next); + + // propagate those -- not exceedingly precise, this is, + // as still it contains all the state from the + // call site + merge(new_state, end_of_function); + } +} + +/*******************************************************************\ + +Function: static_analysis_baset::do_function_call_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::do_function_call_rec( + locationt l_call, + const exprt &function, + const exprt::operandst &arguments, + statet &new_state, + const goto_functionst &goto_functions) +{ + // see if we have the functions at all + if(goto_functions.function_map.empty()) + return; + + if(function.id()==ID_symbol) + { + const irep_idt &identifier=function.get(ID_identifier); + + if(recursion_set.find(identifier)!=recursion_set.end()) + { + // recursion detected! + return; + } + else + recursion_set.insert(identifier); + + goto_functionst::function_mapt::const_iterator it= + goto_functions.function_map.find(identifier); + + if(it==goto_functions.function_map.end()) + throw "failed to find function "+id2string(identifier); + + do_function_call( + l_call, + goto_functions, + it, + arguments, + new_state); + + recursion_set.erase(identifier); + } + else if(function.id()==ID_if) + { + if(function.operands().size()!=3) + throw "if takes three arguments"; + + std::auto_ptr n2(make_temporary_state(new_state)); + + do_function_call_rec( + l_call, + function.op1(), + arguments, + new_state, + goto_functions); + + do_function_call_rec( + l_call, + function.op2(), + arguments, + *n2, + goto_functions); + + merge(new_state, *n2); + } + else if(function.id()==ID_dereference) + { + // get value set + std::list values; + get_reference_set(l_call, function, values); + + std::auto_ptr state_from(make_temporary_state(new_state)); + + // now call all of these + for(std::list::const_iterator it=values.begin(); + it!=values.end(); + it++) + { + if(it->id()==ID_object_descriptor) + { + const object_descriptor_exprt &o=to_object_descriptor_expr(*it); + std::auto_ptr n2(make_temporary_state(new_state)); + do_function_call_rec(l_call, o.object(), arguments, *n2, goto_functions); + merge(new_state, *n2); + } + } + } + else if(function.id()=="NULL-object") + { + // ignore, can't be a function + } + else if(function.id()==ID_member || function.id()==ID_index) + { + // ignore, can't be a function + } + else if(function.id()=="builtin-function") + { + // ignore, someone else needs to worry about this + } + else + { + throw "unexpected function_call argument: "+ + function.id_string(); + } +} + +/*******************************************************************\ + +Function: static_analysis_baset::fixedpoint + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void static_analysis_baset::fixedpoint( + const goto_functionst &goto_functions) +{ + // do each function at least once + + for(goto_functionst::function_mapt::const_iterator + it=goto_functions.function_map.begin(); + it!=goto_functions.function_map.end(); + it++) + if(functions_done.find(it->first)== + functions_done.end()) + { + fixedpoint(it, goto_functions); + } +} + +/*******************************************************************\ + +Function: static_analysis_baset::fixedpoint + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool static_analysis_baset::fixedpoint( + const goto_functionst::function_mapt::const_iterator it, + const goto_functionst &goto_functions) +{ + functions_done.insert(it->first); + return fixedpoint(it->second.body, goto_functions); +} diff --git a/src/goto-programs/static_analysis.h b/src/goto-programs/static_analysis.h new file mode 100644 index 00000000000..48241b267bc --- /dev/null +++ b/src/goto-programs/static_analysis.h @@ -0,0 +1,337 @@ +/*******************************************************************\ + +Module: Static Analysis + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_STATIC_ANALYSIS_H +#define CPROVER_GOTO_PROGRAMS_STATIC_ANALYSIS_H + +#include +#include + +#include "goto_functions.h" + +// don't use me -- I am just a base class +// please derive from me +class domain_baset +{ +public: + domain_baset():seen(false) + { + } + + typedef goto_programt::const_targett locationt; + + // will go away + virtual void initialize( + const namespacet &ns, + locationt l) + { + } + + // how function calls are treated: + // a) there is an edge from each call site to the function head + // b) there is an edge from each return to the last instruction (END_FUNCTION) + // of each function + // c) there is an edge from the last instruction of the function + // to the instruction following the call site + // (for setting the LHS) + + virtual void transform( + const namespacet &ns, + locationt from, + locationt to)=0; + + virtual ~domain_baset() + { + } + + virtual void output( + const namespacet &ns, + std::ostream &out) const + { + } + + typedef hash_set_cont expr_sett; + + // will go away + virtual void get_reference_set( + const namespacet &ns, + const exprt &expr, + std::list &dest) + { + // dummy, overload me! + dest.clear(); + } + + // also add + // + // bool merge(const T &b); + // + // this computes the join between "this" and b + // return true if "this" has changed + +protected: + bool seen; + + friend class static_analysis_baset; +}; + +// don't use me -- I am just a base class +// use static_analysist instead +class static_analysis_baset +{ +public: + typedef domain_baset statet; + typedef goto_programt::const_targett locationt; + + static_analysis_baset(const namespacet &_ns): + ns(_ns), + initialized(false) + { + } + + virtual void initialize( + const goto_programt &goto_program) + { + if(!initialized) + { + initialized=true; + generate_states(goto_program); + } + } + + virtual void initialize( + const goto_functionst &goto_functions) + { + if(!initialized) + { + initialized=true; + generate_states(goto_functions); + } + } + + virtual void update(const goto_programt &goto_program); + virtual void update(const goto_functionst &goto_functions); + + virtual void operator()( + const goto_programt &goto_program); + + virtual void operator()( + const goto_functionst &goto_functions); + + virtual ~static_analysis_baset() + { + } + + virtual void clear() + { + initialized=false; + } + + virtual void output( + const goto_functionst &goto_functions, + std::ostream &out) const; + + void output( + const goto_programt &goto_program, + std::ostream &out) const + { + output(goto_program, "", out); + } + + virtual bool has_location(locationt l) const=0; + + void insert(locationt l) + { + generate_state(l); + } + + // utilities + + // get guard of a conditional edge + static exprt get_guard(locationt from, locationt to); + + // get lhs that return value is assigned to + // for an edge that returns from a function + static exprt get_return_lhs(locationt to); + +protected: + const namespacet &ns; + + virtual void output( + const goto_programt &goto_program, + const irep_idt &identifier, + std::ostream &out) const; + + typedef std::map working_sett; + + locationt get_next(working_sett &working_set); + + void put_in_working_set( + working_sett &working_set, + locationt l) + { + working_set.insert( + std::pair(l->location_number, l)); + } + + // true = found s.th. new + bool fixedpoint( + const goto_programt &goto_program, + const goto_functionst &goto_functions); + + bool fixedpoint( + goto_functionst::function_mapt::const_iterator it, + const goto_functionst &goto_functions); + + void fixedpoint( + const goto_functionst &goto_functions); + + // true = found s.th. new + bool visit( + locationt l, + working_sett &working_set, + const goto_programt &goto_program, + const goto_functionst &goto_functions); + + static locationt successor(locationt l) + { + l++; + return l; + } + + virtual bool merge(statet &a, const statet &b)=0; + + typedef std::set functions_donet; + functions_donet functions_done; + + typedef std::set recursion_sett; + recursion_sett recursion_set; + + void generate_states( + const goto_functionst &goto_functions); + + void generate_states( + const goto_programt &goto_program); + + bool initialized; + + // function calls + void do_function_call_rec( + locationt l_call, + const exprt &function, + const exprt::operandst &arguments, + statet &new_state, + const goto_functionst &goto_functions); + + void do_function_call( + locationt l_call, + const goto_functionst &goto_functions, + const goto_functionst::function_mapt::const_iterator f_it, + const exprt::operandst &arguments, + statet &new_state); + + // abstract methods + + virtual void generate_state(locationt l)=0; + virtual statet &get_state(locationt l)=0; + virtual const statet &get_state(locationt l) const=0; + virtual statet* make_temporary_state(statet &s)=0; + + typedef domain_baset::expr_sett expr_sett; + + virtual void get_reference_set( + locationt l, + const exprt &expr, + std::list &dest)=0; +}; + +// T is expected to be derived from domain_baset +template +class static_analysist:public static_analysis_baset +{ +public: + // constructor + static_analysist(const namespacet &_ns): + static_analysis_baset(_ns) + { + } + + typedef goto_programt::const_targett locationt; + + inline T &operator[](locationt l) + { + typename state_mapt::iterator it=state_map.find(l); + if(it==state_map.end()) throw "failed to find state"; + return it->second; + } + + inline const T &operator[](locationt l) const + { + typename state_mapt::const_iterator it=state_map.find(l); + if(it==state_map.end()) throw "failed to find state"; + return it->second; + } + + virtual void clear() + { + state_map.clear(); + static_analysis_baset::clear(); + } + + virtual bool has_location(locationt l) const + { + return state_map.count(l)!=0; + } + +protected: + typedef std::map state_mapt; + state_mapt state_map; + + virtual statet &get_state(locationt l) + { + typename state_mapt::iterator it=state_map.find(l); + if(it==state_map.end()) throw "failed to find state"; + return it->second; + } + + virtual const statet &get_state(locationt l) const + { + typename state_mapt::const_iterator it=state_map.find(l); + if(it==state_map.end()) throw "failed to find state"; + return it->second; + } + + virtual bool merge(statet &a, const statet &b) + { + return static_cast(a).merge(static_cast(b)); + } + + virtual statet *make_temporary_state(statet &s) + { + return new T(static_cast(s)); + } + + virtual void generate_state(locationt l) + { + state_map[l].initialize(ns, l); + } + + virtual void get_reference_set( + locationt l, + const exprt &expr, + std::list &dest) + { + state_map[l].get_reference_set(ns, expr, dest); + } + +private: + // to enforce that T is derived from domain_baset + void dummy(const T &s) { const statet &x=dummy1(s); } +}; + +#endif diff --git a/src/goto-programs/string_abstraction.cpp b/src/goto-programs/string_abstraction.cpp new file mode 100644 index 00000000000..42cebf602e2 --- /dev/null +++ b/src/goto-programs/string_abstraction.cpp @@ -0,0 +1,1192 @@ +/*******************************************************************\ + +Module: String Abstraction + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "pointer_arithmetic.h" +#include "string_abstraction.h" + +/*******************************************************************\ + +Function: string_abstraction + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstraction( + contextt &context, + message_handlert &message_handler, + goto_programt &dest) +{ + string_abstractiont string_abstraction(context, message_handler); + string_abstraction(dest); +} + +/*******************************************************************\ + +Function: string_abstraction + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstraction( + contextt &context, + message_handlert &message_handler, + goto_functionst &dest) +{ + string_abstractiont string_abstraction(context, message_handler); + string_abstraction(dest); +} + +/*******************************************************************\ + +Function: string_abstractiont::string_abstractiont + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +string_abstractiont::string_abstractiont( + contextt &_context, + message_handlert &_message_handler): + message_streamt(_message_handler), + context(_context), + ns(_context) +{ + struct_typet s; + + s.components().resize(3); + + s.components()[0].set_name("is_zero"); + s.components()[0].set_pretty_name("is_zero"); + s.components()[0].type()=build_type(IS_ZERO); + + s.components()[1].set_name("length"); + s.components()[1].set_pretty_name("length"); + s.components()[1].type()=build_type(LENGTH); + + s.components()[2].set_name("size"); + s.components()[2].set_pretty_name("size"); + s.components()[2].type()=build_type(SIZE); + + string_struct=s; +} + +/*******************************************************************\ + +Function: string_abstractiont::member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::member(const exprt &a, whatt what) +{ + if(a.is_nil()) return a; + + member_exprt result; + result.type()=build_type(what); + result.struct_op()=a; + + switch(what) + { + case IS_ZERO: result.set_component_name("is_zero"); break; + case SIZE: result.set_component_name("size"); break; + case LENGTH: result.set_component_name("length"); break; + default: assert(false); + } + + return result; +} + +/*******************************************************************\ + +Function: string_abstractiont::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::operator()(goto_functionst &dest) +{ + for(goto_functionst::function_mapt::iterator + it=dest.function_map.begin(); + it!=dest.function_map.end(); + it++) + abstract(it->second.body); + + // do we have a main? + goto_functionst::function_mapt::iterator + m_it=dest.function_map.find(dest.main_id()); + + if(m_it!=dest.function_map.end()) + { + goto_programt &main=m_it->second.body; + + // do initialization + initialization.destructive_append(main); + main.swap(initialization); + initialization.clear(); + } +} + +/*******************************************************************\ + +Function: string_abstractiont::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::operator()(goto_programt &dest) +{ + abstract(dest); + + // do initialization + initialization.destructive_append(dest); + dest.swap(initialization); + initialization.clear(); +} + +/*******************************************************************\ + +Function: string_abstractiont::abstract + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::abstract(goto_programt &dest) +{ + locals.clear(); + + Forall_goto_program_instructions(it, dest) + abstract(dest, it); + + // go over it again for the newly added locals + if(!locals.empty()) + { + Forall_goto_program_instructions(it, dest) + { + // do declaration/initializations of those locals + if(it->is_decl()) + { + assert(it->code.get(ID_statement)==ID_decl); + assert(it->code.operands().size()==1); + assert(it->code.op0().id()==ID_symbol); + + const irep_idt &identifier= + to_symbol_expr(it->code.op0()).get_identifier(); + + localst::const_iterator l_it=locals.find(identifier); + if(l_it==locals.end()) continue; + + const symbolt &symbol=ns.lookup(l_it->second); + + // do declaration + goto_programt tmp; + + goto_programt::targett decl1=tmp.add_instruction(); + decl1->make_decl(); + decl1->code=code_declt(symbol_expr(symbol)); + decl1->location=it->location; + + if(symbol.value.is_not_nil()) + { + // initialization + goto_programt::targett assignment1=tmp.add_instruction(ASSIGN); + assignment1->code=code_assignt(symbol_expr(symbol), symbol.value); + assignment1->location=it->location; + } + + goto_programt::targett it_next=it; + it_next++; + + dest.destructive_insert(it_next, tmp); + } + } + } + + locals.clear(); +} + +/*******************************************************************\ + +Function: string_abstractiont::abstract + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::abstract( + goto_programt &dest, + goto_programt::targett it) +{ + switch(it->type) + { + case ASSIGN: + abstract_assign(dest, it); + break; + + case GOTO: + case ASSERT: + case ASSUME: + if(has_string_macros(it->guard)) + replace_string_macros(it->guard, false, it->location); + break; + + case FUNCTION_CALL: + abstract_function_call(dest, it); + break; + + case RETURN: + if(it->code.operands().size()!=0) + replace_string_macros(it->code.op0(), false, it->location); + break; + + default:; + } +} + +/*******************************************************************\ + +Function: string_abstractiont::has_string_macros + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool string_abstractiont::has_string_macros(const exprt &expr) +{ + if(expr.id()=="is_zero_string" || + expr.id()=="zero_string_length" || + expr.id()=="buffer_size") + return true; + + forall_operands(it, expr) + if(has_string_macros(*it)) + return true; + + return false; +} + +/*******************************************************************\ + +Function: string_abstractiont::replace_string_macros + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::replace_string_macros( + exprt &expr, + bool lhs, + const locationt &location) +{ + if(expr.id()=="is_zero_string") + { + assert(expr.operands().size()==1); + exprt tmp=is_zero_string(expr.op0(), lhs, location); + expr.swap(tmp); + } + else if(expr.id()=="zero_string_length") + { + assert(expr.operands().size()==1); + exprt tmp=zero_string_length(expr.op0(), lhs, location); + expr.swap(tmp); + } + else if(expr.id()=="buffer_size") + { + assert(expr.operands().size()==1); + exprt tmp=buffer_size(expr.op0(), location); + expr.swap(tmp); + } + else + Forall_operands(it, expr) + replace_string_macros(*it, lhs, location); +} + +/*******************************************************************\ + +Function: string_abstractiont::build_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet string_abstractiont::build_type(whatt what) +{ + typet type; + + switch(what) + { + case IS_ZERO: type=bool_typet(); break; + case LENGTH: type=uint_type(); break; + case SIZE: type=uint_type(); break; + default: assert(false); + } + + return type; +} + +/*******************************************************************\ + +Function: string_abstractiont::build_unknown + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::build_unknown(whatt what, bool write) +{ + typet type=build_type(what); + + if(write) + return exprt("NULL-object", type); + + exprt result; + + switch(what) + { + case IS_ZERO: + result=false_exprt(); + break; + + case LENGTH: + case SIZE: + result=exprt(ID_sideeffect, type); + result.set(ID_statement, ID_nondet); + break; + + default: assert(false); + } + + return result; +} + +/*******************************************************************\ + +Function: string_abstractiont::build_unknown + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::build_unknown(bool write) +{ + typet type=pointer_typet(); + type.subtype()=string_struct; + + if(write) + return exprt("NULL-object", type); + + exprt result=exprt("constant", type); + result.set(ID_value, ID_NULL); + + return result; +} + +/*******************************************************************\ + +Function: string_abstractiont::build + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::build( + const exprt &pointer, + whatt what, + bool write, + const locationt &location) +{ + // take care of pointer typecasts now + if(pointer.id()==ID_typecast) + { + // cast from another pointer type? + assert(pointer.operands().size()==1); + if(pointer.op0().type().id()!=ID_pointer) + return build_unknown(what, write); + + // recursive call + return build(pointer.op0(), what, write, location); + } + + exprt str_ptr=build(pointer, write); + + exprt deref=dereference_exprt(string_struct); + deref.op0()=str_ptr; + deref.location()=location; + + exprt result=member(deref, what); + + if(what==LENGTH || what==SIZE) + { + // adjust for offset + exprt pointer_offset(ID_pointer_offset, uint_type()); + pointer_offset.copy_to_operands(pointer); + result=sub(result, pointer_offset); + } + + return result; +} + +/*******************************************************************\ + +Function: string_abstractiont::build_symbol_ptr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::build_symbol_ptr(const exprt &object) +{ + std::string suffix="#str"; + const exprt *p=&object; + + while(p->id()==ID_member) + { + suffix="#"+p->get_string(ID_component_name)+suffix; + assert(p->operands().size()==1); + p=&(p->op0()); + } + + if(p->id()!=ID_symbol) + return static_cast(get_nil_irep()); + + const symbol_exprt &expr_symbol=to_symbol_expr(*p); + + const symbolt &symbol=ns.lookup(expr_symbol.get_identifier()); + irep_idt identifier=id2string(symbol.name)+suffix; + + typet type=pointer_typet(); + type.subtype()=string_struct; + + if(context.symbols.find(identifier)== + context.symbols.end()) + { + symbolt new_symbol; + new_symbol.name=identifier; + new_symbol.mode=symbol.mode; + new_symbol.type=type; + new_symbol.is_statevar=true; + new_symbol.lvalue=true; + new_symbol.static_lifetime=symbol.static_lifetime; + new_symbol.thread_local=symbol.thread_local; + new_symbol.pretty_name=id2string(symbol.pretty_name)+suffix; + new_symbol.module=symbol.module; + new_symbol.base_name=id2string(symbol.base_name)+suffix; + + context.move(new_symbol); + } + + const symbolt &str_symbol=ns.lookup(identifier); + + if(!str_symbol.static_lifetime) + locals[symbol.name]=str_symbol.name; + + return symbol_expr(str_symbol); +} + +/*******************************************************************\ + +Function: string_abstractiont::build + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::build(const exprt &pointer, bool write) +{ + // take care of typecasts + if(pointer.id()==ID_typecast) + { + // cast from another pointer type? + assert(pointer.operands().size()==1); + if(pointer.op0().type().id()!=ID_pointer) + return build_unknown(write); + + // recursive call + return build(pointer.op0(), write); + } + + // take care of if + if(pointer.id()==ID_if) + { + exprt result=if_exprt(); + + // recursive call + result.op0()=pointer.op0(); + result.op1()=build(pointer.op1(), write); + result.op2()=build(pointer.op2(), write); + result.type()=result.op1().type(); + return result; + } + + pointer_arithmetict ptr(pointer); + + if(ptr.pointer.id()==ID_address_of) + { + if(write) + build_unknown(write); + + assert(ptr.pointer.operands().size()==1); + + if(ptr.pointer.op0().id()==ID_index) + { + assert(ptr.pointer.op0().operands().size()==2); + + const exprt &o=ptr.pointer.op0().op0(); + + if(o.id()==ID_string_constant) + { + exprt symbol=build_symbol_constant(o.get(ID_value)); + + if(symbol.is_nil()) + return build_unknown(write); + + exprt address_of(ID_address_of, pointer_typet()); + address_of.type().subtype()=string_struct; + address_of.copy_to_operands(symbol); + + return address_of; + } + + exprt symbol=build_symbol_buffer(o); + + if(symbol.is_nil()) + return build_unknown(write); + + exprt address_of(ID_address_of, pointer_typet()); + address_of.type().subtype()=string_struct; + address_of.copy_to_operands(symbol); + + return address_of; + } + } + else + { + exprt result=build_symbol_ptr(ptr.pointer); + + if(result.is_nil()) + return build_unknown(write); + + return result; + } + + return build_unknown(write); +} + +/*******************************************************************\ + +Function: string_abstractiont::build_symbol_buffer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::build_symbol_buffer(const exprt &object) +{ + // first of all, it must be a buffer + const typet &obj_t=ns.follow(object.type()); + + if(obj_t.id()!=ID_array) + return static_cast(get_nil_irep()); + + const array_typet &obj_array_type=to_array_type(obj_t); + + // we do buffers, arrays of buffers, and a buffer in a struct + + if(object.id()==ID_index) + { + assert(object.operands().size()==2); + + const typet &t=ns.follow(object.op0().type()); + + if(object.op0().id()!=ID_symbol || + t.id()!=ID_array) + return static_cast(get_nil_irep()); + + const symbol_exprt &expr_symbol=to_symbol_expr(object.op0()); + + const symbolt &symbol=ns.lookup(expr_symbol.get_identifier()); + std::string suffix="#str_array"; + irep_idt identifier=id2string(symbol.name)+suffix; + + if(context.symbols.find(identifier)== + context.symbols.end()) + { + array_typet new_type; + new_type.size()=to_array_type(t).size(); + new_type.subtype()=string_struct; + + symbolt new_symbol; + new_symbol.name=identifier; + new_symbol.mode=symbol.mode; + new_symbol.type=new_type; + new_symbol.is_statevar=true; + new_symbol.lvalue=true; + new_symbol.static_lifetime=symbol.static_lifetime; + new_symbol.thread_local=symbol.thread_local; + new_symbol.pretty_name=id2string(symbol.pretty_name)+suffix; + new_symbol.module=symbol.module; + new_symbol.base_name=id2string(symbol.base_name)+suffix; + new_symbol.value.make_nil(); + + { + exprt struct_expr=exprt(ID_struct, string_struct); + struct_expr.operands().resize(3); + struct_expr.op0()=false_exprt(); + struct_expr.op1()=obj_array_type.size(); + make_type(struct_expr.op1(), build_type(SIZE)); + struct_expr.op2()=struct_expr.op1(); + + exprt value=exprt(ID_array_of, new_type); + value.copy_to_operands(struct_expr); + + new_symbol.value=value; + } + + if(symbol.static_lifetime) + { + // initialization + goto_programt::targett assignment1= + initialization.add_instruction(ASSIGN); + assignment1->code=code_assignt(symbol_expr(new_symbol), new_symbol.value); + } + else + { + // declaration + + } + + context.move(new_symbol); + } + + const symbolt &str_array_symbol=ns.lookup(identifier); + + if(!str_array_symbol.static_lifetime) + locals[symbol.name]=str_array_symbol.name; + + index_exprt result; + result.array()=symbol_expr(str_array_symbol); + result.index()=object.op1(); + result.type()=string_struct; + + return result; + } + + // possibly walk over some members + + std::string suffix="#str"; + const exprt *p=&object; + + while(p->id()==ID_member) + { + suffix="#"+p->get_string(ID_component_name)+suffix; + assert(p->operands().size()==1); + p=&(p->op0()); + } + + if(p->id()!=ID_symbol) + return static_cast(get_nil_irep()); + + const symbol_exprt &expr_symbol=to_symbol_expr(*p); + + const symbolt &symbol=ns.lookup(expr_symbol.get_identifier()); + irep_idt identifier=id2string(symbol.name)+suffix; + + if(context.symbols.find(identifier)== + context.symbols.end()) + { + symbolt new_symbol; + new_symbol.name=identifier; + new_symbol.mode=symbol.mode; + new_symbol.type=string_struct; + new_symbol.is_statevar=true; + new_symbol.lvalue=true; + new_symbol.static_lifetime=symbol.static_lifetime; + new_symbol.thread_local=symbol.thread_local; + new_symbol.pretty_name=id2string(symbol.pretty_name)+suffix; + new_symbol.module=symbol.module; + new_symbol.base_name=id2string(symbol.base_name)+suffix; + + { + exprt value=exprt(ID_struct, string_struct); + value.operands().resize(3); + value.op0()=false_exprt(); + value.op1()=obj_array_type.size(); + make_type(value.op1(), build_type(SIZE)); + value.op2()=value.op1(); + + new_symbol.value=value; + } + + if(symbol.static_lifetime) + { + // initialization + goto_programt::targett assignment1=initialization.add_instruction(ASSIGN); + assignment1->code=code_assignt(symbol_expr(new_symbol), new_symbol.value); + } + else + { + // declaration + } + + context.move(new_symbol); + } + + const symbolt &str_symbol=ns.lookup(identifier); + + if(!str_symbol.static_lifetime) + locals[symbol.name]=str_symbol.name; + + return symbol_expr(str_symbol); +} + +/*******************************************************************\ + +Function: string_abstractiont::build_symbol_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::build_symbol_constant(const irep_idt &str) +{ + unsigned l=strlen(str.c_str()); + irep_idt base="string_constant_str_"+i2string(l); + irep_idt identifier="string_abstraction::"+id2string(base); + + if(context.symbols.find(identifier)== + context.symbols.end()) + { + symbolt new_symbol; + new_symbol.name=identifier; + new_symbol.mode="C"; + new_symbol.type=string_struct; + new_symbol.is_statevar=true; + new_symbol.lvalue=true; + new_symbol.static_lifetime=true; + new_symbol.pretty_name=base; + new_symbol.base_name=base; + + { + exprt value=exprt(ID_struct, string_struct); + value.operands().resize(3); + + value.op0()=true_exprt(); + value.op1()=from_integer(l, build_type(LENGTH)); + value.op2()=from_integer(l+1, build_type(SIZE)); + + // initialization + goto_programt::targett assignment1=initialization.add_instruction(ASSIGN); + assignment1->code=code_assignt(symbol_expr(new_symbol), value); + } + + context.move(new_symbol); + } + + symbol_exprt symbol_expr; + symbol_expr.type()=string_struct; + symbol_expr.set_identifier(identifier); + + return symbol_expr; +} + +/*******************************************************************\ + +Function: string_abstractiont::is_zero_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::is_zero_string( + const exprt &object, + bool write, + const locationt &location) +{ + return build(object, IS_ZERO, write, location); +} + +/*******************************************************************\ + +Function: string_abstractiont::zero_string_length + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::zero_string_length( + const exprt &object, + bool write, + const locationt &location) +{ + return build(object, LENGTH, write, location); +} + +/*******************************************************************\ + +Function: string_abstractiont::buffer_size + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt string_abstractiont::buffer_size( + const exprt &object, + const locationt &location) +{ + return build(object, SIZE, false, location); +} + +/*******************************************************************\ + +Function: string_abstractiont::move_lhs_arithmetic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::move_lhs_arithmetic(exprt &lhs, exprt &rhs) +{ + if(lhs.id()=="-") + { + // move op1 to rhs + exprt rest=lhs.op0(); + exprt sum=exprt("+", lhs.type()); + sum.copy_to_operands(rhs, lhs.op1()); + // overwrite + rhs=sum; + lhs=rest; + } +} + +/*******************************************************************\ + +Function: string_abstractiont::abstract_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::abstract_assign( + goto_programt &dest, + goto_programt::targett target) +{ + code_assignt &assign=to_code_assign(target->code); + + exprt &lhs=assign.lhs(); + exprt &rhs=assign.rhs(); + + if(has_string_macros(lhs)) + { + replace_string_macros(lhs, true, target->location); + move_lhs_arithmetic(lhs, rhs); + } + + if(has_string_macros(rhs)) + replace_string_macros(rhs, false, target->location); + + if(lhs.type().id()==ID_pointer) + abstract_pointer_assign(dest, target); + else if(is_char_type(lhs.type())) + abstract_char_assign(dest, target); +} + +/*******************************************************************\ + +Function: string_abstractiont::abstract_pointer_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::abstract_pointer_assign( + goto_programt &dest, + goto_programt::targett target) +{ + code_assignt &assign=to_code_assign(target->code); + + exprt &lhs=assign.lhs(); + exprt rhs=assign.rhs(); + exprt *rhsp=&(assign.rhs()); + + while(rhsp->id()==ID_typecast) + rhsp=&(rhsp->op0()); + + // we only care about char pointers for now + if(!is_char_type(rhsp->type().subtype())) + return; + + // assign length and is_zero as well + + goto_programt tmp; + + goto_programt::targett assignment=tmp.add_instruction(ASSIGN); + assignment->code=code_assignt(build(lhs, true), build(rhs, false)); + assignment->location=target->location; + + target++; + dest.destructive_insert(target, tmp); +} + +/*******************************************************************\ + +Function: string_abstractiont::abstract_char_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::abstract_char_assign( + goto_programt &dest, + goto_programt::targett target) +{ + code_assignt &assign=to_code_assign(target->code); + + exprt &lhs=assign.lhs(); + exprt &rhs=assign.rhs(); + + // we only care if the constant zero is assigned + if(!rhs.is_zero()) + return; + + if(lhs.id()==ID_index) + { + assert(lhs.operands().size()==2); + + goto_programt tmp; + + const exprt symbol_buffer=build_symbol_buffer(lhs.op0()); + + const exprt i1=member(symbol_buffer, IS_ZERO); + if(i1.is_not_nil()) + { + goto_programt::targett assignment1=tmp.add_instruction(ASSIGN); + assignment1->code=code_assignt(i1, true_exprt()); + assignment1->code.location()=target->location; + assignment1->location=target->location; + } + + const exprt i2=member(symbol_buffer, LENGTH); + if(i2.is_not_nil()) + { + exprt new_length=lhs.op1(); + make_type(new_length, i2.type()); + + if_exprt min_expr; + min_expr.cond()=binary_relation_exprt(new_length, "<", i2); + min_expr.true_case()=new_length; + min_expr.false_case()=i2; + min_expr.type()=i2.type(); + + goto_programt::targett assignment2=tmp.add_instruction(ASSIGN); + assignment2->code=code_assignt(i2, min_expr); + assignment2->code.location()=target->location; + assignment2->location=target->location; + + move_lhs_arithmetic( + assignment2->code.op0(), + assignment2->code.op1()); + } + + target++; + dest.destructive_insert(target, tmp); + } + else if(lhs.id()==ID_dereference) + { + assert(lhs.operands().size()==1); + + } +} + +/*******************************************************************\ + +Function: string_abstractiont::abstract_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_abstractiont::abstract_function_call( + goto_programt &dest, + goto_programt::targett target) +{ + const code_function_callt &call=to_code_function_call(target->code); + const code_function_callt::argumentst &arguments=call.arguments(); + + contextt::symbolst::const_iterator f_it = + context.symbols.find(call.function().get(ID_identifier)); + + if(f_it==context.symbols.end()) + throw "invalid function call"; + + const code_typet &fnc_type = + static_cast(f_it->second.type); + const code_typet::argumentst &argument_types=fnc_type.arguments(); + + exprt::operandst::const_iterator it1=arguments.begin(); + + for(code_typet::argumentst::const_iterator it2=argument_types.begin(); + it2!=argument_types.end(); + it2++) + { + if(it1==arguments.end()) + { + err_location(target->location); + throw "function call: not enough arguments"; + } + + const exprt &argument=static_cast(*it2); + + const exprt actual(*it1); + + const exprt *tcfree = &actual; + while(tcfree->id()==ID_typecast) + tcfree=&tcfree->op0(); + + if(tcfree->type().id()==ID_pointer && + is_char_type(tcfree->type().subtype())) + { + const irep_idt &identifier=argument.get("#identifier"); + + if(identifier=="") continue; // just forget it + + goto_programt tmp; + + goto_programt::targett assignment=tmp.add_instruction(ASSIGN); + assignment->code=code_assignt( + build(symbol_exprt(identifier, argument.type()), true), + build(actual, false)); + assignment->location=target->location; + assignment->function=call.function().get(ID_identifier); + + // inserts _before_ the call + dest.destructive_insert(target, tmp); + } + + it1++; + } +} diff --git a/src/goto-programs/string_abstraction.h b/src/goto-programs/string_abstraction.h new file mode 100644 index 00000000000..88d4d246599 --- /dev/null +++ b/src/goto-programs/string_abstraction.h @@ -0,0 +1,140 @@ +/*******************************************************************\ + +Module: String Abstraction + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_STRING_ABSTRACTION_H +#define CPROVER_GOTO_PROGRAMS_STRING_ABSTRACTION_H + +#include +#include +#include +#include + +#include "goto_functions.h" + +/*******************************************************************\ + + Class: string_abstractiont + + Purpose: + +\*******************************************************************/ + +class string_abstractiont:public message_streamt +{ +public: + string_abstractiont( + contextt &_context, + message_handlert &_message_handler); + + void operator()(goto_programt &dest); + void operator()(goto_functionst &dest); + + exprt is_zero_string( + const exprt &object, + bool write, + const locationt &location); + + exprt zero_string_length( + const exprt &object, + bool write, + const locationt &location); + + exprt buffer_size( + const exprt &object, + const locationt &location); + + static bool has_string_macros(const exprt &expr); + + void replace_string_macros( + exprt &expr, + bool lhs, + const locationt &location); + + typet get_string_struct(void) { return string_struct; } + +protected: + contextt &context; + namespacet ns; + + void move_lhs_arithmetic(exprt &lhs, exprt &rhs); + + bool is_char_type(const typet &type) const + { + if(type.id()==ID_symbol) + return is_char_type(ns.follow(type)); + + if(type.id()!=ID_signedbv && + type.id()!=ID_unsignedbv) + return false; + + return bv_width(type)==config.ansi_c.char_width; + } + + void make_type(exprt &dest, const typet &type) + { + if(dest.is_not_nil() && + ns.follow(dest.type())!=ns.follow(type)) + dest.make_typecast(type); + } + + void abstract(goto_programt &dest, goto_programt::targett it); + void abstract_assign(goto_programt &dest, goto_programt::targett it); + void abstract_pointer_assign(goto_programt &dest, goto_programt::targett it); + void abstract_char_assign(goto_programt &dest, goto_programt::targett it); + void abstract_function_call(goto_programt &dest, goto_programt::targett it); + + typedef enum { IS_ZERO, LENGTH, SIZE } whatt; + + exprt build( + const exprt &pointer, + whatt what, + bool write, + const locationt &location); + + exprt build(const exprt &ptr, bool write); + exprt build_symbol_ptr(const exprt &object); + exprt build_symbol_buffer(const exprt &object); + exprt build_symbol_constant(const irep_idt &str); + exprt build_unknown(whatt what, bool write); + exprt build_unknown(bool write); + static typet build_type(whatt what); + + exprt sub(const exprt &a, const exprt &b) + { + if(b.is_nil() || b.is_zero()) return a; + exprt result("-", a.type()); + result.copy_to_operands(a, b); + make_type(result.op1(), result.type()); + return result; + } + + exprt member(const exprt &a, whatt what); + + typet string_struct; + goto_programt initialization; + + typedef std::map localst; + localst locals; + + void abstract(goto_programt &dest); +}; + + +// keep track of length of strings + +void string_abstraction( + contextt &context, + message_handlert &message_handler, + goto_programt &dest); + +void string_abstraction( + contextt &context, + message_handlert &message_handler, + goto_functionst &dest); + +#endif diff --git a/src/goto-programs/string_instrumentation.cpp b/src/goto-programs/string_instrumentation.cpp new file mode 100644 index 00000000000..e7c730510e5 --- /dev/null +++ b/src/goto-programs/string_instrumentation.cpp @@ -0,0 +1,1115 @@ +/*******************************************************************\ + +Module: String Abstraction + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "string_instrumentation.h" + +/*******************************************************************\ + + Class: string_instrumentationt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +class string_instrumentationt:public message_streamt +{ +public: + string_instrumentationt( + contextt &_context, + message_handlert &_message_handler): + message_streamt(_message_handler), + context(_context), + ns(_context) + { + } + + void operator()(goto_programt &dest); + void operator()(goto_functionst &dest); + + exprt is_zero_string( + const exprt &what, + bool write=false) + { + exprt result=predicate_exprt("is_zero_string"); + result.copy_to_operands(what); + result.set("lhs", write); + return result; + } + + exprt zero_string_length( + const exprt &what, + bool write=false) + { + exprt result("zero_string_length", uint_type()); + result.copy_to_operands(what); + result.set("lhs", write); + return result; + } + + exprt buffer_size(const exprt &what) + { + exprt result("buffer_size", uint_type()); + result.copy_to_operands(what); + return result; + } + +protected: + contextt &context; + namespacet ns; + + void make_type(exprt &dest, const typet &type) + { + if(ns.follow(dest.type())!=ns.follow(type)) + dest.make_typecast(type); + } + + void instrument(goto_programt &dest, goto_programt::targett it); + void do_function_call(goto_programt &dest, goto_programt::targett it); + + // strings + void do_sprintf (goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_snprintf(goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_strcat (goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_strncmp (goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_strchr (goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_strrchr (goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_strstr (goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_strtok (goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_strerror(goto_programt &dest, goto_programt::targett it, code_function_callt &call); + void do_fscanf (goto_programt &dest, goto_programt::targett it, code_function_callt &call); + + void do_format_string_read( + goto_programt &dest, + goto_programt::const_targett target, + const code_function_callt::argumentst &arguments, + unsigned format_string_inx, + unsigned argument_start_inx, + const std::string &function_name); + + void do_format_string_write( + goto_programt &dest, + goto_programt::const_targett target, + const code_function_callt::argumentst &arguments, + unsigned format_string_inx, + unsigned argument_start_inx, + const std::string &function_name); + + bool is_string_type(const typet &t) const + { + return ((t.id()=="pointer" || t.id()=="array") && + (t.subtype().id()=="signedbv" || t.subtype().id()=="unsignedbv") && + (bv_width(t.subtype())==config.ansi_c.char_width)); + } + + void invalidate_buffer( + goto_programt &dest, + goto_programt::const_targett target, + const exprt &buffer, + const typet &buf_type, + const mp_integer &limit); +}; + +/*******************************************************************\ + +Function: string_instrumentation + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentation( + contextt &context, + message_handlert &message_handler, + goto_programt &dest) +{ + string_instrumentationt string_instrumentation(context, message_handler); + string_instrumentation(dest); +} + +/*******************************************************************\ + +Function: string_instrumentation + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentation( + contextt &context, + message_handlert &message_handler, + goto_functionst &dest) +{ + string_instrumentationt string_instrumentation(context, message_handler); + string_instrumentation(dest); +} + +/*******************************************************************\ + +Function: string_instrumentationt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::operator()(goto_functionst &dest) +{ + for(goto_functionst::function_mapt::iterator + it=dest.function_map.begin(); + it!=dest.function_map.end(); + it++) + { + (*this)(it->second.body); + } +} + +/*******************************************************************\ + +Function: string_instrumentationt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::operator()(goto_programt &dest) +{ + Forall_goto_program_instructions(it, dest) + instrument(dest, it); +} + +/*******************************************************************\ + +Function: string_instrumentationt::instrument + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::instrument( + goto_programt &dest, + goto_programt::targett it) +{ + switch(it->type) + { + case ASSIGN: + break; + + case FUNCTION_CALL: + do_function_call(dest, it); + break; + + default:; + } +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_function_call( + goto_programt &dest, + goto_programt::targett target) +{ + code_function_callt &call= + to_code_function_call(target->code); + exprt &function=call.function(); + //const exprt &lhs=call.lhs(); + + if(function.id()=="symbol") + { + const irep_idt &identifier= + to_symbol_expr(function).get_identifier(); + + if(identifier=="c::strcoll") + { + } + else if(identifier=="c::strncmp") + do_strncmp(dest, target, call); + else if(identifier=="c::strxfrm") + { + } + else if(identifier=="c::strchr") + do_strchr(dest, target, call); + else if(identifier=="c::strcspn") + { + } + else if(identifier=="c::strpbrk") + { + } + else if(identifier=="c::strrchr") + do_strrchr(dest, target, call); + else if(identifier=="c::strspn") + { + } + else if(identifier=="c::strerror") + do_strerror(dest, target, call); + else if(identifier=="c::strstr") + do_strstr(dest, target, call); + else if(identifier=="c::strtok") + do_strtok(dest, target, call); + else if(identifier=="c::sprintf") + do_sprintf(dest, target, call); + else if(identifier=="c::snprintf") + do_snprintf(dest, target, call); + else if(identifier=="c::fscanf") + do_fscanf(dest, target, call); + + dest.update(); + } +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_sprintf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_sprintf( + goto_programt &dest, + goto_programt::targett target, + code_function_callt &call) +{ + const code_function_callt::argumentst &arguments=call.arguments(); + + if(arguments.size()<2) + { + err_location(target->location); + throw "sprintf expected to have two or more arguments"; + } + + goto_programt tmp; + + goto_programt::targett assertion=tmp.add_instruction(); + assertion->location=target->location; + assertion->location.set("property", "string"); + assertion->location.set("comment", "sprintf buffer overflow"); + + // in the abstract model, we have to report a + // (possibly false) positive here + assertion->make_assertion(false_exprt()); + + do_format_string_read(tmp, target, arguments, 1, 2, "sprintf"); + + if(call.lhs().is_not_nil()) + { + goto_programt::targett return_assignment=tmp.add_instruction(ASSIGN); + return_assignment->location=target->location; + + exprt rhs=side_effect_expr_nondett(call.lhs().type()); + rhs.location()=target->location; + + return_assignment->code=code_assignt(call.lhs(), rhs); + } + + target->make_skip(); + dest.insert_before_swap(target, tmp); +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_snprintf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_snprintf( + goto_programt &dest, + goto_programt::targett target, + code_function_callt &call) +{ + const code_function_callt::argumentst &arguments=call.arguments(); + + if(arguments.size()<3) + { + err_location(target->location); + throw "snprintf expected to have three or more arguments"; + } + + goto_programt tmp; + + goto_programt::targett assertion=tmp.add_instruction(); + assertion->location=target->location; + assertion->location.set("property", "string"); + assertion->location.set("comment", "snprintf buffer overflow"); + + exprt bufsize = buffer_size(arguments[0]); + assertion->make_assertion(binary_relation_exprt(bufsize, ">=", arguments[1])); + + do_format_string_read(tmp, target, arguments, 2, 3, "snprintf"); + + if(call.lhs().is_not_nil()) + { + goto_programt::targett return_assignment=tmp.add_instruction(ASSIGN); + return_assignment->location=target->location; + + exprt rhs=side_effect_expr_nondett(call.lhs().type()); + rhs.location()=target->location; + + return_assignment->code=code_assignt(call.lhs(), rhs); + } + + target->make_skip(); + dest.insert_before_swap(target, tmp); +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_fscanf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_fscanf( + goto_programt &dest, + goto_programt::targett target, + code_function_callt &call) +{ + const code_function_callt::argumentst &arguments=call.arguments(); + + if(arguments.size()<2) + { + err_location(target->location); + throw "fscanf expected to have two or more arguments"; + } + + goto_programt tmp; + + do_format_string_write(tmp, target, arguments, 1, 2, "fscanf"); + + if(call.lhs().is_not_nil()) + { + goto_programt::targett return_assignment=tmp.add_instruction(ASSIGN); + return_assignment->location=target->location; + + exprt rhs=side_effect_expr_nondett(call.lhs().type()); + rhs.location()=target->location; + + return_assignment->code=code_assignt(call.lhs(), rhs); + } + + target->make_skip(); + dest.insert_before_swap(target, tmp); +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_format_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_format_string_read( + goto_programt &dest, + goto_programt::const_targett target, + const code_function_callt::argumentst &arguments, + unsigned format_string_inx, + unsigned argument_start_inx, + const std::string &function_name) +{ + const exprt &format_arg = arguments[format_string_inx]; + + if(format_arg.id()=="address_of" && + format_arg.op0().id()=="index" && + format_arg.op0().op0().id()==ID_string_constant) + { + format_token_listt token_list; + parse_format_string(format_arg.op0().op0(), token_list); + + unsigned args=0; + + for(format_token_listt::const_iterator it=token_list.begin(); + it!=token_list.end(); + it++) + { + if(it->type==format_tokent::STRING) + { + const exprt &arg = arguments[argument_start_inx+args]; + const typet &arg_type = ns.follow(arg.type()); + + if(arg.id()!=ID_string_constant) // we don't need to check constants + { + goto_programt::targett assertion=dest.add_instruction(); + assertion->location=target->location; + assertion->location.set("property", "string"); + std::string comment("zero-termination of string argument of "); + comment += function_name; + assertion->location.set("comment", comment); + + exprt temp(arg); + + if(arg_type.id()!="pointer") + { + index_exprt index; + index.array()=temp; + index.index()=gen_zero(uint_type()); + index.type()=arg_type.subtype(); + temp=address_of_exprt(index); + } + + assertion->make_assertion(is_zero_string(temp)); + } + } + + if(it->type!=format_tokent::TEXT && + it->type!=format_tokent::UNKNOWN) args++; + + if(find(it->flags.begin(), it->flags.end(), format_tokent::ASTERISK)!= + it->flags.end()) + args++; // just eat the additional argument + } + } + else // non-const format string + { + goto_programt::targett format_ass=dest.add_instruction(); + format_ass->make_assertion(is_zero_string(arguments[1])); + format_ass->location=target->location; + format_ass->location.set("property", "string"); + std::string comment("zero-termination of format string of "); + comment += function_name; + format_ass->location.set("comment", comment); + + for(unsigned i=2; ilocation=target->location; + assertion->location.set("property", "string"); + std::string comment("zero-termination of string argument of "); + comment += function_name; + assertion->location.set("comment", comment); + + exprt temp(arg); + + if(arg_type.id()!="pointer") + { + index_exprt index; + index.array()=temp; + index.index()=gen_zero(uint_type()); + index.type()=arg_type.subtype(); + temp=address_of_exprt(index); + } + + assertion->make_assertion(is_zero_string(temp)); + } + } + } +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_format_string_write + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_format_string_write( + goto_programt &dest, + goto_programt::const_targett target, + const code_function_callt::argumentst &arguments, + unsigned format_string_inx, + unsigned argument_start_inx, + const std::string &function_name) +{ + const exprt &format_arg = arguments[format_string_inx]; + + if(format_arg.id()=="address_of" && + format_arg.op0().id()=="index" && + format_arg.op0().op0().id()==ID_string_constant) // constant format + { + format_token_listt token_list; + parse_format_string(format_arg.op0().op0(), token_list); + + unsigned args=0; + + for(format_token_listt::const_iterator it=token_list.begin(); + it!=token_list.end(); + it++) + { + if(find(it->flags.begin(), it->flags.end(), format_tokent::ASTERISK)!= + it->flags.end()) + continue; // asterisk means `ignore this' + + switch(it->type) + { + case format_tokent::STRING: + { + + const exprt &argument=arguments[argument_start_inx+args]; + const typet &arg_type=ns.follow(argument.type()); + + goto_programt::targett assertion=dest.add_instruction(); + assertion->location=target->location; + assertion->location.set("property", "string"); + std::string comment("format string buffer overflow in "); + comment += function_name; + assertion->location.set("comment", comment); + + if(it->field_width!=0) + { + exprt fwidth = from_integer(it->field_width, uint_type()); + exprt fw_1("+", uint_type()); + exprt one = gen_one(uint_type()); + fw_1.move_to_operands(fwidth); + fw_1.move_to_operands(one); // +1 for 0-char + + exprt fw_lt_bs; + + if(arg_type.id()=="pointer") + fw_lt_bs=binary_relation_exprt(fw_1, "<=", buffer_size(argument)); + else + { + index_exprt index; + index.array()=argument; + index.index()=gen_zero(uint_type()); + address_of_exprt aof(index); + fw_lt_bs=binary_relation_exprt(fw_1, "<=", buffer_size(aof)); + } + + assertion->make_assertion(fw_lt_bs); + } + else + { + // this is a possible overflow. + assertion->make_assertion(false_exprt()); + } + + // now kill the contents + invalidate_buffer(dest, target, argument, arg_type, it->field_width); + + args++; + break; + } + case format_tokent::TEXT: + case format_tokent::UNKNOWN: + { + // nothing + break; + } + default: // everything else + { + const exprt &argument=arguments[argument_start_inx+args]; + const typet &arg_type=ns.follow(argument.type()); + + goto_programt::targett assignment=dest.add_instruction(ASSIGN); + assignment->location=target->location; + + exprt lhs("dereference", arg_type.subtype()); + lhs.copy_to_operands(argument); + + exprt rhs=side_effect_expr_nondett(lhs.type()); + rhs.location()=target->location; + + assignment->code=code_assignt(lhs, rhs); + + args++; + break; + } + } + } + } + else // non-const format string + { + for(unsigned i=argument_start_inx; ilocation=target->location; + assertion->location.set("property", "string"); + std::string comment("format string buffer overflow in "); + comment += function_name; + assertion->location.set("comment", comment); + + // as we don't know any field width for the %s that + // should be here during runtime, we just report a + // possibly false positive + assertion->make_assertion(false_exprt()); + + invalidate_buffer(dest, target, arguments[i], arg_type, 0); + } + else + { + goto_programt::targett assignment = dest.add_instruction(ASSIGN); + assignment->location=target->location; + + exprt lhs("dereference", arg_type.subtype()); + lhs.copy_to_operands(arguments[i]); + + exprt rhs=side_effect_expr_nondett(lhs.type()); + rhs.location()=target->location; + + assignment->code=code_assignt(lhs, rhs); + } + } + } +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_strncmp + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_strncmp( + goto_programt &dest, + goto_programt::targett target, + code_function_callt &call) +{ +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_strchr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_strchr( + goto_programt &dest, + goto_programt::targett target, + code_function_callt &call) +{ + const code_function_callt::argumentst &arguments=call.arguments(); + + if(arguments.size()!=2) + { + err_location(target->location); + throw "strchr expected to have two arguments"; + } + + goto_programt tmp; + + goto_programt::targett assertion=tmp.add_instruction(); + assertion->make_assertion(is_zero_string(arguments[0])); + assertion->location=target->location; + assertion->location.set("property", "string"); + assertion->location.set("comment", "zero-termination of string argument of strchr"); + + target->make_skip(); + dest.insert_before_swap(target, tmp); +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_strrchr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_strrchr( + goto_programt &dest, + goto_programt::targett target, + code_function_callt &call) +{ + const code_function_callt::argumentst &arguments=call.arguments(); + + if(arguments.size()!=2) + { + err_location(target->location); + throw "strrchr expected to have two arguments"; + } + + goto_programt tmp; + + goto_programt::targett assertion=tmp.add_instruction(); + assertion->make_assertion(is_zero_string(arguments[0])); + assertion->location=target->location; + assertion->location.set("property", "string"); + assertion->location.set("comment", "zero-termination of string argument of strrchr"); + + target->make_skip(); + dest.insert_before_swap(target, tmp); +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_strstr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_strstr( + goto_programt &dest, + goto_programt::targett target, + code_function_callt &call) +{ + const code_function_callt::argumentst &arguments=call.arguments(); + + if(arguments.size()!=2) + { + err_location(target->location); + throw "strstr expected to have two arguments"; + } + + goto_programt tmp; + + goto_programt::targett assertion0=tmp.add_instruction(); + assertion0->make_assertion(is_zero_string(arguments[0])); + assertion0->location=target->location; + assertion0->location.set("property", "string"); + assertion0->location.set("comment", "zero-termination of 1st string argument of strstr"); + + goto_programt::targett assertion1=tmp.add_instruction(); + assertion1->make_assertion(is_zero_string(arguments[1])); + assertion1->location=target->location; + assertion1->location.set("property", "string"); + assertion1->location.set("comment", "zero-termination of 2nd string argument of strstr"); + + target->make_skip(); + dest.insert_before_swap(target, tmp); +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_strtok + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_strtok( + goto_programt &dest, + goto_programt::targett target, + code_function_callt &call) +{ + const code_function_callt::argumentst &arguments=call.arguments(); + + if(arguments.size()!=2) + { + err_location(target->location); + throw "strtok expected to have two arguments"; + } + + goto_programt tmp; + + goto_programt::targett assertion0=tmp.add_instruction(); + assertion0->make_assertion(is_zero_string(arguments[0])); + assertion0->location=target->location; + assertion0->location.set("property", "string"); + assertion0->location.set("comment", "zero-termination of 1st string argument of strtok"); + + goto_programt::targett assertion1=tmp.add_instruction(); + assertion1->make_assertion(is_zero_string(arguments[1])); + assertion1->location=target->location; + assertion1->location.set("property", "string"); + assertion1->location.set("comment", "zero-termination of 2nd string argument of strtok"); + + target->make_skip(); + dest.insert_before_swap(target, tmp); +} + +/*******************************************************************\ + +Function: string_instrumentationt::do_strerror + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::do_strerror( + goto_programt &dest, + goto_programt::targett it, + code_function_callt &call) +{ + if(call.lhs().is_nil()) + { + it->make_skip(); + return; + } + + irep_idt identifier_buf="c::__strerror_buffer"; + irep_idt identifier_size="c::__strerror_buffer_size"; + + if(context.symbols.find(identifier_buf)==context.symbols.end()) + { + symbolt new_symbol_size; + new_symbol_size.base_name="__strerror_buffer_size"; + new_symbol_size.pretty_name=new_symbol_size.base_name; + new_symbol_size.name=identifier_size; + new_symbol_size.mode="C"; + new_symbol_size.type=uint_type(); + new_symbol_size.is_statevar=true; + new_symbol_size.lvalue=true; + new_symbol_size.static_lifetime=true; + + array_typet type; + type.subtype()=char_type(); + type.size()=symbol_expr(new_symbol_size); + symbolt new_symbol_buf; + new_symbol_buf.mode="C"; + new_symbol_buf.type=type; + new_symbol_buf.is_statevar=true; + new_symbol_buf.lvalue=true; + new_symbol_buf.static_lifetime=true; + new_symbol_buf.base_name="__strerror_buffer"; + new_symbol_buf.pretty_name=new_symbol_buf.base_name; + new_symbol_buf.name="c::"+id2string(new_symbol_buf.base_name); + + context.move(new_symbol_buf); + context.move(new_symbol_size); + } + + const symbolt &symbol_size=ns.lookup(identifier_size); + const symbolt &symbol_buf=ns.lookup(identifier_buf); + + goto_programt tmp; + + { + goto_programt::targett assignment1=tmp.add_instruction(ASSIGN); + exprt nondet_size=side_effect_expr_nondett(uint_type()); + + assignment1->code=code_assignt(symbol_expr(symbol_size), nondet_size); + assignment1->location=it->location; + + goto_programt::targett assumption1=tmp.add_instruction(); + + assumption1->make_assumption(binary_relation_exprt( + symbol_expr(symbol_size), "notequal", + gen_zero(symbol_size.type))); + + assumption1->location=it->location; + } + + // return a pointer to some magic buffer + exprt index=exprt("index", char_type()); + index.copy_to_operands(symbol_expr(symbol_buf), gen_zero(uint_type())); + + exprt ptr=exprt("address_of", pointer_typet()); + ptr.type().subtype()=char_type(); + ptr.copy_to_operands(index); + + // make that zero-terminated + { + goto_programt::targett assignment2=tmp.add_instruction(ASSIGN); + assignment2->code=code_assignt(is_zero_string(ptr, true), true_exprt()); + assignment2->location=it->location; + } + + // assign address + { + goto_programt::targett assignment3=tmp.add_instruction(ASSIGN); + exprt rhs=ptr; + make_type(rhs, call.lhs().type()); + assignment3->code=code_assignt(call.lhs(), rhs); + assignment3->location=it->location; + } + + it->make_skip(); + dest.insert_before_swap(it, tmp); +} + +/*******************************************************************\ + +Function: string_instrumentationt::invalidate_buffer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void string_instrumentationt::invalidate_buffer( + goto_programt &dest, + goto_programt::const_targett target, + const exprt &buffer, + const typet &buf_type, + const mp_integer &limit) +{ + irep_idt cntr_id="string_instrumentation::$counter"; + + if(context.symbols.find(cntr_id)==context.symbols.end()) + { + symbolt new_symbol; + new_symbol.base_name="$counter"; + new_symbol.pretty_name=new_symbol.base_name; + new_symbol.name=cntr_id; + new_symbol.mode="C"; + new_symbol.type=uint_type(); + new_symbol.is_statevar=true; + new_symbol.lvalue=true; + new_symbol.static_lifetime=true; + + context.move(new_symbol); + } + + const symbolt &cntr_sym=ns.lookup(cntr_id); + + // create a loop that runs over the buffer + // and invalidates every element + + goto_programt::targett init=dest.add_instruction(ASSIGN); + init->location=target->location; + init->code=code_assignt(symbol_expr(cntr_sym), gen_zero(cntr_sym.type)); + + goto_programt::targett check=dest.add_instruction(); + check->location=target->location; + + goto_programt::targett invalidate=dest.add_instruction(ASSIGN); + invalidate->location=target->location; + + goto_programt::targett increment=dest.add_instruction(ASSIGN); + increment->location=target->location; + + exprt plus("+", uint_type()); + plus.copy_to_operands(symbol_expr(cntr_sym)); + plus.copy_to_operands(gen_one(uint_type())); + + increment->code=code_assignt(symbol_expr(cntr_sym), plus); + + goto_programt::targett back=dest.add_instruction(); + back->location=target->location; + back->make_goto(check); + back->guard=true_exprt(); + + goto_programt::targett exit=dest.add_instruction(); + exit->location=target->location; + exit->make_skip(); + + exprt cnt_bs, bufp; + + if(buf_type.id()=="pointer") + bufp = buffer; + else + { + index_exprt index; + index.array()=buffer; + index.index()=gen_zero(uint_type()); + index.type()=buf_type.subtype(); + bufp = address_of_exprt(index); + } + + exprt deref("dereference", buf_type.subtype()); + exprt b_plus_i("+", bufp.type()); + b_plus_i.copy_to_operands(bufp); + b_plus_i.copy_to_operands(symbol_expr(cntr_sym)); + deref.copy_to_operands(b_plus_i); + + check->make_goto(exit); + + if(limit==0) + check->guard= + binary_relation_exprt(symbol_expr(cntr_sym), ">=", + buffer_size(bufp)); + else + check->guard= + binary_relation_exprt(symbol_expr(cntr_sym), ">", + from_integer(limit, uint_type())); + + exprt nondet=side_effect_expr_nondett(buf_type.subtype()); + invalidate->code=code_assignt(deref, nondet); +} diff --git a/src/goto-programs/string_instrumentation.h b/src/goto-programs/string_instrumentation.h new file mode 100644 index 00000000000..a1b6df80894 --- /dev/null +++ b/src/goto-programs/string_instrumentation.h @@ -0,0 +1,24 @@ +/*******************************************************************\ + +Module: String Abstraction + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_STRING_INSTRUMENTATION_H +#define CPROVER_GOTO_PROGRAMS_STRING_INSTRUMENTATION_H + +#include "goto_functions.h" + +void string_instrumentation( + contextt &context, + message_handlert &message_handler, + goto_programt &dest); + +void string_instrumentation( + contextt &context, + message_handlert &message_handler, + goto_functionst &dest); + +#endif diff --git a/src/goto-programs/test_wp.cpp b/src/goto-programs/test_wp.cpp new file mode 100644 index 00000000000..e5af5b4772b --- /dev/null +++ b/src/goto-programs/test_wp.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include +#include + +#include +#include +#include + +#include "goto_convert_functions.h" +#include "wp.h" + +int main(int argc, const char **argv) +{ + try + { + config.ansi_c.set_32(); + + register_language(new_ansi_c_language); + + ansi_c_languaget language; + console_message_handlert message_handler; + + language.parse(std::cin, "", message_handler); + + contextt context; + language.typecheck(context, "cin", message_handler); + + optionst options; + options.set_option("assertions", true); + options.set_option("assumptions", true); + goto_functionst goto_functions; + + goto_convert(context, options, goto_functions, message_handler); + + goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.find("c::f"); + + if(f_it==goto_functions.function_map.end()) + { + std::cerr << "no function f" << std::endl; + return 2; + } + + const goto_programt &p=f_it->second.body; + + //p.output(namespacet(context), "c::f", std::cout); + + forall_goto_program_instructions(it, p) + { + if(it->is_end_function()) break; + codet code=it->code; + + it++; + if(!it->is_assert()) + { + std::cerr << "f is expected to have assertion" << std::endl; + return 4; + } + + exprt post=it->guard; + + namespacet ns(context); + + exprt pre=wp(code, post, ns); + + std::cout << "CODE: " << expr2c(code, ns) + << "wp(" << expr2c(post, ns) << "): " + << expr2c(pre, ns) << std::endl; + simplify(pre, ns); + std::cout << "Simp: " << expr2c(pre, ns) << std::endl; + std::cout << std::endl; + } + } + + catch(std::string s) + { + std::cerr << s << std::endl; + } +} diff --git a/src/goto-programs/test_wp_in1.c b/src/goto-programs/test_wp_in1.c new file mode 100644 index 00000000000..c474603d2d6 --- /dev/null +++ b/src/goto-programs/test_wp_in1.c @@ -0,0 +1,25 @@ +// test input for test_wp + +int x, *p, a[100], i; + +struct S +{ + int f1, f2; +} s, t; + +void f() +{ + x++; assert(x==1); + *p=1; assert(x==1); + (*p)++; assert(x==1); + x++; assert(s.f1==1); + *p=1; assert(s.f1==1); + s.f1++; assert(s.f1==1); + a[10]++; assert(a[10]==1); + a[20]++; assert(a[10]==1); + a[20]++; assert(a[i]==1); + *p=1; assert(a[i]==1); + *p=1; assert(*p==1); + x=1; assert(*p==1); + s=t; assert(s.f1==1); +} diff --git a/src/goto-programs/wp.cpp b/src/goto-programs/wp.cpp new file mode 100644 index 00000000000..366dd2b7277 --- /dev/null +++ b/src/goto-programs/wp.cpp @@ -0,0 +1,361 @@ +/*******************************************************************\ + +Module: Weakest Preconditions + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +//#include + +#include +#include +#include +#include + +#include "wp.h" + +/*******************************************************************\ + +Function: has_nondet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool has_nondet(const exprt &dest) +{ + forall_operands(it, dest) + if(has_nondet(*it)) + return true; + + if(dest.id()==ID_sideeffect) + { + const side_effect_exprt &side_effect_expr=to_side_effect_expr(dest); + const irep_idt &statement=side_effect_expr.get_statement(); + + if(statement==ID_nondet) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: approximate_nondet_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void approximate_nondet_rec(exprt &dest, unsigned &count) +{ + if(dest.id()==ID_sideeffect && + to_side_effect_expr(dest).get_statement()==ID_nondet) + { + count++; + dest.set(ID_identifier, "wp::nondet::"+i2string(count)); + dest.id(ID_nondet_symbol); + return; + } + + Forall_operands(it, dest) + approximate_nondet_rec(*it, count); +} + +/*******************************************************************\ + +Function: approximate_nondet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void approximate_nondet(exprt &dest) +{ + static unsigned count=0; // not proper, should be quantified + approximate_nondet_rec(dest, count); +} + +/*******************************************************************\ + +Function: aliasing + + Inputs: + + Outputs: + + Purpose: consider possible aliasing + +\*******************************************************************/ + +typedef enum { A_MAY, A_MUST, A_MUSTNOT } aliasingt; + +aliasingt aliasing( + const exprt &e1, const exprt &e2, + const namespacet &ns) +{ + // deal with some dereferencing + if(e1.id()==ID_dereference && + e1.operands().size()==1 && + e1.op0().id()==ID_address_of && + e1.op0().operands().size()==1) + return aliasing(e1.op0().op0(), e2, ns); + + if(e2.id()==ID_dereference && + e2.operands().size()==1 && + e2.op0().id()==ID_address_of && + e2.op0().operands().size()==1) + return aliasing(e1, e2.op0().op0(), ns); + + // fairly radical. Ignores struct prefixes and the like. + if(!base_type_eq(e1.type(), e2.type(), ns)) + return A_MUSTNOT; + + // syntactically the same? + if(e1==e2) + return A_MUST; + + // the trivial case first + if(e1.id()==ID_symbol && e2.id()==ID_symbol) + { + if(to_symbol_expr(e1).get_identifier()== + to_symbol_expr(e2).get_identifier()) + return A_MUST; + else + return A_MUSTNOT; + } + + // an array or struct will never alias with a variable, + // nor will a struct alias with an array + + if(e1.id()==ID_index || e1.id()==ID_struct) + if(e1.id()!=e2.id()) + return A_MUSTNOT; + + if(e2.id()==ID_index || e2.id()==ID_struct) + if(e1.id()!=e2.id()) + return A_MUSTNOT; + + // we give up, and say it may + // (could do much more here) + + return A_MAY; +} + +/*******************************************************************\ + +Function: substitute_rec + + Inputs: + + Outputs: + + Purpose: replace 'what' by 'by' in 'dest', + considering possible aliasing + +\*******************************************************************/ + +void substitute_rec( + exprt &dest, + const exprt &what, + const exprt &by, + const namespacet &ns) +{ + if(dest.id()!=ID_address_of) + Forall_operands(it, dest) + substitute_rec(*it, what, by, ns); + + // possibly substitute? + if(dest.id()==ID_member || + dest.id()==ID_index || + dest.id()==ID_dereference || + dest.id()==ID_symbol) + { + // could these be possible the same? + switch(aliasing(dest, what, ns)) + { + case A_MUST: + dest=by; // they are always the same + break; + + case A_MAY: + { + // consider possible aliasing between 'what' and 'dest' + exprt what_address=address_of_exprt(what); + exprt dest_address=address_of_exprt(dest); + + equality_exprt alias_cond=equality_exprt(what_address, dest_address); + + if_exprt if_expr; + + if_expr.cond()=alias_cond; + if_expr.type()=dest.type(); + if_expr.true_case()=by; + if_expr.false_case()=dest; + + dest=if_expr; + return; + } + + case A_MUSTNOT: + // nothing to do + break; + } + } +} + +/*******************************************************************\ + +Function: rewrite_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void rewrite_assignment(exprt &lhs, exprt &rhs) +{ + if(lhs.id()==ID_member) // turn s.x:=e into s:=(s with .x=e) + { + const member_exprt member_expr=to_member_expr(lhs); + irep_idt component_name=member_expr.get_component_name(); + exprt new_lhs=member_expr.struct_op(); + + with_exprt new_rhs; + new_rhs.type()=new_lhs.type(); + new_rhs.old()=new_lhs; + new_rhs.where().id(ID_member_name); + new_rhs.where().set(ID_component_name, component_name); + new_rhs.new_value()=rhs; + + lhs=new_lhs; + rhs=new_rhs; + + rewrite_assignment(lhs, rhs); // rec. call + } + else if(lhs.id()==ID_index) // turn s[i]:=e into s:=(s with [i]=e) + { + const index_exprt index_expr=to_index_expr(lhs); + exprt new_lhs=index_expr.array(); + + with_exprt new_rhs; + new_rhs.type()=new_lhs.type(); + new_rhs.old()=new_lhs; + new_rhs.where()=index_expr.index(); + new_rhs.new_value()=rhs; + + lhs=new_lhs; + rhs=new_rhs; + + rewrite_assignment(lhs, rhs); // rec. call + } +} + +/*******************************************************************\ + +Function: wp_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt wp_assign( + const code_assignt &code, + const exprt &post, + const namespacet &ns) +{ + exprt pre=post; + + exprt lhs=code.lhs(), + rhs=code.rhs(); + + rewrite_assignment(lhs, rhs); + + // replace lhs by rhs in pre + substitute_rec(pre, lhs, rhs, ns); + + // take care of non-determinism in the RHS + approximate_nondet(pre); + + return pre; +} + +/*******************************************************************\ + +Function: wp_assume + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt wp_assume( + const code_assumet &code, + const exprt &post, + const namespacet &ns) +{ + return implies_exprt(code.assumption(), post); +} + +/*******************************************************************\ + +Function: wp + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt wp( + const codet &code, + const exprt &post, + const namespacet &ns) +{ + const irep_idt &statement=code.get_statement(); + + if(statement==ID_assign) + return wp_assign(to_code_assign(code), post, ns); + else if(statement==ID_assume) + return wp_assume(to_code_assume(code), post, ns); + else if(statement==ID_skip) + return post; + else if(statement==ID_decl) + return post; + else if(statement==ID_assert) + return post; + else if(statement==ID_expression) + return post; + else if(statement==ID_printf) + return post; + else if(statement==ID_free) + return post; + else + throw "sorry, wp("+id2string(statement)+"...) not implemented"; +} diff --git a/src/goto-programs/wp.h b/src/goto-programs/wp.h new file mode 100644 index 00000000000..3d451000f22 --- /dev/null +++ b/src/goto-programs/wp.h @@ -0,0 +1,17 @@ +/*******************************************************************\ + +Module: Weakest Preconditions + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_WP_H +#define CPROVER_WP_H + +#include +#include + +exprt wp(const codet &code, const exprt &post, const namespacet &ns); + +#endif diff --git a/src/goto-programs/write_goto_binary.cpp b/src/goto-programs/write_goto_binary.cpp new file mode 100644 index 00000000000..8a732f73775 --- /dev/null +++ b/src/goto-programs/write_goto_binary.cpp @@ -0,0 +1,215 @@ +/*******************************************************************\ + +Module: Write GOTO binaries + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include + +#include "write_goto_binary.h" + +/*******************************************************************\ + +Function: goto_programt::write_goto_binary_v2 + + Inputs: + + Outputs: + + Purpose: Writes a goto program to disc, using goto binary format ver 2 + +\*******************************************************************/ + +bool write_goto_binary_v2( + std::ostream &out, + const contextt &lcontext, + const goto_functionst &functions, + irep_serializationt &irepconverter, + goto_function_serializationt &gfconverter) +{ + // first write symbol table + + write_long(out, lcontext.symbols.size()); + + forall_symbols(it, lcontext.symbols) + { + // In version 2, symbols are not converted to ireps, + // instead they are saved in a custom binary format + + const symbolt &sym = it->second; + + irepconverter.reference_convert(sym.type, out); + irepconverter.reference_convert(sym.value, out); + irepconverter.reference_convert(sym.location, out); + + irepconverter.write_string_ref(out, sym.name); + irepconverter.write_string_ref(out, sym.module); + irepconverter.write_string_ref(out, sym.base_name); + irepconverter.write_string_ref(out, sym.mode); + irepconverter.write_string_ref(out, sym.pretty_name); + + write_long(out, sym.ordering); + + unsigned flags=0; + flags = (flags << 1) | (int)sym.is_type; + flags = (flags << 1) | (int)sym.theorem; + flags = (flags << 1) | (int)sym.is_macro; + flags = (flags << 1) | (int)sym.is_exported; + flags = (flags << 1) | (int)sym.is_input; + flags = (flags << 1) | (int)sym.is_output; + flags = (flags << 1) | (int)sym.is_statevar; + flags = (flags << 1) | (int)sym.is_actual; + flags = (flags << 1) | (int)sym.free_var; + flags = (flags << 1) | (int)sym.binding; + flags = (flags << 1) | (int)sym.lvalue; + flags = (flags << 1) | (int)sym.static_lifetime; + flags = (flags << 1) | (int)sym.thread_local; + flags = (flags << 1) | (int)sym.file_local; + flags = (flags << 1) | (int)sym.is_extern; + flags = (flags << 1) | (int)sym.is_volatile; + + write_long(out, flags); + } + + // now write functions, but only those with body + + unsigned cnt=0; + forall_goto_functions(it, functions) + if(it->second.body_available) + cnt++; + + write_long(out, cnt); + + for(goto_functionst::function_mapt::const_iterator + it=functions.function_map.begin(); + it!=functions.function_map.end(); + it++) + { + if(it->second.body_available) + { + // In version 2, goto functions are not converted to ireps, + // instead they are saved in a custom binary format + + write_string(out, it->first.as_string()); // name + write_long(out, it->second.body.instructions.size()); // # instructions + + forall_goto_program_instructions(i_it, it->second.body) + { + const goto_programt::instructiont &instruction = *i_it; + + irepconverter.reference_convert(instruction.code, out); + irepconverter.write_string_ref(out, instruction.function); + irepconverter.reference_convert(instruction.location, out); + write_long(out, (long)instruction.type); + irepconverter.reference_convert(instruction.guard, out); + irepconverter.write_string_ref(out, irep_idt()); // former event + write_long(out, instruction.target_number); + + write_long(out, instruction.targets.size()); + + for(goto_programt::targetst::const_iterator + t_it=instruction.targets.begin(); + t_it!=instruction.targets.end(); + t_it++) + write_long(out, (*t_it)->target_number); + + write_long(out, instruction.labels.size()); + + for(goto_programt::instructiont::labelst::const_iterator + l_it=instruction.labels.begin(); + l_it!=instruction.labels.end(); + l_it++) + irepconverter.write_string_ref(out, *l_it); + } + } + } + + //irepconverter.output_map(f); + //irepconverter.output_string_map(f); + + return false; +} + +/*******************************************************************\ + +Function: goto_programt::write_goto_binary + + Inputs: + + Outputs: + + Purpose: Writes a goto program to disc + +\*******************************************************************/ + +bool write_goto_binary( + std::ostream &out, + const contextt &lcontext, + const goto_functionst &functions, + int version) +{ + // header + out << char(0x7f) << "GBF"; + write_long(out, version); + + irep_serializationt::ireps_containert irepc; + irep_serializationt irepconverter(irepc); + goto_function_serializationt gfconverter(irepc); + + switch(version) + { + case 1: + throw "version 1 no longer supported"; + + case 2: + return write_goto_binary_v2( + out, lcontext, functions, + irepconverter, + gfconverter); + + default: + throw "Unknown goto binary version"; + } + + return false; +} + +/*******************************************************************\ + +Function: goto_programt::write_goto_binary + + Inputs: + + Outputs: + + Purpose: Writes a goto program to disc + +\*******************************************************************/ + +bool write_goto_binary( + const std::string &filename, + const contextt &context, + const goto_functionst &goto_functions, + message_handlert &message_handler) +{ + std::ofstream out(filename.c_str(), std::ios::binary); + + if(!out) + { + messaget message(message_handler); + message.error( + std::string("Failed to open `")+filename+"'"); + return true; + } + + return write_goto_binary(out, context, goto_functions); +} + diff --git a/src/goto-programs/write_goto_binary.h b/src/goto-programs/write_goto_binary.h new file mode 100644 index 00000000000..559fc16193d --- /dev/null +++ b/src/goto-programs/write_goto_binary.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: Write GOTO binaries + +Author: CM Wintersteiger + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_WRITE_GOTO_BINARY_H_ +#define CPROVER_GOTO_PROGRAMS_WRITE_GOTO_BINARY_H_ + +#define GOTO_BINARY_VERSION 2 + +#include + +#include +#include +#include + +bool write_goto_binary( + std::ostream &out, + const contextt &context, + const goto_functionst &goto_functions, + int version=GOTO_BINARY_VERSION); + +bool write_goto_binary( + const std::string &filename, + const contextt &lcontext, + const goto_functionst &goto_functions, + message_handlert &message_handler); + +#endif diff --git a/src/goto-symex/Makefile b/src/goto-symex/Makefile new file mode 100644 index 00000000000..2c9281a2f5a --- /dev/null +++ b/src/goto-symex/Makefile @@ -0,0 +1,25 @@ +SRC = symex_target.cpp symex_target_equation.cpp basic_symex.cpp \ + symex_main.cpp goto_trace.cpp build_goto_trace.cpp \ + symex_function.cpp goto_symex_state.cpp symex_dereference.cpp \ + symex_goto.cpp builtin_functions.cpp slice.cpp symex_other.cpp \ + slice_by_trace.cpp xml_goto_trace.cpp symex_decl.cpp \ + precondition.cpp postcondition.cpp symex_clean_expr.cpp \ + symex_dereference_state.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) + +INCLUDES= -I .. -I ../util + +include ../config.inc +include ../common + +all: goto-symex$(LIBEXT) + +############################################################################### + +goto-symex$(LIBEXT): $(OBJ) + $(LINKLIB) + +clean: + rm -f $(OBJ) goto-symex$(LIBEXT) + diff --git a/src/goto-symex/basic_symex.cpp b/src/goto-symex/basic_symex.cpp new file mode 100644 index 00000000000..28b17273b9b --- /dev/null +++ b/src/goto-symex/basic_symex.cpp @@ -0,0 +1,593 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic_symex.h" + +unsigned basic_symext::nondet_count=0; +unsigned basic_symext::dynamic_counter=0; + +/*******************************************************************\ + +Function: basic_symext::do_simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::do_simplify(exprt &expr) +{ + if(options.get_bool_option("simplify")) + simplify(expr, ns); +} + +/*******************************************************************\ + +Function: basic_symext::symex + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex(statet &state, const codet &code) +{ + const irep_idt &statement=code.get_statement(); + + if(statement==ID_block) + symex_block(state, code); + else if(statement==ID_assign) + symex_assign(state, to_code_assign(code)); + else if(statement==ID_decl) + { + // behaves like non-deterministic assignment + if(code.operands().size()!=1) + throw "decl expected to have one operand"; + + exprt rhs(ID_nondet_symbol, code.op0().type()); + rhs.set(ID_identifier, "symex::nondet"+i2string(nondet_count++)); + rhs.location()=code.location(); + + exprt new_lhs(code.op0()); + read(new_lhs); + + guardt guard; // NOT the state guard! + symex_assign_rec(state, new_lhs, rhs, guard, VISIBLE); + } + else if(statement==ID_expression) + { + // ignore + } + else if(statement==ID_cpp_delete || + statement=="cpp_delete[]") + symex_cpp_delete(state, code); + else if(statement==ID_free) + { + // like skip + } + else if(statement==ID_nondet) + { + // like skip + } + else if(statement==ID_printf) + symex_printf(state, static_cast(get_nil_irep()), code); + else if(statement==ID_asm) + { + // ignore for now + } + else + { + std::cerr << code.pretty() << std::endl; + throw "unexpected statement: "+id2string(statement); + } +} + +/*******************************************************************\ + +Function: basic_symext::symex_block + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_block(statet &state, const codet &code) +{ + forall_operands(it, code) + symex(state, to_code(*it)); +} + +/*******************************************************************\ + +Function: basic_symext::symex_assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_assign(statet &state, const code_assignt &code) +{ + exprt lhs=code.lhs(); + exprt rhs=code.rhs(); + + read(lhs); + read(rhs); + + replace_nondet(lhs); + replace_nondet(rhs); + + if(rhs.id()==ID_sideeffect) + { + const side_effect_exprt &side_effect_expr=to_side_effect_expr(rhs); + const irep_idt &statement=side_effect_expr.get_statement(); + + if(statement==ID_function_call) + { + assert(side_effect_expr.operands().size()!=0); + + if(side_effect_expr.op0().id()!=ID_symbol) + throw "symex_assign: expected symbol as function"; + + const irep_idt &identifier= + to_symbol_expr(side_effect_expr.op0()).get_identifier(); + + throw "symex_assign: unexpected function call: "+id2string(identifier); + } + else if(statement==ID_cpp_new || + statement=="cpp_new[]") + symex_cpp_new(state, lhs, side_effect_expr); + else if(statement==ID_malloc) + symex_malloc(state, lhs, side_effect_expr); + else if(statement==ID_printf) + symex_printf(state, lhs, side_effect_expr); + else + throw "symex_assign: unexpected sideeffect: "+id2string(statement); + } + else + { + guardt guard; // NOT the state guard! + symex_assign_rec(state, lhs, rhs, guard, VISIBLE); + } +} + +/*******************************************************************\ + +Function: basic_symext::read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::read(exprt &expr) +{ +} + +/*******************************************************************\ + +Function: basic_symext::symex_assign_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_assign_rec( + statet &state, + const exprt &lhs, + const exprt &rhs, + guardt &guard, + visibilityt visibility) +{ + if(lhs.id()==ID_symbol) + symex_assign_symbol(state, to_symbol_expr(lhs), rhs, guard, visibility); + else if(lhs.id()==ID_index) + symex_assign_array(state, to_index_expr(lhs), rhs, guard, visibility); + else if(lhs.id()==ID_member) + symex_assign_member(state, to_member_expr(lhs), rhs, guard, visibility); + else if(lhs.id()==ID_if) + symex_assign_if(state, to_if_expr(lhs), rhs, guard, visibility); + else if(lhs.id()==ID_typecast) + symex_assign_typecast(state, to_typecast_expr(lhs), rhs, guard, visibility); + else if(lhs.id()==ID_string_constant || + lhs.id()=="NULL-object" || + lhs.id()=="zero_string" || + lhs.id()=="is_zero_string" || + lhs.id()=="zero_string_length") + { + // ignore + } + else if(lhs.id()==ID_byte_extract_little_endian || + lhs.id()==ID_byte_extract_big_endian) + symex_assign_byte_extract(state, lhs, rhs, guard, visibility); + else + throw "assignment to `"+lhs.id_string()+"' not handled"; +} + +/*******************************************************************\ + +Function: basic_symext::symex_assign_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_assign_symbol( + statet &state, + const symbol_exprt &lhs, + const exprt &rhs, + guardt &guard, + visibilityt visibility) +{ + exprt new_rhs=rhs; + + // put assignment guard into the rhs + if(!guard.empty()) + { + if_exprt tmp_new_rhs; + tmp_new_rhs.type()=new_rhs.type(); + tmp_new_rhs.cond()=guard.as_expr(); + tmp_new_rhs.true_case()=new_rhs; + tmp_new_rhs.false_case()=lhs; + tmp_new_rhs.swap(new_rhs); + } + + exprt original_lhs=lhs; + state.get_original_name(original_lhs); + + state.rename(new_rhs, ns); + do_simplify(new_rhs); + + exprt new_lhs=lhs; + state.assignment(new_lhs, new_rhs, ns, constant_propagation); + + guardt tmp_guard(state.guard); + tmp_guard.append(guard); + + // do the assignment + target.assignment( + tmp_guard, + new_lhs, original_lhs, + new_rhs, + state.source, + symex_targett::STATE); +} + +/*******************************************************************\ + +Function: basic_symext::symex_assign_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_assign_typecast( + statet &state, + const typecast_exprt &lhs, + const exprt &rhs, + guardt &guard, + visibilityt visibility) +{ + // these may come from dereferencing on the lhs + + assert(lhs.operands().size()==1); + + exprt rhs_typecasted=rhs; + + rhs_typecasted.make_typecast(lhs.op0().type()); + + symex_assign_rec(state, lhs.op0(), rhs_typecasted, guard, visibility); +} + +/*******************************************************************\ + +Function: basic_symext::symex_assign_array + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_assign_array( + statet &state, + const index_exprt &lhs, + const exprt &rhs, + guardt &guard, + visibilityt visibility) +{ + // lhs must be index operand + // that takes two operands: the first must be an array + // the second is the index + + if(lhs.operands().size()!=2) + throw "index must have two operands"; + + const exprt &lhs_array=lhs.op0(); + const exprt &lhs_index=lhs.op1(); + const typet &lhs_type=ns.follow(lhs_array.type()); + + if(lhs_type.id()!=ID_array) + throw "index must take array type operand, but got `"+ + lhs_type.id_string()+"'"; + + // turn + // a[i]=e + // into + // a'==a WITH [i:=e] + + exprt new_rhs(ID_with, lhs_type); + new_rhs.copy_to_operands(lhs_array, lhs_index, rhs); + + symex_assign_rec(state, lhs_array, new_rhs, guard, visibility); +} + +/*******************************************************************\ + +Function: basic_symext::symex_assign_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_assign_member( + statet &state, + const member_exprt &lhs, + const exprt &rhs, + guardt &guard, + visibilityt visibility) +{ + // symbolic execution of a struct member assignment + + // lhs must be member operand + // that takes one operands, which must be a structure + + exprt lhs_struct=lhs.op0(); + typet struct_type=ns.follow(lhs_struct.type()); + + if(struct_type.id()!=ID_struct && + struct_type.id()!=ID_union) + throw "member must take struct/union type operand but got " + +struct_type.pretty(); + + const irep_idt &component_name=lhs.get(ID_component_name); + + // typecasts involved? C++ does that for inheritance. + if(lhs_struct.id()==ID_typecast) + { + assert(lhs_struct.operands().size()==1); + + if(lhs_struct.op0().id()=="NULL-object") + { + // ignore, and give up + return; + } + else + { + // remove the type cast, we assume that the member is there + exprt tmp=lhs_struct.op0(); + struct_type=ns.follow(tmp.type()); + + if(struct_type.id()==ID_struct || + struct_type.id()==ID_union) + lhs_struct=tmp; + else + return; // ignore and give up + } + } + + // turn + // a.c=e + // into + // a'==a WITH [c:=e] + + exprt new_rhs(ID_with, struct_type); + new_rhs.copy_to_operands(lhs_struct, exprt(ID_member_name), rhs); + new_rhs.op1().set(ID_component_name, component_name); + + symex_assign_rec(state, lhs_struct, new_rhs, guard, visibility); +} + +/*******************************************************************\ + +Function: basic_symext::symex_assign_if + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_assign_if( + statet &state, + const if_exprt &lhs, + const exprt &rhs, + guardt &guard, + visibilityt visibility) +{ + // we have (c?a:b)=e; + + unsigned old_guard_size=guard.size(); + + exprt renamed_guard=lhs.cond(); + state.rename(renamed_guard, ns); + do_simplify(renamed_guard); + + if(!renamed_guard.is_false()) + { + guard.add(renamed_guard); + symex_assign_rec(state, lhs.true_case(), rhs, guard, visibility); + guard.resize(old_guard_size); + } + + if(!renamed_guard.is_true()) + { + guard.add(gen_not(renamed_guard)); + symex_assign_rec(state, lhs.false_case(), rhs, guard, visibility); + guard.resize(old_guard_size); + } +} + +/*******************************************************************\ + +Function: basic_symext::symex_assign_byte_extract + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_assign_byte_extract( + statet &state, + const exprt &lhs, + const exprt &rhs, + guardt &guard, + visibilityt visibility) +{ + // we have byte_extract_X(l, b)=r + // turn into l=byte_update_X(l, b, r) + + if(lhs.operands().size()!=2) + throw "byte_extract must have two operands"; + + exprt new_rhs; + + if(lhs.id()==ID_byte_extract_little_endian) + new_rhs.id("byte_update_little_endian"); + else if(lhs.id()==ID_byte_extract_big_endian) + new_rhs.id("byte_update_big_endian"); + else + assert(false); + + new_rhs.copy_to_operands(lhs.op0(), lhs.op1(), rhs); + new_rhs.type()=lhs.op0().type(); + + symex_assign_rec(state, lhs.op0(), new_rhs, guard, visibility); +} + +/*******************************************************************\ + +Function: basic_symext::replace_nondet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::replace_nondet(exprt &expr) +{ + if(expr.id()==ID_sideeffect && + expr.get(ID_statement)==ID_nondet) + { + exprt new_expr(ID_nondet_symbol, expr.type()); + new_expr.set(ID_identifier, "symex::nondet"+i2string(nondet_count++)); + new_expr.location()=expr.location(); + expr.swap(new_expr); + } + else + Forall_operands(it, expr) + replace_nondet(*it); +} + +/*******************************************************************\ + +Function: basic_symex + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symex( + const codet &code, + const namespacet &ns, + symex_targett &target, + goto_symex_statet &state) +{ + contextt new_context; + basic_symext basic_symex(ns, new_context, target); + basic_symex.symex(state, code); +} + +/*******************************************************************\ + +Function: basic_symex + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symex( + const codet &code, + const namespacet &ns, + symex_targett &target) +{ + contextt new_context; + basic_symext basic_symex(ns, new_context, target); + goto_symex_statet state; + basic_symex.symex(state, code); +} diff --git a/src/goto-symex/basic_symex.h b/src/goto-symex/basic_symex.h new file mode 100644 index 00000000000..3ec69f37a6a --- /dev/null +++ b/src/goto-symex/basic_symex.h @@ -0,0 +1,103 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_BASIC_SYMEX_H +#define CPROVER_BASIC_SYMEX_H + +#include +#include + +#include +#include +#include +#include +#include + +#include "symex_target.h" +#include "goto_symex_state.h" + +class basic_symext +{ +public: + basic_symext( + const namespacet &_ns, + contextt &_new_context, + symex_targett &_target): + constant_propagation(true), + new_context(_new_context), + ns(_ns), + target(_target) + { + options.set_option("simplify", true); + } + + virtual ~basic_symext() { } + + typedef goto_symex_statet statet; + + virtual void symex(statet &state, const codet &code); + bool constant_propagation; + + optionst options; + contextt &new_context; + +protected: + const namespacet &ns; + symex_targett ⌖ + + virtual void do_simplify(exprt &expr); + + virtual void symex_block(statet &state, const codet &code); + virtual void symex_assign(statet &state, const code_assignt &code); + + typedef enum { VISIBLE, HIDDEN } visibilityt; + + void symex_assign_rec(statet &state, const exprt &lhs, const exprt &rhs, guardt &guard, visibilityt visibility); + void symex_assign_symbol(statet &state, const symbol_exprt &lhs, const exprt &rhs, guardt &guard, visibilityt visibility); + void symex_assign_typecast(statet &state, const typecast_exprt &lhs, const exprt &rhs, guardt &guard, visibilityt visibility); + void symex_assign_array(statet &state, const index_exprt &lhs, const exprt &rhs, guardt &guard, visibilityt visibility); + void symex_assign_member(statet &state, const member_exprt &lhs, const exprt &rhs, guardt &guard, visibilityt visibility); + void symex_assign_if(statet &state, const if_exprt &lhs, const exprt &rhs, guardt &guard, visibilityt visibility); + void symex_assign_byte_extract(statet &state, const exprt &lhs, const exprt &rhs, guardt &guard, visibilityt visibility); + + virtual void symex_malloc (statet &state, const exprt &lhs, const side_effect_exprt &code); + virtual void symex_cpp_delete (statet &state, const codet &code); + virtual void symex_cpp_new (statet &state, const exprt &lhs, const side_effect_exprt &code); + virtual void symex_fkt (statet &state, const code_function_callt &code); + virtual void symex_macro (statet &state, const code_function_callt &code); + virtual void symex_trace (statet &state, const code_function_callt &code); + virtual void symex_printf (statet &state, const exprt &lhs, const exprt &rhs); + virtual void symex_input (statet &state, const codet &code); + virtual void symex_output (statet &state, const codet &code); + + static unsigned nondet_count; + static unsigned dynamic_counter; + + void read(exprt &expr); + void replace_nondet(exprt &expr); + + #if 0 + void assignment( + statet &state, + const exprt &lhs, + exprt &rhs); + #endif +}; + +void basic_symex( + const codet &code, + const namespacet &ns, + symex_targett &target, + goto_symex_statet &state); + +void basic_symex( + const codet &code, + const namespacet &ns, + symex_targett &target); + +#endif diff --git a/src/goto-symex/build_goto_trace.cpp b/src/goto-symex/build_goto_trace.cpp new file mode 100644 index 00000000000..c117c3729b1 --- /dev/null +++ b/src/goto-symex/build_goto_trace.cpp @@ -0,0 +1,105 @@ +/*******************************************************************\ + +Module: Traces of GOTO Programs + +Author: Daniel Kroening + + Date: July 2005 + +\*******************************************************************/ + +#include + +#include "build_goto_trace.h" + +/*******************************************************************\ + +Function: build_goto_trace + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void build_goto_trace( + const symex_target_equationt &target, + const prop_convt &prop_conv, + goto_tracet &goto_trace) +{ + unsigned step_nr=0; + + for(symex_target_equationt::SSA_stepst::const_iterator + it=target.SSA_steps.begin(); + it!=target.SSA_steps.end(); + it++) + { + const symex_target_equationt::SSA_stept &SSA_step=*it; + + if(prop_conv.prop.l_get(SSA_step.guard_literal)!=tvt(true)) + continue; + + if(it->is_assignment() && + SSA_step.assignment_type==symex_target_equationt::HIDDEN) + continue; + + step_nr++; + + goto_trace.steps.push_back(goto_trace_stept()); + goto_trace_stept &goto_trace_step=goto_trace.steps.back(); + + goto_trace_step.thread_nr=SSA_step.source.thread_nr; + goto_trace_step.lhs=SSA_step.lhs; + goto_trace_step.rhs=SSA_step.rhs; + goto_trace_step.pc=SSA_step.source.pc; + goto_trace_step.comment=SSA_step.comment; + goto_trace_step.original_lhs=SSA_step.original_lhs; + goto_trace_step.type=SSA_step.type; + goto_trace_step.step_nr=step_nr; + goto_trace_step.format_string=SSA_step.format_string; + goto_trace_step.io_id=SSA_step.io_id; + goto_trace_step.formatted=SSA_step.formatted; + + if(SSA_step.lhs.is_not_nil()) + goto_trace_step.value=prop_conv.get(SSA_step.lhs); + + for(std::list::const_iterator + j=SSA_step.converted_io_args.begin(); + j!=SSA_step.converted_io_args.end(); + j++) + { + const exprt &arg=*j; + if(arg.is_constant() || + arg.id()==ID_string_constant) + goto_trace_step.io_args.push_back(arg); + else + { + exprt tmp=prop_conv.get(arg); + goto_trace_step.io_args.push_back(tmp); + } + } + + if(SSA_step.is_assert() || + SSA_step.is_assume()) + { + goto_trace_step.cond_expr=SSA_step.cond_expr; + + goto_trace_step.cond_value= + prop_conv.prop.l_get(SSA_step.cond_literal).is_true(); + } + + if(SSA_step.is_assert()) + { + // we stop after a violated assertion + if(!goto_trace_step.cond_value) + break; + } + else if(SSA_step.is_assume()) + { + // assumptions can't be false + assert(goto_trace_step.cond_value); + } + } +} diff --git a/src/goto-symex/build_goto_trace.h b/src/goto-symex/build_goto_trace.h new file mode 100644 index 00000000000..73614c1dc0e --- /dev/null +++ b/src/goto-symex/build_goto_trace.h @@ -0,0 +1,23 @@ +/*******************************************************************\ + +Module: Traces of GOTO Programs + +Author: Daniel Kroening + +Date: July 2005 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_BUILD_GOTO_TRACE_H +#define CPROVER_GOTO_SYMEX_BUILD_GOTO_TRACE_H + +#include "goto_trace.h" +#include "symex_target_equation.h" +#include "goto_symex_state.h" + +void build_goto_trace( + const symex_target_equationt &target, + const prop_convt &prop_conv, + goto_tracet &goto_trace); + +#endif diff --git a/src/goto-symex/builtin_functions.cpp b/src/goto-symex/builtin_functions.cpp new file mode 100644 index 00000000000..f7ec481e861 --- /dev/null +++ b/src/goto-symex/builtin_functions.cpp @@ -0,0 +1,443 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "basic_symex.h" + +/*******************************************************************\ + +Function: basic_symext::symex_malloc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_malloc( + statet &state, + const exprt &lhs, + const side_effect_exprt &code) +{ + if(code.operands().size()!=1) + throw "malloc expected to have one operand"; + + if(lhs.is_nil()) + return; // ignore + + dynamic_counter++; + + exprt size=code.op0(); + + // value + symbolt symbol; + + symbol.base_name="dynamic_object"+i2string(dynamic_counter); + symbol.name="symex_dynamic::"+id2string(symbol.base_name); + symbol.lvalue=true; + symbol.type=array_typet(uchar_type(), size); + symbol.type.set("#dynamic", true); + symbol.mode=ID_C; + + new_context.add(symbol); + + exprt rhs(ID_address_of, pointer_typet()); + + exprt index_expr(ID_index, symbol.type.subtype()); + index_expr.copy_to_operands(symbol_expr(symbol), gen_zero(index_type())); + rhs.type().subtype()=symbol.type.subtype(); + rhs.move_to_operands(index_expr); + + if(rhs.type()!=lhs.type()) + rhs.make_typecast(lhs.type()); + + state.rename(rhs, ns); + + guardt guard; + symex_assign_rec(state, lhs, rhs, guard, VISIBLE); +} + +/*******************************************************************\ + +Function: basic_symext::get_string_argument + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +irep_idt get_string_argument(const exprt &src) +{ + if(src.id()==ID_typecast) + { + assert(src.operands().size()==1); + return get_string_argument(src.op0()); + } + else if(src.id()==ID_address_of) + { + assert(src.operands().size()==1); + if(src.op0().id()==ID_index) + { + assert(src.op0().operands().size()==2); + + if(src.op0().op0().id()==ID_string_constant && + src.op0().op1().is_zero()) + { + const exprt &fmt_str=src.op0().op0(); + return fmt_str.get_string(ID_value); + } + } + } + + return ""; +} + +/*******************************************************************\ + +Function: basic_symext::symex_printf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void basic_symext::symex_printf( + statet &state, + const exprt &lhs, + const exprt &rhs) +{ + if(rhs.operands().empty()) + throw "printf expected to have at least one operand"; + + exprt tmp_rhs=rhs; + state.rename(tmp_rhs, ns); + + const exprt::operandst &operands=tmp_rhs.operands(); + std::list args; + + for(unsigned i=1; i args; + + for(unsigned i=1; i args; + + for(unsigned i=1; i=debug_lvl) + { + std::list vars; + + irep_idt event=code.arguments()[1].op0().get(ID_value); + + for(unsigned j=2; j +#include + +void replace_dynamic_allocation( + const namespacet &ns, + exprt &expr); + +#endif diff --git a/src/goto-symex/goto_symex.h b/src/goto-symex/goto_symex.h new file mode 100644 index 00000000000..596b3f17ce2 --- /dev/null +++ b/src/goto-symex/goto_symex.h @@ -0,0 +1,170 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_GOTO_SYMEX_H +#define CPROVER_GOTO_SYMEX_GOTO_SYMEX_H + +#include +#include + +#include "basic_symex.h" + +class goto_symext: + public basic_symext +{ +public: + goto_symext( + const namespacet &_ns, + contextt &_new_context, + symex_targett &_target): + basic_symext(_ns, _new_context, _target), + total_claims(0), + remaining_claims(0), + guard_identifier("goto_symex::\\guard") + { + options.set_option("assertions", true); + } + + // all at once + virtual void operator()( + const goto_functionst &goto_functions); + + virtual void operator()( + const goto_functionst &goto_functions, + const goto_programt &goto_program); + + // start in a given state + virtual void operator()( + statet &state, + const goto_functionst &goto_functions, + const goto_programt &goto_program); + + // execute one step + virtual void symex_step( + const goto_functionst &goto_functions, + statet &state); + + // these bypass the target maps + virtual void symex_step_return(statet &state); + virtual void symex_step_goto(statet &state, bool taken); + + // statistics + unsigned total_claims, remaining_claims; + +protected: + friend class symex_dereference_statet; + + void new_name(symbolt &symbol); + + // this does the following: + // a) rename non-det choices + // b) remove pointer dereferencing + // c) rewrite array_equal expression into equality + void clean_expr( + exprt &expr, statet &state, bool write); + + void replace_array_equal(exprt &expr); + void process_array_expr(exprt &expr); + + virtual void dereference( + exprt &expr, + statet &state, + const bool write); + + void dereference_rec( + exprt &expr, + guardt &guard, + class dereferencet &dereference, + const bool write); + + // guards + + irep_idt guard_identifier; + + // symex + + virtual void symex_goto(statet &state); + virtual void symex_decl(statet &state); + virtual void symex_return(statet &state); + + virtual void symex_other( + const goto_functionst &goto_functions, + statet &state); + + virtual void claim( + const exprt &expr, + const std::string &msg, + statet &state); + + // gotos + void merge_gotos(statet &state); + + void merge_value_sets( + const statet::goto_statet &goto_state, + statet &dest); + + void phi_function( + const statet::goto_statet &goto_state, + statet &state); + + virtual bool get_unwind( + const symex_targett::sourcet &source, + unsigned unwind); + + virtual void loop_bound_exceeded(statet &state, const exprt &guard); + + // function calls + + void pop_frame(statet &state); + void return_assignment(statet &state); + + virtual void no_body(const irep_idt &identifier) + { + } + + virtual void symex_function_call( + const goto_functionst &goto_functions, + statet &state, + const code_function_callt &call); + + virtual void symex_end_of_function(statet &state); + + virtual void symex_function_call_symbol( + const goto_functionst &goto_functions, + statet &state, + const code_function_callt &call); + + virtual void symex_function_call_code( + const goto_functionst &goto_functions, + statet &state, + const code_function_callt &call); + + virtual bool get_unwind_recursion( + const irep_idt &identifier, + unsigned unwind); + + void argument_assignments( + const code_typet &function_type, + statet &state, + const exprt::operandst &arguments); + + void locality( + unsigned frame_counter, + statet &state, + const goto_functionst::goto_functiont &goto_function); + + void add_end_of_function( + exprt &code, + const irep_idt &identifier); + + std::map function_unwind; + std::map function_frame; + std::map unwind_map; +}; + +#endif diff --git a/src/goto-symex/goto_symex_state.cpp b/src/goto-symex/goto_symex_state.cpp new file mode 100644 index 00000000000..83afb2b34f3 --- /dev/null +++ b/src/goto-symex/goto_symex_state.cpp @@ -0,0 +1,745 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include "goto_symex_state.h" + +/*******************************************************************\ + +Function: goto_symex_statet::goto_symex_statet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +goto_symex_statet::goto_symex_statet() +{ + depth=0; + new_frame(); +} + +/*******************************************************************\ + +Function: goto_symex_statet::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::initialize(const goto_functionst &goto_functions) +{ + goto_functionst::function_mapt::const_iterator it= + goto_functions.function_map.find(ID_main); + + if(it==goto_functions.function_map.end()) + throw "main symbol not found; please set an entry point"; + + const goto_programt &body=it->second.body; + + source.is_set=true; + source.pc=body.instructions.begin(); + top().end_of_function=--body.instructions.end(); + top().calling_location=top().end_of_function; +} + +/*******************************************************************\ + +Function: goto_symex_statet::name_frame + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string goto_symex_statet::level1t::name( + const irep_idt &identifier, + unsigned frame) const +{ + return id2string(identifier)+"@"+i2string(frame); +} + +/*******************************************************************\ + +Function: goto_symex_statet::name_count + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string goto_symex_statet::level2t::name( + const irep_idt &identifier, + unsigned count) const +{ + return id2string(identifier)+"#"+i2string(count); +} + +/*******************************************************************\ + +Function: goto_symex_statet::current_number + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned goto_symex_statet::level2t::current_number( + const irep_idt &identifier) const +{ + current_namest::const_iterator it=current_names.find(identifier); + if(it==current_names.end()) return 0; + return it->second.count; +} + +/*******************************************************************\ + +Function: goto_symex_statet::level1t::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string goto_symex_statet::level1t::operator()( + const irep_idt &identifier) const +{ + current_namest::const_iterator it= + current_names.find(identifier); + + if(it==current_names.end()) + return id2string(identifier); + + return name(identifier, it->second); +} + +/*******************************************************************\ + +Function: goto_symex_statet::level2t::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string goto_symex_statet::level2t::operator()( + const irep_idt &identifier) const +{ + current_namest::const_iterator it= + current_names.find(identifier); + + if(it==current_names.end()) + return name(identifier, 0); + + return name(identifier, it->second.count); +} + +/*******************************************************************\ + +Function: goto_symex_statet::constant_propagation + + Inputs: + + Outputs: + + Purpose: This function determines what expressions are to + be propagated as "constants" + +\*******************************************************************/ + +bool goto_symex_statet::constant_propagation(const exprt &expr) const +{ + if(expr.is_constant()) return true; + + if(expr.id()==ID_address_of) + { + const address_of_exprt &address_of_expr=to_address_of_expr(expr); + + return constant_propagation_reference(address_of_expr.object()); + } + else if(expr.id()==ID_typecast) + { + const typecast_exprt &typecast_expr=to_typecast_expr(expr); + + return constant_propagation(typecast_expr.op()); + } + else if(expr.id()==ID_plus) + { + forall_operands(it, expr) + if(!constant_propagation(*it)) + return false; + + return true; + } + else if(expr.id()==ID_array_of) + { + /* This is slow + if(expr.operands().size()==1) + return constant_propagation(expr.op0()); + */ + return false; + } + else if(expr.id()==ID_with) + { + // this is bad + /* + forall_operands(it, expr) + if(!constant_propagation(expr.op0())) + return false; + + return true; + */ + return false; + } + else if(expr.id()==ID_struct) + { + forall_operands(it, expr) + if(!constant_propagation(*it)) + return false; + + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: goto_symex_statet::constant_propagation_reference + + Inputs: + + Outputs: + + Purpose: this function determines which reference-typed + expressions are constant + +\*******************************************************************/ + +bool goto_symex_statet::constant_propagation_reference(const exprt &expr) const +{ + if(expr.id()==ID_symbol) + return true; + else if(expr.id()==ID_index) + { + const index_exprt &index_expr=to_index_expr(expr); + + return constant_propagation_reference(index_expr.array()) && + constant_propagation(index_expr.index()); + } + else if(expr.id()==ID_member) + { + if(expr.operands().size()!=1) + throw "member expects one operand"; + + return constant_propagation_reference(expr.op0()); + } + else if(expr.id()==ID_string_constant) + return true; + + return false; +} + +/*******************************************************************\ + +Function: goto_symex_statet::assignment + + Inputs: + + Outputs: + + Purpose: write to a variable + +\*******************************************************************/ + +void goto_symex_statet::assignment( + exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool record_value) +{ + assert(lhs.id()==ID_symbol); + + // the type might need renaming + rename(lhs.type(), ns); + + const irep_idt &identifier= + lhs.get(ID_identifier); + + // identifier should be l0 or l1, make sure it's l1 + + const std::string l1_identifier=top().level1(identifier); + + // do the l2 renaming + level2t::valuet &entry=level2.current_names[l1_identifier]; + + entry.count++; + + level2.rename(l1_identifier, entry.count); + + lhs.set(ID_identifier, level2.name(l1_identifier, entry.count)); + + if(record_value) + { + // for constant propagation + + if(constant_propagation(rhs)) + entry.constant=rhs; + else + entry.constant.make_nil(); + } + else + entry.constant.make_nil(); + + { + // update value sets + value_sett::expr_sett rhs_value_set; + exprt l1_rhs(rhs); + level2.get_original_name(l1_rhs); + + symbol_exprt l1_lhs(l1_identifier, lhs.type()); + + value_set.assign(l1_lhs, l1_rhs, ns); + } + + #if 0 + std::cout << "Assigning " << identifier << std::endl; + value_set.output(ns, std::cout); + std::cout << "**********************" << std::endl; + #endif +} + +/*******************************************************************\ + +Function: goto_symex_statet::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::rename(exprt &expr, const namespacet &ns) +{ + // rename all the symbols with their last known value + + rename(expr.type(), ns); + + if(expr.id()==ID_symbol) + { + top().level1.rename(expr); + level2.rename(expr); + } + else if(expr.id()==ID_address_of) + { + assert(expr.operands().size()==1); + rename_address(expr.op0(), ns); + } + else + { + // do this recursively + Forall_operands(it, expr) + rename(*it, ns); + } +} + +/*******************************************************************\ + +Function: goto_symex_statet::rename_address + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::rename_address( + exprt &expr, + const namespacet &ns) +{ + // rename all the symbols with their last known value + + rename(expr.type(), ns); + + if(expr.id()==ID_symbol) + { + // only do L1! + top().level1.rename(expr); + } + else if(expr.id()==ID_index) + { + assert(expr.operands().size()==2); + rename_address(expr.op0(), ns); + + // the index is not an address + rename(expr.op1(), ns); + } + else if(expr.id()==ID_if) + { + // the condition is not an address + if_exprt &if_expr=to_if_expr(expr); + rename(if_expr.cond(), ns); + rename_address(if_expr.true_case(), ns); + rename_address(if_expr.false_case(), ns); + } + else if(expr.id()==ID_member) + { + rename_address(to_member_expr(expr).struct_op(), ns); + } + else + { + // do this recursively; we assume here + // that all the operands are addresses + Forall_operands(it, expr) + rename_address(*it, ns); + } +} + +/*******************************************************************\ + +Function: goto_symex_statet::level1t::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::level1t::rename(exprt &expr) +{ + // rename all the symbols with their last known value + + rename(expr.type()); + + if(expr.id()==ID_symbol) + { + const irep_idt &identifier=expr.get(ID_identifier); + + // first see if it's already an l1 name + + if(original_identifiers.find(identifier)!= + original_identifiers.end()) + return; + + const current_namest::const_iterator it= + current_names.find(identifier); + + if(it!=current_names.end()) + expr.set(ID_identifier, name(identifier, it->second)); + } + else if(expr.id()==ID_address_of) + { + assert(expr.operands().size()==1); + rename(expr.op0()); + } + else + { + // do this recursively + Forall_operands(it, expr) + rename(*it); + } +} +/*******************************************************************\ + +Function: goto_symex_statet::level2t::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::level2t::rename(exprt &expr) +{ + // rename all the symbols with their last known value + + rename(expr.type()); + + if(expr.id()==ID_symbol) + { + const irep_idt &identifier=expr.get(ID_identifier); + + // first see if it's already an l2 name + + if(original_identifiers.find(identifier)!= + original_identifiers.end()) + return; + + const current_namest::const_iterator it= + current_names.find(identifier); + + if(it!=current_names.end()) + { + if(it->second.constant.is_not_nil()) + expr=it->second.constant; + else + expr.set(ID_identifier, name(identifier, it->second.count)); + } + else + { + std::string new_identifier=name(identifier, 0); + original_identifiers[new_identifier]=identifier; + expr.set(ID_identifier, new_identifier); + } + } + else if(expr.id()==ID_address_of) + { + // do nothing + } + else + { + // do this recursively + Forall_operands(it, expr) + rename(*it); + } +} + +/*******************************************************************\ + +Function: goto_symex_statet::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::rename( + typet &type, + const namespacet &ns) +{ + // rename all the symbols with their last known value + + if(type.id()==ID_array) + { + rename(type.subtype(), ns); + rename(static_cast(type.add(ID_size)), ns); + } + else if(type.id()==ID_struct || + type.id()==ID_union || + type.id()==ID_class) + { + // TODO + } + else if(type.id()==ID_pointer) + { + // rename(type.subtype(), ns); + // don't do this, or it might get cyclic + } + else if(type.id()==ID_symbol) + { + const symbolt &symbol=ns.lookup(type.get(ID_identifier)); + type=symbol.type; + rename(type, ns); + } +} + +/*******************************************************************\ + +Function: goto_symex_statet::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::renaming_levelt::rename(typet &type) +{ + // rename all the symbols with their last known value + + if(type.id()==ID_array) + { + rename(type.subtype()); + rename((exprt &)type.add(ID_size)); + } + else if(type.id()==ID_struct || + type.id()==ID_union || + type.id()==ID_class) + { + // TODO + } + else if(type.id()==ID_pointer) + { + rename(type.subtype()); + } +} + +/*******************************************************************\ + +Function: goto_symex_statet::get_original_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::get_original_name(exprt &expr) const +{ + Forall_operands(it, expr) + get_original_name(*it); + + if(expr.id()==ID_symbol) + { + level2.get_original_name(expr); + top().level1.get_original_name(expr); + } +} + +/*******************************************************************\ + +Function: goto_symex_statet::renaming_levelt::get_original_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::renaming_levelt::get_original_name(exprt &expr) const +{ + Forall_operands(it, expr) + get_original_name(*it); + + if(expr.id()==ID_symbol) + { + original_identifierst::const_iterator it= + original_identifiers.find(expr.get(ID_identifier)); + if(it==original_identifiers.end()) return; + assert(it->second!=""); + expr.set(ID_identifier, it->second); + } +} + +/*******************************************************************\ + +Function: goto_symex_statet::renaming_levelt::get_original_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const irep_idt &goto_symex_statet::renaming_levelt::get_original_name( + const irep_idt &identifier) const +{ + original_identifierst::const_iterator it= + original_identifiers.find(identifier); + if(it==original_identifiers.end()) return identifier; + return it->second; +} + +/*******************************************************************\ + +Function: goto_symex_statet::get_original_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const irep_idt &goto_symex_statet::get_original_name( + const irep_idt &identifier) const +{ + return top().level1.get_original_name( + level2.get_original_name(identifier)); +} + +/*******************************************************************\ + +Function: goto_symex_statet::level1t::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::level1t::print(std::ostream &out) const +{ + for(current_namest::const_iterator + it=current_names.begin(); + it!=current_names.end(); + it++) + out << it->first << " --> " + << name(it->first, it->second) << std::endl; +} + +/*******************************************************************\ + +Function: goto_symex_statet::level2t::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symex_statet::level2t::print(std::ostream &out) const +{ + for(current_namest::const_iterator + it=current_names.begin(); + it!=current_names.end(); + it++) + out << it->first << " --> " + << name(it->first, it->second.count) << std::endl; +} + diff --git a/src/goto-symex/goto_symex_state.h b/src/goto-symex/goto_symex_state.h new file mode 100644 index 00000000000..defec2c653a --- /dev/null +++ b/src/goto-symex/goto_symex_state.h @@ -0,0 +1,248 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_GOTO_SYMEX_STATE_H +#define CPROVER_GOTO_SYMEX_GOTO_SYMEX_STATE_H + +#include + +#include + +#include +#include + +#include "symex_target.h" + +// central data structure: state +class goto_symex_statet +{ +public: + goto_symex_statet(); + + // distance from entry + unsigned depth; + + guardt guard; + symex_targett::sourcet source; + + void initialize(const goto_functionst &goto_functions); + + // we have a two-level renaming + + typedef std::map original_identifierst; + typedef std::set declaration_historyt; + + // we remember all declarations + declaration_historyt declaration_history; + + struct renaming_levelt + { + public: + virtual const irep_idt &get_original_name(const irep_idt &identifier) const; + virtual void get_original_name(exprt &expr) const; + virtual void rename(exprt &expr)=0; + virtual void rename(typet &type); + virtual void remove(const irep_idt &identifier)=0; + + virtual std::string operator()(const irep_idt &identifier) const=0; + + virtual ~renaming_levelt() { } + + virtual void print(std::ostream &out) const { } + + protected: + original_identifierst original_identifiers; + }; + + // level 1 -- function frames + // this is to preserve locality in case of recursion + + struct level1t:public renaming_levelt + { + public: + std::string name( + const irep_idt &identifier, + unsigned frame) const; + + typedef std::map current_namest; + current_namest current_names; + + virtual void rename(exprt &expr); + virtual void rename(typet &type) { renaming_levelt::rename(type); } + virtual std::string operator()(const irep_idt &identifier) const; + virtual void remove(const irep_idt &identifier) { current_names.erase(identifier); } + + void rename(const irep_idt &identifier, unsigned frame) + { + current_names[identifier]=frame; + original_identifiers[name(identifier, frame)]=identifier; + } + + level1t() { } + virtual ~level1t() { } + + virtual void print(std::ostream &out) const; + }; + + // level 2 -- SSA + + struct level2t:public renaming_levelt + { + public: + virtual void rename(exprt &expr); + virtual void rename(typet &type) { renaming_levelt::rename(type); } + virtual std::string operator()(const irep_idt &identifier) const; + virtual void remove(const irep_idt &identifier) { current_names.erase(identifier); } + + std::string name( + const irep_idt &identifier, + unsigned count) const; + + struct valuet + { + unsigned count; + exprt constant; + + valuet(): + count(0), + constant(static_cast(get_nil_irep())) + { + } + }; + + typedef std::map current_namest; + current_namest current_names; + + void rename(const irep_idt &identifier, unsigned count) + { + valuet &entry=current_names[identifier]; + entry.count=count; + original_identifiers[name(identifier, entry.count)]=identifier; + } + + void get_variables(std::set &vars) const + { + for(current_namest::const_iterator it=current_names.begin(); + it!=current_names.end(); + it++) + vars.insert(it->first); + } + + unsigned current_number(const irep_idt &identifier) const; + + level2t() { } + virtual ~level2t() { } + + virtual void print(std::ostream &out) const; + } level2; + + void rename(exprt &expr, const namespacet &ns); + void rename_address(exprt &expr, const namespacet &ns); + void rename(typet &type, const namespacet &ns); + + void assignment( + exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool record_value); + + // what to propagate + bool constant_propagation(const exprt &expr) const; + bool constant_propagation_reference(const exprt &expr) const; + + // undoes both levels of renaming + const irep_idt &get_original_name(const irep_idt &identifier) const; + void get_original_name(exprt &expr) const; + + // does both levels of renaming + std::string current_name(const irep_idt &identifier) const + { + return current_name(level2, identifier); + } + + std::string current_name( + const level2t &level2, + const irep_idt &identifier) const + { + return level2(top().level1(identifier)); + } + + // uses level 1 names, and is used to + // do dereferencing + value_sett value_set; + + class goto_statet + { + public: + unsigned depth; + level2t level2; + value_sett value_set; + guardt guard; + + explicit goto_statet(const goto_symex_statet &s): + depth(s.depth), + level2(s.level2), + value_set(s.value_set), + guard(s.guard) + { + } + }; + + std::string current_name( + const goto_statet &goto_state, + const irep_idt &identifier) const + { + return current_name(goto_state.level2, identifier); + } + + // gotos + typedef std::list goto_state_listt; + typedef std::map goto_state_mapt; + + // function calls + class framet + { + public: + irep_idt function_identifier; + goto_state_mapt goto_state_map; + level1t level1; + symex_targett::sourcet calling_location; + + goto_programt::const_targett end_of_function; + exprt return_value; + + typedef std::set local_variablest; + local_variablest local_variables; + + framet(): + return_value(static_cast(get_nil_irep())) + { + } + }; + + typedef std::vector call_stackt; + call_stackt call_stack; + + inline framet &top() + { + assert(!call_stack.empty()); + return call_stack.back(); + } + + inline const framet &top() const + { + assert(!call_stack.empty()); + return call_stack.back(); + } + + inline framet &new_frame() { call_stack.push_back(framet()); return call_stack.back(); } + inline void pop_frame() { call_stack.pop_back(); } + inline const framet &previous_frame() { return *(--(--call_stack.end())); } +}; + +#endif diff --git a/src/goto-symex/goto_trace.cpp b/src/goto-symex/goto_trace.cpp new file mode 100644 index 00000000000..f14736030e2 --- /dev/null +++ b/src/goto-symex/goto_trace.cpp @@ -0,0 +1,508 @@ +/*******************************************************************\ + +Module: Traces of GOTO Programs + +Author: Daniel Kroening + + Date: July 2005 + +\*******************************************************************/ + +#include + +#include +#include + +#include "goto_trace.h" + +/*******************************************************************\ + +Function: goto_tracet::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_tracet::output( + const class namespacet &ns, + std::ostream &out) const +{ + for(stepst::const_iterator it=steps.begin(); + it!=steps.end(); + it++) + it->output(ns, out); +} + +/*******************************************************************\ + +Function: goto_tracet::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_trace_stept::output( + const namespacet &ns, + std::ostream &out) const +{ + out << "*** "; + + switch(type) + { + case goto_trace_stept::ASSERT: out << "ASSERT"; break; + case goto_trace_stept::ASSUME: out << "ASSUME"; break; + case goto_trace_stept::LOCATION: out << "LOCATION"; break; + case goto_trace_stept::ASSIGNMENT: out << "ASSIGNMENT"; break; + case goto_trace_stept::OUTPUT: out << "OUTPUT"; break; + case goto_trace_stept::INPUT: out << "INPUT"; break; + default: assert(false); + } + + if(type==ASSERT || type==ASSUME) + out << " (" << cond_value << ")"; + + out << std::endl; + + if(!pc->location.is_nil()) + out << pc->location << std::endl; + + if(pc->is_goto()) + out << "GOTO "; + else if(pc->is_assume()) + out << "ASSUME "; + else if(pc->is_assert()) + out << "ASSERT "; + else if(pc->is_dead()) + out << "DEAD "; + else if(pc->is_other()) + out << "OTHER "; + else if(pc->is_assign()) + out << "ASSIGN "; + else if(pc->is_function_call()) + out << "CALL "; + else + out << "(?) "; + + out << std::endl; + + if(pc->is_other() || pc->is_assign()) + { + irep_idt identifier; + + if(original_lhs.is_not_nil()) + identifier=original_lhs.get(ID_identifier); + else + identifier=lhs.get(ID_identifier); + + out << " " << identifier + << " = " << from_expr(ns, identifier, value) + << std::endl; + } + else if(pc->is_assert()) + { + if(!cond_value) + { + out << "Violated property:" << std::endl; + if(pc->location.is_nil()) + out << " " << pc->location << std::endl; + + if(comment!="") + out << " " << comment << std::endl; + out << " " << from_expr(ns, "", pc->guard) << std::endl; + out << std::endl; + } + } + + out << std::endl; +} + +/*******************************************************************\ + +Function: counterexample_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string counterexample_value_binary( + const exprt &expr, + const namespacet &ns) +{ + const typet &type=ns.follow(expr.type()); + + if(expr.id()==ID_constant) + { + if(type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bv || + type.id()==ID_fixedbv || + type.id()==ID_floatbv) + { + return expr.get_string(ID_value); + } + else if(type.id()==ID_bool) + { + return expr.is_true()?"1":"0"; + } + } + else if(expr.id()==ID_array) + { + std::string result; + + forall_operands(it, expr) + { + if(result=="") result="{ "; else result+=", "; + result+=counterexample_value_binary(*it, ns); + } + + return result+" }"; + } + else if(expr.id()==ID_struct) + { + std::string result; + + forall_operands(it, expr) + { + if(result=="") result="{ "; else result+=", "; + result+=counterexample_value_binary(*it, ns); + } + + return result+" }"; + } + else if(expr.id()==ID_union) + { + assert(expr.operands().size()==1); + return counterexample_value_binary(expr.op0(), ns); + } + + return "?"; +} + +/*******************************************************************\ + +Function: counterexample_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void counterexample_value( + std::ostream &out, + const namespacet &ns, + const exprt &lhs, + const exprt &value, + const pretty_namest &pretty_names) +{ + const irep_idt &identifier=lhs.get(ID_identifier); + std::string value_string; + + if(value.is_nil()) + value_string="(assignment removed)"; + else + { + value_string=from_expr(ns, identifier, value); + + // the binary representation + value_string+=" ("+counterexample_value_binary(value, ns)+")"; + } + + #if 1 + std::string name=id2string(identifier); + + const symbolt *symbol; + if(!ns.lookup(identifier, symbol)) + if(symbol->pretty_name!="") + name=id2string(symbol->pretty_name); + + #else + std::string name=pretty_names.pretty_name(identifier) + #endif + + out << " " << name << "=" << value_string + << std::endl; +} + +/*******************************************************************\ + +Function: show_goto_trace_gui + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_goto_trace_gui( + std::ostream &out, + const namespacet &ns, + const goto_tracet &goto_trace) +{ + locationt previous_location; + + for(goto_tracet::stepst::const_iterator + it=goto_trace.steps.begin(); + it!=goto_trace.steps.end(); + it++) + { + const locationt &location=it->pc->location; + + if(it->type==goto_trace_stept::ASSERT && + !it->cond_value) + { + out << "FAILED" << std::endl + << it->comment << std::endl // value + << std::endl // PC + << location.get_file() << std::endl + << location.get_line() << std::endl + << location.get_column() << std::endl; + } + else if(it->type==goto_trace_stept::ASSIGNMENT) + { + irep_idt identifier; + + if(it->original_lhs.is_not_nil()) + identifier=it->original_lhs.get(ID_identifier); + else + identifier=it->lhs.get(ID_identifier); + + std::string value_string=from_expr(ns, identifier, it->value); + + const symbolt *symbol; + irep_idt base_name; + if(!ns.lookup(identifier, symbol)) + base_name=symbol->base_name; + + out << "TRACE" << std::endl; + + out << identifier << "," + << base_name << "," + << it->value.type().to_string() << "," + << value_string << std::endl + << it->step_nr << std::endl + << it->pc->location.get_file() << std::endl + << it->pc->location.get_line() << std::endl + << it->pc->location.get_column() << std::endl; + } + else if(location!=previous_location) + { + // just the location + + if(location.get_file()!="") + { + out << "TRACE" << std::endl; + + out << "," // identifier + << "," // base_name + << "," // type + << "" << std::endl // value + << it->step_nr << std::endl + << location.get_file() << std::endl + << location.get_line() << std::endl + << location.get_column() << std::endl; + } + } + + previous_location=location; + } +} + +/*******************************************************************\ + +Function: show_state_header + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_state_header( + std::ostream &out, + const goto_trace_stept &state, + const locationt &location, + unsigned step_nr) +{ + out << std::endl; + + if(step_nr==0) + out << "Initial State"; + else + out << "State " << step_nr; + + out << " " << location + << " thread " << state.thread_nr << std::endl; + out << "----------------------------------------------------" << std::endl; +} + +/*******************************************************************\ + +Function: show_goto_trace + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_goto_trace( + std::ostream &out, + const namespacet &ns, + const goto_tracet &goto_trace) +{ + pretty_namest pretty_names; + + { + pretty_namest::symbolst pretty_symbols; + + forall_symbols(it, ns.get_context().symbols) + pretty_symbols.insert(it->first); + + pretty_names.get_pretty_names(pretty_symbols, ns); + } + + show_goto_trace(out, ns, pretty_names, goto_trace); +} + +/*******************************************************************\ + +Function: show_goto_trace + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_goto_trace( + std::ostream &out, + const namespacet &ns, + const pretty_namest &pretty_names, + const goto_tracet &goto_trace) +{ + unsigned prev_step_nr=0; + bool first_step=true; + + for(goto_tracet::stepst::const_iterator + it=goto_trace.steps.begin(); + it!=goto_trace.steps.end(); + it++) + { + switch(it->type) + { + case goto_trace_stept::ASSERT: + if(!it->cond_value) + { + out << std::endl; + out << "Violated property:" << std::endl; + if(!it->pc->location.is_nil()) + out << " " << it->pc->location << std::endl; + out << " " << it->comment << std::endl; + + if(it->pc->is_assert()) + out << " " << from_expr(ns, "", it->pc->guard) << std::endl; + + out << std::endl; + } + break; + + case goto_trace_stept::ASSUME: + break; + + case goto_trace_stept::LOCATION: + break; + + case goto_trace_stept::ASSIGNMENT: + if(it->pc->is_assign() || + it->pc->is_return() || // lhs! + it->pc->is_function_call() || + (it->pc->is_other() && it->lhs.is_not_nil())) + { + if(prev_step_nr!=it->step_nr || first_step) + { + first_step=false; + prev_step_nr=it->step_nr; + show_state_header(out, *it, it->pc->location, it->step_nr); + } + + counterexample_value(out, ns, it->original_lhs, + it->value, pretty_names); + } + break; + + case goto_trace_stept::OUTPUT: + if(it->formatted) + { + printf_formattert printf_formatter(ns); + printf_formatter(id2string(it->format_string), it->io_args); + printf_formatter.print(out); + out << std::endl; + } + else + { + show_state_header(out, *it, it->pc->location, it->step_nr); + out << " OUTPUT " << it->io_id << ":"; + + for(std::list::const_iterator + l_it=it->io_args.begin(); + l_it!=it->io_args.end(); + l_it++) + { + if(l_it!=it->io_args.begin()) out << ";"; + out << " " << from_expr(ns, "", *l_it); + + // the binary representation + out << " (" << counterexample_value_binary(*l_it, ns) << ")"; + } + + out << std::endl; + } + break; + + case goto_trace_stept::INPUT: + show_state_header(out, *it, it->pc->location, it->step_nr); + out << " INPUT " << it->io_id << ":"; + + for(std::list::const_iterator + l_it=it->io_args.begin(); + l_it!=it->io_args.end(); + l_it++) + { + if(l_it!=it->io_args.begin()) out << ";"; + out << " " << from_expr(ns, "", *l_it); + + // the binary representation + out << " (" << counterexample_value_binary(*l_it, ns) << ")"; + } + + out << std::endl; + break; + + default: + assert(false); + } + } +} diff --git a/src/goto-symex/goto_trace.h b/src/goto-symex/goto_trace.h new file mode 100644 index 00000000000..3676842bc82 --- /dev/null +++ b/src/goto-symex/goto_trace.h @@ -0,0 +1,138 @@ +/*******************************************************************\ + +Module: Traces of GOTO Programs + +Author: Daniel Kroening + +Date: July 2005 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_GOTO_TRACE_H +#define CPROVER_GOTO_SYMEX_GOTO_TRACE_H + +#include +#include + +#include + +#include + +class goto_trace_stept +{ +public: + unsigned step_nr; + + bool is_assignment() const { return type==ASSIGNMENT; } + bool is_assume() const { return type==ASSUME; } + bool is_assert() const { return type==ASSERT; } + bool is_location() const { return type==LOCATION; } + bool is_output() const { return type==OUTPUT; } + bool is_input() const { return type==INPUT; } + + typedef enum { NONE, ASSIGNMENT, ASSUME, ASSERT, + LOCATION, INPUT, OUTPUT } typet; + typet type; + + goto_programt::const_targett pc; + + // this transition done by given thread number + unsigned thread_nr; + + // for assume, assert + bool cond_value; + exprt cond_expr; + + // for goto + bool goto_taken; + + // for assert + std::string comment; + + // in SSA + exprt lhs, rhs; + + // this is a constant + exprt value; + + // original expression + exprt original_lhs; + + // for INPUT/OUTPUT + irep_idt format_string, io_id; + typedef std::list io_argst; + io_argst io_args; + bool formatted; + + void output( + const class namespacet &ns, + std::ostream &out) const; + + goto_trace_stept(): + step_nr(0), + type(NONE), + thread_nr(0), + cond_value(false), + formatted(false) + { + lhs.make_nil(); + rhs.make_nil(); + value.make_nil(); + original_lhs.make_nil(); + cond_expr.make_nil(); + } +}; + +class goto_tracet +{ +public: + typedef std::list stepst; + stepst steps; + + std::string mode; + + inline void clear() + { + mode.clear(); + steps.clear(); + } + + void output( + const class namespacet &ns, + std::ostream &out) const; + + inline void swap(goto_tracet &other) + { + other.steps.swap(steps); + other.mode.swap(mode); + } +}; + +void show_goto_trace_gui( + std::ostream &out, + const namespacet &ns, + const goto_tracet &goto_trace); + +void show_goto_trace( + std::ostream &out, + const namespacet &ns, + const goto_tracet &goto_trace); + +void show_goto_trace( + std::ostream &out, + const namespacet &ns, + const pretty_namest &pretty_names, + const goto_tracet &goto_trace); + +void counterexample_value( + std::ostream &out, + const namespacet &ns, + const irep_idt &identifier, + const exprt &value, + const pretty_namest &pretty_names); + +std::string counterexample_value_binary( + const exprt &expr, + const namespacet &ns); + +#endif diff --git a/src/goto-symex/postcondition.cpp b/src/goto-symex/postcondition.cpp new file mode 100644 index 00000000000..70f900b51fd --- /dev/null +++ b/src/goto-symex/postcondition.cpp @@ -0,0 +1,278 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "goto_symex_state.h" +#include "postcondition.h" + +/*******************************************************************\ + + Class: postconditiont + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +class postconditiont +{ +public: + postconditiont( + const namespacet &_ns, + const value_sett &_value_set, + const symex_target_equationt::SSA_stept &_SSA_step, + const goto_symex_statet &_s): + ns(_ns), + value_set(_value_set), + SSA_step(_SSA_step), + s(_s) + { + } + +protected: + const namespacet &ns; + const value_sett &value_set; + const symex_target_equationt::SSA_stept &SSA_step; + const goto_symex_statet &s; + +public: + void compute(exprt &dest); + +protected: + void strengthen(exprt &dest); + void weaken(exprt &dest); + bool is_used_address_of(const exprt &expr, const irep_idt &identifier); + bool is_used(const exprt &expr, const irep_idt &identifier); +}; + +/*******************************************************************\ + +Function: postcondition + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void postcondition( + const namespacet &ns, + const value_sett &value_set, + const symex_target_equationt &equation, + const goto_symex_statet &s, + exprt &dest) +{ + for(symex_target_equationt::SSA_stepst::const_iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); + it++) + { + postconditiont postcondition(ns, value_set, *it, s); + postcondition.compute(dest); + if(dest.is_false()) return; + } +} + +/*******************************************************************\ + +Function: postconditiont::is_used_address_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool postconditiont::is_used_address_of( + const exprt &expr, + const irep_idt &identifier) +{ + if(expr.id()=="symbol") + { + // leave alone + } + else if(expr.id()=="index") + { + assert(expr.operands().size()==2); + + return + is_used_address_of(expr.op0(), identifier) || + is_used(expr.op1(), identifier); + } + else if(expr.id()=="member") + { + assert(expr.operands().size()==1); + return is_used_address_of(expr.op0(), identifier); + } + else if(expr.id()=="dereference" || + expr.id()=="implicit_dereference") + { + assert(expr.operands().size()==1); + return is_used(expr.op0(), identifier); + } + + return false; +} + +/*******************************************************************\ + +Function: postconditiont::compute + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void postconditiont::compute(exprt &dest) +{ + // weaken due to assignment + weaken(dest); + + // strengthen due to assignment + strengthen(dest); +} + +/*******************************************************************\ + +Function: postconditiont::strengthen + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void postconditiont::weaken(exprt &dest) +{ + if(dest.id()=="and" && + dest.type().id()=="bool") // this distributes over "and" + { + Forall_operands(it, dest) + weaken(*it); + + return; + } + + // we are lazy: + // if lhs is mentioned in dest, we use "true". + + const irep_idt &lhs_identifier= + s.get_original_name(SSA_step.lhs.get("identifier")); + + if(is_used(dest, lhs_identifier)) + dest.make_true(); + + // otherwise, no weakening needed +} + +/*******************************************************************\ + +Function: postconditiont::strengthen + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void postconditiont::strengthen(exprt &dest) +{ + const irep_idt &lhs_identifier= + s.get_original_name(SSA_step.lhs.get("identifier")); + + if(!is_used(SSA_step.rhs, lhs_identifier)) + { + // we don't do arrays or structs + if(SSA_step.lhs.type().id()=="array" || + SSA_step.lhs.type().id()=="struct") return; + + exprt equality("=", typet("bool")); + equality.copy_to_operands(SSA_step.lhs, SSA_step.rhs); + s.get_original_name(equality); + + if(dest.is_true()) + dest.swap(equality); + else + dest=gen_and(dest, equality); + } +} + +/*******************************************************************\ + +Function: postconditiont::is_used + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool postconditiont::is_used( + const exprt &expr, + const irep_idt &identifier) +{ + if(expr.id()=="address_of" || + expr.id()=="reference_to" || + expr.id()=="implicit_address_of") + { + // only do index! + assert(expr.operands().size()==1); + return is_used_address_of(expr.op0(), identifier); + } + else if(expr.id()=="symbol") + { + return s.get_original_name(expr.get("identifier"))==identifier; + } + else if(expr.id()=="dereference" || + expr.id()=="implicit_dereference") + { + assert(expr.operands().size()==1); + + // aliasing may happen here + + value_setst::valuest expr_set; + value_set.get_value_set(expr.op0(), expr_set, ns); + hash_set_cont symbols; + + for(value_setst::valuest::const_iterator + it=expr_set.begin(); + it!=expr_set.end(); + it++) + { + exprt tmp(*it); + s.get_original_name(tmp); + find_symbols(tmp, symbols); + } + + return symbols.find(identifier)!=symbols.end(); + } + else + forall_operands(it, expr) + if(is_used(*it, identifier)) + return true; + + return false; +} diff --git a/src/goto-symex/postcondition.h b/src/goto-symex/postcondition.h new file mode 100644 index 00000000000..30888393011 --- /dev/null +++ b/src/goto-symex/postcondition.h @@ -0,0 +1,23 @@ +/*******************************************************************\ + +Module: Generate Equation using Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_POSTCONDITION_H +#define CPROVER_GOTO_SYMEX_POSTCONDITION_H + +#include + +#include "symex_target_equation.h" + +void postcondition( + const namespacet &ns, + const value_setst &value_sets, + const symex_target_equationt &equation, + const class goto_symex_statet &s, + exprt &dest); + +#endif diff --git a/src/goto-symex/precondition.cpp b/src/goto-symex/precondition.cpp new file mode 100644 index 00000000000..e89ca43d0a1 --- /dev/null +++ b/src/goto-symex/precondition.cpp @@ -0,0 +1,214 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "goto_symex_state.h" +#include "precondition.h" + +/*******************************************************************\ + + Class: preconditiont + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +class preconditiont +{ +public: + preconditiont( + const namespacet &_ns, + value_setst &_value_sets, + const goto_programt::const_targett _target, + const symex_target_equationt::SSA_stept &_SSA_step, + const goto_symex_statet &_s): + ns(_ns), + value_sets(_value_sets), + target(_target), + SSA_step(_SSA_step), + s(_s) + { + } + +protected: + const namespacet &ns; + value_setst &value_sets; + const goto_programt::const_targett target; + const symex_target_equationt::SSA_stept &SSA_step; + const goto_symex_statet &s; + void compute_rec(exprt &dest); + +public: + void compute(exprt &dest); + +protected: + void compute_address_of(exprt &dest); +}; + +/*******************************************************************\ + +Function: precondition + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void precondition( + const namespacet &ns, + value_setst &value_sets, + const goto_programt::const_targett target, + const symex_target_equationt &equation, + const goto_symex_statet &s, + exprt &dest) +{ + for(symex_target_equationt::SSA_stepst::const_reverse_iterator + it=equation.SSA_steps.rbegin(); + it!=equation.SSA_steps.rend(); + it++) + { + preconditiont precondition(ns, value_sets, target, *it, s); + precondition.compute(dest); + if(dest.is_false()) return; + } +} + +/*******************************************************************\ + +Function: preconditiont::compute_address_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void preconditiont::compute_address_of(exprt &dest) +{ + if(dest.id()=="symbol") + { + // leave alone + } + else if(dest.id()=="index") + { + assert(dest.operands().size()==2); + compute_address_of(dest.op0()); + compute(dest.op1()); + } + else if(dest.id()=="member") + { + assert(dest.operands().size()==1); + compute_address_of(dest.op0()); + } + else if(dest.id()=="dereference" || + dest.id()=="implicit_dereference") + { + assert(dest.operands().size()==1); + compute(dest.op0()); + } +} + +/*******************************************************************\ + +Function: preconditiont::compute + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void preconditiont::compute(exprt &dest) +{ + compute_rec(dest); +} + +/*******************************************************************\ + +Function: preconditiont::compute_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void preconditiont::compute_rec(exprt &dest) +{ + if(dest.id()=="address_of" || + dest.id()=="reference_to" || + dest.id()=="implicit_address_of") + { + // only do index! + assert(dest.operands().size()==1); + compute_address_of(dest.op0()); + } + else if(dest.id()=="symbol") + { + if(dest.get("identifier")== + s.get_original_name(SSA_step.lhs.get("identifier"))) + { + dest=SSA_step.rhs; + s.get_original_name(dest); + } + } + else if(dest.id()=="dereference" || + dest.id()=="implicit_dereference") + { + assert(dest.operands().size()==1); + + const irep_idt &lhs_identifier= + s.get_original_name(SSA_step.lhs.get("identifier")); + + // aliasing may happen here + + value_setst::valuest expr_set; + value_sets.get_values(target, dest.op0(), expr_set); + hash_set_cont symbols; + + for(value_setst::valuest::const_iterator + it=expr_set.begin(); + it!=expr_set.end(); + it++) + find_symbols(*it, symbols); + + if(symbols.find(lhs_identifier)!=symbols.end()) + { + // may alias! + exprt tmp; + tmp.swap(dest.op0()); + dereference(target, tmp, ns, value_sets); + dest.swap(tmp); + compute_rec(dest); + } + else + { + // nah, ok + compute_rec(dest.op0()); + } + } + else + Forall_operands(it, dest) + compute_rec(*it); +} diff --git a/src/goto-symex/precondition.h b/src/goto-symex/precondition.h new file mode 100644 index 00000000000..03539b89361 --- /dev/null +++ b/src/goto-symex/precondition.h @@ -0,0 +1,24 @@ +/*******************************************************************\ + +Module: Generate Equation using Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_PRECONDITION_H +#define CPROVER_GOTO_SYMEX_PRECONDITION_H + +#include + +#include "symex_target_equation.h" + +void precondition( + const namespacet &ns, + value_setst &value_sets, + const goto_programt::const_targett target, + const symex_target_equationt &equation, + const class goto_symex_statet &s, + exprt &dest); + +#endif diff --git a/src/goto-symex/renaming_ns.h b/src/goto-symex/renaming_ns.h new file mode 100644 index 00000000000..9955f389ab4 --- /dev/null +++ b/src/goto-symex/renaming_ns.h @@ -0,0 +1,39 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_RENAMING_NS_H +#define CPROVER_GOTO_SYMEX_RENAMING_NS_H + +#include + +class renaming_nst:public namespacet +{ +public: + renaming_nst( + const namespacet &_ns, + class goto_symex_statet &_state): + namespacet(_ns), + state(_state) + { + } + + virtual bool lookup(const irep_idt &name, const symbolt *&symbol) const + { + return namespacet::lookup(state.get_original_name(name), symbol); + } + + const symbolt &lookup(const irep_idt &name) const + { + return namespacet::lookup(state.get_original_name(name)); + } + +protected: + class goto_symex_statet &state; +}; + +#endif diff --git a/src/goto-symex/slice.cpp b/src/goto-symex/slice.cpp new file mode 100644 index 00000000000..15dfcbf7958 --- /dev/null +++ b/src/goto-symex/slice.cpp @@ -0,0 +1,327 @@ +/*******************************************************************\ + +Module: Slicer for symex traces + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "slice.h" +#include "symex_slice_class.h" + +/*******************************************************************\ + +Function: symex_slicet::get_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slicet::get_symbols(const exprt &expr) +{ + get_symbols(expr.type()); + + forall_operands(it, expr) + get_symbols(*it); + + if(expr.id()==ID_symbol) + depends.insert(to_symbol_expr(expr).get_identifier()); +} + +/*******************************************************************\ + +Function: symex_slicet::get_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slicet::get_symbols(const typet &type) +{ + // TODO +} + +/*******************************************************************\ + +Function: symex_slicet::slice + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slicet::slice( + symex_target_equationt &equation, + const expr_listt &exprs) +{ + // collect dependencies + forall_expr_list(expr_it, exprs) + get_symbols(*expr_it); + + slice(equation); +} + +/*******************************************************************\ + +Function: symex_slicet::slice + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slicet::slice(symex_target_equationt &equation) +{ + for(symex_target_equationt::SSA_stepst::reverse_iterator + it=equation.SSA_steps.rbegin(); + it!=equation.SSA_steps.rend(); + it++) + slice(*it); +} + +/*******************************************************************\ + +Function: symex_slicet::slice + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slicet::slice(symex_target_equationt::SSA_stept &SSA_step) +{ + get_symbols(SSA_step.guard_expr); + + switch(SSA_step.type) + { + case goto_trace_stept::ASSERT: + get_symbols(SSA_step.cond_expr); + break; + + case goto_trace_stept::ASSUME: + get_symbols(SSA_step.cond_expr); + break; + + case goto_trace_stept::LOCATION: + // ignore + break; + + case goto_trace_stept::ASSIGNMENT: + slice_assignment(SSA_step); + break; + + case goto_trace_stept::OUTPUT: + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: symex_slicet::slice_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slicet::slice_assignment( + symex_target_equationt::SSA_stept &SSA_step) +{ + assert(SSA_step.lhs.id()==ID_symbol); + const irep_idt &id=SSA_step.lhs.get(ID_identifier); + + if(depends.find(id)==depends.end()) + { + // we don't really need it + SSA_step.ignore=true; + } + else + get_symbols(SSA_step.rhs); +} + +/*******************************************************************\ + +Function: symex_slice_classt::collect_open_variables + + Inputs: equation - symex trace + open_variables - target set + + Outputs: None. But open_variables is modified as a side-effect. + + Purpose: Collect the open variables, i.e. variables that are used in RHS + but never written in LHS + +\*******************************************************************/ + +void symex_slicet::collect_open_variables( + const symex_target_equationt &equation, + symbol_sett &open_variables) +{ + symbol_sett lhs; + + for(symex_target_equationt::SSA_stepst::const_iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); + it++) + { + const symex_target_equationt::SSA_stept &SSA_step=*it; + + get_symbols(SSA_step.guard_expr); + + switch(SSA_step.type) + { + case goto_trace_stept::ASSERT: + get_symbols(SSA_step.cond_expr); + break; + + case goto_trace_stept::ASSUME: + get_symbols(SSA_step.cond_expr); + break; + + case goto_trace_stept::LOCATION: + // ignore + break; + + case goto_trace_stept::ASSIGNMENT: + get_symbols(SSA_step.rhs); + lhs.insert(SSA_step.lhs.get(ID_identifier)); + break; + + case goto_trace_stept::OUTPUT: + break; + + default: + assert(false); + } + } + + open_variables=depends; + + // remove the ones that are defined + open_variables.erase(lhs.begin(), lhs.end()); +} + +/*******************************************************************\ + +Function: slice + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void slice(symex_target_equationt &equation) +{ + symex_slicet symex_slice; + symex_slice.slice(equation); +} + +/*******************************************************************\ + +Function: collect_open_variables + + Inputs: equation - symex trace + open_variables - target set + + Outputs: None. But open_variables is modified as a side-effect. + + Purpose: Collect the open variables, i.e. variables that are used in RHS + but never written in LHS + +\*******************************************************************/ + +void collect_open_variables( + const symex_target_equationt &equation, + symbol_sett &open_variables) +{ + symex_slicet symex_slice; + symex_slice.collect_open_variables(equation, open_variables); +} + +/*******************************************************************\ + +Function: slice + + Inputs: equation - symex trace to be sliced + expression - list of expressions, targets for slicing + + Outputs: None. But equation is modified as a side-effect. + + Purpose: Slice the symex trace with respect to a list of expressions + +\*******************************************************************/ + +void slice(symex_target_equationt &equation, + const expr_listt &expressions) +{ + symex_slicet symex_slice; + symex_slice.slice(equation, expressions); +} + +/*******************************************************************\ + +Function: simple_slice + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void simple_slice(symex_target_equationt &equation) +{ + // just find the last assertion + symex_target_equationt::SSA_stepst::iterator + last_assertion=equation.SSA_steps.end(); + + for(symex_target_equationt::SSA_stepst::iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); + it++) + if(it->is_assert()) + last_assertion=it; + + // slice away anything after it + + symex_target_equationt::SSA_stepst::iterator s_it= + last_assertion; + + if(s_it!=equation.SSA_steps.end()) + for(s_it++; + s_it!=equation.SSA_steps.end(); + s_it++) + s_it->ignore=true; +} + diff --git a/src/goto-symex/slice.h b/src/goto-symex/slice.h new file mode 100644 index 00000000000..c0d3441bb06 --- /dev/null +++ b/src/goto-symex/slice.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: Slicer for symex traces + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_SLICE_H +#define CPROVER_GOTO_SYMEX_SLICE_H + +#include "symex_target_equation.h" + +// slice an equation with respect to the assertions contained therein +void slice(symex_target_equationt &equation); + +// this simply slices away anything after the last assertion +void simple_slice(symex_target_equationt &equation); + +// Slice the symex trace with respect to a list of given expressions +void slice(symex_target_equationt &equation, + const expr_listt &expressions); + +// Collects "open" variables that are used but not assigned + +typedef hash_set_cont symbol_sett; + +void collect_open_variables( + const symex_target_equationt &equation, + symbol_sett &open_variables); + +#endif diff --git a/src/goto-symex/slice_by_trace.cpp b/src/goto-symex/slice_by_trace.cpp new file mode 100644 index 00000000000..33d9c35a886 --- /dev/null +++ b/src/goto-symex/slice_by_trace.cpp @@ -0,0 +1,701 @@ +/*******************************************************************\ + +Module: Slicer for symex traces + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "slice_by_trace.h" + +/*******************************************************************\ + +Function: slice_by_trace + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slice_by_tracet::slice_by_trace(std::string trace_files, + symex_target_equationt &equation) +{ + std::cout << "Slicing by trace..." << std::endl; + + merge_identifier = "goto_symex::\\merge"; + merge_symbol=exprt("symbol", typet("bool")); + merge_symbol.set("identifier", merge_identifier); + + std::vector trace_conditions; + + size_t length = trace_files.length(); + for(size_t idx = 0; idx < length; idx++) { + const std::string::size_type next = trace_files.find(",", idx); + std::string filename = trace_files.substr(idx, next - idx); + + read_trace(filename); + + compute_ts_back(equation); + + exprt t_copy (t[0]); + trace_conditions.push_back(t_copy); + + if(next == std::string::npos) break; + idx = next; + } + + exprt trace_condition; + + if (trace_conditions.size() == 1) { + trace_condition = trace_conditions[0]; + } else { + trace_condition = exprt("and",typet("bool")); + trace_condition.operands().reserve(trace_conditions.size()); + for (std::vector::iterator i = trace_conditions.begin(); + i != trace_conditions.end(); i++) { + trace_condition.move_to_operands(*i); + } + } + + simplify(trace_condition, ns); + + std::set implications = implied_guards(trace_condition); + + for(std::set::iterator i = sliced_guards.begin(); i != + sliced_guards.end(); i++) + { + exprt g_copy (*i); + + if (g_copy.id() == "symbol" || g_copy.id() == "not") + { + g_copy.make_not(); + simplify(g_copy, ns); + implications.insert(g_copy); + } + else if (g_copy.id() == "and") + { + exprt copy_last (g_copy.operands().back()); + copy_last.make_not(); + simplify(copy_last, ns); + implications.insert(copy_last); + } + else if (!(g_copy.id() == "constant")) { + throw "Guards should only be and, symbol, constant, or not."; + } + } + + slice_SSA_steps(equation, implications); // Slice based on implications + + guardt t_guard; + t_guard.make_true(); + symex_targett::sourcet empty_source; + equation.SSA_steps.push_front(symex_target_equationt::SSA_stept()); + symex_target_equationt::SSA_stept &SSA_step = equation.SSA_steps.front(); + + SSA_step.guard_expr=t_guard.as_expr(); + SSA_step.lhs.make_nil(); + SSA_step.cond_expr.swap(trace_condition); + SSA_step.type=goto_trace_stept::ASSUME; + SSA_step.source=empty_source; + + assign_merges(equation); // Now add the merge variable assignments to eqn + + std::cout << "Finished slicing by trace..." << std::endl; +} + +/*******************************************************************\ + +Function: read_trace + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slice_by_tracet::read_trace(std::string filename) +{ + std::cout << "Reading trace from file " << filename << std::endl; + std::ifstream file(filename.c_str()); + if (file.fail()) + throw "Failed to read from trace file."; + + // In case not the first trace read + alphabet.clear(); + sigma.clear(); + sigma_vals.clear(); + t.clear(); + + std::string read_line; + bool done = false; + bool begin = true; + alphabet_parity = true; + + while (!done && !file.eof ()) { + str_getline(file, read_line); + if (begin && (read_line == "!")) + alphabet_parity = false; + else + done = parse_alphabet(read_line); + } + + while (!file.eof ()) { + str_getline(file,read_line); + parse_events(read_line); + } + + for (size_t i = 0; i < sigma.size(); i++) { + exprt f_e = static_cast(get_nil_irep()); + f_e.make_false(); + t.push_back(f_e); + } + + exprt t_e = static_cast(get_nil_irep()); + t_e.make_true(); + t.push_back(t_e); +} + +/*******************************************************************\ + +Function: parse_alphabet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool symex_slice_by_tracet::parse_alphabet(std::string read_line) { + if ((read_line == ":") || (read_line == ":exact") || + (read_line == ":suffix") || (read_line == ":exact-suffix") || + (read_line == ":prefix")) { + semantics = read_line; + return true; + } else { + std::cout << "Alphabet: "; + if (!alphabet_parity) + std::cout << "!"; + std::cout << read_line << std::endl; + alphabet.insert(read_line); + } + return false; +} + +/*******************************************************************\ + +Function: parse_events + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slice_by_tracet::parse_events(std::string read_line) { + if (read_line == "") + return; + bool parity = strstr(read_line.c_str(),"!")==NULL; + bool universe = strstr(read_line.c_str(),"?")!=NULL; + bool has_values = strstr(read_line.c_str()," ")!=NULL; + std::cout << "Trace: " << read_line << std::endl; + std::vector value_v; + if (has_values) { + std::string::size_type sloc = read_line.find(" ",0); + std::string values = (read_line.substr(sloc, read_line.size()-1)); + size_t length = values.length(); + for(size_t idx = 0; idx < length; idx++) { + const std::string::size_type next = values.find(",", idx); + std::string value = values.substr(idx, next - idx); + value_v.push_back(value); + if(next == std::string::npos) break; + idx = next; + } + read_line = read_line.substr(0,sloc); + } + sigma_vals.push_back(value_v); + if (universe) + parity = false; + if (!parity) + read_line = read_line.substr(1,read_line.size()-1); + std::set eis; + size_t vlength = read_line.length(); + for(size_t vidx = 0; vidx < vlength; vidx++) { + const std::string::size_type vnext = read_line.find(",", vidx); + std::string event = read_line.substr(vidx, vnext - vidx); + eis.insert(event); + if ((!alphabet.empty()) && ((alphabet.count(event) != 0) != + alphabet_parity)) + throw ("Trace uses symbol not in alphabet: " + event); + if(vnext == std::string::npos) break; + vidx = vnext; + } + event_sett es = event_sett(eis, parity); + sigma.push_back(es); +} + +/*******************************************************************\ + +Function: compute_ts_back + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slice_by_tracet::compute_ts_back( + symex_target_equationt &equation) +{ + size_t merge_count = 0; + + for(symex_target_equationt::SSA_stepst::reverse_iterator + i=equation.SSA_steps.rbegin(); + i!=equation.SSA_steps.rend(); + i++) + { + if(i->is_output() && + !i->io_args.empty() && + i->io_args.front().id()=="trace_event") + { + irep_idt event = i->io_args.front().get("event"); + + if (!alphabet.empty()) + { + bool present = (alphabet.count(event) != 0); + if (alphabet_parity != present) + continue; + } + + exprt guard = i->guard_expr; + +#if 0 + std::cout << "EVENT: " << event << std::endl; + std::cout << "GUARD: " << from_expr(ns, "", guard) << std::endl; + for (size_t j = 0; j < t.size(); j++) { + std::cout << "t[" << j << "] = " << from_expr(ns, "", t[j]) << + std::endl; + } +#endif + + bool slice_this = (semantics != ":prefix"); + std::vector merge; + + for(size_t j = 0; j < t.size(); j++) { + if ((t[j].is_true()) || (t[j].is_false())) { + merge.push_back(t[j]); + } else { + exprt merge_sym =exprt("symbol", typet("bool")); + merge_sym.set("identifier", id2string(merge_identifier)+"#"+ + i2string(merge_count++)); + exprt t_copy (t[j]); + merge_map_back.push_back(t_copy); + std::set empty_impls; + merge_impl_cache_back.push_back + (std::pair >(false, empty_impls)); + merge.push_back(merge_sym); + } + } + + for(size_t j = 0; j < t.size(); j++) { + exprt u_lhs = exprt("and", typet("bool")); + if ((j < sigma.size()) && (matches(sigma[j],event))) { + u_lhs.operands().reserve(2); + u_lhs.copy_to_operands(guard); + if (!sigma_vals[j].empty()) { + std::list eq_conds; + std::list::iterator pvi = i->io_args.begin(); + for (std::vector::iterator k = sigma_vals[j].begin(); + k != sigma_vals[j].end(); k++) { + + exprt equal_cond=exprt("=", typet("bool")); + equal_cond.operands().reserve(2); + equal_cond.copy_to_operands(*pvi); + // Should eventually change to handle non-bv types! + exprt constant_value = exprt("constant",(*pvi).type()); + std::string bit_string = + integer2binary(atoi(k->c_str()), bv_width((*pvi).type())); + constant_value.set("value", bit_string); + equal_cond.move_to_operands(constant_value); + eq_conds.push_back(equal_cond); + pvi++; + } + exprt val_merge = exprt("and", typet("bool")); + val_merge.operands().reserve(eq_conds.size()+1); + val_merge.copy_to_operands(merge[j+1]); + for (std::list::iterator k = eq_conds.begin(); + k!= eq_conds.end(); k++) { + val_merge.copy_to_operands(*k); + } + u_lhs.move_to_operands(val_merge); + } else { + u_lhs.copy_to_operands(merge[j+1]); + } + + simplify(u_lhs, ns); + + if ((!u_lhs.is_false()) && implies_false(u_lhs)) + u_lhs.make_false(); + if (!u_lhs.is_false()) + slice_this = false; + } else { + u_lhs.make_false(); + } + exprt u_rhs = exprt ("and", typet("bool")); + if ((semantics != ":suffix") || (j != 0)) { + u_rhs.operands().reserve(2); + u_rhs.copy_to_operands(guard); + u_rhs.copy_to_operands(merge[j]); + u_rhs.op0().make_not(); + } else { + u_rhs.swap(merge[j]); + } + exprt u_j = exprt ("or", typet("bool")); + u_j.operands().reserve(2); + u_j.copy_to_operands(u_lhs); + u_j.copy_to_operands(u_rhs); + + simplify(u_j, ns); + + t[j] = u_j; + } + + if (semantics == ":prefix") + t[t.size()-1].make_true(); + + if (slice_this) { + exprt guard_copy(guard); + + sliced_guards.insert(guard_copy); + } + } + } +} + +/*******************************************************************\ + +Function: compute_ts_fd + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slice_by_tracet::compute_ts_fd( + symex_target_equationt &equation) +{ +} + +/*******************************************************************\ + +Function: slice_SSA_steps + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slice_by_tracet::slice_SSA_steps( + symex_target_equationt &equation, + std::set implications) +{ + //Some statistics for our benefit. + size_t conds_seen = 0; + size_t sliced_SSA_steps = 0; + size_t potential_SSA_steps = 0; + size_t sliced_conds = 0; + size_t trace_SSA_steps = 0; + size_t location_SSA_steps = 0; + size_t trace_loc_sliced = 0; + + for(symex_target_equationt::SSA_stepst::iterator + it=equation.SSA_steps.begin(); + it!=equation.SSA_steps.end(); + it++) + { + if (it->is_output()) + trace_SSA_steps++; + if (it->is_location()) + location_SSA_steps++; + bool sliced_SSA_step = false; + exprt guard=it->guard_expr; + + simplify(guard, ns); + + if (!guard.is_true()) + potential_SSA_steps++; + //it->output(ns,std::cout); + //std::cout << "-----------------" << std::endl; + + if ((guard.id() == "symbol") || (guard.id() == "not")) + { + guard.make_not(); + simplify(guard, ns); + + if (implications.count(guard) != 0) { + it->cond_expr.make_true(); + it->rhs.make_true(); + it->guard_expr.make_false(); + sliced_SSA_steps++; + if (it->is_output() || it->is_location()) + trace_loc_sliced++; + sliced_SSA_step = true; + } + } + else if(guard.id()=="and") + { + Forall_operands(git,guard) + { + exprt neg_expr=*git; + neg_expr.make_not(); + simplify(neg_expr, ns); + + if (implications.count(neg_expr) != 0) { + it->cond_expr.make_true(); + it->rhs.make_true(); + it->guard_expr.make_false(); + sliced_SSA_steps++; + if (it->is_output() || it->is_location()) + trace_loc_sliced++; + sliced_SSA_step = true; + break; // Sliced, so no need to consider the rest + } + } else if (guard.id() == "or") { + std::cout << "Guarded by an OR." << std::endl; + } + } + + if (!sliced_SSA_step && it->is_assignment()) + { + if (it->rhs.id() == "if") + { + conds_seen++; + exprt cond_copy (it->rhs.op0()); + simplify(cond_copy, ns); + + if (implications.count(cond_copy) != 0) { + sliced_conds++; + exprt t_copy1 (it->rhs.op1()); + exprt t_copy2 (it->rhs.op1()); + it->rhs = t_copy1; + it->cond_expr.op1().swap(t_copy2); + } else { + cond_copy.make_not(); + simplify(cond_copy, ns); + if (implications.count(cond_copy) != 0) { + sliced_conds++; + exprt f_copy1 (it->rhs.op2()); + exprt f_copy2 (it->rhs.op2()); + it->rhs = f_copy1; + it->cond_expr.op1().swap(f_copy2); + } + } + } + } + } + + std::cout << "Trace slicing effectively removed " + << (sliced_SSA_steps + sliced_conds) << " out of " + << equation.SSA_steps.size() << " SSA_steps." << std::endl; + std::cout << " (" + << ((sliced_SSA_steps + sliced_conds) - trace_loc_sliced) + << " out of " + << (equation.SSA_steps.size() - trace_SSA_steps - location_SSA_steps) + << " non-trace, non-location SSA_steps)" << std::endl; +} + +/*******************************************************************\ + +Function: matches + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool symex_slice_by_tracet::matches( + event_sett s, + irep_idt event) +{ + bool present = s.first.count(event) != 0; + return ((s.second && present) || (!s.second && !present)); +} + +/*******************************************************************\ + +Function: assign_merges + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_slice_by_tracet::assign_merges( + symex_target_equationt &equation) +{ + size_t merge_count = (merge_map_back.size()) - 1; + for (std::vector::reverse_iterator i = merge_map_back.rbegin(); + i != merge_map_back.rend(); i++) { + exprt merge_sym =exprt("symbol", typet("bool")); + merge_sym.set("identifier", id2string(merge_identifier)+"#"+i2string(merge_count)); + merge_count--; + guardt t_guard; + t_guard.make_true(); + symex_targett::sourcet empty_source; + + exprt merge_copy (*i); + + equation.SSA_steps.push_front(symex_target_equationt::SSA_stept()); + symex_target_equationt::SSA_stept &SSA_step = equation.SSA_steps.front(); + + SSA_step.guard_expr=t_guard.as_expr(); + SSA_step.lhs=merge_sym; + SSA_step.original_lhs=merge_symbol; + SSA_step.rhs.swap(merge_copy); + SSA_step.assignment_type=symex_targett::HIDDEN; + + SSA_step.cond_expr=equality_exprt(SSA_step.lhs, SSA_step.rhs); + SSA_step.type=goto_trace_stept::ASSIGNMENT; + SSA_step.source=empty_source; + } +} + +/*******************************************************************\ + +Function: implied_guards + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::set symex_slice_by_tracet::implied_guards(exprt e) +{ + std::set s; + + if (e.id() == "symbol") { // Guard or merge + const char* merge_loc = strstr(e.get("identifier").c_str(),"merge#"); + if (merge_loc == NULL) { + exprt e_copy (e); + simplify(e_copy, ns); + s.insert(e_copy); + return s; + } else { + int i = atoi(merge_loc+1); + if (merge_impl_cache_back[i].first) { + return merge_impl_cache_back[i].second; + } else { + merge_impl_cache_back[i].first = true; + exprt merge_copy (merge_map_back[i]); + merge_impl_cache_back[i].second = implied_guards(merge_copy); + return merge_impl_cache_back[i].second; + } + } + } else if (e.id() == "not") { // Definitely a guard + exprt e_copy(e); + simplify(e_copy, ns); + s.insert(e_copy); + return s; + } else if (e.id() == "and") { // Descend into and + Forall_operands(it,e) { + std::set r = implied_guards(*it); + for (std::set::iterator i = r.begin(); + i != r.end(); i++) { + s.insert(*i); + } + } + return s; + } else if (e.id() == "or") { // Descend into or + std::vector > rs; + Forall_operands(it,e) { + rs.push_back(implied_guards(*it)); + } + for (std::set::iterator i = rs.front().begin(); + i != rs.front().end();) { + for (std::vector >::iterator j = rs.begin(); + j != rs.end(); j++) { + if (j == rs.begin()) + j++; + std::set::iterator k = i; + i++; + if (j->count(*k) == 0) { + rs.front().erase(k); + break; + } + } + } + s = rs.front(); + return s; + } + + return s; +} + +/*******************************************************************\ + +Function: symex_slice_by_tracet::implies_false + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool symex_slice_by_tracet::implies_false(const exprt e) +{ + std::set imps = implied_guards(e); + + for(std::set::iterator + i=imps.begin(); + i!=imps.end(); i++) + { + exprt i_copy (*i); + i_copy.make_not(); + simplify(i_copy, ns); + if (imps.count(i_copy) != 0) + return true; + } + + return false; +} diff --git a/src/goto-symex/slice_by_trace.h b/src/goto-symex/slice_by_trace.h new file mode 100644 index 00000000000..97100434482 --- /dev/null +++ b/src/goto-symex/slice_by_trace.h @@ -0,0 +1,77 @@ +/*******************************************************************\ + +Module: Slicer for matching with trace files + +Author: Alex Groce (agroce@gmail.com) + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_SLICE_BY_TRACE_H +#define CPROVER_GOTO_SYMEX_SLICE_BY_TRACE_H + +#include "symex_target_equation.h" + +class symex_slice_by_tracet +{ +public: + symex_slice_by_tracet(const namespacet &_ns):ns(_ns) + { + } + + void slice_by_trace(std::string trace_files, + symex_target_equationt &equation); + + protected: + const namespacet &ns; + typedef std::set alphabett; + alphabett alphabet; + bool alphabet_parity; + std::string semantics; + + typedef std::pair, bool> event_sett; + typedef std::vector event_tracet; + + event_tracet sigma; + + typedef std::vector > value_tracet; + + value_tracet sigma_vals; + + typedef std::vector trace_conditionst; + + trace_conditionst t; + + std::set sliced_guards; + + std::vector merge_map_back; + + std::vector > > merge_impl_cache_back; + + irep_idt merge_identifier; + + exprt merge_symbol; + + void read_trace(std::string filename); + + bool parse_alphabet(std::string read_line); + + void parse_events(std::string read_line); + + void compute_ts_fd(symex_target_equationt &equation); + + void compute_ts_back(symex_target_equationt &equation); + + void slice_SSA_steps( + symex_target_equationt &equation, + std::set implications); + + bool matches(event_sett s, irep_idt event); + + void assign_merges(symex_target_equationt &equation); + + std::set implied_guards (exprt e); + + bool implies_false (exprt e); +}; + +#endif diff --git a/src/goto-symex/symex_clean_expr.cpp b/src/goto-symex/symex_clean_expr.cpp new file mode 100644 index 00000000000..744dc30aaeb --- /dev/null +++ b/src/goto-symex/symex_clean_expr.cpp @@ -0,0 +1,115 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "goto_symex.h" + +/*******************************************************************\ + +Function: goto_symext::process_array_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::process_array_expr(exprt &expr) +{ + if(expr.id()==ID_if) + { + if_exprt &if_expr=to_if_expr(expr); + process_array_expr(if_expr.true_case()); + process_array_expr(if_expr.false_case()); + if_expr.type()=if_expr.true_case().type(); + } + else if(expr.id()==ID_index) + { + // strip index + index_exprt &index_expr=to_index_expr(expr); + exprt tmp=index_expr.array(); + expr.swap(tmp); + } + else if(expr.id()==ID_typecast) + { + // strip + exprt tmp=to_typecast_expr(expr).op0(); + expr.swap(tmp); + process_array_expr(expr); + } + else if(expr.id()==ID_address_of) + { + // strip + exprt tmp=to_address_of_expr(expr).op0(); + expr.swap(tmp); + process_array_expr(expr); + } + else + Forall_operands(it, expr) + process_array_expr(*it); +} + +/*******************************************************************\ + +Function: goto_symext::replace_array_equal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::replace_array_equal(exprt &expr) +{ + if(expr.id()==ID_array_equal) + { + assert(expr.operands().size()==2); + + // we expect two index expressions + process_array_expr(expr.op0()); + process_array_expr(expr.op1()); + + // type checking + if(ns.follow(expr.op0().type())!= + ns.follow(expr.op1().type())) + expr.make_false(); + else + { + equality_exprt equality_expr(expr.op0(), expr.op1()); + expr.swap(equality_expr); + } + } + + Forall_operands(it, expr) + replace_array_equal(*it); +} + +/*******************************************************************\ + +Function: goto_symext::clean_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::clean_expr( + exprt &expr, + statet &state, + const bool write) +{ + replace_nondet(expr); + dereference(expr, state, write); + replace_array_equal(expr); +} diff --git a/src/goto-symex/symex_decl.cpp b/src/goto-symex/symex_decl.cpp new file mode 100644 index 00000000000..d10e2acea16 --- /dev/null +++ b/src/goto-symex/symex_decl.cpp @@ -0,0 +1,100 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include + +#include "goto_symex.h" + +/*******************************************************************\ + +Function: goto_symext::symex_decl + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_decl(statet &state) +{ + const goto_programt::instructiont &instruction=*state.source.pc; + + const codet &code=to_code(instruction.code); + + if(code.operands().size()==2) + throw "two-operand decl not supported here"; + + if(code.operands().size()!=1) + throw "decl expects one operand"; + + if(code.op0().id()!=ID_symbol) + throw "decl expects symbol as first operand"; + + // just do the L1 renaming to preserve locality + const irep_idt &identifier=code.op0().get(ID_identifier); + + irep_idt l1_identifier=state.top().level1(identifier); + + // forget the old l2 renaming to avoid SSA for it + state.level2.remove(l1_identifier); + + // increase the frame if we have seen this declaration before + while(state.declaration_history.find(l1_identifier)!= + state.declaration_history.end()) + { + unsigned index=state.top().level1.current_names[identifier]; + state.top().level1.rename(identifier, index+1); + l1_identifier=state.top().level1(identifier); + } + + state.declaration_history.insert(l1_identifier); + + state.top().local_variables.insert(l1_identifier); + + // seen it before? + // it should get a fresh value in any case + statet::level2t::current_namest::iterator it= + state.level2.current_names.find(l1_identifier); + + if(it!=state.level2.current_names.end()) + { + state.level2.rename(l1_identifier, it->second.count+1); + it->second.constant.make_nil(); + } + + // in case of pointers, put something into the value set + if(ns.follow(code.op0().type()).id()==ID_pointer) + { + exprt failed= + get_failed_symbol(to_symbol_expr(code.op0()), ns); + + exprt rhs; + + if(failed.is_not_nil()) + { + address_of_exprt address_of_expr; + address_of_expr.object()=failed; + address_of_expr.type()=code.op0().type(); + rhs=address_of_expr; + } + else + rhs=exprt(ID_invalid); + + symbol_exprt l1_lhs; + l1_lhs.type()=code.op0().type(); + l1_lhs.set_identifier(l1_identifier); + state.value_set.assign(l1_lhs, rhs, ns); + } +} diff --git a/src/goto-symex/symex_dereference.cpp b/src/goto-symex/symex_dereference.cpp new file mode 100644 index 00000000000..4a345859aa7 --- /dev/null +++ b/src/goto-symex/symex_dereference.cpp @@ -0,0 +1,150 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "goto_symex.h" +#include "renaming_ns.h" +#include "symex_dereference_state.h" + +/*******************************************************************\ + +Function: goto_symext::dereference_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::dereference_rec( + exprt &expr, + guardt &guard, + dereferencet &dereference, + const bool write) +{ + if(expr.id()==ID_dereference) + { + if(expr.operands().size()!=1) + throw "dereference takes one operand"; + + exprt tmp1; + tmp1.swap(expr.op0()); + + // first make sure there are no dereferences in there + dereference_rec(tmp1, guard, dereference, false); + + exprt tmp2=dereference.dereference( + tmp1, guard, write?dereferencet::WRITE:dereferencet::READ); + + expr.swap(tmp2); + } + else if(expr.id()=="implicit_dereference") + { + // old stuff, should be gone + assert(false); + } + else if(expr.id()==ID_index && + expr.operands().size()==2 && + expr.op0().type().id()==ID_pointer) + { + // old stuff, will go away + + exprt tmp=rewrite_index(to_index_expr(expr)).pointer(); + + // first make sure there are no dereferences in there + dereference_rec(tmp, guard, dereference, false); + + dereference.dereference(tmp, guard, write?dereferencet::WRITE:dereferencet::READ); + tmp.swap(expr); + } + else if(expr.id()==ID_address_of) + { + address_of_exprt &address_of_expr=to_address_of_expr(expr); + + exprt &object=address_of_expr.object(); + + if(object.id()==ID_dereference) + { + // ANSI-C guarantees &*p == p no matter what p is, + // even if it's complete garbage + assert(object.operands().size()==1); + exprt tmp=object.op0(); + expr.swap(tmp); + + // do rec. call, as p might have dereferencing in it + dereference_rec(expr, guard, dereference, false); + } + else + { + // Could be member, could be if, could be index. + // We first try the simplifier: this is to support stuff like + // ((char *)&((type *) 0)->mem - (char *)((type *) 0))) + // If this fails, we simply dereference. + + exprt tmp_copy=expr; + simplify(tmp_copy, ns); + + if(tmp_copy.is_constant() || + (tmp_copy.id()==ID_typecast && + tmp_copy.operands().size()==1 && + tmp_copy.op0().is_constant())) + { + tmp_copy.location()=expr.location(); + expr=tmp_copy; + } + else + dereference_rec(object, guard, dereference, false); + } + } + else + { + Forall_operands(it, expr) + dereference_rec(*it, guard, dereference, write); + } +} + +/*******************************************************************\ + +Function: goto_symext::dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::dereference( + exprt &expr, + statet &state, + const bool write) +{ + symex_dereference_statet symex_dereference_state(*this, state); + renaming_nst renaming_ns(ns, state); + + dereferencet dereference( + renaming_ns, + new_context, + options, + symex_dereference_state); + + // needs to be renamed to level 1 + assert(!state.call_stack.empty()); + state.top().level1.rename(expr); + + guardt guard; + dereference_rec(expr, guard, dereference, write); +} diff --git a/src/goto-symex/symex_dereference_state.cpp b/src/goto-symex/symex_dereference_state.cpp new file mode 100644 index 00000000000..6fc0a9acb91 --- /dev/null +++ b/src/goto-symex/symex_dereference_state.cpp @@ -0,0 +1,104 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "symex_dereference_state.h" +#include "renaming_ns.h" + +/*******************************************************************\ + +Function: symex_dereference_statet::dereference_failure + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_dereference_statet::dereference_failure( + const std::string &property, + const std::string &msg, + const guardt &guard) +{ +} + +/*******************************************************************\ + +Function: symex_dereference_statet::has_failed_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool symex_dereference_statet::has_failed_symbol( + const exprt &expr, + const symbolt *&symbol) +{ + renaming_nst renaming_ns(goto_symex.ns, state); + + if(expr.id()==ID_symbol) + { + const symbolt &ptr_symbol= + renaming_ns.lookup(to_symbol_expr(expr).get_identifier()); + + const irep_idt &failed_symbol= + ptr_symbol.type.get("#failed_symbol"); + + if(failed_symbol=="") return false; + + return !renaming_ns.lookup(failed_symbol, symbol); + } + + return false; +} + +/*******************************************************************\ + +Function: symex_dereference_statet::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_dereference_statet::get_value_set( + const exprt &expr, + value_setst::valuest &value_set) +{ + renaming_nst renaming_ns(goto_symex.ns, state); + + state.value_set.get_value_set(expr, value_set, renaming_ns); + + #if 0 + std::cout << "**************************\n"; + state.value_set.output(std::cout, renaming_ns); + std::cout << "**************************\n"; + #endif + + #if 0 + std::cout << "E: " << expr.pretty() << std::endl; + #endif + + #if 0 + std::cout << "**************************\n"; + for(expr_sett::const_iterator it=value_set.begin(); + it!=value_set.end(); + it++) + std::cout << from_expr(renaming_ns, "", *it) << std::endl; + std::cout << "**************************\n"; + #endif +} + diff --git a/src/goto-symex/symex_dereference_state.h b/src/goto-symex/symex_dereference_state.h new file mode 100644 index 00000000000..49aca552c97 --- /dev/null +++ b/src/goto-symex/symex_dereference_state.h @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "goto_symex.h" + +/*******************************************************************\ + + Class: symex_dereference_statet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +class symex_dereference_statet: + public dereference_callbackt +{ +public: + symex_dereference_statet( + goto_symext &_goto_symex, + goto_symext::statet &_state): + goto_symex(_goto_symex), + state(_state) + { + } + +protected: + goto_symext &goto_symex; + goto_symext::statet &state; + + virtual void dereference_failure( + const std::string &property, + const std::string &msg, + const guardt &guard); + + virtual void get_value_set( + const exprt &expr, + value_setst::valuest &value_set); + + virtual bool has_failed_symbol( + const exprt &expr, + const symbolt *&symbol); +}; + diff --git a/src/goto-symex/symex_function.cpp b/src/goto-symex/symex_function.cpp new file mode 100644 index 00000000000..5c43e4cd5c6 --- /dev/null +++ b/src/goto-symex/symex_function.cpp @@ -0,0 +1,479 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "goto_symex.h" + +/*******************************************************************\ + +Function: goto_symext::get_unwind_recursion + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_symext::get_unwind_recursion( + const irep_idt &identifier, + unsigned unwind) +{ + return false; +} + +/*******************************************************************\ + +Function: goto_symext::argument_assignments + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::argument_assignments( + const code_typet &function_type, + statet &state, + const exprt::operandst &arguments) +{ + // iterates over the operands + exprt::operandst::const_iterator it1=arguments.begin(); + + // these are the types of the arguments + const code_typet::argumentst &argument_types=function_type.arguments(); + + // iterates over the types of the arguments + for(code_typet::argumentst::const_iterator + it2=argument_types.begin(); + it2!=argument_types.end(); + it2++) + { + // if you run out of actual arguments there was a mismatch + if(it1==arguments.end()) + throw "function call: not enough arguments"; + + const code_typet::argumentt &argument=*it2; + + // this is the type the n-th argument should be + const typet &arg_type=argument.type(); + + const irep_idt &identifier=argument.get_identifier(); + + if(identifier==irep_idt()) + throw "no identifier for function argument"; + + const symbolt &symbol=ns.lookup(identifier); + symbol_exprt lhs=symbol_expr(symbol); + + if(it1->is_nil()) + { + } + else + { + exprt rhs=*it1; + + // it should be the same exact type + if(!base_type_eq(arg_type, rhs.type(), ns)) + { + const typet &f_arg_type=ns.follow(arg_type); + const typet &f_rhs_type=ns.follow(rhs.type()); + + // we are willing to do some limited conversion + if((f_arg_type.id()==ID_signedbv || + f_arg_type.id()==ID_unsignedbv || + f_arg_type.id()==ID_bool || + f_arg_type.id()==ID_pointer) && + (f_rhs_type.id()==ID_signedbv || + f_rhs_type.id()==ID_unsignedbv || + f_rhs_type.id()==ID_bool || + f_rhs_type.id()==ID_pointer)) + { + rhs.make_typecast(arg_type); + } + else + { + std::string error="function call: argument \""+ + id2string(identifier)+"\" type mismatch: got "+ + it1->type().to_string()+", expected "+ + arg_type.to_string(); + throw error; + } + } + + guardt guard; + symex_assign_symbol(state, lhs, rhs, guard, VISIBLE); + } + + it1++; + } + + if(function_type.has_ellipsis()) + { + for(; it1!=arguments.end(); it1++) + { + } + } + else if(it1!=arguments.end()) + { + // we got too many arguments, but we will just ignore them + } +} + +/*******************************************************************\ + +Function: goto_symext::symex_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_function_call( + const goto_functionst &goto_functions, + statet &state, + const code_function_callt &code) +{ + const exprt &function=code.function(); + + if(function.id()==ID_symbol) + symex_function_call_symbol(goto_functions, state, code); + else if(function.id()==ID_if) + throw "symex_function_call can't do if"; + else if(function.id()==ID_dereference) + throw "symex_function_call can't do dereference"; + else + throw "unexpected function for symex_function_call: "+function.id_string(); +} + +/*******************************************************************\ + +Function: goto_symext::symex_function_call_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_function_call_symbol( + const goto_functionst &goto_functions, + statet &state, + const code_function_callt &code) +{ + target.location(state.guard, state.source); + + assert(code.function().id()==ID_symbol); + + const irep_idt &identifier= + to_symbol_expr(code.function()).get_identifier(); + + if(identifier=="c::CBMC_trace") + { + symex_trace(state, code); + } + if(has_prefix(id2string(identifier), CPROVER_FKT_PREFIX)) + { + symex_fkt(state, code); + } + else if(has_prefix(id2string(identifier), CPROVER_MACRO_PREFIX)) + { + symex_macro(state, code); + } + else + symex_function_call_code(goto_functions, state, code); +} + +/*******************************************************************\ + +Function: goto_symext::symex_function_call_code + + Inputs: + + Outputs: + + Purpose: do function call by inlining + +\*******************************************************************/ + +void goto_symext::symex_function_call_code( + const goto_functionst &goto_functions, + statet &state, + const code_function_callt &call) +{ + const irep_idt &identifier= + to_symbol_expr(call.function()).get_identifier(); + + // find code in function map + + goto_functionst::function_mapt::const_iterator it= + goto_functions.function_map.find(identifier); + + if(it==goto_functions.function_map.end()) + throw "failed to find `"+id2string(identifier)+"' in function_map"; + + const goto_functionst::goto_functiont &goto_function=it->second; + + unsigned &unwinding_counter=function_unwind[identifier]; + + // see if it's too much + if(get_unwind_recursion(identifier, unwinding_counter)) + { + if(options.get_bool_option("unwinding-assertions")) + claim(false_exprt(), "recursion unwinding assertion", state); + + state.source.pc++; + return; + } + + if(!goto_function.body_available) + { + no_body(identifier); + + if(call.lhs().is_not_nil()) + { + exprt rhs=exprt("nondet_symbol", call.lhs().type()); + rhs.set(ID_identifier, "symex::"+i2string(nondet_count++)); + rhs.location()=call.location(); + code_assignt code(call.lhs(), rhs); + basic_symext::symex(state, code); + } + + state.source.pc++; + return; + } + + // read the arguments -- before the locality renaming + exprt::operandst arguments=call.arguments(); + for(unsigned i=0; i local_identifiers; + + get_local_identifiers(goto_function, local_identifiers); + + statet::framet &frame=state.top(); + + for(std::set::const_iterator + it=local_identifiers.begin(); + it!=local_identifiers.end(); + it++) + { + frame.level1.rename(*it, frame_nr); + irep_idt l1_name=frame.level1(*it); + frame.local_variables.insert(l1_name); + } +} + +/*******************************************************************\ + +Function: goto_symext::return_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::return_assignment(statet &state) +{ + statet::framet &frame=state.top(); + + const goto_programt::instructiont &instruction=*state.source.pc; + assert(instruction.is_return()); + const code_returnt &code=to_code_return(instruction.code); + + target.location(state.guard, state.source); + + if(code.operands().size()==1) + { + exprt value(code.op0()); + + dereference(value, state, false); + + if(frame.return_value.is_not_nil()) + { + code_assignt assignment(frame.return_value, value); + + assert( + ns.follow(assignment.lhs().type())== + ns.follow(assignment.rhs().type())); + basic_symext::symex_assign(state, assignment); + } + } + else + { + if(frame.return_value.is_not_nil()) + throw "return with unexpected value"; + } +} + +/*******************************************************************\ + +Function: goto_symext::symex_return + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_return(statet &state) +{ + return_assignment(state); + + // we treat this like an unconditional + // goto to the end of the function + + // put into state-queue + statet::goto_state_listt &goto_state_list= + state.top().goto_state_map[state.top().end_of_function]; + + goto_state_list.push_back(statet::goto_statet(state)); + + // kill this one + state.guard.make_false(); +} + +/*******************************************************************\ + +Function: goto_symext::symex_step_return + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_step_return(statet &state) +{ + return_assignment(state); +} + diff --git a/src/goto-symex/symex_goto.cpp b/src/goto-symex/symex_goto.cpp new file mode 100644 index 00000000000..b6762098e67 --- /dev/null +++ b/src/goto-symex/symex_goto.cpp @@ -0,0 +1,424 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "goto_symex.h" + +/*******************************************************************\ + +Function: goto_symext::symex_goto + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_goto(statet &state) +{ + const goto_programt::instructiont &instruction=*state.source.pc; + + exprt old_guard=instruction.guard; + dereference(old_guard, state, false); + + exprt new_guard=old_guard; + state.rename(new_guard, ns); + do_simplify(new_guard); + + target.location(state.guard, state.source); + + if(new_guard.is_false() || + state.guard.is_false()) + { + // reset unwinding counter + unwind_map[state.source]=0; + + // next instruction + state.source.pc++; + return; // nothing to do + } + + assert(!instruction.targets.empty()); + + // we only do deterministic gotos for now + if(instruction.targets.size()!=1) + throw "no support for non-deterministic gotos"; + + goto_programt::const_targett goto_target= + instruction.targets.front(); + + bool forward= + state.source.pc->location_number< + goto_target->location_number; + + if(!forward) // backwards? + { + unsigned &unwind=unwind_map[state.source]; + unwind++; + + if(get_unwind(state.source, unwind)) + { + loop_bound_exceeded(state, new_guard); + + // reset unwinding + unwind_map[state.source]=0; + + // next instruction + state.source.pc++; + return; + } + + if(new_guard.is_true()) + { + state.source.pc=goto_target; + return; // nothing else to do + } + } + + goto_programt::const_targett new_state_pc, state_pc; + + if(forward) + { + new_state_pc=goto_target; + state_pc=state.source.pc; + state_pc++; + } + else + { + new_state_pc=state.source.pc; + new_state_pc++; + state_pc=goto_target; + } + + state.source.pc=state_pc; + + // put into state-queue + statet::goto_state_listt &goto_state_list= + state.top().goto_state_map[new_state_pc]; + + goto_state_list.push_back(statet::goto_statet(state)); + statet::goto_statet &new_state=goto_state_list.back(); + + // adjust guards + if(new_guard.is_true()) + { + state.guard.make_false(); + } + else + { + // produce new guard symbol + exprt guard_expr; + + if(new_guard.id()==ID_symbol || + (new_guard.id()==ID_not && + new_guard.operands().size()==1 && + new_guard.op0().id()==ID_symbol)) + guard_expr=new_guard; + else + { + guard_expr=symbol_exprt(guard_identifier, bool_typet()); + exprt new_rhs=new_guard, rhs=old_guard; + new_rhs.make_not(); + rhs.make_not(); + + exprt new_lhs=guard_expr; + state.assignment(new_lhs, new_rhs, ns, false); + + guardt guard; + + target.assignment( + guard, + new_lhs, guard_expr, + new_rhs, + state.source, + symex_targett::HIDDEN); + + guard_expr.make_not(); + state.rename(guard_expr, ns); + } + + if(forward) + { + new_state.guard.add(guard_expr); + guard_expr.make_not(); + state.guard.add(guard_expr); + } + else + { + state.guard.add(guard_expr); + guard_expr.make_not(); + new_state.guard.add(guard_expr); + } + } +} + +/*******************************************************************\ + +Function: goto_symext::symex_step_goto + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_step_goto(statet &state, bool taken) +{ + const goto_programt::instructiont &instruction=*state.source.pc; + + exprt guard(instruction.guard); + dereference(guard, state, false); + state.rename(guard, ns); + + if(!taken) guard.make_not(); + + state.guard.guard_expr(guard); + do_simplify(guard); + + target.assumption(state.guard, guard, state.source); +} + +/*******************************************************************\ + +Function: goto_symext::merge_gotos + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::merge_gotos(statet &state) +{ + statet::framet &frame=state.top(); + + // first, see if this is a target at all + statet::goto_state_mapt::iterator state_map_it= + frame.goto_state_map.find(state.source.pc); + + if(state_map_it==frame.goto_state_map.end()) + return; // nothing to do + + // we need to merge + statet::goto_state_listt &state_list=state_map_it->second; + + for(statet::goto_state_listt::reverse_iterator + list_it=state_list.rbegin(); + list_it!=state_list.rend(); + list_it++) + { + statet::goto_statet &goto_state=*list_it; + + // do SSA phi functions + phi_function(goto_state, state); + + merge_value_sets(goto_state, state); + + // adjust guard + state.guard|=goto_state.guard; + + // adjust depth + state.depth=std::min(state.depth, goto_state.depth); + } + + // clean up to save some memory + frame.goto_state_map.erase(state_map_it); +} + +/*******************************************************************\ + +Function: goto_symext::merge_value_sets + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::merge_value_sets( + const statet::goto_statet &src, + statet &dest) +{ + if(dest.guard.is_false()) + { + dest.value_set=src.value_set; + return; + } + + dest.value_set.make_union(src.value_set); +} + +/*******************************************************************\ + +Function: goto_symext::phi_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::phi_function( + const statet::goto_statet &goto_state, + statet &dest_state) +{ + // go over all variables to see what changed + std::set variables; + + goto_state.level2.get_variables(variables); + dest_state.level2.get_variables(variables); + + for(std::set::const_iterator + it=variables.begin(); + it!=variables.end(); + it++) + { + if(*it==guard_identifier) + continue; // just a guard, don't bother + + if(goto_state.level2.current_number(*it)== + dest_state.level2.current_number(*it)) + continue; // not at all changed + + irep_idt original_identifier= + dest_state.get_original_name(*it); + + // changed! + const symbolt &symbol=ns.lookup(original_identifier); + + typet type(symbol.type); + + // type may need renaming + dest_state.rename(type, ns); + + exprt rhs; + + if(dest_state.guard.is_false()) + { + rhs=symbol_exprt(dest_state.current_name(goto_state, symbol.name), type); + } + else if(goto_state.guard.is_false()) + { + rhs=symbol_exprt(dest_state.current_name(symbol.name), type); + } + else + { + guardt tmp_guard(goto_state.guard); + + // this gets the diff between the guards + tmp_guard-=dest_state.guard; + + rhs=if_exprt(); + rhs.type()=type; + rhs.op0()=tmp_guard.as_expr(); + rhs.op1()=symbol_exprt(dest_state.current_name(goto_state, symbol.name), type); + rhs.op2()=symbol_exprt(dest_state.current_name(symbol.name), type); + } + + exprt lhs(symbol_expr(symbol)); + exprt new_lhs(lhs); + + dest_state.assignment(new_lhs, rhs, ns, false); + + guardt true_guard; + + target.assignment( + true_guard, + new_lhs, lhs, + rhs, + dest_state.source, + symex_targett::HIDDEN); + } +} + +/*******************************************************************\ + +Function: goto_symext::loop_bound_exceeded + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::loop_bound_exceeded( + statet &state, + const exprt &guard) +{ + const unsigned loop_number=state.source.pc->loop_number; + + exprt negated_cond; + + if(guard.is_true()) + negated_cond=false_exprt(); + else + negated_cond=gen_not(guard); + + bool unwinding_assertions= + options.get_bool_option("unwinding-assertions"); + + bool partial_loops= + options.get_bool_option("partial-loops"); + + if(!partial_loops) + { + if(unwinding_assertions) + { + // generate unwinding assertion + claim(negated_cond, + "unwinding assertion loop "+i2string(loop_number), + state); + } + else + { + // generate unwinding assumption, unless we permit partial loops + exprt guarded_expr=negated_cond; + state.guard.guard_expr(guarded_expr); + target.assumption(state.guard, guarded_expr, state.source); + } + + // add to state guard to prevent further assignments + state.guard.add(negated_cond); + } +} + +/*******************************************************************\ + +Function: goto_symext::get_unwind + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_symext::get_unwind( + const symex_targett::sourcet &source, + unsigned unwind) +{ + return false; +} diff --git a/src/goto-symex/symex_main.cpp b/src/goto-symex/symex_main.cpp new file mode 100644 index 00000000000..380f724da3d --- /dev/null +++ b/src/goto-symex/symex_main.cpp @@ -0,0 +1,326 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include "goto_symex.h" + +/*******************************************************************\ + +Function: goto_symext::new_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::new_name(symbolt &symbol) +{ + get_new_name(symbol, ns); + new_context.add(symbol); +} + +/*******************************************************************\ + +Function: goto_symext::claim + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::claim( + const exprt &claim_expr, + const std::string &msg, + statet &state) +{ + total_claims++; + + exprt expr=claim_expr; + state.rename(expr, ns); + + // first try simplifier on it + do_simplify(expr); + + if(expr.is_true()) return; + + state.guard.guard_expr(expr); + + remaining_claims++; + target.assertion(state.guard, expr, msg, state.source); +} + +/*******************************************************************\ + +Function: goto_symext::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::operator()( + statet& state, + const goto_functionst &goto_functions, + const goto_programt &goto_program) +{ + state.source.is_set=true; + state.source.pc=goto_program.instructions.begin(); + state.top().end_of_function=--goto_program.instructions.end(); + state.top().calling_location=state.top().end_of_function; + + while(state.source.pc!=goto_program.instructions.end()) + { + #if 0 + goto_program.output_instruction(ns, "", std::cout, state.source.pc); + #endif + + symex_step(goto_functions, state); + } +} + +/*******************************************************************\ + +Function: goto_symext::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::operator()( + const goto_functionst &goto_functions, + const goto_programt &goto_program) +{ + statet state; + operator() (state, goto_functions, goto_program); +} + +/*******************************************************************\ + +Function: goto_symext::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::operator()(const goto_functionst &goto_functions) +{ + goto_functionst::function_mapt::const_iterator it= + goto_functions.function_map.find(ID_main); + + if(it==goto_functions.function_map.end()) + throw "main symbol not found; please set an entry point"; + + const goto_programt &body=it->second.body; + + statet state; + state.initialize(goto_functions); + + while(state.source.pc!=body.instructions.end()) + symex_step(goto_functions, state); +} + +/*******************************************************************\ + +Function: goto_symext::symex_step + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_step( + const goto_functionst &goto_functions, + statet &state) +{ + #if 0 + std::cout << "\ninstruction type is " << state.source.pc->type << std::endl; + std::cout << state.source.pc->code.pretty(0, 100) << std::endl; + #endif + + assert(!state.call_stack.empty()); + + const goto_programt::instructiont &instruction=*state.source.pc; + + merge_gotos(state); + + // depth exceeded? + { + unsigned max_depth=atoi(options.get_option("depth").c_str()); + if(max_depth!=0 && state.depth>max_depth) + state.guard.add(false_exprt()); + state.depth++; + } + + // actually do instruction + switch(instruction.type) + { + case SKIP: + // really ignore + state.source.pc++; + break; + + case END_FUNCTION: + symex_end_of_function(state); + state.source.pc++; + break; + + case LOCATION: + target.location(state.guard, state.source); + state.source.pc++; + break; + + case GOTO: + symex_goto(state); + break; + + case ASSUME: + if(!state.guard.is_false()) + { + exprt tmp=instruction.guard; + clean_expr(tmp, state, false); + state.rename(tmp, ns); + do_simplify(tmp); + + if(!tmp.is_true()) + { + exprt tmp2=tmp; + state.guard.guard_expr(tmp2); + target.assumption(state.guard, tmp2, state.source); + + #if 0 + // we also add it to the state guard + state.guard.add(tmp); + #endif + } + } + + state.source.pc++; + break; + + case ASSERT: + if(!state.guard.is_false()) + if(options.get_bool_option("assertions") || + !state.source.pc->location.get_bool("user-provided")) + { + std::string msg=id2string(state.source.pc->location.get_comment()); + if(msg=="") msg="assertion"; + exprt tmp(instruction.guard); + clean_expr(tmp, state, false); + claim(tmp, msg, state); + } + + state.source.pc++; + break; + + case RETURN: + if(!state.guard.is_false()) + symex_return(state); + + state.source.pc++; + break; + + case ASSIGN: + if(!state.guard.is_false()) + { + code_assignt deref_code=to_code_assign(instruction.code); + + clean_expr(deref_code.lhs(), state, true); + clean_expr(deref_code.rhs(), state, false); + + basic_symext::symex_assign(state, deref_code); + } + + state.source.pc++; + break; + + case FUNCTION_CALL: + if(!state.guard.is_false()) + { + code_function_callt deref_code= + to_code_function_call(instruction.code); + + if(deref_code.lhs().is_not_nil()) + clean_expr(deref_code.lhs(), state, true); + + clean_expr(deref_code.function(), state, false); + + Forall_expr(it, deref_code.arguments()) + clean_expr(*it, state, false); + + symex_function_call(goto_functions, state, deref_code); + } + else + state.source.pc++; + break; + + case OTHER: + if(!state.guard.is_false()) + symex_other(goto_functions, state); + + state.source.pc++; + break; + + case DECL: + if(!state.guard.is_false()) + symex_decl(state); + + state.source.pc++; + break; + + case DEAD: + // ignore for now + state.source.pc++; + break; + + case START_THREAD: + throw "START_THREAD not yet implemented"; + + case END_THREAD: + { + // behaves like assume(0); + state.guard.add(false_exprt()); + exprt tmp=state.guard.as_expr(); + target.assumption(state.guard, tmp, state.source); + } + state.source.pc++; + break; + + case ATOMIC_BEGIN: + case ATOMIC_END: + // these don't have path semantics + state.source.pc++; + break; + + default: + assert(false); + } +} diff --git a/src/goto-symex/symex_other.cpp b/src/goto-symex/symex_other.cpp new file mode 100644 index 00000000000..106c08b66f6 --- /dev/null +++ b/src/goto-symex/symex_other.cpp @@ -0,0 +1,154 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "goto_symex.h" + +/*******************************************************************\ + +Function: goto_symext::symex_other + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_symext::symex_other( + const goto_functionst &goto_functions, + statet &state) +{ + const goto_programt::instructiont &instruction=*state.source.pc; + + const codet &code=to_code(instruction.code); + + const irep_idt &statement=code.get_statement(); + + if(statement==ID_expression) + { + // ignore + } + else if(statement==ID_cpp_delete || + statement=="cpp_delete[]") + { + codet clean_code=code; + clean_expr(clean_code, state, false); + symex_cpp_delete(state, clean_code); + } + else if(statement==ID_free) + { + // ignore + } + else if(statement==ID_printf) + { + codet clean_code=code; + clean_expr(clean_code, state, false); + symex_printf(state, nil_exprt(), clean_code); + } + else if(statement==ID_input) + { + codet clean_code(code); + clean_expr(clean_code, state, false); + symex_input(state, clean_code); + } + else if(statement==ID_output) + { + codet clean_code(code); + clean_expr(clean_code, state, false); + symex_output(state, clean_code); + } + else if(statement==ID_decl) + { + assert(false); // see symex_decl.cpp + } + else if(statement==ID_nondet) + { + // like skip + } + else if(statement==ID_asm) + { + // we ignore this for now + } + else if(statement==ID_array_copy) + { + assert(code.operands().size()==2); + + codet clean_code(code); + + // we need to add dereferencing for both operands + dereference_exprt d0, d1; + d0.op0()=code.op0(); + d0.type()=code.op0().type().subtype(); + d1.op0()=code.op1(); + d1.type()=code.op1().type().subtype(); + + clean_code.op0()=d0; + clean_code.op1()=d1; + + clean_expr(clean_code, state, false); + + process_array_expr(clean_code.op0()); + process_array_expr(clean_code.op1()); + + if(ns.follow(clean_code.op0().type()).id()!=ID_array) + throw "array_copy expects array operand"; + + if(!base_type_eq(clean_code.op0().type(), + clean_code.op1().type(), ns)) + throw "array_copy expects matching array types"; + + code_assignt assignment; + assignment.lhs()=clean_code.op0(); + assignment.rhs()=clean_code.op1(); + basic_symext::symex_assign(state, assignment); + } + else if(statement==ID_array_set) + { + assert(code.operands().size()==2); + + codet clean_code(code); + + // we need to add dereferencing for the first operand + dereference_exprt d0; + d0.op0()=code.op0(); + d0.type()=code.op0().type().subtype(); + + clean_code.op0()=d0; + + clean_expr(clean_code, state, false); + + process_array_expr(clean_code.op0()); + + const typet &array_type=ns.follow(clean_code.op0().type()); + + if(array_type.id()!=ID_array) + throw "array_set expects array operand"; + + if(!base_type_eq(array_type.subtype(), + clean_code.op1().type(), ns)) + clean_code.op1().make_typecast(array_type.subtype()); + + code_assignt assignment; + assignment.lhs()=clean_code.op0(); + assignment.rhs()=array_of_exprt(clean_code.op1(), clean_code.op0().type()); + basic_symext::symex_assign(state, assignment); + } + else if(statement==ID_user_specified_predicate) + { + // like skip + } + else + throw "unexpected statement: "+id2string(statement); +} diff --git a/src/goto-symex/symex_slice_class.h b/src/goto-symex/symex_slice_class.h new file mode 100644 index 00000000000..647b62a01bc --- /dev/null +++ b/src/goto-symex/symex_slice_class.h @@ -0,0 +1,41 @@ +/*******************************************************************\ + +Module: Slicer for symex traces + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "symex_target_equation.h" +#include "slice.h" + +/*******************************************************************\ + + Class: symex_slicet + + Purpose: + +\*******************************************************************/ + +class symex_slicet +{ +public: + void slice(symex_target_equationt &equation); + + void slice(symex_target_equationt &equation, + const expr_listt &expressions); + + void collect_open_variables( + const symex_target_equationt &equation, + symbol_sett &open_variables); + +protected: + symbol_sett depends; + + void get_symbols(const exprt &expr); + void get_symbols(const typet &type); + + void slice(symex_target_equationt::SSA_stept &SSA_step); + void slice_assignment(symex_target_equationt::SSA_stept &SSA_step); +}; + diff --git a/src/goto-symex/symex_target.cpp b/src/goto-symex/symex_target.cpp new file mode 100644 index 00000000000..0026100c057 --- /dev/null +++ b/src/goto-symex/symex_target.cpp @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "symex_target.h" + +/*******************************************************************\ + +Function: operator < + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator < (const symex_targett::sourcet &a, const symex_targett::sourcet &b) +{ + if(a.thread_nr < b.thread_nr) return true; + return a.pc < b.pc; +} diff --git a/src/goto-symex/symex_target.h b/src/goto-symex/symex_target.h new file mode 100644 index 00000000000..1e088f5bc24 --- /dev/null +++ b/src/goto-symex/symex_target.h @@ -0,0 +1,96 @@ +/*******************************************************************\ + +Module: Generate Equation using Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_SYMEX_TARGET_H +#define CPROVER_GOTO_SYMEX_SYMEX_TARGET_H + +#include +#include +#include + +#include + +class symex_targett +{ +public: + virtual ~symex_targett() { } + + struct sourcet + { + unsigned thread_nr; + goto_programt::const_targett pc; + bool is_set; + + sourcet():thread_nr(0), is_set(false) + { + } + + sourcet(goto_programt::const_targett _pc): + thread_nr(0), pc(_pc), is_set(true) + { + is_set=true; + } + }; + + typedef enum { STATE, HIDDEN } assignment_typet; + + // write to a variable - must be symbol + virtual void assignment( + const guardt &guard, + const exprt &lhs, + const exprt &original_lhs, + const exprt &rhs, + const sourcet &source, + assignment_typet assignment_type)=0; + + // just record a location + virtual void location( + const guardt &guard, + const sourcet &source)=0; + + // record output + virtual void output( + const guardt &guard, + const sourcet &source, + const irep_idt &output_id, + const std::list &args)=0; + + // record formatted output + virtual void output_fmt( + const guardt &guard, + const sourcet &source, + const irep_idt &output_id, + const irep_idt &fmt, + const std::list &args)=0; + + // record input + virtual void input( + const guardt &guard, + const sourcet &source, + const irep_idt &input_id, + const std::list &args)=0; + + // record an assumption + virtual void assumption( + const guardt &guard, + const exprt &cond, + const sourcet &source)=0; + + // record an assertion + virtual void assertion( + const guardt &guard, + const exprt &cond, + const std::string &msg, + const sourcet &source)=0; +}; + +bool operator < ( + const symex_targett::sourcet &a, + const symex_targett::sourcet &b); + +#endif diff --git a/src/goto-symex/symex_target_equation.cpp b/src/goto-symex/symex_target_equation.cpp new file mode 100644 index 00000000000..548823d0d9f --- /dev/null +++ b/src/goto-symex/symex_target_equation.cpp @@ -0,0 +1,539 @@ +/*******************************************************************\ + +Module: Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include + +#include "goto_symex_state.h" +#include "symex_target_equation.h" + +/*******************************************************************\ + +Function: symex_target_equationt::assignment + + Inputs: + + Outputs: + + Purpose: write to a variable + +\*******************************************************************/ + +void symex_target_equationt::assignment( + const guardt &guard, + const exprt &lhs, + const exprt &original_lhs, + const exprt &rhs, + const sourcet &source, + assignment_typet assignment_type) +{ + assert(lhs.is_not_nil()); + + SSA_steps.push_back(SSA_stept()); + SSA_stept &SSA_step=SSA_steps.back(); + + SSA_step.guard_expr=guard.as_expr(); + SSA_step.lhs=lhs; + SSA_step.original_lhs=original_lhs; + SSA_step.rhs=rhs; + SSA_step.assignment_type=assignment_type; + + SSA_step.cond_expr=equality_exprt(SSA_step.lhs, SSA_step.rhs); + SSA_step.type=goto_trace_stept::ASSIGNMENT; + SSA_step.source=source; +} + +/*******************************************************************\ + +Function: symex_target_equationt::location + + Inputs: + + Outputs: + + Purpose: just record a location + +\*******************************************************************/ + +void symex_target_equationt::location( + const guardt &guard, + const sourcet &source) +{ + SSA_steps.push_back(SSA_stept()); + SSA_stept &SSA_step=SSA_steps.back(); + + SSA_step.guard_expr=guard.as_expr(); + SSA_step.lhs.make_nil(); + SSA_step.type=goto_trace_stept::LOCATION; + SSA_step.source=source; +} + +/*******************************************************************\ + +Function: symex_target_equationt::output + + Inputs: + + Outputs: + + Purpose: just record output + +\*******************************************************************/ + +void symex_target_equationt::output( + const guardt &guard, + const sourcet &source, + const irep_idt &output_id, + const std::list &args) +{ + SSA_steps.push_back(SSA_stept()); + SSA_stept &SSA_step=SSA_steps.back(); + + SSA_step.guard_expr=guard.as_expr(); + SSA_step.lhs.make_nil(); + SSA_step.type=goto_trace_stept::OUTPUT; + SSA_step.source=source; + SSA_step.io_args=args; + SSA_step.io_id=output_id; +} + +/*******************************************************************\ + +Function: symex_target_equationt::output_fmt + + Inputs: + + Outputs: + + Purpose: just record formatted output + +\*******************************************************************/ + +void symex_target_equationt::output_fmt( + const guardt &guard, + const sourcet &source, + const irep_idt &output_id, + const irep_idt &fmt, + const std::list &args) +{ + SSA_steps.push_back(SSA_stept()); + SSA_stept &SSA_step=SSA_steps.back(); + + SSA_step.guard_expr=guard.as_expr(); + SSA_step.lhs.make_nil(); + SSA_step.type=goto_trace_stept::OUTPUT; + SSA_step.source=source; + SSA_step.io_args=args; + SSA_step.io_id=output_id; + SSA_step.formatted=true; + SSA_step.format_string=fmt; +} + +/*******************************************************************\ + +Function: symex_target_equationt::input + + Inputs: + + Outputs: + + Purpose: just record input + +\*******************************************************************/ + +void symex_target_equationt::input( + const guardt &guard, + const sourcet &source, + const irep_idt &input_id, + const std::list &args) +{ + SSA_steps.push_back(SSA_stept()); + SSA_stept &SSA_step=SSA_steps.back(); + + SSA_step.guard_expr=guard.as_expr(); + SSA_step.lhs.make_nil(); + SSA_step.type=goto_trace_stept::INPUT; + SSA_step.source=source; + SSA_step.io_args=args; + SSA_step.io_id=input_id; +} + +/*******************************************************************\ + +Function: symex_target_equationt::assumption + + Inputs: + + Outputs: + + Purpose: record an assumption + +\*******************************************************************/ + +void symex_target_equationt::assumption( + const guardt &guard, + const exprt &cond, + const sourcet &source) +{ + SSA_steps.push_back(SSA_stept()); + SSA_stept &SSA_step=SSA_steps.back(); + + SSA_step.guard_expr=guard.as_expr(); + SSA_step.lhs.make_nil(); + SSA_step.cond_expr=cond; + SSA_step.type=goto_trace_stept::ASSUME; + SSA_step.source=source; +} + +/*******************************************************************\ + +Function: symex_target_equationt::assertion + + Inputs: + + Outputs: + + Purpose: record an assertion + +\*******************************************************************/ + +void symex_target_equationt::assertion( + const guardt &guard, + const exprt &cond, + const std::string &msg, + const sourcet &source) +{ + SSA_steps.push_back(SSA_stept()); + SSA_stept &SSA_step=SSA_steps.back(); + + SSA_step.guard_expr=guard.as_expr(); + SSA_step.lhs.make_nil(); + SSA_step.cond_expr=cond; + SSA_step.type=goto_trace_stept::ASSERT; + SSA_step.source=source; + SSA_step.comment=msg; +} + +/*******************************************************************\ + +Function: symex_target_equationt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_target_equationt::convert( + prop_convt &prop_conv) +{ + convert_guards(prop_conv); + convert_assignments(prop_conv); + convert_assumptions(prop_conv); + convert_assertions(prop_conv); + convert_io(prop_conv); +} + +/*******************************************************************\ + +Function: symex_target_equationt::convert_assignments + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_target_equationt::convert_assignments( + decision_proceduret &decision_procedure) const +{ + for(SSA_stepst::const_iterator it=SSA_steps.begin(); + it!=SSA_steps.end(); it++) + { + if(it->is_assignment() && !it->ignore) + { + exprt tmp(it->cond_expr); + decision_procedure.set_to_true(tmp); + } + } +} + +/*******************************************************************\ + +Function: symex_target_equationt::convert_guards + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_target_equationt::convert_guards( + prop_convt &prop_conv) +{ + for(SSA_stepst::iterator it=SSA_steps.begin(); + it!=SSA_steps.end(); it++) + { + if(it->ignore) + it->guard_literal=const_literal(false); + else + { + exprt tmp(it->guard_expr); + it->guard_literal=prop_conv.convert(tmp); + } + } +} + +/*******************************************************************\ + +Function: symex_target_equationt::convert_assumptions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_target_equationt::convert_assumptions( + prop_convt &prop_conv) +{ + for(SSA_stepst::iterator it=SSA_steps.begin(); + it!=SSA_steps.end(); it++) + { + if(it->is_assume()) + { + if(it->ignore) + it->cond_literal=const_literal(true); + else + { + exprt tmp(it->cond_expr); + it->cond_literal=prop_conv.convert(tmp); + } + } + } +} + +/*******************************************************************\ + +Function: symex_target_equationt::convert_assertions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_target_equationt::convert_assertions( + prop_convt &prop_conv) +{ + // we find out if there is only _one_ assertion, + // which allows for a simpler formula + + unsigned number_of_assertions=count_assertions(); + + if(number_of_assertions==0) + return; + + if(number_of_assertions==1) + { + for(SSA_stepst::iterator it=SSA_steps.begin(); + it!=SSA_steps.end(); it++) + if(it->is_assert()) + { + prop_conv.set_to_false(it->cond_expr); + it->cond_literal=prop_conv.convert(it->cond_expr); + return; // prevent further assumptions! + } + else if(it->is_assume()) + prop_conv.set_to_true(it->cond_expr); + + assert(false); // unreachable + } + + bvt bv; + + bv.reserve(number_of_assertions); + + literalt assumption_literal=const_literal(true); + + for(SSA_stepst::iterator it=SSA_steps.begin(); + it!=SSA_steps.end(); it++) + if(it->is_assert()) + { + // do the expression + literalt tmp_literal=prop_conv.convert(it->cond_expr); + + it->cond_literal=prop_conv.prop.limplies(assumption_literal, tmp_literal); + + bv.push_back(prop_conv.prop.lnot(it->cond_literal)); + } + else if(it->is_assume()) + assumption_literal= + prop_conv.prop.land(assumption_literal, it->cond_literal); + + if(!bv.empty()) + prop_conv.prop.lcnf(bv); +} + +/*******************************************************************\ + +Function: symex_target_equationt::convert_io + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_target_equationt::convert_io( + decision_proceduret &dec_proc) +{ + unsigned io_count=0; + + for(SSA_stepst::iterator it=SSA_steps.begin(); + it!=SSA_steps.end(); it++) + if(!it->ignore) + { + for(std::list::const_iterator + o_it=it->io_args.begin(); + o_it!=it->io_args.end(); + o_it++) + { + exprt tmp=*o_it; + if(tmp.is_constant() || + tmp.id()==ID_string_constant) + it->converted_io_args.push_back(tmp); + else + { + symbol_exprt symbol; + symbol.type()=tmp.type(); + symbol.set_identifier("symex::io::"+i2string(io_count++)); + dec_proc.set_to(equality_exprt(tmp, symbol), true); + it->converted_io_args.push_back(symbol); + } + } + } +} + +/*******************************************************************\ + +Function: symex_target_equationt::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_target_equationt::output(std::ostream &out) const +{ + for(SSA_stepst::const_iterator + it=SSA_steps.begin(); + it!=SSA_steps.end(); + it++) + { + it->output(ns, out); + out << "--------------" << std::endl; + } +} + +/*******************************************************************\ + +Function: symex_target_equationt::SSA_stept::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void symex_target_equationt::SSA_stept::output( + const namespacet &ns, + std::ostream &out) const +{ + if(source.is_set) + { + out << "Thread " << source.thread_nr; + + if(source.pc->location.is_not_nil()) + out << " " << source.pc->location << std::endl; + else + out << std::endl; + } + + switch(type) + { + case goto_trace_stept::ASSERT: out << "ASSERT" << std::endl; break; + case goto_trace_stept::ASSUME: out << "ASSUME" << std::endl; break; + case goto_trace_stept::LOCATION: out << "LOCATION" << std::endl; break; + case goto_trace_stept::OUTPUT: out << "OUTPUT" << std::endl; break; + + case goto_trace_stept::ASSIGNMENT: + out << "ASSIGNMENT ("; + switch(assignment_type) + { + case HIDDEN: out << "HIDDEN"; break; + case STATE: out << "STATE"; break; + default:; + } + + out << ")" << std::endl; + break; + + default: assert(false); + } + + if(is_assert() || is_assume() || is_assignment()) + out << from_expr(ns, "", cond_expr) << std::endl; + + if(is_assert()) + out << comment << std::endl; + + out << "Guard: " << from_expr(ns, "", guard_expr) << std::endl; +} + +/*******************************************************************\ + +Function: operator << + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::ostream &operator<<( + std::ostream &out, + const symex_target_equationt &equation) +{ + equation.output(out); + return out; +} diff --git a/src/goto-symex/symex_target_equation.h b/src/goto-symex/symex_target_equation.h new file mode 100644 index 00000000000..244cb50d6fd --- /dev/null +++ b/src/goto-symex/symex_target_equation.h @@ -0,0 +1,187 @@ +/*******************************************************************\ + +Module: Generate Equation using Symbolic Execution + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_BASIC_SYMEX_EQUATION_H +#define CPROVER_BASIC_SYMEX_EQUATION_H + +#include +#include + +#include + +#include +#include + +#include "symex_target.h" +#include "goto_trace.h" + +class symex_target_equationt:public symex_targett +{ +public: + symex_target_equationt(const namespacet &_ns):ns(_ns) { } + + // assignment to a variable - must be symbol + virtual void assignment( + const guardt &guard, + const exprt &lhs, + const exprt &original_lhs, + const exprt &rhs, + const sourcet &source, + assignment_typet assignment_type); + + // just record a location + virtual void location( + const guardt &guard, + const sourcet &source); + + // output + virtual void output( + const guardt &guard, + const sourcet &source, + const irep_idt &fmt, + const std::list &args); + + // output, formatted + virtual void output_fmt( + const guardt &guard, + const sourcet &source, + const irep_idt &output_id, + const irep_idt &fmt, + const std::list &args); + + // input + virtual void input( + const guardt &guard, + const sourcet &source, + const irep_idt &input_id, + const std::list &args); + + // record an assumption + virtual void assumption( + const guardt &guard, + const exprt &cond, + const sourcet &source); + + // record an assertion + virtual void assertion( + const guardt &guard, + const exprt &cond, + const std::string &msg, + const sourcet &source); + + void convert(prop_convt &prop_conv); + void convert_assignments(decision_proceduret &decision_procedure) const; + void convert_assumptions(prop_convt &prop_conv); + void convert_assertions(prop_convt &prop_conv); + void convert_guards(prop_convt &prop_conv); + void convert_io(decision_proceduret &decision_procedure); + + exprt make_expression() const; + + class SSA_stept + { + public: + sourcet source; + goto_trace_stept::typet type; + + bool is_assert() const { return type==goto_trace_stept::ASSERT; } + bool is_assume() const { return type==goto_trace_stept::ASSUME; } + bool is_assignment() const { return type==goto_trace_stept::ASSIGNMENT; } + bool is_location() const { return type==goto_trace_stept::LOCATION; } + bool is_output() const { return type==goto_trace_stept::OUTPUT; } + + exprt guard_expr; + literalt guard_literal; + + // for ASSIGNMENT + exprt lhs, rhs, original_lhs; + assignment_typet assignment_type; + + // for ASSUME/ASSERT + exprt cond_expr; + literalt cond_literal; + std::string comment; + + // for INPUT/OUTPUT + irep_idt format_string, io_id; + bool formatted; + std::list io_args; + std::list converted_io_args; + + // for slicing + bool ignore; + + SSA_stept(): + guard_expr(static_cast(get_nil_irep())), + cond_expr(static_cast(get_nil_irep())), + formatted(false), + ignore(false) + { + } + + void output( + const namespacet &ns, + std::ostream &out) const; + }; + + unsigned count_assertions() const + { + unsigned i=0; + for(SSA_stepst::const_iterator + it=SSA_steps.begin(); + it!=SSA_steps.end(); it++) + if(it->is_assert()) i++; + return i; + } + + unsigned count_ignored_SSA_steps() const + { + unsigned i=0; + for(SSA_stepst::const_iterator + it=SSA_steps.begin(); + it!=SSA_steps.end(); it++) + if(it->ignore) i++; + return i; + } + + typedef std::list SSA_stepst; + SSA_stepst SSA_steps; + + SSA_stepst::iterator get_SSA_step(unsigned s) + { + SSA_stepst::iterator it=SSA_steps.begin(); + for(; s!=0; s--) + { + assert(it!=SSA_steps.end()); + it++; + } + return it; + } + + void output(std::ostream &out) const; + + void clear() + { + SSA_steps.clear(); + } + +protected: + const namespacet &ns; +}; + +extern inline bool operator<( + const symex_target_equationt::SSA_stepst::const_iterator a, + const symex_target_equationt::SSA_stepst::const_iterator b) +{ + return &(*a)<&(*b); +} + +std::ostream &operator<<(std::ostream &out, const symex_target_equationt::SSA_stept &step); +std::ostream &operator<<(std::ostream &out, const symex_target_equationt &equation); + +#endif diff --git a/src/goto-symex/xml_goto_trace.cpp b/src/goto-symex/xml_goto_trace.cpp new file mode 100644 index 00000000000..f490885e1a8 --- /dev/null +++ b/src/goto-symex/xml_goto_trace.cpp @@ -0,0 +1,276 @@ +/*******************************************************************\ + +Module: Traces of GOTO Programs + +Author: Daniel Kroening + + Date: November 2005 + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "xml_goto_trace.h" + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +xmlt convert( + const exprt &expr, + const namespacet &ns) +{ + const typet &type=ns.follow(expr.type()); + xmlt result; + + if(expr.id()==ID_constant) + { + if(type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bv) + { + result.name="integer"; + result.set_attribute("binary", expr.get_string(ID_value)); + + mp_integer i; + if(!to_integer(expr, i)) + result.data=integer2string(i); + } + else if(type.id()==ID_fixedbv) + { + result.name="fixed"; + result.set_attribute("binary", expr.get_string(ID_value)); + result.data=fixedbvt(expr).to_ansi_c_string(); + } + else if(type.id()==ID_floatbv) + { + result.name="float"; + result.set_attribute("binary", expr.get_string(ID_value)); + result.data=ieee_floatt(expr).to_ansi_c_string(); + } + else if(type.id()==ID_pointer) + { + result.name="pointer"; + if(expr.get(ID_value)==ID_NULL) + result.data="NULL"; + } + } + else if(expr.id()==ID_array) + { + result.name="array"; + + unsigned index=0; + + forall_operands(it, expr) + { + xmlt &e=result.new_element("element"); + e.set_attribute("index", index); + e.new_element(convert(*it, ns)); + index++; + } + } + else if(expr.id()==ID_struct) + { + result.name="struct"; + + forall_operands(it, expr) + { + xmlt &e=result.new_element("member"); + e.new_element(convert(*it, ns)); + } + } + else if(expr.id()==ID_union) + { + result.name="union"; + + assert(expr.operands().size()==1); + + xmlt &e=result.new_element("member"); + e.new_element(convert(expr.op0(), ns)); + } + else + result.name="unknown"; + + return result; +} + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert( + const namespacet &ns, + const goto_tracet &goto_trace, + xmlt &xml) +{ + xml=xmlt("goto_trace"); + + xml.new_element("mode").data=goto_trace.mode; + + locationt previous_location; + + for(goto_tracet::stepst::const_iterator + it=goto_trace.steps.begin(); + it!=goto_trace.steps.end(); + it++) + { + const locationt &location=it->pc->location; + + xmlt xml_location; + if(location.is_not_nil() && location.get_file()!="") + { + xml_location.new_element("file").data=id2string(location.get_file()); + xml_location.new_element("line").data=id2string(location.get_line()); + xml_location.name="location"; + } + + switch(it->type) + { + case goto_trace_stept::ASSERT: + if(!it->cond_value) + { + xmlt &xml_failure=xml.new_element("failure"); + xml_failure.new_element("reason").data=id2string(it->comment); + + xml_failure.new_element("thread").data=i2string(it->thread_nr); + + if(xml_location.name!="") + xml_failure.new_element().swap(xml_location); + } + break; + + case goto_trace_stept::ASSIGNMENT: + { + irep_idt identifier; + + if(it->original_lhs.is_not_nil()) + identifier=it->original_lhs.get("identifier"); + else + identifier=it->lhs.get("identifier"); + + xmlt &xml_assignment=xml.new_element("assignment"); + + if(xml_location.name!="") + xml_assignment.new_element().swap(xml_location); + + std::string value_string, binary_string, type_string; + + if(it->value.is_not_nil()) + value_string=from_expr(ns, identifier, it->value); + + if(it->value.type().is_not_nil()) + type_string=from_type(ns, identifier, it->value.type()); + + const symbolt *symbol; + irep_idt base_name, display_name; + + if(!ns.lookup(identifier, symbol)) + { + base_name=symbol->base_name; + display_name=symbol->display_name(); + if(type_string=="") + type_string=from_type(ns, identifier, symbol->type); + + xml_assignment.new_element("mode").data=id2string(symbol->mode); + } + + xml_assignment.new_element("thread").data=i2string(it->thread_nr); + xml_assignment.new_element("identifier").data=id2string(identifier); + xml_assignment.new_element("base_name").data=id2string(base_name); + xml_assignment.new_element("display_name").data=id2string(display_name); + xml_assignment.new_element("value").data=id2string(value_string); + xml_assignment.new_element("type").data=id2string(type_string); + xml_assignment.new_element("step_nr").data=i2string(it->step_nr); + + if(it->value.is_not_nil()) + xml_assignment.new_element("value_expression").new_element(convert(it->value, ns)); + } + break; + + case goto_trace_stept::OUTPUT: + { + printf_formattert printf_formatter(ns); + printf_formatter(id2string(it->format_string), it->io_args); + std::string text=printf_formatter.as_string(); + xmlt &xml_output=xml.new_element("output"); + xml_output.new_element("step_nr").data=i2string(it->step_nr); + xml_output.new_element("thread").data=i2string(it->thread_nr); + xml_output.new_element("text").data=text; + xml_output.new_element().swap(xml_location); + + for(std::list::const_iterator + l_it=it->io_args.begin(); + l_it!=it->io_args.end(); + l_it++) + { + xml_output.new_element("value").data=from_expr(ns, "", *l_it); + xml_output.new_element("value_expression"). + new_element(convert(*l_it, ns)); + } + } + break; + + case goto_trace_stept::INPUT: + { + xmlt &xml_output=xml.new_element("input"); + xml_output.new_element("input_id").data=id2string(it->io_id); + xml_output.new_element("step_nr").data=i2string(it->step_nr); + xml_output.new_element("thread").data=i2string(it->thread_nr); + + for(std::list::const_iterator + l_it=it->io_args.begin(); + l_it!=it->io_args.end(); + l_it++) + { + xml_output.new_element("value").data=from_expr(ns, "", *l_it); + xml_output.new_element("value_expression"). + new_element(convert(*l_it, ns)); + } + + xml_output.new_element().swap(xml_location); + } + break; + + default: + if(location!=previous_location) + { + // just the location + if(xml_location.name!="") + { + xmlt &xml_location_only=xml.new_element("location-only"); + xml_location_only.new_element("step_nr").data=i2string(it->step_nr); + xml_location_only.new_element("thread").data=i2string(it->thread_nr); + xml_location_only.new_element().swap(xml_location); + } + } + } + + if(location.is_not_nil() && location.get_file()!="") + previous_location=location; + } +} diff --git a/src/goto-symex/xml_goto_trace.h b/src/goto-symex/xml_goto_trace.h new file mode 100644 index 00000000000..b8326bb781d --- /dev/null +++ b/src/goto-symex/xml_goto_trace.h @@ -0,0 +1,23 @@ +/*******************************************************************\ + +Module: Traces of GOTO Programs + +Author: Daniel Kroening + +Date: November 2005 + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_SYMEX_XML_GOTO_TRACE_H +#define CPROVER_GOTO_SYMEX_XML_GOTO_TRACE_H + +#include + +#include "goto_trace.h" + +void convert( + const namespacet &ns, + const goto_tracet &goto_trace, + xmlt &xml); + +#endif diff --git a/src/langapi/Makefile b/src/langapi/Makefile new file mode 100644 index 00000000000..9f989d0b32b --- /dev/null +++ b/src/langapi/Makefile @@ -0,0 +1,20 @@ +SRC = mode.cpp language_ui.cpp languages.cpp language_util.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) + +INCLUDES= -I .. -I ../util + +include ../config.inc +include ../common + +all: langapi$(LIBEXT) + +############################################################################### + +langapi$(LIBEXT): $(OBJ) + $(LINKLIB) + +clean: + rm -f $(OBJ) langapi$(LIBEXT) + + diff --git a/src/langapi/language_ui.cpp b/src/langapi/language_ui.cpp new file mode 100644 index 00000000000..e23fc19693f --- /dev/null +++ b/src/langapi/language_ui.cpp @@ -0,0 +1,327 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include + +#include + +#include "language_ui.h" +#include "mode.h" + +/*******************************************************************\ + +Function: get_ui_cmdline + + Inputs: + + Outputs: + + Purpose: Constructor + +\*******************************************************************/ + +static ui_message_handlert::uit get_ui_cmdline(const cmdlinet &cmdline) +{ + if(cmdline.isset("gui")) + return ui_message_handlert::OLD_GUI; + else if(cmdline.isset("xml-ui")) + return ui_message_handlert::XML_UI; + + return ui_message_handlert::PLAIN; +} + +/*******************************************************************\ + +Function: language_uit::language_uit + + Inputs: + + Outputs: + + Purpose: Constructor + +\*******************************************************************/ + +language_uit::language_uit( + const std::string &program, + const cmdlinet &__cmdline): + ui_message_handler(get_ui_cmdline(__cmdline), program), + _cmdline(__cmdline) +{ + set_message_handler(ui_message_handler); +} + +/*******************************************************************\ + +Function: language_uit::~language_uit + + Inputs: + + Outputs: + + Purpose: Destructor + +\*******************************************************************/ + +language_uit::~language_uit() +{ +} + +/*******************************************************************\ + +Function: language_uit::parse() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool language_uit::parse() +{ + for(unsigned i=0; i<_cmdline.args.size(); i++) + { + if(parse(_cmdline.args[i])) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: language_uit::parse() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool language_uit::parse(const std::string &filename) +{ + std::ifstream infile(filename.c_str()); + + if(!infile) + { + error("failed to open input file", filename); + return true; + } + + std::pair + result=language_files.filemap.insert( + std::pair(filename, language_filet())); + + language_filet &lf=result.first->second; + + lf.filename=filename; + lf.language=get_language_from_filename(filename); + + if(lf.language==NULL) + { + error("failed to figure out type of file", filename); + return true; + } + + languaget &language=*lf.language; + + status("Parsing", filename); + + if(language.parse(infile, filename, get_message_handler())) + { + if(get_ui()==ui_message_handlert::PLAIN) + std::cerr << "PARSING ERROR" << std::endl; + + return true; + } + + lf.get_modules(); + + return false; +} + +/*******************************************************************\ + +Function: language_uit::typecheck + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool language_uit::typecheck() +{ + status("Converting"); + + language_files.set_message_handler(*message_handler); + language_files.set_verbosity(get_verbosity()); + + if(language_files.typecheck(context)) + { + if(get_ui()==ui_message_handlert::PLAIN) + std::cerr << "CONVERSION ERROR" << std::endl; + + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: language_uit::final + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool language_uit::final() +{ + language_files.set_message_handler(*message_handler); + language_files.set_verbosity(get_verbosity()); + + if(language_files.final(context)) + { + if(get_ui()==ui_message_handlert::PLAIN) + std::cerr << "CONVERSION ERROR" << std::endl; + + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: language_uit::show_symbol_table + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void language_uit::show_symbol_table() +{ + switch(get_ui()) + { + case ui_message_handlert::PLAIN: + show_symbol_table_plain(std::cout); + break; + + case ui_message_handlert::XML_UI: + show_symbol_table_xml_ui(); + break; + + default: + error("cannot show symbol table in this format"); + } +} + +/*******************************************************************\ + +Function: language_uit::show_symbol_table_xml_ui + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void language_uit::show_symbol_table_xml_ui() +{ + error("cannot show symbol table in this format"); +} + +/*******************************************************************\ + +Function: language_uit::show_symbol_table_plain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void language_uit::show_symbol_table_plain(std::ostream &out) +{ + out << std::endl << "Symbols:" << std::endl << std::endl; + + const namespacet ns(context); + + forall_symbols(it, context.symbols) + { + const symbolt &symbol=it->second; + + languaget *ptr; + + if(symbol.mode=="") + ptr=get_default_language(); + else + { + ptr=get_language_from_mode(symbol.mode); + if(ptr==NULL) throw "symbol "+id2string(symbol.name)+" has unknown mode"; + } + + std::auto_ptr p(ptr); + std::string type_str, value_str; + + if(symbol.type.is_not_nil()) + p->from_type(symbol.type, type_str, ns); + + if(symbol.value.is_not_nil()) + p->from_expr(symbol.value, value_str, ns); + + out << "Symbol......: " << symbol.name << std::endl; + out << "Pretty name.: " << symbol.pretty_name << std::endl; + out << "Module......: " << symbol.module << std::endl; + out << "Base name...: " << symbol.base_name << std::endl; + out << "Mode........: " << symbol.mode << std::endl; + out << "Type........: " << type_str << std::endl; + out << "Value.......: " << value_str << std::endl; + out << "Flags.......:"; + + if(symbol.lvalue) out << " lvalue"; + if(symbol.static_lifetime) out << " static_lifetime"; + if(symbol.thread_local) out << " thread_local"; + if(symbol.file_local) out << " file_local"; + if(symbol.theorem) out << " theorem"; + if(symbol.is_type) out << " type"; + if(symbol.is_extern) out << " extern"; + if(symbol.is_input) out << " input"; + if(symbol.is_output) out << " output"; + if(symbol.is_macro) out << " macro"; + if(symbol.is_actual) out << " actual"; + if(symbol.binding) out << " binding"; + if(symbol.free_var) out << " free_var"; + if(symbol.is_statevar) out << " statevar"; + + out << std::endl; + out << "Location....: " << symbol.location << std::endl; + + out << std::endl; + } +} diff --git a/src/langapi/language_ui.h b/src/langapi/language_ui.h new file mode 100644 index 00000000000..c1d0f42c142 --- /dev/null +++ b/src/langapi/language_ui.h @@ -0,0 +1,56 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_LANGUAGE_UI_H +#define CPROVER_LANGUAGE_UI_H + +#include +#include +#include +#include +#include + +class language_uit:public messaget +{ +public: + language_filest language_files; + contextt context; + + language_uit( + const std::string &program, + const cmdlinet &__cmdline); + virtual ~language_uit(); + + virtual bool parse(); + virtual bool parse(const std::string &filename); + virtual bool typecheck(); + virtual bool final(); + + virtual void clear_parse() + { + language_files.clear(); + } + + virtual void show_symbol_table(); + virtual void show_symbol_table_plain(std::ostream &out); + virtual void show_symbol_table_xml_ui(); + + typedef ui_message_handlert::uit uit; + + uit get_ui() + { + return ui_message_handler.get_ui(); + } + + ui_message_handlert ui_message_handler; + +protected: + const cmdlinet &_cmdline; +}; + +#endif diff --git a/src/langapi/language_util.cpp b/src/langapi/language_util.cpp new file mode 100644 index 00000000000..77f857a91f7 --- /dev/null +++ b/src/langapi/language_util.cpp @@ -0,0 +1,143 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include + +#include "language_util.h" +#include "mode.h" + +/*******************************************************************\ + +Function: from_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string from_expr( + const namespacet &ns, + const irep_idt &identifier, + const exprt &expr) +{ + std::auto_ptr p; + + if(identifier=="") + p=std::auto_ptr(get_default_language()); + else + { + const symbolt *symbol; + + if(ns.lookup(identifier, symbol)) + p=std::auto_ptr(get_default_language()); + else if(symbol->mode=="") + p=std::auto_ptr(get_default_language()); + else + { + languaget *ptr=get_language_from_mode(symbol->mode); + + if(ptr==NULL) + throw "symbol `"+id2string(symbol->name)+ + "' has unknown mode '"+id2string(symbol->mode)+"'"; + + p=std::auto_ptr(ptr); + } + } + + std::string result; + p->from_expr(expr, result, ns); + + return result; +} + +/*******************************************************************\ + +Function: from_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string from_type( + const namespacet &ns, + const irep_idt &identifier, + const typet &type) +{ + std::auto_ptr p; + + if(identifier=="") + p=std::auto_ptr(get_default_language()); + else + { + const symbolt *symbol; + + if(ns.lookup(identifier, symbol)) + p=std::auto_ptr(get_default_language()); + else if(symbol->mode=="") + p=std::auto_ptr(get_default_language()); + else + { + languaget *ptr=get_language_from_mode(symbol->mode); + + if(ptr==NULL) + throw "symbol `"+id2string(symbol->name)+ + "' has unknown mode '"+id2string(symbol->mode)+"'"; + + p=std::auto_ptr(ptr); + } + } + + std::string result; + p->from_type(type, result, ns); + + return result; +} + +/*******************************************************************\ + +Function: from_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string from_expr(const exprt &expr) +{ + contextt context; + return from_expr(namespacet(context), "", expr); +} + +/*******************************************************************\ + +Function: from_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string from_type(const typet &type) +{ + contextt context; + return from_type(namespacet(context), "", type); +} + diff --git a/src/langapi/language_util.h b/src/langapi/language_util.h new file mode 100644 index 00000000000..20c0065f61a --- /dev/null +++ b/src/langapi/language_util.h @@ -0,0 +1,28 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_LANGUAGE_UTIL_H +#define CPROVER_LANGUAGE_UTIL_H + +#include + +std::string from_expr( + const namespacet &ns, + const irep_idt &identifier, + const exprt &expr); + +std::string from_expr(const exprt &expr); + +std::string from_type( + const namespacet &ns, + const irep_idt &identifier, + const typet &type); + +std::string from_type(const typet &type); + +#endif diff --git a/src/langapi/languages.cpp b/src/langapi/languages.cpp new file mode 100644 index 00000000000..0de2304a1b0 --- /dev/null +++ b/src/langapi/languages.cpp @@ -0,0 +1,44 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include "languages.h" + +/*******************************************************************\ + +Function: languagest::languagest + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +languagest::languagest(const namespacet &_ns, languaget *_language):ns(_ns) +{ + language=_language; +} + +/*******************************************************************\ + +Function: languagest::~languagest + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +languagest::~languagest() +{ + delete language; +} + diff --git a/src/langapi/languages.h b/src/langapi/languages.h new file mode 100644 index 00000000000..e3f71489967 --- /dev/null +++ b/src/langapi/languages.h @@ -0,0 +1,48 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_LANGUAGES_H +#define CPROVER_LANGUAGES_H + +#include + +class languagest +{ +public: + // conversion of expressions + + bool from_expr(const exprt &expr, std::string &code) + { + return language->from_expr(expr, code, ns); + } + + bool from_type(const typet &type, std::string &code) + { + return language->from_type(type, code, ns); + } + + bool to_expr( + const std::string &code, + const std::string &module, + exprt &expr, + message_handlert &message_handler) + { + return language->to_expr(code, module, expr, message_handler, ns); + } + + // constructor / destructor + + languagest(const namespacet &_ns, languaget *_language); + virtual ~languagest(); + +protected: + const namespacet &ns; + languaget *language; +}; + +#endif diff --git a/src/langapi/mode.cpp b/src/langapi/mode.cpp new file mode 100644 index 00000000000..5805493e43f --- /dev/null +++ b/src/langapi/mode.cpp @@ -0,0 +1,133 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#include +#include + +#ifdef _WIN32 +#include +#endif + +#include "mode.h" + +struct language_entryt +{ + language_factoryt factory; + std::set extensions; + irep_idt mode; +}; + +typedef std::list languagest; +languagest languages; + +/*******************************************************************\ + +Function: register_language + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void register_language(language_factoryt factory) +{ + languages.push_back(language_entryt()); + std::auto_ptr l(factory()); + languages.back().factory=factory; + languages.back().extensions=l->extensions(); + languages.back().mode=l->id(); +} + +/*******************************************************************\ + +Function: get_language_from_mode + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +languaget *get_language_from_mode(const irep_idt &mode) +{ + for(languagest::const_iterator it=languages.begin(); + it!=languages.end(); + it++) + if(mode==it->mode) + return it->factory(); + + return NULL; +} + +/*******************************************************************\ + +Function: get_language_from_filename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +languaget *get_language_from_filename(const std::string &filename) +{ + std::size_t ext_pos=filename.rfind('.'); + + if(ext_pos==std::string::npos) return NULL; + + std::string extension= + std::string(filename, ext_pos+1, std::string::npos); + + if(extension=="") return NULL; + + for(languagest::const_iterator + l_it=languages.begin(); + l_it!=languages.end(); + l_it++) + { + #ifdef _WIN32 + for(std::set::const_iterator + e_it=l_it->extensions.begin(); + e_it!=l_it->extensions.end(); + e_it++) + if(stricmp(extension.c_str(), e_it->c_str())==0) + return l_it->factory(); + #else + if(l_it->extensions.find(extension)!=l_it->extensions.end()) + return l_it->factory(); + #endif + } + + return NULL; +} + +/*******************************************************************\ + +Function: get_default_language + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +languaget *get_default_language() +{ + assert(!languages.empty()); + return languages.front().factory(); +} + diff --git a/src/langapi/mode.h b/src/langapi/mode.h new file mode 100644 index 00000000000..7d173b70217 --- /dev/null +++ b/src/langapi/mode.h @@ -0,0 +1,21 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@cs.cmu.edu + +\*******************************************************************/ + +#ifndef CPROVER_MODE_H +#define CPROVER_MODE_H + +#include + +languaget *get_language_from_mode(const irep_idt &mode); +languaget *get_language_from_filename(const std::string &filename); +languaget *get_default_language(); + +typedef languaget *(*language_factoryt)(); +void register_language(language_factoryt factory); + +#endif diff --git a/src/pointer-analysis/Makefile b/src/pointer-analysis/Makefile new file mode 100644 index 00000000000..45455accc25 --- /dev/null +++ b/src/pointer-analysis/Makefile @@ -0,0 +1,25 @@ +SRC = value_set.cpp goto_program_dereference.cpp value_set_analysis.cpp \ + dereference.cpp pointer_offset_sum.cpp add_failed_symbols.cpp \ + show_value_sets.cpp value_set_domain.cpp rewrite_index.cpp \ + value_set_analysis_fi.cpp value_set_fi.cpp value_set_domain_fi.cpp \ + value_set_analysis_fivr.cpp value_set_fivr.cpp value_set_domain_fivr.cpp \ + value_set_analysis_fivrns.cpp value_set_fivrns.cpp \ + value_set_domain_fivrns.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) + +INCLUDES= -I .. -I ../util + +include ../config.inc +include ../common + +all: pointer-analysis$(LIBEXT) + +############################################################################### + +pointer-analysis$(LIBEXT): $(OBJ) + $(LINKLIB) + +clean: + rm -f $(OBJ) pointer-analysis$(LIBEXT) + diff --git a/src/pointer-analysis/add_failed_symbols.cpp b/src/pointer-analysis/add_failed_symbols.cpp new file mode 100644 index 00000000000..ef70308579a --- /dev/null +++ b/src/pointer-analysis/add_failed_symbols.cpp @@ -0,0 +1,125 @@ +/*******************************************************************\ + +Module: Pointer Dereferencing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "add_failed_symbols.h" + +/*******************************************************************\ + +Function: failed_symbol_id + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +irep_idt failed_symbol_id(const irep_idt &id) +{ + return id2string(id)+"$object"; +} + +/*******************************************************************\ + +Function: add_failed_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void add_failed_symbol(symbolt &symbol, contextt &context) +{ + if(!symbol.lvalue) return; + + if(symbol.type.get("#failed_symbol")!="") + return; + + if(symbol.type.id()==ID_pointer) + { + symbolt new_symbol; + new_symbol.lvalue=true; + new_symbol.module=symbol.module; + new_symbol.mode=symbol.mode; + new_symbol.base_name=id2string(symbol.base_name)+"$object"; + new_symbol.name=failed_symbol_id(symbol.name); + new_symbol.type=symbol.type.subtype(); + new_symbol.value.make_nil(); + new_symbol.type.set(ID_C_is_failed_symbol, true); + + symbol.type.set(ID_C_failed_symbol, new_symbol.name); + + if(new_symbol.type.id()==ID_pointer) + add_failed_symbol(new_symbol, context); // recursive call + + context.move(new_symbol); + } +} + +/*******************************************************************\ + +Function: add_failed_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void add_failed_symbols(contextt &context) +{ + // the symbol table iterators are not stable, and + // we are adding new symbols, this + // is why we need a list of pointers + typedef std::list< ::symbolt *> symbol_listt; + symbol_listt symbol_list; + + Forall_symbols(it, context.symbols) + symbol_list.push_back(&(it->second)); + + for(symbol_listt::const_iterator + it=symbol_list.begin(); + it!=symbol_list.end(); + it++) + { + add_failed_symbol(**it, context); + } +} + +/*******************************************************************\ + +Function: get_failed_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt get_failed_symbol( + const symbol_exprt &expr, + const namespacet &ns) +{ + const symbolt &symbol=ns.lookup(expr); + irep_idt failed_symbol_id=symbol.type.get("#failed_symbol"); + + if(failed_symbol_id==irep_idt()) + return nil_exprt(); + + const symbolt &failed_symbol=ns.lookup(failed_symbol_id); + + return symbol_exprt(failed_symbol_id, failed_symbol.type); +} diff --git a/src/pointer-analysis/add_failed_symbols.h b/src/pointer-analysis/add_failed_symbols.h new file mode 100644 index 00000000000..cc0b0a65452 --- /dev/null +++ b/src/pointer-analysis/add_failed_symbols.h @@ -0,0 +1,24 @@ +/*******************************************************************\ + +Module: Pointer Dereferencing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_FAILED_SYMBOLS_H +#define CPROVER_POINTER_ANALYSIS_FAILED_SYMBOLS_H + +#include +#include +#include + +void add_failed_symbols(contextt &context); + +irep_idt failed_symbol_id(const irep_idt &identifier); + +exprt get_failed_symbol( + const symbol_exprt &expr, + const namespacet &ns); + +#endif diff --git a/src/pointer-analysis/dereference.cpp b/src/pointer-analysis/dereference.cpp new file mode 100644 index 00000000000..313c31111b0 --- /dev/null +++ b/src/pointer-analysis/dereference.cpp @@ -0,0 +1,970 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "dereference.h" +#include "pointer_offset_sum.h" + +// global data, horrible +unsigned int dereferencet::invalid_counter=0; + +/*******************************************************************\ + +Function: dereferencet::has_dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool dereferencet::has_dereference(const exprt &expr) const +{ + forall_operands(it, expr) + if(has_dereference(*it)) + return true; + + if(expr.id()==ID_dereference) + return true; + + // we no longer do this one + if(expr.id()==ID_index && + expr.operands().size()==2 && + expr.op0().type().id()==ID_pointer) + assert(false); + + return false; +} + +/*******************************************************************\ + +Function: dereferencet::get_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const exprt &dereferencet::get_symbol(const exprt &expr) +{ + if(expr.id()==ID_member || expr.id()==ID_index) + return get_symbol(expr.op0()); + + return expr; +} + +/*******************************************************************\ + +Function: dereferencet::dereference + + Inputs: expression dest, to be dereferenced under given guard, + and given mode + + Outputs: returns pointer after dereferencing + + Purpose: + +\*******************************************************************/ + +exprt dereferencet::dereference( + const exprt &pointer, + const guardt &guard, + const modet mode) +{ + if(pointer.type().id()!=ID_pointer) + throw "dereference expected pointer type, but got "+ + pointer.type().pretty(); + + // type of the object + const typet &type=pointer.type().subtype(); + + #if 0 + std::cout << "DEREF: " << from_expr(ns, "", pointer) << std::endl; + #endif + + // first see if we have a "failed object" for this pointer + + const symbolt *failed_symbol; + exprt failure_value; + + if(dereference_callback.has_failed_symbol( + pointer, failed_symbol)) + { + // yes! + failure_value=symbol_expr(*failed_symbol); + } + else + { + // else: produce new symbol + + symbolt symbol; + symbol.name="symex::invalid_object"+i2string(invalid_counter++); + symbol.base_name="invalid_object"; + symbol.type=type; + + // make it a lvalue, so we can assign to it + symbol.lvalue=true; + + get_new_name(symbol, ns); + + failure_value=symbol_expr(symbol); + + new_context.move(symbol); + } + + failure_value.set("#invalid_object", true); + + // collect objects the pointer may point to + value_setst::valuest points_to_set; + + dereference_callback.get_value_set(pointer, points_to_set); + + #if 0 + for(value_setst::valuest::const_iterator + it=points_to_set.begin(); + it!=points_to_set.end(); + it++) + std::cout << "P: " << from_expr(ns, "", *it) << std::endl; + #endif + + // now build big case split + // only "good" objects + + exprt value=failure_value; + + for(value_setst::valuest::const_iterator + it=points_to_set.begin(); + it!=points_to_set.end(); + it++) + { + exprt new_value, pointer_guard; + + build_reference_to( + *it, mode, pointer, guard, + new_value, pointer_guard); + + if(new_value.is_not_nil()) + value=if_exprt(pointer_guard, new_value, value); + } + + #if 0 + std::cout << "R: " << from_expr(ns, "", value) << std::endl + << std::endl; + #endif + + if(value==failure_value && + options.get_bool_option("pointer-check")) + { + dereference_callback.dereference_failure( + "pointer dereference", + "invalid pointer", guard); + } + + return value; +} + +/*******************************************************************\ + +Function: dereferencet::add_checks + + Inputs: expression to be checked under guard and mode + + Outputs: + + Purpose: add pointer safety checks for given expression + +\*******************************************************************/ + +void dereferencet::add_checks( + const exprt &pointer, + const guardt &guard, + const modet mode) +{ + if(pointer.type().id()!=ID_pointer) + throw "dereference expected pointer type, but got "+ + pointer.type().pretty(); + + #if 0 + std::cout << "ADD CHECK: " << pointer.pretty() << std::endl; + #endif + + // collect objects the pointer may point to + value_setst::valuest points_to_set; + + dereference_callback.get_value_set(pointer, points_to_set); + + // if it's empty, we have a problem + if(points_to_set.empty()) + { + if(options.get_bool_option("pointer-check")) + { + dereference_callback.dereference_failure( + "pointer dereference", + "invalid pointer", guard); + } + } + else + { + for(value_setst::valuest::const_iterator + it=points_to_set.begin(); + it!=points_to_set.end(); + it++) + { + exprt new_value, pointer_guard; + + build_reference_to( + *it, mode, pointer, guard, + new_value, pointer_guard); + } + } +} + +/*******************************************************************\ + +Function: dereferencet::dereference_type_compare + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool dereferencet::dereference_type_compare( + const typet &object_type, + const typet &dereference_type) const +{ + if(dereference_type.id()==ID_empty) + return true; // always ok + + if(base_type_eq(object_type, dereference_type, ns)) + return true; // ok, they just match + + // check for struct prefixes + + const typet ot_base=ns.follow(object_type), + dt_base=ns.follow(dereference_type); + + if(ot_base.id()==ID_struct && + dt_base.id()==ID_struct) + { + if(to_struct_type(dt_base).is_prefix_of( + to_struct_type(ot_base))) + return true; // ok, dt is a prefix of ot + } + + // we are generous about code pointers + if(dereference_type.id()==ID_code && + object_type.id()==ID_code) + return true; + + // really different + + return false; +} + +/*******************************************************************\ + +Function: dereferencet::build_reference_to + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dereferencet::build_reference_to( + const exprt &what, + const modet mode, + const exprt &pointer_expr, + const guardt &guard, + exprt &value, + exprt &pointer_guard) +{ + const typet &dereference_type= + ns.follow(pointer_expr.type()).subtype(); + + value.make_nil(); + pointer_guard.make_false(); + + if(what.id()==ID_unknown || + what.id()==ID_invalid) + { + if(options.get_bool_option("pointer-check")) + { + // constraint that it actually is an invalid pointer + + exprt invalid_pointer_expr("invalid-pointer", bool_typet()); + invalid_pointer_expr.copy_to_operands(pointer_expr); + + // produce new guard + + guardt tmp_guard(guard); + tmp_guard.add(invalid_pointer_expr); + dereference_callback.dereference_failure( + "pointer dereference", + "invalid pointer", + tmp_guard); + } + + return; + } + + if(what.id()!=ID_object_descriptor) + throw "unknown points-to: "+what.id_string(); + + const object_descriptor_exprt &o=to_object_descriptor_expr(what); + + const exprt &root_object=o.root_object(); + const exprt &object=o.object(); + + #if 0 + std::cout << "O: " << from_expr(ns, "", root_object) << std::endl; + #endif + + if(root_object.id()=="NULL-object") + { + if(options.get_bool_option("pointer-check")) + { + null_pointer_exprt null_pointer(to_pointer_type(pointer_expr.type())); + + guardt tmp_guard(guard); + + if(o.offset().is_zero()) + { + tmp_guard.add(equality_exprt(pointer_expr, null_pointer)); + + dereference_callback.dereference_failure( + "pointer dereference", + "NULL pointer", tmp_guard); + } + else + { + exprt pointer_guard(ID_same_object, bool_typet()); + pointer_guard.copy_to_operands(pointer_expr, null_pointer); + tmp_guard.add(pointer_guard); + + dereference_callback.dereference_failure( + "pointer dereference", + "NULL plus offset pointer", tmp_guard); + } + } + } + else if(root_object.id()==ID_dynamic_object) + { + //const dynamic_object_exprt &dynamic_object= + // to_dynamic_object_expr(root_object); + + // can't remove here + + value=exprt(ID_dereference, dereference_type); + value.copy_to_operands(pointer_expr); + + if(options.get_bool_option("pointer-check")) + { + // constraint that it actually is a dynamic object + + exprt dynamic_object_expr(ID_dynamic_object, bool_typet()); + dynamic_object_expr.copy_to_operands(pointer_expr); + + //if(!dynamic_object.valid().is_true()) + { + // check if it is still alive + guardt tmp_guard(guard); + tmp_guard.add(dynamic_object_expr); + tmp_guard.add(gen_not(valid_object(ns, pointer_expr))); + dereference_callback.dereference_failure( + "pointer dereference", + "invalidated dynamic object", + tmp_guard); + } + + if(options.get_bool_option("bounds-check")) + { + if(!o.offset().is_zero()) + { + // check lower bound + exprt zero=gen_zero(index_type()); + assert(zero.is_not_nil()); + + exprt object_offset=unary_exprt( + ID_pointer_offset, pointer_expr, index_type()); + + binary_relation_exprt + inequality(object_offset, ID_lt, zero); + + guardt tmp_guard(guard); + tmp_guard.add(dynamic_object_expr); + tmp_guard.add(inequality); + dereference_callback.dereference_failure( + "pointer dereference", + "dynamic object lower bound", tmp_guard); + } + + { + // check upper bound + + // we check SAME_OBJECT(__CPROVER_bounds_check, p) && + // POINTER_OFFSET(p)+size>__CPROVER_malloc_size + + exprt malloc_object= + symbol_expr(ns.lookup(CPROVER_PREFIX "malloc_object")); + + exprt malloc_size= + symbol_expr(ns.lookup(CPROVER_PREFIX "malloc_size")); + + assert(ns.follow(malloc_object.type()).id()==ID_pointer); + + pointer_guard=exprt(ID_same_object, bool_typet()); + pointer_guard.copy_to_operands(pointer_expr, malloc_object); + + exprt object_offset=unary_exprt( + ID_pointer_offset, pointer_expr, index_type()); + + mp_integer element_size= + pointer_offset_size(ns, dereference_type); + + if(element_size<0) element_size=0; + + exprt size=from_integer(element_size, object_offset.type()); + + // need to add size + exprt sum=plus_exprt(object_offset, size); + + if(ns.follow(sum.type())!= + ns.follow(malloc_size.type())) + sum.make_typecast(malloc_size.type()); + + binary_relation_exprt + inequality(sum, ID_gt, malloc_size); + + guardt tmp_guard(guard); + tmp_guard.add(pointer_guard); + tmp_guard.add(inequality); + dereference_callback.dereference_failure( + "pointer dereference", + "dynamic object upper bound", tmp_guard); + } + } + } + } + else if(root_object.id()==ID_integer_address) + { + // this is stuff like *((char *)5) + const symbolt &memory_symbol=ns.lookup(CPROVER_PREFIX "memory"); + + exprt symbol_expr=symbol_exprt(memory_symbol.name, memory_symbol.type); + + exprt pointer_offset=unary_exprt( + ID_pointer_offset, pointer_expr, index_type()); + + exprt index_expr=index_exprt(symbol_expr, pointer_offset); + index_expr.type()=ns.follow(memory_symbol.type).subtype(); + + if(base_type_eq(index_expr.type(), dereference_type, ns)) + { + // types match already, what a coincidence! + } + else + { + // not quite ok + value=typecast_exprt(index_expr, dereference_type); + } + } + else + { + // something generic -- really has to be a symbol + address_of_exprt object_pointer(object); + + if(o.offset().is_zero()) + { + equality_exprt equality(pointer_expr, object_pointer); + + if(ns.follow(equality.lhs().type())!=ns.follow(equality.rhs().type())) + equality.lhs().make_typecast(equality.rhs().type()); + + pointer_guard=equality; + } + else + { + pointer_guard=exprt(ID_same_object, bool_typet()); + pointer_guard.copy_to_operands(pointer_expr, object_pointer); + } + + guardt tmp_guard(guard); + tmp_guard.add(pointer_guard); + + valid_check(object, tmp_guard, mode); + + const typet &object_type=ns.follow(object.type()); + const exprt &root_object=o.root_object(); + const typet &root_object_type=ns.follow(root_object.type()); + + if(dereference_type_compare(object_type, dereference_type) && + o.offset().is_zero()) + { + // The simplest case: types match, and offset is zero! + // This is great, we are almost done. + + value=object; + + if(object_type!=ns.follow(dereference_type)) + value.make_typecast(dereference_type); + } + else if(root_object_type.id()==ID_array && + dereference_type_compare(root_object_type.subtype(), dereference_type)) + { + // we have an array with a subtype that matches + // the dereferencing type + // we will require well-alignedness! + + exprt offset; + + // this should work as the object is essentially the root object + if(o.offset().is_constant()) + offset=o.offset(); + else + offset=unary_exprt(ID_pointer_offset, pointer_expr, index_type()); + + exprt adjusted_offset; + + // are we doing a byte? + mp_integer element_size= + pointer_offset_size(ns, dereference_type); + + if(element_size==1) + { + // no need to adjust offset + adjusted_offset=offset; + } + else + { + exprt element_size_expr= + from_integer(element_size, offset.type()); + + adjusted_offset=binary_exprt( + offset, ID_div, element_size_expr, offset.type()); + + // TODO: need to assert well-alignedness + } + + index_exprt index_expr= + index_exprt(root_object, adjusted_offset, root_object_type.subtype()); + + bounds_check(index_expr, guard); + + value=index_expr; + + if(ns.follow(value.type())!=ns.follow(dereference_type)) + value.make_typecast(dereference_type); + } + else + { + // we extract something from the root object + value=o.root_object(); + + // this is relative to the root object + const exprt offset= + unary_exprt(ID_pointer_offset, pointer_expr, index_type()); + + if(memory_model(value, dereference_type, tmp_guard, offset)) + { + // ok, done + } + else + { + if(options.get_bool_option("pointer-check")) + { + std::string msg="memory model not applicable (got `"; + msg+=from_type(ns, "", value.type()); + msg+="', expected `"; + msg+=from_type(ns, "", dereference_type); + msg+="')"; + + dereference_callback.dereference_failure( + "pointer dereference", + msg, tmp_guard); + } + + value.make_nil(); + return; // give up, no way that this is ok + } + } + } +} + +/*******************************************************************\ + +Function: dereferencet::valid_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dereferencet::valid_check( + const exprt &object, + const guardt &guard, + const modet mode) +{ + if(!options.get_bool_option("pointer-check")) + return; + + const exprt &symbol_expr=get_symbol(object); + + if(symbol_expr.id()==ID_string_constant) + { + // always valid, but can't write + + if(mode==WRITE) + { + dereference_callback.dereference_failure( + "pointer dereference", + "write access to string constant", + guard); + } + } + else if(symbol_expr.is_nil() || + symbol_expr.get_bool("#invalid_object")) + { + // always "valid", shut up + return; + } + else if(symbol_expr.id()==ID_symbol) + { + const irep_idt identifier= + to_symbol_expr(symbol_expr).get_identifier(); + + const symbolt &symbol=ns.lookup(identifier); + + if(symbol.type.get_bool(ID_C_is_failed_symbol)) + { + dereference_callback.dereference_failure( + "pointer dereference", + "invalid pointer", + guard); + } + + #if 0 + if(dereference_callback.is_valid_object(identifier)) + return; // always ok + #endif + } +} + +/*******************************************************************\ + +Function: dereferencet::bounds_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dereferencet::bounds_check( + const index_exprt &expr, + const guardt &guard) +{ + if(!options.get_bool_option("pointer-check")) + return; + + if(!options.get_bool_option("bounds-check")) + return; + + const typet &array_type=ns.follow(expr.op0().type()); + + if(array_type.id()!=ID_array) + throw "bounds check expected array type"; + + std::string name=array_name(ns, expr.array()); + + { + mp_integer i; + if(!to_integer(expr.index(), i) && + i>=0) + { + } + else + { + exprt zero=gen_zero(expr.index().type()); + + if(zero.is_nil()) + throw "no zero constant of index type "+ + expr.index().type().to_string(); + + binary_relation_exprt + inequality(expr.index(), ID_lt, zero); + + guardt tmp_guard(guard); + tmp_guard.add(inequality); + dereference_callback.dereference_failure( + "array bounds", + name+" lower bound", tmp_guard); + } + } + + const exprt &size_expr= + to_array_type(array_type).size(); + + if(size_expr.id()!=ID_infinity) + { + if(size_expr.is_nil()) + throw "index array operand of wrong type"; + + binary_relation_exprt inequality(expr.index(), ID_ge, size_expr); + + if(c_implicit_typecast( + inequality.op0(), + inequality.op1().type(), + ns)) + throw "index address of wrong type"; + + guardt tmp_guard(guard); + tmp_guard.add(inequality); + dereference_callback.dereference_failure( + "array bounds", + name+" upper bound", tmp_guard); + } +} + +/*******************************************************************\ + +Function: dereferencet::memory_model + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static unsigned bv_width(const typet &type) +{ + return atoi(type.get(ID_width).c_str()); +} + +static bool is_a_bv_type(const typet &type) +{ + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bv || + type.id()==ID_fixedbv || + type.id()==ID_floatbv; +} + +bool dereferencet::memory_model( + exprt &value, + const typet &to_type, + const guardt &guard, + const exprt &offset) +{ + // we will allow more or less arbitrary pointer type cast + + const typet from_type=value.type(); + + // first, check if it's really just a conversion, + // i.e., both are bit-vector types and the size is + // exacly the same + + if(is_a_bv_type(from_type) && + is_a_bv_type(to_type) && + bv_width(from_type)==bv_width(to_type)) + return memory_model_conversion(value, to_type, guard, offset); + + // otherwise, we will stich it together from bytes + + return memory_model_bytes(value, to_type, guard, offset); +} + +/*******************************************************************\ + +Function: dereferencet::memory_model_conversion + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool dereferencet::memory_model_conversion( + exprt &value, + const typet &to_type, + const guardt &guard, + const exprt &offset) +{ + const typet from_type=value.type(); + + // avoid semantic conversion in case of + // cast to float or fixed-point + if(from_type.id()!=ID_bv && + (to_type.id()==ID_fixedbv || to_type.id()==ID_floatbv)) + { + value.make_typecast(bv_typet(bv_width(from_type))); + value.make_typecast(to_type); + } + else + { + // only doing type conversion + // just do the typecast + value.make_typecast(to_type); + } + + // also assert that offset is zero + + if(options.get_bool_option("pointer-check")) + { + equality_exprt offset_not_zero(offset, gen_zero(offset.type())); + offset_not_zero.make_not(); + + guardt tmp_guard(guard); + tmp_guard.add(offset_not_zero); + dereference_callback.dereference_failure( + "word bounds", + "offset not zero", tmp_guard); + } + + return true; +} + +/*******************************************************************\ + +Function: dereferencet::memory_model_bytes + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool dereferencet::memory_model_bytes( + exprt &value, + const typet &to_type, + const guardt &guard, + const exprt &offset) +{ + const typet from_type=value.type(); + + // we refuse to convert to/from code + if(from_type.id()==ID_code || to_type.id()==ID_code) + return false; + + // won't do this without a committment to an endianess + if(config.ansi_c.endianess==configt::ansi_ct::NO_ENDIANESS) + return false; + + // But everything else we will try! + // We just rely on byte_extract to do the job! + + exprt byte_extract(byte_extract_id(), to_type); + byte_extract.copy_to_operands(value, offset); + value=byte_extract; + + // are we within the bounds? + if(options.get_bool_option("pointer-check")) + { + // upper bound + { + mp_integer from_width=pointer_offset_size(ns, from_type); + mp_integer to_width=pointer_offset_size(ns, to_type); + + exprt bound=from_integer(from_width-to_width, offset.type()); + + binary_relation_exprt + offset_upper_bound(offset, ID_gt, bound); + + guardt tmp_guard(guard); + tmp_guard.add(offset_upper_bound); + dereference_callback.dereference_failure( + "pointer dereference", + "object upper bound", tmp_guard); + } + + // lower bound is easy + if(!offset.is_zero()) + { + binary_relation_exprt + offset_lower_bound(offset, ID_lt, + gen_zero(offset.type())); + + guardt tmp_guard(guard); + tmp_guard.add(offset_lower_bound); + dereference_callback.dereference_failure( + "pointer dereference", + "object lower bound", tmp_guard); + } + } + + return true; +} + +/*******************************************************************\ + +Function: dereferencet::byte_extract_id + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +irep_idt dereferencet::byte_extract_id() +{ + switch(config.ansi_c.endianess) + { + case configt::ansi_ct::IS_LITTLE_ENDIAN: + return ID_byte_extract_little_endian; + + case configt::ansi_ct::IS_BIG_ENDIAN: + return ID_byte_extract_big_endian; + + default: + assert(false); + } +} diff --git a/src/pointer-analysis/dereference.h b/src/pointer-analysis/dereference.h new file mode 100644 index 00000000000..833d053802a --- /dev/null +++ b/src/pointer-analysis/dereference.h @@ -0,0 +1,132 @@ +/*******************************************************************\ + +Module: Pointer Dereferencing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_DEREFERENCE_H +#define CPROVER_POINTER_ANALYSIS_DEREFERENCE_H + +#include + +#include +#include +#include +#include +#include +#include + +#include "value_sets.h" + +class dereference_callbackt +{ +public: + virtual ~dereference_callbackt() + { + } + + virtual void dereference_failure( + const std::string &property, + const std::string &msg, + const guardt &guard)=0; + + typedef hash_set_cont expr_sett; + + virtual void get_value_set( + const exprt &expr, + value_setst::valuest &value_set)=0; + + virtual bool has_failed_symbol( + const exprt &expr, + const symbolt *&symbol)=0; +}; + +class dereferencet +{ +public: + dereferencet( + const namespacet &_ns, + contextt &_new_context, + const optionst &_options, + dereference_callbackt &_dereference_callback): + ns(_ns), + new_context(_new_context), + options(_options), + dereference_callback(_dereference_callback) + { } + + virtual ~dereferencet() { } + + typedef enum { READ, WRITE } modet; + + virtual exprt dereference( + const exprt &pointer, + const guardt &guard, + const modet mode); + + virtual void add_checks( + const exprt &pointer, + const guardt &guard, + const modet mode); + + bool has_dereference(const exprt &expr) const; + + typedef hash_set_cont expr_sett; + +private: + const namespacet &ns; + contextt &new_context; + const optionst &options; + dereference_callbackt &dereference_callback; + static unsigned invalid_counter; + + bool dereference_type_compare( + const typet &object_type, + const typet &dereference_type) const; + + void offset_sum( + exprt &dest, + const exprt &offset) const; + + void build_reference_to( + const exprt &what, + const modet mode, + const exprt &pointer, + const guardt &guard, + exprt &value, + exprt &pointer_guard); + + bool get_value_guard( + const exprt &symbol, + const exprt &premise, + exprt &value); + + static const exprt &get_symbol(const exprt &object); + + void bounds_check(const class index_exprt &expr, const guardt &guard); + void valid_check(const exprt &expr, const guardt &guard, const modet mode); + + bool memory_model( + exprt &value, + const typet &type, + const guardt &guard, + const exprt &offset); + + bool memory_model_conversion( + exprt &value, + const typet &type, + const guardt &guard, + const exprt &offset); + + bool memory_model_bytes( + exprt &value, + const typet &type, + const guardt &guard, + const exprt &offset); + + static irep_idt byte_extract_id(); +}; + +#endif diff --git a/src/pointer-analysis/goto_program_dereference.cpp b/src/pointer-analysis/goto_program_dereference.cpp new file mode 100644 index 00000000000..16bb879194b --- /dev/null +++ b/src/pointer-analysis/goto_program_dereference.cpp @@ -0,0 +1,632 @@ +/*******************************************************************\ + +Module: Dereferencing Operations on GOTO Programs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#include "goto_program_dereference.h" + +/*******************************************************************\ + +Function: goto_program_dereferencet::has_failed_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_program_dereferencet::has_failed_symbol( + const exprt &expr, + const symbolt *&symbol) +{ + if(expr.id()==ID_symbol) + { + if(expr.get_bool("#invalid_object")) + return false; + + const symbolt &ptr_symbol=ns.lookup(expr); + + const irep_idt &failed_symbol= + ptr_symbol.type.get("#failed_symbol"); + + if(failed_symbol==irep_idt()) return false; + + return !ns.lookup(failed_symbol, symbol); + } + + return false; +} + +/*******************************************************************\ + +Function: goto_program_dereferencet::is_valid_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool goto_program_dereferencet::is_valid_object( + const irep_idt &identifier) +{ + const symbolt &symbol=ns.lookup(identifier); + + if(symbol.type.id()==ID_code) + return true; + + if(symbol.static_lifetime) + return true; // global/static + + #if 0 + if(valid_local_variables->find(symbol.name)!= + valid_local_variables->end()) + return true; // valid local + #else + return true; + #endif + + return false; +} + +/*******************************************************************\ + +Function: goto_program_dereferencet::dereference_failure + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_program_dereferencet::dereference_failure( + const std::string &property, + const std::string &msg, + const guardt &guard) +{ + exprt guard_expr=guard.as_expr(); + + if(assertions.insert(guard_expr).second) + { + guard_expr.make_not(); + + // first try simplifier on it + if(options.get_bool_option("simplify")) + simplify(guard_expr, ns); + + if(!guard_expr.is_true()) + { + goto_programt::targett t=new_code.add_instruction(ASSERT); + t->guard.swap(guard_expr); + t->location=dereference_location; + t->location.set(ID_property, property); + t->location.set(ID_comment, "dereference failure: "+msg); + } + } +} + +/*******************************************************************\ + +Function: goto_program_dereferencet::dereference_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_program_dereferencet::dereference_rec( + exprt &expr, + guardt &guard, + const dereferencet::modet mode) +{ + if(!dereference.has_dereference(expr)) + return; + + if(expr.id()==ID_and || expr.id()==ID_or) + { + if(!expr.is_boolean()) + throw expr.id_string()+" must be Boolean, but got "+ + expr.pretty(); + + unsigned old_guards=guard.size(); + + for(unsigned i=0; isecond.body, checks_only); +} + +/*******************************************************************\ + +Function: goto_program_dereferencet::dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_program_dereferencet::dereference_instruction( + goto_programt::targett target, + bool checks_only) +{ + current_target=target; + #if 0 + valid_local_variables=&target->local_variables; + #endif + goto_programt::instructiont &i=*target; + + dereference_expr(i.guard, checks_only, dereferencet::READ); + + if(i.is_assign()) + { + if(i.code.operands().size()!=2) + throw "assignment expects two operands"; + + dereference_expr(i.code.op0(), checks_only, dereferencet::WRITE); + dereference_expr(i.code.op1(), checks_only, dereferencet::READ); + } + else if(i.is_function_call()) + { + code_function_callt &function_call=to_code_function_call(to_code(i.code)); + + if(function_call.lhs().is_not_nil()) + dereference_expr(function_call.lhs(), checks_only, dereferencet::WRITE); + + dereference_expr(function_call.function(), checks_only, dereferencet::READ); + dereference_expr(function_call.op2(), checks_only, dereferencet::READ); + } + else if(i.is_return()) + { + Forall_operands(it, i.code) + dereference_expr(*it, checks_only, dereferencet::READ); + } + else if(i.is_other()) + { + const irep_idt &statement=i.code.get(ID_statement); + + if(statement==ID_expression) + { + if(i.code.operands().size()!=1) + throw "expression expects one operand"; + + dereference_expr(i.code.op0(), checks_only, dereferencet::READ); + } + else if(statement==ID_printf) + { + Forall_operands(it, i.code) + dereference_expr(*it, checks_only, dereferencet::READ); + } + } +} + +/*******************************************************************\ + +Function: goto_program_dereferencet::dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_program_dereferencet::dereference_expression( + goto_programt::const_targett target, + exprt &expr) +{ + current_target=target; + #if 0 + valid_local_variables=&target->local_variables; + #endif + + dereference_expr(expr, false, dereferencet::READ); +} + +/*******************************************************************\ + +Function: goto_program_dereferencet::pointer_checks + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_program_dereferencet::pointer_checks( + goto_programt &goto_program) +{ + dereference_program(goto_program, true); +} + +/*******************************************************************\ + +Function: goto_program_dereferencet::pointer_checks + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void goto_program_dereferencet::pointer_checks( + goto_functionst &goto_functions) +{ + dereference_program(goto_functions, true); +} + +/*******************************************************************\ + +Function: remove_pointers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void remove_pointers( + goto_programt &goto_program, + contextt &context, + const optionst &options, + value_setst &value_sets) +{ + namespacet ns(context); + + goto_program_dereferencet + goto_program_dereference(ns, context, options, value_sets); + + goto_program_dereference.dereference_program(goto_program); +} + +/*******************************************************************\ + +Function: remove_pointers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void remove_pointers( + goto_functionst &goto_functions, + contextt &context, + const optionst &options, + value_setst &value_sets) +{ + namespacet ns(context); + + goto_program_dereferencet + goto_program_dereference(ns, context, options, value_sets); + + Forall_goto_functions(it, goto_functions) + goto_program_dereference.dereference_program(it->second.body); +} + +/*******************************************************************\ + +Function: pointer_checks + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void pointer_checks( + goto_programt &goto_program, + const namespacet &ns, + const optionst &options, + value_setst &value_sets) +{ + contextt new_context; + goto_program_dereferencet + goto_program_dereference(ns, new_context, options, value_sets); + goto_program_dereference.pointer_checks(goto_program); +} + +/*******************************************************************\ + +Function: pointer_checks + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void pointer_checks( + goto_functionst &goto_functions, + const namespacet &ns, + const optionst &options, + value_setst &value_sets) +{ + contextt new_context; + goto_program_dereferencet + goto_program_dereference(ns, new_context, options, value_sets); + goto_program_dereference.pointer_checks(goto_functions); +} + +/*******************************************************************\ + +Function: dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dereference( + goto_programt::const_targett target, + exprt &expr, + const namespacet &ns, + value_setst &value_sets) +{ + optionst options; + contextt new_context; + goto_program_dereferencet + goto_program_dereference(ns, new_context, options, value_sets); + goto_program_dereference.dereference_expression(target, expr); +} diff --git a/src/pointer-analysis/goto_program_dereference.h b/src/pointer-analysis/goto_program_dereference.h new file mode 100644 index 00000000000..18998f80ab0 --- /dev/null +++ b/src/pointer-analysis/goto_program_dereference.h @@ -0,0 +1,115 @@ +/*******************************************************************\ + +Module: Value Set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_GOTO_PROGRAM_DEREFERENCE_H +#define CPROVER_POINTER_ANALYSIS_GOTO_PROGRAM_DEREFERENCE_H + +#include +#include + +#include "value_sets.h" +#include "dereference.h" + +class goto_program_dereferencet:protected dereference_callbackt +{ +public: + goto_program_dereferencet( + const namespacet &_ns, + contextt &_new_context, + const optionst &_options, + value_setst &_value_sets): + options(_options), + ns(_ns), + value_sets(_value_sets), + dereference(_ns, _new_context, _options, *this) { } + + void dereference_program( + goto_programt &goto_program, + bool checks_only=false); + + void dereference_program( + goto_functionst &goto_functions, + bool checks_only=false); + + void pointer_checks(goto_programt &goto_program); + void pointer_checks(goto_functionst &goto_functions); + + void dereference_expression( + goto_programt::const_targett target, + exprt &expr); + + virtual ~goto_program_dereferencet() + { + } + +protected: + const optionst &options; + const namespacet &ns; + value_setst &value_sets; + dereferencet dereference; + + virtual bool is_valid_object(const irep_idt &identifier); + + virtual bool has_failed_symbol( + const exprt &expr, + const symbolt *&symbol); + + virtual void dereference_failure( + const std::string &property, + const std::string &msg, + const guardt &guard); + + virtual void get_value_set(const exprt &expr, value_setst::valuest &dest); + + void dereference_instruction( + goto_programt::targett target, + bool checks_only=false); + +protected: + void dereference_rec(exprt &expr, guardt &guard, const dereferencet::modet mode); + void dereference_expr(exprt &expr, const bool checks_only, const dereferencet::modet mode); + + const std::set *valid_local_variables; + locationt dereference_location; + goto_programt::const_targett current_target; + + std::set assertions; + goto_programt new_code; +}; + +void dereference( + goto_programt::const_targett target, + exprt &expr, + const namespacet &ns, + value_setst &value_sets); + +void remove_pointers( + goto_programt &goto_program, + contextt &context, + const optionst &options, + value_setst &value_sets); + +void remove_pointers( + goto_functionst &goto_functions, + contextt &context, + const optionst &options, + value_setst &value_sets); + +void pointer_checks( + goto_programt &goto_program, + const namespacet &ns, + const optionst &options, + value_setst &value_sets); + +void pointer_checks( + goto_functionst &goto_functions, + const namespacet &ns, + const optionst &options, + value_setst &value_sets); + +#endif diff --git a/src/pointer-analysis/object_numbering.h b/src/pointer-analysis/object_numbering.h new file mode 100644 index 00000000000..8cc269eb9fc --- /dev/null +++ b/src/pointer-analysis/object_numbering.h @@ -0,0 +1,18 @@ +/*******************************************************************\ + +Module: Value Set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_OBJECT_NUMBERING_H +#define CPROVER_POINTER_ANALYSIS_OBJECT_NUMBERING_H + +#include +#include +#include + +typedef hash_numbering object_numberingt; + +#endif diff --git a/src/pointer-analysis/pointer_offset_sum.cpp b/src/pointer-analysis/pointer_offset_sum.cpp new file mode 100644 index 00000000000..41454de4a50 --- /dev/null +++ b/src/pointer-analysis/pointer_offset_sum.cpp @@ -0,0 +1,41 @@ +/*******************************************************************\ + +Module: Pointer Analysis + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "pointer_offset_sum.h" + +/*******************************************************************\ + +Function: pointer_offset_sum + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt pointer_offset_sum(const exprt &a, const exprt &b) +{ + if(a.id()=="unknown") + return a; + else if(b.id()=="unknown") + return b; + else if(a.is_zero()) + return b; + else if(b.is_zero()) + return a; + + exprt new_offset("+", a.type()); + new_offset.copy_to_operands(a, b); + + if(new_offset.op1().type()!=a.type()) + new_offset.op1().make_typecast(a.type()); + + return new_offset; +} diff --git a/src/pointer-analysis/pointer_offset_sum.h b/src/pointer-analysis/pointer_offset_sum.h new file mode 100644 index 00000000000..ef2ce6ac0f2 --- /dev/null +++ b/src/pointer-analysis/pointer_offset_sum.h @@ -0,0 +1,16 @@ +/*******************************************************************\ + +Module: Pointer Dereferencing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_POINTER_OFFSET_SUM_H +#define CPROVER_POINTER_ANALYSIS_POINTER_OFFSET_SUM_H + +#include + +exprt pointer_offset_sum(const exprt &a, const exprt &b); + +#endif diff --git a/src/pointer-analysis/rewrite_index.cpp b/src/pointer-analysis/rewrite_index.cpp new file mode 100644 index 00000000000..3fc4c68f2b4 --- /dev/null +++ b/src/pointer-analysis/rewrite_index.cpp @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: Pointer Dereferencing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "rewrite_index.h" + +/*******************************************************************\ + +Function: rewrite_index + + Inputs: + + Outputs: + + Purpose: rewrite a[i] to *(a+i) + +\*******************************************************************/ + +dereference_exprt rewrite_index(const index_exprt &index_expr) +{ + dereference_exprt result; + result.pointer()=plus_exprt(index_expr.array(), index_expr.index()); + result.type()=index_expr.type(); + result.location()=index_expr.location(); + return result; +} diff --git a/src/pointer-analysis/rewrite_index.h b/src/pointer-analysis/rewrite_index.h new file mode 100644 index 00000000000..da0ffa8af59 --- /dev/null +++ b/src/pointer-analysis/rewrite_index.h @@ -0,0 +1,18 @@ +/*******************************************************************\ + +Module: Pointer Dereferencing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_REWRITE_INDEX_H +#define CPROVER_POINTER_ANALYSIS_REWRITE_INDEX_H + +#include + +// rewrite a[i] to *(a+i) + +dereference_exprt rewrite_index(const index_exprt &index_expr); + +#endif diff --git a/src/pointer-analysis/show_value_sets.cpp b/src/pointer-analysis/show_value_sets.cpp new file mode 100644 index 00000000000..e6e81c0af3c --- /dev/null +++ b/src/pointer-analysis/show_value_sets.cpp @@ -0,0 +1,79 @@ +/*******************************************************************\ + +Module: Show Value Sets + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "show_value_sets.h" + +/*******************************************************************\ + +Function: show_value_sets + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_value_sets( + ui_message_handlert::uit ui, + const goto_functionst &goto_functions, + const value_set_analysist &value_set_analysis) +{ + switch(ui) + { + case ui_message_handlert::XML_UI: + { + xmlt xml; + convert(goto_functions, value_set_analysis, xml); + std::cout << xml << std::endl; + } + break; + + case ui_message_handlert::PLAIN: + value_set_analysis.output(goto_functions, std::cout); + break; + + default:; + } +} + +/*******************************************************************\ + +Function: show_value_sets + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_value_sets( + ui_message_handlert::uit ui, + const goto_programt &goto_program, + const value_set_analysist &value_set_analysis) +{ + switch(ui) + { + case ui_message_handlert::XML_UI: + { + xmlt xml; + convert(goto_program, value_set_analysis, xml); + std::cout << xml << std::endl; + } + break; + + case ui_message_handlert::PLAIN: + value_set_analysis.output(goto_program, std::cout); + break; + + default:; + } +} diff --git a/src/pointer-analysis/show_value_sets.h b/src/pointer-analysis/show_value_sets.h new file mode 100644 index 00000000000..7fb92688be8 --- /dev/null +++ b/src/pointer-analysis/show_value_sets.h @@ -0,0 +1,29 @@ +/*******************************************************************\ + +Module: Show Value Sets + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GOTO_PROGRAMS_SHOW_VALUE_SETS_H +#define CPROVER_GOTO_PROGRAMS_SHOW_VALUE_SETS_H + +#include +#include + +#include + +#include "value_set_analysis.h" + +void show_value_sets( + ui_message_handlert::uit ui, + const goto_functionst &goto_functions, + const value_set_analysist &value_set_analysis); + +void show_value_sets( + ui_message_handlert::uit ui, + const goto_programt &goto_program, + const value_set_analysist &value_set_analysis); + +#endif diff --git a/src/pointer-analysis/value_set.cpp b/src/pointer-analysis/value_set.cpp new file mode 100644 index 00000000000..48b2addafb5 --- /dev/null +++ b/src/pointer-analysis/value_set.cpp @@ -0,0 +1,1565 @@ +/*******************************************************************\ + +Module: Value Set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "value_set.h" +#include "add_failed_symbols.h" + +const value_sett::object_map_dt value_sett::object_map_dt::empty; +object_numberingt value_sett::object_numbering; + +/*******************************************************************\ + +Function: value_sett::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::output( + const namespacet &ns, + std::ostream &out) const +{ + for(valuest::const_iterator + v_it=values.begin(); + v_it!=values.end(); + v_it++) + { + irep_idt identifier, display_name; + + const entryt &e=v_it->second; + + if(has_prefix(id2string(e.identifier), "value_set::dynamic_object")) + { + display_name=id2string(e.identifier)+e.suffix; + identifier=""; + } + else if(e.identifier=="value_set::return_value") + { + display_name="RETURN_VALUE"+e.suffix; + identifier=""; + } + else + { + #if 0 + const symbolt &symbol=ns.lookup(e.identifier); + display_name=symbol.display_name()+e.suffix; + identifier=symbol.name; + #else + identifier=id2string(e.identifier); + display_name=id2string(identifier)+e.suffix; + #endif + } + + out << display_name; + + out << " = { "; + + const object_map_dt &object_map=e.object_map.read(); + + unsigned width=0; + + for(object_map_dt::const_iterator + o_it=object_map.begin(); + o_it!=object_map.end(); + o_it++) + { + const exprt &o=object_numbering[o_it->first]; + + std::string result; + + if(o.id()==ID_invalid || o.id()==ID_unknown) + result=from_expr(ns, identifier, o); + else + { + result="<"+from_expr(ns, identifier, o)+", "; + + if(o_it->second.offset_is_set) + result+=integer2string(o_it->second.offset)+""; + else + result+="*"; + + result+=", "+from_type(ns, identifier, o.type()); + + result+=">"; + } + + out << result; + + width+=result.size(); + + object_map_dt::const_iterator next(o_it); + next++; + + if(next!=object_map.end()) + { + out << ", "; + if(width>=40) out << "\n "; + } + } + + out << " } " << std::endl; + } +} + +/*******************************************************************\ + +Function: value_sett::to_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt value_sett::to_expr(object_map_dt::const_iterator it) const +{ + const exprt &object=object_numbering[it->first]; + + if(object.id()==ID_invalid || + object.id()==ID_unknown) + return object; + + object_descriptor_exprt od; + + od.object()=object; + + if(it->second.offset_is_set) + od.offset()=from_integer(it->second.offset, index_type()); + + od.type()=od.object().type(); + + return od; +} + +/*******************************************************************\ + +Function: value_sett::make_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_sett::make_union(const value_sett::valuest &new_values) +{ + bool result=false; + + for(valuest::const_iterator + it=new_values.begin(); + it!=new_values.end(); + it++) + { + valuest::iterator it2=values.find(it->first); + + if(it2==values.end()) + { + // we always track these + if(has_prefix(id2string(it->second.identifier), + "value_set::dynamic_object") || + it->second.identifier=="value_set::return_value") + { + values.insert(*it); + result=true; + } + + continue; + } + + entryt &e=it2->second; + const entryt &new_e=it->second; + + if(make_union(e.object_map, new_e.object_map)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: value_sett::make_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_sett::make_union(object_mapt &dest, const object_mapt &src) const +{ + bool result=false; + + for(object_map_dt::const_iterator it=src.read().begin(); + it!=src.read().end(); + it++) + { + if(insert(dest, it)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: value_sett::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::get_value_set( + const exprt &expr, + value_setst::valuest &dest, + const namespacet &ns) const +{ + object_mapt object_map; + get_value_set(expr, object_map, ns); + + for(object_map_dt::const_iterator + it=object_map.read().begin(); + it!=object_map.read().end(); + it++) + dest.push_back(to_expr(it)); + + #if 0 + for(value_setst::valuest::const_iterator it=dest.begin(); it!=dest.end(); it++) + std::cout << "GET_VALUE_SET: " << from_expr(ns, "", *it) << std::endl; + #endif +} + +/*******************************************************************\ + +Function: value_sett::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::get_value_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const +{ + exprt tmp(expr); + simplify(tmp, ns); + + get_value_set_rec(tmp, dest, "", tmp.type(), ns); +} + +/*******************************************************************\ + +Function: value_sett::get_value_set_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::get_value_set_rec( + const exprt &expr, + object_mapt &dest, + const std::string &suffix, + const typet &original_type, + const namespacet &ns) const +{ + #if 0 + std::cout << "GET_VALUE_SET_REC EXPR: " << from_expr(ns, "", expr) << std::endl; + std::cout << "GET_VALUE_SET_REC SUFFIX: " << suffix << std::endl; + std::cout << std::endl; + #endif + + const typet &expr_type=ns.follow(expr.type()); + + if(expr.id()==ID_unknown || expr.id()==ID_invalid) + { + insert(dest, exprt(ID_unknown, original_type)); + } + else if(expr.id()==ID_index) + { + assert(expr.operands().size()==2); + + const typet &type=ns.follow(expr.op0().type()); + + assert(type.id()==ID_array || + type.id()==ID_incomplete_array); + + get_value_set_rec(expr.op0(), dest, "[]"+suffix, original_type, ns); + } + else if(expr.id()==ID_member) + { + assert(expr.operands().size()==1); + + const typet &type=ns.follow(expr.op0().type()); + + assert(type.id()==ID_struct || + type.id()==ID_union || + type.id()==ID_incomplete_struct || + type.id()==ID_incomplete_union); + + const std::string &component_name= + expr.get_string(ID_component_name); + + get_value_set_rec(expr.op0(), dest, + "."+component_name+suffix, original_type, ns); + } + else if(expr.id()==ID_symbol) + { + // is it a pointer, array or struct? + if(expr_type.id()==ID_pointer || + expr_type.id()==ID_struct || + expr_type.id()==ID_union || + expr_type.id()==ID_array) + { + // look it up + valuest::const_iterator v_it= + values.find(expr.get_string(ID_identifier)+suffix); + + if(v_it!=values.end()) + make_union(dest, v_it->second.object_map); + else + insert(dest, exprt(ID_unknown, original_type)); + } + else if(expr_type.id()==ID_signedbv || + expr_type.id()==ID_unsignedbv) + { + // integer got turned into a pointer + insert(dest, exprt(ID_integer_address, uchar_type())); + } + else + insert(dest, exprt(ID_unknown, original_type)); + } + else if(expr.id()==ID_if) + { + if(expr.operands().size()!=3) + throw "if takes three operands"; + + get_value_set_rec(expr.op1(), dest, suffix, original_type, ns); + get_value_set_rec(expr.op2(), dest, suffix, original_type, ns); + } + else if(expr.id()==ID_address_of) + { + if(expr.operands().size()!=1) + throw expr.id_string()+" expected to have one operand"; + + get_reference_set(expr.op0(), dest, ns); + } + else if(expr.id()==ID_dereference) + { + object_mapt reference_set; + get_reference_set(expr, reference_set, ns); + const object_map_dt &object_map=reference_set.read(); + + if(object_map.begin()==object_map.end()) + insert(dest, exprt(ID_unknown, original_type)); + else + { + for(object_map_dt::const_iterator + it1=object_map.begin(); + it1!=object_map.end(); + it1++) + { + const exprt &object=object_numbering[it1->first]; + get_value_set_rec(object, dest, suffix, original_type, ns); + } + } + } + else if(expr.id()=="reference_to") + { + object_mapt reference_set; + + get_reference_set(expr, reference_set, ns); + + const object_map_dt &object_map=reference_set.read(); + + if(object_map.begin()==object_map.end()) + insert(dest, exprt(ID_unknown, original_type)); + else + { + for(object_map_dt::const_iterator + it=object_map.begin(); + it!=object_map.end(); + it++) + { + const exprt &object=object_numbering[it->first]; + get_value_set_rec(object, dest, suffix, original_type, ns); + } + } + } + else if(expr.is_constant()) + { + // check if NULL + if(expr.get(ID_value)==ID_NULL && + expr_type.id()==ID_pointer) + { + insert(dest, exprt("NULL-object", expr_type.subtype()), 0); + } + else if(expr_type.id()==ID_unsignedbv || + expr_type.id()==ID_signedbv) + { + // an integer constant got turned into a pointer + insert(dest, exprt(ID_integer_address, uchar_type())); + } + else + insert(dest, exprt(ID_unknown, original_type)); + } + else if(expr.id()==ID_typecast) + { + if(expr.operands().size()!=1) + throw "typecast takes one operand"; + + // let's see what gets converted to what + + const typet &op_type=ns.follow(expr.op0().type()); + + if(op_type.id()==ID_pointer) + { + // we just ignore pointer TCs + get_value_set_rec(expr.op0(), dest, suffix, original_type, ns); + } + else if(op_type.id()==ID_unsignedbv || + op_type.id()==ID_signedbv) + { + // an integer constant got turned into a pointer + + if(expr.op0().is_zero()) + insert(dest, exprt("NULL-object", expr_type.subtype()), 0); + else + insert(dest, exprt(ID_integer_address, uchar_type())); + } + else + insert(dest, exprt(ID_unknown, original_type)); + } + else if(expr.id()==ID_plus || expr.id()==ID_minus) + { + if(expr.operands().size()<2) + throw expr.id_string()+" expected to have at least two operands"; + + if(expr_type.id()==ID_pointer) + { + // find the pointer operand + const exprt *ptr_operand=NULL; + + forall_operands(it, expr) + if(it->type().id()==ID_pointer) + { + if(ptr_operand==NULL) + ptr_operand=&(*it); + else + throw "more than one pointer operand in pointer arithmetic"; + } + + if(ptr_operand==NULL) + throw "pointer type sum expected to have pointer operand"; + + mp_integer i; + bool i_is_set=false; + + if(expr.operands().size()==2) + { + if(expr.op0().type().id()!=ID_pointer) + i_is_set=!to_integer(expr.op0(), i); + else + i_is_set=!to_integer(expr.op1(), i); + + if(i_is_set) + { + i*=pointer_offset_size(ns, ptr_operand->type().subtype()); + + if(expr.id()==ID_minus) i.negate(); + } + } + + object_mapt pointer_expr_set; + + get_value_set_rec( + *ptr_operand, pointer_expr_set, "", ptr_operand->type(), ns); + + for(object_map_dt::const_iterator + it=pointer_expr_set.read().begin(); + it!=pointer_expr_set.read().end(); + it++) + { + objectt object=it->second; + + // adjust by offset + if(object.offset_is_zero() && i_is_set) + object.offset=i; + else + object.offset_is_set=false; + + insert(dest, it->first, object); + } + } + else + { + // some other arithmetic + insert(dest, exprt(ID_unknown, original_type)); + } + } + else if(expr.id()==ID_sideeffect) + { + const irep_idt &statement=expr.get(ID_statement); + + if(statement==ID_function_call) + { + // these should be gone + throw "unexpected function_call sideeffect"; + } + else if(statement==ID_malloc) + { + assert(suffix==""); + + const typet &dynamic_type= + static_cast(expr.find("#type")); + + dynamic_object_exprt dynamic_object(dynamic_type); + dynamic_object.instance()=from_integer(location_number, typet(ID_natural)); + dynamic_object.valid()=true_exprt(); + + insert(dest, dynamic_object, 0); + } + else if(statement==ID_cpp_new || + statement=="cpp_new[]") + { + assert(suffix==""); + assert(expr_type.id()==ID_pointer); + + dynamic_object_exprt dynamic_object(expr_type.subtype()); + dynamic_object.instance()=from_integer(location_number, typet(ID_natural)); + dynamic_object.valid()=true_exprt(); + + insert(dest, dynamic_object, 0); + } + else + insert(dest, exprt(ID_unknown, original_type)); + } + else if(expr.id()==ID_struct) + { + // this is like a static struct object + insert(dest, address_of_exprt(expr), 0); + } + else if(expr.id()==ID_with) + { + assert(expr.operands().size()==3); + + // this is the array/struct + object_mapt tmp_map0; + get_value_set_rec(expr.op0(), tmp_map0, suffix, original_type, ns); + + // this is the update value -- note NO SUFFIX + object_mapt tmp_map2; + get_value_set_rec(expr.op2(), tmp_map2, "", original_type, ns); + + if(expr_type.id()==ID_struct) + { + #if 0 + const object_map_dt &object_map0=tmp_map0.read(); + irep_idt component_name=expr.op1().get(ID_component_name); + + bool insert=true; + + for(object_map_dt::const_iterator + it=object_map0.begin(); + it!=object_map0.end(); + it++) + { + const exprt &e=to_expr(it); + + if(e.id()==ID_member && + e.get(ID_component_name)==component_name) + { + if(insert) + { + dest.write().insert(tmp_map2.read().begin(), tmp_map2.read().end()); + insert=false; + } + } + else + dest.write().insert(*it); + } + #else + // Should be more precise! We only want "suffix" + make_union(dest, tmp_map0); + make_union(dest, tmp_map2); + #endif + } + else + { + make_union(dest, tmp_map0); + make_union(dest, tmp_map2); + } + } + else if(expr.id()==ID_array_of || + expr.id()==ID_array) + { + // give up + insert(dest, exprt(ID_unknown, original_type)); + } + else if(expr.id()==ID_dynamic_object) + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(expr); + + const std::string name= + "value_set::dynamic_object"+ + dynamic_object.instance().get_string(ID_value)+ + suffix; + + // look it up + valuest::const_iterator v_it=values.find(name); + + if(v_it==values.end()) + insert(dest, exprt(ID_unknown, original_type)); + else + make_union(dest, v_it->second.object_map); + } + + #if 0 + std::cout << "GET_VALUE_SET_REC RESULT:"; + for(object_map_dt::const_iterator + it=dest.read().begin(); + it!=dest.read().end(); + it++) + { + const exprt &e=to_expr(it); + std::cout << " " << from_expr(ns, "", e); + } + std::cout << std::endl << std::endl; + #endif +} + +/*******************************************************************\ + +Function: value_sett::dereference_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::dereference_rec( + const exprt &src, + exprt &dest) const +{ + // remove pointer typecasts + if(src.id()==ID_typecast) + { + assert(src.type().id()==ID_pointer); + + if(src.operands().size()!=1) + throw "typecast expects one operand"; + + dereference_rec(src.op0(), dest); + } + else + dest=src; +} + +/*******************************************************************\ + +Function: value_sett::get_reference_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::get_reference_set( + const exprt &expr, + value_setst::valuest &dest, + const namespacet &ns) const +{ + object_mapt object_map; + get_reference_set(expr, object_map, ns); + + for(object_map_dt::const_iterator + it=object_map.read().begin(); + it!=object_map.read().end(); + it++) + dest.push_back(to_expr(it)); +} + +/*******************************************************************\ + +Function: value_sett::get_reference_set_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::get_reference_set_rec( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const +{ + #if 0 + std::cout << "GET_REFERENCE_SET_REC EXPR: " << from_expr(ns, "", expr) << std::endl; + #endif + + if(expr.id()==ID_symbol || + expr.id()==ID_dynamic_object || + expr.id()==ID_string_constant) + { + if(expr.type().id()==ID_array && + expr.type().subtype().id()==ID_array) + insert(dest, expr); + else + insert(dest, expr, 0); + + return; + } + else if(expr.id()==ID_dereference) + { + if(expr.operands().size()!=1) + throw expr.id_string()+" expected to have one operand"; + + get_value_set_rec(expr.op0(), dest, "", expr.op0().type(), ns); + + #if 0 + for(expr_sett::const_iterator it=value_set.begin(); it!=value_set.end(); it++) + std::cout << "VALUE_SET: " << from_expr(ns, "", *it) << std::endl; + #endif + + return; + } + else if(expr.id()==ID_index) + { + if(expr.operands().size()!=2) + throw "index expected to have two operands"; + + const index_exprt &index_expr=to_index_expr(expr); + const exprt &array=index_expr.array(); + const exprt &offset=index_expr.index(); + const typet &array_type=ns.follow(array.type()); + + assert(array_type.id()==ID_array || + array_type.id()==ID_incomplete_array); + + object_mapt array_references; + get_reference_set(array, array_references, ns); + + const object_map_dt &object_map=array_references.read(); + + for(object_map_dt::const_iterator + a_it=object_map.begin(); + a_it!=object_map.end(); + a_it++) + { + const exprt &object=object_numbering[a_it->first]; + + if(object.id()==ID_unknown) + insert(dest, exprt(ID_unknown, expr.type())); + else + { + index_exprt index_expr(expr.type()); + index_expr.array()=object; + index_expr.index()=gen_zero(index_type()); + + // adjust type? + if(ns.follow(object.type())!=array_type) + index_expr.make_typecast(array.type()); + + objectt o=a_it->second; + mp_integer i; + + if(offset.is_zero()) + { + } + else if(!to_integer(offset, i) && + o.offset_is_zero()) + o.offset=i*pointer_offset_size(ns, array_type.subtype()); + else + o.offset_is_set=false; + + insert(dest, index_expr, o); + } + } + + return; + } + else if(expr.id()==ID_member) + { + const irep_idt &component_name=expr.get(ID_component_name); + + if(expr.operands().size()!=1) + throw "member expected to have one operand"; + + const exprt &struct_op=expr.op0(); + + object_mapt struct_references; + get_reference_set(struct_op, struct_references, ns); + + const object_map_dt &object_map=struct_references.read(); + + for(object_map_dt::const_iterator + it=object_map.begin(); + it!=object_map.end(); + it++) + { + const exprt &object=object_numbering[it->first]; + + if(object.id()==ID_unknown) + insert(dest, exprt(ID_unknown, expr.type())); + else + { + objectt o=it->second; + + member_exprt member_expr(expr.type()); + member_expr.op0()=object; + member_expr.set_component_name(component_name); + + // We cannot introduce a cast from scalar to non-scalar, + // thus, we can only adjust the types of structs and unions. + + const typet& final_object_type = ns.follow(object.type()); + + if(final_object_type.id()==ID_struct || + final_object_type.id()==ID_union) + { + // adjust type? + if(ns.follow(struct_op.type())!=final_object_type) + member_expr.op0().make_typecast(struct_op.type()); + + insert(dest, member_expr, o); + } + else + insert(dest, exprt(ID_unknown, expr.type())); + } + } + + return; + } + else if(expr.id()==ID_if) + { + if(expr.operands().size()!=3) + throw "if takes three operands"; + + get_reference_set_rec(expr.op1(), dest, ns); + get_reference_set_rec(expr.op2(), dest, ns); + return; + } + + insert(dest, exprt(ID_unknown, expr.type())); +} + +/*******************************************************************\ + +Function: value_sett::assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::assign( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool add_to_sets) +{ + #if 0 + std::cout << "ASSIGN LHS: " << from_expr(ns, "", lhs) << std::endl; + std::cout << "ASSIGN RHS: " << from_expr(ns, "", rhs) << std::endl; + output(ns, std::cout); + #endif + + const typet &type=ns.follow(lhs.type()); + + if(type.id()==ID_struct || + type.id()==ID_union) + { + const struct_union_typet &struct_union_type= + to_struct_union_type(type); + + for(struct_union_typet::componentst::const_iterator + c_it=struct_union_type.components().begin(); + c_it!=struct_union_type.components().end(); + c_it++) + { + const typet &subtype=c_it->type(); + const irep_idt &name=c_it->get(ID_name); + + // ignore methods and padding + if(subtype.id()==ID_code || + c_it->get_is_padding()) continue; + + member_exprt lhs_member(subtype); + lhs_member.set_component_name(name); + lhs_member.op0()=lhs; + + exprt rhs_member; + + if(rhs.id()==ID_unknown || + rhs.id()==ID_invalid) + { + rhs_member=exprt(rhs.id(), subtype); + } + else + { + if(!base_type_eq(rhs.type(), type, ns)) + assert(false); + + rhs_member=make_member(rhs, name, ns); + + assign(lhs_member, rhs_member, ns, add_to_sets); + } + } + } + else if(type.id()==ID_array) + { + exprt lhs_index(ID_index, type.subtype()); + lhs_index.copy_to_operands(lhs, exprt(ID_unknown, index_type())); + + if(rhs.id()==ID_unknown || + rhs.id()==ID_invalid) + { + assign(lhs_index, exprt(rhs.id(), type.subtype()), ns, add_to_sets); + } + else + { + assert(base_type_eq(rhs.type(), type, ns)); + + if(rhs.id()==ID_array_of) + { + assert(rhs.operands().size()==1); + assign(lhs_index, rhs.op0(), ns, add_to_sets); + } + else if(rhs.id()==ID_array || + rhs.id()==ID_constant) + { + forall_operands(o_it, rhs) + { + assign(lhs_index, *o_it, ns, add_to_sets); + add_to_sets=true; + } + } + else if(rhs.id()==ID_with) + { + assert(rhs.operands().size()==3); + + exprt op0_index(ID_index, type.subtype()); + op0_index.copy_to_operands(rhs.op0(), exprt(ID_unknown, index_type())); + + assign(lhs_index, op0_index, ns, add_to_sets); + assign(lhs_index, rhs.op2(), ns, true); + } + else + { + exprt rhs_index(ID_index, type.subtype()); + rhs_index.copy_to_operands(rhs, exprt(ID_unknown, index_type())); + assign(lhs_index, rhs_index, ns, true); + } + } + } + else + { + // basic type + object_mapt values_rhs; + get_value_set(rhs, values_rhs, ns); + + assign_rec(lhs, values_rhs, "", ns, add_to_sets); + } +} + +/*******************************************************************\ + +Function: value_sett::do_free + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::do_free( + const exprt &op, + const namespacet &ns) +{ + // op must be a pointer + if(op.type().id()!=ID_pointer) + throw "free expected to have pointer-type operand"; + + // find out what it points to + object_mapt value_set; + get_value_set(op, value_set, ns); + + const object_map_dt &object_map=value_set.read(); + + // find out which *instances* interest us + expr_sett to_mark; + + for(object_map_dt::const_iterator + it=object_map.begin(); + it!=object_map.end(); + it++) + { + const exprt &object=object_numbering[it->first]; + + if(object.id()==ID_dynamic_object) + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(object); + + if(dynamic_object.valid().is_true()) + to_mark.insert(dynamic_object.instance()); + } + } + + // mark these as 'may be invalid' + // this, unfortunately, destroys the sharing + for(valuest::iterator v_it=values.begin(); + v_it!=values.end(); + v_it++) + { + object_mapt new_object_map; + + const object_map_dt &old_object_map= + v_it->second.object_map.read(); + + bool changed=false; + + for(object_map_dt::const_iterator + o_it=old_object_map.begin(); + o_it!=old_object_map.end(); + o_it++) + { + const exprt &object=object_numbering[o_it->first]; + + if(object.id()==ID_dynamic_object) + { + const exprt &instance= + to_dynamic_object_expr(object).instance(); + + if(to_mark.count(instance)==0) + set(new_object_map, o_it); + else + { + // adjust + objectt o=o_it->second; + exprt tmp(object); + to_dynamic_object_expr(tmp).valid()=exprt(ID_unknown); + insert(new_object_map, tmp, o); + changed=true; + } + } + else + set(new_object_map, o_it); + } + + if(changed) + v_it->second.object_map=new_object_map; + } +} + +/*******************************************************************\ + +Function: value_sett::assign_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::assign_rec( + const exprt &lhs, + const object_mapt &values_rhs, + const std::string &suffix, + const namespacet &ns, + bool add_to_sets) +{ + #if 0 + std::cout << "ASSIGN_REC LHS: " << from_expr(ns, "", lhs) << std::endl; + std::cout << "ASSIGN_REC SUFFIX: " << suffix << std::endl; + + for(object_map_dt::const_iterator it=values_rhs.read().begin(); + it!=values_rhs.read().end(); + it++) + std::cout << "ASSIGN_REC RHS: " << + object_numbering[it->first] << std::endl; + #endif + + if(lhs.id()==ID_symbol) + { + const irep_idt &identifier=lhs.get(ID_identifier); + + if(add_to_sets) + make_union(get_entry(identifier, suffix).object_map, values_rhs); + else + get_entry(identifier, suffix).object_map=values_rhs; + } + else if(lhs.id()==ID_dynamic_object) + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(lhs); + + const std::string name= + "value_set::dynamic_object"+ + dynamic_object.instance().get_string(ID_value); + + make_union(get_entry(name, suffix).object_map, values_rhs); + } + else if(lhs.id()==ID_dereference) + { + if(lhs.operands().size()!=1) + throw lhs.id_string()+" expected to have one operand"; + + object_mapt reference_set; + get_reference_set(lhs, reference_set, ns); + + if(reference_set.read().size()!=1) + add_to_sets=true; + + for(object_map_dt::const_iterator + it=reference_set.read().begin(); + it!=reference_set.read().end(); + it++) + { + const exprt &object=object_numbering[it->first]; + + if(object.id()!=ID_unknown) + assign_rec(object, values_rhs, suffix, ns, add_to_sets); + } + } + else if(lhs.id()==ID_index) + { + if(lhs.operands().size()!=2) + throw "index expected to have two operands"; + + const typet &type=ns.follow(lhs.op0().type()); + + assert(type.id()==ID_array || type.id()==ID_incomplete_array); + + assign_rec(lhs.op0(), values_rhs, "[]"+suffix, ns, true); + } + else if(lhs.id()==ID_member) + { + if(lhs.operands().size()!=1) + throw "member expected to have one operand"; + + const std::string &component_name=lhs.get_string(ID_component_name); + + const typet &type=ns.follow(lhs.op0().type()); + + assert(type.id()==ID_struct || + type.id()==ID_union || + type.id()==ID_incomplete_struct || + type.id()==ID_incomplete_union); + + assign_rec(lhs.op0(), values_rhs, "."+component_name+suffix, ns, add_to_sets); + } + else if(lhs.id()=="valid_object" || + lhs.id()=="dynamic_size" || + lhs.id()=="dynamic_type" || + lhs.id()=="is_zero_string" || + lhs.id()=="zero_string" || + lhs.id()=="zero_string_length") + { + // we ignore this here + } + else if(lhs.id()==ID_string_constant) + { + // someone writes into a string-constant + // evil guy + } + else if(lhs.id()=="NULL-object") + { + // evil as well + } + else if(lhs.id()==ID_typecast) + { + const typecast_exprt &typecast_expr=to_typecast_expr(lhs); + + assign_rec(typecast_expr.op(), values_rhs, suffix, ns, add_to_sets); + } + else if(lhs.id()==ID_byte_extract_little_endian || + lhs.id()==ID_byte_extract_big_endian) + { + assert(lhs.operands().size()==2); + assign_rec(lhs.op0(), values_rhs, suffix, ns, true); + } + else if(lhs.id()==ID_integer_address) + { + // that's like assigning into __CPROVER_memory[...], + // which we don't track + } + else + throw "assign NYI: `"+lhs.id_string()+"'"; +} + +/*******************************************************************\ + +Function: value_sett::do_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::do_function_call( + const irep_idt &function, + const exprt::operandst &arguments, + const namespacet &ns) +{ + const symbolt &symbol=ns.lookup(function); + + const code_typet &type=to_code_type(symbol.type); + const code_typet::argumentst &argument_types=type.arguments(); + + // these first need to be assigned to dummy, temporary arguments + // and only thereafter to the actuals, in order + // to avoid overwriting actuals that are needed for recursive + // calls + + for(unsigned i=0; iget_identifier(); + if(identifier=="") continue; + + add_var(identifier, ""); + + const exprt v_expr= + symbol_exprt("value_set::dummy_arg_"+i2string(i), it->type()); + + exprt actual_lhs=symbol_exprt(identifier, it->type()); + assign(actual_lhs, v_expr, ns, true); + i++; + } +} + +/*******************************************************************\ + +Function: value_sett::do_end_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::do_end_function( + const exprt &lhs, + const namespacet &ns) +{ + if(lhs.is_nil()) return; + + symbol_exprt rhs("value_set::return_value", lhs.type()); + + assign(lhs, rhs, ns); +} + +/*******************************************************************\ + +Function: value_sett::apply_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::apply_code( + const codet &code, + const namespacet &ns) +{ + const irep_idt &statement=code.get(ID_statement); + + if(statement==ID_block) + { + forall_operands(it, code) + apply_code(to_code(*it), ns); + } + else if(statement==ID_function_call) + { + // shouldn't be here + assert(false); + } + else if(statement==ID_assign || + statement==ID_init) + { + if(code.operands().size()!=2) + throw "assignment expected to have two operands"; + + assign(code.op0(), code.op1(), ns); + } + else if(statement==ID_decl) + { + if(code.operands().size()!=1) + throw "decl expected to have one operand"; + + const exprt &lhs=code.op0(); + + if(lhs.id()!=ID_symbol) + throw "decl expected to have symbol on lhs"; + + if(ns.follow(lhs.type()).id()==ID_pointer) + { + // assign the address of the failed object + exprt failed=get_failed_symbol(to_symbol_expr(lhs), ns); + + if(failed.is_not_nil()) + { + address_of_exprt address_of_expr; + address_of_expr.object()=failed; + address_of_expr.type()=lhs.type(); + assign(lhs, address_of_expr, ns); + } + else + assign(lhs, exprt(ID_invalid), ns); + } + } + else if(statement=="specc_notify" || + statement=="specc_wait") + { + // ignore, does not change variables + } + else if(statement==ID_expression) + { + // can be ignored, we don't expect sideeffects here + } + else if(statement=="cpp_delete" || + statement=="cpp_delete[]") + { + // does nothing + } + else if(statement==ID_free) + { + // this may kill a valid bit + + if(code.operands().size()!=1) + throw "free expected to have one operand"; + + do_free(code.op0(), ns); + } + else if(statement=="lock" || statement=="unlock") + { + // ignore for now + } + else if(statement==ID_asm) + { + // ignore for now, probably not safe + } + else if(statement==ID_nondet) + { + // doesn't do anything + } + else if(statement==ID_printf) + { + // doesn't do anything + } + else if(statement==ID_return) + { + // this is turned into an assignment + if(code.operands().size()==1) + { + symbol_exprt lhs("value_set::return_value", code.op0().type()); + assign(lhs, code.op0(), ns); + } + } + else if(statement==ID_array_set) + { + } + else if(statement==ID_array_copy) + { + } + else if(statement==ID_assume) + { + guard(to_code_assume(code).op0(), ns); + } + else if(statement==ID_user_specified_predicate) + { + // doesn't do anything + } + else + { + std::cerr << code.pretty() << std::endl; + throw "value_sett: unexpected statement: "+id2string(statement); + } +} + +/*******************************************************************\ + +Function: value_sett::guard + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_sett::guard( + const exprt &expr, + const namespacet &ns) +{ + if(expr.id()==ID_and) + { + forall_operands(it, expr) + guard(*it, ns); + } + else if(expr.id()==ID_equal) + { + } + else if(expr.id()==ID_notequal) + { + } + else if(expr.id()==ID_not) + { + } + else if(expr.id()==ID_dynamic_object) + { + assert(expr.operands().size()==1); + + dynamic_object_exprt dynamic_object(uchar_type()); + //dynamic_object.instance()=from_integer(location_number, typet(ID_natural)); + dynamic_object.valid()=true_exprt(); + + address_of_exprt address_of(dynamic_object); + address_of.type()=expr.op0().type(); + + assign(expr.op0(), address_of, ns); + } +} + +/*******************************************************************\ + +Function: value_sett::make_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt value_sett::make_member( + const exprt &src, + const irep_idt &component_name, + const namespacet &ns) +{ + const struct_union_typet &struct_type= + to_struct_type(ns.follow(src.type())); + + if(src.id()==ID_struct || + src.id()==ID_constant) + { + unsigned no=struct_type.component_number(component_name); + assert(no + +#include +#include +#include + +#include "object_numbering.h" +#include "value_sets.h" + +class value_sett +{ +public: + value_sett():location_number(0) + { + } + + unsigned location_number; + static object_numberingt object_numbering; + + typedef irep_idt idt; + + class objectt + { + public: + objectt():offset_is_set(false) + { + } + + explicit objectt(const mp_integer &_offset): + offset(_offset), + offset_is_set(true) + { + } + + mp_integer offset; + bool offset_is_set; + bool offset_is_zero() const + { return offset_is_set && offset.is_zero(); } + }; + + class object_map_dt:public std::map + { + public: + const static object_map_dt empty; + }; + + exprt to_expr(object_map_dt::const_iterator it) const; + + typedef reference_counting object_mapt; + + void set(object_mapt &dest, object_map_dt::const_iterator it) const + { + dest.write()[it->first]=it->second; + } + + bool insert(object_mapt &dest, object_map_dt::const_iterator it) const + { + return insert(dest, it->first, it->second); + } + + bool insert(object_mapt &dest, const exprt &src) const + { + return insert(dest, object_numbering.number(src), objectt()); + } + + bool insert(object_mapt &dest, const exprt &src, const mp_integer &offset) const + { + return insert(dest, object_numbering.number(src), objectt(offset)); + } + + bool insert(object_mapt &dest, unsigned n, const objectt &object) const + { + if(dest.read().find(n)==dest.read().end()) + { + // new + dest.write()[n]=object; + return true; + } + else + { + objectt &old=dest.write()[n]; + + if(old.offset_is_set && object.offset_is_set) + { + if(old.offset==object.offset) + return false; + else + { + old.offset_is_set=false; + return true; + } + } + else if(!old.offset_is_set) + return false; + else + { + old.offset_is_set=false; + return true; + } + } + } + + bool insert(object_mapt &dest, const exprt &expr, const objectt &object) const + { + return insert(dest, object_numbering.number(expr), object); + } + + struct entryt + { + object_mapt object_map; + idt identifier; + std::string suffix; + + entryt() + { + } + + entryt(const idt &_identifier, const std::string _suffix): + identifier(_identifier), + suffix(_suffix) + { + } + }; + + typedef std::set expr_sett; + + static void add_objects(const entryt &src, expr_sett &dest); + + #ifdef USE_DSTRING + typedef std::map valuest; + #else + typedef hash_map_cont valuest; + #endif + + void get_value_set( + const exprt &expr, + value_setst::valuest &dest, + const namespacet &ns) const; + + expr_sett &get( + const idt &identifier, + const std::string &suffix); + + void make_any() + { + values.clear(); + } + + void clear() + { + values.clear(); + } + + void add_var(const idt &id, const std::string &suffix) + { + get_entry(id, suffix); + } + + void add_var(const entryt &e) + { + get_entry(e.identifier, e.suffix); + } + + entryt &get_entry(const idt &id, const std::string &suffix) + { + return get_entry(entryt(id, suffix)); + } + + entryt &get_entry(const entryt &e) + { + std::string index=id2string(e.identifier)+e.suffix; + + std::pair r= + values.insert(std::pair(index, e)); + + return r.first->second; + } + + void add_vars(const std::list &vars) + { + for(std::list::const_iterator + it=vars.begin(); + it!=vars.end(); + it++) + add_var(*it); + } + + void output( + const namespacet &ns, + std::ostream &out) const; + + valuest values; + + // true = added s.th. new + bool make_union(object_mapt &dest, const object_mapt &src) const; + + // true = added s.th. new + bool make_union(const valuest &new_values); + + // true = added s.th. new + bool make_union(const value_sett &new_values) + { + return make_union(new_values.values); + } + + void guard( + const exprt &expr, + const namespacet &ns); + + void apply_code( + const codet &code, + const namespacet &ns); + + void assign( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool add_to_sets=false); + + void do_function_call( + const irep_idt &function, + const exprt::operandst &arguments, + const namespacet &ns); + + // edge back to call site + void do_end_function( + const exprt &lhs, + const namespacet &ns); + + void get_reference_set( + const exprt &expr, + value_setst::valuest &dest, + const namespacet &ns) const; + +protected: + void get_value_set_rec( + const exprt &expr, + object_mapt &dest, + const std::string &suffix, + const typet &original_type, + const namespacet &ns) const; + + void get_value_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const; + + void get_reference_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const + { + get_reference_set_rec(expr, dest, ns); + } + + void get_reference_set_rec( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const; + + void dereference_rec( + const exprt &src, + exprt &dest) const; + + void assign_rec( + const exprt &lhs, + const object_mapt &values_rhs, + const std::string &suffix, + const namespacet &ns, + bool add_to_sets); + + void do_free( + const exprt &op, + const namespacet &ns); + + exprt make_member( + const exprt &src, + const irep_idt &component_name, + const namespacet &ns); +}; + +#endif diff --git a/src/pointer-analysis/value_set_analysis.cpp b/src/pointer-analysis/value_set_analysis.cpp new file mode 100644 index 00000000000..9952920b5ff --- /dev/null +++ b/src/pointer-analysis/value_set_analysis.cpp @@ -0,0 +1,414 @@ +/*******************************************************************\ + +Module: Value Set Propagation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +//#include + +#include +#include +#include + +#include + +#include "value_set_analysis.h" + +/*******************************************************************\ + +Function: value_set_analysist::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysist::initialize( + const goto_programt &goto_program) +{ + baset::initialize(goto_program); + add_vars(goto_program); +} + +/*******************************************************************\ + +Function: value_set_analysist::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysist::initialize( + const goto_functionst &goto_functions) +{ + baset::initialize(goto_functions); + add_vars(goto_functions); +} + +/*******************************************************************\ + +Function: value_set_analysist::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysist::add_vars( + const goto_programt &goto_program) +{ + typedef std::list entry_listt; + + // get the globals + entry_listt globals; + get_globals(globals); + + // get the locals + goto_programt::decl_identifierst locals; + goto_program.get_decl_identifiers(locals); + + // cache the list for the locals to speed things up + typedef hash_map_cont entry_cachet; + entry_cachet entry_cache; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + value_sett &v=(*this)[i_it].value_set; + + v.add_vars(globals); + + for(goto_programt::decl_identifierst::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + // cache hit? + entry_cachet::const_iterator e_it=entry_cache.find(*l_it); + + if(e_it==entry_cache.end()) + { + const symbolt &symbol=ns.lookup(*l_it); + + std::list &entries=entry_cache[*l_it]; + get_entries(symbol, entries); + v.add_vars(entries); + } + else + v.add_vars(e_it->second); + } + } +} + +/*******************************************************************\ + +Function: value_set_analysist::get_entries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysist::get_entries( + const symbolt &symbol, + std::list &dest) +{ + get_entries_rec(symbol.name, "", symbol.type, dest); +} + +/*******************************************************************\ + +Function: value_set_analysist::get_entries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysist::get_entries_rec( + const irep_idt &identifier, + const std::string &suffix, + const typet &type, + std::list &dest) +{ + const typet &t=ns.follow(type); + + if(t.id()==ID_struct || + t.id()==ID_union) + { + const struct_typet &struct_type=to_struct_type(t); + + const struct_typet::componentst &c=struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=c.begin(); + it!=c.end(); + it++) + { + get_entries_rec( + identifier, + suffix+"."+it->get_string(ID_name), + it->type(), + dest); + } + } + else if(t.id()==ID_array) + { + get_entries_rec(identifier, suffix+"[]", t.subtype(), dest); + } + else if(check_type(t)) + { + dest.push_back(value_sett::entryt(identifier, suffix)); + } +} + +/*******************************************************************\ + +Function: value_set_analysist::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysist::add_vars( + const goto_functionst &goto_functions) +{ + // get the globals + std::list globals; + get_globals(globals); + + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + { + // get the locals + std::set locals; + get_local_identifiers(f_it->second, locals); + + forall_goto_program_instructions(i_it, f_it->second.body) + { + value_sett &v=(*this)[i_it].value_set; + + v.add_vars(globals); + + for(std::set::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + const symbolt &symbol=ns.lookup(*l_it); + + std::list entries; + get_entries(symbol, entries); + v.add_vars(entries); + } + } + } +} + +/*******************************************************************\ + +Function: value_set_analysist::get_globals + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysist::get_globals( + std::list &dest) +{ + // static ones + forall_symbols(it, ns.get_context().symbols) + if(it->second.lvalue && + it->second.static_lifetime) + get_entries(it->second, dest); +} + +/*******************************************************************\ + +Function: value_set_analysist::check_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_analysist::check_type(const typet &type) +{ + if(type.id()==ID_pointer) + return true; + else if(type.id()==ID_struct || + type.id()==ID_union) + { + const struct_typet &struct_type=to_struct_type(type); + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(check_type(it->type())) return true; + } + } + else if(type.id()==ID_array) + return check_type(type.subtype()); + else if(type.id()==ID_symbol) + return check_type(ns.follow(type)); + + return false; +} + +/*******************************************************************\ + +Function: value_set_analysist::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysist::convert( + const goto_programt &goto_program, + const irep_idt &identifier, + xmlt &dest) const +{ + ::locationt previous_location; + + forall_goto_program_instructions(i_it, goto_program) + { + const ::locationt &location=i_it->location; + + if(location==previous_location) continue; + + if(location.is_nil() || location.get_file()==irep_idt()) + continue; + + // find value set + const value_sett &value_set=(*this)[i_it].value_set; + + xmlt &i=dest.new_element("instruction"); + xmlt &xml_location=i.new_element("location"); + ::convert(location, xml_location); + xml_location.name="location"; + + for(value_sett::valuest::const_iterator + v_it=value_set.values.begin(); + v_it!=value_set.values.end(); + v_it++) + { + xmlt &var=i.new_element("variable"); + var.new_element("identifier").data= + id2string(v_it->first); + + #if 0 + const value_sett::expr_sett &expr_set= + v_it->second.expr_set(); + + for(value_sett::expr_sett::const_iterator + e_it=expr_set.begin(); + e_it!=expr_set.end(); + e_it++) + { + std::string value_str= + from_expr(ns, identifier, *e_it); + + var.new_element("value").data= + xmlt::escape(value_str); + } + #endif + } + } +} +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert( + const goto_functionst &goto_functions, + const value_set_analysist &value_set_analysis, + xmlt &dest) +{ + dest=xmlt("value_set_analysis"); + + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + { + xmlt &f=dest.new_element("function"); + f.new_element("identifier").data=id2string(f_it->first); + value_set_analysis.convert(f_it->second.body, f_it->first, f); + } +} + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert( + const goto_programt &goto_program, + const value_set_analysist &value_set_analysis, + xmlt &dest) +{ + dest=xmlt("value_set_analysis"); + + value_set_analysis.convert( + goto_program, + "", + dest.new_element("program")); +} + diff --git a/src/pointer-analysis/value_set_analysis.h b/src/pointer-analysis/value_set_analysis.h new file mode 100644 index 00000000000..be81e16eb25 --- /dev/null +++ b/src/pointer-analysis/value_set_analysis.h @@ -0,0 +1,77 @@ +/*******************************************************************\ + +Module: Value Set Propagation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_VALUE_PROPAGATION_H +#define CPROVER_POINTER_ANALYSIS_VALUE_PROPAGATION_H + +#include + +#include + +#include "value_set_domain.h" +#include "value_sets.h" + +class value_set_analysist: + public value_setst, + public static_analysist +{ +public: + value_set_analysist(const namespacet &_ns): + static_analysist(_ns) + { + } + + typedef static_analysist baset; + + // overloading + virtual void initialize(const goto_programt &goto_program); + virtual void initialize(const goto_functionst &goto_functions); + + friend void convert( + const goto_functionst &goto_functions, + const value_set_analysist &value_set_analysis, + xmlt &dest); + + friend void convert( + const goto_programt &goto_program, + const value_set_analysist &value_set_analysis, + xmlt &dest); + + void convert( + const goto_programt &goto_program, + const irep_idt &identifier, + xmlt &dest) const; + +protected: + bool check_type(const typet &type); + void get_globals(std::list &dest); + void add_vars(const goto_functionst &goto_functions); + void add_vars(const goto_programt &goto_programa); + + void get_entries( + const symbolt &symbol, + std::list &dest); + + void get_entries_rec( + const irep_idt &identifier, + const std::string &suffix, + const typet &type, + std::list &dest); + +public: + // interface value_sets + virtual void get_values( + locationt l, + const exprt &expr, + value_setst::valuest &dest) + { + (*this)[l].value_set.get_value_set(expr, dest, ns); + } +}; + +#endif diff --git a/src/pointer-analysis/value_set_analysis_fi.cpp b/src/pointer-analysis/value_set_analysis_fi.cpp new file mode 100644 index 00000000000..b04c9e9b048 --- /dev/null +++ b/src/pointer-analysis/value_set_analysis_fi.cpp @@ -0,0 +1,314 @@ +/*******************************************************************\ + +Module: Value Set Propagation (Flow Insensitive) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +//#include + +#include +#include +#include + +#include + +#include "value_set_analysis_fi.h" + +/*******************************************************************\ + +Function: value_set_analysis_fit::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fit::initialize( + const goto_programt &goto_program) +{ + baset::initialize(goto_program); + add_vars(goto_program); +} + +/*******************************************************************\ + +Function: value_set_analysis_fit::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fit::initialize( + const goto_functionst &goto_functions) +{ + baset::initialize(goto_functions); + add_vars(goto_functions); +} + +/*******************************************************************\ + +Function: value_set_analysis_fit::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fit::add_vars( + const goto_programt &goto_program) +{ + typedef std::list entry_listt; + + // get the globals + entry_listt globals; + get_globals(globals); + + // get the locals + goto_programt::decl_identifierst locals; + goto_program.get_decl_identifiers(locals); + + // cache the list for the locals to speed things up + typedef hash_map_cont entry_cachet; + entry_cachet entry_cache; + + value_set_fit &v=state.value_set; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + v.add_vars(globals); + + for(goto_programt::decl_identifierst::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + // cache hit? + entry_cachet::const_iterator e_it=entry_cache.find(*l_it); + + if(e_it==entry_cache.end()) + { + const symbolt &symbol=ns.lookup(*l_it); + + std::list &entries=entry_cache[*l_it]; + get_entries(symbol, entries); + v.add_vars(entries); + } + else + v.add_vars(e_it->second); + } + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fit::get_entries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fit::get_entries( + const symbolt &symbol, + std::list &dest) +{ + get_entries_rec(symbol.name, "", symbol.type, dest); +} + +/*******************************************************************\ + +Function: value_set_analysis_fit::get_entries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fit::get_entries_rec( + const irep_idt &identifier, + const std::string &suffix, + const typet &type, + std::list &dest) +{ + const typet &t=ns.follow(type); + + if(t.id()=="struct" || + t.id()=="union") + { + const struct_typet &struct_type=to_struct_type(t); + + const struct_typet::componentst &c=struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=c.begin(); + it!=c.end(); + it++) + { + get_entries_rec( + identifier, + suffix+"."+it->get_string("name"), + it->type(), + dest); + } + } + else if(t.id()=="array") + { + get_entries_rec(identifier, suffix+"[]", t.subtype(), dest); + } + else if(check_type(t)) + { + dest.push_back(value_set_fit::entryt(identifier, suffix)); + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fit::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fit::add_vars( + const goto_functionst &goto_functions) +{ + // get the globals + std::list globals; + get_globals(globals); + + value_set_fit &v=state.value_set; + + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + { + // get the locals + std::set locals; + get_local_identifiers(f_it->second, locals); + + forall_goto_program_instructions(i_it, f_it->second.body) + { + v.add_vars(globals); + + for(std::set::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + const symbolt &symbol=ns.lookup(*l_it); + + std::list entries; + get_entries(symbol, entries); + v.add_vars(entries); + } + } + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fit::get_globals + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fit::get_globals( + std::list &dest) +{ + // static ones + forall_symbols(it, ns.get_context().symbols) + if(it->second.lvalue && + it->second.static_lifetime) + get_entries(it->second, dest); +} + +/*******************************************************************\ + +Function: value_set_analysis_fit::check_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_analysis_fit::check_type(const typet &type) +{ + if(type.id()=="pointer") + { + switch(track_options) { + case TRACK_ALL_POINTERS: + { return true; break; } + case TRACK_FUNCTION_POINTERS: + { + if(type.id()=="pointer") + { + const typet *t = &type; + while (t->id()=="pointer") t = &(t->subtype()); + + return (t->id()=="code"); + } + + break; + } + default: // don't track. + break; + } + } + else if(type.id()=="struct" || + type.id()=="union") + { + const struct_typet &struct_type=to_struct_type(type); + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(check_type(it->type())) return true; + } + } + else if(type.id()=="array") + return check_type(type.subtype()); + else if(type.id()=="symbol") + return check_type(ns.follow(type)); + + return false; +} diff --git a/src/pointer-analysis/value_set_analysis_fi.h b/src/pointer-analysis/value_set_analysis_fi.h new file mode 100644 index 00000000000..eff5861cc5c --- /dev/null +++ b/src/pointer-analysis/value_set_analysis_fi.h @@ -0,0 +1,73 @@ +/*******************************************************************\ + +Module: Value Set Propagation (flow insensitive) + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_VALUE_PROPAGATION_FI_H +#define CPROVER_POINTER_ANALYSIS_VALUE_PROPAGATION_FI_H + +#include + +#include "value_set_domain_fi.h" +#include "value_sets.h" + +class value_set_analysis_fit : + public value_setst, + public flow_insensitive_analysist +{ +public: + enum track_optionst { TRACK_ALL_POINTERS, TRACK_FUNCTION_POINTERS }; + + // constructor + value_set_analysis_fit( + const namespacet &_ns, + track_optionst _track_options=TRACK_ALL_POINTERS): + flow_insensitive_analysist(_ns), + track_options(_track_options) + { + } + + typedef flow_insensitive_analysist baset; + + virtual void initialize(const goto_programt &goto_program); + virtual void initialize(const goto_functionst &goto_functions); + +protected: + track_optionst track_options; + + bool check_type(const typet &type); + void get_globals(std::list &dest); + void add_vars(const goto_functionst &goto_functions); + void add_vars(const goto_programt &goto_programa); + + void get_entries( + const symbolt &symbol, + std::list &dest); + + void get_entries_rec( + const irep_idt &identifier, + const std::string &suffix, + const typet &type, + std::list &dest); + +public: + // interface value_sets + virtual void get_values( + locationt l, + const exprt &expr, + std::list &dest) + { + state.value_set.from_function = + state.value_set.function_numbering.number(l->function); + state.value_set.to_function = + state.value_set.function_numbering.number(l->function); + state.value_set.from_target_index = l->location_number; + state.value_set.to_target_index = l->location_number; + state.value_set.get_value_set(expr, dest, ns); + } +}; + +#endif /*VALUE_PROPAGATION_FUI_H_*/ diff --git a/src/pointer-analysis/value_set_analysis_fivr.cpp b/src/pointer-analysis/value_set_analysis_fivr.cpp new file mode 100644 index 00000000000..7180cb4d2e9 --- /dev/null +++ b/src/pointer-analysis/value_set_analysis_fivr.cpp @@ -0,0 +1,314 @@ +/*******************************************************************\ + +Module: Value Set Propagation (Flow Insensitive) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +//#include + +#include +#include +#include + +#include + +#include "value_set_analysis_fivr.h" + +/*******************************************************************\ + +Function: value_set_analysis_fivrt::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrt::initialize( + const goto_programt &goto_program) +{ + baset::initialize(goto_program); + add_vars(goto_program); +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrt::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrt::initialize( + const goto_functionst &goto_functions) +{ + baset::initialize(goto_functions); + add_vars(goto_functions); +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrt::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrt::add_vars( + const goto_programt &goto_program) +{ + typedef std::list entry_listt; + + // get the globals + entry_listt globals; + get_globals(globals); + + // get the locals + goto_programt::decl_identifierst locals; + goto_program.get_decl_identifiers(locals); + + // cache the list for the locals to speed things up + typedef hash_map_cont entry_cachet; + entry_cachet entry_cache; + + value_set_fivrt &v=state.value_set; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + v.add_vars(globals); + + for(goto_programt::decl_identifierst::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + // cache hit? + entry_cachet::const_iterator e_it=entry_cache.find(*l_it); + + if(e_it==entry_cache.end()) + { + const symbolt &symbol=ns.lookup(*l_it); + + std::list &entries=entry_cache[*l_it]; + get_entries(symbol, entries); + v.add_vars(entries); + } + else + v.add_vars(e_it->second); + } + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrt::get_entries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrt::get_entries( + const symbolt &symbol, + std::list &dest) +{ + get_entries_rec(symbol.name, "", symbol.type, dest); +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrt::get_entries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrt::get_entries_rec( + const irep_idt &identifier, + const std::string &suffix, + const typet &type, + std::list &dest) +{ + const typet &t=ns.follow(type); + + if(t.id()=="struct" || + t.id()=="union") + { + const struct_typet &struct_type=to_struct_type(t); + + const struct_typet::componentst &c=struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=c.begin(); + it!=c.end(); + it++) + { + get_entries_rec( + identifier, + suffix+"."+it->get_string("name"), + it->type(), + dest); + } + } + else if(t.id()=="array") + { + get_entries_rec(identifier, suffix+"[]", t.subtype(), dest); + } + else if(check_type(t)) + { + dest.push_back(value_set_fivrt::entryt(identifier, suffix)); + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrt::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrt::add_vars( + const goto_functionst &goto_functions) +{ + // get the globals + std::list globals; + get_globals(globals); + + value_set_fivrt &v=state.value_set; + + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + { + // get the locals + std::set locals; + get_local_identifiers(f_it->second, locals); + + forall_goto_program_instructions(i_it, f_it->second.body) + { + v.add_vars(globals); + + for(std::set::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + const symbolt &symbol=ns.lookup(*l_it); + + std::list entries; + get_entries(symbol, entries); + v.add_vars(entries); + } + } + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrt::get_globals + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrt::get_globals( + std::list &dest) +{ + // static ones + forall_symbols(it, ns.get_context().symbols) + if(it->second.lvalue && + it->second.static_lifetime) + get_entries(it->second, dest); +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrt::check_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_analysis_fivrt::check_type(const typet &type) +{ + if(type.id()=="pointer") + { + switch(track_options) { + case TRACK_ALL_POINTERS: + { return true; break; } + case TRACK_FUNCTION_POINTERS: + { + if(type.id()=="pointer") + { + const typet *t = &type; + while (t->id()=="pointer") t = &(t->subtype()); + + return (t->id()=="code"); + } + + break; + } + default: // don't track. + break; + } + } + else if(type.id()=="struct" || + type.id()=="union") + { + const struct_typet &struct_type=to_struct_type(type); + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(check_type(it->type())) return true; + } + } + else if(type.id()=="array") + return check_type(type.subtype()); + else if(type.id()=="symbol") + return check_type(ns.follow(type)); + + return false; +} diff --git a/src/pointer-analysis/value_set_analysis_fivr.h b/src/pointer-analysis/value_set_analysis_fivr.h new file mode 100644 index 00000000000..51e139e8cdd --- /dev/null +++ b/src/pointer-analysis/value_set_analysis_fivr.h @@ -0,0 +1,92 @@ +/*******************************************************************\ + +Module: Value Set Propagation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_VALUE_PROPAGATION_FIVR_H +#define CPROVER_POINTER_ANALYSIS_VALUE_PROPAGATION_FIVR_H + +#include + +#include "value_set_domain_fivr.h" +#include "value_sets.h" + +class value_set_analysis_fivrt : + public value_setst, + public flow_insensitive_analysist +{ +public: + enum track_optionst { TRACK_ALL_POINTERS, TRACK_FUNCTION_POINTERS }; + + // constructor + value_set_analysis_fivrt( + const namespacet &_ns, + track_optionst _track_options=TRACK_ALL_POINTERS): + flow_insensitive_analysist(_ns), + track_options(_track_options) + { + } + + typedef flow_insensitive_analysist baset; + + virtual void initialize(const goto_programt &goto_program); + virtual void initialize(const goto_functionst &goto_functions); + + void output(locationt l, std::ostream &out) + { + state.value_set.set_from(l->function, l->location_number); + state.value_set.set_to(l->function, l->location_number); + state.output(ns, out); + } + + void output(const goto_programt &goto_program, std::ostream &out) + { + forall_goto_program_instructions(it, goto_program) + { + std::cout << "**** " << it->location << std::endl; + output(it, out); + std::cout << std::endl; + goto_program.output_instruction(ns, "", std::cout, it); + std::cout << std::endl; + } + } + +protected: + track_optionst track_options; + + bool check_type(const typet &type); + void get_globals(std::list &dest); + void add_vars(const goto_functionst &goto_functions); + void add_vars(const goto_programt &goto_programa); + + void get_entries( + const symbolt &symbol, + std::list &dest); + + void get_entries_rec( + const irep_idt &identifier, + const std::string &suffix, + const typet &type, + std::list &dest); + +public: + // interface value_sets + virtual void get_values( + locationt l, + const exprt &expr, + std::list &dest) + { + state.value_set.from_function = + state.value_set.function_numbering.number(l->function); + state.value_set.to_function = + state.value_set.function_numbering.number(l->function); + state.value_set.from_target_index = l->location_number; + state.value_set.to_target_index = l->location_number; + state.value_set.get_value_set(expr, dest, ns); + } +}; + +#endif /*VALUE_PROPAGATION_INCR_H_*/ diff --git a/src/pointer-analysis/value_set_analysis_fivrns.cpp b/src/pointer-analysis/value_set_analysis_fivrns.cpp new file mode 100644 index 00000000000..2d8bb9ddfd4 --- /dev/null +++ b/src/pointer-analysis/value_set_analysis_fivrns.cpp @@ -0,0 +1,314 @@ +/*******************************************************************\ + +Module: Value Set Propagation (Flow Insensitive, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +//#include + +#include +#include +#include + +#include + +#include "value_set_analysis_fivrns.h" + +/*******************************************************************\ + +Function: value_set_analysis_fivrnst::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrnst::initialize( + const goto_programt &goto_program) +{ + baset::initialize(goto_program); + add_vars(goto_program); +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrnst::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrnst::initialize( + const goto_functionst &goto_functions) +{ + baset::initialize(goto_functions); + add_vars(goto_functions); +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrnst::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrnst::add_vars( + const goto_programt &goto_program) +{ + typedef std::list entry_listt; + + // get the globals + entry_listt globals; + get_globals(globals); + + // get the locals + goto_programt::decl_identifierst locals; + goto_program.get_decl_identifiers(locals); + + // cache the list for the locals to speed things up + typedef hash_map_cont entry_cachet; + entry_cachet entry_cache; + + value_set_fivrnst &v=state.value_set; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + v.add_vars(globals); + + for(goto_programt::decl_identifierst::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + // cache hit? + entry_cachet::const_iterator e_it=entry_cache.find(*l_it); + + if(e_it==entry_cache.end()) + { + const symbolt &symbol=ns.lookup(*l_it); + + std::list &entries=entry_cache[*l_it]; + get_entries(symbol, entries); + v.add_vars(entries); + } + else + v.add_vars(e_it->second); + } + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrnst::get_entries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrnst::get_entries( + const symbolt &symbol, + std::list &dest) +{ + get_entries_rec(symbol.name, "", symbol.type, dest); +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrnst::get_entries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrnst::get_entries_rec( + const irep_idt &identifier, + const std::string &suffix, + const typet &type, + std::list &dest) +{ + const typet &t=ns.follow(type); + + if(t.id()=="struct" || + t.id()=="union") + { + const struct_typet &struct_type=to_struct_type(t); + + const struct_typet::componentst &c=struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=c.begin(); + it!=c.end(); + it++) + { + get_entries_rec( + identifier, + suffix+"."+it->get_string("name"), + it->type(), + dest); + } + } + else if(t.id()=="array") + { + get_entries_rec(identifier, suffix+"[]", t.subtype(), dest); + } + else if(check_type(t)) + { + dest.push_back(value_set_fivrnst::entryt(identifier, suffix)); + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrnst::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrnst::add_vars( + const goto_functionst &goto_functions) +{ + // get the globals + std::list globals; + get_globals(globals); + + value_set_fivrnst &v=state.value_set; + + for(goto_functionst::function_mapt::const_iterator + f_it=goto_functions.function_map.begin(); + f_it!=goto_functions.function_map.end(); + f_it++) + { + // get the locals + std::set locals; + get_local_identifiers(f_it->second, locals); + + forall_goto_program_instructions(i_it, f_it->second.body) + { + v.add_vars(globals); + + for(std::set::const_iterator + l_it=locals.begin(); + l_it!=locals.end(); + l_it++) + { + const symbolt &symbol=ns.lookup(*l_it); + + std::list entries; + get_entries(symbol, entries); + v.add_vars(entries); + } + } + } +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrnst::get_globals + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_analysis_fivrnst::get_globals( + std::list &dest) +{ + // static ones + forall_symbols(it, ns.get_context().symbols) + if(it->second.lvalue && + it->second.static_lifetime) + get_entries(it->second, dest); +} + +/*******************************************************************\ + +Function: value_set_analysis_fivrnst::check_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_analysis_fivrnst::check_type(const typet &type) +{ + if(type.id()=="pointer") + { + switch(track_options) { + case TRACK_ALL_POINTERS: + { return true; break; } + case TRACK_FUNCTION_POINTERS: + { + if(type.id()=="pointer") + { + const typet *t = &type; + while (t->id()=="pointer") t = &(t->subtype()); + + return (t->id()=="code"); + } + + break; + } + default: // don't track. + break; + } + } + else if(type.id()=="struct" || + type.id()=="union") + { + const struct_typet &struct_type=to_struct_type(type); + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(check_type(it->type())) return true; + } + } + else if(type.id()=="array") + return check_type(type.subtype()); + else if(type.id()=="symbol") + return check_type(ns.follow(type)); + + return false; +} diff --git a/src/pointer-analysis/value_set_analysis_fivrns.h b/src/pointer-analysis/value_set_analysis_fivrns.h new file mode 100644 index 00000000000..5427cb5f8eb --- /dev/null +++ b/src/pointer-analysis/value_set_analysis_fivrns.h @@ -0,0 +1,93 @@ +/*******************************************************************\ + +Module: Value Set Analysis (Flow Insensitive, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com, + CM Wintersteiger + +\*******************************************************************/ + +#ifndef __CPROVER_VALUE_SET_ANALYSIS_FIVRNS_H_ +#define __CPROVER_VALUE_SET_ANALYSIS_FIVRNS_H_ + +#include + +#include "value_set_domain_fivrns.h" +#include "value_sets.h" + +class value_set_analysis_fivrnst : + public value_setst, + public flow_insensitive_analysist +{ +public: + enum track_optionst { TRACK_ALL_POINTERS, TRACK_FUNCTION_POINTERS }; + + // constructor + value_set_analysis_fivrnst( + const namespacet &_ns, + track_optionst _track_options=TRACK_ALL_POINTERS): + flow_insensitive_analysist(_ns), + track_options(_track_options) + { + } + + typedef flow_insensitive_analysist baset; + + virtual void initialize(const goto_programt &goto_program); + virtual void initialize(const goto_functionst &goto_functions); + + void output(locationt l, std::ostream &out) + { + state.value_set.set_from(l->function, l->location_number); + state.value_set.set_to(l->function, l->location_number); + state.output(ns, out); + } + + void output(const goto_programt &goto_program, std::ostream &out) + { + forall_goto_program_instructions(it, goto_program) + { + std::cout << "**** " << it->location << std::endl; + output(it, out); + std::cout << std::endl; + goto_program.output_instruction(ns, "", std::cout, it); + std::cout << std::endl; + } + } + +protected: + track_optionst track_options; + + bool check_type(const typet &type); + void get_globals(std::list &dest); + void add_vars(const goto_functionst &goto_functions); + void add_vars(const goto_programt &goto_programa); + + void get_entries( + const symbolt &symbol, + std::list &dest); + + void get_entries_rec( + const irep_idt &identifier, + const std::string &suffix, + const typet &type, + std::list &dest); + +public: + // interface value_sets + virtual void get_values( + locationt l, + const exprt &expr, + std::list &dest) + { + state.value_set.from_function = + state.value_set.function_numbering.number(l->function); + state.value_set.to_function = + state.value_set.function_numbering.number(l->function); + state.value_set.from_target_index = l->location_number; + state.value_set.to_target_index = l->location_number; + state.value_set.get_value_set(expr, dest, ns); + } +}; + +#endif /*__CPROVER_VALUE_SET_ANALYSIS_FIVRNS_H_*/ diff --git a/src/pointer-analysis/value_set_domain.cpp b/src/pointer-analysis/value_set_domain.cpp new file mode 100644 index 00000000000..006118ce6e0 --- /dev/null +++ b/src/pointer-analysis/value_set_domain.cpp @@ -0,0 +1,63 @@ +/*******************************************************************\ + +Module: Value Set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "value_set_domain.h" + +/*******************************************************************\ + +Function: value_set_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_domaint::transform( + const namespacet &ns, + locationt from_l, + locationt to_l) +{ + switch(from_l->type) + { + case GOTO: + // ignore for now + break; + + case END_FUNCTION: + value_set.do_end_function(static_analysis_baset::get_return_lhs(to_l), ns); + break; + + case RETURN: + case OTHER: + case ASSIGN: + case DECL: + value_set.apply_code(from_l->code, ns); + break; + + case ASSUME: + value_set.guard(from_l->guard, ns); + break; + + case FUNCTION_CALL: + { + const code_function_callt &code= + to_code_function_call(from_l->code); + + value_set.do_function_call(to_l->function, code.arguments(), ns); + } + break; + + default:; + // do nothing + } +} diff --git a/src/pointer-analysis/value_set_domain.h b/src/pointer-analysis/value_set_domain.h new file mode 100644 index 00000000000..e1aa654ecb9 --- /dev/null +++ b/src/pointer-analysis/value_set_domain.h @@ -0,0 +1,58 @@ +/*******************************************************************\ + +Module: Value Set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_VALUE_SET_DOMAIN_H +#define CPROVER_POINTER_ANALYSIS_VALUE_SET_DOMAIN_H + +#include + +#include "value_set.h" + +class value_set_domaint:public domain_baset +{ +public: + value_sett value_set; + + // overloading + + virtual bool merge(const value_set_domaint &other) + { + return value_set.make_union(other.value_set); + } + + virtual void output( + const namespacet &ns, + std::ostream &out) const + { + value_set.output(ns, out); + } + + virtual void initialize( + const namespacet &ns, + locationt l) + { + value_set.clear(); + value_set.location_number=l->location_number; + } + + virtual void transform( + const namespacet &ns, + locationt from_l, + locationt to_l); + + virtual void get_reference_set( + const namespacet &ns, + const exprt &expr, + value_setst::valuest &dest) + { + value_set.get_reference_set(expr, dest, ns); + } + +}; + +#endif diff --git a/src/pointer-analysis/value_set_domain_fi.cpp b/src/pointer-analysis/value_set_domain_fi.cpp new file mode 100644 index 00000000000..0b2136ae130 --- /dev/null +++ b/src/pointer-analysis/value_set_domain_fi.cpp @@ -0,0 +1,70 @@ +/*******************************************************************\ + +Module: Value Set Domain (Flow Insensitive) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#include + +#include "value_set_domain_fi.h" + +/*******************************************************************\ + +Function: value_set_domain_fit::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_domain_fit::transform( + const namespacet &ns, + locationt from_l, + locationt to_l) +{ + value_set.changed = false; + + value_set.set_from(from_l->function, from_l->location_number); + value_set.set_to(to_l->function, to_l->location_number); + +// std::cout << "transforming: " << +// from_l->function << " " << from_l->location_number << " to " << +// to_l->function << " " << to_l->location_number << std::endl; + + switch(from_l->type) + { + case GOTO: + // ignore for now + break; + + case END_FUNCTION: + value_set.do_end_function(get_return_lhs(to_l), ns); + break; + + case RETURN: + case OTHER: + case ASSIGN: + value_set.apply_code(from_l->code, ns); + break; + + case FUNCTION_CALL: + { + const code_function_callt &code= + to_code_function_call(from_l->code); + + value_set.do_function_call(to_l->function, code.arguments(), ns); + } + break; + + default:; + // do nothing + } + + return (value_set.changed); +} diff --git a/src/pointer-analysis/value_set_domain_fi.h b/src/pointer-analysis/value_set_domain_fi.h new file mode 100644 index 00000000000..bd18f3ba208 --- /dev/null +++ b/src/pointer-analysis/value_set_domain_fi.h @@ -0,0 +1,62 @@ +/*******************************************************************\ + +Module: Value Set (Flow Insensitive) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#ifndef VALUE_SET_DOMAIN_FI_H_ +#define VALUE_SET_DOMAIN_FI_H_ + +#include + +#include "value_set_fi.h" + +class value_set_domain_fit:public flow_insensitive_abstract_domain_baset +{ +public: + value_set_fit value_set; + + // overloading + +// virtual bool merge(const value_set_domain_fit &other) +// { +// return value_set.make_union(other.value_set); +// } + + virtual void output( + const namespacet &ns, + std::ostream &out) const + { + value_set.output(ns, out); + } + + virtual void initialize( + const namespacet &ns) + { + value_set.clear(); + } + + virtual bool transform( + const namespacet &ns, + locationt from_l, + locationt to_l); + + virtual void get_reference_set( + const namespacet &ns, + const exprt &expr, + expr_sett &expr_set) + { + value_set.get_reference_set(expr, expr_set, ns); + } + + virtual void clear( void ) + { + value_set.clear(); + } + +}; + +#endif /*VALUE_SET_DOMAIN_FI_H_*/ diff --git a/src/pointer-analysis/value_set_domain_fivr.cpp b/src/pointer-analysis/value_set_domain_fivr.cpp new file mode 100644 index 00000000000..b162ef2dc36 --- /dev/null +++ b/src/pointer-analysis/value_set_domain_fivr.cpp @@ -0,0 +1,65 @@ +/*******************************************************************\ + +Module: Value Set Domain (Flow Insensitive, Sharing, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#include + +#include "value_set_domain_fivr.h" + +/*******************************************************************\ + +Function: value_set_domain_fivrt::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_domain_fivrt::transform( + const namespacet &ns, + locationt from_l, + locationt to_l) +{ + value_set.set_from(from_l->function, from_l->location_number); + value_set.set_to(to_l->function, to_l->location_number); + + #if 0 + std::cout << "Transforming: " << + from_l->function << " " << from_l->location_number << " to " << + to_l->function << " " << to_l->location_number << std::endl; + #endif + + switch(from_l->type) + { + case END_FUNCTION: + value_set.do_end_function(get_return_lhs(to_l), ns); + break; + + case RETURN: + case OTHER: + case ASSIGN: + value_set.apply_code(from_l->code, ns); + break; + + case FUNCTION_CALL: + { + const code_function_callt &code= + to_code_function_call(from_l->code); + + value_set.do_function_call(to_l->function, code.arguments(), ns); + break; + } + + default:; + } + + return value_set.handover(); +} diff --git a/src/pointer-analysis/value_set_domain_fivr.h b/src/pointer-analysis/value_set_domain_fivr.h new file mode 100644 index 00000000000..e821e81708d --- /dev/null +++ b/src/pointer-analysis/value_set_domain_fivr.h @@ -0,0 +1,57 @@ +/*******************************************************************\ + +Module: Value Set (Flow Insensitive, Sharing, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#ifndef VALUE_SET_DOMAIN_INCR_H_ +#define VALUE_SET_DOMAIN_INCR_H_ + +#include + +#include "value_set_fivr.h" + +class value_set_domain_fivrt:public flow_insensitive_abstract_domain_baset +{ +public: + value_set_fivrt value_set; + + // overloading + + virtual void output( + const namespacet &ns, + std::ostream &out) const + { + value_set.output(ns, out); + } + + virtual void initialize( + const namespacet &ns) + { + value_set.clear(); + } + + virtual bool transform( + const namespacet &ns, + locationt from_l, + locationt to_l); + + virtual void get_reference_set( + const namespacet &ns, + const exprt &expr, + expr_sett &expr_set) + { + value_set.get_reference_set(expr, expr_set, ns); + } + + virtual void clear( void ) + { + value_set.clear(); + } + +}; + +#endif /*VALUE_SET_DOMAIN_INCR_H_*/ diff --git a/src/pointer-analysis/value_set_domain_fivrns.cpp b/src/pointer-analysis/value_set_domain_fivrns.cpp new file mode 100644 index 00000000000..35883402891 --- /dev/null +++ b/src/pointer-analysis/value_set_domain_fivrns.cpp @@ -0,0 +1,65 @@ +/*******************************************************************\ + +Module: Value Set Domain (Flow Insensitive, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#include + +#include "value_set_domain_fivrns.h" + +/*******************************************************************\ + +Function: value_set_domain_fivrnst::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_domain_fivrnst::transform( + const namespacet &ns, + locationt from_l, + locationt to_l) +{ + value_set.set_from(from_l->function, from_l->location_number); + value_set.set_to(to_l->function, to_l->location_number); + + #if 0 + std::cout << "Transforming: " << + from_l->function << " " << from_l->location_number << " to " << + to_l->function << " " << to_l->location_number << std::endl; + #endif + + switch(from_l->type) + { + case END_FUNCTION: + value_set.do_end_function(get_return_lhs(to_l), ns); + break; + + case RETURN: + case OTHER: + case ASSIGN: + value_set.apply_code(from_l->code, ns); + break; + + case FUNCTION_CALL: + { + const code_function_callt &code= + to_code_function_call(from_l->code); + + value_set.do_function_call(to_l->function, code.arguments(), ns); + break; + } + + default:; + } + + return value_set.handover(); +} diff --git a/src/pointer-analysis/value_set_domain_fivrns.h b/src/pointer-analysis/value_set_domain_fivrns.h new file mode 100644 index 00000000000..95d257caa46 --- /dev/null +++ b/src/pointer-analysis/value_set_domain_fivrns.h @@ -0,0 +1,58 @@ +/*******************************************************************\ + +Module: Value Set Domain (Flow Insensitive, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#ifndef __CPROVER_VALUE_SET_DOMAIN_FIVRNS_H_ +#define __CPROVER_VALUE_SET_DOMAIN_FIVRNS_H_ + +#include + +#include "value_set_fivrns.h" + +class value_set_domain_fivrnst : + public flow_insensitive_abstract_domain_baset +{ +public: + value_set_fivrnst value_set; + + // overloading + + virtual void output( + const namespacet &ns, + std::ostream &out) const + { + value_set.output(ns, out); + } + + virtual void initialize( + const namespacet &ns) + { + value_set.clear(); + } + + virtual bool transform( + const namespacet &ns, + locationt from_l, + locationt to_l); + + virtual void get_reference_set( + const namespacet &ns, + const exprt &expr, + expr_sett &expr_set) + { + value_set.get_reference_set(expr, expr_set, ns); + } + + virtual void clear( void ) + { + value_set.clear(); + } + +}; + +#endif /*__CPROVER_VALUE_SET_DOMAIN_FIVRNS_H_*/ diff --git a/src/pointer-analysis/value_set_fi.cpp b/src/pointer-analysis/value_set_fi.cpp new file mode 100644 index 00000000000..a1152a7408a --- /dev/null +++ b/src/pointer-analysis/value_set_fi.cpp @@ -0,0 +1,1711 @@ +/*******************************************************************\ + +Module: Value Set (Flow Insensitive, Sharing) + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "value_set_fi.h" + +const value_set_fit::object_map_dt value_set_fit::object_map_dt::empty; +object_numberingt value_set_fit::object_numbering; +hash_numbering value_set_fit::function_numbering; + +static std::string alloc_adapter_prefix = "alloc_adaptor::"; + +#define forall_objects(it, map) \ + for(object_map_dt::const_iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) + +#define Forall_objects(it, map) \ + for(object_map_dt::iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) + +/*******************************************************************\ + +Function: value_set_fit::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::output( + const namespacet &ns, + std::ostream &out) const +{ + for(valuest::const_iterator + v_it=values.begin(); + v_it!=values.end(); + v_it++) + { + irep_idt identifier, display_name; + + const entryt &e=v_it->second; + + if(has_prefix(id2string(e.identifier), "value_set::dynamic_object")) + { + display_name=id2string(e.identifier)+e.suffix; + identifier=""; + } + else + { + #if 0 + const symbolt &symbol=ns.lookup(id2string(e.identifier)); + display_name=symbol.display_name()+e.suffix; + identifier=symbol.name; + #else + identifier=id2string(e.identifier); + display_name=id2string(identifier)+e.suffix; + #endif + } + + out << display_name; + + out << " = { "; + + object_mapt object_map; + flatten(e, object_map); + + unsigned width=0; + + forall_objects(o_it, object_map.read()) + { + const exprt &o=object_numbering[o_it->first]; + + std::string result; + + if(o.id()==ID_invalid || o.id()==ID_unknown) + { + result="<"; + result+=from_expr(ns, identifier, o); + result+=", *, "; // offset unknown + if (o.type().id()=="unknown") + result+="*"; + else + result+=from_type(ns, identifier, o.type()); + result+=">"; + } + else + { + result="<"+from_expr(ns, identifier, o)+", "; + + if(o_it->second.offset_is_set) + result+=integer2string(o_it->second.offset)+""; + else + result+="*"; + + result+=", "; + + if (o.type().id()=="unknown") + result+="*"; + else + result+=from_type(ns, identifier, o.type()); + + result+=">"; + } + + out << result; + + width+=result.size(); + + object_map_dt::const_iterator next(o_it); + next++; + + if(next!=object_map.read().end()) + { + out << ", "; + if(width>=40) out << "\n "; + } + } + + out << " } " << std::endl; + } +} + +/*******************************************************************\ + +Function: value_set_fit::flatten + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::flatten( + const entryt &e, + object_mapt &dest) const +{ + #if 0 + std::cout << "FLATTEN: " << e.identifier << e.suffix << std::endl; + #endif + + flatten_seent seen; + flatten_rec(e, dest, seen); + + #if 0 + std::cout << "FLATTEN: Done." << std::endl; + #endif +} + +/*******************************************************************\ + +Function: value_set_fit::flatten_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::flatten_rec( + const entryt &e, + object_mapt &dest, + flatten_seent &seen) const +{ + #if 0 + std::cout << "FLATTEN_REC: " << e.identifier << e.suffix << std::endl; + #endif + + std::string identifier = e.identifier.as_string(); + assert(seen.find(identifier + e.suffix)==seen.end()); + + bool generalize_index = false; + + seen.insert(identifier + e.suffix); + + forall_objects(it, e.object_map.read()) + { + const exprt& o=object_numbering[it->first]; + + if (o.type().id()=="#REF#") + { + if (seen.find(o.get("identifier"))!=seen.end()) + { + generalize_index = true; + continue; + } + + valuest::const_iterator fi = values.find(o.get("identifier")); + if (fi==values.end()) + { + // this is some static object, keep it in. + exprt se("symbol", o.type().subtype()); + se.set("identifier", o.get("identifier")); + insert(dest, se, 0); + } + else + { + object_mapt temp; + flatten_rec(fi->second, temp, seen); + + for(object_map_dt::iterator t_it=temp.write().begin(); + t_it!=temp.write().end(); + t_it++) + { + if(t_it->second.offset_is_set && + it->second.offset_is_set) + { + t_it->second.offset += it->second.offset; + } + else + t_it->second.offset_is_set=false; + } + + forall_objects(oit, temp.read()) + insert(dest, oit); + } + + } + else + insert(dest, it); + } + + if (generalize_index) // this means we had recursive symbols in there + { + Forall_objects(it, dest.write()) + it->second.offset_is_set = false; + } + + seen.erase(identifier + e.suffix); +} + +/*******************************************************************\ + +Function: value_set_fit::to_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt value_set_fit::to_expr(object_map_dt::const_iterator it) const +{ + const exprt &object=object_numbering[it->first]; + + if(object.id()=="invalid" || + object.id()=="unknown") + return object; + + object_descriptor_exprt od; + + od.object()=object; + + if(it->second.offset_is_set) + od.offset()=from_integer(it->second.offset, index_type()); + + od.type()=od.object().type(); + + return od; +} + +/*******************************************************************\ + +Function: value_set_fit::make_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fit::make_union(const value_set_fit::valuest &new_values) +{ + assert(0); + bool result=false; + + for(valuest::const_iterator + it=new_values.begin(); + it!=new_values.end(); + it++) + { + valuest::iterator it2=values.find(it->first); + + if(it2==values.end()) + { + // we always track these + if(has_prefix(id2string(it->second.identifier), + "value_set::dynamic_object") || + has_prefix(id2string(it->second.identifier), + "value_set::return_value")) + { + values.insert(*it); + result=true; + } + + continue; + } + + entryt &e=it2->second; + const entryt &new_e=it->second; + + if(make_union(e.object_map, new_e.object_map)) + result=true; + } + + changed = result; + + return result; +} + +/*******************************************************************\ + +Function: value_set_fit::make_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fit::make_union(object_mapt &dest, const object_mapt &src) const +{ + bool result=false; + + forall_objects(it, src.read()) + { + if(insert(dest, it)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: value_set_fit::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::get_value_set( + const exprt &expr, + std::list &value_set, + const namespacet &ns) const +{ + object_mapt object_map; + get_value_set(expr, object_map, ns); + + object_mapt flat_map; + + forall_objects(it, object_map.read()) + { + const exprt &object=object_numbering[it->first]; + if (object.type().id()=="#REF#") + { + assert(object.id()=="symbol"); + + const irep_idt &ident = object.get("identifier"); + valuest::const_iterator v_it = values.find(ident); + + if (v_it!=values.end()) + { + object_mapt temp; + flatten(v_it->second, temp); + + for(object_map_dt::iterator t_it=temp.write().begin(); + t_it!=temp.write().end(); + t_it++) + { + if(t_it->second.offset_is_set && + it->second.offset_is_set) + { + t_it->second.offset += it->second.offset; + } + else + t_it->second.offset_is_set=false; + + flat_map.write()[t_it->first]=t_it->second; + } + } + } + else + flat_map.write()[it->first]=it->second; + } + + forall_objects(fit, flat_map.read()) + value_set.push_back(to_expr(fit)); + + #if 0 + // Sanity check! + for(std::list::const_iterator it=value_set.begin(); + it!=value_set.end(); + it++) + assert(it->type().id()!="#REF"); + #endif + + #if 0 + for(expr_sett::const_iterator it=value_set.begin(); it!=value_set.end(); it++) + std::cout << "GET_VALUE_SET: " << from_expr(ns, "", *it) << std::endl; + #endif +} + +/*******************************************************************\ + +Function: value_set_fit::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::get_value_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const +{ + exprt tmp(expr); + simplify(tmp, ns); + + gvs_recursion_sett recset; + get_value_set_rec(tmp, dest, "", tmp.type(), ns, recset); +} + +/*******************************************************************\ + +Function: value_set_fit::get_value_set_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::get_value_set_rec( + const exprt &expr, + object_mapt &dest, + const std::string &suffix, + const typet &original_type, + const namespacet &ns, + gvs_recursion_sett &recursion_set) const +{ + #if 0 + std::cout << "GET_VALUE_SET_REC EXPR: " << from_expr(ns, "", expr) << std::endl; + std::cout << "GET_VALUE_SET_REC SUFFIX: " << suffix << std::endl; + std::cout << std::endl; + #endif + + if(expr.type().id()=="#REF#") + { + valuest::const_iterator fi = values.find(expr.get("identifier")); + + if(fi!=values.end()) + { + forall_objects(it, fi->second.object_map.read()) + get_value_set_rec(object_numbering[it->first], dest, suffix, + original_type, ns, recursion_set); + return; + } + else + { + insert(dest, exprt("unknown", original_type)); + return; + } + } + else if(expr.id()=="unknown" || expr.id()=="invalid") + { + insert(dest, exprt("unknown", original_type)); + return; + } + else if(expr.id()=="index") + { + assert(expr.operands().size()==2); + + const typet &type=ns.follow(expr.op0().type()); + + assert(type.id()=="array" || + type.id()=="incomplete_array" || + type.id()=="#REF#"); + + get_value_set_rec(expr.op0(), dest, "[]"+suffix, + original_type, ns, recursion_set); + + return; + } + else if(expr.id()=="member") + { + assert(expr.operands().size()==1); + + if(expr.op0().is_not_nil()) + { + const typet &type=ns.follow(expr.op0().type()); + + assert(type.id()=="struct" || + type.id()=="union" || + type.id()=="incomplete_struct" || + type.id()=="incomplete_union"); + + const std::string &component_name= + expr.get_string("component_name"); + + get_value_set_rec(expr.op0(), dest, "."+component_name+suffix, + original_type, ns, recursion_set); + + return; + } + } + else if(expr.id()=="symbol") + { + // just keep a reference to the ident in the set + // (if it exists) + irep_idt ident = expr.get_string("identifier")+suffix; + valuest::const_iterator v_it=values.find(ident); + + if(has_prefix(id2string(ident), alloc_adapter_prefix)) + { + insert(dest, expr, 0); + return; + } + else if(v_it!=values.end()) + { + typet t("#REF#"); + t.subtype() = expr.type(); + symbol_exprt sym(ident, t); + insert(dest, sym, 0); + return; + } + } + else if(expr.id()=="if") + { + if(expr.operands().size()!=3) + throw "if takes three operands"; + + get_value_set_rec(expr.op1(), dest, suffix, + original_type, ns, recursion_set); + get_value_set_rec(expr.op2(), dest, suffix, + original_type, ns, recursion_set); + + return; + } + else if(expr.id()=="address_of") + { + if(expr.operands().size()!=1) + throw expr.id_string()+" expected to have one operand"; + + get_reference_set_sharing(expr.op0(), dest, ns); + + return; + } + else if(expr.id()=="dereference" || + expr.id()=="implicit_dereference") + { + object_mapt reference_set; + get_reference_set_sharing(expr, reference_set, ns); + const object_map_dt &object_map=reference_set.read(); + + if(object_map.begin()!=object_map.end()) + { + forall_objects(it1, object_map) + { + const exprt &object=object_numbering[it1->first]; + get_value_set_rec(object, dest, suffix, + original_type, ns, recursion_set); + } + + return; + } + } + else if(expr.id()=="reference_to") + { + object_mapt reference_set; + + get_reference_set_sharing(expr, reference_set, ns); + + const object_map_dt &object_map=reference_set.read(); + + if(object_map.begin()!=object_map.end()) + { + forall_objects(it, object_map) + { + const exprt &object=object_numbering[it->first]; + get_value_set_rec(object, dest, suffix, + original_type, ns, recursion_set); + } + + return; + } + } + else if(expr.is_constant()) + { + // check if NULL + if(expr.get("value")=="NULL" && + expr.type().id()=="pointer") + { + insert(dest, exprt("NULL-object", expr.type().subtype()), 0); + return; + } + } + else if(expr.id()=="typecast") + { + if(expr.operands().size()!=1) + throw "typecast takes one operand"; + + get_value_set_rec(expr.op0(), dest, suffix, + original_type, ns, recursion_set); + + return; + } + else if(expr.id()=="+" || expr.id()=="-") + { + if(expr.operands().size()<2) + throw expr.id_string()+" expected to have at least two operands"; + + if(expr.type().id()=="pointer") + { + // find the pointer operand + const exprt *ptr_operand=NULL; + + forall_operands(it, expr) + if(it->type().id()=="pointer") + { + if(ptr_operand==NULL) + ptr_operand=&(*it); + else + throw "more than one pointer operand in pointer arithmetic"; + } + + if(ptr_operand==NULL) + throw "pointer type sum expected to have pointer operand"; + + object_mapt pointer_expr_set; + get_value_set_rec(*ptr_operand, pointer_expr_set, "", + ptr_operand->type(), ns, recursion_set); + + forall_objects(it, pointer_expr_set.read()) + { + objectt object=it->second; + + if(object.offset_is_zero() && + expr.operands().size()==2) + { + if(expr.op0().type().id()!="pointer") + { + mp_integer i; + if(to_integer(expr.op0(), i)) + object.offset_is_set=false; + else + object.offset=(expr.id()=="+")? i : -i; + } + else + { + mp_integer i; + if(to_integer(expr.op1(), i)) + object.offset_is_set=false; + else + object.offset=(expr.id()=="+")? i : -i; + } + } + else + object.offset_is_set=false; + + insert(dest, it->first, object); + } + + return; + } + } + else if(expr.id()=="sideeffect") + { + const irep_idt &statement=expr.get("statement"); + + if(statement=="function_call") + { + // these should be gone + throw "unexpected function_call sideeffect"; + } + else if(statement=="malloc") + { + if(expr.type().id()!="pointer") + throw "malloc expected to return pointer type"; + + assert(suffix==""); + + const typet &dynamic_type= + static_cast(expr.find("#type")); + + dynamic_object_exprt dynamic_object(dynamic_type); + // let's make up a `unique' number for this object... + dynamic_object.instance()=from_integer( + (from_function << 16) | from_target_index, typet("natural")); + dynamic_object.valid()=true_exprt(); + + insert(dest, dynamic_object, 0); + return; + } + else if(statement=="cpp_new" || + statement=="cpp_new[]") + { + assert(suffix==""); + assert(expr.type().id()=="pointer"); + + dynamic_object_exprt dynamic_object(expr.type().subtype()); + dynamic_object.instance()=from_integer( + (from_function << 16) | from_target_index, typet("natural")); + dynamic_object.valid()=true_exprt(); + + insert(dest, dynamic_object, 0); + return; + } + } + else if(expr.id()=="struct") + { + // this is like a static struct object + insert(dest, address_of_exprt(expr), 0); + return; + } + else if(expr.id()=="with" || + expr.id()=="array_of" || + expr.id()=="array") + { + // these are supposed to be done by assign() + throw "unexpected value in get_value_set: "+expr.id_string(); + } + else if(expr.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(expr); + + const std::string name= + "value_set::dynamic_object"+ + dynamic_object.instance().get_string("value")+ + suffix; + + // look it up + valuest::const_iterator v_it=values.find(name); + + if(v_it!=values.end()) + { + make_union(dest, v_it->second.object_map); + return; + } + } + + insert(dest, exprt("unknown", original_type)); +} + +/*******************************************************************\ + +Function: value_set_fit::dereference_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::dereference_rec( + const exprt &src, + exprt &dest) const +{ + // remove pointer typecasts + if(src.id()=="typecast") + { + assert(src.type().id()=="pointer"); + + if(src.operands().size()!=1) + throw "typecast expects one operand"; + + dereference_rec(src.op0(), dest); + } + else + dest=src; +} + +/*******************************************************************\ + +Function: value_set_fit::get_reference_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::get_reference_set( + const exprt &expr, + expr_sett &dest, + const namespacet &ns) const +{ + object_mapt object_map; + get_reference_set_sharing(expr, object_map, ns); + + forall_objects(it, object_map.read()) + { + const exprt& expr = object_numbering[it->first]; + + if (expr.type().id()=="#REF#") + { + const irep_idt& ident = expr.get("identifier"); + valuest::const_iterator vit = values.find(ident); + if (vit==values.end()) + { + // Assume the variable never was assigned, + // so assume it's reference set is unknown. + dest.insert(exprt("unknown", expr.type())); + } + else + { + object_mapt omt; + flatten(vit->second, omt); + + for(object_map_dt::iterator t_it=omt.write().begin(); + t_it!=omt.write().end(); + t_it++) + { + if(t_it->second.offset_is_set && + it->second.offset_is_set) + { + t_it->second.offset += it->second.offset; + } + else + t_it->second.offset_is_set=false; + } + + forall_objects(it, omt.read()) + dest.insert(to_expr(it)); + } + } + else + dest.insert(to_expr(it)); + } +} + +/*******************************************************************\ + +Function: value_set_fit::get_reference_set_sharing + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::get_reference_set_sharing( + const exprt &expr, + expr_sett &dest, + const namespacet &ns) const +{ + object_mapt object_map; + get_reference_set_sharing(expr, object_map, ns); + + forall_objects(it, object_map.read()) + dest.insert(to_expr(it)); +} + +/*******************************************************************\ + +Function: value_set_fit::get_reference_set_sharing_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::get_reference_set_sharing_rec( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const +{ + #if 0 + std::cout << "GET_REFERENCE_SET_REC EXPR: " << from_expr(ns, "", expr) << std::endl; + #endif + + if(expr.type().id()=="#REF#") + { + valuest::const_iterator fi = values.find(expr.get("identifier")); + if(fi!=values.end()) + { + forall_objects(it, fi->second.object_map.read()) + get_reference_set_sharing_rec(object_numbering[it->first], dest, ns); + return; + } + } + else if(expr.id()=="symbol" || + expr.id()=="dynamic_object" || + expr.id()==ID_string_constant) + { + if(expr.type().id()=="array" && + expr.type().subtype().id()=="array") + insert(dest, expr); + else + insert(dest, expr, 0); + + return; + } + else if(expr.id()=="dereference" || + expr.id()=="implicit_dereference") + { + if(expr.operands().size()!=1) + throw expr.id_string()+" expected to have one operand"; + + gvs_recursion_sett recset; + object_mapt temp; + get_value_set_rec(expr.op0(), temp, "", expr.op0().type(), ns, recset); + + // REF's need to be dereferenced manually! + forall_objects(it, temp.read()) + { + const exprt &obj = object_numbering[it->first]; + if (obj.type().id()=="#REF#") + { + const irep_idt &ident = obj.get("identifier"); + valuest::const_iterator v_it = values.find(ident); + + if (v_it!=values.end()) + { + object_mapt t2; + flatten(v_it->second, t2); + + for(object_map_dt::iterator t_it=t2.write().begin(); + t_it!=t2.write().end(); + t_it++) + { + if(t_it->second.offset_is_set && + it->second.offset_is_set) + { + t_it->second.offset += it->second.offset; + } + else + t_it->second.offset_is_set=false; + } + + forall_objects(it2, t2.read()) + insert(dest, it2); + } + else + insert(dest, exprt("unknown", obj.type().subtype())); + } + else + insert(dest, it); + } + + #if 0 + for(expr_sett::const_iterator it=value_set.begin(); it!=value_set.end(); it++) + std::cout << "VALUE_SET: " << from_expr(ns, "", *it) << std::endl; + #endif + + return; + } + else if(expr.id()=="index") + { + if(expr.operands().size()!=2) + throw "index expected to have two operands"; + + const exprt &array=expr.op0(); + const exprt &offset=expr.op1(); + const typet &array_type=ns.follow(array.type()); + + assert(array_type.id()=="array" || + array_type.id()=="incomplete_array"); + + object_mapt array_references; + get_reference_set_sharing(array, array_references, ns); + + const object_map_dt &object_map=array_references.read(); + + forall_objects(a_it, object_map) + { + const exprt &object=object_numbering[a_it->first]; + + if(object.id()=="unknown") + insert(dest, exprt("unknown", expr.type())); + else + { + exprt index_expr("index", expr.type()); + index_expr.operands().resize(2); + index_expr.op0()=object; + index_expr.op1()=gen_zero(index_type()); + + // adjust type? + if(object.type().id()!="#REF#" && + ns.follow(object.type())!=array_type) + index_expr.make_typecast(array.type()); + + objectt o=a_it->second; + mp_integer i; + + if(offset.is_zero()) + { + } + else if(!to_integer(offset, i) && + o.offset_is_zero()) + o.offset=i; + else + o.offset_is_set=false; + + insert(dest, index_expr, o); + } + } + + return; + } + else if(expr.id()=="member") + { + const irep_idt &component_name=expr.get("component_name"); + + if(expr.operands().size()!=1) + throw "member expected to have one operand"; + + const exprt &struct_op=expr.op0(); + + object_mapt struct_references; + get_reference_set_sharing(struct_op, struct_references, ns); + + forall_objects(it, struct_references.read()) + { + const exprt &object=object_numbering[it->first]; + const typet &obj_type=ns.follow(object.type()); + + if(object.id()=="unknown") + insert(dest, exprt("unknown", expr.type())); + else if(object.id()=="dynamic_object" && + obj_type.id()!="struct" && + obj_type.id()!="union") + { + // we catch dynamic objects of the wrong type, + // to avoid non-integral typecasts. + insert(dest, exprt("unknown", expr.type())); + } + else + { + objectt o=it->second; + + exprt member_expr("member", expr.type()); + member_expr.copy_to_operands(object); + member_expr.set("component_name", component_name); + + // adjust type? + if(ns.follow(struct_op.type())!=ns.follow(object.type())) + member_expr.op0().make_typecast(struct_op.type()); + + insert(dest, member_expr, o); + } + } + + return; + } + else if(expr.id()=="if") + { + if(expr.operands().size()!=3) + throw "if takes three operands"; + + get_reference_set_sharing_rec(expr.op1(), dest, ns); + get_reference_set_sharing_rec(expr.op2(), dest, ns); + return; + } + + insert(dest, exprt("unknown", expr.type())); +} + +/*******************************************************************\ + +Function: value_set_fit::assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::assign( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns) +{ + #if 0 + std::cout << "ASSIGN LHS: " << from_expr(ns, "", lhs) << std::endl; + std::cout << "ASSIGN RHS: " << from_expr(ns, "", rhs) << std::endl; + #endif + + if(rhs.id()=="if") + { + if(rhs.operands().size()!=3) + throw "if takes three operands"; + + assign(lhs, rhs.op1(), ns); + assign(lhs, rhs.op2(), ns); + return; + } + + const typet &type=ns.follow(lhs.type()); + + if(type.id()=="struct" || + type.id()=="union") + { + const struct_typet &struct_type=to_struct_type(type); + + unsigned no=0; + + for(struct_typet::componentst::const_iterator + c_it=struct_type.components().begin(); + c_it!=struct_type.components().end(); + c_it++, no++) + { + const typet &subtype=c_it->type(); + const irep_idt &name=c_it->get("name"); + + // ignore methods + if(subtype.id()=="code") continue; + + exprt lhs_member("member", subtype); + lhs_member.set("component_name", name); + lhs_member.copy_to_operands(lhs); + + exprt rhs_member; + + if(rhs.id()=="unknown" || + rhs.id()=="invalid") + { + rhs_member=exprt(rhs.id(), subtype); + } + else + { + assert(base_type_eq(rhs.type(), type, ns)); + + if(rhs.id()=="struct" || + rhs.id()=="constant") + { + assert(nofirst]; + + if(object.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(object); + + if(dynamic_object.valid().is_true()) + to_mark.insert(dynamic_object.instance()); + } + } + + // mark these as 'may be invalid' + // this, unfortunately, destroys the sharing + for(valuest::iterator v_it=values.begin(); + v_it!=values.end(); + v_it++) + { + object_mapt new_object_map; + + const object_map_dt &old_object_map= + v_it->second.object_map.read(); + + bool changed=false; + + forall_objects(o_it, old_object_map) + { + const exprt &object=object_numbering[o_it->first]; + + if(object.id()=="dynamic_object") + { + const exprt &instance= + to_dynamic_object_expr(object).instance(); + + if(to_mark.count(instance)==0) + set(new_object_map, o_it); + else + { + // adjust + objectt o=o_it->second; + exprt tmp(object); + to_dynamic_object_expr(tmp).valid()=exprt("unknown"); + insert(new_object_map, tmp, o); + changed=true; + } + } + else + set(new_object_map, o_it); + } + + if(changed) + v_it->second.object_map=new_object_map; + } +} + +/*******************************************************************\ + +Function: value_set_fit::assign_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::assign_rec( + const exprt &lhs, + const object_mapt &values_rhs, + const std::string &suffix, + const namespacet &ns, + assign_recursion_sett &recursion_set) +{ + #if 0 + std::cout << "ASSIGN_REC LHS: " << from_expr(ns, "", lhs) << std::endl; + std::cout << "ASSIGN_REC SUFFIX: " << suffix << std::endl; + + for(object_map_dt::const_iterator it=values_rhs.read().begin(); + it!=values_rhs.read().end(); it++) + std::cout << "ASSIGN_REC RHS: " << to_expr(it) << std::endl; + #endif + + if(lhs.type().id()=="#REF#") + { + const irep_idt &ident = lhs.get("identifier"); + object_mapt temp; + gvs_recursion_sett recset; + get_value_set_rec(lhs, temp, "", lhs.type().subtype(), ns, recset); + + if(recursion_set.find(ident)!=recursion_set.end()) + { + recursion_set.insert(ident); + + forall_objects(it, temp.read()) + if(object_numbering[it->first].id()!="unknown") + assign_rec(object_numbering[it->first], values_rhs, + suffix, ns, recursion_set); + + recursion_set.erase(ident); + } + } + else if(lhs.id()=="symbol") + { + const irep_idt &identifier=lhs.get("identifier"); + + if(has_prefix(id2string(identifier), + "value_set::dynamic_object") || + has_prefix(id2string(identifier), + "value_set::return_value") || + values.find(id2string(identifier)+suffix)!=values.end()) + // otherwise we don't track this value + { + entryt &entry = get_entry(identifier, suffix); + + if (make_union(entry.object_map, values_rhs)) + changed = true; + } + } + else if(lhs.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(lhs); + + const std::string name= + "value_set::dynamic_object"+ + dynamic_object.instance().get_string("value"); + + if (make_union(get_entry(name, suffix).object_map, values_rhs)) + changed = true; + } + else if(lhs.id()=="dereference" || + lhs.id()=="implicit_dereference") + { + if(lhs.operands().size()!=1) + throw lhs.id_string()+" expected to have one operand"; + + object_mapt reference_set; + get_reference_set_sharing(lhs, reference_set, ns); + + forall_objects(it, reference_set.read()) + { + const exprt &object=object_numbering[it->first]; + + if(object.id()!="unknown") + assign_rec(object, values_rhs, suffix, ns, recursion_set); + } + } + else if(lhs.id()=="index") + { + if(lhs.operands().size()!=2) + throw "index expected to have two operands"; + + const typet &type=ns.follow(lhs.op0().type()); + + assert(type.id()=="array" || type.id()=="incomplete_array" || type.id()=="#REF#"); + + assign_rec(lhs.op0(), values_rhs, "[]"+suffix, ns, recursion_set); + } + else if(lhs.id()=="member") + { + if(lhs.operands().size()!=1) + throw "member expected to have one operand"; + + if(lhs.op0().is_nil()) return; + + const std::string &component_name=lhs.get_string("component_name"); + + const typet &type=ns.follow(lhs.op0().type()); + + assert(type.id()=="struct" || + type.id()=="union" || + type.id()=="incomplete_struct" || + type.id()=="incomplete_union"); + + assign_rec(lhs.op0(), values_rhs, "."+component_name+suffix, + ns, recursion_set); + } + else if(lhs.id()=="valid_object" || + lhs.id()=="dynamic_size" || + lhs.id()=="dynamic_type") + { + // we ignore this here + } + else if(lhs.id()==ID_string_constant) + { + // someone writes into a string-constant + // evil guy + } + else if(lhs.id()=="NULL-object") + { + // evil as well + } + else if(lhs.id()=="typecast") + { + const typecast_exprt &typecast_expr=to_typecast_expr(lhs); + + assign_rec(typecast_expr.op(), values_rhs, suffix, ns, recursion_set); + } + else if(lhs.id()=="zero_string" || + lhs.id()=="is_zero_string" || + lhs.id()=="zero_string_length") + { + // ignore + } + else if(lhs.id()=="byte_extract_little_endian" || + lhs.id()=="byte_extract_big_endian") + { + assert(lhs.operands().size()==2); + assign_rec(lhs.op0(), values_rhs, suffix, ns, recursion_set); + } + else + throw "assign NYI: `"+lhs.id_string()+"'"; +} + +/*******************************************************************\ + +Function: value_set_fit::do_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::do_function_call( + const irep_idt &function, + const exprt::operandst &arguments, + const namespacet &ns) +{ + const symbolt &symbol=ns.lookup(function); + + const code_typet &type=to_code_type(symbol.type); + const code_typet::argumentst &argument_types=type.arguments(); + + // these first need to be assigned to dummy, temporary arguments + // and only thereafter to the actuals, in order + // to avoid overwriting actuals that are needed for recursive + // calls + + for(unsigned i=0; iget_identifier(); + if(identifier=="") continue; + + add_var(identifier, ""); + + const exprt v_expr= + symbol_exprt("value_set::" + function.as_string() + "::" + + "argument$"+i2string(i), it->type()); + + exprt actual_lhs=symbol_exprt(identifier, it->type()); + assign(actual_lhs, v_expr, ns); + i++; + } +} + +/*******************************************************************\ + +Function: value_set_fit::do_end_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::do_end_function( + const exprt &lhs, + const namespacet &ns) +{ + if(lhs.is_nil()) return; + + std::string rvs = "value_set::return_value" + i2string(from_function); + symbol_exprt rhs(rvs, lhs.type()); + + assign(lhs, rhs, ns); +} + +/*******************************************************************\ + +Function: value_set_fit::apply_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fit::apply_code( + const exprt &code, + const namespacet &ns) +{ + const irep_idt &statement=code.get("statement"); + + if(statement=="block") + { + forall_operands(it, code) + apply_code(*it, ns); + } + else if(statement=="function_call") + { + // shouldn't be here + assert(false); + } + else if(statement=="assign" || + statement=="init") + { + if(code.operands().size()!=2) + throw "assignment expected to have two operands"; + + assign(code.op0(), code.op1(), ns); + } + else if(statement=="decl") + { + if(code.operands().size()!=1) + throw "decl expected to have one operand"; + + const exprt &lhs=code.op0(); + + if(lhs.id()!="symbol") + throw "decl expected to have symbol on lhs"; + + assign(lhs, exprt("invalid", lhs.type()), ns); + } + else if(statement=="specc_notify" || + statement=="specc_wait") + { + // ignore, does not change variables + } + else if(statement=="expression") + { + // can be ignored, we don't expect sideeffects here + } + else if(statement=="cpp_delete" || + statement=="cpp_delete[]") + { + // does nothing + } + else if(statement=="free") + { + // this may kill a valid bit + + if(code.operands().size()!=1) + throw "free expected to have one operand"; + + do_free(code.op0(), ns); + } + else if(statement=="lock" || statement=="unlock") + { + // ignore for now + } + else if(statement=="asm") + { + // ignore for now, probably not safe + } + else if(statement=="nondet") + { + // doesn't do anything + } + else if(statement=="printf") + { + // doesn't do anything + } + else if(statement=="return") + { + // this is turned into an assignment + if(code.operands().size()==1) + { + std::string rvs = "value_set::return_value" + i2string(from_function); + symbol_exprt lhs(rvs, code.op0().type()); + assign(lhs, code.op0(), ns); + } + } + else + { + std::cerr << code.pretty() << std::endl; + throw "value_set_fit: unexpected statement: "+id2string(statement); + } +} diff --git a/src/pointer-analysis/value_set_fi.h b/src/pointer-analysis/value_set_fi.h new file mode 100644 index 00000000000..d7348a21749 --- /dev/null +++ b/src/pointer-analysis/value_set_fi.h @@ -0,0 +1,329 @@ +/*******************************************************************\ + +Module: Value Set (Flow Insensitive, Sharing) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#ifndef VALUE_SET_FI_H_ +#define VALUE_SET_FI_H_ + +#include + +#include +#include +#include + +#include "object_numbering.h" + +class value_set_fit +{ +public: + value_set_fit() + { + } + + unsigned to_function, from_function; + unsigned to_target_index, from_target_index; + static object_numberingt object_numbering; + static hash_numbering function_numbering; + + void set_from(const irep_idt& function, unsigned inx) + { + from_function = function_numbering.number(function); + from_target_index = inx; + } + + void set_to(const irep_idt &function, unsigned inx) + { + to_function = function_numbering.number(function); + to_target_index = inx; + } + + typedef irep_idt idt; + + static const std::string &id2string(const idt &id) + { + #ifdef USE_DSTRING + return id.as_string(); + #else + return id; + #endif + } + + class objectt + { + public: + objectt():offset_is_set(false) + { + } + + explicit objectt(const mp_integer &_offset): + offset(_offset), + offset_is_set(true) + { + } + + mp_integer offset; + bool offset_is_set; + bool offset_is_zero() const + { return offset_is_set && offset.is_zero(); } + }; + + class object_map_dt:public std::map + { + public: + const static object_map_dt empty; + }; + + exprt to_expr(object_map_dt::const_iterator it) const; + + typedef reference_counting object_mapt; + + void set(object_mapt &dest, object_map_dt::const_iterator it) const + { + dest.write()[it->first]=it->second; + } + + bool insert(object_mapt &dest, object_map_dt::const_iterator it) const + { + return insert(dest, it->first, it->second); + } + + bool insert(object_mapt &dest, const exprt &src) const + { + return insert(dest, object_numbering.number(src), objectt()); + } + + bool insert(object_mapt &dest, const exprt &src, const mp_integer &offset) const + { + return insert(dest, object_numbering.number(src), objectt(offset)); + } + + bool insert(object_mapt &dest, unsigned n, const objectt &object) const + { + if(dest.read().find(n)==dest.read().end()) + { + // new + dest.write()[n]=object; + return true; + } + else + { + objectt &old=dest.write()[n]; + + if(old.offset_is_set && object.offset_is_set) + { + if(old.offset==object.offset) + return false; + else + { + old.offset_is_set=false; + return true; + } + } + else if(!old.offset_is_set) + return false; + else + { + old.offset_is_set=false; + return true; + } + } + } + + bool insert(object_mapt &dest, const exprt &expr, const objectt &object) const + { + return insert(dest, object_numbering.number(expr), object); + } + + struct entryt + { + object_mapt object_map; + idt identifier; + std::string suffix; + + entryt() + { + } + + entryt(const idt &_identifier, const std::string _suffix): + identifier(_identifier), + suffix(_suffix) + { + } + }; + + typedef hash_set_cont expr_sett; + + static void add_objects(const entryt &src, expr_sett &dest); + + #ifdef USE_DSTRING + typedef std::map valuest; + typedef std::set flatten_seent; + typedef hash_set_cont gvs_recursion_sett; + typedef hash_set_cont recfind_recursion_sett; + typedef hash_set_cont assign_recursion_sett; + #else + typedef hash_map_cont valuest; + typedef hash_set_cont flatten_seent; + typedef hash_set_cont gvs_recursion_sett; + typedef hash_set_cont recfind_recursion_sett; + typedef hash_set_cont assign_recursion_sett; + #endif + + void get_value_set( + const exprt &expr, + std::list &dest, + const namespacet &ns) const; + + expr_sett &get( + const idt &identifier, + const std::string &suffix); + + void make_any() + { + values.clear(); + } + + void clear() + { + values.clear(); + } + + void add_var(const idt &id, const std::string &suffix) + { + get_entry(id, suffix); + } + + void add_var(const entryt &e) + { + get_entry(e.identifier, e.suffix); + } + + entryt &get_entry(const idt &id, const std::string &suffix) + { + return get_entry(entryt(id, suffix)); + } + + entryt &get_entry(const entryt &e) + { + std::string index=id2string(e.identifier)+e.suffix; + + std::pair r= + values.insert(std::pair(index, e)); + + return r.first->second; + } + + void add_vars(const std::list &vars) + { + for(std::list::const_iterator + it=vars.begin(); + it!=vars.end(); + it++) + add_var(*it); + } + + void output( + const namespacet &ns, + std::ostream &out) const; + + valuest values; + + bool changed; + + // true = added s.th. new + bool make_union(object_mapt &dest, const object_mapt &src) const; + + // true = added s.th. new + bool make_union(const valuest &new_values); + + // true = added s.th. new + bool make_union(const value_set_fit &new_values) + { + return make_union(new_values.values); + } + + void apply_code( + const exprt &code, + const namespacet &ns); + + void assign( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns); + + void do_function_call( + const irep_idt &function, + const exprt::operandst &arguments, + const namespacet &ns); + + // edge back to call site + void do_end_function( + const exprt &lhs, + const namespacet &ns); + + void get_reference_set( + const exprt &expr, + expr_sett &expr_set, + const namespacet &ns) const; + +protected: + void get_reference_set_sharing( + const exprt &expr, + expr_sett &expr_set, + const namespacet &ns) const; + + void get_value_set_rec( + const exprt &expr, + object_mapt &dest, + const std::string &suffix, + const typet &original_type, + const namespacet &ns, + gvs_recursion_sett &recursion_set) const; + + + void get_value_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const; + + void get_reference_set_sharing( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const + { + get_reference_set_sharing_rec(expr, dest, ns); + } + + void get_reference_set_sharing_rec( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const; + + void dereference_rec( + const exprt &src, + exprt &dest) const; + + void assign_rec( + const exprt &lhs, + const object_mapt &values_rhs, + const std::string &suffix, + const namespacet &ns, + assign_recursion_sett &recursion_set); + + void do_free( + const exprt &op, + const namespacet &ns); + + void flatten(const entryt &e, object_mapt &dest) const; + + void flatten_rec( const entryt&, + object_mapt&, + flatten_seent&) const; +}; + +#endif /*VALUE_SET_FI_H_*/ diff --git a/src/pointer-analysis/value_set_fivr.cpp b/src/pointer-analysis/value_set_fivr.cpp new file mode 100644 index 00000000000..9d44d18924f --- /dev/null +++ b/src/pointer-analysis/value_set_fivr.cpp @@ -0,0 +1,2247 @@ +/*******************************************************************\ + +Module: Value Set (Flow Insensitive, Sharing, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com, + CM Wintersteiger + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "value_set_fivr.h" + +const value_set_fivrt::object_map_dt value_set_fivrt::object_map_dt::empty; +object_numberingt value_set_fivrt::object_numbering; +hash_numbering value_set_fivrt::function_numbering; + +static std::string alloc_adapter_prefix = "alloc_adaptor::"; + +#define forall_objects(it, map) \ + for(object_map_dt::const_iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) + +#define forall_valid_objects(it, map) \ + for(object_map_dt::const_iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) \ + if((map).is_valid_at((it)->first, from_function, from_target_index)) + +#define Forall_objects(it, map) \ + for(object_map_dt::iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) + +#define Forall_valid_objects(it, map) \ + for(object_map_dt::iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) \ + if((map).is_valid_at((it)->first, from_function, from_target_index)) + +/*******************************************************************\ + +Function: value_set_fivrt::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::output( + const namespacet &ns, + std::ostream &out) const +{ + for(valuest::const_iterator + v_it=values.begin(); + v_it!=values.end(); + v_it++) + { + irep_idt identifier, display_name; + + const entryt &e=v_it->second; + + // do we need to output at all? +// bool yes=false; +// for (object_map_dt::const_iterator it=e.object_map.read().begin(); +// it!=e.object_map.read().end(); +// it++) +// if (e.object_map.read().is_valid_at(it->first, +// from_function, +// from_target_index)) yes=true; +// if (!yes) continue; + +// const object_mapt &object_map = e.object_map; + object_mapt object_map; + flatten(e, object_map); + +// if(has_prefix(id2string(e.identifier), "value_set::dynamic_object")) +// { +// display_name=id2string(e.identifier)+e.suffix; +// identifier=""; +// } +// else if(e.identifier=="value_set::return_value") +// { +// display_name="RETURN_VALUE"+e.suffix; +// identifier=""; +// } +// else + { + #if 0 + const symbolt &symbol=ns.lookup(id2string(e.identifier)); + display_name=symbol.display_name()+e.suffix; + identifier=symbol.name; + #else + identifier=id2string(e.identifier); + display_name=id2string(identifier)+e.suffix; + #endif + } + + out << display_name << " = { "; + if(object_map.read().size()!=0) out << std::endl << " "; + + unsigned width=0; + + forall_objects(o_it, object_map.read()) + { + const exprt &o=object_numbering[o_it->first]; + + std::string result="<"; //+i2string(o_it->first) + ","; + + if(o.id()==ID_invalid) + { + result+="#"; + result+=", *, "; // offset unknown + if (o.type().id()==ID_unknown) + result+="*"; + else if (o.type().id()==ID_invalid) + result+="#"; + else + result+=from_type(ns, identifier, o.type()); + result+=">"; + } + else if (o.id()==ID_unknown) + { + result+="*"; + result+=", *, "; // offset unknown + if (o.type().id()==ID_unknown) + result+="*"; + else if (o.type().id()==ID_invalid) + result+="#"; + else + result+=from_type(ns, identifier, o.type()); + result+=">"; + } + else + { + result+=from_expr(ns, identifier, o)+", "; + + if(o_it->second.offset_is_set) + result+=integer2string(o_it->second.offset)+""; + else + result+="*"; + + result+=", "; + + if (o.type().id()=="unknown") + result+="*"; + else + { + if (o.type().id()=="#REF#") + result += "#REF#"; + else + result+=from_type(ns, identifier, o.type()); + } + + + result+=">"; + } + + out << result << std::endl; + + #if 0 + object_map_dt::validity_rangest::const_iterator vr = + object_map.read().validity_ranges.find(o_it->first); + + if (vr != object_map.read().validity_ranges.end()) + { + if (vr->second.size()==0) + std::cout << " Empty validity record" << std::endl; + else + for (object_map_dt::vrange_listt::const_iterator vit = + vr->second.begin(); + vit!=vr->second.end(); + vit++) + { + out << " valid at " << function_numbering[vit->function] << + " [" << vit->from << "," << vit->to << "]"; + if (from_function==vit->function && + from_target_index>=vit->from && + from_target_index<=vit->to) + out << " (*)"; + out << std::endl; + } + } + else + { + out << " No validity information" << std::endl; + } + #endif + + width+=result.size(); + + object_map_dt::const_iterator next(o_it); + next++; + + if(next!=object_map.read().end()) + { + out << "\n "; + } + } + + out << " } " << std::endl; + } +} + +/*******************************************************************\ + +Function: value_set_fivrt::flatten + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::flatten( + const entryt &e, + object_mapt &dest) const +{ + #if 0 + std::cout << "FLATTEN: " << e.identifier << e.suffix << std::endl; + #endif + + flatten_seent seen; + flatten_rec(e, dest, seen, from_function, from_target_index); + + #if 0 + std::cout << "FLATTEN: Done." << std::endl; + #endif +} + +/*******************************************************************\ + +Function: value_set_fivrt::flatten_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::flatten_rec( + const entryt &e, + object_mapt &dest, + flatten_seent &seen, + unsigned at_function, + unsigned at_index) const +{ + #if 0 + std::cout << "FLATTEN_REC: " << e.identifier << e.suffix << std::endl; + #endif + + std::string identifier = e.identifier.as_string(); + assert(seen.find(identifier + e.suffix)==seen.end()); + + bool generalize_index = false; + std::list add_ranges; + + seen.insert(identifier + e.suffix); + + forall_valid_objects(it, e.object_map.read()) + { + const exprt& o=object_numbering[it->first]; + + if (o.type().id()=="#REF#") + { + if (seen.find(o.get("identifier"))!=seen.end()) + { + generalize_index = true; + + object_map_dt::validity_rangest::const_iterator vit= + e.object_map.read().validity_ranges.find(it->first); + + if (vit!=e.object_map.read().validity_ranges.end()) + { + const object_map_dt::vrange_listt &vl = vit->second; + add_ranges.push_back(&vl); + } + continue; + } + + valuest::const_iterator fi = values.find(o.get("identifier")); + if (fi==values.end()) + { + // this is some static object, keep it in. + exprt se("symbol", o.type().subtype()); + se.set("identifier", o.get("identifier")); + insert_from(dest, se, 0); + } + else + { + // we need to flatten_rec wherever the entry + // _started_ to become valid + + object_map_dt::validity_rangest::const_iterator ranges_it = + e.object_map.read().validity_ranges.find(it->first); + if (ranges_it!=e.object_map.read().validity_ranges.end()) + { + for(object_map_dt::vrange_listt::const_iterator r_it = + ranges_it->second.begin(); + r_it!=ranges_it->second.end(); + r_it++) + { + // we only need to check the current function; + // the entry must have been valid within that function + if(r_it->function==at_function) + { + object_mapt temp; + flatten_rec(fi->second, temp, seen, r_it->function, r_it->from); + + for(object_map_dt::iterator t_it=temp.write().begin(); + t_it!=temp.write().end(); + t_it++) + { + if(t_it->second.offset_is_set && + it->second.offset_is_set) + { + t_it->second.offset += it->second.offset; + } + else + t_it->second.offset_is_set=false; + } + + forall_objects(oit, temp.read()) + insert_from(dest, oit); + } + } + } + } + + } + else + insert_from(dest, it); + } + + if (generalize_index) // this means we had recursive symbols in there + { + Forall_objects(it, dest.write()) + { + it->second.offset_is_set = false; + for (std::list::const_iterator vit = + add_ranges.begin(); + vit!=add_ranges.end(); + vit++) + { + for (object_map_dt::vrange_listt::const_iterator lit = + (*vit)->begin(); + lit!=(*vit)->end(); + lit++) + dest.write().set_valid_at(it->first, *lit); + } + } + } + + seen.erase(identifier + e.suffix); +} + +/*******************************************************************\ + +Function: value_set_fivrt::to_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt value_set_fivrt::to_expr(object_map_dt::const_iterator it) const +{ + const exprt &object=object_numbering[it->first]; + + if(object.id()=="invalid" || + object.id()=="unknown") + return object; + + object_descriptor_exprt od; + + od.object()=object; + + if(it->second.offset_is_set) + od.offset()=from_integer(it->second.offset, index_type()); + + od.type()=od.object().type(); + + return od; +} + +/*******************************************************************\ + +Function: value_set_fivrt::make_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::make_union( + object_mapt &dest, + const object_mapt &src) const +{ + bool result=false; + + forall_objects(it, src.read()) + { + if(insert_to(dest, it)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::make_valid_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::make_valid_union( + object_mapt &dest, + const object_mapt &src) const +{ + bool result=false; + + forall_valid_objects(it, src.read()) + { + if(insert_to(dest, it)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: value_set_fivrt::copy_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::copy_objects( + object_mapt &dest, + const object_mapt &src) const +{ + forall_valid_objects(it, src.read()) + { + dest.write()[it->first] = it->second; + dest.write().validity_ranges[it->first].push_back( + object_map_dt::validity_ranget(from_function, + from_target_index, + from_target_index)); + } +} + +/*******************************************************************\ + +Function: value_set_fivrt::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::get_value_set( + const exprt &expr, + std::list &value_set, + const namespacet &ns) const +{ + object_mapt object_map; + get_value_set(expr, object_map, ns); + + object_mapt flat_map; + + forall_objects(it, object_map.read()) + { + const exprt &object=object_numbering[it->first]; + if (object.type().id()=="#REF#") + { + assert(object.id()=="symbol"); + + const irep_idt &ident = object.get("identifier"); + valuest::const_iterator v_it = values.find(ident); + + if (v_it!=values.end()) + { + object_mapt temp; + flatten(v_it->second, temp); + + for(object_map_dt::iterator t_it=temp.write().begin(); + t_it!=temp.write().end(); + t_it++) + { + if(t_it->second.offset_is_set && + it->second.offset_is_set) + { + t_it->second.offset += it->second.offset; + } + else + t_it->second.offset_is_set=false; + + flat_map.write()[t_it->first]=t_it->second; + } + } + } + else + flat_map.write()[it->first]=it->second; + } + + forall_objects(fit, flat_map.read()) + value_set.push_back(to_expr(fit)); + + #if 0 + // Sanity check! + for(std::list::const_iterator it=value_set.begin(); + it!=value_set.end(); + it++) + assert(it->type().id()!="#REF"); + #endif + + #if 0 + for(std::list::const_iterator it=value_set.begin(); it!=value_set.end(); it++) + std::cout << "GET_VALUE_SET: " << from_expr(ns, "", *it) << std::endl; + #endif +} + +/*******************************************************************\ + +Function: value_set_fivrt::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::get_value_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const +{ + exprt tmp(expr); + simplify(tmp, ns); + + gvs_recursion_sett recset; + get_value_set_rec(tmp, dest, "", tmp.type(), ns, recset); +} + +/*******************************************************************\ + +Function: value_set_fivrt::get_value_set_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::get_value_set_rec( + const exprt &expr, + object_mapt &dest, + const std::string &suffix, + const typet &original_type, + const namespacet &ns, + gvs_recursion_sett &recursion_set) const +{ + #if 0 + std::cout << "GET_VALUE_SET_REC EXPR: " << expr << std::endl; + std::cout << "GET_VALUE_SET_REC SUFFIX: " << suffix << std::endl; + std::cout << std::endl; + #endif + + if(expr.type().id()=="#REF#") + { + valuest::const_iterator fi = values.find(expr.get("identifier")); + + if(fi!=values.end()) + { + forall_valid_objects(it, fi->second.object_map.read()) + get_value_set_rec(object_numbering[it->first], dest, suffix, + original_type, ns, recursion_set); + return; + } + else + { + insert_from(dest, exprt("unknown", original_type)); + return; + } + } + else if(expr.id()=="unknown" || expr.id()=="invalid") + { + insert_from(dest, exprt("unknown", original_type)); + return; + } + else if(expr.id()=="index") + { + assert(expr.operands().size()==2); + + const typet &type=ns.follow(expr.op0().type()); + + assert(type.id()=="array" || + type.id()=="incomplete_array" || + type.id()=="#REF#"); + + get_value_set_rec(expr.op0(), dest, "[]"+suffix, + original_type, ns, recursion_set); + + return; + } + else if(expr.id()=="member") + { + assert(expr.operands().size()==1); + + if(expr.op0().is_not_nil()) + { + const typet &type=ns.follow(expr.op0().type()); + + assert(type.id()=="struct" || + type.id()=="union" || + type.id()=="incomplete_struct" || + type.id()=="incomplete_union"); + + const std::string &component_name= + expr.get_string("component_name"); + + get_value_set_rec(expr.op0(), dest, "."+component_name+suffix, + original_type, ns, recursion_set); + + return; + } + } + else if(expr.id()=="symbol") + { + // just keep a reference to the ident in the set + // (if it exists) + irep_idt ident = expr.get_string("identifier")+suffix; + + if(has_prefix(id2string(ident), alloc_adapter_prefix)) + { + insert_from(dest, expr, 0); + return; + } + else + { + valuest::const_iterator v_it=values.find(ident); + + if(v_it!=values.end()) + { + typet t("#REF#"); + t.subtype() = expr.type(); + symbol_exprt sym(ident, t); + insert_from(dest, sym, 0); + return; + } + } + } + else if(expr.id()=="if") + { + if(expr.operands().size()!=3) + throw "if takes three operands"; + + get_value_set_rec(expr.op1(), dest, suffix, + original_type, ns, recursion_set); + get_value_set_rec(expr.op2(), dest, suffix, + original_type, ns, recursion_set); + + return; + } + else if(expr.id()=="address_of") + { + if(expr.operands().size()!=1) + throw expr.id_string()+" expected to have one operand"; + + get_reference_set_sharing(expr.op0(), dest, ns); + + return; + } + else if(expr.id()=="dereference" || + expr.id()=="implicit_dereference") + { + object_mapt reference_set; + get_reference_set_sharing(expr, reference_set, ns); + const object_map_dt &object_map=reference_set.read(); + + if(object_map.begin()!=object_map.end()) + { + forall_objects(it1, object_map) + { + const exprt &object=object_numbering[it1->first]; + get_value_set_rec(object, dest, suffix, + original_type, ns, recursion_set); + } + + return; + } + } + else if(expr.id()=="reference_to") + { + object_mapt reference_set; + + get_reference_set_sharing(expr, reference_set, ns); + + const object_map_dt &object_map=reference_set.read(); + + if(object_map.begin()!=object_map.end()) + { + forall_objects(it, object_map) + { + const exprt &object=object_numbering[it->first]; + get_value_set_rec(object, dest, suffix, + original_type, ns, recursion_set); + } + + return; + } + } + else if(expr.is_constant()) + { + // check if NULL + if(expr.get("value")=="NULL" && + expr.type().id()=="pointer") + { + insert_from(dest, exprt("NULL-object", expr.type().subtype()), 0); + return; + } + } + else if(expr.id()=="typecast") + { + if(expr.operands().size()!=1) + throw "typecast takes one operand"; + + get_value_set_rec(expr.op0(), dest, suffix, + original_type, ns, recursion_set); + + return; + } + else if(expr.id()=="+" || expr.id()=="-") + { + if(expr.operands().size()<2) + throw expr.id_string()+" expected to have at least two operands"; + + if(expr.type().id()=="pointer") + { + // find the pointer operand + const exprt *ptr_operand=NULL; + + forall_operands(it, expr) + if(it->type().id()=="pointer") + { + if(ptr_operand==NULL) + ptr_operand=&(*it); + else + throw "more than one pointer operand in pointer arithmetic"; + } + + if(ptr_operand==NULL) + throw "pointer type sum expected to have pointer operand"; + + object_mapt pointer_expr_set; + get_value_set_rec(*ptr_operand, pointer_expr_set, "", + ptr_operand->type(), ns, recursion_set); + + forall_objects(it, pointer_expr_set.read()) + { + objectt object=it->second; + + if(object.offset_is_zero() && + expr.operands().size()==2) + { + if(expr.op0().type().id()!="pointer") + { + mp_integer i; + if(to_integer(expr.op0(), i)) + object.offset_is_set=false; + else + object.offset=(expr.id()=="+")? i : -i; + } + else + { + mp_integer i; + if(to_integer(expr.op1(), i)) + object.offset_is_set=false; + else + object.offset=(expr.id()=="+")? i : -i; + } + } + else + object.offset_is_set=false; + + insert_from(dest, it->first, object); + } + + return; + } + } + else if(expr.id()=="sideeffect") + { + const irep_idt &statement=expr.get("statement"); + + if(statement=="function_call") + { + // these should be gone + throw "unexpected function_call sideeffect"; + } + else if(statement=="malloc") + { + if(expr.type().id()!="pointer") + throw "malloc expected to return pointer type"; + + assert(suffix==""); + + const typet &dynamic_type= + static_cast(expr.find("#type")); + + dynamic_object_exprt dynamic_object(dynamic_type); + // let's make up a `unique' number for this object... + dynamic_object.instance()=from_integer( + (from_function << 16) | from_target_index, typet("natural")); + dynamic_object.valid()=true_exprt(); + + insert_from(dest, dynamic_object, 0); + return; + } + else if(statement=="cpp_new" || + statement=="cpp_new[]") + { + assert(suffix==""); + assert(expr.type().id()=="pointer"); + + dynamic_object_exprt dynamic_object(expr.type().subtype()); + // let's make up a unique number for this object... + dynamic_object.instance()=from_integer( + (from_function << 16) | from_target_index, typet("natural")); + dynamic_object.valid()=true_exprt(); + + insert_from(dest, dynamic_object, 0); + return; + } + } + else if(expr.id()=="struct") + { + // this is like a static struct object + insert_from(dest, address_of_exprt(expr), 0); + return; + } + else if(expr.id()=="with" || + expr.id()=="array_of" || + expr.id()=="array") + { + // these are supposed to be done by assign() + throw "unexpected value in get_value_set: "+expr.id_string(); + } + else if(expr.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(expr); + + const std::string name= + "value_set::dynamic_object"+ + dynamic_object.instance().get_string("value")+ + suffix; + + // look it up + valuest::const_iterator v_it=values.find(name); + + if(v_it!=values.end()) + { + copy_objects(dest, v_it->second.object_map); + return; + } + } + + insert_from(dest, exprt("unknown", original_type)); +} + +/*******************************************************************\ + +Function: value_set_fivrt::dereference_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::dereference_rec( + const exprt &src, + exprt &dest) const +{ + // remove pointer typecasts + if(src.id()=="typecast") + { + assert(src.type().id()=="pointer"); + + if(src.operands().size()!=1) + throw "typecast expects one operand"; + + dereference_rec(src.op0(), dest); + } + else + dest=src; +} + +/*******************************************************************\ + +Function: value_set_fivrt::get_reference_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::get_reference_set( + const exprt &expr, + expr_sett &dest, + const namespacet &ns) const +{ + object_mapt object_map; + get_reference_set_sharing(expr, object_map, ns); + + forall_objects(it, object_map.read()) + { + const exprt& expr = object_numbering[it->first]; + + if (expr.type().id()=="#REF#") + { + const irep_idt& ident = expr.get("identifier"); + valuest::const_iterator vit = values.find(ident); + if (vit==values.end()) + { + // Assume the variable never was assigned, + // so assume it's reference set is unknown. + dest.insert(exprt("unknown", expr.type())); + } + else + { + object_mapt omt; + flatten(vit->second, omt); + + for(object_map_dt::iterator t_it=omt.write().begin(); + t_it!=omt.write().end(); + t_it++) + { + if(t_it->second.offset_is_set && + it->second.offset_is_set) + { + t_it->second.offset += it->second.offset; + } + else + t_it->second.offset_is_set=false; + } + + forall_objects(it, omt.read()) + dest.insert(to_expr(it)); + } + } + else + dest.insert(to_expr(it)); + } +} + +/*******************************************************************\ + +Function: value_set_fivrt::get_reference_set_sharing + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::get_reference_set_sharing( + const exprt &expr, + expr_sett &dest, + const namespacet &ns) const +{ + object_mapt object_map; + get_reference_set_sharing(expr, object_map, ns); + + forall_objects(it, object_map.read()) + dest.insert(to_expr(it)); +} + +/*******************************************************************\ + +Function: value_set_fivrt::get_reference_set_sharing_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::get_reference_set_sharing_rec( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const +{ + #if 0 + std::cout << "GET_REFERENCE_SET_REC EXPR: " << from_expr(ns, "", expr) << std::endl; + #endif + + if(expr.type().id()=="#REF#") + { + valuest::const_iterator fi = values.find(expr.get("identifier")); + if(fi!=values.end()) + { + forall_valid_objects(it, fi->second.object_map.read()) + get_reference_set_sharing_rec(object_numbering[it->first], dest, ns); + return; + } + } + else if(expr.id()=="symbol" || + expr.id()=="dynamic_object" || + expr.id()==ID_string_constant) + { + if(expr.type().id()=="array" && + expr.type().subtype().id()=="array") + insert_from(dest, expr); + else + insert_from(dest, expr, 0); + + return; + } + else if(expr.id()=="dereference" || + expr.id()=="implicit_dereference") + { + if(expr.operands().size()!=1) + throw expr.id_string()+" expected to have one operand"; + + gvs_recursion_sett recset; + object_mapt temp; + get_value_set_rec(expr.op0(), temp, "", expr.op0().type(), ns, recset); + + // REF's need to be dereferenced manually! + forall_objects(it, temp.read()) + { + const exprt &obj = object_numbering[it->first]; + if (obj.type().id()=="#REF#") + { + const irep_idt &ident = obj.get("identifier"); + valuest::const_iterator v_it = values.find(ident); + + if (v_it!=values.end()) + { + object_mapt t2; + flatten(v_it->second, t2); + + for(object_map_dt::iterator t_it=t2.write().begin(); + t_it!=t2.write().end(); + t_it++) + { + if(t_it->second.offset_is_set && + it->second.offset_is_set) + { + t_it->second.offset += it->second.offset; + } + else + t_it->second.offset_is_set=false; + } + + forall_objects(it2, t2.read()) + insert_from(dest, it2); + } + else + insert_from(dest, exprt("unknown", obj.type().subtype())); + } + else + insert_from(dest, it); + } + + #if 0 + for(expr_sett::const_iterator it=value_set.begin(); it!=value_set.end(); it++) + std::cout << "VALUE_SET: " << from_expr(ns, "", *it) << std::endl; + #endif + + return; + } + else if(expr.id()=="index") + { + if(expr.operands().size()!=2) + throw "index expected to have two operands"; + + const exprt &array=expr.op0(); + const exprt &offset=expr.op1(); + const typet &array_type=ns.follow(array.type()); + + assert(array_type.id()=="array" || + array_type.id()=="incomplete_array"); + + object_mapt array_references; + get_reference_set_sharing(array, array_references, ns); + + const object_map_dt &object_map=array_references.read(); + + forall_objects(a_it, object_map) + { + const exprt &object=object_numbering[a_it->first]; + + if(object.id()=="unknown") + insert_from(dest, exprt("unknown", expr.type())); + else + { + exprt index_expr("index", expr.type()); + index_expr.operands().resize(2); + index_expr.op0()=object; + index_expr.op1()=gen_zero(index_type()); + + // adjust type? + if(object.type().id()!="#REF#" && + ns.follow(object.type())!=array_type) + index_expr.make_typecast(array.type()); + + objectt o=a_it->second; + mp_integer i; + + if(offset.is_zero()) + { + } + else if(!to_integer(offset, i) && + o.offset_is_zero()) + o.offset=i; + else + o.offset_is_set=false; + + insert_from(dest, index_expr, o); + } + } + + return; + } + else if(expr.id()=="member") + { + const irep_idt &component_name=expr.get("component_name"); + + if(expr.operands().size()!=1) + throw "member expected to have one operand"; + + const exprt &struct_op=expr.op0(); + + object_mapt struct_references; + get_reference_set_sharing(struct_op, struct_references, ns); + + const object_map_dt &object_map=struct_references.read(); + + forall_objects(it, object_map) + { + const exprt &object=object_numbering[it->first]; + const typet &obj_type=ns.follow(object.type()); + + if(object.id()=="unknown") + insert_from(dest, exprt("unknown", expr.type())); + else if(object.id()=="dynamic_object" && + obj_type.id()!="struct" && + obj_type.id()!="union") + { + // we catch dynamic objects of the wrong type, + // to avoid non-integral typecasts. + insert_from(dest, exprt("unknown", expr.type())); + } + else + { + objectt o=it->second; + + exprt member_expr("member", expr.type()); + member_expr.copy_to_operands(object); + member_expr.set("component_name", component_name); + + // adjust type? + if(ns.follow(struct_op.type())!=ns.follow(object.type())) + member_expr.op0().make_typecast(struct_op.type()); + + insert_from(dest, member_expr, o); + } + } + + return; + } + else if(expr.id()=="if") + { + if(expr.operands().size()!=3) + throw "if takes three operands"; + + get_reference_set_sharing_rec(expr.op1(), dest, ns); + get_reference_set_sharing_rec(expr.op2(), dest, ns); + return; + } + + insert_from(dest, exprt("unknown", expr.type())); +} + +/*******************************************************************\ + +Function: value_set_fivrt::assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::assign( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool add_to_sets) +{ + #if 0 + std::cout << "ASSIGN LHS: " << lhs << std::endl; + std::cout << "ASSIGN LTYPE: " << ns.follow(lhs.type()) << std::endl; + std::cout << "ASSIGN RHS: " << from_expr(ns, "", rhs) << std::endl; + #endif + + if(rhs.id()=="if") + { + if(rhs.operands().size()!=3) + throw "if takes three operands"; + + assign(lhs, rhs.op1(), ns, add_to_sets); + assign(lhs, rhs.op2(), ns, true); + return; + } + + const typet &type=ns.follow(lhs.type()); + + if(type.id()=="struct" || + type.id()=="union") + { + const struct_typet &struct_type=to_struct_type(type); + + unsigned no=0; + + for(struct_typet::componentst::const_iterator + c_it=struct_type.components().begin(); + c_it!=struct_type.components().end(); + c_it++, no++) + { + const typet &subtype=c_it->type(); + const irep_idt &name=c_it->get("name"); + + // ignore methods + if(subtype.id()=="code") continue; + + exprt lhs_member("member", subtype); + lhs_member.set("component_name", name); + lhs_member.copy_to_operands(lhs); + + exprt rhs_member; + + if(rhs.id()=="unknown" || + rhs.id()=="invalid") + { + rhs_member=exprt(rhs.id(), subtype); + } + else + { + if (!base_type_eq(rhs.type(), type, ns)) + { + std::cout << "RHS: " << rhs.type() << std::endl; + std::cout << "LHS: " << type << std::endl; + } + + assert(base_type_eq(rhs.type(), type, ns)); + + if(rhs.id()=="struct" || + rhs.id()=="constant") + { + assert(nofirst]; + + if(object.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(object); + + if(dynamic_object.valid().is_true()) + to_mark.insert(dynamic_object.instance()); + } + } + + // mark these as 'may be invalid' + // this, unfortunately, destroys the sharing + for(valuest::iterator v_it=values.begin(); + v_it!=values.end(); + v_it++) + { + object_mapt new_object_map; + + const object_map_dt &old_object_map= + v_it->second.object_map.read(); + + bool changed=false; + + forall_objects(o_it, old_object_map) + { + const exprt &object=object_numbering[o_it->first]; + + if(object.id()=="dynamic_object") + { + const exprt &instance= + to_dynamic_object_expr(object).instance(); + + if(to_mark.count(instance)==0) + set(new_object_map, o_it); + else + { + // adjust + objectt o=o_it->second; + exprt tmp(object); + to_dynamic_object_expr(tmp).valid()=exprt("unknown"); + insert_to(new_object_map, tmp, o); + changed=true; + } + } + else + set(new_object_map, o_it); + } + + if(changed) + { + entryt &temp_entry = get_temporary_entry(v_it->second.identifier, + v_it->second.suffix); + temp_entry.object_map=new_object_map; + } + } +} + +/*******************************************************************\ + +Function: value_set_fivrt::assign_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::assign_rec( + const exprt &lhs, + const object_mapt &values_rhs, + const std::string &suffix, + const namespacet &ns, + assign_recursion_sett &recursion_set, + bool add_to_sets) +{ + #if 0 + std::cout << "ASSIGN_REC LHS: " << lhs << std::endl; + std::cout << "ASSIGN_REC SUFFIX: " << suffix << std::endl; + + for(object_map_dt::const_iterator it=values_rhs.read().begin(); + it!=values_rhs.read().end(); it++) + std::cout << "ASSIGN_REC RHS: " << to_expr(it) << std::endl; + #endif + + if(lhs.type().id()=="#REF#") + { + const irep_idt &ident = lhs.get("identifier"); + object_mapt temp; + gvs_recursion_sett recset; + get_value_set_rec(lhs, temp, "", lhs.type().subtype(), ns, recset); + + if(recursion_set.find(ident)!=recursion_set.end()) + { + recursion_set.insert(ident); + + forall_objects(it, temp.read()) + assign_rec(object_numbering[it->first], values_rhs, + suffix, ns, recursion_set, add_to_sets); + + recursion_set.erase(ident); + } + } + else if(lhs.id()=="symbol") + { + const irep_idt &identifier=lhs.get("identifier"); + + if(has_prefix(id2string(identifier), + "value_set::dynamic_object") || + has_prefix(id2string(identifier), + "value_set::return_value") || + values.find(id2string(identifier)+suffix)!=values.end()) + // otherwise we don't track this value + { + entryt &temp_entry = get_temporary_entry(identifier, suffix); + + // check if the right hand side contains a reference to ourselves, + // in that case we need to include all old values! + + recfind_recursion_sett recset; + if(add_to_sets || + recursive_find(identifier, values_rhs, recset)) + { + entryt &state_entry = get_entry(identifier, suffix); + make_valid_union(temp_entry.object_map, state_entry.object_map); + } + + make_union(temp_entry.object_map, values_rhs); + } + } + else if(lhs.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(lhs); + + const std::string name= + "value_set::dynamic_object"+ + dynamic_object.instance().get_string("value"); + + entryt &temp_entry = get_temporary_entry(name, suffix); + + // check if the right hand side contains a reference to ourselves, + // in that case we need to include all old values! + + recfind_recursion_sett recset; + if(add_to_sets || + recursive_find(name, values_rhs, recset)) + { + entryt &state_entry = get_entry(name, suffix); + make_valid_union(temp_entry.object_map, state_entry.object_map); + } + + make_union(temp_entry.object_map, values_rhs); + } + else if(lhs.id()=="dereference" || + lhs.id()=="implicit_dereference") + { + if(lhs.operands().size()!=1) + throw lhs.id_string()+" expected to have one operand"; + + object_mapt reference_set; + get_reference_set_sharing(lhs, reference_set, ns); + + forall_objects(it, reference_set.read()) + { + const exprt &object=object_numbering[it->first]; + + if(object.id()!="unknown") + assign_rec(object, values_rhs, suffix, ns, recursion_set, add_to_sets); + } + } + else if(lhs.id()=="index") + { + if(lhs.operands().size()!=2) + throw "index expected to have two operands"; + + const typet &type=ns.follow(lhs.op0().type()); + + assert(type.id()=="array" || type.id()=="incomplete_array" || type.id()=="#REF#"); + + assign_rec(lhs.op0(), values_rhs, "[]"+suffix, ns, recursion_set, add_to_sets); + } + else if(lhs.id()=="member") + { + if(lhs.operands().size()!=1) + throw "member expected to have one operand"; + + if(lhs.op0().is_nil()) return; + + const std::string &component_name=lhs.get_string("component_name"); + + const typet &type=ns.follow(lhs.op0().type()); + + assert(type.id()=="struct" || + type.id()=="union" || + type.id()=="incomplete_struct" || + type.id()=="incomplete_union"); + + assign_rec(lhs.op0(), values_rhs, "."+component_name+suffix, + ns, recursion_set, add_to_sets); + } + else if(lhs.id()=="valid_object" || + lhs.id()=="dynamic_size" || + lhs.id()=="dynamic_type") + { + // we ignore this here + } + else if(lhs.id()==ID_string_constant) + { + // someone writes into a string-constant + // evil guy + } + else if(lhs.id()=="NULL-object") + { + // evil as well + } + else if(lhs.id()=="typecast") + { + const typecast_exprt &typecast_expr=to_typecast_expr(lhs); + + assign_rec(typecast_expr.op(), values_rhs, suffix, + ns, recursion_set, add_to_sets); + } + else if(lhs.id()=="zero_string" || + lhs.id()=="is_zero_string" || + lhs.id()=="zero_string_length") + { + // ignore + } + else if(lhs.id()=="byte_extract_little_endian" || + lhs.id()=="byte_extract_big_endian") + { + assert(lhs.operands().size()==2); + assign_rec(lhs.op0(), values_rhs, suffix, ns, recursion_set, true); + } + else + throw "assign NYI: `"+lhs.id_string()+"'"; +} + +/*******************************************************************\ + +Function: value_set_fivrt::do_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::do_function_call( + const irep_idt &function, + const exprt::operandst &arguments, + const namespacet &ns) +{ + const symbolt &symbol=ns.lookup(function); + + const code_typet &type=to_code_type(symbol.type); + const code_typet::argumentst &argument_types=type.arguments(); + + // these first need to be assigned to dummy, temporary arguments + // and only thereafter to the actuals, in order + // to avoid overwriting actuals that are needed for recursive + // calls + + // the assigned data must be valid on from! + unsigned old_to_function=to_function; + unsigned old_to_target_index=to_target_index; + + to_function=from_function; + to_target_index=from_target_index; + + for(unsigned i=0; iget_identifier(); + if(identifier=="") continue; + + add_var(identifier, ""); + + const exprt v_expr= + symbol_exprt("value_set::" + function.as_string() + "::" + + "argument$"+i2string(i), it->type()); + + exprt actual_lhs=symbol_exprt(identifier, it->type()); + assign(actual_lhs, v_expr, ns, true); + i++; + } +} + +/*******************************************************************\ + +Function: value_set_fivrt::do_end_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::do_end_function( + const exprt &lhs, + const namespacet &ns) +{ + if(lhs.is_nil()) return; + + std::string rvs = "value_set::return_value" + i2string(from_function); + symbol_exprt rhs(rvs, lhs.type()); + + assign(lhs, rhs, ns); +} + +/*******************************************************************\ + +Function: value_set_fivrt::apply_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrt::apply_code( + const exprt &code, + const namespacet &ns) +{ + const irep_idt &statement=code.get("statement"); + + if(statement=="block") + { + forall_operands(it, code) + apply_code(*it, ns); + } + else if(statement=="function_call") + { + // shouldn't be here + assert(false); + } + else if(statement=="assign" || + statement=="init") + { + if(code.operands().size()!=2) + throw "assignment expected to have two operands"; + + assign(code.op0(), code.op1(), ns); + } + else if(statement=="decl") + { + if(code.operands().size()!=1) + throw "decl expected to have one operand"; + + const exprt &lhs=code.op0(); + + if(lhs.id()!="symbol") + throw "decl expected to have symbol on lhs"; + + assign(lhs, exprt("invalid", lhs.type()), ns); + } + else if(statement=="specc_notify" || + statement=="specc_wait") + { + // ignore, does not change variables + } + else if(statement=="expression") + { + // can be ignored, we don't expect sideeffects here + } + else if(statement=="cpp_delete" || + statement=="cpp_delete[]") + { + // does nothing + } + else if(statement=="free") + { + // this may kill a valid bit + + if(code.operands().size()!=1) + throw "free expected to have one operand"; + + do_free(code.op0(), ns); + } + else if(statement=="lock" || statement=="unlock") + { + // ignore for now + } + else if(statement=="asm") + { + // ignore for now, probably not safe + } + else if(statement=="nondet") + { + // doesn't do anything + } + else if(statement=="printf") + { + // doesn't do anything + } + else if(statement=="return") + { + // this is turned into an assignment + if(code.operands().size()==1) + { + std::string rvs = "value_set::return_value" + i2string(from_function); + symbol_exprt lhs(rvs, code.op0().type()); + assign(lhs, code.op0(), ns); + } + } + else + { + std::cerr << code.pretty() << std::endl; + throw "value_set_fivrt: unexpected statement: "+id2string(statement); + } +} + +/*******************************************************************\ + +Function: value_set_fivrt::insert_to + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::insert_to( + object_mapt &dest, + unsigned n, + const objectt &object) const +{ + object_map_dt &map = dest.write(); + if(map.find(n)==map.end()) + { +// std::cout << "NEW(" << n << "): " << object_numbering[n] << std::endl; + // new + map[n]=object; + map.set_valid_at(n, to_function, to_target_index); + return true; + } + else + { +// std::cout << "UPD " << n << std::endl; + objectt &old=map[n]; + + bool res = map.set_valid_at(n, to_function, to_target_index); + + if(old.offset_is_set && object.offset_is_set) + { + if(old.offset==object.offset) + return res; + else + { + old.offset_is_set=false; + return true; + } + } + else if(!old.offset_is_set) + return res; + else + { + old.offset_is_set=false; + return true; + } + } +} + +/*******************************************************************\ + +Function: value_set_fivrt::insert_from + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::insert_from( + object_mapt &dest, + unsigned n, + const objectt &object) const +{ + object_map_dt &map = dest.write(); + if(map.find(n)==map.end()) + { +// std::cout << "NEW(" << n << "): " << object_numbering[n] << std::endl; + // new + map[n]=object; + map.set_valid_at(n, from_function, from_target_index); + return true; + } + else + { +// std::cout << "UPD " << n << std::endl; + objectt &old=map[n]; + + bool res = map.set_valid_at(n, from_function, from_target_index); + + if(old.offset_is_set && object.offset_is_set) + { + if(old.offset==object.offset) + return res; + else + { + old.offset_is_set=false; + return true; + } + } + else if(!old.offset_is_set) + return res; + else + { + old.offset_is_set=false; + return true; + } + } +} + +/*******************************************************************\ + +Function: value_set_fivrt::object_map_dt::set_valid_at + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::object_map_dt::set_valid_at( + unsigned inx, + const validity_ranget &vr) +{ + bool res = false; + + for(unsigned i=vr.from; i<=vr.to; i++) + if(set_valid_at(inx, vr.function, i)) res = true; + + return res; +} + +/*******************************************************************\ + +Function: value_set_fivrt::object_map_dt::set_valid_at + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::object_map_dt::set_valid_at( + unsigned inx, + unsigned f, + unsigned line) +{ + if(is_valid_at(inx, f, line)) return false; + + vrange_listt &ranges = validity_ranges[inx]; + vrange_listt::iterator it=ranges.begin(); + + while(it->function!=f && it!=ranges.end()) it++; // ffw to function block + + for(; + it!=ranges.end() && it->function==f && it->from <= line; + it++) + { + if(it->function==f) + { + if( line == it->to+1) + { + it->to++; + + // by any chance: does the next one connect to this one? + vrange_listt::iterator n_it = it; n_it++; + if(n_it!=ranges.end() && + it->function == n_it->function && + it->to+1 == n_it->from) + { + n_it->from = it->from; // connected! + it = ranges.erase(it); + } + return true; + } + } + } + + // it now points to either the end, + // the first of a new function block,or + // the first one that has from > line + if(it!=ranges.end()) + { + if(it->function==f) + { + if( line == it->from - 1) + { + it->from--; + + // by any chance: does the previous one connect to this one? + if(it!=ranges.begin()) + { + vrange_listt::iterator p_it = it; p_it--; + if(p_it->function == it->function && + p_it->to+1 == it->from) + { + p_it->to = it->to; // connected! + it = ranges.erase(it); + } + } + return true; + } + } + } + + // none matched + validity_ranget insr(f, line, line); + ranges.insert(it, insr); + + return true; +} + +/*******************************************************************\ + +Function: value_set_fivrt::object_map_dt::is_valid_at + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::object_map_dt::is_valid_at( + unsigned inx, + unsigned f, + unsigned line) const +{ + #if 0 + std::cout << "IS_VALID_AT: " << inx << ", " << f << ", line " << line << + std::endl; + #endif + + validity_rangest::const_iterator vrs = validity_ranges.find(inx); + if (vrs!=validity_ranges.end()) + { + const vrange_listt &ranges = vrs->second; + + object_map_dt::vrange_listt::const_iterator it = ranges.begin(); + + while(it->function!=f && + it!=ranges.end()) it++; // ffw to function block + + for(; + it!=ranges.end() && it->function==f && it->from<=line ; + it++) + if(it->contains(f, line)) return true; + } + return false; +} + +/*******************************************************************\ + +Function: value_set_fivrt::recursive_find + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::recursive_find( + const irep_idt &ident, + const object_mapt &rhs, + recfind_recursion_sett &recursion_set) const +{ + forall_objects(it, rhs.read()) + { + const exprt &o = object_numbering[it->first]; + + if (o.id()=="symbol" && o.get("identifier")==ident) + return true; + else if (o.type().id()=="#REF#") + { + const irep_idt oid = o.get("identifier"); + + if (recursion_set.find(oid)!=recursion_set.end()) + return false; // we hit some other cycle on the way down + + if(oid==ident) + return true; + else + { + valuest::const_iterator vit = values.find(oid); + if(vit!=values.end()) + { + const entryt &e = vit->second; + + recursion_set.insert(oid); + if (recursive_find(ident, e.object_map, recursion_set)) + return true; + recursion_set.erase(oid); + } + } + } + } + + return false; +} + +/*******************************************************************\ + +Function: value_set_fivrt::handover + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrt::handover(void) +{ + bool changed=false; + + for (valuest::iterator it=values.begin(); + it!=values.end(); + it++) + { + object_mapt &state_map=it->second.object_map; + + irep_idt ident = id2string(it->second.identifier)+it->second.suffix; + + valuest::const_iterator t_it=temporary_values.find(ident); + + if(t_it==temporary_values.end()) + { +// std::cout << "OLD VALUES FOR: " << ident << std::endl; + Forall_valid_objects(o_it, state_map.write()) + { + if(state_map.write().set_valid_at(o_it->first, + to_function, to_target_index)) + changed = true; + } + } + else + { +// std::cout << "NEW VALUES FOR: " << ident << std::endl; + if(make_union(state_map, t_it->second.object_map)) + changed = true; + } + } + + temporary_values.clear(); + + return changed; +} diff --git a/src/pointer-analysis/value_set_fivr.h b/src/pointer-analysis/value_set_fivr.h new file mode 100644 index 00000000000..330206dfc1b --- /dev/null +++ b/src/pointer-analysis/value_set_fivr.h @@ -0,0 +1,387 @@ +/*******************************************************************\ + +Module: Value Set (Flow Insensitive, Sharing, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#ifndef VALUE_SET_FIVR_H_ +#define VALUE_SET_FIVR_H_ + +#include +#include +#include + +#include "object_numbering.h" + +class value_set_fivrt +{ +public: + value_set_fivrt() + { + } + + unsigned to_function, from_function; + unsigned to_target_index, from_target_index; + static object_numberingt object_numbering; + static hash_numbering function_numbering; + + void set_from(const irep_idt& function, unsigned inx) + { + from_function = function_numbering.number(function); + from_target_index = inx; + } + + void set_to(const irep_idt &function, unsigned inx) + { + to_function = function_numbering.number(function); + to_target_index = inx; + } + + typedef irep_idt idt; + + static const std::string &id2string(const idt &id) + { + #ifdef USE_DSTRING + return id.as_string(); + #else + return id; + #endif + } + + class objectt + { + public: + objectt() : + offset_is_set(false) + { + } + + explicit objectt(const mp_integer &_offset): + offset(_offset), + offset_is_set(true) + { + } + + mp_integer offset; + bool offset_is_set; + bool offset_is_zero() const + { return offset_is_set && offset.is_zero(); } + }; + + class object_map_dt + { + public: + const static object_map_dt empty; + + typedef std::map objmapt; + objmapt objmap; + + typedef objmapt::const_iterator const_iterator; + typedef objmapt::iterator iterator; + + const_iterator find(unsigned k) { return objmap.find(k); } + iterator begin(void) { return objmap.begin(); } + const_iterator begin(void) const { return objmap.begin(); } + iterator end(void) { return objmap.end(); } + const_iterator end(void) const { return objmap.end(); } + size_t size(void) const { return objmap.size(); } + void clear(void) { objmap.clear(); validity_ranges.clear(); } + + objectt& operator[](unsigned k) { + return objmap[k]; + } + + // operator[] is the only way to insert something! + std::pair insert (const std::pair&) + { assert(false); } + iterator insert(iterator, const std::pair&) + { assert(false); } + + class validity_ranget + { + public: + unsigned function; + unsigned from, to; + + validity_ranget(void) : + function(0),from(0), to(0) {}; + + validity_ranget(unsigned fnc, unsigned f, unsigned t) : + function(fnc),from(f), to(t) {}; + + bool contains(unsigned f, unsigned line) const + { + return (function==f && from<=line && line<=to); + } + }; + + typedef std::list vrange_listt; + typedef std::map validity_rangest; + validity_rangest validity_ranges; + + bool set_valid_at(unsigned inx, unsigned f, unsigned line); + bool set_valid_at(unsigned inx, const validity_ranget &vr); + bool is_valid_at(unsigned inx, unsigned f, unsigned line) const; + }; + + exprt to_expr(object_map_dt::const_iterator it) const; + + typedef reference_counting object_mapt; + + void set(object_mapt &dest, object_map_dt::const_iterator it) const + { + dest.write()[it->first]=it->second; + } + + bool insert_to(object_mapt &dest, object_map_dt::const_iterator it) const + { + return insert_to(dest, it->first, it->second); + } + + bool insert_to(object_mapt &dest, const exprt &src) const + { + return insert_to(dest, object_numbering.number(src), objectt()); + } + + bool insert_to(object_mapt &dest, const exprt &src, const mp_integer &offset) const + { + return insert_to(dest, object_numbering.number(src), objectt(offset)); + } + + bool insert_to(object_mapt &dest, unsigned n, const objectt &object) const; + + bool insert_to(object_mapt &dest, const exprt &expr, const objectt &object) const + { + return insert_to(dest, object_numbering.number(expr), object); + } + + bool insert_from(object_mapt &dest, object_map_dt::const_iterator it) const + { + return insert_from(dest, it->first, it->second); + } + + bool insert_from(object_mapt &dest, const exprt &src) const + { + return insert_from(dest, object_numbering.number(src), objectt()); + } + + bool insert_from(object_mapt &dest, const exprt &src, const mp_integer &offset) const + { + return insert_from(dest, object_numbering.number(src), objectt(offset)); + } + + bool insert_from(object_mapt &dest, unsigned n, const objectt &object) const; + + bool insert_from(object_mapt &dest, const exprt &expr, const objectt &object) const + { + return insert_from(dest, object_numbering.number(expr), object); + } + + struct entryt + { + object_mapt object_map; + idt identifier; + std::string suffix; + + entryt() { } + + entryt(const idt &_identifier, const std::string _suffix): + identifier(_identifier), + suffix(_suffix) + { + } + }; + + typedef hash_set_cont expr_sett; + + static void add_objects(const entryt &src, expr_sett &dest); + + #ifdef USE_DSTRING + typedef std::map valuest; + typedef hash_set_cont flatten_seent; + typedef hash_set_cont gvs_recursion_sett; + typedef hash_set_cont recfind_recursion_sett; + typedef hash_set_cont assign_recursion_sett; + #else + typedef hash_map_cont valuest; + typedef hash_set_cont flatten_seent; + typedef hash_set_cont gvs_recursion_sett; + typedef hash_set_cont recfind_recursion_sett; + typedef hash_set_cont assign_recursion_sett; + #endif + + void get_value_set( + const exprt &expr, + std::list &expr_set, + const namespacet &ns) const; + + expr_sett &get( + const idt &identifier, + const std::string &suffix); + + void make_any() + { + values.clear(); + } + + void clear() + { + values.clear(); + } + + void add_var(const idt &id, const std::string &suffix) + { + get_entry(id, suffix); + } + + void add_var(const entryt &e) + { + get_entry(e.identifier, e.suffix); + } + + entryt &get_entry(const idt &id, const std::string &suffix) + { + return get_entry(entryt(id, suffix)); + } + + entryt &get_entry(const entryt &e) + { + std::string index=id2string(e.identifier)+e.suffix; + + std::pair r= + values.insert(std::pair(index, e)); + + return r.first->second; + } + + entryt &get_temporary_entry(const idt &id, const std::string &suffix) + { + std::string index=id2string(id)+suffix; + return temporary_values[index]; + } + + void add_vars(const std::list &vars) + { + for(std::list::const_iterator + it=vars.begin(); + it!=vars.end(); + it++) + add_var(*it); + } + + void output( + const namespacet &ns, + std::ostream &out) const; + + valuest values; + valuest temporary_values; + + // true = added s.th. new + bool make_union( + object_mapt &dest, + const object_mapt &src) const; + + bool make_valid_union( + object_mapt &dest, + const object_mapt &src) const; + + void copy_objects( + object_mapt &dest, + const object_mapt &src) const; + + void apply_code( + const exprt &code, + const namespacet &ns); + + bool handover(void); + + void assign( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool add_to_sets=false); + + void do_function_call( + const irep_idt &function, + const exprt::operandst &arguments, + const namespacet &ns); + + // edge back to call site + void do_end_function( + const exprt &lhs, + const namespacet &ns); + + void get_reference_set( + const exprt &expr, + expr_sett &expr_set, + const namespacet &ns) const; + +protected: + void get_reference_set_sharing( + const exprt &expr, + expr_sett &expr_set, + const namespacet &ns) const; + + void get_value_set_rec( + const exprt &expr, + object_mapt &dest, + const std::string &suffix, + const typet &original_type, + const namespacet &ns, + gvs_recursion_sett &recursion_set) const; + + void get_value_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const; + + void get_reference_set_sharing( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const + { + get_reference_set_sharing_rec(expr, dest, ns); + } + + void get_reference_set_sharing_rec( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const; + + void dereference_rec( + const exprt &src, + exprt &dest) const; + + void assign_rec( + const exprt &lhs, + const object_mapt &values_rhs, + const std::string &suffix, + const namespacet &ns, + assign_recursion_sett &recursion_set, + bool add_to_sets); + + void do_free( + const exprt &op, + const namespacet &ns); + + void flatten( + const entryt &e, + object_mapt &dest) const; + + void flatten_rec( + const entryt&, + object_mapt&, + flatten_seent&, + unsigned from_function, + unsigned from_index) const; + + bool recursive_find( + const irep_idt &ident, + const object_mapt &rhs, + recfind_recursion_sett &recursion_set) const; +}; + +#endif /*VALUE_SET_INCR_H_*/ diff --git a/src/pointer-analysis/value_set_fivrns.cpp b/src/pointer-analysis/value_set_fivrns.cpp new file mode 100644 index 00000000000..21c39a89321 --- /dev/null +++ b/src/pointer-analysis/value_set_fivrns.cpp @@ -0,0 +1,1809 @@ +/*******************************************************************\ + +Module: Value Set (Flow Insensitive, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com, + CM Wintersteiger + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "value_set_fivrns.h" + +const value_set_fivrnst::object_map_dt value_set_fivrnst::object_map_dt::empty; +object_numberingt value_set_fivrnst::object_numbering; +hash_numbering value_set_fivrnst::function_numbering; + +static std::string alloc_adapter_prefix = "alloc_adaptor::"; + +#define forall_objects(it, map) \ + for(object_map_dt::const_iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) + +#define forall_valid_objects(it, map) \ + for(object_map_dt::const_iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) \ + if((map).is_valid_at((it)->first, from_function, from_target_index)) + +#define Forall_objects(it, map) \ + for(object_map_dt::iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) + +#define Forall_valid_objects(it, map) \ + for(object_map_dt::iterator (it) = (map).begin(); \ + (it)!=(map).end(); \ + (it)++) \ + if((map).is_valid_at((it)->first, from_function, from_target_index)) + +/*******************************************************************\ + +Function: value_set_fivrnst::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::output( + const namespacet &ns, + std::ostream &out) const +{ + for(valuest::const_iterator + v_it=values.begin(); + v_it!=values.end(); + v_it++) + output_entry(v_it->second, ns, out); +} + +/*******************************************************************\ + +Function: value_set_fivrnst::output_entry + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::output_entry( + const entryt &e, + const namespacet &ns, + std::ostream &out) const +{ + irep_idt identifier, display_name; +// if(has_prefix(id2string(e.identifier), "value_set::dynamic_object")) +// { +// display_name=id2string(e.identifier)+e.suffix; +// identifier=""; +// } +// else if(e.identifier=="value_set::return_value") +// { +// display_name="RETURN_VALUE"+e.suffix; +// identifier=""; +// } +// else + { + #if 0 + const symbolt &symbol=ns.lookup(id2string(e.identifier)); + display_name=symbol.display_name()+e.suffix; + identifier=symbol.name; + #else + display_name=id2string(e.identifier)+e.suffix; + #endif + } + + const object_mapt &object_map=e.object_map; + + out << display_name << " = { "; + if(object_map.read().size()!=0) out << std::endl << " "; + + unsigned width=0; + + forall_valid_objects(o_it, object_map.read()) + { + const exprt &o=object_numbering[o_it->first]; + + std::string result="<"; //+i2string(o_it->first) + ","; + + if(o.id()==ID_invalid) + { + result+="#"; + result+=", *, "; // offset unknown + if (o.type().id()==ID_unknown) + result+="*"; + else if (o.type().id()==ID_invalid) + result+="#"; + else + result+=from_type(ns, identifier, o.type()); + result+=">"; + } + else if (o.id()==ID_unknown) + { + result+="*"; + result+=", *, "; // offset unknown + if (o.type().id()==ID_unknown) + result+="*"; + else if (o.type().id()==ID_invalid) + result+="#"; + else + result+=from_type(ns, identifier, o.type()); + result+=">"; + } + else + { + result+=from_expr(ns, identifier, o)+", "; + + if(o_it->second.offset_is_set) + result+=integer2string(o_it->second.offset)+""; + else + result+="*"; + + result+=", "; + + if (o.type().id()==ID_unknown) + result+="*"; + else + { + result+=from_type(ns, identifier, o.type()); + } + + + result+=">"; + } + + out << result << std::endl; + + #if 0 + object_map_dt::validity_rangest::const_iterator vr = + object_map.read().validity_ranges.find(o_it->first); + + if (vr != object_map.read().validity_ranges.end()) + { + if (vr->second.size()==0) + std::cout << " Empty validity record" << std::endl; + else + for (object_map_dt::vrange_listt::const_iterator vit = + vr->second.begin(); + vit!=vr->second.end(); + vit++) + { + out << " valid at " << function_numbering[vit->function] << + " [" << vit->from << "," << vit->to << "]"; + if (from_function==vit->function && + from_target_index>=vit->from && + from_target_index<=vit->to) + out << " (*)"; + out << std::endl; + } + } + else + { + out << " No validity information" << std::endl; + } + #endif + + width+=result.size(); + + object_map_dt::const_iterator next(o_it); + next++; + + if(next!=object_map.read().end()) + { + out << "\n "; + } + } + + out << " } " << std::endl; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::to_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt value_set_fivrnst::to_expr(object_map_dt::const_iterator it) const +{ + const exprt &object=object_numbering[it->first]; + + if(object.id()=="invalid" || + object.id()=="unknown") + return object; + + object_descriptor_exprt od; + + od.object()=object; + + if(it->second.offset_is_set) + od.offset()=from_integer(it->second.offset, index_type()); + + od.type()=od.object().type(); + + return od; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::make_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrnst::make_union( + object_mapt &dest, + const object_mapt &src) const +{ + bool result=false; + + forall_objects(it, src.read()) + { + if(insert_to(dest, it)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::make_valid_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrnst::make_valid_union( + object_mapt &dest, + const object_mapt &src) const +{ + bool result=false; + + forall_valid_objects(it, src.read()) + { + if(insert_to(dest, it)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::copy_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::copy_objects( + object_mapt &dest, + const object_mapt &src) const +{ + forall_valid_objects(it, src.read()) + { + dest.write()[it->first] = it->second; + dest.write().validity_ranges[it->first].push_back( + object_map_dt::validity_ranget(from_function, + from_target_index, + from_target_index)); + } +} + +/*******************************************************************\ + +Function: value_set_fivrnst::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::get_value_set( + const exprt &expr, + std::list &value_set, + const namespacet &ns) const +{ + object_mapt object_map; + get_value_set(expr, object_map, ns); + + forall_objects(it, object_map.read()) + value_set.push_back(to_expr(it)); + + #if 0 + for(std::list::const_iterator it=value_set.begin(); it!=value_set.end(); it++) + std::cout << "GET_VALUE_SET: " << from_expr(ns, "", *it) << std::endl; + #endif +} + +/*******************************************************************\ + +Function: value_set_fivrnst::get_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::get_value_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const +{ + exprt tmp(expr); + simplify(tmp, ns); + + get_value_set_rec(tmp, dest, "", tmp.type(), ns); +} + +/*******************************************************************\ + +Function: value_set_fivrnst::get_value_set_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::get_value_set_rec( + const exprt &expr, + object_mapt &dest, + const std::string &suffix, + const typet &original_type, + const namespacet &ns) const +{ + #if 0 + std::cout << "GET_VALUE_SET_REC EXPR: " << expr << std::endl; + std::cout << "GET_VALUE_SET_REC SUFFIX: " << suffix << std::endl; + std::cout << std::endl; + #endif + + if(expr.id()=="unknown" || expr.id()=="invalid") + { + insert_from(dest, exprt("unknown", original_type)); + return; + } + else if(expr.id()=="index") + { + assert(expr.operands().size()==2); + + const typet &type=ns.follow(expr.op0().type()); + + assert(type.id()=="array" || + type.id()=="incomplete_array"); + + get_value_set_rec(expr.op0(), dest, "[]"+suffix, original_type, ns); + + return; + } + else if(expr.id()=="member") + { + assert(expr.operands().size()==1); + + if(expr.op0().is_not_nil()) + { + const typet &type=ns.follow(expr.op0().type()); + + assert(type.id()=="struct" || + type.id()=="union" || + type.id()=="incomplete_struct" || + type.id()=="incomplete_union"); + + const std::string &component_name= + expr.get_string("component_name"); + + get_value_set_rec(expr.op0(), dest, "."+component_name+suffix, + original_type, ns); + + return; + } + } + else if(expr.id()=="symbol") + { + // just keep a reference to the ident in the set + // (if it exists) + irep_idt ident = expr.get_string("identifier")+suffix; + + if(has_prefix(id2string(ident), alloc_adapter_prefix)) + { + insert_from(dest, expr, 0); + return; + } + else + { + valuest::const_iterator v_it=values.find(ident); + + if(v_it!=values.end()) + { + copy_objects(dest, v_it->second.object_map); + return; + } + } + } + else if(expr.id()=="if") + { + if(expr.operands().size()!=3) + throw "if takes three operands"; + + get_value_set_rec(expr.op1(), dest, suffix, + original_type, ns); + get_value_set_rec(expr.op2(), dest, suffix, + original_type, ns); + + return; + } + else if(expr.id()=="address_of") + { + if(expr.operands().size()!=1) + throw expr.id_string()+" expected to have one operand"; + + get_reference_set(expr.op0(), dest, ns); + + return; + } + else if(expr.id()=="dereference" || + expr.id()=="implicit_dereference") + { + object_mapt reference_set; + get_reference_set(expr, reference_set, ns); + const object_map_dt &object_map=reference_set.read(); + + if(object_map.begin()!=object_map.end()) + { + forall_objects(it1, object_map) + { + const exprt &object=object_numbering[it1->first]; + get_value_set_rec(object, dest, suffix, + original_type, ns); + } + + return; + } + } + else if(expr.id()=="reference_to") + { + object_mapt reference_set; + + get_reference_set(expr, reference_set, ns); + + const object_map_dt &object_map=reference_set.read(); + + if(object_map.begin()!=object_map.end()) + { + forall_objects(it, object_map) + { + const exprt &object=object_numbering[it->first]; + get_value_set_rec(object, dest, suffix, + original_type, ns); + } + + return; + } + } + else if(expr.is_constant()) + { + // check if NULL + if(expr.get("value")=="NULL" && + expr.type().id()=="pointer") + { + insert_from(dest, exprt("NULL-object", expr.type().subtype()), 0); + return; + } + } + else if(expr.id()=="typecast") + { + if(expr.operands().size()!=1) + throw "typecast takes one operand"; + + get_value_set_rec(expr.op0(), dest, suffix, + original_type, ns); + + return; + } + else if(expr.id()=="+" || expr.id()=="-") + { + if(expr.operands().size()<2) + throw expr.id_string()+" expected to have at least two operands"; + + if(expr.type().id()=="pointer") + { + // find the pointer operand + const exprt *ptr_operand=NULL; + + forall_operands(it, expr) + if(it->type().id()=="pointer") + { + if(ptr_operand==NULL) + ptr_operand=&(*it); + else + throw "more than one pointer operand in pointer arithmetic"; + } + + if(ptr_operand==NULL) + throw "pointer type sum expected to have pointer operand"; + + object_mapt pointer_expr_set; + get_value_set_rec(*ptr_operand, pointer_expr_set, "", + ptr_operand->type(), ns); + + forall_objects(it, pointer_expr_set.read()) + { + objectt object=it->second; + + if(object.offset_is_zero() && + expr.operands().size()==2) + { + if(expr.op0().type().id()!="pointer") + { + mp_integer i; + if(to_integer(expr.op0(), i)) + object.offset_is_set=false; + else + object.offset=(expr.id()=="+")? i : -i; + } + else + { + mp_integer i; + if(to_integer(expr.op1(), i)) + object.offset_is_set=false; + else + object.offset=(expr.id()=="+")? i : -i; + } + } + else + object.offset_is_set=false; + + insert_from(dest, it->first, object); + } + + return; + } + } + else if(expr.id()=="sideeffect") + { + const irep_idt &statement=expr.get("statement"); + + if(statement=="function_call") + { + // these should be gone + throw "unexpected function_call sideeffect"; + } + else if(statement=="malloc") + { + if(expr.type().id()!="pointer") + throw "malloc expected to return pointer type"; + + assert(suffix==""); + + const typet &dynamic_type= + static_cast(expr.find("#type")); + + dynamic_object_exprt dynamic_object(dynamic_type); + // let's make up a `unique' number for this object... + dynamic_object.instance()=from_integer( + (from_function << 16) | from_target_index, typet("natural")); + dynamic_object.valid()=true_exprt(); + + insert_from(dest, dynamic_object, 0); + return; + } + else if(statement=="cpp_new" || + statement=="cpp_new[]") + { + assert(suffix==""); + assert(expr.type().id()=="pointer"); + + dynamic_object_exprt dynamic_object(expr.type().subtype()); + // let's make up a unique number for this object... + dynamic_object.instance()=from_integer( + (from_function << 16) | from_target_index, typet("natural")); + dynamic_object.valid()=true_exprt(); + + insert_from(dest, dynamic_object, 0); + return; + } + } + else if(expr.id()=="struct") + { + // this is like a static struct object + insert_from(dest, address_of_exprt(expr), 0); + return; + } + else if(expr.id()=="with" || + expr.id()=="array_of" || + expr.id()=="array") + { + // these are supposed to be done by assign() + throw "unexpected value in get_value_set: "+expr.id_string(); + } + else if(expr.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(expr); + + const std::string name= + "value_set::dynamic_object"+ + dynamic_object.instance().get_string("value")+ + suffix; + + // look it up + valuest::const_iterator v_it=values.find(name); + + if(v_it!=values.end()) + { + copy_objects(dest, v_it->second.object_map); + return; + } + } + + insert_from(dest, exprt("unknown", original_type)); +} + +/*******************************************************************\ + +Function: value_set_fivrnst::dereference_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::dereference_rec( + const exprt &src, + exprt &dest) const +{ + // remove pointer typecasts + if(src.id()=="typecast") + { + assert(src.type().id()=="pointer"); + + if(src.operands().size()!=1) + throw "typecast expects one operand"; + + dereference_rec(src.op0(), dest); + } + else + dest=src; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::get_reference_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::get_reference_set( + const exprt &expr, + expr_sett &dest, + const namespacet &ns) const +{ + object_mapt object_map; + get_reference_set(expr, object_map, ns); + + forall_objects(it, object_map.read()) + dest.insert(to_expr(it)); +} + +/*******************************************************************\ + +Function: value_set_fivrnst::get_reference_set_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::get_reference_set_rec( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const +{ + #if 0 + std::cout << "GET_REFERENCE_SET_REC EXPR: " << from_expr(ns, "", expr) << std::endl; + #endif + + if(expr.id()=="symbol" || + expr.id()=="dynamic_object" || + expr.id()==ID_string_constant) + { + if(expr.type().id()=="array" && + expr.type().subtype().id()=="array") + insert_from(dest, expr); + else + insert_from(dest, expr, 0); + + return; + } + else if(expr.id()=="dereference" || + expr.id()=="implicit_dereference") + { + if(expr.operands().size()!=1) + throw expr.id_string()+" expected to have one operand"; + + get_value_set_rec(expr.op0(), dest, "", expr.op0().type(), ns); + + #if 0 + for(expr_sett::const_iterator it=value_set.begin(); it!=value_set.end(); it++) + std::cout << "VALUE_SET: " << from_expr(ns, "", *it) << std::endl; + #endif + + return; + } + else if(expr.id()=="index") + { + if(expr.operands().size()!=2) + throw "index expected to have two operands"; + + const exprt &array=expr.op0(); + const exprt &offset=expr.op1(); + const typet &array_type=ns.follow(array.type()); + + assert(array_type.id()=="array" || + array_type.id()=="incomplete_array"); + + + object_mapt array_references; + get_reference_set(array, array_references, ns); + + const object_map_dt &object_map=array_references.read(); + + forall_objects(a_it, object_map) + { + const exprt &object=object_numbering[a_it->first]; + + if(object.id()=="unknown") + insert_from(dest, exprt("unknown", expr.type())); + else + { + exprt index_expr("index", expr.type()); + index_expr.operands().resize(2); + index_expr.op0()=object; + index_expr.op1()=gen_zero(index_type()); + + // adjust type? + if(ns.follow(object.type())!=array_type) + index_expr.make_typecast(array.type()); + + objectt o=a_it->second; + mp_integer i; + + if(offset.is_zero()) + { + } + else if(!to_integer(offset, i) && + o.offset_is_zero()) + o.offset=i; + else + o.offset_is_set=false; + + insert_from(dest, index_expr, o); + } + } + + return; + } + else if(expr.id()=="member") + { + const irep_idt &component_name=expr.get("component_name"); + + if(expr.operands().size()!=1) + throw "member expected to have one operand"; + + const exprt &struct_op=expr.op0(); + + object_mapt struct_references; + get_reference_set(struct_op, struct_references, ns); + + const object_map_dt &object_map=struct_references.read(); + + forall_objects(it, object_map) + { + const exprt &object=object_numbering[it->first]; + const typet &obj_type=ns.follow(object.type()); + + if(object.id()=="unknown") + insert_from(dest, exprt("unknown", expr.type())); + else if(object.id()=="dynamic_object" && + obj_type.id()!="struct" && + obj_type.id()!="union") + { + // we catch dynamic objects of the wrong type, + // to avoid non-integral typecasts. + insert_from(dest, exprt("unknown", expr.type())); + } + else + { + objectt o=it->second; + + exprt member_expr("member", expr.type()); + member_expr.copy_to_operands(object); + member_expr.set("component_name", component_name); + + // adjust type? + if(ns.follow(struct_op.type())!=ns.follow(object.type())) + member_expr.op0().make_typecast(struct_op.type()); + + insert_from(dest, member_expr, o); + } + } + + return; + } + else if(expr.id()=="if") + { + if(expr.operands().size()!=3) + throw "if takes three operands"; + + get_reference_set_rec(expr.op1(), dest, ns); + get_reference_set_rec(expr.op2(), dest, ns); + return; + } + + insert_from(dest, exprt("unknown", expr.type())); +} + +/*******************************************************************\ + +Function: value_set_fivrnst::assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::assign( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool add_to_sets) +{ + #if 0 + std::cout << "ASSIGN LHS: " << lhs << std::endl; + std::cout << "ASSIGN LTYPE: " << ns.follow(lhs.type()) << std::endl; + std::cout << "ASSIGN RHS: " << from_expr(ns, "", rhs) << std::endl; + #endif + + if(rhs.id()=="if") + { + if(rhs.operands().size()!=3) + throw "if takes three operands"; + + assign(lhs, rhs.op1(), ns, add_to_sets); + assign(lhs, rhs.op2(), ns, true); + return; + } + + const typet &type=ns.follow(lhs.type()); + + if(type.id()=="struct" || + type.id()=="union") + { + const struct_typet &struct_type=to_struct_type(type); + + unsigned no=0; + + for(struct_typet::componentst::const_iterator + c_it=struct_type.components().begin(); + c_it!=struct_type.components().end(); + c_it++, no++) + { + const typet &subtype=c_it->type(); + const irep_idt &name=c_it->get("name"); + + // ignore methods + if(subtype.id()=="code") continue; + + exprt lhs_member("member", subtype); + lhs_member.set("component_name", name); + lhs_member.copy_to_operands(lhs); + + exprt rhs_member; + + if(rhs.id()=="unknown" || + rhs.id()=="invalid") + { + rhs_member=exprt(rhs.id(), subtype); + } + else + { + if (!base_type_eq(rhs.type(), type, ns)) + { + std::cout << "RHS: " << rhs.type() << std::endl; + std::cout << "LHS: " << type << std::endl; + } + + assert(base_type_eq(rhs.type(), type, ns)); + + if(rhs.id()=="struct" || + rhs.id()=="constant") + { + assert(nofirst]; + + if(object.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(object); + + if(dynamic_object.valid().is_true()) + to_mark.insert(dynamic_object.instance()); + } + } + + // mark these as 'may be invalid' + // this, unfortunately, destroys the sharing + for(valuest::iterator v_it=values.begin(); + v_it!=values.end(); + v_it++) + { + object_mapt new_object_map; + + const object_map_dt &old_object_map= + v_it->second.object_map.read(); + + bool changed=false; + + forall_valid_objects(o_it, old_object_map) + { + const exprt &object=object_numbering[o_it->first]; + + if(object.id()=="dynamic_object") + { + const exprt &instance= + to_dynamic_object_expr(object).instance(); + + if(to_mark.count(instance)==0) + set(new_object_map, o_it); + else + { + // adjust + objectt o=o_it->second; + exprt tmp(object); + to_dynamic_object_expr(tmp).valid()=exprt("unknown"); + insert_to(new_object_map, tmp, o); + changed=true; + } + } + else + set(new_object_map, o_it); + } + + if(changed) + { + entryt &temp_entry = get_temporary_entry(v_it->second.identifier, + v_it->second.suffix); + temp_entry.object_map=new_object_map; + } + } +} + +/*******************************************************************\ + +Function: value_set_fivrnst::assign_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::assign_rec( + const exprt &lhs, + const object_mapt &values_rhs, + const std::string &suffix, + const namespacet &ns, + bool add_to_sets) +{ + #if 0 + std::cout << "ASSIGN_REC LHS: " << lhs << std::endl; + std::cout << "ASSIGN_REC SUFFIX: " << suffix << std::endl; + + for(object_map_dt::const_iterator it=values_rhs.read().begin(); + it!=values_rhs.read().end(); it++) + std::cout << "ASSIGN_REC RHS: " << to_expr(it) << std::endl; + #endif + + if(lhs.id()=="symbol") + { + const irep_idt &identifier=lhs.get("identifier"); + + if(has_prefix(id2string(identifier), + "value_set::dynamic_object") || + has_prefix(id2string(identifier), + "value_set::return_value") || + values.find(id2string(identifier)+suffix)!=values.end()) + // otherwise we don't track this value + { + entryt &temp_entry = get_temporary_entry(identifier, suffix); + + if(add_to_sets) + { + entryt &state_entry = get_entry(identifier, suffix); + make_valid_union(temp_entry.object_map, state_entry.object_map); + } + + make_union(temp_entry.object_map, values_rhs); + } + } + else if(lhs.id()=="dynamic_object") + { + const dynamic_object_exprt &dynamic_object= + to_dynamic_object_expr(lhs); + + const std::string name= + "value_set::dynamic_object"+ + dynamic_object.instance().get_string("value"); + + entryt &temp_entry = get_temporary_entry(name, suffix); + + if(add_to_sets) + { + entryt &state_entry = get_entry(name, suffix); + make_valid_union(temp_entry.object_map, state_entry.object_map); + } + + make_union(temp_entry.object_map, values_rhs); + } + else if(lhs.id()=="dereference" || + lhs.id()=="implicit_dereference") + { + if(lhs.operands().size()!=1) + throw lhs.id_string()+" expected to have one operand"; + + object_mapt reference_set; + get_reference_set(lhs, reference_set, ns); + + forall_objects(it, reference_set.read()) + { + const exprt &object=object_numbering[it->first]; + + if(object.id()!="unknown") + assign_rec(object, values_rhs, suffix, ns, add_to_sets); + } + } + else if(lhs.id()=="index") + { + if(lhs.operands().size()!=2) + throw "index expected to have two operands"; + + const typet &type=ns.follow(lhs.op0().type()); + + assert(type.id()=="array" || type.id()=="incomplete_array"); + + assign_rec(lhs.op0(), values_rhs, "[]"+suffix, ns, add_to_sets); + } + else if(lhs.id()=="member") + { + if(lhs.operands().size()!=1) + throw "member expected to have one operand"; + + if(lhs.op0().is_nil()) return; + + const std::string &component_name=lhs.get_string("component_name"); + + const typet &type=ns.follow(lhs.op0().type()); + + assert(type.id()=="struct" || + type.id()=="union" || + type.id()=="incomplete_struct" || + type.id()=="incomplete_union"); + + assign_rec(lhs.op0(), values_rhs, "."+component_name+suffix, + ns, add_to_sets); + } + else if(lhs.id()=="valid_object" || + lhs.id()=="dynamic_size" || + lhs.id()=="dynamic_type") + { + // we ignore this here + } + else if(lhs.id()==ID_string_constant) + { + // someone writes into a string-constant + // evil guy + } + else if(lhs.id()=="NULL-object") + { + // evil as well + } + else if(lhs.id()=="typecast") + { + const typecast_exprt &typecast_expr=to_typecast_expr(lhs); + + assign_rec(typecast_expr.op(), values_rhs, suffix, + ns, add_to_sets); + } + else if(lhs.id()=="zero_string" || + lhs.id()=="is_zero_string" || + lhs.id()=="zero_string_length") + { + // ignore + } + else if(lhs.id()=="byte_extract_little_endian" || + lhs.id()=="byte_extract_big_endian") + { + assert(lhs.operands().size()==2); + assign_rec(lhs.op0(), values_rhs, suffix, ns, true); + } + else + throw "assign NYI: `"+lhs.id_string()+"'"; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::do_function_call + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::do_function_call( + const irep_idt &function, + const exprt::operandst &arguments, + const namespacet &ns) +{ + const symbolt &symbol=ns.lookup(function); + + const code_typet &type=to_code_type(symbol.type); + const code_typet::argumentst &argument_types=type.arguments(); + + // these first need to be assigned to dummy, temporary arguments + // and only thereafter to the actuals, in order + // to avoid overwriting actuals that are needed for recursive + // calls + + // the assigned data must be valid on from! + unsigned old_to_function=to_function; + unsigned old_to_target_index=to_target_index; + + to_function=from_function; + to_target_index=from_target_index; + + for(unsigned i=0; iget_identifier(); + if(identifier=="") continue; + + add_var(identifier, ""); + + const exprt v_expr= + symbol_exprt("value_set::" + function.as_string() + "::" + + "argument$"+i2string(i), it->type()); + + exprt actual_lhs=symbol_exprt(identifier, it->type()); + assign(actual_lhs, v_expr, ns, true); + i++; + } +} + +/*******************************************************************\ + +Function: value_set_fivrnst::do_end_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::do_end_function( + const exprt &lhs, + const namespacet &ns) +{ + if(lhs.is_nil()) return; + + irep_idt rvs = std::string("value_set::return_value") + + i2string(from_function); + add_var(rvs, ""); + symbol_exprt rhs(rvs, lhs.type()); + + assign(lhs, rhs, ns); +} + +/*******************************************************************\ + +Function: value_set_fivrnst::apply_code + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void value_set_fivrnst::apply_code( + const exprt &code, + const namespacet &ns) +{ + const irep_idt &statement=code.get("statement"); + + if(statement=="block") + { + forall_operands(it, code) + apply_code(*it, ns); + } + else if(statement=="function_call") + { + // shouldn't be here + assert(false); + } + else if(statement=="assign" || + statement=="init") + { + if(code.operands().size()!=2) + throw "assignment expected to have two operands"; + + assign(code.op0(), code.op1(), ns); + } + else if(statement=="decl") + { + if(code.operands().size()!=1) + throw "decl expected to have one operand"; + + const exprt &lhs=code.op0(); + + if(lhs.id()!="symbol") + throw "decl expected to have symbol on lhs"; + + assign(lhs, exprt("invalid", lhs.type()), ns); + } + else if(statement=="specc_notify" || + statement=="specc_wait") + { + // ignore, does not change variables + } + else if(statement=="expression") + { + // can be ignored, we don't expect sideeffects here + } + else if(statement=="cpp_delete" || + statement=="cpp_delete[]") + { + // does nothing + } + else if(statement=="free") + { + // this may kill a valid bit + + if(code.operands().size()!=1) + throw "free expected to have one operand"; + + do_free(code.op0(), ns); + } + else if(statement=="lock" || statement=="unlock") + { + // ignore for now + } + else if(statement=="asm") + { + // ignore for now, probably not safe + } + else if(statement=="nondet") + { + // doesn't do anything + } + else if(statement=="printf") + { + // doesn't do anything + } + else if(statement=="return") + { + // this is turned into an assignment + if(code.operands().size()==1) + { + irep_idt rvs = std::string("value_set::return_value") + + i2string(from_function); + add_var(rvs, ""); + symbol_exprt lhs(rvs, code.op0().type()); + assign(lhs, code.op0(), ns); + } + } + else + { + std::cerr << code.pretty() << std::endl; + throw "value_set_fivrnst: unexpected statement: "+id2string(statement); + } +} + +/*******************************************************************\ + +Function: value_set_fivrnst::insert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrnst::insert_to( + object_mapt &dest, + unsigned n, + const objectt &object) const +{ + object_map_dt &map = dest.write(); + if(map.find(n)==map.end()) + { +// std::cout << "NEW(" << n << "): " << object_numbering[n] << std::endl; + // new + map[n]=object; + map.set_valid_at(n, to_function, to_target_index); + return true; + } + else + { +// std::cout << "UPD " << n << std::endl; + objectt &old=map[n]; + + bool res = map.set_valid_at(n, to_function, to_target_index); + + if(old.offset_is_set && object.offset_is_set) + { + if(old.offset==object.offset) + return res; + else + { + old.offset_is_set=false; + return true; + } + } + else if(!old.offset_is_set) + return res; + else + { + old.offset_is_set=false; + return true; + } + } +} + +/*******************************************************************\ + +Function: value_set_fivrnst::insert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrnst::insert_from( + object_mapt &dest, + unsigned n, + const objectt &object) const +{ + object_map_dt &map = dest.write(); + if(map.find(n)==map.end()) + { +// std::cout << "NEW(" << n << "): " << object_numbering[n] << std::endl; + // new + map[n]=object; + map.set_valid_at(n, from_function, from_target_index); + return true; + } + else + { +// std::cout << "UPD " << n << std::endl; + objectt &old=map[n]; + + bool res = map.set_valid_at(n, from_function, from_target_index); + + if(old.offset_is_set && object.offset_is_set) + { + if(old.offset==object.offset) + return res; + else + { + old.offset_is_set=false; + return true; + } + } + else if(!old.offset_is_set) + return res; + else + { + old.offset_is_set=false; + return true; + } + } +} + +/*******************************************************************\ + +Function: value_set_fivrnst::object_map_dt::set_valid_at + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrnst::object_map_dt::set_valid_at( + unsigned inx, + unsigned f, + unsigned line) +{ + if(is_valid_at(inx, f, line)) return false; + + vrange_listt &ranges = validity_ranges[inx]; + vrange_listt::iterator it=ranges.begin(); + + while(it->function!=f && it!=ranges.end()) it++; // ffw to function block + + for(; + it!=ranges.end() && it->function==f && it->from <= line; + it++) + { + if(it->function==f) + { + if( line == it->to+1) + { + it->to++; + + // by any chance: does the next one connect to this one? + vrange_listt::iterator n_it = it; n_it++; + if(n_it!=ranges.end() && + it->function == n_it->function && + it->to+1 == n_it->from) + { + n_it->from = it->from; // connected! + it = ranges.erase(it); + } + return true; + } + } + } + + // it now points to either the end, + // the first of a new function block,or + // the first one that has from > line + if(it!=ranges.end()) + { + if(it->function==f) + { + if( line == it->from - 1) + { + it->from--; + + // by any chance: does the previous one connect to this one? + if(it!=ranges.begin()) + { + vrange_listt::iterator p_it = it; p_it--; + if(p_it->function == it->function && + p_it->to+1 == it->from) + { + p_it->to = it->to; // connected! + it = ranges.erase(it); + } + } + return true; + } + } + } + + // none matched + validity_ranget insr(f, line, line); + ranges.insert(it, insr); + + return true; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::object_map_dt::is_valid_at + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrnst::object_map_dt::is_valid_at( + unsigned inx, + unsigned f, + unsigned line) const +{ + #if 0 + std::cout << "IS_VALID_AT: " << inx << ", " << f << ", line " << line << + std::endl; + #endif + + validity_rangest::const_iterator vrs = validity_ranges.find(inx); + if(vrs!=validity_ranges.end()) + { + const vrange_listt &ranges = vrs->second; + + object_map_dt::vrange_listt::const_iterator it = ranges.begin(); + + while(it->function!=f && + it!=ranges.end()) it++; // ffw to function block + + for(; + it!=ranges.end() && it->function==f && it->from<=line ; + it++) + if(it->contains(f, line)) return true; + } + + return false; +} + +/*******************************************************************\ + +Function: value_set_fivrnst::handover + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool value_set_fivrnst::handover(void) +{ + bool changed=false; + + for (valuest::iterator it=values.begin(); + it!=values.end(); + it++) + { + object_mapt &state_map=it->second.object_map; + + irep_idt ident = id2string(it->second.identifier)+it->second.suffix; + + valuest::const_iterator t_it=temporary_values.find(ident); + + if(t_it==temporary_values.end()) + { +// std::cout << "OLD VALUES FOR: " << ident << std::endl; + Forall_valid_objects(o_it, state_map.write()) + { +// std::cout << "STILL VALID: " << to_expr(o_it) << std::endl; + if(state_map.write().set_valid_at(o_it->first, + to_function, to_target_index)) + changed = true; + } + } + else + { +// std::cout << "NEW VALUES FOR: " << ident << std::endl; + if(make_union(state_map, t_it->second.object_map)) + changed = true; + } + } + + temporary_values.clear(); + + return changed; +} diff --git a/src/pointer-analysis/value_set_fivrns.h b/src/pointer-analysis/value_set_fivrns.h new file mode 100644 index 00000000000..6c46b381709 --- /dev/null +++ b/src/pointer-analysis/value_set_fivrns.h @@ -0,0 +1,360 @@ +/*******************************************************************\ + +Module: Value Set (Flow Insensitive, Validity Regions) + +Author: Daniel Kroening, kroening@kroening.com + CM Wintersteiger + +\*******************************************************************/ + +#ifndef __CPROVER_VALUE_SET_FIVRNS_H_ +#define __CPROVER_VALUE_SET_FIVRNS_H_ + +#include +#include +#include + +#include "object_numbering.h" + +class value_set_fivrnst +{ +public: + value_set_fivrnst() + { + } + + unsigned to_function, from_function; + unsigned to_target_index, from_target_index; + static object_numberingt object_numbering; + static hash_numbering function_numbering; + + void set_from(const irep_idt& function, unsigned inx) + { + from_function = function_numbering.number(function); + from_target_index = inx; + } + + void set_to(const irep_idt &function, unsigned inx) + { + to_function = function_numbering.number(function); + to_target_index = inx; + } + + typedef irep_idt idt; + + static const std::string &id2string(const idt &id) + { + #ifdef USE_DSTRING + return id.as_string(); + #else + return id; + #endif + } + + class objectt + { + public: + objectt() : + offset_is_set(false) + { + } + + explicit objectt(const mp_integer &_offset): + offset(_offset), + offset_is_set(true) + { + } + + mp_integer offset; + bool offset_is_set; + bool offset_is_zero() const + { return offset_is_set && offset.is_zero(); } + }; + + class object_map_dt + { + public: + const static object_map_dt empty; + + typedef std::map objmapt; + objmapt objmap; + + typedef objmapt::const_iterator const_iterator; + typedef objmapt::iterator iterator; + + const_iterator find(unsigned k) { return objmap.find(k); } + iterator begin(void) { return objmap.begin(); } + const_iterator begin(void) const { return objmap.begin(); } + iterator end(void) { return objmap.end(); } + const_iterator end(void) const { return objmap.end(); } + size_t size(void) const { return objmap.size(); } + void clear(void) { objmap.clear(); validity_ranges.clear(); } + + objectt& operator[](unsigned k) { + return objmap[k]; + } + + // operator[] is the only way to insert something! + std::pair insert (const std::pair&) + { assert(false); } + iterator insert(iterator, const std::pair&) + { assert(false); } + + class validity_ranget + { + public: + unsigned function; + unsigned from, to; + + validity_ranget(void) : + function(0),from(0), to(0) {}; + + validity_ranget(unsigned fnc, unsigned f, unsigned t) : + function(fnc),from(f), to(t) {}; + + bool contains(unsigned f, unsigned line) const + { + return (function==f && from<=line && line<=to); + } + }; + + typedef std::list vrange_listt; + typedef std::map validity_rangest; + validity_rangest validity_ranges; + + bool set_valid_at(unsigned inx, unsigned f, unsigned line); + bool is_valid_at(unsigned inx, unsigned f, unsigned line) const; + }; + + exprt to_expr(object_map_dt::const_iterator it) const; + + typedef reference_counting object_mapt; + + void set(object_mapt &dest, object_map_dt::const_iterator it) const + { + dest.write()[it->first]=it->second; + } + + bool insert_to(object_mapt &dest, object_map_dt::const_iterator it) const + { + return insert_to(dest, it->first, it->second); + } + + bool insert_to(object_mapt &dest, const exprt &src) const + { + return insert_to(dest, object_numbering.number(src), objectt()); + } + + bool insert_to(object_mapt &dest, const exprt &src, const mp_integer &offset) const + { + return insert_to(dest, object_numbering.number(src), objectt(offset)); + } + + bool insert_to(object_mapt &dest, unsigned n, const objectt &object) const; + + bool insert_to(object_mapt &dest, const exprt &expr, const objectt &object) const + { + return insert_to(dest, object_numbering.number(expr), object); + } + + bool insert_from(object_mapt &dest, object_map_dt::const_iterator it) const + { + return insert_from(dest, it->first, it->second); + } + + bool insert_from(object_mapt &dest, const exprt &src) const + { + return insert_from(dest, object_numbering.number(src), objectt()); + } + + bool insert_from(object_mapt &dest, const exprt &src, const mp_integer &offset) const + { + return insert_from(dest, object_numbering.number(src), objectt(offset)); + } + + bool insert_from(object_mapt &dest, unsigned n, const objectt &object) const; + + bool insert_from(object_mapt &dest, const exprt &expr, const objectt &object) const + { + return insert_from(dest, object_numbering.number(expr), object); + } + + struct entryt + { + object_mapt object_map; + idt identifier; + std::string suffix; + + entryt() { } + + entryt(const idt &_identifier, const std::string _suffix): + identifier(_identifier), + suffix(_suffix) + { + } + }; + + typedef hash_set_cont expr_sett; + + static void add_objects(const entryt &src, expr_sett &dest); + + #ifdef USE_DSTRING + typedef std::map valuest; + #else + typedef hash_map_cont valuest; + #endif + + void get_value_set( + const exprt &expr, + std::list &expr_set, + const namespacet &ns) const; + + expr_sett &get( + const idt &identifier, + const std::string &suffix); + + void make_any() + { + values.clear(); + } + + void clear() + { + values.clear(); + } + + void add_var(const idt &id, const std::string &suffix) + { + get_entry(id, suffix); + } + + void add_var(const entryt &e) + { + get_entry(e.identifier, e.suffix); + } + + entryt &get_entry(const idt &id, const std::string &suffix) + { + return get_entry(entryt(id, suffix)); + } + + entryt &get_entry(const entryt &e) + { + std::string index=id2string(e.identifier)+e.suffix; + + std::pair r= + values.insert(std::pair(index, e)); + + return r.first->second; + } + + entryt &get_temporary_entry(const idt &id, const std::string &suffix) + { + std::string index=id2string(id)+suffix; + return temporary_values[index]; + } + + void add_vars(const std::list &vars) + { + for(std::list::const_iterator + it=vars.begin(); + it!=vars.end(); + it++) + add_var(*it); + } + + void output( + const namespacet &ns, + std::ostream &out) const; + + void output_entry( + const entryt &e, + const namespacet &ns, + std::ostream &out) const; + + valuest values; + valuest temporary_values; + + // true = added s.th. new + bool make_union( + object_mapt &dest, + const object_mapt &src) const; + + bool make_valid_union( + object_mapt &dest, + const object_mapt &src) const; + + void copy_objects( + object_mapt &dest, + const object_mapt &src) const; + + void apply_code( + const exprt &code, + const namespacet &ns); + + bool handover(void); + + void assign( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool add_to_sets=false); + + void do_function_call( + const irep_idt &function, + const exprt::operandst &arguments, + const namespacet &ns); + + // edge back to call site + void do_end_function( + const exprt &lhs, + const namespacet &ns); + + void get_reference_set( + const exprt &expr, + expr_sett &expr_set, + const namespacet &ns) const; + +protected: + void get_value_set_rec( + const exprt &expr, + object_mapt &dest, + const std::string &suffix, + const typet &original_type, + const namespacet &ns) const; + + void get_value_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const; + + void get_reference_set( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const + { + get_reference_set_rec(expr, dest, ns); + } + + void get_reference_set_rec( + const exprt &expr, + object_mapt &dest, + const namespacet &ns) const; + + void dereference_rec( + const exprt &src, + exprt &dest) const; + + void assign_rec( + const exprt &lhs, + const object_mapt &values_rhs, + const std::string &suffix, + const namespacet &ns, + bool add_to_sets); + + void do_free( + const exprt &op, + const namespacet &ns); +}; + +#endif /*__CPROVER_VALUE_SET_FIVR_H_*/ diff --git a/src/pointer-analysis/value_sets.h b/src/pointer-analysis/value_sets.h new file mode 100644 index 00000000000..3b992a3d96c --- /dev/null +++ b/src/pointer-analysis/value_sets.h @@ -0,0 +1,38 @@ +/*******************************************************************\ + +Module: Value Set Propagation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_ANALYSIS_VALUE_SETS_H +#define CPROVER_POINTER_ANALYSIS_VALUE_SETS_H + +#include + +#include + +// an abstract base class + +class value_setst +{ +public: + value_setst() + { + } + + typedef std::list valuest; + + // this is not const to allow a lazy evaluation + virtual void get_values( + goto_programt::const_targett l, + const exprt &expr, + valuest &dest)=0; + + virtual ~value_setst() + { + } +}; + +#endif diff --git a/src/solvers/Makefile b/src/solvers/Makefile new file mode 100644 index 00000000000..017d388dfd9 --- /dev/null +++ b/src/solvers/Makefile @@ -0,0 +1,135 @@ +include ../config.inc + +all: solvers$(LIBEXT) + +ifneq ($(CHAFF),) + CHAFF_SRC=sat/satcheck_zchaff.cpp sat/satcheck_zcore.cpp + CHAFF_INCLUDE=-I $(CHAFF) + CHAFF_LIB=$(CHAFF)/libsat.a +endif + +ifneq ($(BOOLEFORCE),) + BOOLEFORCE_SRC=sat/satcheck_booleforce.cpp + BOOLEFORCE_INCLUDE=-I $(BOOLEFORCE) + BOOLEFORCE_LIB=$(BOOLEFORCE)/libbooleforce.a +endif + +ifneq ($(Z3),) + Z3_SRC=z3/z3_capi.cpp z3/z3_conv.cpp z3/z3_prop.cpp z3/z3_dec.cpp + Z3_INCLUDE=-I $(Z3) + Z3_LIB= + CXXFLAGS += -DHAVE_Z3 +endif + +ifneq ($(BOOLECTOR),) + BOOLECTOR_SRC=boolector/boolector_get.cpp \ + boolector/boolector_prop.cpp boolector/boolector_dec.cpp + BOOLECTOR_INCLUDE=-I $(BOOLECTOR) + BOOLECTOR_LIB=$(BOOLECTOR)/lib/libboolector.a + CXXFLAGS += -DHAVE_BOOLECTOR +endif + +ifneq ($(MINISAT),) + MINISAT_SRC=sat/satcheck_minisat.cpp + MINISAT_INCLUDE=-I $(MINISAT) + MINISAT_LIB=$(MINISAT)/Solver$(OBJEXT) $(MINISAT)/Proof$(OBJEXT) $(MINISAT)/File$(OBJEXT) + CXXFLAGS += -DHAVE_MINISAT +endif + +ifneq ($(MINISAT2),) + MINISAT2_SRC=sat/satcheck_minisat2.cpp + MINISAT2_INCLUDE=-I $(MINISAT2) + MINISAT2_LIB=$(MINISAT2)/simp/SimpSolver$(OBJEXT) $(MINISAT2)/core/Solver$(OBJEXT) + CXXFLAGS += -DHAVE_MINISAT2 -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +endif + +ifneq ($(SMVSAT),) + SMVSAT_SRC=sat/satcheck_smvsat.cpp + SMVSAT_INCLUDE=-I $(SMVSAT)/include + SMVSAT_LIB=$(SMVSAT)/lib/libsmvsat.a +endif + +ifneq ($(SQUOLEM2),) + SQUOLEM2_SRC=qbf/qbf_squolem.cpp qbf/qbf_squolem_core.cpp + SQUOLEM2_INCLUDE=-I $(SQUOLEM2) + SQUOLEM2_LIB=-L $(SQUOLEM2) -lsquolem2 +endif + +ifneq ($(CUDD),) + CUDD_SRC=qbf/qbf_bdd_core.cpp qbf/qbf_skizzo_core.cpp + CUDD_INCLUDE=-I $(CUDD)/include + CUDD_LIB=-L $(CUDD)/cudd -L $(CUDD)/util -L $(CUDD)/mtr \ + -L $(CUDD)/st -L $(CUDD)/obj -L $(CUDD)/dddmp \ + -L $(CUDD)/epd -lobj -lcudd -lmtr -lst -lutil -lepd -ldddmp +endif + +SRC = $(CHAFF_SRC) $(BOOLEFORCE_SRC) $(MINISAT_SRC) $(MINISAT2_SRC) \ + $(SMVSAT_SRC) $(SQUOLEM2_SRC) $(CUDD_SRC) $(BOOLECTOR_SRC) \ + $(Z3_SRC) \ + sat/cnf.cpp sat/dimacs_cnf.cpp sat/cnf_clause_list.cpp \ + sat/pbs_dimacs_cnf.cpp sat/read_dimacs_cnf.cpp \ + sat/resolution_proof.cpp sat/satcheck.cpp \ + qbf/qdimacs_cnf.cpp qbf/qbf_quantor.cpp \ + qbf/qbf_skizzo.cpp qbf/qdimacs_core.cpp qbf/qbf_qube.cpp \ + qbf/qbf_qube_core.cpp \ + prop/prop.cpp prop/prop_conv.cpp prop/prop_conv_store.cpp \ + prop/aig_formula.cpp \ + prop/aig.cpp prop/aig_prop.cpp \ + cvc/cvc_prop.cpp cvc/cvc_conv.cpp cvc/cvc_dec.cpp \ + smt1/smt1_dec.cpp smt1/smt1_prop.cpp smt1/smt1_conv.cpp \ + smt2/smt2_dec.cpp smt2/smt2_prop.cpp smt2/smt2_conv.cpp \ + dplib/dplib_conv.cpp dplib/dplib_dec.cpp dplib/dplib_prop.cpp \ + flattening/equality.cpp flattening/arrays.cpp \ + flattening/functions.cpp flattening/sat_minimizer.cpp \ + flattening/boolbv_width.cpp flattening/boolbv.cpp \ + flattening/boolbv_constraint_select_one.cpp \ + flattening/bv_pointers.cpp flattening/bv_utils.cpp \ + flattening/boolbv_abs.cpp flattening/boolbv_with.cpp \ + flattening/boolbv_typecast.cpp flattening/boolbv_index.cpp \ + flattening/boolbv_member.cpp flattening/boolbv_if.cpp \ + flattening/boolbv_byte_extract.cpp flattening/boolbv_add_sub.cpp \ + flattening/boolbv_mult.cpp flattening/boolbv_constant.cpp \ + flattening/boolbv_extractbit.cpp flattening/boolbv_bv_rel.cpp \ + flattening/boolbv_shift.cpp flattening/boolbv_case.cpp \ + flattening/boolbv_cond.cpp flattening/boolbv_concatenation.cpp \ + flattening/boolbv_div.cpp flattening/boolbv_mod.cpp \ + flattening/boolbv_extractbits.cpp flattening/boolbv_replication.cpp \ + flattening/boolbv_reduction.cpp flattening/boolbv_overflow.cpp \ + flattening/boolbv_get.cpp flattening/boolbv_bitwise.cpp \ + flattening/boolbv_equality.cpp flattening/boolbv_unary_minus.cpp \ + flattening/boolbv_ieee_float_rel.cpp flattening/pointer_logic.cpp \ + flattening/boolbv_quantifier.cpp flattening/boolbv_struct.cpp \ + flattening/boolbv_byte_update.cpp flattening/boolbv_array_of.cpp \ + flattening/boolbv_map.cpp flattening/boolbv_type.cpp \ + flattening/boolbv_union.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) + +ifneq ($(SQUOLEM2),) + CXXFLAGS += -DHAVE_QBF_CORE +else +ifneq ($(CUDD),) + CXXFLAGS += -DHAVE_QBF_CORE +endif +endif + +ifdef MODULE_FLOATBV + CXXFLAGS += -DHAVE_FLOATBV + OBJ += ../floatbv/floatbv$(LIBEXT) +endif + +INCLUDES= -I .. \ + $(CHAFF_INCLUDE) $(BOOLEFORCE_INCLUDE) $(MINISAT_INCLUDE) $(MINISAT2_INCLUDE) \ + $(SMVSAT_INCLUDE) $(SQUOLEM2_INC) $(CUDD_INCLUDE) $(BOOLECTOR_INCLUDE) -I ../util + +include ../common + +############################################################################### + +solvers$(LIBEXT): $(OBJ) $(CHAFF_LIB) $(BOOLEFORCE_LIB) $(MINISAT_LIB) \ + $(MINISAT2_LIB) $(SMVSAT_LIB) $(SQUOLEM2_LIB) $(CUDD_LIB) \ + $(BOOLECTOR_LIB) + $(LINKLIB) + +clean: + rm -f $(OBJ) solvers$(LIBEXT) diff --git a/src/solvers/boolector/boolector_dec.cpp b/src/solvers/boolector/boolector_dec.cpp new file mode 100644 index 00000000000..1bf26caeef4 --- /dev/null +++ b/src/solvers/boolector/boolector_dec.cpp @@ -0,0 +1,2302 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "boolector_dec.h" + +extern "C" { +#include "include/boolector.h" +} + +#define DEBUG + +/*******************************************************************\ + +Function: boolector_dect::boolector_dect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +boolector_dect::boolector_dect():prop_convt(boolector_prop) +{ + number_variables_boolector=0; + set_to_counter=0; + boolector_prop.boolector_ctx = boolector_new(); + boolector_ctx = boolector_prop.boolector_ctx; + boolector_enable_model_gen(boolector_ctx); + + //boolector_enable_inc_usage(boolector_ctx); + //btorFile = fopen ( "btor.txt" , "wb" ); + //smtFile = fopen ( "smt.txt" , "wb" ); +} + +/*******************************************************************\ + +Function: boolector_dect::dec_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt boolector_dect::dec_solve() +{ + //status("Number of variables: " + integer2string(get_number_variables_boolector())); + + post_process(); + status("Solving with SMT solver Boolector"); + + return read_boolector_result(); +} + +/*******************************************************************\ + +Function: boolector_dect::read_assert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolector_dect::read_assert(std::istream &in, std::string &line) +{ +} + +/*******************************************************************\ + +Function: boolector_dect::read_boolector_result + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt boolector_dect::read_boolector_result() +{ + int result; + + result = check_boolector_properties(); + + + if (result==BOOLECTOR_UNSAT) + return D_UNSATISFIABLE; + else if (result==BOOLECTOR_SAT) + return D_SATISFIABLE; + + error("Unexpected result from Boolector"); + + return D_ERROR; +} + +/******************************************************************* + Function: boolector_dect::print_data_types + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +void boolector_dect::print_data_types(BtorExp* operand0, BtorExp* operand1) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::check_all_types + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool boolector_dect::check_all_types(const typet &type) +{ + if (type.id()=="bool" || type.id()=="signedbv" || type.id()=="unsignedbv" || + type.id()=="symbol" || type.id()=="empty" || type.id() == "fixedbv" || + type.id()=="array" || type.id()=="struct" || type.id()=="pointer" || + type.id()=="union") + { + return true; + } + + return false; +} + +/******************************************************************* + Function: boolector_dect::is_signed + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool boolector_dect::is_signed(const typet &type) +{ + if (type.id()=="signedbv" || type.id()=="fixedbv") + return true; + + return false; +} + +/******************************************************************* + Function: boolector_dect::check_boolector_properties + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +int boolector_dect::check_boolector_properties(void) +{ + return boolector_sat(boolector_ctx); +} + +/******************************************************************* + Function: boolector_dect::is_ptr + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool boolector_dect::is_ptr(const typet &type) +{ + return type.id()=="pointer" || type.id()=="reference"; +} + +/******************************************************************* + Function: boolector_dect::convert_pointer_offset + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_pointer_offset(unsigned bits) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} +/******************************************************************* + Function: boolector_dect::select_pointer_value + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::select_pointer_value(BtorExp* object, BtorExp* offset) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::create_boolector_array + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::create_boolector_array(const typet &type, std::string identifier) +{ + BtorExp *array; + unsigned int width = 0; + + if (type.subtype().id() == "bool") + { + array = boolector_array(boolector_ctx, 1, config.ansi_c.int_width, identifier.c_str()); + } + else if (type.subtype().id() == "fixedbv") + { + width = atoi(type.subtype().get("width").c_str()); + array = boolector_array(boolector_ctx, width, config.ansi_c.int_width, identifier.c_str()); + } + else if (type.subtype().id() == "signedbv" || type.subtype().id() == "unsignedbv") + { + width = atoi(type.subtype().get("width").c_str()); + array = boolector_array(boolector_ctx, width, config.ansi_c.int_width, identifier.c_str()); + } + else if (type.subtype().id() == "pointer") + { + array = create_boolector_array(type.subtype(), identifier); + } + + return array; +} + +/******************************************************************* + Function: boolector_dect::convert_identifier + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_identifier(const std::string &identifier, const typet &type) +{ + BtorExp* identifier_var; + unsigned int width = 0; + + width = atoi(type.get("width").c_str()); + + if (type.id()=="bool") + { + identifier_var = boolector_var(boolector_ctx, 1, identifier.c_str()); + } + else if (type.id()=="signedbv" || type.id()=="unsignedbv") + { + identifier_var = boolector_var(boolector_ctx, width, identifier.c_str()); + } + else if (type.id()== "fixedbv") + { + identifier_var = boolector_var(boolector_ctx, width, identifier.c_str()); + } + else if (type.id()=="array") + { + identifier_var = create_boolector_array(type, identifier); + } + else if (type.id()=="pointer") + { + identifier_var = convert_identifier(identifier, type.subtype()); + } + + ++number_variables_boolector; + + return identifier_var; +} + +/*******************************************************************\ + +Function: boolector_dect::convert_bv + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +BtorExp* boolector_dect::convert_bv(const exprt &expr) +{ + bv_cachet::const_iterator cache_result=bv_cache.find(expr); + if(cache_result!=bv_cache.end()) + { + //std::cout << "Cache hit on " << expr.pretty() << "\n"; + return cache_result->second; + } + + BtorExp* result; + + result = convert_boolector_expr(expr); + + // insert into cache + bv_cache.insert(std::pair(expr, result)); + + return result; +} + +/******************************************************************* + Function: boolector_dect::read_cache + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::read_cache(const exprt &expr) +{ + std::string symbol; + unsigned int size = pointer_cache.size(); + + symbol = expr.get_string("identifier"); + + for(pointer_cachet::const_iterator it = pointer_cache.begin(); + it != pointer_cache.end(); it++) + { + if (symbol.compare((*it).second.c_str())==0) + { + //std::cout << "Cache hit on: " << (*it).first.pretty() << "\n"; + return convert_bv((*it).first); + } + } + + return convert_bv(expr); +} + +/******************************************************************* + Function: boolector_dect::write_cache + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +void boolector_dect::write_cache(const exprt &expr) +{ + std::string symbol, identifier; + + identifier = expr.get_string("identifier"); + + for (std::string::const_iterator it = identifier.begin(); it + != identifier.end(); it++) + { + char ch = *it; + + if (isalnum(ch) || ch == '$' || ch == '?') + { + symbol += ch; + } + else if (ch == '#') + { + pointer_cache.insert(std::pair(expr, symbol)); + return; + } + } +} + +/******************************************************************* + Function: boolector_dect::convert_lt + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_lt(const exprt &expr) +{ + assert(expr.operands().size()==2); + BtorExp *constraint, *operand0, *operand1; + + if (expr.op0().type().id()=="array") + write_cache(expr.op0()); + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + + if (expr.op1().type().id()=="signedbv" || expr.op1().type().id()=="fixedbv") + constraint = boolector_slt(boolector_ctx, operand0, operand1); + else if (expr.op1().type().id()=="unsignedbv") + constraint = boolector_ult(boolector_ctx, operand0, operand1); + + return constraint; +} + +/******************************************************************* + Function: boolector_dect::convert_gt + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_gt(const exprt &expr) +{ + assert(expr.operands().size()==2); + BtorExp *constraint, *operand0, *operand1; + + if (expr.op0().type().id()=="array") + write_cache(expr.op0()); + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + + if (expr.op1().type().id()=="signedbv" || expr.op1().type().id()=="fixedbv") + constraint = boolector_sgt(boolector_ctx, operand0, operand1); + else if (expr.op1().type().id()=="unsignedbv") + constraint = boolector_ugt(boolector_ctx, operand0, operand1); + + return constraint; +} + + +/******************************************************************* + Function: boolector_dect::convert_le + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_le(const exprt &expr) +{ + assert(expr.operands().size()==2); + BtorExp *constraint, *operand0, *operand1; + + if (expr.op0().type().id()=="array") + write_cache(expr.op0()); + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + + if (expr.op1().type().id()=="signedbv" || expr.op1().type().id()=="fixedbv") + constraint = boolector_slte(boolector_ctx, operand0, operand1); + else if (expr.op1().type().id()=="unsignedbv") + constraint = boolector_ulte(boolector_ctx, operand0, operand1); + + return constraint; +} + +/******************************************************************* + Function: boolector_dect::convert_ge + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_ge(const exprt &expr) +{ + assert(expr.operands().size()==2); + BtorExp *constraint, *operand0, *operand1; + + if (expr.op0().type().id()=="array") + write_cache(expr.op0()); + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + + if (expr.op1().type().id()=="signedbv" || expr.op1().type().id()=="fixedbv") + constraint = boolector_sgte(boolector_ctx, operand0, operand1); + else if (expr.op1().type().id()=="unsignedbv") + constraint = boolector_ugte(boolector_ctx, operand0, operand1); + + return constraint; +} + + +/******************************************************************* + Function: boolector_dect::convert_eq + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_eq(const exprt &expr) +{ + assert(expr.operands().size()==2); + static BtorExp *constraint, *operand0, *operand1; + + if (expr.op0().type().id()=="array") + write_cache(expr.op0()); + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + + if (expr.id() == "=") + constraint = boolector_eq(boolector_ctx, operand0, operand1); + else + constraint = boolector_ne(boolector_ctx, operand0, operand1); + + return constraint; +} + +/******************************************************************* + Function: boolector_dect::convert_invalid_pointer + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_invalid(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::convert_same_object + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_same_object(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::convert_dynamic_object + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_dynamic_object(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::convert_overflow_sum + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_overflow_sum(const exprt &expr) +{ + assert(expr.operands().size()==2); + static BtorExp *bv, *operand[2]; + + operand[0] = convert_bv(expr.op0()); + operand[1] = convert_bv(expr.op1()); + + if (expr.op0().type().id()=="signedbv" && expr.op1().type().id()=="signedbv") + bv = boolector_saddo(boolector_ctx, operand[0], operand[1]); + else if (expr.op0().type().id()=="unsignedbv" && expr.op1().type().id()=="unsignedbv") + bv = boolector_uaddo(boolector_ctx, operand[0], operand[1]); + + return bv; //boolector_not(boolector_ctx, bv); +} + +/******************************************************************* + Function: boolector_dect::convert_overflow_sub + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_overflow_sub(const exprt &expr) +{ + assert(expr.operands().size()==2); + static BtorExp *bv, *operand[2]; + + operand[0] = convert_bv(expr.op0()); + operand[1] = convert_bv(expr.op1()); + + if (expr.op0().type().id()=="signedbv" && expr.op1().type().id()=="signedbv") + bv = boolector_ssubo(boolector_ctx, operand[0], operand[1]); + else if (expr.op0().type().id()=="unsignedbv" && expr.op1().type().id()=="unsignedbv") + bv = boolector_usubo(boolector_ctx, operand[0], operand[1]); + + return bv; //boolector_not(boolector_ctx, bv); +} + +/******************************************************************* + Function: boolector_dect::convert_overflow_mul + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_overflow_mul(const exprt &expr) +{ + assert(expr.operands().size()==2); + static BtorExp *bv, *operand[2]; + + operand[0] = convert_bv(expr.op0()); + operand[1] = convert_bv(expr.op1()); + + if (expr.op0().type().id()=="signedbv" && expr.op1().type().id()=="signedbv") + bv = boolector_smulo(boolector_ctx, operand[0], operand[1]); + else if (expr.op0().type().id()=="unsignedbv" && expr.op1().type().id()=="unsignedbv") + bv = boolector_umulo(boolector_ctx, operand[0], operand[1]); + + return bv; //boolector_not(boolector_ctx, bv); +} + +/******************************************************************* + Function: boolector_dect::convert_overflow_unary + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_overflow_unary(const exprt &expr) +{ + assert(expr.operands().size()==1); + static BtorExp *bv, *operand; + u_int i, width; + + operand = convert_bv(expr.op0()); + boolbv_get_width(expr.op0().type(), width); + + if (expr.op0().type().id()=="signedbv") + bv = boolector_slt(boolector_ctx, boolector_neg(boolector_ctx, operand), boolector_ones(boolector_ctx,width)); + else if (expr.op0().type().id()=="unsignedbv") + bv = boolector_slt(boolector_ctx, boolector_neg(boolector_ctx, operand), boolector_ones(boolector_ctx,width)); + + return bv; +} + +/******************************************************************* + Function: boolector_dect::convert_overflow_typecast + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_overflow_typecast(const exprt &expr) +{ + unsigned bits=atoi(expr.id().c_str()+18); + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()!=1) + throw "operand "+expr.id_string()+" takes one operand"; + + static BtorExp *bv, *operand[3], *mid, *overflow[2], *tmp, *minus_one, *two; + u_int i, result=1, width; + std::string value; + + boolbv_get_width(expr.op0().type(), width); + + if(bits>=width || bits==0) + throw "overflow-typecast got wrong number of bits"; + + assert(bits <= 32); + + for(i=0; i") + constraint = convert_gt(expr); + else if (expr.id() == "<=") + constraint = convert_le(expr); + else if (expr.id() == ">=") + constraint = convert_ge(expr); + else if (expr.id() == "overflow-+") + constraint = convert_overflow_sum(expr); + else if (expr.id() == "overflow--") + constraint = convert_overflow_sub(expr); + else if (expr.id() == "overflow-*") + constraint = convert_overflow_mul(expr); + else if (expr.id() == "overflow-unary-") + constraint = convert_overflow_unary(expr); + else if(has_prefix(expr.id_string(), "overflow-typecast-")) + constraint = convert_overflow_typecast(expr); + else + throw "convert_boolector_expr: " + expr.id_string() + " is not supported yet"; + + formula = boolector_iff(boolector_ctx, boolector_prop.boolector_literal(l), constraint); + boolector_assert(boolector_ctx, formula); + + //boolector_dump_btor(boolector_ctx, btorFile, formula); + //boolector_dump_smt(boolector_ctx, smtFile, formula); + + return l; +} + +/******************************************************************* + Function: boolector_dect::convert_rel + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_rel(const exprt &expr) +{ + assert(expr.operands().size()==2); + + BtorExp *result, *operand0, *operand1; + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + + const typet &op_type=expr.op0().type(); + + if (op_type.id()=="unsignedbv" || op_type.subtype().id()=="unsignedbv") + { + if(expr.id()=="<=") + result = boolector_ulte(boolector_ctx,operand0,operand1); + else if(expr.id()=="<") + result = boolector_ult(boolector_ctx,operand0,operand1); + else if(expr.id()==">=") + result = boolector_ugt(boolector_ctx,operand0,operand1); + else if(expr.id()==">") + result = boolector_ugte(boolector_ctx,operand0,operand1); + } + else if (op_type.id()=="signedbv" || op_type.id()=="fixedbv" || + op_type.subtype().id()=="signedbv" || op_type.subtype().id()=="fixedbv" ) + { + if(expr.id()=="<=") + result = boolector_slte(boolector_ctx,operand0,operand1); + else if(expr.id()=="<") + result = boolector_ult(boolector_ctx,operand0,operand1); + else if(expr.id()==">=") + result = boolector_sgt(boolector_ctx,operand0,operand1); + else if(expr.id()==">") + result = boolector_sgte(boolector_ctx,operand0,operand1); + } + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_typecast + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_typecast(const exprt &expr) +{ + assert(expr.operands().size()==1); + + BtorExp *result, *operand; + const exprt &op=expr.op0(); + + if(expr.type().id()=="signedbv" || expr.type().id()=="unsignedbv" + || expr.type().id()=="fixedbv") + { + unsigned to_width=atoi(expr.type().get("width").c_str()); + + if(op.type().id()=="signedbv" || op.type().id()=="fixedbv") + { + unsigned from_width=atoi(op.type().get("width").c_str()); + //std::cout << "from_width: " << from_width << "\n"; + //std::cout << "to_width: " << to_width << "\n"; + + if(from_width==to_width) + result = convert_bv(op); + else if(from_widthto_width) + { + operand = convert_bv(op); + result = boolector_slice(boolector_ctx, operand, (to_width-1), 0); + } + } + else if(op.type().id()=="unsignedbv") + { + unsigned from_width=atoi(op.type().get("width").c_str()); + //std::cout << "from_width: " << from_width << "\n"; + //std::cout << "to_width: " << to_width << "\n"; + + if(from_width==to_width) + { + result = convert_bv(op); + } + else if(from_widthto_width) + { + //std::cout << "slice from_width: " << from_width << "\n"; + //std::cout << "slice to_width: " << to_width << "\n"; + operand = convert_bv(op); + result = boolector_slice(boolector_ctx, operand, (to_width-1), 0); + } + } + else if (op.type().id()=="bool") + { + BtorExp *zero, *one; + unsigned width; + + boolbv_get_width(expr.type(), width); + if (expr.type().id()=="signedbv" || expr.type().id()=="fixedbv") + { + zero = boolector_int(boolector_ctx, 0, width); + one = boolector_int(boolector_ctx, 1, width); + } + else if (expr.type().id()=="unsignedbv") + { + zero = boolector_unsigned_int(boolector_ctx, 0, width); + one = boolector_unsigned_int(boolector_ctx, 1, width); + } + + operand = convert_bv(op); + result = boolector_cond(boolector_ctx, operand, one, zero); + } + } + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_struct + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_struct(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::convert_union + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_union(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::convert_boolector_pointer + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_boolector_pointer(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::convert_zero_string + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_zero_string(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + + BtorExp *zero_string; + + zero_string = create_boolector_array(expr.type(), "zero_string"); + + return zero_string; +} + +/******************************************************************* + Function: boolector_dect::convert_array + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_array(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif +} + +/******************************************************************* + Function: boolector_dect::convert_constant_array + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_constant_array(const exprt &expr) +{ + unsigned int width=0, i=0; + BtorExp *array_cte, *int_cte, *val_cte; + std::string value_cte, tmp, identifier; + char i_str[2]; + + width = atoi(expr.type().subtype().get("width").c_str()); + identifier = expr.get_string("identifier") + expr.type().subtype().get("width").c_str(); + + array_cte = create_boolector_array(expr.type(), identifier); + + i=0; + forall_operands(it, expr) + { + sprintf(i_str,"%i",i); + int_cte = boolector_int(boolector_ctx, atoi(i_str), config.ansi_c.int_width); + if (is_signed(it->type())) + value_cte = integer2string(binary2integer(it->get("value").c_str(), true),10); + else + value_cte = integer2string(binary2integer(it->get("value").c_str(), false),10); + + val_cte = boolector_int(boolector_ctx, atoi(value_cte.c_str()), width); + array_cte = boolector_write(boolector_ctx, array_cte, int_cte, val_cte); + ++i; + } + + return array_cte; +} + +/******************************************************************* + Function: boolector_dect::convert_constant + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_constant(const exprt &expr) +{ + BtorExp *const_var; + std::string value; + unsigned int width; + + if (is_signed(expr.type())) + value = integer2string(binary2integer(expr.get_string("value"), true),10); + else + value = integer2string(binary2integer(expr.get_string("value"), false),10); + + width = atoi(expr.type().get("width").c_str()); + + if (expr.type().id()=="bool") + { + if (expr.is_false()) + const_var = boolector_false(boolector_ctx); + else if (expr.is_true()) + const_var = boolector_true(boolector_ctx); + } + else if (expr.type().id() == "signedbv" || expr.type().id() == "c_enum") + { + const_var = boolector_int(boolector_ctx, atoi(value.c_str()), width); + } + else if (expr.type().id() == "unsignedbv") + { + const_var = boolector_unsigned_int(boolector_ctx, atoi(value.c_str()), width); + } + else if (expr.type().id()== "fixedbv") + { + const_var = boolector_int(boolector_ctx, atoi(value.c_str()), width); + } + else if (expr.type().id()== "array") + { + const_var = convert_constant_array(expr); + } + else if (expr.type().id()== "pointer") + { + width = atoi(expr.type().subtype().get("width").c_str()); + const_var = boolector_int(boolector_ctx, atoi(value.c_str()), width); + } + + return const_var; +} + +/******************************************************************* + Function: boolector_dect::convert_concatenation + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_concatenation(const exprt &expr) { + +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::convert_bitwise + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_bitwise(const exprt &expr) +{ + assert(expr.operands().size()==2); + + BtorExp *bitwise_var, *args[2]; + + args[0] = convert_bv(expr.op0()); + args[1] = convert_bv(expr.op1()); + + if(expr.id()=="bitand") + bitwise_var = boolector_and(boolector_ctx, args[0], args[1]); + else if(expr.id()=="bitor") + bitwise_var = boolector_or(boolector_ctx, args[0], args[1]); + else if(expr.id()=="bitxor") + bitwise_var = boolector_xor(boolector_ctx, args[0], args[1]); + else if (expr.id()=="bitnand") + bitwise_var = boolector_nand(boolector_ctx, args[0], args[1]); + else if (expr.id()=="bitnor") + bitwise_var = boolector_nor(boolector_ctx, args[0], args[1]); + else if (expr.id()=="bitnxor") + bitwise_var = boolector_xnor(boolector_ctx, args[0], args[1]); + + return bitwise_var; +} + +/******************************************************************* + Function: boolector_dect::convert_unary_minus + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_unary_minus(const exprt &expr) +{ + assert(expr.operands().size()==1); + + BtorExp *result; + + result = boolector_neg(boolector_ctx, convert_bv(expr.op0())); + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_if + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_if(const exprt &expr) +{ + assert(expr.operands().size()==3); + + BtorExp *result, *operand0, *operand1, *operand2; + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + operand2 = convert_bv(expr.op2()); + + result = boolector_cond(boolector_ctx, operand0, operand1, operand2); + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_logical_ops + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_logical_ops(const exprt &expr) +{ + assert(expr.type().id()=="bool"); + assert(expr.operands().size()>=1); + + u_int i=0, size; + + size=expr.operands().size(); + BtorExp *args[size], *result; + + if (size==1) + { + result = convert_bv(expr.op0()); + } + else + { + forall_operands(it, expr) + { + args[i] = convert_bv(*it); + + if (i>=1) + { + if(expr.id()=="and") + args[i] = boolector_and(boolector_ctx, args[i-1], args[i]); + else if(expr.id()=="or") + args[i] = boolector_or(boolector_ctx, args[i-1], args[i]); + else if(expr.id()=="xor") + args[i] = boolector_xor(boolector_ctx, args[i-1], args[i]); + } + + ++i; + } + + result = args[size-1]; + } + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_logical_not + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_logical_not(const exprt &expr) +{ + assert(expr.operands().size()==1); + + BtorExp *operand0; + + operand0 = convert_bv(expr.op0()); + + return boolector_not(boolector_ctx, operand0); +} + +/******************************************************************* + Function: boolector_dect::convert_equality + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_equality(const exprt &expr) +{ + assert(expr.operands().size()==2); + assert(expr.op0().type()==expr.op1().type()); + + BtorExp *result=0, *args[2]; + + args[0] = convert_bv(expr.op0()); + args[1] = convert_bv(expr.op1()); + + if (expr.id()=="=") + result = boolector_eq(boolector_ctx, args[0], args[1]); + else + result = boolector_ne(boolector_ctx, args[0], args[1]); + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_add + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_add(const exprt &expr) +{ + assert(expr.operands().size()>=2); + u_int i=0, size; + + size=expr.operands().size()+1; + BtorExp *args[size]; + + forall_operands(it, expr) + { + args[i] = convert_bv(*it); + + if (i==1) + args[size-1] = boolector_add(boolector_ctx, args[0], args[1]); + else if (i>1) + args[size-1] = boolector_add(boolector_ctx, args[size-1], args[i]); + ++i; + } + + return args[i]; +} + +/******************************************************************* + Function: boolector_dect::convert_sub + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_sub(const exprt &expr) +{ + assert(expr.operands().size()>=2); + u_int i=0, size; + + size=expr.operands().size()+1; + BtorExp *args[size]; + + forall_operands(it, expr) + { + args[i] = convert_bv(*it); + + if (i==1) + args[size-1] = boolector_sub(boolector_ctx, args[0], args[1]); + else if (i>1) + args[size-1] = boolector_sub(boolector_ctx, args[size-1], args[i]); + ++i; + } + + return args[i]; +} + +/******************************************************************* + Function: boolector_dect::convert_div + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_div(const exprt &expr) +{ + assert(expr.operands().size()==2); + + BtorExp *result, *args[2], *overflow, *zero; + + args[0] = convert_bv(expr.op0()); + args[1] = convert_bv(expr.op1()); + + if (expr.type().id()=="signedbv" || expr.type().id()=="fixedbv") + result = boolector_sdiv(boolector_ctx, args[0], args[1]); + else if (expr.type().id()=="unsignedbv") + result = boolector_udiv(boolector_ctx, args[0], args[1]); + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_mod + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_mod(const exprt &expr) +{ + assert(expr.operands().size()==2); + + BtorExp *result, *operand0, *operand1; + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + + if(expr.type().id()=="signedbv") + result = boolector_srem(boolector_ctx, operand0, operand1); + else if (expr.type().id()=="unsignedbv") + result = boolector_urem(boolector_ctx, operand0, operand1); + else if (expr.type().id()=="fixedbv") + throw "unsupported type for mod: "+expr.type().id_string(); + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_mul + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_mul(const exprt &expr) +{ + assert(expr.operands().size()>=2); + u_int i=0, size; + + size=expr.operands().size()+1; + BtorExp *args[size]; + + forall_operands(it, expr) + { + args[i] = convert_bv(*it); + + if (i==1) + args[size-1] = boolector_mul(boolector_ctx, args[0], args[1]); + else if (i>1) + args[size-1] = boolector_mul(boolector_ctx, args[size-1], args[i]); + ++i; + } + + return args[i]; +} + +/******************************************************************* + Function: boolector_dect::convert_pointer + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_pointer(const exprt &expr) +{ + assert(expr.operands().size()==1); + assert(expr.type().id()=="pointer"); + + BtorExp *result, *args[2]; + std::string symbol_name; + + if (expr.op0().id() == "index") + { + const exprt &object=expr.op0().operands()[0]; + const exprt &index=expr.op0().operands()[1]; + + args[0] = read_cache(object); + args[1] = convert_bv(index); + + result = boolector_read(boolector_ctx, args[0], args[1]); + } + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_array_of + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_array_of(const exprt &expr) +{ + BtorExp *index, *value, *array_of_var; + const array_typet &array_type_size=to_array_type(expr.type()); + std::string tmp; + unsigned int j, size, width; + + tmp = integer2string(binary2integer(array_type_size.size().get_string("value"), false),10); + size = atoi(tmp.c_str()); + width = atoi(expr.type().subtype().get("width").c_str()); + + if (expr.type().subtype().id()=="bool") + { + value = boolector_false(boolector_ctx); + array_of_var = boolector_array(boolector_ctx, 1, config.ansi_c.int_width,"ARRAY_OF(false)"); + } + else if (expr.type().subtype().id()=="signedbv" || expr.type().subtype().id()=="unsignedbv") + { + value = convert_bv(expr.op0()); + array_of_var = boolector_array(boolector_ctx, width, config.ansi_c.int_width,"ARRAY_OF(0)"); + } + else if (expr.type().subtype().id()=="fixedbv") + { + value = convert_bv(expr.op0()); + array_of_var = boolector_array(boolector_ctx, width, config.ansi_c.int_width, "ARRAY_OF(0l)"); + } + else if (expr.type().subtype().id()=="pointer") + { + const exprt &object=expr.op0().operands()[0]; + const exprt &index=expr.op0().operands()[1]; + + width = atoi(expr.op0().type().subtype().get("width").c_str()); + value = convert_bv(expr.op0()); + array_of_var = boolector_array(boolector_ctx, width, config.ansi_c.int_width, "&(ZERO_STRING())[0]"); + } + + if (size==0) + size=1; //update at leat the first element of the array of bool + + //update array + for (j=0; j=1); + BtorExp *result, *array, *index, *value; + + array = convert_bv(expr.op0()); + index = convert_bv(expr.op1()); + value = convert_bv(expr.op2()); + + result = boolector_write(boolector_ctx, array, index, value); + + return result; +} + +/******************************************************************* + Function: boolector_dect::convert_abs + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_abs(const exprt &expr) +{ + unsigned width; + std::string out; + + boolbv_get_width(expr.type(), width); + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()!=1) + throw "abs takes one operand"; + + const exprt &op0=expr.op0(); + static BtorExp *zero, *minus_one, *is_negative, *val_orig, *val_mul; + + out = "width: "+ width; + if (expr.type().id()=="signedbv" || expr.type().id()=="fixedbv") + zero = boolector_int(boolector_ctx, 0, width); + else if (expr.type().id()=="unsignedbv") + zero = boolector_unsigned_int(boolector_ctx, 0, width); + + minus_one = boolector_int(boolector_ctx, -1, width); + + val_orig = convert_bv(op0); + + if (expr.type().id()=="signedbv" || expr.type().id()=="fixedbv") + is_negative = boolector_slt(boolector_ctx, val_orig, zero); + + val_mul = boolector_mul(boolector_ctx, val_orig, minus_one); + + return boolector_cond(boolector_ctx, is_negative, val_mul, val_orig); +} + +/******************************************************************* + Function: boolector_dect::convert_bitnot + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_bitnot(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::convert_bitnot + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +unsigned int boolector_dect::convert_member_name(const exprt &lhs, const exprt &rhs) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + + +/******************************************************************* + Function: boolector_dect::convert_extractbit + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_extractbit(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif +} + +/******************************************************************* + Function: boolector_dect::convert_object + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_object(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: boolector_dect::select_pointer_offset + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::select_pointer_offset(const exprt &expr) +{ + assert(expr.operands().size()==1); + + BtorExp *offset; + + offset = convert_bv(expr.op0()); + + return offset; +} + +/******************************************************************* + Function: boolector_dect::convert_member + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_member(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + + +/******************************************************************* + Function: convert_invalid_pointer + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_invalid_pointer(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} + +/******************************************************************* + Function: convert_pointer_object + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_pointer_object(const exprt &expr) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + +} +/******************************************************************* + Function: boolector_dect::convert_boolector_expr + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +BtorExp* boolector_dect::convert_boolector_expr(const exprt &expr) +{ + if (expr.id() == "symbol") + return convert_identifier(expr.get_string("identifier"), expr.type()); + else if (expr.id() == "nondet_symbol") { + return convert_identifier("nondet$"+expr.get_string("identifier"), expr.type()); + } else if (expr.id() == "typecast") + return convert_typecast(expr); +#if 0 + else if (expr.id() == "struct") + return convert_struct(expr); + else if (expr.id() == "union") + return convert_union(expr); +#endif + else if (expr.id() == "constant") + return convert_constant(expr); + else if (expr.id() == "concatenation") + return convert_concatenation(expr); + else if (expr.id() == "bitand" || expr.id() == "bitor" || expr.id() == "bitxor" + || expr.id() == "bitnand" || expr.id() == "bitnor" || expr.id() == "bitnxor") + return convert_bitwise(expr); + else if (expr.id() == "bitnot") + return convert_bitnot(expr); + else if (expr.id() == "unary-") + return convert_unary_minus(expr); + else if (expr.id() == "if") + return convert_if(expr); + else if (expr.id() == "and" || expr.id() == "or" || expr.id() == "xor") + return convert_logical_ops(expr); + else if (expr.id() == "not") + return convert_logical_not(expr); + else if (expr.id() == "=" || expr.id() == "notequal") + return convert_equality(expr); + else if (expr.id() == "<=" || expr.id() == "<" || expr.id() == ">=" + || expr.id() == ">") + return convert_rel(expr); + else if (expr.id() == "+") + return convert_add(expr); + else if (expr.id() == "-") + return convert_sub(expr); + else if (expr.id() == "/") + return convert_div(expr); + else if (expr.id() == "mod") + return convert_mod(expr); + else if (expr.id() == "*") + return convert_mul(expr); + else if(expr.id()=="abs") + return convert_abs(expr); + else if (expr.id() == "address_of" || expr.id() == "implicit_address_of" + || expr.id() == "reference_to") + return convert_pointer(expr); + else if (expr.id() == "array_of") + return convert_array_of(expr); + else if (expr.id() == "index") + return convert_index(expr); + else if (expr.id() == "ashr" || expr.id() == "lshr" || expr.id() == "shl") + return convert_shift(expr); + else if (expr.id() == "with") + return convert_with(expr); + else if (expr.id() == "member") + return convert_member(expr); +#if 0 + else if (expr.id() == "invalid-pointer") + return convert_invalid_pointer(expr); +#endif + else if (expr.id()=="zero_string") + return convert_zero_string(expr); + else if (expr.id() == "pointer_offset") + return select_pointer_offset(expr); + else if (expr.id() == "pointer_object") + return convert_pointer_object(expr); +#if 0 + else if (expr.id() == "same-object") + return convert_object(expr); +#endif + else if (expr.id() == ID_string_constant) { + exprt tmp; + string2array(expr, tmp); + return convert_boolector_expr(tmp); + } else if (expr.id() == "extractbit") + return convert_extractbit(expr); + else if (expr.id() == "replication") { + assert(expr.operands().size()==2); + } else + throw "convert_boolector_expr: " + expr.id_string() + " is not supported yet"; +} + +/******************************************************************* + Function: boolector_dect::assign_boolector_expr + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool boolector_dect::assign_boolector_expr(const exprt expr) +{ + if (expr.op0().type().id() == "pointer" && expr.op0().type().subtype().id()=="code") + { + ignoring(expr); + return false; + } + else if (expr.op0().type().id() == "array" && expr.op0().type().subtype().id()=="struct") + { + ignoring(expr); + return false; + } + else if (expr.op0().type().id() == "pointer" && expr.op0().type().subtype().id()=="symbol") + { + ignoring(expr); + return false; + } + + return true; +} + + +/******************************************************************* + Function: boolector_dect::set_to + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +void boolector_dect::set_to(const exprt &expr, bool value) +{ + if(expr.type().id()!="bool") + { + std::string msg="prop_convt::set_to got " + "non-boolean expression:\n"; + msg+=expr.to_string(); + throw msg; + } + + bool boolean=true; + + forall_operands(it, expr) + if(it->type().id()!="bool") + { + boolean=false; + break; + } + + if(boolean && assign_boolector_expr(expr)) + { + if(expr.id()=="not") + { + if(expr.operands().size()==1) + { + set_to(expr.op0(), !value); + return; + } + } + else + { + if(value) + { + // set_to_true + + if(expr.id()=="and") + { + forall_operands(it, expr) + set_to_true(*it); + + return; + } + else if(expr.id()=="or") + { + if(expr.operands().size()>0) + { + bvt bv; + bv.reserve(expr.operands().size()); + + forall_operands(it, expr) + bv.push_back(convert(*it)); + prop.lcnf(bv); + return; + } + } + else if(expr.id()=="=>") + { + if(expr.operands().size()==2) + { + bvt bv; + bv.resize(2); + bv[0]=prop.lnot(convert(expr.op0())); + bv[1]=convert(expr.op1()); + prop.lcnf(bv); + return; + } + } + else if(expr.id()=="=") + { + if(!set_equality_to_true(expr)) + return; + } + } + else + { + // set_to_false + if(expr.id()=="=>") // !(a=>b) == (a && !b) + { + if(expr.operands().size()==2) + { + set_to_true(expr.op0()); + set_to_false(expr.op1()); + } + } + else if(expr.id()=="or") // !(a || b) == (!a && !b) + { + forall_operands(it, expr) + set_to_false(*it); + } + } + } + } + + // fall back to convert + prop.l_set_to(convert(expr), value); + + if (value && expr.id() == "and") + { + forall_operands(it, expr) + set_to(*it, true); + return; + } + + if (value && expr.is_true()) + return; + + + if (expr.id() == "=" && value) + { + assert(expr.operands().size()==2); + + BtorExp *formula, *operand0, *operand1; + + if (assign_boolector_expr(expr)) + { + + operand0 = convert_bv(expr.op0()); + operand1 = convert_bv(expr.op1()); + + formula = boolector_eq(boolector_ctx, operand0, operand1); + boolector_assert(boolector_ctx, formula); + //boolector_dump_btor(boolector_ctx, btorFile, formula); + //boolector_dump_smt(boolector_ctx, smtFile, formula); + } + } +} + +/******************************************************************* + Function: boolector_dect::get_number_variables_z + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +unsigned int boolector_dect::get_number_variables_boolector(void) +{ + return number_variables_boolector; +} + diff --git a/src/solvers/boolector/boolector_dec.h b/src/solvers/boolector/boolector_dec.h new file mode 100644 index 00000000000..cb9b742b804 --- /dev/null +++ b/src/solvers/boolector/boolector_dec.h @@ -0,0 +1,154 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#ifndef CPROVER_PROP_BOOLECTOR_DEC_H +#define CPROVER_PROP_BOOLECTOR_DEC_H + +#include + +#include +#include + +#include "boolector_prop.h" + +class boolector_prop_wrappert +{ +public: + boolector_prop_wrappert() { } + +protected: + boolector_propt boolector_prop; +}; + +class boolector_dect:protected boolector_prop_wrappert, public prop_convt +{ +public: + boolector_dect(); + + ~boolector_dect() + { + } + + unsigned int get_number_variables_boolector(); + int check_boolector_properties(); + // overloading + virtual decision_proceduret::resultt dec_solve(); + // overloading + virtual exprt get(const exprt &expr) const; + +protected: + virtual literalt convert_rest(const exprt &expr); + virtual void set_to(const exprt &expr, bool value); + + bool assign_boolector_expr(const exprt expr); + class BtorExp *convert_boolector_expr(const exprt &expr); + class BtorExp *convert_rel(const exprt &expr); + class BtorExp *convert_typecast(const exprt &expr); + class BtorExp *convert_struct(const exprt &expr); + class BtorExp *convert_union(const exprt &expr); + class BtorExp *convert_constant(const exprt &expr); + class BtorExp *convert_concatenation(const exprt &expr); + class BtorExp *convert_bitwise(const exprt &expr); + class BtorExp *convert_bitnot(const exprt &expr); + class BtorExp *convert_unary_minus(const exprt &expr); + class BtorExp *convert_if(const exprt &expr); + class BtorExp *convert_logical_ops(const exprt &expr); + class BtorExp *convert_logical_not(const exprt &expr); + class BtorExp *convert_equality(const exprt &expr); + class BtorExp *convert_add(const exprt &expr); + class BtorExp *convert_sub(const exprt &expr); + class BtorExp *convert_div(const exprt &expr); + class BtorExp *convert_mod(const exprt &expr); + class BtorExp *convert_mul(const exprt &expr); + class BtorExp *convert_pointer(const exprt &expr); + class BtorExp *convert_array_of(const exprt &expr); + class BtorExp *convert_array_of_array(const std::string identifier, const exprt &expr); + class BtorExp *convert_index(const exprt &expr); + class BtorExp *convert_shift(const exprt &expr); + class BtorExp *convert_with(const exprt &expr); + class BtorExp *convert_extractbit(const exprt &expr); + class BtorExp *convert_member(const exprt &expr); + unsigned int convert_member_name(const exprt &lhs, const exprt &rhs); + class BtorExp *convert_object(const exprt &expr); + class BtorExp *select_pointer_offset(const exprt &expr); + class BtorExp *convert_invalid_pointer(const exprt &expr); + class BtorExp *convert_pointer_object(const exprt &expr); + class BtorExp *convert_zero_string(const exprt &expr); + class BtorExp *convert_overflow_sum(const exprt &expr); + class BtorExp *convert_overflow_sub(const exprt &expr); + class BtorExp *convert_overflow_mul(const exprt &expr); + class BtorExp *convert_overflow_typecast(const exprt &expr); + class BtorExp *convert_overflow_unary(const exprt &expr); + class BtorExp *convert_boolector_pointer(const exprt &expr); + class BtorExp *convert_array(const exprt &expr); + class BtorExp *select_pointer_value(class BtorExp *object, class BtorExp *offset); + class BtorExp *convert_abs(const exprt &expr); + class BtorExp *convert_pointer_offset(unsigned bits); + + pointer_logict pointer_logic; + + typedef hash_map_cont pointer_cachet; + pointer_cachet pointer_cache; + + typedef hash_map_cont bv_cachet; + bv_cachet bv_cache; + + exprt bv_get_rec( + class BtorExp *bv, + std::vector &unknown, + const bool cache, + const typet &type) const; + + resultt read_boolector_result(); + void read_assert(std::istream &in, std::string &line); + +private: + bool is_ptr(const typet &type); + bool check_all_types(const typet &type); + bool is_signed(const typet &type); + bool is_set(); + void write_cache(const exprt &expr); + class BtorExp *convert_constant_array(const exprt &expr); + class BtorExp *convert_bv(const exprt &expr); + class BtorExp *convert_shift_constant(const exprt &expr); + class BtorExp *create_boolector_array(const typet &type, std::string identifier); + class BtorExp *read_cache(const exprt &expr); + class BtorExp *convert_eq(const exprt &expr); + class BtorExp *convert_ge(const exprt &expr); + class BtorExp *convert_le(const exprt &expr); + class BtorExp *convert_gt(const exprt &expr); + class BtorExp *convert_lt(const exprt &expr); + class BtorExp *convert_dynamic_object(const exprt &expr); + class BtorExp *convert_same_object(const exprt &expr); + class BtorExp *convert_invalid(const exprt &expr); + void create_boolector_context(); + class BtorExp *convert_identifier(const std::string &identifier, const typet &type); + void print_data_types(class BtorExp *operand0, class BtorExp *operand1); + int literal_flag; + unsigned int number_variables_boolector, set_to_counter, variable_number; + Btor *boolector_ctx; + + struct identifiert + { + typet type; + exprt value; + + identifiert() + { + type.make_nil(); + value.make_nil(); + } + }; + + typedef hash_map_cont + identifier_mapt; + + identifier_mapt identifier_map; +}; + +#endif diff --git a/src/solvers/boolector/boolector_get.cpp b/src/solvers/boolector/boolector_get.cpp new file mode 100644 index 00000000000..2d46bde1f45 --- /dev/null +++ b/src/solvers/boolector/boolector_get.cpp @@ -0,0 +1,231 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "boolector_dec.h" + +extern "C" { +#include "include/boolector.h" +} + +/*******************************************************************\ + +Function: boolector_convt::get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt boolector_dect::get(const exprt &expr) const +{ + if(expr.id()=="symbol" || + expr.id()=="nondet_symbol") + { + std::string tmp; + std::vector unknown; + BtorExp *bv; + + bv_cachet::const_iterator cache_result=bv_cache.find(expr); + if(cache_result!=bv_cache.end()) + { + bv = cache_result->second; + return bv_get_rec(bv, unknown, true, expr.type()); + } + + return bv_get_rec(bv, unknown, false, expr.type()); + } + else if(expr.id()=="constant") + return expr; + + return expr; +} + +/*******************************************************************\ + +Function: boolector_convt::bv_get_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt boolector_dect::bv_get_rec( + BtorExp *bv, + std::vector &unknown, + const bool cache, + const typet &type) const +{ + unsigned width; + + if(boolbv_get_width(type, width)) + return nil_exprt(); + + if(type.id()=="bool") + { + std::string value; + size_t found; + + if (cache) + { + value = boolector_bv_assignment(boolector_ctx, bv); + found=value.find("1"); + } + else + { + found=std::string::npos; + } + + if (found!=std::string::npos) + return true_exprt(); + else + return false_exprt(); // default + } + + bvtypet bvtype=get_bvtype(type); + + if(bvtype==IS_UNKNOWN) + { + if(type.id()=="array") + { +#if 0 + unsigned sub_width; + const typet &subtype=type.subtype(); + + if(!boolbv_get_width(subtype, sub_width)) + { + unsigned i, size=width/sub_width; + std::cout << "size: " << size << "\n"; + int array_size; + char **idx, **val; + + std::cout << "var: " << boolector_get_symbol_of_var(boolector_ctx, bv) << "\n"; + boolector_array_assignment(boolector_ctx, bv, &idx, &val, &array_size); + std::cout << "is array: " << boolector_is_array(boolector_ctx, bv) << "\n"; + std::cout << "cache: " << cache << "\n"; + std::cout << "array_size: " << array_size << "\n"; + + if (array_size > 0) + { + for (i = 0; i < array_size; i++) + { + std::cout << "idx: " << idx[i] << "val: " << val[i] << "\n"; + boolector_free_bv_assignment (boolector_ctx, idx[i]); + boolector_free_bv_assignment (boolector_ctx, val[i]); + } + delete (idx); + delete (val); + } + + } +#endif + return nil_exprt(); + } + else if(type.id()=="struct") + { +#if 0 + const irept &components=type.find("components"); + exprt::operandst op; + op.reserve(components.get_sub().size()); + unsigned int size; + + size = components.get_sub().size(); + + exprt expr; + unsigned i=0; + Z3_app app = Z3_to_app(z3_ctx, bv); + unsigned num_fields = Z3_get_app_num_args(z3_ctx, app); + Z3_ast tmp; + + if (num_fields == 0) + return nil_exprt(); + + forall_irep(it, components.get_sub()) + { + const typet &subtype=static_cast(it->find("type")); + op.push_back(nil_exprt()); + + unsigned sub_width; + if(!boolbv_get_width(subtype, sub_width)) + { + tmp = Z3_get_app_arg(z3_ctx, app, i); + expr=bv_get_rec(tmp, unknown, true, subtype); + if (!expr.is_nil()) + unknown.push_back(expr); + op.back()=unknown.back(); + ++i; + } + } + + exprt dest=exprt(type.id(), type); + dest.operands().swap(op); + + return dest; +#endif + return nil_exprt(); + } + else if(type.id()=="union") + { + //@TODO: reconstruct the counter-example for unions + return nil_exprt(); + + } + } + + std::string value; + + if (cache) + value = boolector_bv_assignment(boolector_ctx, bv); + + switch(bvtype) + { + case IS_C_ENUM: + { + constant_exprt value_expr(type); + value_expr.set_value(integer2string(binary2integer(value, true))); + return value_expr; + } + break; + + case IS_UNKNOWN: + break; + + case IS_RANGE: + { + mp_integer int_value=binary2integer(value, false); + mp_integer from=string2integer(type.get_string("from")); + + constant_exprt value_expr(type); + value_expr.set_value(integer2string(int_value+from)); + return value_expr; + } + break; + + default: + unsigned width; + boolbv_get_width(type, width); + constant_exprt value_expr(type); + value_expr.set_value(value); + return value_expr; + } + + return nil_exprt(); +} diff --git a/src/solvers/boolector/boolector_prop.cpp b/src/solvers/boolector/boolector_prop.cpp new file mode 100644 index 00000000000..d5aabd7d845 --- /dev/null +++ b/src/solvers/boolector/boolector_prop.cpp @@ -0,0 +1,628 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#include +#include +#include + +#include "boolector_prop.h" + +extern "C" { +#include "include/boolector.h" +} + +/*******************************************************************\ + +Function: boolector_propt::boolector_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +boolector_propt::boolector_propt() +{ + boolector_ctx=boolector_new(); + _no_variables=0; +} + +/*******************************************************************\ + +Function: boolector_propt::~boolector_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +boolector_propt::~boolector_propt() +{ + delete boolector_ctx; +} + +/*******************************************************************\ + +Function: boolector_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::land(const bvt &bv) +{ + literalt l=new_variable(); + u_int size=bv.size()+1; + BtorExp *args[size], *result; + + for(unsigned int i=0; i1) + result = boolector_and(boolector_ctx, result, args[i]); + } + + boolector_assert(boolector_ctx, boolector_iff(boolector_ctx, boolector_literal(l), result)); + + return l; +} + + +/*******************************************************************\ + +Function: boolector_propt::lor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lor(const bvt &bv) +{ + literalt l=new_variable(); + u_int size=bv.size()+1; + BtorExp *args[size], *result; + + for(unsigned int i=0; i1) + result = boolector_or(boolector_ctx, result, args[i]); + } + + boolector_assert(boolector_ctx, boolector_iff(boolector_ctx, boolector_literal(l), result)); + + return l; +} + +/*******************************************************************\ + +Function: boolector_propt::lxor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lxor(const bvt &bv) +{ + if(bv.size()==0) return const_literal(false); + if(bv.size()==1) return bv[0]; + + literalt l=new_variable(); + u_int size=bv.size()+1; + BtorExp *args[size], *result; + + for(unsigned int i=0; i1) + result = boolector_xor(boolector_ctx, result, args[i]); + } + + boolector_assert(boolector_ctx, boolector_iff(boolector_ctx, boolector_literal(l), result)); + + return l; +} + +/*******************************************************************\ + +Function: boolector_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::land(literalt a, literalt b) +{ + if(a==const_literal(true)) return b; + if(b==const_literal(true)) return a; + if(a==const_literal(false)) return const_literal(false); + if(b==const_literal(false)) return const_literal(false); + if(a==b) return a; + + literalt l=new_variable(); + BtorExp *result; + + result = boolector_and(boolector_ctx, boolector_literal(a), boolector_literal(b)); + boolector_assert(boolector_ctx, boolector_iff(boolector_ctx, boolector_literal(l), result)); + + return l; +} + +/*******************************************************************\ + +Function: boolector_propt::lor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lor(literalt a, literalt b) +{ + if(a==const_literal(false)) return b; + if(b==const_literal(false)) return a; + if(a==const_literal(true)) return const_literal(true); + if(b==const_literal(true)) return const_literal(true); + if(a==b) return a; + + literalt l=new_variable(); + BtorExp *result; + + result = boolector_or(boolector_ctx, boolector_literal(a), boolector_literal(b)); + boolector_assert(boolector_ctx, boolector_iff(boolector_ctx, boolector_literal(l), result)); + + return l; +} + +/*******************************************************************\ + +Function: boolector_propt::lnot + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lnot(literalt a) +{ + a.invert(); + return a; +} + +/*******************************************************************\ + +Function: boolector_propt::lxor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lxor(literalt a, literalt b) +{ + if(a==const_literal(false)) return b; + if(b==const_literal(false)) return a; + if(a==const_literal(true)) return lnot(b); + if(b==const_literal(true)) return lnot(a); + + literalt l=new_variable(); + BtorExp *result; + + result = boolector_xor(boolector_ctx, boolector_literal(a), boolector_literal(b)); + boolector_assert(boolector_ctx, boolector_iff(boolector_ctx, boolector_literal(l), result)); + + + return l; +} + +/*******************************************************************\ + +Function: boolector_propt::lnand + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lnand(literalt a, literalt b) +{ + return lnot(land(a, b)); +} + +/*******************************************************************\ + +Function: boolector_propt::lnor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lnor(literalt a, literalt b) +{ + return lnot(lor(a, b)); +} + + +/*******************************************************************\ + +Function: boolector_propt::lequal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lequal(literalt a, literalt b) +{ + return lnot(lxor(a, b)); +} + +/*******************************************************************\ + +Function: boolector_propt::limplies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::limplies(literalt a, literalt b) +{ + return lor(lnot(a), b); +} + +/*******************************************************************\ + +Function: boolector_propt::lselect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::lselect(literalt a, literalt b, literalt c) +{ + if(a==const_literal(true)) return b; + if(a==const_literal(false)) return c; + if(b==c) return b; + + if(a==const_literal(false)) return b; + if(b==const_literal(false)) return a; + if(a==const_literal(true)) return lnot(b); + if(b==const_literal(true)) return lnot(a); + + literalt l=new_variable(); + BtorExp *result; + + result = boolector_cond(boolector_ctx, boolector_literal(a), boolector_literal(b), boolector_literal(c)); + boolector_assert(boolector_ctx, boolector_iff(boolector_ctx, boolector_literal(l), result)); + + return l; +} + +/*******************************************************************\ + +Function: boolector_propt::new_variable + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolector_propt::new_variable() +{ + literalt l; + l.set(_no_variables, false); + _no_variables++; + + //std::cout << "literal: l" << _no_variables << "\n"; + + return l; +} + +/*******************************************************************\ + +Function: boolector_propt::eliminate_duplicates + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolector_propt::eliminate_duplicates(const bvt &bv, bvt &dest) +{ + std::set s; + + dest.reserve(bv.size()); + + for(bvt::const_iterator it=bv.begin(); it!=bv.end(); it++) + { + if(s.insert(*it).second) + dest.push_back(*it); + } +} + +/*******************************************************************\ + +Function: boolector_propt::process_clause + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool boolector_propt::process_clause(const bvt &bv, bvt &dest) +{ + dest.clear(); + + if(bv.empty()) return false; + + std::set s; + + dest.reserve(bv.size()); + + for(bvt::const_iterator it=bv.begin(); + it!=bv.end(); + it++) + { + literalt l=*it; + + if (l.var_no()==0) + return true; + + if(l.is_true()) + return true; // clause satisfied + + if(l.is_false()) + continue; + + if(l.var_no()>=_no_variables) + std::cout << "l.var_no()=" << l.var_no() << " _no_variables=" << _no_variables << std::endl; + assert(l.var_no()<_no_variables); + + // prevent duplicate literals + if(s.insert(l).second) + dest.push_back(l); + + if(s.find(lnot(l))!=s.end()) + return true; // clause satisfied + } + + return false; +} + +/*******************************************************************\ + +Function: boolector_propt::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolector_propt::lcnf(const bvt &bv) +{ + bvt new_bv; + + if(process_clause(bv, new_bv)) + return; + + BtorExp *lor_var, *args[new_bv.size()]; + unsigned int i=0, j=0; + + for(bvt::const_iterator it=new_bv.begin(); it!=new_bv.end(); it++, i++) + args[i] = boolector_literal(*it); + + if (i>1) + { + lor_var = boolector_or(boolector_ctx, args[0], args[1]); + + for(j=2; jfirst << "\n"; + return cache_result->second; + } + + BtorExp* result; + + literal_s = "l"+i2string(l); + result = boolector_var(boolector_ctx, 1, literal_s.c_str()); + + // insert into cache + literal_cache.insert(std::pair(l, result)); + + return result; +} + +/*******************************************************************\ + +Function: boolector_propt::boolector_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +BtorExp* boolector_propt::boolector_literal(literalt l) +{ + BtorExp *literal_l; + std::string literal_s; + + if(l==const_literal(false)) + return boolector_false(boolector_ctx); + else if(l==const_literal(true)) + return boolector_true(boolector_ctx); + + literal_l = convert_literal(l.var_no()); + + if(l.sign()) + return boolector_not(boolector_ctx, literal_l); + + return literal_l; +} + +/*******************************************************************\ + +Function: boolector_propt::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt boolector_propt::prop_solve() +{ + return P_ERROR; +} + +/*******************************************************************\ + +Function: boolector_propt::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt boolector_propt::l_get(literalt a) const +{ + tvt result=tvt(false); + std::string literal; + BtorExp *boolector_literal; + size_t found; + + if(a.is_true()) + return tvt(true); + else if(a.is_false()) + return tvt(false); + + literal_cachet::const_iterator cache_result=literal_cache.find(a.var_no()); + if(cache_result!=literal_cache.end()) + boolector_literal = cache_result->second; + else + return tvt(tvt::TV_UNKNOWN); + + literal = boolector_bv_assignment(boolector_ctx, boolector_literal); + found=literal.find("1"); + + if (found!=std::string::npos) + result=tvt(true); + else + result=tvt(false); + + if (a.sign()) result=!result; + + return result; +} diff --git a/src/solvers/boolector/boolector_prop.h b/src/solvers/boolector/boolector_prop.h new file mode 100644 index 00000000000..9cc2b49696e --- /dev/null +++ b/src/solvers/boolector/boolector_prop.h @@ -0,0 +1,100 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#ifndef CPROVER_PROP_BOOLECTOR_PROP_H +#define CPROVER_PROP_BOOLECTOR_PROP_H + +#include + +#include + +class boolector_propt:public propt +{ +public: + boolector_propt(); + virtual ~boolector_propt(); + + virtual literalt land(literalt a, literalt b); + virtual literalt lor(literalt a, literalt b); + virtual literalt land(const bvt &bv); + virtual literalt lor(const bvt &bv); + virtual literalt lxor(const bvt &bv); + virtual literalt lnot(literalt a); + virtual literalt lxor(literalt a, literalt b); + virtual literalt lnand(literalt a, literalt b); + virtual literalt lnor(literalt a, literalt b); + virtual literalt lequal(literalt a, literalt b); + virtual literalt limplies(literalt a, literalt b); + virtual literalt lselect(literalt a, literalt b, literalt c); // a?b:c + + virtual literalt new_variable(); + virtual unsigned no_variables() const { return _no_variables; } + virtual void set_no_variables(unsigned no) { assert(false); } + + virtual void lcnf(const bvt &bv); + + class BtorExp *convert_literal(unsigned l); + + virtual const std::string solver_text() + { return "Boolector"; } + + virtual tvt l_get(literalt a) const; + virtual propt::resultt prop_solve(); + + virtual void clear() + { + literal_map.clear(); + } + + virtual void reset_assignment() + { + literal_map.clear(); + } + + + void gate_and(literalt a, literalt b, literalt o); + void gate_or(literalt a, literalt b, literalt o); + void gate_xor(literalt a, literalt b, literalt o); + void gate_nand(literalt a, literalt b, literalt o); + void gate_nor(literalt a, literalt b, literalt o); + void gate_equal(literalt a, literalt b, literalt o); + void gate_implies(literalt a, literalt b, literalt o); + + static void eliminate_duplicates(const bvt &bv, bvt &dest); + + friend class boolector_dect; + + class Btor *boolector_ctx; + +protected: + unsigned _no_variables; + + bool process_clause(const bvt &bv, bvt &dest); + BtorExp* boolector_literal(literalt l); + + static bool is_all(const bvt &bv, literalt l) + { + for(unsigned i=0; i literal_cachet; + literal_cachet literal_cache; + + struct map_entryt + { + class BTorExp *exp; + bool value; + }; + + typedef std::vector literal_mapt; + literal_mapt literal_map; +}; + +#endif diff --git a/src/solvers/cvc/cvc_conv.cpp b/src/solvers/cvc/cvc_conv.cpp new file mode 100644 index 00000000000..640dd646bc6 --- /dev/null +++ b/src/solvers/cvc/cvc_conv.cpp @@ -0,0 +1,1370 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cvc_conv.h" + +/*******************************************************************\ + +Function: cvc_convt::bin_zero + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cvc_convt::bin_zero(unsigned bits) +{ + assert(bits!=0); + std::string result="0bin"; + while(bits!=0) { result+='0'; bits--; } + return result; +} + +/*******************************************************************\ + +Function: cvc_convt::cvc_pointer_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cvc_convt::cvc_pointer_type() +{ + assert(config.ansi_c.pointer_width!=0); + return "[# object: INT, offset: BITVECTOR("+ + i2string(config.ansi_c.pointer_width)+") #]"; +} + +/*******************************************************************\ + +Function: cvc_convt::array_index_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cvc_convt::array_index_type() +{ + return std::string("BITVECTOR(")+ + i2string(32)+")"; +} + +/*******************************************************************\ + +Function: cvc_convt::gen_array_index_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet cvc_convt::gen_array_index_type() +{ + typet t("signedbv"); + t.set("width", 32); + return t; +} + +/*******************************************************************\ + +Function: cvc_convt::array_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string cvc_convt::array_index(unsigned i) +{ + return "0bin"+integer2binary(i, config.ansi_c.int_width); +} + +/*******************************************************************\ + +Function: cvc_convt::convert_array_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::convert_array_index(const exprt &expr) +{ + if(expr.type()==gen_array_index_type()) + { + convert_cvc_expr(expr); + } + else + { + exprt tmp("typecast", gen_array_index_type()); + tmp.copy_to_operands(expr); + convert_cvc_expr(tmp); + } +} + +/*******************************************************************\ + +Function: cvc_convt::convert_address_of_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::convert_address_of_rec(const exprt &expr) +{ + if(expr.id()=="symbol" || + expr.id()=="constant" || + expr.id()==ID_string_constant) + { + cvc_prop.out + << "(# object:=" + << pointer_logic.add_object(expr) + << ", offset:=" + << bin_zero(config.ansi_c.pointer_width) << " #)"; + } + else if(expr.id()=="index") + { + if(expr.operands().size()!=2) + throw "index takes two operands"; + + const exprt &array=expr.op0(); + const exprt &index=expr.op1(); + + if(index.is_zero()) + { + if(array.type().id()=="pointer") + convert_cvc_expr(array); + else if(array.type().id()=="array") + convert_address_of_rec(array); + else + assert(false); + } + else + { + cvc_prop.out << "(LET P: "; + cvc_prop.out << cvc_pointer_type(); + cvc_prop.out << " = "; + + if(array.type().id()=="pointer") + convert_cvc_expr(array); + else if(array.type().id()=="array") + convert_address_of_rec(array); + else + assert(false); + + cvc_prop.out << " IN P WITH .offset:=BVPLUS(" + << config.ansi_c.pointer_width + << ", P.offset, "; + convert_cvc_expr(index); + cvc_prop.out << "))"; + } + } + else if(expr.id()=="member") + { + if(expr.operands().size()!=1) + throw "member takes one operand"; + + const exprt &struct_op=expr.op0(); + + cvc_prop.out << "(LET P: "; + cvc_prop.out << cvc_pointer_type(); + cvc_prop.out << " = "; + + convert_address_of_rec(struct_op); + + const irep_idt &component_name= + to_member_expr(expr).get_component_name(); + + mp_integer offset=member_offset(ns, + to_struct_type(struct_op.type()), + component_name); + + typet index_type("unsignedbv"); + index_type.set("width", config.ansi_c.pointer_width); + + exprt index=from_integer(offset, index_type); + + cvc_prop.out << " IN P WITH .offset:=BVPLUS(" + << config.ansi_c.pointer_width + << ", P.offset, "; + convert_cvc_expr(index); + cvc_prop.out << "))"; + } + else + throw "don't know how to take address of: "+expr.id_string(); +} + +/*******************************************************************\ + +Function: cvc_convt::convert_rest + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt cvc_convt::convert_rest(const exprt &expr) +{ + //cvc_prop.out << "%% E: " << expr << std::endl; + + literalt l=prop.new_variable(); + + find_symbols(expr); + + cvc_prop.out << "ASSERT " << cvc_prop.cvc_literal(l) << " <=> ("; + convert_cvc_expr(expr); + cvc_prop.out << ");" << std::endl << std::endl; + + return l; +} + +/*******************************************************************\ + +Function: cvc_convt::convert_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::convert_identifier(const std::string &identifier) +{ + for(std::string::const_iterator + it=identifier.begin(); + it!=identifier.end(); + it++) + { + char ch=*it; + + if(isalnum(ch) || ch=='$' || ch=='?') + cvc_prop.out << ch; + else if(ch==':') + { + std::string::const_iterator next_it(it); + next_it++; + if(next_it!=identifier.end() && *next_it==':') + { + cvc_prop.out << "__"; + it=next_it; + } + else + { + cvc_prop.out << '_'; + cvc_prop.out << int(ch); + cvc_prop.out << '_'; + } + } + else + { + cvc_prop.out << '_'; + cvc_prop.out << int(ch); + cvc_prop.out << '_'; + } + } +} + +/*******************************************************************\ + +Function: cvc_convt::convert_as_bv + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::convert_as_bv(const exprt &expr) +{ + if(expr.type().id()=="bool") + { + if(expr.is_true()) + cvc_prop.out << "0bin1"; + else if(expr.is_false()) + cvc_prop.out << "0bin0"; + else + { + cvc_prop.out << "IF "; + convert_cvc_expr(expr); + cvc_prop.out << " THEN 0bin1 ELSE 0bin0 ENDIF"; + } + } + else + convert_cvc_expr(expr); +} + +/*******************************************************************\ + +Function: cvc_convt::convert_array_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::convert_array_value(const exprt &expr) +{ + convert_as_bv(expr); +} + +/*******************************************************************\ + +Function: cvc_convt::convert_cvc_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::convert_cvc_expr(const exprt &expr) +{ + if(expr.id()=="symbol") + { + convert_identifier(expr.get_string("identifier")); + } + else if(expr.id()=="nondet_symbol") + { + convert_identifier("nondet$"+expr.get_string("identifier")); + } + else if(expr.id()=="typecast") + { + assert(expr.operands().size()==1); + const exprt &op=expr.op0(); + + if(expr.type().id()=="bool") + { + if(op.type().id()=="signedbv" || + op.type().id()=="unsignedbv" || + op.type().id()=="pointer") + { + convert_cvc_expr(op); + cvc_prop.out << "/="; + convert_cvc_expr(gen_zero(op.type())); + } + else + { + throw "TODO typecast1 "+op.type().id_string()+" -> bool"; + } + } + else if(expr.type().id()=="signedbv" || + expr.type().id()=="unsignedbv") + { + unsigned to_width=atoi(expr.type().get("width").c_str()); + + if(op.type().id()=="signedbv") + { + unsigned from_width=atoi(op.type().get("width").c_str()); + + if(from_width==to_width) + convert_cvc_expr(op); + else if(from_width1) + { + cvc_prop.out << "(0bin"; + + for(unsigned i=1; i "+expr.type().id_string(); + } + } + else if(expr.type().id()=="pointer") + { + if(op.type().id()=="pointer") + { + convert_cvc_expr(op); + } + else + throw "TODO typecast3 "+op.type().id_string()+" -> pointer"; + } + else + throw "TODO typecast4 ? -> "+expr.type().id_string(); + } + else if(expr.id()=="struct") + { + cvc_prop.out << "(# "; + + const struct_typet &struct_type=to_struct_type(expr.type()); + + const struct_typet::componentst &components= + struct_type.components(); + + assert(components.size()==expr.operands().size()); + + unsigned i=0; + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++, i++) + { + if(i!=0) cvc_prop.out << ", "; + cvc_prop.out << it->get("name"); + cvc_prop.out << ":="; + convert_cvc_expr(expr.operands()[i]); + } + + cvc_prop.out << " #)"; + } + else if(expr.id()=="constant") + { + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv" || + expr.type().id()=="bv") + { + const irep_idt &value=expr.get("value"); + + if(value.size()==8 || + value.size()==16 || + value.size()==32 || + value.size()==64) + { + unsigned w=value.size()/4; + + mp_integer i=binary2integer(id2string(value), false); + std::string hex=integer2string(i, 16); + + while(hex.size()=3); + + exprt tmp(expr); + tmp.operands().resize(tmp.operands().size()-1); + + cvc_prop.out << "BVXOR("; + convert_cvc_expr(tmp); + cvc_prop.out << ", "; + convert_cvc_expr(expr.operands().back()); + cvc_prop.out << ")"; + } + } + else if(expr.id()=="bitnand") + { + assert(expr.operands().size()==2); + + cvc_prop.out << "BVNAND("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ", "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + else if(expr.id()=="bitnot") + { + assert(expr.operands().size()==1); + cvc_prop.out << "~("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ")"; + } + else if(expr.id()=="unary-") + { + assert(expr.operands().size()==1); + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + cvc_prop.out << "BVUMINUS("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ")"; + } + else + throw "unsupported type for unary-: "+expr.type().id_string(); + } + else if(expr.id()=="if") + { + assert(expr.operands().size()==3); + cvc_prop.out << "IF "; + convert_cvc_expr(expr.op0()); + cvc_prop.out << " THEN "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << " ELSE "; + convert_cvc_expr(expr.op2()); + cvc_prop.out << " ENDIF"; + } + else if(expr.id()=="and" || + expr.id()=="or" || + expr.id()=="xor") + { + assert(expr.type().id()=="bool"); + + if(expr.operands().size()>=2) + { + forall_operands(it, expr) + { + if(it!=expr.operands().begin()) + { + if(expr.id()=="and") + cvc_prop.out << " AND "; + else if(expr.id()=="or") + cvc_prop.out << " OR "; + else if(expr.id()=="xor") + cvc_prop.out << " XOR "; + } + + cvc_prop.out << "("; + convert_cvc_expr(*it); + cvc_prop.out << ")"; + } + } + else if(expr.operands().size()==1) + { + convert_cvc_expr(expr.op0()); + } + else + assert(false); + } + else if(expr.id()=="not") + { + assert(expr.operands().size()==1); + cvc_prop.out << "NOT ("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ")"; + } + else if(expr.id()=="=" || + expr.id()=="notequal") + { + assert(expr.operands().size()==2); + assert(expr.op0().type()==expr.op1().type()); + + if(expr.op0().type().id()=="bool") + { + if(expr.id()=="notequal") cvc_prop.out << "NOT ("; + cvc_prop.out << "("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ") <=> ("; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + if(expr.id()=="notequal") cvc_prop.out << ")"; + } + else + { + cvc_prop.out << "("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ")"; + cvc_prop.out << (expr.id()=="="?"=":"/="); + cvc_prop.out << "("; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + } + else if(expr.id()=="<=" || + expr.id()=="<" || + expr.id()==">=" || + expr.id()==">") + { + assert(expr.operands().size()==2); + + const typet &op_type=expr.op0().type(); + + if(op_type.id()=="unsignedbv") + { + if(expr.id()=="<=") + cvc_prop.out << "BVLE"; + else if(expr.id()=="<") + cvc_prop.out << "BVLT"; + else if(expr.id()==">=") + cvc_prop.out << "BVGE"; + else if(expr.id()==">") + cvc_prop.out << "BVGT"; + + cvc_prop.out << "("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ", "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + else if(op_type.id()=="signedbv") + { + if(expr.id()=="<=") + cvc_prop.out << "SBVLE"; + else if(expr.id()=="<") + cvc_prop.out << "SBVLT"; + else if(expr.id()==">=") + cvc_prop.out << "SBVGE"; + else if(expr.id()==">") + cvc_prop.out << "SBVGT"; + + cvc_prop.out << "("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ", "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + else + throw "unsupported type for "+expr.id_string()+": "+expr.type().id_string(); + } + else if(expr.id()=="+") + { + if(expr.operands().size()>=2) + { + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + cvc_prop.out << "BVPLUS(" << expr.type().get("width"); + + forall_operands(it, expr) + { + cvc_prop.out << ", "; + convert_cvc_expr(*it); + } + + cvc_prop.out << ")"; + } + else if(expr.type().id()=="pointer") + { + if(expr.operands().size()!=2) + throw "pointer arithmetic with more than two operands"; + + const exprt *p, *i; + + if(expr.op0().type().id()=="pointer") + { + p=&expr.op0(); + i=&expr.op1(); + } + else if(expr.op1().type().id()=="pointer") + { + p=&expr.op1(); + i=&expr.op0(); + } + else + throw "unexpected mixture in pointer arithmetic"; + + cvc_prop.out << "(LET P: " << cvc_pointer_type() << " = "; + convert_cvc_expr(*p); + cvc_prop.out << " IN P WITH .offset:=BVPLUS(" + << config.ansi_c.pointer_width + << ", P.offset, "; + convert_cvc_expr(*i); + cvc_prop.out << "))"; + } + else + throw "unsupported type for +: "+expr.type().id_string(); + } + else if(expr.operands().size()==1) + { + convert_cvc_expr(expr.op0()); + } + else + assert(false); + } + else if(expr.id()=="-") + { + if(expr.operands().size()==2) + { + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + cvc_prop.out << "BVSUB(" << expr.type().get("width") << ", "; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ", "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + else + throw "unsupported type for -: "+expr.type().id_string(); + } + else if(expr.operands().size()==1) + { + convert_cvc_expr(expr.op0()); + } + else + assert(false); + } + else if(expr.id()=="/") + { + assert(expr.operands().size()==2); + + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + if(expr.type().id()=="unsignedbv") + cvc_prop.out << "BVDIV"; + else + cvc_prop.out << "SBVDIV"; + + cvc_prop.out << "(" << expr.type().get("width") << ", "; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ", "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + else + throw "unsupported type for /: "+expr.type().id_string(); + } + else if(expr.id()=="mod") + { + assert(expr.operands().size()==2); + + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + if(expr.type().id()=="unsignedbv") + cvc_prop.out << "BVMOD"; + else + cvc_prop.out << "SBVMOD"; + + cvc_prop.out << "(" << expr.type().get("width") << ", "; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ", "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + else + throw "unsupported type for mod: "+expr.type().id_string(); + } + else if(expr.id()=="*") + { + if(expr.operands().size()==2) + { + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + cvc_prop.out << "BVMULT(" << expr.type().get("width") << ", "; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ", "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + else + throw "unsupported type for *: "+expr.type().id_string(); + } + else if(expr.operands().size()==1) + { + convert_cvc_expr(expr.op0()); + } + else + assert(false); + } + else if(expr.id()=="address_of" || + expr.id()=="implicit_address_of" || + expr.id()=="reference_to") + { + assert(expr.operands().size()==1); + assert(expr.type().id()=="pointer"); + convert_address_of_rec(expr.op0()); + } + else if(expr.id()=="array_of") + { + assert(expr.type().id()=="array"); + assert(expr.operands().size()==1); + cvc_prop.out << "(ARRAY (i: " << array_index_type() << "): "; + convert_array_value(expr.op0()); + cvc_prop.out << ")"; + } + else if(expr.id()=="index") + { + assert(expr.operands().size()==2); + cvc_prop.out << "("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ")["; + convert_array_index(expr.op1()); + cvc_prop.out << "]"; + } + else if(expr.id()=="ashr" || + expr.id()=="lshr" || + expr.id()=="shl") + { + assert(expr.operands().size()==2); + + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + if(expr.id()=="ashr") + cvc_prop.out << "BVASHR"; + else if(expr.id()=="lshr") + cvc_prop.out << "BVLSHR"; + else if(expr.id()=="shl") + cvc_prop.out << "BVSHL"; + else + assert(false); + + cvc_prop.out << "(" << expr.type().get("width") << ", "; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ", "; + convert_cvc_expr(expr.op1()); + cvc_prop.out << ")"; + } + else + throw "unsupported type for "+expr.id_string()+": "+expr.type().id_string(); + } + else if(expr.id()=="with") + { + assert(expr.operands().size()>=1); + cvc_prop.out << "("; + convert_cvc_expr(expr.op0()); + cvc_prop.out << ")"; + + for(unsigned i=1; i s_set; + + ::find_symbols(expr.op1(), s_set); + + if(s_set.find(identifier)==s_set.end()) + { + id.type=expr.op0().type(); + + find_symbols(expr.op1()); + + convert_identifier(id2string(identifier)); + cvc_prop.out << ": "; + convert_cvc_type(expr.op0().type()); + cvc_prop.out << " = "; + convert_cvc_expr(expr.op1()); + + cvc_prop.out << ";" << std::endl << std::endl; + return; + } + } + } + } + + find_symbols(expr); + + cvc_prop.out << "ASSERT "; + + if(!value) + cvc_prop.out << "NOT ("; + + convert_cvc_expr(expr); + + if(!value) + cvc_prop.out << ")"; + + cvc_prop.out << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: cvc_convt::find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::find_symbols(const exprt &expr) +{ + find_symbols(expr.type()); + + forall_operands(it, expr) + find_symbols(*it); + + if(expr.id()=="symbol") + { + if(expr.type().id()=="code") + return; + + const irep_idt &identifier=expr.get("identifier"); + + identifiert &id=identifier_map[identifier]; + + if(id.type.is_nil()) + { + id.type=expr.type(); + + convert_identifier(id2string(identifier)); + cvc_prop.out << ": "; + convert_cvc_type(expr.type()); + cvc_prop.out << ";" << std::endl; + } + } + else if(expr.id()=="nondet_symbol") + { + if(expr.type().id()=="code") + return; + + const irep_idt identifier="nondet$"+expr.get_string("identifier"); + + identifiert &id=identifier_map[identifier]; + + if(id.type.is_nil()) + { + id.type=expr.type(); + + convert_identifier(id2string(identifier)); + cvc_prop.out << ": "; + convert_cvc_type(expr.type()); + cvc_prop.out << ";" << std::endl; + } + } +} + +/*******************************************************************\ + +Function: cvc_convt::convert_cvc_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::convert_cvc_type(const typet &type) +{ + if(type.id()=="array") + { + const array_typet &array_type=to_array_type(type); + + cvc_prop.out << "ARRAY " << array_index_type() + << " OF "; + + if(array_type.subtype().id()=="bool") + cvc_prop.out << "BITVECTOR(1)"; + else + convert_cvc_type(array_type.subtype()); + } + else if(type.id()=="bool") + { + cvc_prop.out << "BOOLEAN"; + } + else if(type.id()=="struct" || + type.id()=="union") + { + const struct_typet &struct_type=to_struct_type(type); + + cvc_prop.out << "[#"; + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it!=components.begin()) cvc_prop.out << ","; + cvc_prop.out << " "; + cvc_prop.out << it->get("name"); + cvc_prop.out << ": "; + convert_cvc_type(it->type()); + } + + cvc_prop.out << " #]"; + } + else if(type.id()=="pointer" || + type.id()=="reference") + { + cvc_prop.out << cvc_pointer_type(); + } + else if(type.id()=="integer") + { + cvc_prop.out << "INT"; + } + else if(type.id()=="signedbv") + { + unsigned width=to_signedbv_type(type).get_width(); + + if(width==0) + throw "zero-width vector type: "+type.id_string(); + + cvc_prop.out << "BITVECTOR(" << width << ")"; + } + else if(type.id()=="unsignedbv") + { + unsigned width=to_unsignedbv_type(type).get_width(); + + if(width==0) + throw "zero-width vector type: "+type.id_string(); + + cvc_prop.out << "BITVECTOR(" << width << ")"; + } + else + throw "unsupported type: "+type.id_string(); +} + +/*******************************************************************\ + +Function: cvc_convt::find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_convt::find_symbols(const typet &type) +{ + if(type.id()=="array") + { + const array_typet &array_type=to_array_type(type); + find_symbols(array_type.size()); + } + else if(type.id()=="struct" || + type.id()=="union") + { + } +} diff --git a/src/solvers/cvc/cvc_conv.h b/src/solvers/cvc/cvc_conv.h new file mode 100644 index 00000000000..13c2b11febf --- /dev/null +++ b/src/solvers/cvc/cvc_conv.h @@ -0,0 +1,79 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PROP_CVC_CONV_H +#define CPROVER_PROP_CVC_CONV_H + +#include + +#include +#include + +#include "cvc_prop.h" + +class cvc_prop_wrappert +{ +public: + cvc_prop_wrappert(std::ostream &_out):cvc_prop(_out) { } + +protected: + cvc_propt cvc_prop; +}; + +class cvc_convt:protected cvc_prop_wrappert, public prop_convt +{ +public: + cvc_convt(const namespacet &_ns, + std::ostream &_out): + cvc_prop_wrappert(_out), + prop_convt(_ns, cvc_prop), + pointer_logic(_ns) { } + + virtual ~cvc_convt() { } + +protected: + virtual literalt convert_rest(const exprt &expr); + virtual void convert_cvc_expr(const exprt &expr); + virtual void convert_cvc_type(const typet &type); + virtual void set_to(const exprt &expr, bool value); + virtual void convert_address_of_rec(const exprt &expr); + + pointer_logict pointer_logic; + +private: + void convert_identifier(const std::string &identifier); + void find_symbols(const exprt &expr); + void find_symbols(const typet &type); + void convert_array_value(const exprt &expr); + void convert_as_bv(const exprt &expr); + void convert_array_index(const exprt &expr); + static typet gen_array_index_type(); + static std::string bin_zero(unsigned bits); + static std::string array_index_type(); + static std::string array_index(unsigned i); + static std::string cvc_pointer_type(); + + struct identifiert + { + typet type; + exprt value; + + identifiert() + { + type.make_nil(); + value.make_nil(); + } + }; + + typedef hash_map_cont + identifier_mapt; + + identifier_mapt identifier_map; +}; + +#endif diff --git a/src/solvers/cvc/cvc_dec.cpp b/src/solvers/cvc/cvc_dec.cpp new file mode 100644 index 00000000000..178406e888f --- /dev/null +++ b/src/solvers/cvc/cvc_dec.cpp @@ -0,0 +1,212 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#ifdef _WIN32 +#include +#define getpid _getpid +#endif + +#include +#include +#include + +#include "cvc_dec.h" + +/*******************************************************************\ + +Function: cvc_temp_filet::cvc_temp_filet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cvc_temp_filet::cvc_temp_filet() +{ + temp_out_filename="cvc_dec_out_"+i2string(getpid())+".tmp"; + + temp_out.open( + temp_out_filename.c_str(), + std::ios_base::out | std::ios_base::trunc); +} + +/*******************************************************************\ + +Function: cvc_temp_filet::~cvc_temp_filet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cvc_temp_filet::~cvc_temp_filet() +{ + temp_out.close(); + + if(temp_out_filename!="") + unlink(temp_out_filename.c_str()); + + if(temp_result_filename!="") + unlink(temp_result_filename.c_str()); +} + +/*******************************************************************\ + +Function: cvc_dect::dec_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt cvc_dect::dec_solve() +{ + cvc_prop.out << "QUERY FALSE;" << std::endl; + cvc_prop.out << "COUNTERMODEL;" << std::endl; + + post_process(); + + temp_out.close(); + + temp_result_filename= + "cvc_dec_result_"+i2string(getpid())+".tmp"; + + std::string command= + "cvcl "+temp_out_filename+" > "+temp_result_filename+" 2>&1"; + + system(command.c_str()); + + status("Reading result from CVCL"); + + return read_cvcl_result(); +} + +/*******************************************************************\ + +Function: cvc_dect::read_assert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_dect::read_assert(std::istream &in, std::string &line) +{ + // strip ASSERT + line=std::string(line, strlen("ASSERT "), std::string::npos); + if(line=="") return; + + // bit-vector + if(line[0]=='(') + { + // get identifier + std::string::size_type pos= + line.find(' '); + + std::string identifier=std::string(line, 1, pos-1); + + // get value + if(!str_getline(in, line)) return; + + // skip spaces + pos=0; + while(pos" << identifier << "< = >" << value << "<"; + + std::cout << std::endl; + } + else + { + // boolean + tvt value=tvt(true); + + if(has_prefix(line, "NOT ")) + { + line=std::string(line, strlen("NOT "), std::string::npos); + value=tvt(false); + } + + if(line=="") return; + + if(line[0]=='l') + { + unsigned number=atoi(line.c_str()+1); + assert(number + +#include "cvc_conv.h" + +class cvc_temp_filet +{ +public: + cvc_temp_filet(); + ~cvc_temp_filet(); + +protected: + std::ofstream temp_out; + std::string temp_out_filename, temp_result_filename; +}; + +class cvc_dect:protected cvc_temp_filet, public cvc_convt +{ +public: + explicit cvc_dect(const namespacet &_ns):cvc_convt(_ns, temp_out) + { + } + + virtual resultt dec_solve(); + +protected: + resultt read_cvcl_result(); + void read_assert(std::istream &in, std::string &line); +}; + +#endif diff --git a/src/solvers/cvc/cvc_prop.cpp b/src/solvers/cvc/cvc_prop.cpp new file mode 100644 index 00000000000..897bd4b4de0 --- /dev/null +++ b/src/solvers/cvc/cvc_prop.cpp @@ -0,0 +1,608 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include + +#include "cvc_prop.h" + +/*******************************************************************\ + +Function: cvc_propt::cvc_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cvc_propt::cvc_propt(std::ostream &_out):out(_out) +{ + _no_variables=0; +} + +/*******************************************************************\ + +Function: cvc_propt::~cvc_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cvc_propt::~cvc_propt() +{ +} + +/*******************************************************************\ + +Function: + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_propt::land(literalt a, literalt b, literalt o) +{ + out << "%% land" << std::endl; + out << "ASSERT (" << cvc_literal(a) << " AND " + << cvc_literal(b) << ") <=> " << cvc_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_propt::lor(literalt a, literalt b, literalt o) +{ + out << "%% lor" << std::endl; + out << "ASSERT (" << cvc_literal(a) << " OR " + << cvc_literal(b) << ") <=> " << cvc_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_propt::lxor(literalt a, literalt b, literalt o) +{ + out << "%% lxor" << std::endl; + out << "ASSERT (" << cvc_literal(a) << " XOR " + << cvc_literal(b) << ") <=> " << cvc_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_propt::lnand(literalt a, literalt b, literalt o) +{ + out << "%% lnand" << std::endl; + out << "ASSERT (NOT (" << cvc_literal(a) << " AND " + << cvc_literal(b) << ")) <=> " << cvc_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_propt::lnor(literalt a, literalt b, literalt o) +{ + out << "%% lnor" << std::endl; + out << "ASSERT (NOT (" << cvc_literal(a) << " OR " + << cvc_literal(b) << ")) <=> " << cvc_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: cvc_propt::lequal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_propt::lequal(literalt a, literalt b, literalt o) +{ + out << "%% lequal" << std::endl; + out << "ASSERT (" << cvc_literal(a) << " <=> " + << cvc_literal(b) << ") <=> " << cvc_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: cvc_propt::limplies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cvc_propt::limplies(literalt a, literalt b, literalt o) +{ + out << "%% limplies" << std::endl; + out << "ASSERT (" << cvc_literal(a) << " => " + << cvc_literal(b) << ") <=> " << cvc_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: cvc_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt cvc_propt::land(const bvt &bv) +{ + out << "%% land" << std::endl; + + literalt literal=def_cvc_literal(); + + for(unsigned int i=0; i + +#include + +class cvc_propt:virtual public propt +{ +public: + cvc_propt(std::ostream &_out); + virtual ~cvc_propt(); + + virtual void land(literalt a, literalt b, literalt o); + virtual void lor(literalt a, literalt b, literalt o); + virtual void lxor(literalt a, literalt b, literalt o); + virtual void lnand(literalt a, literalt b, literalt o); + virtual void lnor(literalt a, literalt b, literalt o); + virtual void lequal(literalt a, literalt b, literalt o); + virtual void limplies(literalt a, literalt b, literalt o); + + virtual literalt land(literalt a, literalt b); + virtual literalt lor(literalt a, literalt b); + virtual literalt land(const bvt &bv); + virtual literalt lor(const bvt &bv); + virtual literalt lxor(const bvt &bv); + virtual literalt lnot(literalt a); + virtual literalt lxor(literalt a, literalt b); + virtual literalt lnand(literalt a, literalt b); + virtual literalt lnor(literalt a, literalt b); + virtual literalt lequal(literalt a, literalt b); + virtual literalt limplies(literalt a, literalt b); + virtual literalt lselect(literalt a, literalt b, literalt c); // a?b:c + virtual literalt new_variable(); + virtual unsigned no_variables() const { return _no_variables; } + virtual void set_no_variables(unsigned no) { assert(false); } + + virtual void lcnf(const bvt &bv); + + virtual const std::string solver_text() + { return "CVC"; } + + virtual tvt l_get(literalt literal) const + { + unsigned v=literal.var_no(); + if(v>=assignment.size()) return tvt(tvt::TV_UNKNOWN); + tvt r=assignment[v]; + return literal.sign()?!r:r; + } + + virtual propt::resultt prop_solve(); + + friend class cvc_convt; + friend class cvc_dect; + + virtual void clear() + { + assignment.clear(); + } + + void reset_assignment() + { + assignment.clear(); + assignment.resize(no_variables(), tvt(tvt::TV_UNKNOWN)); + } + +protected: + unsigned _no_variables; + std::ostream &out; + + std::string cvc_literal(literalt l); + literalt def_cvc_literal(); + + std::vector assignment; +}; + +#endif diff --git a/src/solvers/dplib/dplib_conv.cpp b/src/solvers/dplib/dplib_conv.cpp new file mode 100644 index 00000000000..12aeebf3441 --- /dev/null +++ b/src/solvers/dplib/dplib_conv.cpp @@ -0,0 +1,1357 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dplib_conv.h" + +/*******************************************************************\ + +Function: dplib_convt::bin_zero + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string dplib_convt::bin_zero(unsigned bits) +{ + assert(bits!=0); + std::string result="0bin"; + while(bits!=0) { result+='0'; bits--; } + return result; +} + +/*******************************************************************\ + +Function: dplib_convt::dplib_pointer_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string dplib_convt::dplib_pointer_type() +{ + assert(config.ansi_c.pointer_width!=0); + return "[# object: INT, offset: BITVECTOR("+ + i2string(config.ansi_c.pointer_width)+") #]"; +} + +/*******************************************************************\ + +Function: dplib_convt::array_index_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string dplib_convt::array_index_type() +{ + return std::string("SIGNED [")+i2string(32)+"]"; +} + +/*******************************************************************\ + +Function: dplib_convt::gen_array_index_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet dplib_convt::gen_array_index_type() +{ + typet t("signedbv"); + t.set("width", 32); + return t; +} + +/*******************************************************************\ + +Function: dplib_convt::array_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string dplib_convt::array_index(unsigned i) +{ + return "0bin"+integer2binary(i, config.ansi_c.int_width); +} + +/*******************************************************************\ + +Function: dplib_convt::convert_array_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::convert_array_index(const exprt &expr) +{ + if(expr.type()==gen_array_index_type()) + { + convert_dplib_expr(expr); + } + else + { + exprt tmp("typecast", gen_array_index_type()); + tmp.copy_to_operands(expr); + convert_dplib_expr(tmp); + } +} + +/*******************************************************************\ + +Function: dplib_convt::convert_address_of_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::convert_address_of_rec(const exprt &expr) +{ + if(expr.id()=="symbol" || + expr.id()=="constant" || + expr.id()==ID_string_constant) + { + dplib_prop.out + << "(# object:=" + << pointer_logic.add_object(expr) + << ", offset:=" + << bin_zero(config.ansi_c.pointer_width) << " #)"; + } + else if(expr.id()=="index") + { + if(expr.operands().size()!=2) + throw "index takes two operands"; + + const exprt &array=expr.op0(); + const exprt &index=expr.op1(); + + if(index.is_zero()) + { + if(array.type().id()=="pointer") + convert_dplib_expr(array); + else if(array.type().id()=="array") + convert_address_of_rec(array); + else + assert(false); + } + else + { + dplib_prop.out << "(LET P: "; + dplib_prop.out << dplib_pointer_type(); + dplib_prop.out << " = "; + + if(array.type().id()=="pointer") + convert_dplib_expr(array); + else if(array.type().id()=="array") + convert_address_of_rec(array); + else + assert(false); + + dplib_prop.out << " IN P WITH .offset:=BVPLUS(" + << config.ansi_c.pointer_width + << ", P.offset, "; + convert_dplib_expr(index); + dplib_prop.out << "))"; + } + } + else if(expr.id()=="member") + { + if(expr.operands().size()!=1) + throw "member takes one operand"; + + const exprt &struct_op=expr.op0(); + + dplib_prop.out << "(LET P: "; + dplib_prop.out << dplib_pointer_type(); + dplib_prop.out << " = "; + + convert_address_of_rec(struct_op); + + const irep_idt &component_name= + to_member_expr(expr).get_component_name(); + + mp_integer offset=member_offset(ns, + to_struct_type(struct_op.type()), component_name); + + typet index_type("unsignedbv"); + index_type.set("width", config.ansi_c.pointer_width); + + exprt index=from_integer(offset, index_type); + + dplib_prop.out << " IN P WITH .offset:=BVPLUS(" + << config.ansi_c.pointer_width + << ", P.offset, "; + convert_dplib_expr(index); + dplib_prop.out << "))"; + } + else + throw "don't know how to take address of: "+expr.id_string(); +} + +/*******************************************************************\ + +Function: dplib_convt::convert_rest + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_convt::convert_rest(const exprt &expr) +{ + //dplib_prop.out << "%% E: " << expr << std::endl; + + literalt l=prop.new_variable(); + + find_symbols(expr); + + if(expr.id()=="=" || expr.id()=="notequal") + { + assert(expr.operands().size()==2); + + dplib_prop.out << "ASSERT " << dplib_prop.dplib_literal(l) << " <=> ("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ((expr.id()=="=")?"=":"/="); + convert_dplib_expr(expr.op1()); + dplib_prop.out << ");" << std::endl; + } + + return l; +} + +/*******************************************************************\ + +Function: dplib_convt::convert_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::convert_identifier(const std::string &identifier) +{ + for(std::string::const_iterator + it=identifier.begin(); + it!=identifier.end(); + it++) + { + char ch=*it; + + if(isalnum(ch) || ch=='$' || ch=='?') + dplib_prop.out << ch; + else if(ch==':') + { + std::string::const_iterator next_it(it); + next_it++; + if(next_it!=identifier.end() && *next_it==':') + { + dplib_prop.out << "__"; + it=next_it; + } + else + { + dplib_prop.out << '_'; + dplib_prop.out << int(ch); + dplib_prop.out << '_'; + } + } + else + { + dplib_prop.out << '_'; + dplib_prop.out << int(ch); + dplib_prop.out << '_'; + } + } +} + +/*******************************************************************\ + +Function: dplib_convt::convert_as_bv + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::convert_as_bv(const exprt &expr) +{ + if(expr.type().id()=="bool") + { + dplib_prop.out << "IF "; + convert_dplib_expr(expr); + dplib_prop.out << " THEN 0bin1 ELSE 0bin0 ENDIF"; + } + else + convert_dplib_expr(expr); +} + +/*******************************************************************\ + +Function: dplib_convt::convert_array_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::convert_array_value(const exprt &expr) +{ + convert_as_bv(expr); +} + +/*******************************************************************\ + +Function: dplib_convt::convert_dplib_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::convert_dplib_expr(const exprt &expr) +{ + if(expr.id()=="symbol") + { + convert_identifier(expr.get_string("identifier")); + } + else if(expr.id()=="nondet_symbol") + { + convert_identifier("nondet$"+expr.get_string("identifier")); + } + else if(expr.id()=="typecast") + { + assert(expr.operands().size()==1); + const exprt &op=expr.op0(); + + if(expr.type().id()=="bool") + { + if(op.type().id()=="signedbv" || + op.type().id()=="unsignedbv" || + op.type().id()=="pointer") + { + convert_dplib_expr(op); + dplib_prop.out << "/="; + convert_dplib_expr(gen_zero(op.type())); + } + else + { + throw "TODO typecast1 "+op.type().id_string()+" -> bool"; + } + } + else if(expr.type().id()=="signedbv" || + expr.type().id()=="unsignedbv") + { + unsigned to_width=atoi(expr.type().get("width").c_str()); + + if(op.type().id()=="signedbv") + { + unsigned from_width=atoi(op.type().get("width").c_str()); + + if(from_width==to_width) + convert_dplib_expr(op); + else if(from_width1) + { + dplib_prop.out << "(0bin"; + + for(unsigned i=1; i "+expr.type().id_string(); + } + } + else if(expr.type().id()=="pointer") + { + if(op.type().id()=="pointer") + { + convert_dplib_expr(op); + } + else + throw "TODO typecast3 "+op.type().id_string()+" -> pointer"; + } + else + throw "TODO typecast4 ? -> "+expr.type().id_string(); + } + else if(expr.id()=="struct") + { + dplib_prop.out << "(# "; + + const struct_typet &struct_type=to_struct_type(expr.type()); + + const struct_typet::componentst &components= + struct_type.components(); + + assert(components.size()==expr.operands().size()); + + unsigned i=0; + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++, i++) + { + if(i!=0) dplib_prop.out << ", "; + dplib_prop.out << it->get("name"); + dplib_prop.out << ":="; + convert_dplib_expr(expr.operands()[i]); + } + + dplib_prop.out << " #)"; + } + else if(expr.id()=="constant") + { + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv" || + expr.type().id()=="bv") + { + dplib_prop.out << "0bin" << expr.get("value"); + } + else if(expr.type().id()=="pointer") + { + const irep_idt &value=expr.get("value"); + + if(value=="NULL") + { + dplib_prop.out << "(# object:=" + << pointer_logic.get_null_object() + << ", offset:=" + << bin_zero(config.ansi_c.pointer_width) << " #)"; + } + else + throw "unknown pointer constant: "+id2string(value); + } + else if(expr.type().id()=="bool") + { + if(expr.is_true()) + dplib_prop.out << "TRUE"; + else if(expr.is_false()) + dplib_prop.out << "FALSE"; + else + throw "unknown boolean constant"; + } + else if(expr.type().id()=="array") + { + dplib_prop.out << "ARRAY (i: " << array_index_type() << "):"; + + assert(expr.operands().size()!=0); + + unsigned i=0; + forall_operands(it, expr) + { + if(i==0) + dplib_prop.out << "\n IF "; + else + dplib_prop.out << "\n ELSIF "; + + dplib_prop.out << "i=" << array_index(i) << " THEN "; + convert_array_value(*it); + i++; + } + + dplib_prop.out << "\n ELSE "; + convert_dplib_expr(expr.op0()); + dplib_prop.out << "\n ENDIF"; + } + else if(expr.type().id()=="integer" || + expr.type().id()=="natural" || + expr.type().id()=="range") + { + dplib_prop.out << expr.get("value"); + } + else + { + std::cerr << expr.pretty() << std::endl; + throw "unknown constant: "+expr.type().id_string(); + } + } + else if(expr.id()=="concatenation" || + expr.id()=="bitand" || + expr.id()=="bitor") + { + dplib_prop.out << "("; + + forall_operands(it, expr) + { + if(it!=expr.operands().begin()) + { + if(expr.id()=="concatenation") + dplib_prop.out << " @ "; + else if(expr.id()=="bitand") + dplib_prop.out << " & "; + else if(expr.id()=="bitor") + dplib_prop.out << " | "; + } + + convert_as_bv(*it); + } + + dplib_prop.out << ")"; + } + else if(expr.id()=="bitxor") + { + assert(!expr.operands().empty()); + + if(expr.operands().size()==1) + { + convert_dplib_expr(expr.op0()); + } + else if(expr.operands().size()==2) + { + dplib_prop.out << "BVXOR("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else + { + assert(expr.operands().size()>=3); + + exprt tmp(expr); + tmp.operands().resize(tmp.operands().size()-1); + + dplib_prop.out << "BVXOR("; + convert_dplib_expr(tmp); + dplib_prop.out << ", "; + convert_dplib_expr(expr.operands().back()); + dplib_prop.out << ")"; + } + } + else if(expr.id()=="bitnand") + { + assert(expr.operands().size()==2); + + dplib_prop.out << "BVNAND("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else if(expr.id()=="bitnot") + { + assert(expr.operands().size()==1); + dplib_prop.out << "~("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ")"; + } + else if(expr.id()=="unary-") + { + assert(expr.operands().size()==1); + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + dplib_prop.out << "BVUMINUS("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ")"; + } + else + throw "unsupported type for unary-: "+expr.type().id_string(); + } + else if(expr.id()=="if") + { + assert(expr.operands().size()==3); + dplib_prop.out << "IF "; + convert_dplib_expr(expr.op0()); + dplib_prop.out << " THEN "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << " ELSE "; + convert_dplib_expr(expr.op2()); + dplib_prop.out << " ENDIF"; + } + else if(expr.id()=="and" || + expr.id()=="or" || + expr.id()=="xor") + { + assert(expr.type().id()=="bool"); + + if(expr.operands().size()>=2) + { + forall_operands(it, expr) + { + if(it!=expr.operands().begin()) + { + if(expr.id()=="and") + dplib_prop.out << " AND "; + else if(expr.id()=="or") + dplib_prop.out << " OR "; + else if(expr.id()=="xor") + dplib_prop.out << " XOR "; + } + + dplib_prop.out << "("; + convert_dplib_expr(*it); + dplib_prop.out << ")"; + } + } + else if(expr.operands().size()==1) + { + convert_dplib_expr(expr.op0()); + } + else + assert(false); + } + else if(expr.id()=="not") + { + assert(expr.operands().size()==1); + dplib_prop.out << "NOT ("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ")"; + } + else if(expr.id()=="=" || + expr.id()=="notequal") + { + assert(expr.operands().size()==2); + assert(expr.op0().type()==expr.op1().type()); + + if(expr.op0().type().id()=="bool") + { + if(expr.id()=="notequal") dplib_prop.out << "NOT ("; + dplib_prop.out << "("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ") <=> ("; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + if(expr.id()=="notequal") dplib_prop.out << ")"; + } + else + { + dplib_prop.out << "("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ")"; + dplib_prop.out << (expr.id()=="="?"=":"/="); + dplib_prop.out << "("; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + } + else if(expr.id()=="<=" || + expr.id()=="<" || + expr.id()==">=" || + expr.id()==">") + { + assert(expr.operands().size()==2); + + const typet &op_type=expr.op0().type(); + + if(op_type.id()=="unsignedbv") + { + if(expr.id()=="<=") + dplib_prop.out << "BVLE"; + else if(expr.id()=="<") + dplib_prop.out << "BVLT"; + else if(expr.id()==">=") + dplib_prop.out << "BVGE"; + else if(expr.id()==">") + dplib_prop.out << "BVGT"; + + dplib_prop.out << "("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else if(op_type.id()=="signedbv") + { + if(expr.id()=="<=") + dplib_prop.out << "SBVLE"; + else if(expr.id()=="<") + dplib_prop.out << "SBVLT"; + else if(expr.id()==">=") + dplib_prop.out << "SBVGE"; + else if(expr.id()==">") + dplib_prop.out << "SBVGT"; + + dplib_prop.out << "("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else + throw "unsupported type for "+expr.id_string()+": "+expr.type().id_string(); + } + else if(expr.id()=="+") + { + if(expr.operands().size()>=2) + { + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + dplib_prop.out << "BVPLUS(" << expr.type().get("width"); + + forall_operands(it, expr) + { + dplib_prop.out << ", "; + convert_dplib_expr(*it); + } + + dplib_prop.out << ")"; + } + else if(expr.type().id()=="pointer") + { + if(expr.operands().size()!=2) + throw "pointer arithmetic with more than two operands"; + + const exprt *p, *i; + + if(expr.op0().type().id()=="pointer") + { + p=&expr.op0(); + i=&expr.op1(); + } + else if(expr.op1().type().id()=="pointer") + { + p=&expr.op1(); + i=&expr.op0(); + } + else + throw "unexpected mixture in pointer arithmetic"; + + dplib_prop.out << "(LET P: " << dplib_pointer_type() << " = "; + convert_dplib_expr(*p); + dplib_prop.out << " IN P WITH .offset:=BVPLUS(" + << config.ansi_c.pointer_width + << ", P.offset, "; + convert_dplib_expr(*i); + dplib_prop.out << "))"; + } + else + throw "unsupported type for +: "+expr.type().id_string(); + } + else if(expr.operands().size()==1) + { + convert_dplib_expr(expr.op0()); + } + else + assert(false); + } + else if(expr.id()=="-") + { + if(expr.operands().size()==2) + { + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + dplib_prop.out << "BVSUB(" << expr.type().get("width") << ", "; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else + throw "unsupported type for -: "+expr.type().id_string(); + } + else if(expr.operands().size()==1) + { + convert_dplib_expr(expr.op0()); + } + else + assert(false); + } + else if(expr.id()=="/") + { + assert(expr.operands().size()==2); + + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + if(expr.type().id()=="unsignedbv") + dplib_prop.out << "BVDIV"; + else + dplib_prop.out << "SBVDIV"; + + dplib_prop.out << "(" << expr.type().get("width") << ", "; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else + throw "unsupported type for /: "+expr.type().id_string(); + } + else if(expr.id()=="mod") + { + assert(expr.operands().size()==2); + + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + if(expr.type().id()=="unsignedbv") + dplib_prop.out << "BVMOD"; + else + dplib_prop.out << "SBVMOD"; + + dplib_prop.out << "(" << expr.type().get("width") << ", "; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else + throw "unsupported type for mod: "+expr.type().id_string(); + } + else if(expr.id()=="*") + { + if(expr.operands().size()==2) + { + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + dplib_prop.out << "BVMULT(" << expr.type().get("width") << ", "; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else + throw "unsupported type for *: "+expr.type().id_string(); + } + else if(expr.operands().size()==1) + { + convert_dplib_expr(expr.op0()); + } + else + assert(false); + } + else if(expr.id()=="address_of" || + expr.id()=="implicit_address_of" || + expr.id()=="reference_to") + { + assert(expr.operands().size()==1); + assert(expr.type().id()=="pointer"); + convert_address_of_rec(expr.op0()); + } + else if(expr.id()=="array_of") + { + assert(expr.type().id()=="array"); + assert(expr.operands().size()==1); + dplib_prop.out << "(ARRAY (i: " << array_index_type() << "): "; + convert_array_value(expr.op0()); + dplib_prop.out << ")"; + } + else if(expr.id()=="index") + { + assert(expr.operands().size()==2); + dplib_prop.out << "("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ")["; + convert_array_index(expr.op1()); + dplib_prop.out << "]"; + } + else if(expr.id()=="ashr" || + expr.id()=="lshr" || + expr.id()=="shl") + { + assert(expr.operands().size()==2); + + if(expr.type().id()=="unsignedbv" || + expr.type().id()=="signedbv") + { + if(expr.id()=="ashr") + dplib_prop.out << "BVASHR"; + else if(expr.id()=="lshr") + dplib_prop.out << "BVLSHR"; + else if(expr.id()=="shl") + dplib_prop.out << "BVSHL"; + else + assert(false); + + dplib_prop.out << "(" << expr.type().get("width") << ", "; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ", "; + convert_dplib_expr(expr.op1()); + dplib_prop.out << ")"; + } + else + throw "unsupported type for "+expr.id_string()+": "+expr.type().id_string(); + } + else if(expr.id()=="with") + { + assert(expr.operands().size()>=1); + dplib_prop.out << "("; + convert_dplib_expr(expr.op0()); + dplib_prop.out << ")"; + + for(unsigned i=1; i s_set; + + ::find_symbols(expr.op1(), s_set); + + if(s_set.find(identifier)==s_set.end()) + { + id.type=expr.op0().type(); + + find_symbols(expr.op1()); + + convert_identifier(id2string(identifier)); + dplib_prop.out << ": "; + convert_dplib_type(expr.op0().type()); + dplib_prop.out << " = "; + convert_dplib_expr(expr.op1()); + + dplib_prop.out << ";" << std::endl << std::endl; + return; + } + } + } + } + + find_symbols(expr); + + dplib_prop.out << "AXIOM "; + + if(!value) + dplib_prop.out << "! ("; + + convert_dplib_expr(expr); + + if(!value) + dplib_prop.out << ")"; + + dplib_prop.out << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_convt::find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::find_symbols(const exprt &expr) +{ + find_symbols(expr.type()); + + forall_operands(it, expr) + find_symbols(*it); + + if(expr.id()=="symbol") + { + if(expr.type().id()=="code") + return; + + const irep_idt &identifier=expr.get("identifier"); + + identifiert &id=identifier_map[identifier]; + + if(id.type.is_nil()) + { + id.type=expr.type(); + + convert_identifier(id2string(identifier)); + dplib_prop.out << ": "; + convert_dplib_type(expr.type()); + dplib_prop.out << ";" << std::endl; + } + } + else if(expr.id()=="nondet_symbol") + { + if(expr.type().id()=="code") + return; + + const irep_idt identifier="nondet$"+expr.get_string("identifier"); + + identifiert &id=identifier_map[identifier]; + + if(id.type.is_nil()) + { + id.type=expr.type(); + + convert_identifier(id2string(identifier)); + dplib_prop.out << ": "; + convert_dplib_type(expr.type()); + dplib_prop.out << ";" << std::endl; + } + } +} + +/*******************************************************************\ + +Function: dplib_convt::convert_dplib_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::convert_dplib_type(const typet &type) +{ + if(type.id()=="array") + { + const array_typet &array_type=to_array_type(type); + + dplib_prop.out << "ARRAY " << array_index_type() + << " OF "; + + if(array_type.subtype().id()=="bool") + dplib_prop.out << "BITVECTOR(1)"; + else + convert_dplib_type(array_type.subtype()); + } + else if(type.id()=="bool") + { + dplib_prop.out << "boolean"; + } + else if(type.id()=="struct" || + type.id()=="union") + { + const struct_typet &struct_type=to_struct_type(type); + + dplib_prop.out << "[#"; + + const struct_typet::componentst &components= + struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + if(it!=components.begin()) dplib_prop.out << ","; + dplib_prop.out << " "; + dplib_prop.out << it->get("name"); + dplib_prop.out << ": "; + convert_dplib_type(it->type()); + } + + dplib_prop.out << " #]"; + } + else if(type.id()=="pointer" || + type.id()=="reference") + { + dplib_prop.out << dplib_pointer_type(); + } + else if(type.id()=="integer") + { + dplib_prop.out << "int"; + } + else if(type.id()=="signedbv") + { + unsigned width=to_signedbv_type(type).get_width(); + + if(width==0) + throw "zero-width vector type: "+type.id_string(); + + dplib_prop.out << "signed[" << width << "]"; + } + else if(type.id()=="unsignedbv") + { + unsigned width=to_unsignedbv_type(type).get_width(); + + if(width==0) + throw "zero-width vector type: "+type.id_string(); + + dplib_prop.out << "unsigned[" << width << "]"; + } + else if(type.id()=="bv") + { + unsigned width=to_bv_type(type).get_width(); + + if(width==0) + throw "zero-width vector type: "+type.id_string(); + + dplib_prop.out << "bv[" << width << "]"; + } + else + throw "unsupported type: "+type.id_string(); +} + +/*******************************************************************\ + +Function: dplib_convt::find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_convt::find_symbols(const typet &type) +{ + if(type.id()=="array") + { + const array_typet &array_type=to_array_type(type); + find_symbols(array_type.size()); + } + else if(type.id()=="struct" || + type.id()=="union") + { + } +} diff --git a/src/solvers/dplib/dplib_conv.h b/src/solvers/dplib/dplib_conv.h new file mode 100644 index 00000000000..de9e98a4ff5 --- /dev/null +++ b/src/solvers/dplib/dplib_conv.h @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PROP_DPLIB_CONV_H +#define CPROVER_PROP_DPLIB_CONV_H + +#include + +#include +#include + +#include "dplib_prop.h" + +class dplib_prop_wrappert +{ +public: + dplib_prop_wrappert(std::ostream &_out):dplib_prop(_out) { } + +protected: + dplib_propt dplib_prop; +}; + +class dplib_convt:protected dplib_prop_wrappert, public prop_convt +{ +public: + dplib_convt( + const namespacet &_ns, + std::ostream &_out): + dplib_prop_wrappert(_out), + prop_convt(_ns, dplib_prop), + pointer_logic(_ns) { } + + virtual ~dplib_convt() { } + +protected: + virtual literalt convert_rest(const exprt &expr); + virtual void convert_dplib_expr(const exprt &expr); + virtual void convert_dplib_type(const typet &type); + virtual void set_to(const exprt &expr, bool value); + virtual void convert_address_of_rec(const exprt &expr); + + pointer_logict pointer_logic; + +private: + void convert_identifier(const std::string &identifier); + void find_symbols(const exprt &expr); + void find_symbols(const typet &type); + void convert_array_value(const exprt &expr); + void convert_as_bv(const exprt &expr); + void convert_array_index(const exprt &expr); + static typet gen_array_index_type(); + static std::string bin_zero(unsigned bits); + static std::string array_index_type(); + static std::string array_index(unsigned i); + static std::string dplib_pointer_type(); + + struct identifiert + { + typet type; + exprt value; + + identifiert() + { + type.make_nil(); + value.make_nil(); + } + }; + + typedef hash_map_cont + identifier_mapt; + + identifier_mapt identifier_map; +}; + +#endif diff --git a/src/solvers/dplib/dplib_dec.cpp b/src/solvers/dplib/dplib_dec.cpp new file mode 100644 index 00000000000..dad42851d98 --- /dev/null +++ b/src/solvers/dplib/dplib_dec.cpp @@ -0,0 +1,212 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#ifdef _WIN32 +#include +#define getpid _getpid +#endif + +#include +#include +#include + +#include "dplib_dec.h" + +/*******************************************************************\ + +Function: dplib_temp_filet::dplib_temp_filet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +dplib_temp_filet::dplib_temp_filet() +{ + temp_out_filename="dplib_dec_out_"+i2string(getpid())+".tmp"; + + temp_out.open( + temp_out_filename.c_str(), + std::ios_base::out | std::ios_base::trunc); +} + +/*******************************************************************\ + +Function: dplib_temp_filet::~dplib_temp_filet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +dplib_temp_filet::~dplib_temp_filet() +{ + temp_out.close(); + + if(temp_out_filename!="") + unlink(temp_out_filename.c_str()); + + if(temp_result_filename!="") + unlink(temp_result_filename.c_str()); +} + +/*******************************************************************\ + +Function: dplib_dect::dec_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt dplib_dect::dec_solve() +{ + dplib_prop.out << "QUERY FALSE;" << std::endl; + dplib_prop.out << "COUNTERMODEL;" << std::endl; + + post_process(); + + temp_out.close(); + + temp_result_filename= + "dplib_dec_result_"+i2string(getpid())+".tmp"; + + std::string command= + "dplibl "+temp_out_filename+" > "+temp_result_filename+" 2>&1"; + + system(command.c_str()); + + status("Reading result from CVCL"); + + return read_dplib_result(); +} + +/*******************************************************************\ + +Function: dplib_dect::read_assert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_dect::read_assert(std::istream &in, std::string &line) +{ + // strip ASSERT + line=std::string(line, strlen("ASSERT "), std::string::npos); + if(line=="") return; + + // bit-vector + if(line[0]=='(') + { + // get identifier + std::string::size_type pos= + line.find(' '); + + std::string identifier=std::string(line, 1, pos-1); + + // get value + if(!str_getline(in, line)) return; + + // skip spaces + pos=0; + while(pos" << identifier << "< = >" << value << "<"; + + std::cout << std::endl; + } + else + { + // boolean + tvt value=tvt(true); + + if(has_prefix(line, "NOT ")) + { + line=std::string(line, strlen("NOT "), std::string::npos); + value=tvt(false); + } + + if(line=="") return; + + if(line[0]=='l') + { + unsigned number=atoi(line.c_str()+1); + assert(number + +#include "dplib_conv.h" + +class dplib_temp_filet +{ +public: + dplib_temp_filet(); + ~dplib_temp_filet(); + +protected: + std::ofstream temp_out; + std::string temp_out_filename, temp_result_filename; +}; + +class dplib_dect:protected dplib_temp_filet, public dplib_convt +{ +public: + explicit dplib_dect(const namespacet &_ns): + dplib_convt(_ns, temp_out) + { + } + + virtual resultt dec_solve(); + +protected: + resultt read_dplib_result(); + void read_assert(std::istream &in, std::string &line); +}; + +#endif diff --git a/src/solvers/dplib/dplib_prop.cpp b/src/solvers/dplib/dplib_prop.cpp new file mode 100644 index 00000000000..8e0abf250b0 --- /dev/null +++ b/src/solvers/dplib/dplib_prop.cpp @@ -0,0 +1,602 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include + +#include "dplib_prop.h" + +/*******************************************************************\ + +Function: dplib_propt::dplib_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +dplib_propt::dplib_propt(std::ostream &_out):out(_out) +{ + // we skip index 0 + _no_variables=1; +} + +/*******************************************************************\ + +Function: dplib_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::land(literalt a, literalt b, literalt o) +{ + out << "// land" << std::endl; + out << "AXIOM (" << dplib_literal(a) << " & " + << dplib_literal(b) << ") <=> " << dplib_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::lor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::lor(literalt a, literalt b, literalt o) +{ + out << "// lor" << std::endl; + out << "AXIOM (" << dplib_literal(a) << " | " + << dplib_literal(b) << ") <=> " << dplib_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::lxor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::lxor(literalt a, literalt b, literalt o) +{ + out << "// lxor" << std::endl; + out << "AXIOM (" << dplib_literal(a) << " <=> " + << dplib_literal(b) << ") <=> !" << dplib_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::lnand + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::lnand(literalt a, literalt b, literalt o) +{ + out << "// lnand" << std::endl; + out << "AXIOM (" << dplib_literal(a) << " & " + << dplib_literal(b) << ") <=> !" << dplib_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::lnor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::lnor(literalt a, literalt b, literalt o) +{ + out << "// lnor" << std::endl; + out << "AXIOM (" << dplib_literal(a) << " | " + << dplib_literal(b) << ") <=> !" << dplib_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::lequal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::lequal(literalt a, literalt b, literalt o) +{ + out << "// lequal" << std::endl; + out << "AXIOM (" << dplib_literal(a) << " <=> " + << dplib_literal(b) << ") <=> " << dplib_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::limplies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::limplies(literalt a, literalt b, literalt o) +{ + out << "// limplies" << std::endl; + out << "AXIOM (" << dplib_literal(a) << " => " + << dplib_literal(b) << ") <=> " << dplib_literal(o) + << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_propt::land(const bvt &bv) +{ + out << "// land" << std::endl; + + literalt literal=def_dplib_literal(); + + for(unsigned int i=0; i " << dplib_literal(b) + << ");" << std::endl << std::endl; + + return o; +} + +/*******************************************************************\ + +Function: dplib_propt::lnand + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_propt::lnand(literalt a, literalt b) +{ + return lnot(land(a, b)); +} + +/*******************************************************************\ + +Function: dplib_propt::lnor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_propt::lnor(literalt a, literalt b) +{ + return lnot(lor(a, b)); +} + +/*******************************************************************\ + +Function: dplib_propt::lequal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_propt::lequal(literalt a, literalt b) +{ + return lnot(lxor(a, b)); +} + +/*******************************************************************\ + +Function: dplib_propt::limplies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_propt::limplies(literalt a, literalt b) +{ + return lor(lnot(a), b); +} + +/*******************************************************************\ + +Function: dplib_propt::lselect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_propt::lselect(literalt a, literalt b, literalt c) +{ + if(a==const_literal(true)) return b; + if(a==const_literal(false)) return c; + if(b==c) return b; + + out << "// lselect" << std::endl; + + literalt o=def_dplib_literal(); + + out << "IF " << dplib_literal(a) << " THEN " + << dplib_literal(b) << " ELSE " + << dplib_literal(c) << " ENDIF;" + << std::endl << std::endl; + + return o; +} + +/*******************************************************************\ + +Function: dplib_propt::new_variable + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_propt::new_variable() +{ + _no_variables++; + out << "l" << _no_variables << ": boolean;" << std::endl; + literalt l; + l.set(_no_variables, false); + return l; +} + +/*******************************************************************\ + +Function: dplib_propt::def_dplib_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt dplib_propt::def_dplib_literal() +{ + _no_variables++; + out << "l" << _no_variables << ": boolean = "; + literalt l; + l.set(_no_variables, false); + return l; +} + +/*******************************************************************\ + +Function: dplib_propt::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::lcnf(const bvt &bv) +{ + if(bv.empty()) return; + bvt new_bv; + + std::set s; + + new_bv.reserve(bv.size()); + + for(bvt::const_iterator it=bv.begin(); it!=bv.end(); it++) + { + if(s.insert(*it).second) + new_bv.push_back(*it); + + if(s.find(lnot(*it))!=s.end()) + return; // clause satisfied + + assert(it->var_no()<=_no_variables); + } + + assert(!new_bv.empty()); + + out << "// lcnf" << std::endl; + out << "AXIOM "; + + for(bvt::const_iterator it=new_bv.begin(); it!=new_bv.end(); it++) + { + if(it!=new_bv.begin()) out << " | "; + out << dplib_literal(*it); + } + + out << ";" << std::endl << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::dplib_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string dplib_propt::dplib_literal(literalt l) +{ + if(l==const_literal(false)) + return "FALSE"; + else if(l==const_literal(true)) + return "TRUE"; + + if(l.sign()) + return "(NOT l"+i2string(l.var_no())+")"; + + return "l"+i2string(l.var_no()); +} + +/*******************************************************************\ + +Function: dplib_propt::finish + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dplib_propt::finish() +{ + // we want satisfiability + out << "THEOREM false;" << std::endl; +} + +/*******************************************************************\ + +Function: dplib_propt::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt dplib_propt::prop_solve() +{ + finish(); + return P_ERROR; +} diff --git a/src/solvers/dplib/dplib_prop.h b/src/solvers/dplib/dplib_prop.h new file mode 100644 index 00000000000..aeacf2ea787 --- /dev/null +++ b/src/solvers/dplib/dplib_prop.h @@ -0,0 +1,88 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PROP_DPLIB_PROP_H +#define CPROVER_PROP_DPLIB_PROP_H + +#include + +#include + +class dplib_propt:virtual public propt +{ +public: + dplib_propt(std::ostream &_out); + virtual ~dplib_propt() { } + + virtual void land(literalt a, literalt b, literalt o); + virtual void lor(literalt a, literalt b, literalt o); + virtual void lxor(literalt a, literalt b, literalt o); + virtual void lnand(literalt a, literalt b, literalt o); + virtual void lnor(literalt a, literalt b, literalt o); + virtual void lequal(literalt a, literalt b, literalt o); + virtual void limplies(literalt a, literalt b, literalt o); + + virtual literalt land(literalt a, literalt b); + virtual literalt lor(literalt a, literalt b); + virtual literalt land(const bvt &bv); + virtual literalt lor(const bvt &bv); + virtual literalt lxor(const bvt &bv); + virtual literalt lnot(literalt a); + virtual literalt lxor(literalt a, literalt b); + virtual literalt lnand(literalt a, literalt b); + virtual literalt lnor(literalt a, literalt b); + virtual literalt lequal(literalt a, literalt b); + virtual literalt limplies(literalt a, literalt b); + virtual literalt lselect(literalt a, literalt b, literalt c); // a?b:c + virtual literalt new_variable(); + virtual unsigned no_variables() const { return _no_variables; } + virtual void set_no_variables(unsigned no) { assert(false); } + //virtual unsigned no_clauses()=0; + + virtual void lcnf(const bvt &bv); + + virtual const std::string solver_text() + { return "DPLIB"; } + + virtual tvt l_get(literalt literal) const + { + unsigned v=literal.var_no(); + if(v>=assignment.size()) return tvt(tvt::TV_UNKNOWN); + tvt r=assignment[v]; + return literal.sign()?!r:r; + } + + virtual propt::resultt prop_solve(); + + friend class dplib_convt; + friend class dplib_dect; + + virtual void clear() + { + assignment.clear(); + } + + void reset_assignment() + { + assignment.clear(); + assignment.resize(no_variables(), tvt(tvt::TV_UNKNOWN)); + } + +protected: + unsigned _no_variables; + std::ostream &out; + + std::string dplib_literal(literalt l); + literalt def_dplib_literal(); + + std::vector assignment; + + void finish(); +}; + +#endif diff --git a/src/solvers/flattening/arrays.cpp b/src/solvers/flattening/arrays.cpp new file mode 100644 index 00000000000..8b0c87d8fab --- /dev/null +++ b/src/solvers/flattening/arrays.cpp @@ -0,0 +1,602 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +//#define DEBUG + +#include + +#include +#include +#include +#include + +#include "arrays.h" + +/*******************************************************************\ + +Function: arrayst::arrayst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +arrayst::arrayst( + const namespacet &_ns, + propt &_prop):equalityt(_ns, _prop) +{ +} + +/*******************************************************************\ + +Function: arrayst::record_array_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::record_array_index(const index_exprt &index) +{ + unsigned number=arrays.number(index.array()); + if(number>=index_map.size()) index_map.resize(number+1); + index_map[number].insert(index.index()); +} + +/*******************************************************************\ + +Function: arrayst::record_array_equality + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt arrayst::record_array_equality( + const equality_exprt &equality) +{ + const exprt &op0=equality.op0(); + const exprt &op1=equality.op1(); + + // check types + if(!base_type_eq(op0.type(), op1.type(), ns)) + { + std::cout << equality.pretty() << std::endl; + throw "record_array_equality got equality without matching types"; + } + + assert(ns.follow(op0.type()).id()==ID_array); + + array_equalities.push_back(array_equalityt()); + + array_equalities.back().f1=op0; + array_equalities.back().f2=op1; + array_equalities.back().l=SUB::equality(op0, op1); + + arrays.make_union(op0, op1); + collect_arrays(op0); + collect_arrays(op1); + + return array_equalities.back().l; +} + +/*******************************************************************\ + +Function: arrayst::collect_arrays + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::collect_arrays(const exprt &a) +{ + const array_typet &array_type= + to_array_type(ns.follow(a.type())); + + if(a.id()==ID_with) + { + if(a.operands().size()!=3) + throw "with expected to have three operands"; + + // check types + if(!base_type_eq(array_type, a.op0().type(), ns)) + { + std::cout << a.pretty() << std::endl; + throw "collect_arrays got with without matching types"; + } + + arrays.make_union(a, a.op0()); + collect_arrays(a.op0()); + + // make sure this shows as an application + index_exprt index_expr; + index_expr.type()=array_type.subtype(); + index_expr.array()=a.op0(); + index_expr.index()=a.op1(); + record_array_index(index_expr); + } + else if(a.id()==ID_if) + { + if(a.operands().size()!=3) + throw "if expected to have three operands"; + + // check types + if(!base_type_eq(array_type, a.op1().type(), ns)) + { + std::cout << a.pretty() << std::endl; + throw "collect_arrays got if without matching types"; + } + + // check types + if(!base_type_eq(array_type, a.op2().type(), ns)) + { + std::cout << a.pretty() << std::endl; + throw "collect_arrays got if without matching types"; + } + + arrays.make_union(a, a.op1()); + arrays.make_union(a, a.op2()); + collect_arrays(a.op1()); + collect_arrays(a.op2()); + } + else if(a.id()==ID_symbol) + { + } + else if(a.id()==ID_nondet_symbol) + { + } + else if(a.id()==ID_member) + { + if(to_member_expr(a).struct_op().id()!=ID_symbol) + throw "unexpected array expression: member with `"+a.op0().id_string()+"'"; + } + else if(a.id()==ID_constant || + a.id()==ID_array || + a.id()==ID_string_constant) + { + } + else if(a.id()==ID_array_of) + { + } + else + throw "unexpected array expression: `"+a.id_string()+"'"; +} + +/*******************************************************************\ + +Function: arrayst::add_array_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::add_array_constraints() +{ + // first get index map + build_index_map(); + + // add constraints for if, with, array_of + for(unsigned i=0; if1)], + *it); + + // add the Ackermann constraints + add_array_Ackermann_constraints(); +} + +/*******************************************************************\ + +Function: arrayst::add_array_Ackermann_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::add_array_Ackermann_constraints() +{ + // this is quadratic! + + // iterate over arrays + for(unsigned i=0; iis_constant() && i2->is_constant()) + continue; + + // index equality + equality_exprt indices_equal(*i1, *i2); + + if(indices_equal.op0().type()!= + indices_equal.op1().type()) + { + indices_equal.op1(). + make_typecast(indices_equal.op0().type()); + } + + literalt indices_equal_lit=convert(indices_equal); + + if(indices_equal_lit!=const_literal(false)) + { + index_exprt index_expr1; + index_expr1.type()=ns.follow(arrays[i].type()).subtype(); + index_expr1.array()=arrays[i]; + index_expr1.index()=*i1; + + index_exprt index_expr2=index_expr1; + index_expr2.index()=*i2; + + equality_exprt values_equal(index_expr1, index_expr2); + + bvt implication; + implication.reserve(2); + implication.push_back(prop.lnot(indices_equal_lit)); + implication.push_back(convert(values_equal)); + prop.lcnf(implication); + } + } + } +} + +/*******************************************************************\ + +Function: arrayst::build_index_map + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::build_index_map() +{ + // merge the indices into the root + + if(index_map.size() x[i]=y[i] + + for(index_sett::const_iterator + it=index_set.begin(); + it!=index_set.end(); + it++) + { + index_exprt index_expr1; + index_expr1.type()=ns.follow(array_equality.f1.type()).subtype(); + index_expr1.array()=array_equality.f1; + index_expr1.index()=*it; + + index_exprt index_expr2; + index_expr2.type()=ns.follow(array_equality.f2.type()).subtype(); + index_expr2.array()=array_equality.f2; + index_expr2.index()=*it; + + assert(index_expr1.type()==index_expr2.type()); + + equality_exprt equality_expr(index_expr1, index_expr2); + + // add constraint + bvt bv; + bv.push_back(prop.lnot(array_equality.l)); + bv.push_back(convert(equality_expr)); + prop.lcnf(bv); + } +} + +/*******************************************************************\ + +Function: arrayst::add_array_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::add_array_constraints( + const index_sett &index_set, + const exprt &expr) +{ + if(expr.id()==ID_with) + return add_array_constraints_with(index_set, to_with_expr(expr)); + else if(expr.id()==ID_if) + return add_array_constraints_if(index_set, to_if_expr(expr)); + else if(expr.id()==ID_array_of) + return add_array_constraints_array_of(index_set, to_array_of_expr(expr)); + else if(expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol || + expr.id()==ID_constant || + expr.id()=="zero_string" || + expr.id()==ID_array || + expr.id()==ID_string_constant) + { + } + else if(expr.id()==ID_member && + to_member_expr(expr).struct_op().id()==ID_symbol) + { + } + else + throw "unexpected array expression: `"+expr.id_string()+"'"; +} + +/*******************************************************************\ + +Function: arrayst::add_array_constraints_with + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::add_array_constraints_with( + const index_sett &index_set, + const with_exprt &expr) +{ + // we got x=(y with [i:=v]) + // add constaint x[i]=v + + const exprt &index=expr.where(); + const exprt &value=expr.new_value(); + + { + index_exprt index_expr; + index_expr.type()=ns.follow(expr.type()).subtype(); + index_expr.array()=expr; + index_expr.index()=index; + + if(index_expr.type()!=value.type()) + { + std::cout << expr.pretty() << std::endl; + assert(false); + } + + set_to_true(equality_exprt(index_expr, value)); + } + + // use other array index applications for "else" case + // add constraint x[I]=y[I] for I!=i + + for(index_sett::const_iterator + it=index_set.begin(); + it!=index_set.end(); + it++) + { + exprt other_index=*it; + + if(other_index!=index) + { + // we first build the guard + + if(other_index.type()!=index.type()) + other_index.make_typecast(index.type()); + + literalt guard_lit=convert(equality_exprt(index, other_index)); + + if(guard_lit!=const_literal(true)) + { + index_exprt index_expr1; + index_expr1.type()=ns.follow(expr.type()).subtype(); + index_expr1.array()=expr; + index_expr1.index()=other_index; + + index_exprt index_expr2; + index_expr2.type()=ns.follow(expr.type()).subtype(); + index_expr2.array()=expr.op0(); + index_expr2.index()=other_index; + + assert(index_expr1.type()==index_expr2.type()); + + equality_exprt equality_expr(index_expr1, index_expr2); + + literalt equality_lit=convert(equality_expr); + + // add constraint + bvt bv; + bv.reserve(2); + bv.push_back(equality_lit); + bv.push_back(guard_lit); + prop.lcnf(bv); + } + } + } +} + +/*******************************************************************\ + +Function: arrayst::add_array_constraints_array_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::add_array_constraints_array_of( + const index_sett &index_set, + const array_of_exprt &expr) +{ + // we got x=array_of[v] + // get other array index applications + // and add constraint x[i]=v + + for(index_sett::const_iterator + it=index_set.begin(); + it!=index_set.end(); + it++) + { + index_exprt index_expr; + index_expr.type()=ns.follow(expr.type()).subtype(); + index_expr.array()=expr; + index_expr.index()=*it; + + assert(base_type_eq(index_expr.type(), expr.op0().type(), ns)); + + // add constraint + set_to_true(equality_exprt(index_expr, expr.op0())); + } +} + +/*******************************************************************\ + +Function: arrayst::add_array_constraints_if + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void arrayst::add_array_constraints_if( + const index_sett &index_set, + const if_exprt &expr) +{ + // we got x=(c?a:b) + literalt cond_lit=convert(expr.cond()); + + // get other array index applications + // and add c => x[i]=a[i] + // !c => x[i]=b[i] + + // first do true case + + for(index_sett::const_iterator + it=index_set.begin(); + it!=index_set.end(); + it++) + { + index_exprt index_expr1; + index_expr1.type()=ns.follow(expr.type()).subtype(); + index_expr1.array()=expr; + index_expr1.index()=*it; + + index_exprt index_expr2; + index_expr2.type()=ns.follow(expr.type()).subtype(); + index_expr2.array()=expr.true_case(); + index_expr2.index()=*it; + + assert(index_expr1.type()==index_expr2.type()); + + // add implication + bvt bv; + bv.push_back(prop.lnot(cond_lit)); + bv.push_back(convert(equality_exprt(index_expr1, index_expr2))); + prop.lcnf(bv); + } + + // now the false case + + for(index_sett::const_iterator + it=index_set.begin(); + it!=index_set.end(); + it++) + { + index_exprt index_expr1; + index_expr1.type()=ns.follow(expr.type()).subtype(); + index_expr1.array()=expr; + index_expr1.index()=*it; + + index_exprt index_expr2; + index_expr2.type()=ns.follow(expr.type()).subtype(); + index_expr2.array()=expr.false_case(); + index_expr2.index()=*it; + + assert(index_expr1.type()==index_expr2.type()); + + // add implication + bvt bv; + bv.push_back(cond_lit); + bv.push_back(convert(equality_exprt(index_expr1, index_expr2))); + prop.lcnf(bv); + } +} diff --git a/src/solvers/flattening/arrays.h b/src/solvers/flattening/arrays.h new file mode 100644 index 00000000000..a6abb5b57fb --- /dev/null +++ b/src/solvers/flattening/arrays.h @@ -0,0 +1,71 @@ +/*******************************************************************\ + +Module: Theory of Arrays with Extensionality + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ARRAYS_H +#define CPROVER_ARRAYS_H + +#include +#include + +#include "equality.h" + +class arrayst:public equalityt +{ +public: + arrayst(const namespacet &_ns, propt &_prop); + + virtual void post_process() + { + post_process_arrays(); + SUB::post_process(); + } + + typedef equalityt SUB; + + literalt record_array_equality(const class equality_exprt &expr); + void record_array_index(const class index_exprt &expr); + +protected: + virtual void post_process_arrays() + { + add_array_constraints(); + } + + struct array_equalityt + { + literalt l; + exprt f1, f2; + }; + + // the list of all equalities between arrays + typedef std::vector array_equalitiest; + array_equalitiest array_equalities; + + // this is used to find the clusters of arrays being compared + union_find arrays; + + // this tracks the array indicies for each array + typedef std::set index_sett; + typedef std::vector index_mapt; + index_mapt index_map; + + // adds all the constraints eagerly + void add_array_constraints(); + void add_array_Ackermann_constraints(); + void add_array_constraints_equality(const index_sett &index_set, const array_equalityt &array_equality); + void add_array_constraints(const index_sett &index_set, const exprt &expr); + void add_array_constraints(const index_sett &index_set, const array_equalityt &array_equality); + void add_array_constraints_if(const index_sett &index_set, const class if_exprt &exprt); + void add_array_constraints_with(const index_sett &index_set, const class with_exprt &expr); + void add_array_constraints_array_of(const index_sett &index_set, const class array_of_exprt &exprt); + + void build_index_map(); + void collect_arrays(const exprt &a); +}; + +#endif diff --git a/src/solvers/flattening/boolbv.cpp b/src/solvers/flattening/boolbv.cpp new file mode 100644 index 00000000000..3d1f4820b88 --- /dev/null +++ b/src/solvers/flattening/boolbv.cpp @@ -0,0 +1,927 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "boolbv.h" +#include "boolbv_type.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +//#define DEBUG + +/*******************************************************************\ + +Function: boolbvt::literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool boolbvt::literal( + const exprt &expr, + const unsigned bit, + literalt &dest) const +{ + if(expr.type().id()==ID_bool) + { + assert(bit==0); + return prop_convt::literal(expr, dest); + } + else + { + if(expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol) + { + const irep_idt &identifier=expr.get(ID_identifier); + + boolbv_mapt::mappingt::const_iterator it_m= + map.mapping.find(identifier); + + if(it_m==map.mapping.end()) return true; + + const boolbv_mapt::map_entryt &map_entry=it_m->second; + + assert(bit(it->find(ID_type)); + + if(it->get(ID_name)==component_name) + return literal(expr.op0(), bit+offset, dest); + + unsigned element_width=boolbv_width(subtype); + + if(element_width==0) + throw "literal expects a bit-vector type"; + + offset+=element_width; + } + + throw "failed to find component"; + } + } + + throw "found no literal for expression"; +} + +/*******************************************************************\ + +Function: boolbvt::convert_bv + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_bv(const exprt &expr, bvt &bv) +{ + // check cache first + + { + bv_cachet::const_iterator cache_result=bv_cache.find(expr); + if(cache_result!=bv_cache.end()) + { + //std::cerr << "Cache hit on " << expr << "\n"; + bv=cache_result->second; + return; + } + } + + convert_bitvector(expr, bv); + + // check + for(unsigned i=0; i(expr, bv)); +} + +/*******************************************************************\ + +Function: boolbvt::conversion_failed + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::conversion_failed(const exprt &expr, bvt &bv) +{ + ignoring(expr); + + // try to make it free bits + unsigned width=boolbv_width(expr.type()); + + bv.resize(width); + + for(unsigned i=0; i=prop.no_variables() && + !bv[i].is_constant()) { std::cout << identifier << std::endl; abort(); } +} + +/*******************************************************************\ + +Function: boolbvt::convert_rest + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbvt::convert_rest(const exprt &expr) +{ + if(expr.type().id()!=ID_bool) + { + std::cerr << expr << std::endl; + throw "boolbvt::convert_rest got non-boolean operand"; + } + + const exprt::operandst &operands=expr.operands(); + + if(expr.id()==ID_typecast) + return convert_typecast(expr); + else if(expr.id()==ID_equal) + return convert_equality(to_equality_expr(expr)); + else if(expr.id()==ID_notequal) + { + if(expr.operands().size()!=2) + throw "notequal expects two operands"; + + equality_exprt e(expr.op0(), expr.op1()); + return prop.lnot(convert_equality(e)); + } + else if(expr.id()==ID_ieee_float_equal || + expr.id()==ID_ieee_float_notequal) + return convert_ieee_float_rel(expr); + else if(expr.id()==ID_le || expr.id()==ID_ge || + expr.id()==ID_lt || expr.id()==ID_gt) + return convert_bv_rel(expr); + else if(expr.id()==ID_extractbit) + return convert_extractbit(expr); + else if(expr.id()==ID_forall) + return convert_quantifier(expr); + else if(expr.id()==ID_exists) + return convert_quantifier(expr); + else if(expr.id()==ID_index) + { + bvt bv; + convert_index(to_index_expr(expr), bv); + + if(bv.size()!=1) + throw "convert_index returned non-bool bitvector"; + + return bv[0]; + } + else if(expr.id()==ID_member) + { + bvt bv; + convert_member(to_member_expr(expr), bv); + + if(bv.size()!=1) + throw "convert_member returned non-bool bitvector"; + + return bv[0]; + } + else if(expr.id()==ID_case) + { + bvt bv; + convert_case(expr, bv); + + if(bv.size()!=1) + throw "convert_case returned non-bool bitvector"; + + return bv[0]; + } + else if(expr.id()==ID_cond) + { + bvt bv; + convert_cond(expr, bv); + + if(bv.size()!=1) + throw "convert_cond returned non-bool bitvector"; + + return bv[0]; + } + else if(expr.id()==ID_sign) + { + if(expr.operands().size()!=1) + throw "sign expects one operand"; + + bvt bv; + convert_bv(operands[0], bv); + + if(bv.size()<1) + throw "sign operator takes one non-empty operand"; + + if(operands[0].type().id()==ID_signedbv) + return bv[bv.size()-1]; + else if(operands[0].type().id()==ID_unsignedbv) + return const_literal(false); + else if(operands[0].type().id()==ID_fixedbv) + return bv[bv.size()-1]; + else if(operands[0].type().id()==ID_floatbv) + return bv[bv.size()-1]; + } + else if(expr.id()==ID_reduction_or || expr.id()==ID_reduction_and || + expr.id()==ID_reduction_nor || expr.id()==ID_reduction_nand || + expr.id()==ID_reduction_xor || expr.id()==ID_reduction_xnor) + return convert_reduction(expr); + else if(has_prefix(expr.id_string(), "overflow-")) + return convert_overflow(expr); + else if(expr.id()==ID_isnan) + { + if(expr.operands().size()!=1) + throw "isnan expects one operand"; + + bvt bv; + convert_bv(operands[0], bv); + + if(expr.op0().type().id()==ID_floatbv) + { + #ifdef HAVE_FLOATBV + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(expr.op0().type()); + return float_utils.is_NaN(bv); + #endif + } + else if(expr.op0().type().id()==ID_fixedbv) + return const_literal(false); + } + else if(expr.id()==ID_isfinite) + { + if(expr.operands().size()!=1) + throw "isfinite expects one operand"; + + bvt bv; + convert_bv(operands[0], bv); + + if(expr.op0().type().id()==ID_floatbv) + { + #ifdef HAVE_FLOATBV + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(expr.op0().type()); + return prop.land( + prop.lnot(float_utils.is_infinity(bv)), + prop.lnot(float_utils.is_NaN(bv))); + #endif + } + else if(expr.op0().type().id()==ID_fixedbv) + return const_literal(true); + } + else if(expr.id()==ID_isinf) + { + if(expr.operands().size()!=1) + throw "isinf expects one operand"; + + bvt bv; + convert_bv(operands[0], bv); + + if(expr.op0().type().id()==ID_floatbv) + { + #ifdef HAVE_FLOATBV + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(expr.op0().type()); + return float_utils.is_infinity(bv); + #endif + } + else if(expr.op0().type().id()==ID_fixedbv) + return const_literal(false); + } + else if(expr.id()==ID_isnormal) + { + if(expr.operands().size()!=1) + throw "isnormal expects one operand"; + + bvt bv; + convert_bv(operands[0], bv); + + if(expr.op0().type().id()==ID_floatbv) + { + #ifdef HAVE_FLOATBV + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(expr.op0().type()); + return float_utils.is_normal(bv); + #endif + } + else if(expr.op0().type().id()==ID_fixedbv) + return const_literal(true); + } + + return SUB::convert_rest(expr); +} + +/*******************************************************************\ + +Function: boolbvt::boolbv_set_equality_to_true + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool boolbvt::boolbv_set_equality_to_true(const exprt &expr) +{ + if(!equality_propagation) return true; + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()==2) + { + if(operands[0].id()==ID_symbol && + operands[0].type()==operands[1].type() && + operands[0].type().id()!=ID_bool) + { + // see if it is an unbounded array + if(is_unbounded_array(operands[0].type())) + return true; + + bvt bv1; + convert_bv(operands[1], bv1); + + const irep_idt &identifier= + operands[0].get(ID_identifier); + + const typet &type=operands[0].type(); + + for(unsigned i=0; i1000) // magic number! + return true; + + return false; +} + +/*******************************************************************\ + +Function: boolbvt::print_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::print_assignment(std::ostream &out) const +{ + for(boolbv_mapt::mappingt::const_iterator it=map.mapping.begin(); + it!=map.mapping.end(); + it++) + { + out << it->first << "="; + const boolbv_mapt::map_entryt &map_entry=it->second; + + std::string result=""; + for(unsigned i=0; i +#include +#include +#include + +#include + +#include "bv_utils.h" +#include "boolbv_width.h" +#include "boolbv_map.h" +#include "arrays.h" +#include "functions.h" + +class boolbvt:public arrayst +{ +public: + boolbvt( + const namespacet &_ns, + propt &_prop): + arrayst(_ns, _prop), + unbounded_array(U_NONE), + bv_utils(_prop), + boolbv_width(_ns), + functions(*this), + map(_prop, _ns, boolbv_width) + { + } + + virtual void convert_bv(const exprt &expr, bvt &bv); // check cache + virtual void convert_bitvector(const exprt &expr, bvt &bv); // no cache + + // overloading + virtual exprt get(const exprt &expr) const; + virtual void set_to(const exprt &expr, bool value); + virtual void print_assignment(std::ostream &out) const; + + virtual void clear_cache() + { + SUB::clear_cache(); + bv_cache.clear(); + } + + virtual void post_process() + { + post_process_quantifiers(); + functions.post_process(); + SUB::post_process(); + } + + // get literals for variables/expressions, if available + virtual bool literal( + const exprt &expr, + unsigned bit, + literalt &literal) const; + + typedef enum { U_NONE, U_ALL, U_AUTO } unbounded_arrayt; + unbounded_arrayt unbounded_array; + + mp_integer get_value(const bvt &bv) + { + return get_value(bv, 0, bv.size()); + } + + mp_integer get_value(const bvt &bv, unsigned offset, unsigned width); + +protected: + bv_utilst bv_utils; + boolbv_widtht boolbv_width; + + // uninterpreted functions + functionst functions; + + // the mapping from identifiers to literals + boolbv_mapt map; + + // overloading + virtual literalt convert_rest(const exprt &expr); + virtual bool boolbv_set_equality_to_true(const exprt &expr); + + typedef arrayst SUB; + + void conversion_failed(const exprt &expr, bvt &bv); + + typedef hash_map_cont bv_cachet; + bv_cachet bv_cache; + + virtual literalt convert_bv_rel(const exprt &expr); + virtual literalt convert_typecast(const exprt &expr); + virtual literalt convert_reduction(const exprt &expr); + virtual literalt convert_extractbit(const exprt &expr); + virtual literalt convert_overflow(const exprt &expr); + virtual literalt convert_equality(const class equality_exprt &expr); + virtual literalt convert_ieee_float_rel(const exprt &expr); + virtual literalt convert_quantifier(const exprt &expr); + + virtual void convert_index(const exprt &array, const mp_integer &index, bvt &bv); + virtual void convert_index(const class index_exprt &expr, bvt &bv); + virtual void convert_byte_extract( + unsigned width, const exprt &expr, + const mp_integer &index, bvt &bv, bool little_endian); + virtual void convert_byte_extract(const exprt &expr, bvt &bv); + virtual void convert_byte_update(const exprt &expr, bvt &bv); + virtual void convert_constraint_select_one(const exprt &expr, bvt &bv); + virtual void convert_if(const exprt &expr, bvt &bv); + virtual void convert_struct(const exprt &expr, bvt &bv); + virtual void convert_array(const exprt &expr, bvt &bv); + virtual void convert_lambda(const exprt &expr, bvt &bv); + virtual void convert_array_of(const exprt &expr, bvt &bv); + virtual void convert_union(const exprt &expr, bvt &bv); + virtual void convert_typecast(const exprt &expr, bvt &bv); + virtual void convert_add_sub(const exprt &expr, bvt &bv); + virtual void convert_mult(const exprt &expr, bvt &bv); + virtual void convert_div(const exprt &expr, bvt &bv); + virtual void convert_mod(const exprt &expr, bvt &bv); + virtual void convert_member(const member_exprt &expr, bvt &bv); + virtual void convert_with(const exprt &expr, bvt &bv); + virtual void convert_case(const exprt &expr, bvt &bv); + virtual void convert_cond(const exprt &expr, bvt &bv); + virtual void convert_shift(const exprt &expr, bvt &bv); + virtual void convert_bitwise(const exprt &expr, bvt &bv); + virtual void convert_unary_minus(const exprt &expr, bvt &bv); + virtual void convert_abs(const exprt &expr, bvt &bv); + virtual void convert_concatenation(const exprt &expr, bvt &bv); + virtual void convert_replication(const exprt &expr, bvt &bv); + virtual void convert_bv_literals(const exprt &expr, bvt &bv); + virtual void convert_constant(const exprt &expr, bvt &bv); + virtual void convert_extractbits(const exprt &expr, bvt &bv); + virtual void convert_symbol(const exprt &expr, bvt &bv); + + virtual void make_bv_expr(const typet &type, const bvt &bv, exprt &dest); + virtual void make_free_bv_expr(const typet &type, exprt &dest); + + void convert_with( + const typet &type, + const exprt &op1, + const exprt &op2, + const bvt &prev_bv, + bvt &next_bv); + + void convert_with_bv( + const typet &type, + const exprt &op1, + const exprt &op2, + const bvt &prev_bv, + bvt &next_bv); + + void convert_with_array( + const array_typet &type, + const exprt &op1, + const exprt &op2, + const bvt &prev_bv, + bvt &next_bv); + + void convert_with_union( + const union_typet &type, + const exprt &op1, + const exprt &op2, + const bvt &prev_bv, + bvt &next_bv); + + void convert_with_struct( + const struct_typet &type, + const exprt &op1, + const exprt &op2, + const bvt &prev_bv, + bvt &next_bv); + + virtual exprt bv_get_unbounded_array( + const irep_idt &identifier, + const class array_typet &type) const; + + virtual exprt bv_get_rec( + const bvt &bv, + const std::vector &unknown, + unsigned offset, + const typet &type) const; + + exprt bv_get(const bvt &bv, const typet &type) const; + exprt bv_get_cache(const exprt &expr) const; + + // unbounded arrays + + bool is_unbounded_array(const typet &type) const; + + // quantifier instantiations + class quantifiert + { + public: + exprt expr; + literalt l; + }; + + typedef std::list quantifier_listt; + quantifier_listt quantifier_list; + + void post_process_quantifiers(); + + typedef std::vector offset_mapt; + void build_offset_map(const struct_typet &src, offset_mapt &dest); +}; + +#endif diff --git a/src/solvers/flattening/boolbv_abs.cpp b/src/solvers/flattening/boolbv_abs.cpp new file mode 100644 index 00000000000..99af2e2f854 --- /dev/null +++ b/src/solvers/flattening/boolbv_abs.cpp @@ -0,0 +1,70 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "boolbv.h" +#include "boolbv_type.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +/*******************************************************************\ + +Function: boolbvt::convert_abs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_abs(const exprt &expr, bvt &bv) +{ + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()!=1) + throw "abs takes one operand"; + + const exprt &op0=expr.op0(); + + bvt op_bv; + convert_bv(op0, op_bv); + + if(op0.type()!=expr.type()) + return conversion_failed(expr, bv); + + bvtypet bvtype=get_bvtype(expr.type()); + + if(bvtype==IS_FIXED || + bvtype==IS_SIGNED || + bvtype==IS_UNSIGNED) + { + bv=bv_utils.absolute_value(op_bv); + return; + } + else if(bvtype==IS_FLOAT) + { + #ifdef HAVE_FLOATBV + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(expr.type()); + bv=float_utils.abs(op_bv); + return; + #endif + } + + conversion_failed(expr, bv); +} diff --git a/src/solvers/flattening/boolbv_add_sub.cpp b/src/solvers/flattening/boolbv_add_sub.cpp new file mode 100644 index 00000000000..8314480084f --- /dev/null +++ b/src/solvers/flattening/boolbv_add_sub.cpp @@ -0,0 +1,164 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "boolbv.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +/*******************************************************************\ + +Function: boolbvt::convert_add_sub + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_add_sub(const exprt &expr, bvt &bv) +{ + const typet &type=ns.follow(expr.type()); + + if(type.id()!=ID_unsignedbv && + type.id()!=ID_signedbv && + type.id()!=ID_fixedbv && + type.id()!=ID_floatbv && + type.id()!=ID_range && + type.id()!=ID_vector) + return conversion_failed(expr, bv); + + unsigned width=boolbv_width(type); + + if(width==0) + return conversion_failed(expr, bv); + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()==0) + throw "operand "+expr.id_string()+" takes at least one operand"; + + const exprt &op0=expr.op0(); + + if(op0.type()!=type) + { + std::cerr << expr.pretty() << std::endl; + throw "add/sub with mixed types"; + } + + convert_bv(op0, bv); + + if(bv.size()!=width) + throw "convert_add_sub: unexpected operand 0 width"; + + bool subtract=(expr.id()==ID_minus || + expr.id()=="no-overflow-minus"); + + bool no_overflow=(expr.id()=="no-overflow-plus" || + expr.id()=="no-overflow-minus"); + + typet arithmetic_type= + (type.id()==ID_vector)?ns.follow(type.subtype()):type; + + bv_utilst::representationt rep= + (arithmetic_type.id()==ID_signedbv || + arithmetic_type.id()==ID_fixedbv)?bv_utilst::SIGNED: + bv_utilst::UNSIGNED; + + for(exprt::operandst::const_iterator + it=operands.begin()+1; + it!=operands.end(); it++) + { + if(it->type()!=type) + { + std::cerr << expr.pretty() << std::endl; + throw "add/sub with mixed types"; + } + + bvt op; + convert_bv(*it, op); + + if(op.size()!=width) + throw "convert_add_sub: unexpected operand width"; + + if(type.id()==ID_vector) + { + const typet &subtype=ns.follow(type.subtype()); + + unsigned sub_width=boolbv_width(subtype); + + if(sub_width==0 || width%sub_width!=0) + throw "convert_add_sub: unexpected vector operand width"; + + unsigned size=width/sub_width; + bv.resize(width); + + for(unsigned i=0; i +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_array_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_array_of(const exprt &expr, bvt &bv) +{ + if(expr.operands().size()!=1) + throw "array_of takes one operand"; + + if(expr.type().id()!="array") + throw "array_of takes array-typed operand"; + + const array_typet &array_type=to_array_type(expr.type()); + + if(is_unbounded_array(array_type)) + return conversion_failed(expr, bv); + + unsigned width=boolbv_width(array_type); + + if(width==0) + return conversion_failed(expr, bv); + + const exprt &array_size=array_type.size(); + + mp_integer size; + + if(to_integer(array_size, size)) + return conversion_failed(expr, bv); + + bvt tmp; + convert_bv(expr.op0(), tmp); + + bv.resize(width); + + if(size*tmp.size()!=width) + throw "convert_array_of: unexpected operand width"; + + unsigned offset=0; + + for(mp_integer i=0; i + +#include "boolbv.h" +#include "boolbv_type.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +/*******************************************************************\ + +Function: boolbvt::convert_bv_rel + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbvt::convert_bv_rel(const exprt &expr) +{ + const exprt::operandst &operands=expr.operands(); + const std::string &rel=expr.id_string(); + + if(operands.size()==2) + { + const exprt &op0=expr.op0(); + const exprt &op1=expr.op1(); + + bvt bv0, bv1; + + convert_bv(op0, bv0); + convert_bv(op1, bv1); + + bvtypet bvtype0=get_bvtype(op0.type()); + bvtypet bvtype1=get_bvtype(op1.type()); + + if(bv0.size()==bv1.size() && bv0.size()!=0 && + bvtype0==bvtype1) + { + if(bvtype0==IS_FLOAT) + { + #ifdef HAVE_FLOATBV + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(op0.type()); + + if(rel=="<=") + return float_utils.relation(bv0, float_utilst::LE, bv1); + else if(rel=="<") + return float_utils.relation(bv0, float_utilst::LT, bv1); + else if(rel==">=") + return float_utils.relation(bv0, float_utilst::GE, bv1); + else if(rel==">") + return float_utils.relation(bv0, float_utilst::GT, bv1); + else + return SUB::convert_rest(expr); + #else + return SUB::convert_rest(expr); + #endif + } + else if((op0.type().id()=="range" && + op1.type()==op0.type()) || + bvtype0==IS_SIGNED || + bvtype0==IS_UNSIGNED || + bvtype0==IS_FIXED) + { + literalt literal; + bool or_equal=(rel=="<=" || rel==">="); + + bv_utilst::representationt rep= + ((bvtype0==IS_SIGNED) || (bvtype0==IS_FIXED))?bv_utilst::SIGNED: + bv_utilst::UNSIGNED; + + if(rel=="<=" || rel=="<") + literal=bv_utils.lt_or_le(or_equal, bv0, bv1, rep); + else if(rel==">=" || rel==">") + literal=bv_utils.lt_or_le(or_equal, bv1, bv0, rep); + // swapped + else + return SUB::convert_rest(expr); + + if(prop.has_set_to()) + { + // it's unclear if this helps + if(bv0.size()>8) + { + literalt equal_lit=equality(op0, op1); + + if(or_equal) + prop.l_set_to_true(prop.limplies(equal_lit, literal)); + else + prop.l_set_to_true(prop.limplies(equal_lit, prop.lnot(literal))); + } + } + + return literal; + } + } + } + + return SUB::convert_rest(expr); +} + diff --git a/src/solvers/flattening/boolbv_byte_extract.cpp b/src/solvers/flattening/boolbv_byte_extract.cpp new file mode 100644 index 00000000000..32903890383 --- /dev/null +++ b/src/solvers/flattening/boolbv_byte_extract.cpp @@ -0,0 +1,178 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_byte_extract + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_byte_extract(const exprt &expr, bvt &bv) +{ + if(expr.operands().size()!=2) + throw "byte_extract takes two operands"; + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + const exprt &op0=expr.op0(); + const exprt &op1=expr.op1(); + + bool little_endian; + + if(expr.id()=="byte_extract_little_endian") + little_endian=true; + else if(expr.id()=="byte_extract_big_endian") + little_endian=false; + else + assert(false); + + // see if the byte number is constant + + mp_integer index; + if(!to_integer(op1, index)) + return convert_byte_extract(width, expr.op0(), index, bv, little_endian); + + bvt op0_bv; + + convert_bv(op0, op0_bv); + + unsigned byte_width=8; + unsigned bytes=op0_bv.size()/byte_width; + + if(prop.has_set_to()) + { + // free variables + + bv.resize(width); + for(unsigned i=0; i + +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_byte_update + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_byte_update(const exprt &expr, bvt &bv) +{ + if(expr.operands().size()!=3) + throw "byte_update takes three operands"; + + const exprt &op0=expr.op0(); + const exprt &op1=expr.op1(); + const exprt &op2=expr.op2(); + + bool little_endian; + + if(expr.id()==ID_byte_update_little_endian) + little_endian=true; + else if(expr.id()==ID_byte_update_big_endian) + little_endian=false; + else + assert(false); + + convert_bv(op0, bv); + + bvt op2_bv; + convert_bv(op2, op2_bv); + unsigned update_width=op2_bv.size(); + unsigned byte_width=8; + + if(update_width>bv.size()) update_width=bv.size(); + + // see if the byte number is constant + + mp_integer index; + if(!to_integer(op1, index)) + { + // yes! + mp_integer offset; + + if(little_endian) + offset=index*byte_width; + else + offset=(mp_integer(bv.size()/byte_width)-index-1)*byte_width; + + if(offset+update_width>mp_integer(bv.size()) || offset<0) + { + // out of bounds + } + else + { + for(unsigned i=0; i &operands=expr.operands(); + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + bv.resize(width); + + // make it free variables + for(unsigned i=0; ipretty() + << std::endl; + + throw "size of compare operand does not match"; + } + + compare_literal=bv_utils.equal(compare_bv, op); + compare_literal=prop.land(prop.lnot(previous_compare), + compare_literal); + + previous_compare=prop.lor(previous_compare, compare_literal); + + what=VALUE; + break; + + case VALUE: + if(bv.size()!=op.size()) + { + std::cerr << "result size: " << bv.size() + << std::endl + << "operand: " << op.size() << std::endl + << it->pretty() + << std::endl; + + throw "size of value operand does not match"; + } + + { + literalt value_literal=bv_utils.equal(bv, op); + + prop.l_set_to_true( + prop.limplies(compare_literal, value_literal)); + } + + what=COMPARE; + break; + + default: + assert(false); + } + } +} + diff --git a/src/solvers/flattening/boolbv_concatenation.cpp b/src/solvers/flattening/boolbv_concatenation.cpp new file mode 100644 index 00000000000..685e54394fe --- /dev/null +++ b/src/solvers/flattening/boolbv_concatenation.cpp @@ -0,0 +1,55 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_concatenation + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_concatenation(const exprt &expr, bvt &bv) +{ + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()==0) + throw "concatenation takes at least one operand"; + + unsigned offset=width; + bv.resize(width); + + forall_expr(it, operands) + { + bvt op; + + convert_bv(*it, op); + + if(op.size()>offset) + throw "concatenation operand width too big"; + + offset-=op.size(); + + for(unsigned i=0; ipretty() + << std::endl; + + throw "size of value operand does not match"; + } + + literalt value_literal=bv_utils.equal(bv, op); + + prop.l_set_to_true(prop.limplies(cond_literal, value_literal)); + } + + condition=!condition; + } + } + else + { + // functional version -- go backwards + for(unsigned i=expr.operands().size(); i!=0; i-=2) + { + assert(i>=2); + const exprt &cond=expr.operands()[i-2]; + const exprt &value=expr.operands()[i-1]; + + literalt cond_literal=convert(cond); + + bvt op; + + convert_bv(value, op); + + if(bv.size()!=op.size()) + throw "unexpected operand size in convert_cond"; + + for(unsigned i=0; i op_bv; + op_bv.resize(expr.operands().size()); + + unsigned i=0; + forall_operands(it, expr) + convert_bv(*it, op_bv[i++]); + + bv=op_bv[0]; + + // add constraints + + bvt equal_bv; + equal_bv.resize(bv.size()); + + bvt b; + b.reserve(op_bv.size()-1); + + for(unsigned i=1; i + +#include "boolbv.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +/*******************************************************************\ + +Function: boolbvt::convert_div + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_div(const exprt &expr, bvt &bv) +{ + if(expr.type().id()!="unsignedbv" && + expr.type().id()!="signedbv" && + expr.type().id()!="fixedbv" && + expr.type().id()!="floatbv") + return conversion_failed(expr, bv); + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + if(expr.operands().size()!=2) + throw "division takes two operands"; + + if(expr.op0().type().id()!=expr.type().id() || + expr.op1().type().id()!=expr.type().id()) + return conversion_failed(expr, bv); + + bvt op0, op1; + + convert_bv(expr.op0(), op0); + convert_bv(expr.op1(), op1); + + if(op0.size()!=width || + op1.size()!=width) + throw "convert_div: unexpected operand width"; + + bvt res, rem; + + if(expr.type().id()=="fixedbv") + { + unsigned fraction_bits= + to_fixedbv_type(expr.type()).get_fraction_bits(); + + bvt zeros; + zeros.resize(fraction_bits, const_literal(false)); + + // add fraction_bits least-significant bits + op0.insert(op0.begin(), zeros.begin(), zeros.end()); + op1=bv_utils.sign_extension(op1, op1.size()+fraction_bits); + + bv_utils.divider(op0, op1, res, rem, bv_utilst::SIGNED); + + // cut it down again + res.resize(width); + } + else if(expr.type().id()=="floatbv") + { + #ifdef HAVE_FLOATBV + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(expr.type()); + res=float_utils.div(op0, op1); + #else + return conversion_failed(expr, bv); + #endif + } + else + { + bv_utilst::representationt rep= + expr.type().id()=="signedbv"?bv_utilst::SIGNED: + bv_utilst::UNSIGNED; + + bv_utils.divider(op0, op1, res, rem, rep); + } + + bv=res; +} diff --git a/src/solvers/flattening/boolbv_equality.cpp b/src/solvers/flattening/boolbv_equality.cpp new file mode 100644 index 00000000000..d790f51e61b --- /dev/null +++ b/src/solvers/flattening/boolbv_equality.cpp @@ -0,0 +1,66 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_equality + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbvt::convert_equality(const equality_exprt &expr) +{ + if(!base_type_eq(expr.op0().type(), expr.op1().type(), ns)) + { + std::cout << "######### op0: " << expr.op0().pretty() << std::endl; + std::cout << "######### op1: " << expr.op1().pretty() << std::endl; + throw "equality without matching types"; + } + + // see if it is an unbounded array + if(is_unbounded_array(expr.op0().type())) + return record_array_equality(expr); + + bvt bv0, bv1; + + convert_bv(expr.op0(), bv0); + convert_bv(expr.op1(), bv1); + + if(bv0.size()!=bv1.size()) + { + std::cerr << "op0: " << expr.op0().pretty() << std::endl; + std::cerr << "op0 size: " << bv0.size() << std::endl; + std::cerr << "op1: " << expr.op1().pretty() << std::endl; + std::cerr << "op1 size: " << bv1.size() << std::endl; + throw "unexpected size mismatch on equality"; + } + + if(bv0.size()==0) + throw "got zero-size BV"; + + if(expr.op0().type().id()=="verilogbv") + { + // TODO + } + else + return bv_utils.equal(bv0, bv1); + + return SUB::convert_rest(expr); +} diff --git a/src/solvers/flattening/boolbv_extractbit.cpp b/src/solvers/flattening/boolbv_extractbit.cpp new file mode 100644 index 00000000000..40d3f957483 --- /dev/null +++ b/src/solvers/flattening/boolbv_extractbit.cpp @@ -0,0 +1,104 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_extractbit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbvt::convert_extractbit(const exprt &expr) +{ + const exprt::operandst &operands=expr.operands(); + + if(operands.size()!=2) + throw "extractbit takes two operands"; + + bvt bv0; + convert_bv(operands[0], bv0); + + mp_integer o; + + if(!to_integer(operands[1], o)) // constant? + { + if(o<0 || o>=bv0.size()) + return prop.new_variable(); // out of range! + else + return bv0[integer2long(o)]; + } + + if(operands[0].type().id()=="verilogbv") + { + // TODO + assert(false); + } + else + { + unsigned width_op0=boolbv_width(operands[0].type()); + unsigned width_op1=boolbv_width(operands[1].type()); + + if(width_op0==0 || width_op1==0) + return SUB::convert_rest(expr); + + mp_integer index_width= + std::max(address_bits(width_op0), mp_integer(width_op1)); + + unsignedbv_typet index_type; + index_type.set_width(integer2long(index_width)); + + equality_exprt equality; + equality.lhs()=operands[1]; // index operand + + if(index_type!=equality.lhs().type()) + equality.lhs().make_typecast(index_type); + + if(prop.has_set_to()) + { + // free variable + literalt l=prop.new_variable(); + + // add implications + for(unsigned i=0; i +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_extractbits + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_extractbits(const exprt &expr, bvt &bv) +{ + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + if(expr.type().id()!=ID_signedbv && + expr.type().id()!=ID_unsignedbv && + expr.type().id()!=ID_bv) + return conversion_failed(expr, bv); + + if(expr.operands().size()!=3) + throw "extractbits takes three operands"; + + mp_integer o1, o2; + bvt bv0; + + convert_bv(expr.op0(), bv0); + + if(to_integer(expr.op1(), o1) || + to_integer(expr.op2(), o2)) + return conversion_failed(expr, bv); + + if(o1<0 || o1>=bv0.size()) + throw "extractbits: second operand out of range: "+expr.to_string(); + + if(o2<0 || o2>=bv0.size()) + throw "extractbits: third operand out of range: "+expr.to_string(); + + if(o2>o1) std::swap(o1, o2); + + // now o2<=o1 + + if((o1-o2+1)!=width) + throw "extractbits: wrong width (expected "+ + i2string(unsigned(integer2long(o1-o2+1)))+" but got "+ + i2string(width)+"): "+expr.to_string(); + + unsigned offset=integer2long(o2); + + bv.resize(width); + + for(unsigned i=0; i + +#include +#include + +#include "boolbv.h" +#include "boolbv_type.h" + +//#define DEBUG + +/*******************************************************************\ + +Function: boolbvt::get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt boolbvt::get(const exprt &expr) const +{ + if(expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol) + { + const irep_idt &identifier=expr.get(ID_identifier); + + boolbv_mapt::mappingt::const_iterator it= + map.mapping.find(identifier); + + if(it!=map.mapping.end()) + { + const boolbv_mapt::map_entryt &map_entry=it->second; + + if(is_unbounded_array(map_entry.type)) + return bv_get_unbounded_array(identifier, to_array_type(map_entry.type)); + + std::vector unknown; + bvt bv; + unsigned width=map_entry.width; + + bv.resize(width); + unknown.resize(width); + + for(unsigned bit_nr=0; bit_nr &unknown, + unsigned offset, + const typet &type) const +{ + if(type.id()==ID_symbol) + return bv_get_rec(bv, unknown, offset, ns.follow(type)); + + unsigned width=boolbv_width(type); + + if(width==0) + return nil_exprt(); + + assert(bv.size()==unknown.size()); + assert(bv.size()>=offset+width); + + if(type.id()==ID_bool) + { + if(!unknown[offset]) + { + switch(prop.l_get(bv[offset]).get_value()) + { + case tvt::TV_FALSE: return false_exprt(); + case tvt::TV_TRUE: return true_exprt(); + default: return false_exprt(); // default + } + } + + return nil_exprt(); + } + + bvtypet bvtype=get_bvtype(type); + + if(bvtype==IS_UNKNOWN) + { + if(type.id()==ID_array) + { + const typet &subtype=type.subtype(); + unsigned sub_width=boolbv_width(subtype); + + if(sub_width!=0) + { + exprt::operandst op; + op.reserve(width/sub_width); + + for(unsigned new_offset=0; + new_offsettype()); + op.push_back(nil_exprt()); + + unsigned sub_width=boolbv_width(subtype); + + if(sub_width!=0) + { + op.back()=bv_get_rec(bv, unknown, offset+new_offset, subtype); + new_offset+=sub_width; + } + } + + exprt dest=exprt(ID_struct, type); + dest.operands().swap(op); + return dest; + } + else if(type.id()==ID_union) + { + const irept &components= + type.find(ID_components); + + unsigned component_bits= + integer2long(address_bits(components.get_sub().size())); + unsigned component_nr=0; + + for(unsigned i=0; i=components.get_sub().size()) + return nil_exprt(); + + exprt value(ID_union, type); + value.operands().resize(1); + + value.set(ID_component_name, + components.get_sub()[component_nr].get(ID_name)); + + const typet &subtype= + static_cast( + components.get_sub()[component_nr].find(ID_type)); + + value.op0()=bv_get_rec(bv, unknown, offset, subtype); + + return value; + } + else if(type.id()==ID_vector) + { + const typet &subtype=ns.follow(type.subtype()); + unsigned sub_width=boolbv_width(subtype); + + if(sub_width!=0 && width%sub_width==0) + { + unsigned size=width/sub_width; + exprt value(ID_vector, type); + value.operands().resize(size); + + for(unsigned i=0; i unknown; + unknown.resize(bv.size(), false); + return bv_get_rec(bv, unknown, 0, type); +} + +/*******************************************************************\ + +Function: boolbvt::bv_get_cache + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt boolbvt::bv_get_cache(const exprt &expr) const +{ + if(expr.type().id()==ID_bool) // boolean? + return get(expr); + + // look up literals in cache + bv_cachet::const_iterator it=bv_cache.find(expr); + if(it==bv_cache.end()) + return nil_exprt(); + + return bv_get(it->second, expr.type()); +} + +/*******************************************************************\ + +Function: boolbvt::bv_get_unbounded_array + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt boolbvt::bv_get_unbounded_array( + const irep_idt &identifier, + const array_typet &type) const +{ + // first, try to get size + + const exprt &size_expr=type.size(); + exprt size=get(size_expr); + + // no size, give up + if(size.is_nil()) return nil_exprt(); + + // get the numeric value, unless it's infinity + mp_integer size_mpint; + + if(size.id()!=ID_infinity) + { + if(to_integer(size, size_mpint)) + return nil_exprt(); + + // simple sanity check + if(size_mpint<0) + return nil_exprt(); + } + else + size_mpint=0; + + // search array indices + + typedef std::map valuest; + valuest values; + + { + unsigned number; + + symbol_exprt array_expr; + array_expr.type()=type; + array_expr.set_identifier(identifier); + + if(arrays.get_number(array_expr, number)) + return nil_exprt(); + + // get root + number=arrays.find_number(number); + + assert(number100 || size.id()==ID_infinity) + { + result=exprt("array-list", type); + result.type().set(ID_size, integer2string(size_mpint)); + + result.operands().reserve(values.size()*2); + + for(valuest::const_iterator it=values.begin(); + it!=values.end(); + it++) + { + exprt index=from_integer(it->first, size.type()); + result.copy_to_operands(index, it->second); + } + } + else + { + // set the size + result=exprt(ID_array, type); + result.type().set(ID_size, size); + + unsigned long size_int=integer2long(size_mpint); + + // allocate operands + result.operands().resize(size_int); + + for(unsigned i=0; ifirst>=0 && it->firstfirst)].swap(it->second); + } + + return result; +} + +/*******************************************************************\ + +Function: boolbvt::get_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer boolbvt::get_value( + const bvt &bv, + unsigned offset, + unsigned width) +{ + mp_integer value=0; + mp_integer weight=1; + + for(unsigned bit_nr=offset; bit_nr + +#include "boolbv.h" +#include "boolbv_type.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +/*******************************************************************\ + +Function: boolbvt::convert_ieee_float_rel + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbvt::convert_ieee_float_rel(const exprt &expr) +{ + #ifdef HAVE_FLOATBV + const exprt::operandst &operands=expr.operands(); + const std::string &rel=expr.id_string(); + + if(operands.size()==2) + { + const exprt &op0=expr.op0(); + const exprt &op1=expr.op1(); + + bvt bv0, bv1; + bvtypet bvtype0=get_bvtype(op0.type()); + bvtypet bvtype1=get_bvtype(op1.type()); + + convert_bv(op0, bv0); + convert_bv(op1, bv1); + + if(bv0.size()==bv1.size() && bv0.size()!=0 && + bvtype0==IS_FLOAT && bvtype1==IS_FLOAT) + { + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(op0.type()); + + if(rel=="ieee_float_equal") + return float_utils.relation(bv0, float_utilst::EQ, bv1); + else if(rel=="ieee_float_notequal") + return prop.lnot(float_utils.relation(bv0, float_utilst::EQ, bv1)); + else + return SUB::convert_rest(expr); + } + } + #endif + + return SUB::convert_rest(expr); +} + diff --git a/src/solvers/flattening/boolbv_if.cpp b/src/solvers/flattening/boolbv_if.cpp new file mode 100644 index 00000000000..bc1a5f3322b --- /dev/null +++ b/src/solvers/flattening/boolbv_if.cpp @@ -0,0 +1,46 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_if + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_if(const exprt &expr, bvt &bv) +{ + const exprt::operandst &operands=expr.operands(); + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + if(operands.size()!=3) + throw "if takes three operands"; + + bvt op1_bv, op2_bv; + + literalt op0=convert(operands[0]); + + convert_bv(operands[1], op1_bv); + convert_bv(operands[2], op2_bv); + + if(op1_bv.size()!=width || op2_bv.size()!=width) + throw "operand size mismatch for if"; + + bv=bv_utils.select(op0, op1_bv, op2_bv); +} diff --git a/src/solvers/flattening/boolbv_index.cpp b/src/solvers/flattening/boolbv_index.cpp new file mode 100644 index 00000000000..26fabc70cdd --- /dev/null +++ b/src/solvers/flattening/boolbv_index.cpp @@ -0,0 +1,213 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_index(const index_exprt &expr, bvt &bv) +{ + if(expr.id()!=ID_index) + throw "expected index expression"; + + if(expr.operands().size()!=2) + throw "index takes two operands"; + + const exprt &array=expr.array(); + const exprt &index=expr.index(); + + const array_typet &array_type= + to_array_type(ns.follow(array.type())); + + // see if the array size is constant + + if(is_unbounded_array(array_type)) + { + // use array decision procedure + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + // free variables + + bv.resize(width); + for(unsigned i=0; i0); + + for(mp_integer i=0; i=0 && + offset+width<=mp_integer(tmp.size())) + { + // in bounds + for(unsigned i=0; i result= + mapping.insert(std::pair( + identifier, map_entryt())); + + map_entryt &map_entry=result.first->second; + + if(result.second) + { // actually inserted + map_entry.type=type; + map_entry.width=boolbv_width(type); + map_entry.bvtype=get_bvtype(type); + map_entry.literal_map.resize(map_entry.width); + } + + assert(map_entry.literal_map.size()==map_entry.width); + + return map_entry; +} + +/*******************************************************************\ + +Function: boolbv_mapt::show + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbv_mapt::show() const +{ + for(mappingt::const_iterator it=mapping.begin(); + it!=mapping.end(); + it++) + { + } +} + +/*******************************************************************\ + +Function: boolbv_mapt::get_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbv_mapt::get_literal( + const irep_idt &identifier, + const unsigned bit, + const typet &type) +{ + map_entryt &map_entry=get_map_entry(identifier, type); + + assert(bit literal_mapt; + + class map_entryt + { + public: + map_entryt():width(0), bvtype(IS_UNKNOWN) + { + } + + unsigned width; + bvtypet bvtype; + typet type; + literal_mapt literal_map; + }; + + typedef hash_map_cont mappingt; + mappingt mapping; + + void show() const; + + map_entryt &get_map_entry( + const irep_idt &identifier, + const typet &type); + + literalt get_literal( + const irep_idt &identifier, + const unsigned bit, + const typet &type); + + void set_literal( + const irep_idt &identifier, + const unsigned bit, + const typet &type, + literalt literal); + +protected: + propt ∝ + const namespacet &ns; + const boolbv_widtht &boolbv_width; +}; + +#endif diff --git a/src/solvers/flattening/boolbv_member.cpp b/src/solvers/flattening/boolbv_member.cpp new file mode 100644 index 00000000000..1d0f5178eab --- /dev/null +++ b/src/solvers/flattening/boolbv_member.cpp @@ -0,0 +1,94 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_member(const member_exprt &expr, bvt &bv) +{ + bvt struct_bv; + const exprt &struct_op=expr.struct_op(); + const typet &struct_op_type=ns.follow(struct_op.type()); + + convert_bv(struct_op, struct_bv); + + if(struct_op_type.id()=="union") + { + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + bv.resize(width); + + if(width>struct_bv.size()) + throw "member/union: unexpected widths"; + + for(unsigned i=0; itype(); + unsigned sub_width=boolbv_width(subtype); + + if(it->get_name()==component_name) + { + if(!base_type_eq(subtype, expr.type(), ns)) + { + #if 0 + std::cout << "DEBUG " << expr.pretty() << "\n"; + #endif + + throw "member: component type does not match: "+ + subtype.to_string()+" vs. "+ + expr.type().to_string(); + } + + bv.resize(sub_width); + assert(offset+sub_width<=struct_bv.size()); + + for(unsigned i=0; i + +#include "boolbv.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +/*******************************************************************\ + +Function: boolbvt::convert_mult + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_mult(const exprt &expr, bvt &bv) +{ + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + bv.resize(width); + + const exprt::operandst &operands=expr.operands(); + if(operands.size()==0) + throw "mult without operands"; + + const exprt &op0=expr.op0(); + + bool no_overflow=expr.id()=="no-overflow-mult"; + + if(expr.type().id()==ID_fixedbv) + { + if(op0.type()!=expr.type()) + throw "multiplication with mixed types"; + + convert_bv(op0, bv); + + if(bv.size()!=width) + throw "convert_mult: unexpected operand width"; + + unsigned fraction_bits= + to_fixedbv_type(expr.type()).get_fraction_bits(); + + // do a sign extension by fraction_bits bits + bv=bv_utils.sign_extension(bv, bv.size()+fraction_bits); + + for(exprt::operandst::const_iterator it=operands.begin()+1; + it!=operands.end(); it++) + { + bvt op; + + if(it->type()!=expr.type()) + throw "multiplication with mixed types"; + + convert_bv(*it, op); + + if(op.size()!=width) + throw "convert_mult: unexpected operand width"; + + op=bv_utils.sign_extension(op, bv.size()); + + bv=bv_utils.signed_multiplier(bv, op); + } + + // cut it down again + bv.erase(bv.begin(), bv.begin()+fraction_bits); + + return; + } + else if(expr.type().id()==ID_floatbv) + { + #ifdef HAVE_FLOATBV + if(op0.type()!=expr.type()) + throw "multiplication with mixed types"; + + convert_bv(op0, bv); + + if(bv.size()!=width) + throw "convert_mult: unexpected operand width"; + + float_utilst float_utils(prop); + float_utils.spec=to_floatbv_type(expr.type()); + + for(exprt::operandst::const_iterator it=operands.begin()+1; + it!=operands.end(); it++) + { + bvt op; + + if(it->type()!=expr.type()) + throw "multiplication with mixed types"; + + convert_bv(*it, op); + + if(op.size()!=width) + throw "convert_mult: unexpected operand width"; + + bv=float_utils.mul(bv, op); + } + + return; + #endif + } + else if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv) + { + if(op0.type()!=expr.type()) + throw "multiplication with mixed types"; + + bv_utilst::representationt rep= + expr.type().id()==ID_signedbv?bv_utilst::SIGNED: + bv_utilst::UNSIGNED; + + convert_bv(op0, bv); + + if(bv.size()!=width) + throw "convert_mult: unexpected operand width"; + + for(exprt::operandst::const_iterator it=operands.begin()+1; + it!=operands.end(); it++) + { + bvt op; + + if(it->type()!=expr.type()) + throw "multiplication with mixed types"; + + convert_bv(*it, op); + + if(op.size()!=width) + throw "convert_mult: unexpected operand width"; + + if(no_overflow) + bv=bv_utils.multiplier_no_overflow(bv, op, rep); + else + bv=bv_utils.multiplier(bv, op, rep); + } + + return; + } + + conversion_failed(expr, bv); +} diff --git a/src/solvers/flattening/boolbv_overflow.cpp b/src/solvers/flattening/boolbv_overflow.cpp new file mode 100644 index 00000000000..77e0d66f50b --- /dev/null +++ b/src/solvers/flattening/boolbv_overflow.cpp @@ -0,0 +1,164 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_overflow + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbvt::convert_overflow(const exprt &expr) +{ + const exprt::operandst &operands=expr.operands(); + + if(expr.id()==ID_overflow_plus || + expr.id()==ID_overflow_minus) + { + if(operands.size()!=2) + throw "operand "+expr.id_string()+" takes two operands"; + + bvt bv0, bv1; + + convert_bv(operands[0], bv0); + convert_bv(operands[1], bv1); + + if(bv0.size()!=bv1.size()) + return SUB::convert_rest(expr); + + bv_utilst::representationt rep= + expr.op0().type().id()==ID_signedbv?bv_utilst::SIGNED: + bv_utilst::UNSIGNED; + + return expr.id()==ID_overflow_minus? + bv_utils.overflow_sub(bv0, bv1, rep): + bv_utils.overflow_add(bv0, bv1, rep); + } + else if(expr.id()==ID_overflow_mult) + { + if(operands.size()!=2) + throw "operand "+expr.id_string()+" takes two operands"; + + if(operands[0].type().id()!=ID_unsignedbv && + operands[0].type().id()!=ID_signedbv) + return SUB::convert_rest(expr); + + bvt bv0, bv1; + + convert_bv(operands[0], bv0); + convert_bv(operands[1], bv1); + + if(bv0.size()!=bv1.size()) + throw "operand size mismatch on overflow-*"; + + bv_utilst::representationt rep= + operands[0].type().id()==ID_signedbv?bv_utilst::SIGNED: + bv_utilst::UNSIGNED; + + if(operands[0].type()!=operands[1].type()) + throw "operand type mismatch on overflow-*"; + + assert(bv0.size()==bv1.size()); + unsigned old_size=bv0.size(); + unsigned new_size=old_size*2; + + // sign/zero extension + bv0=bv_utils.extension(bv0, new_size, rep); + bv1=bv_utils.extension(bv1, new_size, rep); + + bvt result=bv_utils.multiplier(bv0, bv1, rep); + + if(rep==bv_utilst::UNSIGNED) + { + bvt bv_overflow; + bv_overflow.reserve(old_size); + + // get top result bits + for(unsigned i=old_size; i=bv.size() || bits==0) + throw "overflow-typecast got wrong number of bits"; + + // signed or unsigned? + if(op.type().id()==ID_signedbv) + { + bvt tmp_bv; + for(unsigned i=bits; i + +#include +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_quantifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbvt::convert_quantifier(const exprt &expr) +{ + if(expr.operands().size()!=3) + return SUB::convert_rest(expr); + + literalt l=prop.new_variable(); + + quantifier_list.push_back(quantifiert()); + quantifier_list.back().l=l; + quantifier_list.back().expr=expr; + + return l; +} + +/*******************************************************************\ + +Function: boolbvt::post_process_quantifiers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::post_process_quantifiers() +{ + std::set instances; + + if(quantifier_list.empty()) return; + + for(quantifier_listt::const_iterator q_it=quantifier_list.begin(); + q_it!=quantifier_list.end(); + q_it++) + forall_operands(o_it, q_it->expr.op1()) + instances.insert(*o_it); + + if(instances.empty()) + throw "post_process_quantifiers: no instances"; + + for(quantifier_listt::const_iterator q_it=quantifier_list.begin(); + q_it!=quantifier_list.end(); + q_it++) + { + const exprt &expr=q_it->expr; + + bvt inst_bv; + inst_bv.reserve(instances.size()); + + for(std::set::const_iterator it=instances.begin(); + it!=instances.end(); + it++) + { + exprt dest(expr.op2()); + + exprt by(*it); + if(expr.op0().type()!=by.type()) + by.make_typecast(expr.op0().type()); + + replace_expr(expr.op0(), by, dest); + inst_bv.push_back(convert(dest)); + } + + if(expr.id()=="forall") + prop.set_equal(prop.land(inst_bv), q_it->l); + else if(expr.id()=="exists") + prop.set_equal(prop.lor(inst_bv), q_it->l); + else + assert(false); + } +} diff --git a/src/solvers/flattening/boolbv_reduction.cpp b/src/solvers/flattening/boolbv_reduction.cpp new file mode 100644 index 00000000000..5b3ca3874a5 --- /dev/null +++ b/src/solvers/flattening/boolbv_reduction.cpp @@ -0,0 +1,65 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_reduction + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt boolbvt::convert_reduction(const exprt &expr) +{ + if(expr.operands().size()!=1) + throw "reduction operators take one operand"; + + bvt bv0; + + convert_bv(expr.op0(), bv0); + + if(bv0.size()<1) + throw "reduction operators take one non-empty operand"; + + literalt l; + + enum { O_OR, O_AND, O_XOR } op; + + if(expr.id()=="reduction_or" || expr.id()=="reduction_nor") + op=O_OR; + else if(expr.id()=="reduction_and" || expr.id()=="reduction_nand") + op=O_AND; + else if(expr.id()=="reduction_xor" || expr.id()=="reduction_xnor") + op=O_XOR; + else + throw "unexpected reduction operator"; + + l=bv0[0]; + for(unsigned i=1; i + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_replication + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_replication(const exprt &expr, bvt &bv) +{ + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + if(expr.operands().size()!=2) + throw "replication takes two operands"; + + mp_integer times; + if(to_integer(expr.op0(), times)) + throw "replication takes constant as first parameter"; + + bvt op; + + convert_bv(expr.op1(), op); + + unsigned offset=0; + bv.resize(width); + + for(unsigned i=0; iwidth) + throw "replication operand width too big"; + + for(unsigned i=0; i +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_struct + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_struct(const exprt &expr, bvt &bv) +{ + const struct_typet &struct_type=to_struct_type(ns.follow(expr.type())); + + unsigned width=boolbv_width(struct_type); + + if(width==0) + return conversion_failed(expr, bv); + + const struct_typet::componentst &components=struct_type.components(); + + if(expr.operands().size()!=components.size()) + throw "struct: wrong number of arguments"; + + bv.resize(width); + + unsigned offset=0, i=0; + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + const typet &subtype=it->type(); + const exprt &op=expr.operands()[i]; + + if(!base_type_eq(subtype, op.type(), ns)) + throw "struct: component type does not match: "+ + subtype.to_string()+" vs. "+ + op.type().to_string(); + + unsigned subtype_width=boolbv_width(subtype); + + if(subtype_width!=0) + { + bvt op_bv; + + convert_bv(op, op_bv); + + assert(offset + +// new stuff +typedef enum +{ + IS_BV, IS_SIGNED, IS_UNSIGNED, IS_FLOAT, IS_FIXED, + IS_VERILOGBV, IS_RANGE, IS_UNKNOWN, IS_C_ENUM +} bvtypet; + +bvtypet get_bvtype(const typet &type); + +#endif diff --git a/src/solvers/flattening/boolbv_typecast.cpp b/src/solvers/flattening/boolbv_typecast.cpp new file mode 100644 index 00000000000..c824e775933 --- /dev/null +++ b/src/solvers/flattening/boolbv_typecast.cpp @@ -0,0 +1,480 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "boolbv.h" +#include "boolbv_type.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +/*******************************************************************\ + +Function: boolbvt::convert_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_typecast(const exprt &expr, bvt &bv) +{ + if(expr.operands().size()!=1) + throw "typecast takes one operand"; + + const exprt &op=expr.op0(); + + bvt op_bv; + convert_bv(op, op_bv); + + const typet &expr_type=ns.follow(expr.type()); + const typet &op_type=ns.follow(op.type()); + + bvtypet dest_bvtype=get_bvtype(expr_type); + bvtypet op_bvtype=get_bvtype(op_type); + unsigned op_width=op_bv.size(); + + unsigned dest_width=boolbv_width(expr_type); + + if(dest_width==0) + return conversion_failed(expr, bv); + + bv.clear(); + bv.reserve(dest_width); + + if(op_width==0) + return conversion_failed(expr, bv); + + switch(dest_bvtype) + { + case IS_RANGE: + if(op_bvtype==IS_UNSIGNED || op_bvtype==IS_SIGNED) + { + mp_integer dest_from=to_range_type(expr_type).get_from(); + + if(dest_from==0) + { + // do zero extension + bv.resize(dest_width); + for(unsigned i=0; i op_mapt; + op_mapt op_map; + + for(unsigned i=0; isecond].type()) + { + // filling with free variables + for(unsigned j=0; jsecond]; + for(unsigned j=0; j + +#include "boolbv.h" +#include "boolbv_type.h" + +#ifdef HAVE_FLOATBV +#include "../floatbv/float_utils.h" +#endif + +/*******************************************************************\ + +Function: boolbvt::convert_unary_minus + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_unary_minus(const exprt &expr, bvt &bv) +{ + const typet &type=ns.follow(expr.type()); + + unsigned width=boolbv_width(type); + + if(width==0) + return conversion_failed(expr, bv); + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()!=1) + throw "unary minus takes one operand"; + + const exprt &op0=expr.op0(); + + bvt op_bv; + convert_bv(op0, op_bv); + + bvtypet bvtype=get_bvtype(type); + bvtypet op_bvtype=get_bvtype(op0.type()); + unsigned op_width=op_bv.size(); + + bool no_overflow=(expr.id()=="no-overflow-unary-minus"); + + if(op_width==0 || op_width!=width) + return conversion_failed(expr, bv); + + if(bvtype==IS_UNKNOWN && + type.id()==ID_vector) + { + const typet &subtype=ns.follow(type.subtype()); + + unsigned sub_width=boolbv_width(subtype); + + if(sub_width==0 || width%sub_width!=0) + throw "unary-: unexpected vector operand width"; + + unsigned size=width/sub_width; + bv.resize(width); + + for(unsigned i=0; i + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_union(const exprt &expr, bvt &bv) +{ + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + if(expr.operands().size()!=1) + throw "union expects one argument"; + + bvt op_bv; + convert_bv(expr.op0(), op_bv); + + if(width>i)&1); +} diff --git a/src/solvers/flattening/boolbv_width.cpp b/src/solvers/flattening/boolbv_width.cpp new file mode 100644 index 00000000000..b00bd0174db --- /dev/null +++ b/src/solvers/flattening/boolbv_width.cpp @@ -0,0 +1,265 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include + +#include "boolbv_width.h" + +/*******************************************************************\ + +Function: boolbv_widtht::boolbv_widtht + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +boolbv_widtht::boolbv_widtht(const namespacet &_ns):ns(_ns) +{ + cache=new cachet; +} + +/*******************************************************************\ + +Function: boolbv_widtht::~boolbv_widtht + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +boolbv_widtht::~boolbv_widtht() +{ + delete cache; +} + +/*******************************************************************\ + +Function: boolbv_widtht::get_entry + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const boolbv_widtht::entryt &boolbv_widtht::get_entry(const typet &type) const +{ + // check cache first + + std::pair cache_result= + cache->insert(std::pair(type, entryt())); + + entryt &entry=cache_result.first->second; + + if(!cache_result.second) // found! + return entry; + + entry.total_width=0; + + if(type.id()==ID_struct) + { + const struct_typet::componentst &components= + to_struct_type(type).components(); + + unsigned offset=0; + entry.members.resize(components.size()); + + for(unsigned i=0; i=1) + { + entry.total_width=integer2long(address_bits(size)); + assert(entry.total_width!=0); + } + } + else if(type.id()==ID_array) + { + const array_typet &array_type=to_array_type(type); + unsigned sub_width=operator()(array_type.subtype()); + + mp_integer array_size; + + if(to_integer(array_type.size(), array_size)) + { + // we can still use the theory of arrays for this + entry.total_width=0; + } + else + entry.total_width=integer2long(array_size*sub_width); + } + else if(type.id()==ID_vector) + { + const vector_typet &vector_type=to_vector_type(type); + unsigned sub_width=operator()(vector_type.subtype()); + + mp_integer vector_size; + + if(to_integer(vector_type.size(), vector_size)) + { + // we can still use the theory of arrays for this + entry.total_width=0; + } + else + entry.total_width=integer2long(vector_size*sub_width); + } + else if(type.id()==ID_code) + { + } + else if(type.id()==ID_enum) + { + // get number of necessary bits + + unsigned size=type.find(ID_elements).get_sub().size(); + entry.total_width=integer2long(address_bits(size)); + assert(entry.total_width!=0); + } + else if(type.id()==ID_c_enum || + type.id()==ID_incomplete_c_enum) + { + entry.total_width=type.get_int(ID_width); + assert(entry.total_width!=0); + } + else if(type.id()==ID_pointer || + type.id()==ID_reference) + { + entry.total_width=config.ansi_c.pointer_width; + } + else if(type.id()==ID_symbol) + entry=get_entry(ns.follow(type)); + + return entry; +} + +/*******************************************************************\ + +Function: boolbv_widtht::get_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const boolbv_widtht::membert &boolbv_widtht::get_member( + const struct_typet &type, + const irep_idt &member) const +{ + unsigned component_number=type.component_number(member); + + return get_entry(type).members[component_number]; +} + +/*******************************************************************\ + +Function: boolbv_get_width + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +#if 0 +bool boolbv_get_width( + const typet &type, + unsigned &width, + const namespacet &ns) +{ + boolbv_widtht boolbv_width(ns); + width=boolbv_width(type); + return false; +} + +/*******************************************************************\ + +Function: boolbv_member_offset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool boolbv_member_offset( + const struct_typet &type, + const irep_idt &member, + unsigned &width, + const namespacet &ns) +{ + boolbv_widtht boolbv_width(ns); + return boolbv_width.get_member(type, member).offset; +} +#endif diff --git a/src/solvers/flattening/boolbv_width.h b/src/solvers/flattening/boolbv_width.h new file mode 100644 index 00000000000..7859060a791 --- /dev/null +++ b/src/solvers/flattening/boolbv_width.h @@ -0,0 +1,65 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_BOOLBV_WIDTH_H +#define CPROVER_BOOLBV_WIDTH_H + +#include +#include + +class boolbv_widtht +{ +public: + explicit boolbv_widtht(const namespacet &_ns); + ~boolbv_widtht(); + + unsigned operator()(const typet &type) const + { + return get_entry(type).total_width; + } + + struct membert + { + unsigned offset, width; + }; + + const membert &get_member( + const struct_typet &type, + const irep_idt &member) const; + +protected: + const namespacet &ns; + + struct entryt + { + unsigned total_width; + std::vector members; + }; + + typedef hash_map_cont cachet; + + // the pointer is allow const methods above + cachet *cache; + + const entryt &get_entry(const typet &type) const; +}; + +#if 0 +bool boolbv_member_offset( + const struct_typet &type, + const irep_idt &member, + unsigned &offset, + const namespacet &ns); + +bool boolbv_get_width( + const typet &type, + unsigned &width, + const namespacet &ns); +#endif + +#endif diff --git a/src/solvers/flattening/boolbv_with.cpp b/src/solvers/flattening/boolbv_with.cpp new file mode 100644 index 00000000000..c872864e647 --- /dev/null +++ b/src/solvers/flattening/boolbv_with.cpp @@ -0,0 +1,332 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include + +#include "boolbv.h" + +/*******************************************************************\ + +Function: boolbvt::convert_with + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void boolbvt::convert_with(const exprt &expr, bvt &bv) +{ + if(expr.operands().size()<3) + throw "with takes at least three operands"; + + if((expr.operands().size()%2)!=1) + throw "with takes an odd number of operands"; + + convert_bv(expr.op0(), bv); + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + return conversion_failed(expr, bv); + + if(bv.size()!=width) + throw "unexpected operand 0 width"; + + bvt prev_bv; + prev_bv.resize(width); + + const exprt::operandst &ops=expr.operands(); + + for(unsigned op_no=1; op_notype(); + + unsigned sub_width=boolbv_width(subtype); + + if(it->get_name()==component_name) + { + if(!base_type_eq(subtype, op2.type(), ns)) + throw "with/struct: component `"+id2string(component_name)+ + "' type does not match: "+ + subtype.to_string()+" vs. "+ + op2.type().to_string(); + + if(sub_width!=op2_bv.size()) + throw "convert_with_struct: unexpected operand op2 width"; + + for(unsigned i=0; i>i)&1); +} + diff --git a/src/solvers/flattening/bv_pointers.cpp b/src/solvers/flattening/bv_pointers.cpp new file mode 100644 index 00000000000..e86577f5eea --- /dev/null +++ b/src/solvers/flattening/bv_pointers.cpp @@ -0,0 +1,845 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bv_pointers.h" + +/*******************************************************************\ + +Function: bv_pointerst::convert_rest + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt bv_pointerst::convert_rest(const exprt &expr) +{ + if(expr.type().id()!=ID_bool) + throw "bv_pointerst::convert_rest got non-boolean operand"; + + const exprt::operandst &operands=expr.operands(); + + if(expr.id()=="invalid-pointer") + { + if(operands.size()==1 && + is_ptr(operands[0].type())) + { + bvt bv; + convert_bv(operands[0], bv); + + if(bv.size()!=0) + { + bvt invalid_bv, null_bv; + encode(pointer_logic.get_invalid_object(), invalid_bv); + encode(pointer_logic.get_null_object(), null_bv); + + bvt equal_invalid_bv, equal_null_bv; + equal_invalid_bv.resize(object_bits); + equal_null_bv.resize(object_bits); + + for(unsigned i=0; itype().id()==ID_pointer) + { + count++; + convert_bv(*it, bv); + assert(bv.size()==bits); + size=pointer_offset_size(ns, it->type().subtype()); + } + } + + if(count==0) + throw "found no pointer in pointer-type sum"; + else if(count!=1) + throw "found more than one pointer in sum"; + + bvt sum=bv_utils.build_constant(0, bits); + + forall_operands(it, expr) + { + bvt op; + + if(it->type().id()==ID_pointer) continue; + + if(it->type().id()!=ID_unsignedbv && + it->type().id()!=ID_signedbv) + return conversion_failed(expr, bv); + + bv_utilst::representationt rep= + it->type().id()==ID_signedbv?bv_utilst::SIGNED: + bv_utilst::UNSIGNED; + + convert_bv(*it, op); + + if(op.size()==0) + throw "unexpected pointer arithmetic operand width"; + + // we cut any extra bits off + + if(op.size()>bits) + op.resize(bits); + else if(op.size() &unknown, + unsigned offset, + const typet &type) const +{ + if(!is_ptr(type)) + return SUB::bv_get_rec(bv, unknown, offset, type); + + std::string value_addr, value_offset; + + for(unsigned i=0; i>object_bits)) + throw "too many variables"; + + encode(a, bv); +} + +/*******************************************************************\ + +Function: bv_pointerst::do_is_dynamic_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_pointerst::do_is_dynamic_object( + const is_dynamic_objectt &is_dynamic_object) +{ + const pointer_logict::objectst &objects= + pointer_logic.objects; + + unsigned number=0; + + for(pointer_logict::objectst::const_iterator + it=objects.begin(); + it!=objects.end(); + it++, number++) + { + const exprt &expr=*it; + + bool is_dynamic=pointer_logic.is_dynamic_object(expr); + + // only compare object part + bvt bv; + encode(number, bv); + + bv.erase(bv.begin(), bv.begin()+offset_bits); + + literalt l1=bv_utils.equal(bv, is_dynamic_object.bv); + literalt l2=is_dynamic_object.l; + + if(!is_dynamic) l2=prop.lnot(l2); + + prop.l_set_to(prop.limplies(l1, l2), true); + } +} + +/*******************************************************************\ + +Function: bv_pointerst::post_process + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_pointerst::post_process() +{ + for(is_dynamic_object_listt::const_iterator + it=is_dynamic_object_list.begin(); + it!=is_dynamic_object_list.end(); + it++) + do_is_dynamic_object(*it); + + is_dynamic_object_list.clear(); + + SUB::post_process(); +} diff --git a/src/solvers/flattening/bv_pointers.h b/src/solvers/flattening/bv_pointers.h new file mode 100644 index 00000000000..360e68e2615 --- /dev/null +++ b/src/solvers/flattening/bv_pointers.h @@ -0,0 +1,73 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_BV_POINTERS_H +#define CPROVER_BV_POINTERS_H + +#include + +#include "boolbv.h" +#include "pointer_logic.h" + +class bv_pointerst:public boolbvt +{ +public: + bv_pointerst(const namespacet &_ns, propt &_prop); + + virtual void post_process(); + +protected: + pointer_logict pointer_logic; + + typedef boolbvt SUB; + + unsigned object_bits, offset_bits, bits; + + void encode(unsigned object, bvt &bv); + + virtual void convert_pointer_type(const exprt &expr, bvt &bv); + + virtual void add_addr(const exprt &expr, bvt &bv); + + // overloading + virtual literalt convert_rest(const exprt &expr); + + virtual void convert_bitvector(const exprt &expr, bvt &bv); // no cache + + virtual exprt bv_get_rec( + const bvt &bv, + const std::vector &unknown, + unsigned offset, + const typet &type) const; + + bool convert_address_of_rec( + const exprt &expr, + bvt &bv); + + void offset_arithmetic(bvt &bv, const mp_integer &x); + void offset_arithmetic(bvt &bv, const mp_integer &factor, const exprt &index); + void offset_arithmetic(bvt &bv, const mp_integer &factor, const bvt &index_bv); + + struct is_dynamic_objectt + { + bvt bv; + literalt l; + }; + + typedef std::list is_dynamic_object_listt; + is_dynamic_object_listt is_dynamic_object_list; + + void do_is_dynamic_object(const is_dynamic_objectt &is_dynamic_object); + + static bool is_ptr(const typet &type) + { + return type.id()=="pointer" || type.id()=="reference"; + } +}; + +#endif diff --git a/src/solvers/flattening/bv_utils.cpp b/src/solvers/flattening/bv_utils.cpp new file mode 100644 index 00000000000..350bea7e851 --- /dev/null +++ b/src/solvers/flattening/bv_utils.cpp @@ -0,0 +1,1357 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "bv_utils.h" + +/*******************************************************************\ + +Function: bv_utilst::build_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bvt bv_utilst::build_constant(const mp_integer &n, unsigned width) +{ + std::string n_str=integer2binary(n, width); + bvt result; + result.resize(width); + assert(n_str.size()==width); + for(unsigned i=0; i rem < op1 + + prop.l_set_to_true( + prop.limplies(is_not_zero, lt_or_le(false, rem, op1, UNSIGNED))); + + // op1!=0 => res <= op0 + + prop.l_set_to_true( + prop.limplies(is_not_zero, lt_or_le(true, res, op0, UNSIGNED))); +} + +/*******************************************************************\ + +Function: bv_utilst::equal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt bv_utilst::equal(const bvt &op0, const bvt &op1) +{ + assert(op0.size()==op1.size()); + + bvt equal_bv; + equal_bv.resize(op0.size()); + + for(unsigned i=0; i + +#include + +class bv_utilst +{ +public: + bv_utilst(propt &_prop):prop(_prop) { } + + typedef enum { SIGNED, UNSIGNED } representationt; + + bvt build_constant(const mp_integer &i, unsigned width); + + bvt incrementer(const bvt &op, literalt carry_in); + bvt inc(const bvt &op) { return incrementer(op, const_literal(true)); } + void incrementer(bvt &op, literalt carry_in, literalt &carry_out); + + bvt negate(const bvt &op); + bvt negate_no_overflow(const bvt &op); + bvt absolute_value(const bvt &op); + + // returns true iff unary minus will overflow + literalt overflow_negate(const bvt &op); + + // bit-wise negation + bvt inverted(const bvt &op); + + literalt carry(literalt a, literalt b, literalt c); + + bvt add_sub(const bvt &op0, const bvt &op1, bool subtract); + bvt add_sub(const bvt &op0, const bvt &op1, literalt subtract); + bvt add_sub_no_overflow(const bvt &op0, const bvt &op1, bool subtract, representationt rep); + bvt add(const bvt &op0, const bvt &op1) { return add_sub(op0, op1, false); } + bvt sub(const bvt &op0, const bvt &op1) { return add_sub(op0, op1, true); } + literalt overflow_add(const bvt &op0, const bvt &op1, representationt rep); + literalt overflow_sub(const bvt &op0, const bvt &op1, representationt rep); + literalt carry_out(const bvt &op0, const bvt &op1, literalt carry_in); + + typedef enum { LEFT, LRIGHT, ARIGHT } shiftt; + bvt shift(const bvt &op, const shiftt shift, unsigned distance); + bvt shift(const bvt &op, const shiftt shift, const bvt &distance); + + bvt unsigned_multiplier(const bvt &op0, const bvt &op1); + bvt signed_multiplier(const bvt &op0, const bvt &op1); + bvt multiplier(const bvt &op0, const bvt &op1, representationt rep); + bvt multiplier_no_overflow(const bvt &op0, const bvt &op1, representationt rep); + + bvt divider(const bvt &op0, const bvt &op1, + representationt rep) + { + bvt res, rem; + divider(op0, op1, res, rem, rep); + return res; + } + + bvt remainder(const bvt &op0, const bvt &op1, + representationt rep) + { + bvt res, rem; + divider(op0, op1, res, rem, rep); + return rem; + } + + void divider(const bvt &op0, const bvt &op1, + bvt &res, bvt &rem, representationt rep); + + void signed_divider(const bvt &op0, const bvt &op1, + bvt &res, bvt &rem); + + void unsigned_divider(const bvt &op0, const bvt &op1, + bvt &res, bvt &rem); + + literalt equal(const bvt &op0, const bvt &op1); + + literalt is_zero(const bvt &op) + { return prop.lnot(prop.lor(op)); } + + literalt is_one(const bvt &op); + + literalt is_all_ones(const bvt &op) + { return prop.land(op); } + + literalt lt_or_le(bool or_equal, + const bvt &bv0, + const bvt &bv1, + representationt rep); + + literalt unsigned_less_than(const bvt &bv0, const bvt &bv1); + literalt signed_less_than(const bvt &bv0, const bvt &bv1); + + bool is_constant(const bvt &bv); + + bvt extension(const bvt &bv, unsigned new_size, representationt rep); + + bvt sign_extension(const bvt &bv, unsigned new_size) + { + return extension(bv, new_size, SIGNED); + } + + bvt zero_extension(const bvt &bv, unsigned new_size) + { + return extension(bv, new_size, UNSIGNED); + } + + bvt zeros(unsigned new_size) const + { + bvt result; + result.resize(new_size, const_literal(false)); + return result; + } + + void set_equal(const bvt &a, const bvt &b); + + // if cond holds, a has to be equal to b + void cond_implies_equal(literalt cond, const bvt &a, const bvt &b); + + bvt cond_negate(const bvt &bv, const literalt cond); + + bvt select(literalt s, const bvt &a, const bvt &b); + + // computes a[last:first] + bvt extract(const bvt &a, unsigned first, unsigned last) const; + + // put a and b together, where a comes first (lower indices) + bvt concatenate(const bvt &a, const bvt &b) const; + +protected: + propt ∝ + + void adder(bvt &sum, const bvt &op, + literalt carry_in, literalt &carry_out); + + void adder_no_overflow( + bvt &sum, + const bvt &op, + bool subtract, + representationt rep); + + void adder_no_overflow(bvt &sum, const bvt &op); + + bvt unsigned_multiplier_no_overflow( + const bvt &op0, const bvt &op1); + + bvt signed_multiplier_no_overflow( + const bvt &op0, const bvt &op1); + + bvt cond_negate_no_overflow(const bvt &bv, const literalt cond); +}; + +#endif diff --git a/src/solvers/flattening/equality.cpp b/src/solvers/flattening/equality.cpp new file mode 100644 index 00000000000..4954f045e82 --- /dev/null +++ b/src/solvers/flattening/equality.cpp @@ -0,0 +1,183 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +//#define DEBUG + +#include "equality.h" +#include "bv_utils.h" + +/*******************************************************************\ + +Function: equalityt::equality + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt equalityt::equality(const exprt &e1, const exprt &e2) +{ + if(e1 u; + + { + std::pair result= + elements.insert(std::pair(e1, elements.size())); + + u.first=result.first->second; + + if(result.second) + elements_rev[u.first]=e1; + } + + { + std::pair result= + elements.insert(std::pair(e2, elements.size())); + + u.second=result.first->second; + + if(result.second) + elements_rev[u.second]=e2; + } + + literalt l; + + { + equalitiest::const_iterator result=equalities.find(u); + + if(result==equalities.end()) + { + l=prop.new_variable(); + equalities.insert(std::pair, literalt>(u, l)); + } + else + l=result->second; + } + + #ifdef DEBUG + std::cout << "EQUALITY " << l << "<=>" + << e1 << "=" << e2 << std::endl; + #endif + + return l; +} + +/*******************************************************************\ + +Function: equalityt::add_equality_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void equalityt::add_equality_constraints() +{ + for(typemapt::const_iterator it=typemap.begin(); + it!=typemap.end(); it++) + add_equality_constraints(it->second); +} + +/*******************************************************************\ + +Function: equalityt::add_equality_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void equalityt::add_equality_constraints(const typestructt &typestruct) +{ + unsigned no_elements=typestruct.elements.size(); + unsigned bits=0; + + // get number of necessary bits + + for(unsigned i=no_elements; i!=0; bits++) + i=(i>>1); + + // generate bit vectors + + std::vector eq_bvs; + + eq_bvs.resize(no_elements); + + for(unsigned i=0; ifirst.first]; + const bvt &bv2=eq_bvs[it->first.second]; + + prop.set_equal(bv_utils.equal(bv1, bv2), it->second); + } +} diff --git a/src/solvers/flattening/equality.h b/src/solvers/flattening/equality.h new file mode 100644 index 00000000000..68423bb9da5 --- /dev/null +++ b/src/solvers/flattening/equality.h @@ -0,0 +1,55 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SOLVER_FLATTENING_EQUALITY_H +#define CPROVER_SOLVER_FLATTENING_EQUALITY_H + +#include + +#include +#include + +#include + +class equalityt:public prop_convt +{ +public: + equalityt( + const namespacet &_ns, + propt &_prop):prop_convt(_ns, _prop) { } + + virtual literalt equality(const exprt &e1, const exprt &e2); + + virtual void post_process() + { + add_equality_constraints(); + prop_convt::post_process(); + } + +protected: + typedef hash_map_cont elementst; + typedef std::map, literalt> equalitiest; + typedef std::map elements_revt; + + struct typestructt + { + elementst elements; + elements_revt elements_rev; + equalitiest equalities; + }; + + typedef hash_map_cont typemapt; + + typemapt typemap; + + virtual literalt equality2(const exprt &e1, const exprt &e2); + virtual void add_equality_constraints(); + virtual void add_equality_constraints(const typestructt &typestruct); +}; + +#endif diff --git a/src/solvers/flattening/functions.cpp b/src/solvers/flattening/functions.cpp new file mode 100644 index 00000000000..ab9a59b6143 --- /dev/null +++ b/src/solvers/flattening/functions.cpp @@ -0,0 +1,135 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +//#define DEBUG + +#include + +#include +#include + +#include "functions.h" + +/*******************************************************************\ + +Function: functionst::record + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void functionst::record( + const function_application_exprt &function_application) +{ + function_map[function_application.function()].applications. + insert(function_application); +} + +/*******************************************************************\ + +Function: functionst::add_function_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void functionst::add_function_constraints() +{ + for(function_mapt::const_iterator it= + function_map.begin(); + it!=function_map.end(); + it++) + add_function_constraints(it->second); +} + +/*******************************************************************\ + +Function: functionst::add_function_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt functionst::arguments_equal(const exprt::operandst &o1, + const exprt::operandst &o2) +{ + assert(o1.size()==o2.size()); + + if(o1.empty()) + return const_literal(true); + + bvt conjuncts; + conjuncts.resize(o1.size()); + + for(unsigned i=0; i::const_iterator + it1=info.applications.begin(); + it1!=info.applications.end(); + it1++) + { + for(std::set::const_iterator + it2=info.applications.begin(); + it2!=it1; + it2++) + { + literalt arguments_equal_lit= + arguments_equal(it1->arguments(), it2->arguments()); + + if(arguments_equal_lit!=const_literal(false)) + { + bvt implication; + implication.reserve(2); + implication.push_back(prop_conv.prop.lnot(arguments_equal_lit)); + implication.push_back(prop_conv(equal_exprt(*it1, *it2))); + prop_conv.prop.lcnf(implication); + } + } + } +} diff --git a/src/solvers/flattening/functions.h b/src/solvers/flattening/functions.h new file mode 100644 index 00000000000..0d4a4536df0 --- /dev/null +++ b/src/solvers/flattening/functions.h @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: Uninterpreted Functions + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_FUNCTIONS_H +#define CPROVER_FUNCTIONS_H + +#include + +#include + +class functionst +{ +public: + explicit functionst(prop_conv_baset &_prop_conv): + prop_conv(_prop_conv) { } + + virtual ~functionst() + { + } + + void record( + const class function_application_exprt &function_application); + + virtual void post_process() + { + add_function_constraints(); + } + +protected: + prop_conv_baset &prop_conv; + + typedef std::set applicationst; + + struct function_infot + { + applicationst applications; + }; + + typedef std::map function_mapt; + function_mapt function_map; + + virtual void add_function_constraints(); + virtual void add_function_constraints(const function_infot &info); + + literalt arguments_equal(const exprt::operandst &o1, + const exprt::operandst &o2); +}; + +#endif diff --git a/src/solvers/flattening/pointer_logic.cpp b/src/solvers/flattening/pointer_logic.cpp new file mode 100644 index 00000000000..7a732ac2f2f --- /dev/null +++ b/src/solvers/flattening/pointer_logic.cpp @@ -0,0 +1,296 @@ +/*******************************************************************\ + +Module: Pointer Logic + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "pointer_logic.h" + +/*******************************************************************\ + +Function: pointer_logict::is_dynamic_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool pointer_logict::is_dynamic_object(const exprt &expr) const +{ + if(expr.type().get_bool("#dynamic")) return true; + + if(expr.id()==ID_symbol) + if(has_prefix(id2string(to_symbol_expr(expr).get_identifier()), + "symex_dynamic::")) + return true; + + return false; +} + +/*******************************************************************\ + +Function: pointer_logict::get_dynamic_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void pointer_logict::get_dynamic_objects(std::vector &o) const +{ + o.clear(); + unsigned nr=0; + + for(pointer_logict::objectst::const_iterator + it=objects.begin(); + it!=objects.end(); + it++, nr++) + if(is_dynamic_object(*it)) + o.push_back(nr); +} + +/*******************************************************************\ + +Function: pointer_logict::add_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned pointer_logict::add_object(const exprt &expr) +{ + // remove any index/member + + if(expr.id()==ID_index) + { + assert(expr.operands().size()==2); + return add_object(expr.op0()); + } + else if(expr.id()==ID_member) + { + assert(expr.operands().size()==1); + return add_object(expr.op0()); + } + + return objects.number(expr); +} + +/*******************************************************************\ + +Function: pointer_logict::pointer_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt pointer_logict::pointer_expr( + unsigned object, + const typet &type) const +{ + pointert pointer(object, 0); + return pointer_expr(pointer, type); +} + +/*******************************************************************\ + +Function: pointer_logict::pointer_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt pointer_logict::pointer_expr( + const pointert &pointer, + const typet &type) const +{ + if(pointer.object==null_object) // NULL? + { + if(pointer.offset==0) + { + constant_exprt result(type); + result.set_value(ID_NULL); + return result; + } + else + { + constant_exprt null(type); + null.set_value(ID_NULL); + return plus_exprt(null, + from_integer(pointer.offset, integer_typet())); + } + } + else if(pointer.object==invalid_object) // INVALID? + { + constant_exprt result(type); + result.set_value("INVALID"); + return result; + } + + if(pointer.object>=objects.size() || pointer.object<0) + { + constant_exprt result(type); + result.set_value("INVALID-"+i2string(pointer.object)); + return result; + } + + const exprt &object_expr=objects[pointer.object]; + + exprt deep_object=object_rec(pointer.offset, type, object_expr); + + exprt result; + + if(type.id()==ID_pointer) + result=exprt(ID_address_of, type); + else if(type.id()==ID_reference) + result=exprt("reference_to", type); + else + assert(0); + + result.copy_to_operands(deep_object); + return result; +} + +/*******************************************************************\ + +Function: pointer_logict::object_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt pointer_logict::object_rec( + const mp_integer &offset, + const typet &pointer_type, + const exprt &src) const +{ + if(src.type().id()==ID_array) + { + mp_integer size= + pointer_offset_size(ns, src.type().subtype()); + + if(size==0) return src; + + mp_integer index=offset/size; + mp_integer rest=offset%size; + if(rest<0) rest=-rest; + + index_exprt tmp(src.type().subtype()); + tmp.index()=from_integer(index, typet(ID_integer)); + tmp.array()=src; + + return object_rec(rest, pointer_type, tmp); + } + else if(src.type().id()==ID_struct) + { + const struct_typet::componentst &components= + to_struct_type(src.type()).components(); + + if(offset<0) return src; + + mp_integer current_offset=0; + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + assert(offset>=current_offset); + + const typet &subtype=it->type(); + + mp_integer sub_size=pointer_offset_size(ns, subtype); + mp_integer new_offset=current_offset+sub_size; + + if(new_offset>offset) + { + // found it + member_exprt tmp(subtype); + tmp.set_component_name(it->get_name()); + tmp.op0()=src; + + return object_rec( + offset-current_offset, pointer_type, tmp); + } + + assert(new_offset<=offset); + current_offset=new_offset; + assert(current_offset<=offset); + } + + return src; + } + else if(src.type().id()==ID_union) + return src; + + return src; +} + +/*******************************************************************\ + +Function: pointer_logict::pointer_logict + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +pointer_logict::pointer_logict(const namespacet &_ns):ns(_ns) +{ + // add NULL + null_object=objects.number(exprt(ID_NULL)); + assert(null_object==0); + + // add INVALID + invalid_object=objects.number(exprt("INVALID")); +} + +/*******************************************************************\ + +Function: pointer_logict::~pointer_logict + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +pointer_logict::~pointer_logict() +{ +} diff --git a/src/solvers/flattening/pointer_logic.h b/src/solvers/flattening/pointer_logic.h new file mode 100644 index 00000000000..5dcd730b029 --- /dev/null +++ b/src/solvers/flattening/pointer_logic.h @@ -0,0 +1,85 @@ +/*******************************************************************\ + +Module: Pointer Logic + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_POINTER_LOGIC_H +#define CPROVER_POINTER_LOGIC_H + +#include +#include +#include +#include + +#define BV_ADDR_BITS 8 + +class pointer_logict +{ +public: + // this numbers the objects + typedef hash_numbering objectst; + objectst objects; + + struct pointert + { + unsigned object; + mp_integer offset; + + pointert() + { + } + + pointert(unsigned _obj, mp_integer _off):object(_obj), offset(_off) + { + } + }; + + // converts an (object,offset) pair to an expression + exprt pointer_expr( + const pointert &pointer, + const typet &type) const; + + // converts an (object,0) pair to an expression + exprt pointer_expr( + unsigned object, + const typet &type) const; + + ~pointer_logict(); + explicit pointer_logict(const namespacet &_ns); + + unsigned add_object(const exprt &expr); + + // number of NULL object + unsigned get_null_object() const + { + return null_object; + } + + // number of INVALID object + unsigned get_invalid_object() const + { + return invalid_object; + } + + bool is_dynamic_object(const exprt &expr) const; + + void get_dynamic_objects(std::vector &objects) const; + +protected: + const namespacet &ns; + unsigned null_object, invalid_object; + + exprt pointer_expr( + const mp_integer &offset, + const exprt &object) const; + + exprt object_rec( + const mp_integer &offset, + const typet &pointer_type, + const exprt &src) const; +}; + +#endif diff --git a/src/solvers/flattening/sat_minimizer.cpp b/src/solvers/flattening/sat_minimizer.cpp new file mode 100644 index 00000000000..94c18ca6f52 --- /dev/null +++ b/src/solvers/flattening/sat_minimizer.cpp @@ -0,0 +1,96 @@ +/*******************************************************************\ + +Module: + +Author: Georg Weissenbacher, georg.weissenbacher@inf.ethz.ch + +\*******************************************************************/ + +#include + +#include + +#include + +#include "sat_minimizer.h" + +/*******************************************************************\ + +Function: bv_minimizering_dect::minimize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool bv_minimizing_dect::minimize(const minimization_listt &symbols) +{ + // unfortunately, only MiniSat supports this + + #if defined(SATCHECK_MINISAT) || defined(SATCHECK_MINISAT2) + bvt constraints; + + for(minimization_listt::const_iterator + l_it=symbols.begin(); + l_it!=symbols.end(); + l_it++) + { + unsigned result=0; + + const exprt &symbol = *l_it; + + status("Minimizing... "); + + // FIXME: be more gener[ous|al] here! + assert(symbol.type().id()==ID_unsignedbv); + + unsigned width=boolbv_width(symbol.type()); + + for(unsigned i = width; i > 0; i--) + { + literalt lit; +#if 0 + std::cout << "SYMBOL: " << symbol << std::endl; +#endif + if(literal(symbol, i-1, lit)) + continue; // there's no corresponding literal + + if(lit.is_constant()) + continue; + + literalt nlit=satcheck.lnot(lit); + constraints.push_back(nlit); + + if(satcheck.l_get(lit)==tvt(false)) + continue; + + // call Minisat with constraints + satcheck.set_assumptions(constraints); + + if(satcheck.prop_solve() == propt::P_UNSATISFIABLE) + { + // change constraint to 1 + constraints.back().swap(lit); + result |= 1 << (i-1); + + // make sure the model is reconstructed + satcheck.set_assumptions(constraints); + if(satcheck.prop_solve()==propt::P_UNSATISFIABLE) + assert (false); // do not remove call to prop_solve!! + } + } + + status(symbol.get_string(ID_identifier)+" = "+i2string(result)); + } + + return true; + #else + // we don't have it... + return false; + #endif +} + + diff --git a/src/solvers/flattening/sat_minimizer.h b/src/solvers/flattening/sat_minimizer.h new file mode 100644 index 00000000000..cefe5f1dd37 --- /dev/null +++ b/src/solvers/flattening/sat_minimizer.h @@ -0,0 +1,42 @@ +/*******************************************************************\ + +Module: SAT-optimizer for minimizing expressions + +Author: Georg Weissenbacher + +Date: July 2006 + +Purpose: Find a satisfying assignment that minimizes a given set + of symbols + +\*******************************************************************/ + +#ifndef CPROVER_CEGAR_SAT_OPTIMIZER_H +#define CPROVER_CEGAR_SAT_OPTIMIZER_H + +#include +#include + +typedef std::set minimization_listt; + +typedef satcheckt sat_minimizert; + +class bv_minimizing_dect:public bv_pointerst +{ +public: + virtual const std::string description() + { + return "Bit vector minimizing SAT"; + } + + explicit bv_minimizing_dect(const namespacet &_ns): + bv_pointerst(_ns, satcheck) + { + } + + bool minimize(const minimization_listt &symbols); + + sat_minimizert satcheck; +}; + +#endif diff --git a/src/solvers/prop/aig.cpp b/src/solvers/prop/aig.cpp new file mode 100644 index 00000000000..c3b7f880ed0 --- /dev/null +++ b/src/solvers/prop/aig.cpp @@ -0,0 +1,299 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include + +#include "aig.h" + +/*******************************************************************\ + +Function: aigt::label + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string aigt::label(unsigned v) const +{ + return "var("+i2string(v)+")"; +} + +/*******************************************************************\ + +Function: aigt::dot_label + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string aigt::dot_label(unsigned v) const +{ + return "var("+i2string(v)+")"; +} + +/*******************************************************************\ + +Function: aigt::get_terminals + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void aigt::get_terminals(terminalst &terminals) const +{ + for(unsigned n=0; nsecond; // already done + + assert(n &ta=get_terminals_rec(node.a.var_no(), terminals); + t.insert(ta.begin(), ta.end()); + } + + if(!node.b.is_constant()) + { + const std::set &tb=get_terminals_rec(node.b.var_no(), terminals); + t.insert(tb.begin(), tb.end()); + } + } + else // this is a terminal + { + t.insert(n); + } + + return t; +} + +/*******************************************************************\ + +Function: aigt::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void aigt::print( + std::ostream& out, + literalt a) const +{ + if(a==const_literal(false)) + { + out << "FALSE"; + return; + } + else if(a==const_literal(true)) + { + out << "TRUE"; + return; + } + + unsigned node_nr=a.var_no(); + + { + const aig_nodet &node=nodes[node_nr]; + + switch(node.type) + { + case aig_nodet::AND: + if(a.sign()) out << "!("; + print(out, node.a); + out << " & "; + print(out, node.b); + if(a.sign()) out << ")"; + break; + + case aig_nodet::VAR: + if(a.sign()) out << "!"; + out << label(node_nr); + break; + + default: + if(a.sign()) out << "!"; + out << "unknown(" << node_nr << ")"; + } + } +} + +/*******************************************************************\ + +Function: aigt::output_dot_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void aigt::output_dot_node( + std::ostream &out, + unsigned v) const +{ + const aig_nodet &node=nodes[v]; + + if(node.is_and()) + { + out << v << " [label=\"" << v << "\"]" << std::endl; + output_dot_edge(out, v, node.a); + output_dot_edge(out, v, node.b); + } + else // the node is a terminal + { + out << v << " [label=\"" << dot_label(v) << "\"" + << ",shape=box]" << std::endl; + } +} + +/*******************************************************************\ + +Function: aigt::output_dot_edge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void aigt::output_dot_edge( + std::ostream& out, + unsigned v, + literalt l) const +{ + if(l.is_true()) + { + out << "TRUE -> " << v; + } + else if(l.is_false()) + { + out << "TRUE -> " << v; + out << " [arrowhead=odiamond]"; + } + else + { + out << l.var_no() << " -> " << v; + if(l.sign()) out << " [arrowhead=odiamond]"; + } + + out << std::endl; +} + +/*******************************************************************\ + +Function: aigt::output_dot + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void aigt::output_dot(std::ostream& out) const +{ + // constant TRUE + out << "TRUE [label=\"TRUE\", shape=box]" << std::endl; + + // now the nodes + for(unsigned n=0; n literals; + + aig.convert(dest, literals); + + unsigned v=l.var_no(); + + assert(v + +#include "aig_prop.h" + +/*******************************************************************\ + +Function: aig_propt::set_l + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +#if 0 +void aig_propt::set_l(propt &dest, literalt a, literalt l) +{ + unsigned v=a.var_no(); + bool sign=a.sign(); + + assert(v + +#include + +#include "aig.h" + +class aig_propt:public propt +{ +public: + aig_propt(aigt &_dest): + dest(_dest) + { + } + + virtual bool has_set_to() const { return false; } + + virtual literalt land(literalt a, literalt b); + virtual literalt lor(literalt a, literalt b); + virtual literalt land(const bvt &bv); + virtual literalt lor(const bvt &bv); + virtual void lcnf(const bvt &bv) { assert(0); } + virtual literalt lnot(literalt a); + virtual literalt lxor(literalt a, literalt b); + virtual literalt lxor(const bvt &bv); + virtual literalt lnand(literalt a, literalt b); + virtual literalt lnor(literalt a, literalt b); + virtual literalt lequal(literalt a, literalt b); + virtual literalt limplies(literalt a, literalt b); + virtual literalt lselect(literalt a, literalt b, literalt c); // a?b:c + + virtual literalt new_variable() + { + return dest.new_node(); + } + + virtual unsigned no_variables() const + { return dest.number_of_nodes(); } + + virtual const std::string solver_text() + { return "conversion into and-inverter graph"; } + + virtual tvt l_get(literalt a) const + { assert(0); return tvt(tvt::TV_UNKNOWN); } + + virtual resultt prop_solve() + { assert(0); return P_ERROR; } + +protected: + aigt &dest; +}; + +#endif diff --git a/src/solvers/prop/bformula.h b/src/solvers/prop/bformula.h new file mode 100644 index 00000000000..0b997d60783 --- /dev/null +++ b/src/solvers/prop/bformula.h @@ -0,0 +1,128 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PROPDEC_BFORMULA_H +#define CPROVER_PROPDEC_BFORMULA_H + +#include "prop.h" + +class bformulat +{ +protected: + literalt l; + propt *prop; + +public: + // constructors + bformulat():prop(NULL) + { + } + + friend bformulat operator! (bformulat f) + { + f.l.invert(); + return f; + } + + friend bformulat operator? ( + bformulat c, bformulat a, bformulat b); + + friend bformulat operator | (bformulat a, bformulat b); + friend bformulat operator & (bformulat a, bformulat b); + friend bformulat operator ^ (bformulat a, bformulat b); + friend bformulat operator ==(bformulat a, bformulat b); + friend bformulat operator !=(bformulat a, bformulat b); + + // for sets + friend inline bool operator <(const bformulat a, const bformulat b) + { + return a.ll_get(l); + } + + friend inline bformulat const_bformula(bool value) + { + bformulat f; + f.l=const_literal(value); + return f; + } + + inline bool is_constant() const + { + return l.is_constant(); + } +}; + +// constants +bformulat const_bformula(bool value); + +//typedef std::vector bvt; + +#endif diff --git a/src/solvers/prop/literal.h b/src/solvers/prop/literal.h new file mode 100644 index 00000000000..b628e08b24f --- /dev/null +++ b/src/solvers/prop/literal.h @@ -0,0 +1,188 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PROPDEC_LITERAL_H +#define CPROVER_PROPDEC_LITERAL_H + +#include + +// a pair of a variable number and a sign, encoded as follows: +// +// sign='false' means positive +// sign='true' means negative +// + +class literalt +{ +public: + // constructors + literalt() + { + set(unused_var_no(), false); + } + + literalt(unsigned v, bool sign) + { + set(v, sign); + } + + class literalt negation() const + { + literalt result(*this); + result.invert(); + return result; + } + + class literalt cond_negation(bool cond) const + { + literalt result(*this); + result.cond_invert(cond); + return result; + } + + friend inline bool operator ==(const literalt a, const literalt b) + { + return a.l==b.l; + } + + friend inline bool operator !=(const literalt a, const literalt b) + { + return a.l!=b.l; + } + + // for sets + friend inline bool operator <(const literalt a, const literalt b) + { + return a.l>1; + } + + inline bool sign() const + { + return l&1; + } + + inline void set(unsigned _l) + { + l=_l; + } + + inline void set(unsigned v, bool sign) + { + l=(v<<1)|((unsigned)sign); + } + + inline unsigned get() const + { + return l; + } + + inline void invert() + { + l^=1; + } + + inline void cond_invert(bool a) + { + l^=(a?1:0); + } + + // + // Returns a literal in the dimacs CNF encoding + // A negative integer denotes a negated literal + // + int dimacs() const + { + int result=var_no(); + + if(sign()) + result=-result; + + return result; + } + + void from_dimacs(int d) + { + bool sign=d<0; + if(sign) d=-d; + set(d, sign); + } + + void clear() + { + l=0; + } + + inline void swap(literalt &x) + { + std::swap(x.l, l); + } + + // constants + inline void make_true() + { + set(const_var_no(), true); + } + + inline void make_false() + { + set(const_var_no(), false); + } + + inline bool is_true() const + { + return is_constant() && sign(); + } + + inline bool is_false() const + { + return is_constant() && !sign(); + } + + friend inline literalt const_literal(bool value) + { + literalt l; + l.set(literalt::const_var_no(), value); + return l; + } + + inline bool is_constant() const + { + return var_no()==const_var_no(); + } + + friend inline literalt neg(literalt a) { return a.negation(); } + friend inline literalt pos(literalt a) { return a; } + + static inline unsigned const_var_no() + { + return (unsigned(-1)<<1)>>1; + } + + static inline unsigned unused_var_no() + { + return (unsigned(-2)<<1)>>1; + } + +protected: + unsigned l; +}; + +// constants +literalt const_literal(bool value); + +literalt neg(literalt a); +literalt pos(literalt a); + +typedef std::vector bvt; + +#endif diff --git a/src/solvers/prop/prop.cpp b/src/solvers/prop/prop.cpp new file mode 100644 index 00000000000..0175173f5a0 --- /dev/null +++ b/src/solvers/prop/prop.cpp @@ -0,0 +1,127 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "prop.h" + +/*******************************************************************\ + +Function: propt::set_equal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void propt::set_equal(literalt a, literalt b) +{ + bvt bv; + bv.resize(2); + bv[0]=a; + bv[1]=lnot(b); + lcnf(bv); + + bv[0]=lnot(a); + bv[1]=b; + lcnf(bv); +} + +/*******************************************************************\ + +Function: propt::set_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void propt::set_assignment(literalt a, bool value) +{ + assert(false); +} + +/*******************************************************************\ + +Function: propt::copy_assignment_from + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void propt::copy_assignment_from(const propt &src) +{ + assert(false); +} + +/*******************************************************************\ + +Function: propt::is_in_conflict + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool propt::is_in_conflict(literalt l) const +{ + assert(false); + return false; +} + +/*******************************************************************\ + +Function: propt::is_in_core + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool propt::is_in_core(literalt l) const +{ + assert(false); + return false; +} + +/*******************************************************************\ + +Function: propt::new_variables + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bvt propt::new_variables(unsigned width) +{ + bvt result; + result.resize(width); + for(unsigned i=0; i + +#include +#include + +#include "literal.h" + +class propt:public messaget +{ +public: + propt() { } + virtual ~propt() { } + + // boolean operators + virtual literalt land(literalt a, literalt b)=0; + virtual literalt lor(literalt a, literalt b)=0; + virtual literalt land(const bvt &bv)=0; + virtual literalt lor(const bvt &bv)=0; + virtual literalt lnot(literalt a)=0; + virtual literalt lxor(literalt a, literalt b)=0; + virtual literalt lxor(const bvt &bv)=0; + virtual literalt lnand(literalt a, literalt b)=0; + virtual literalt lnor(literalt a, literalt b)=0; + virtual literalt lequal(literalt a, literalt b)=0; + virtual literalt limplies(literalt a, literalt b)=0; + virtual literalt lselect(literalt a, literalt b, literalt c)=0; // a?b:c + virtual void set_equal(literalt a, literalt b); + + virtual void l_set_to(literalt a, bool value) + { + set_equal(a, const_literal(value)); + } + + void l_set_to_true(literalt a) + { l_set_to(a, true); } + void l_set_to_false(literalt a) + { l_set_to(a, false); } + + // constraints + virtual void lcnf(const bvt &bv)=0; + virtual bool has_set_to() const { return true; } + + // assumptions + virtual void set_assumptions(const bvt &_assumptions) { } + virtual bool has_set_assumptions() const { return false; } + + // variables + virtual literalt new_variable()=0; + virtual void set_variable_name(literalt a, const std::string &name) { } + virtual unsigned no_variables() const=0; + bvt new_variables(unsigned width); + + // solving + virtual const std::string solver_text()=0; + typedef enum { P_SATISFIABLE, P_UNSATISFIABLE, P_ERROR } resultt; + virtual resultt prop_solve()=0; + + // satisfying assignment + virtual tvt l_get(literalt a) const=0; + virtual void set_assignment(literalt a, bool value); + virtual void copy_assignment_from(const propt &prop); + + // returns true if an assumption is in the final conflict + virtual bool is_in_conflict(literalt l) const; + virtual bool has_is_in_conflict() const { return false; } + + // cores -- will be removed + virtual bool is_in_core(literalt l) const; + virtual bool has_in_core() const { return false; } +}; + +#endif diff --git a/src/solvers/prop/prop_conv.cpp b/src/solvers/prop/prop_conv.cpp new file mode 100644 index 00000000000..8f2645fe9f7 --- /dev/null +++ b/src/solvers/prop/prop_conv.cpp @@ -0,0 +1,638 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include +#include + +#include "prop_conv.h" + +//#define DEBUG + +/*******************************************************************\ + +Function: prop_convt::literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool prop_convt::literal(const exprt &expr, literalt &dest) const +{ + assert(expr.type().id()==ID_bool); + + if(expr.id()==ID_symbol) + { + const irep_idt &identifier= + to_symbol_expr(expr).get_identifier(); + + symbolst::const_iterator result=symbols.find(identifier); + + if(result==symbols.end()) return true; + dest=result->second; + return false; + } + + throw "found no literal for expression"; +} + +/*******************************************************************\ + +Function: prop_convt::get_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt prop_convt::get_literal(const irep_idt &identifier) +{ + std::pair result= + symbols.insert(std::pair(identifier, literalt())); + + if(!result.second) + return result.first->second; + + // produce new variable + literalt literal=prop.new_variable(); + + // set the name of the new variable + prop.set_variable_name(literal, id2string(identifier)); + + // insert + result.first->second=literal; + + return literal; +} + +/*******************************************************************\ + +Function: prop_convt::get_bool + + Inputs: + + Outputs: + + Purpose: get a boolean value from counter example if not valid + +\*******************************************************************/ + +bool prop_convt::get_bool(const exprt &expr, tvt &value) const +{ + // trivial cases + + if(expr.is_true()) + { + value=tvt(true); + return false; + } + else if(expr.is_false()) + { + value=tvt(false); + return false; + } + else if(expr.id()==ID_symbol) + { + symbolst::const_iterator result= + symbols.find(to_symbol_expr(expr).get_identifier()); + + if(result==symbols.end()) return true; + + value=prop.l_get(result->second); + return false; + } + + // sub-expressions + + if(expr.id()==ID_not) + { + if(expr.type().id()==ID_bool && + expr.operands().size()==1) + { + if(get_bool(expr.op0(), value)) return true; + value=!value; + return false; + } + } + else if(expr.id()==ID_and || expr.id()==ID_or) + { + if(expr.type().id()==ID_bool && + expr.operands().size()>=1) + { + value=tvt(expr.id()==ID_and); + + forall_operands(it, expr) + { + tvt tmp; + if(get_bool(*it, tmp)) return true; + + if(expr.id()==ID_and) + { + if(tmp.is_false()) { value=tvt(false); return false; } + + value=value && tmp; + } + else // or + { + if(tmp.is_true()) { value=tvt(true); return false; } + + value=value || tmp; + } + } + + return false; + } + } + + // check cache + + cachet::const_iterator cache_result=cache.find(expr); + if(cache_result==cache.end()) return true; + + value=prop.l_get(cache_result->second); + return false; +} + +/*******************************************************************\ + +Function: prop_convt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt prop_convt::convert(const exprt &expr) +{ + if(!use_cache || + expr.id()==ID_symbol || + expr.id()==ID_constant) + return convert_bool(expr); + + // check cache first + + std::pair result= + cache.insert(std::pair(expr, literalt())); + + if(!result.second) + return result.first->second; + + literalt literal=convert_bool(expr); + + // insert into cache + + result.first->second=literal; + + #if 0 + std::cout << literal << "=" << expr << std::endl; + #endif + + return literal; +} + +/*******************************************************************\ + +Function: prop_convt::convert_bool + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt prop_convt::convert_bool(const exprt &expr) +{ + if(expr.type().id()!=ID_bool) + { + std::string msg="prop_convt::convert_bool got " + "non-boolean expression:\n"; + msg+=expr.to_string(); + throw msg; + } + + const exprt::operandst &op=expr.operands(); + + if(expr.is_constant()) + { + if(expr.is_true()) + return const_literal(true); + else if(expr.is_false()) + return const_literal(false); + else + throw "unknown boolean constant: "+expr.to_string(); + } + else if(expr.id()==ID_symbol) + { + return get_literal(expr.get(ID_identifier)); + } + else if(expr.id()==ID_literal) + { + literalt l; + l.set(atoi(expr.get(ID_literal).c_str())); + return l; + } + else if(expr.id()==ID_nondet_symbol) + { + return prop.new_variable(); + } + else if(expr.id()==ID_implies) + { + if(op.size()!=2) + throw "implication takes two operands"; + + return prop.limplies(convert(op[0]), convert(op[1])); + } + else if(expr.id()==ID_if) + { + if(op.size()!=3) + throw "if takes three operands"; + + return prop.lselect(convert(op[0]), convert(op[1]), convert(op[2])); + } + else if(expr.id()==ID_constraint_select_one) + { + if(op.size()<2) + throw "constraint_select_one takes at least two operands"; + + std::vector op_bv; + op_bv.resize(op.size()); + + unsigned i=0; + forall_operands(it, expr) + op_bv[i++]=convert(*it); + + // add constraints + + bvt b; + b.reserve(op_bv.size()-1); + + for(unsigned i=1; i result= + symbols.insert(std::pair(identifier, tmp)); + + if(result.second) + return false; // ok, inserted! + + // nah, already there + } + } + + return true; +} + +/*******************************************************************\ + +Function: prop_convt::set_to + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_convt::set_to(const exprt &expr, bool value) +{ + if(expr.type().id()!=ID_bool) + { + std::string msg="prop_convt::set_to got " + "non-boolean expression:\n"; + msg+=expr.to_string(); + throw msg; + } + + bool boolean=true; + + forall_operands(it, expr) + if(it->type().id()!=ID_bool) + { + boolean=false; + break; + } + + if(boolean) + { + if(expr.id()==ID_not) + { + if(expr.operands().size()==1) + { + set_to(expr.op0(), !value); + return; + } + } + else + { + if(value) + { + // set_to_true + + if(expr.id()==ID_and) + { + forall_operands(it, expr) + set_to_true(*it); + + return; + } + else if(expr.id()==ID_or) + { + if(expr.operands().size()>0) + { + bvt bv; + bv.reserve(expr.operands().size()); + + forall_operands(it, expr) + bv.push_back(convert(*it)); + + prop.lcnf(bv); + return; + } + } + else if(expr.id()==ID_implies) + { + if(expr.operands().size()==2) + { + bvt bv; + bv.resize(2); + bv[0]=prop.lnot(convert(expr.op0())); + bv[1]=convert(expr.op1()); + prop.lcnf(bv); + return; + } + } + else if(expr.id()==ID_equal) + { + if(!set_equality_to_true(expr)) + return; + } + } + else + { + // set_to_false + if(expr.id()==ID_implies) // !(a=>b) == (a && !b) + { + if(expr.operands().size()==2) + { + set_to_true(expr.op0()); + set_to_false(expr.op1()); + } + } + else if(expr.id()==ID_or) // !(a || b) == (!a && !b) + { + forall_operands(it, expr) + set_to_false(*it); + } + } + } + } + + // fall back to convert + prop.l_set_to(convert(expr), value); +} + +/*******************************************************************\ + +Function: prop_convt::ignoring + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_convt::ignoring(const exprt &expr) +{ + // fall through + + std::string msg="warning: ignoring "+expr.pretty(); + + print(2, msg); +} + +/*******************************************************************\ + +Function: prop_convt::post_process + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_convt::post_process() +{ +} + +/*******************************************************************\ + +Function: prop_convt::solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt prop_convt::dec_solve() +{ + post_process(); + + print(7, "Solving with "+prop.solver_text()); + + propt::resultt result=prop.prop_solve(); + + switch(result) + { + case propt::P_SATISFIABLE: return D_SATISFIABLE; + case propt::P_UNSATISFIABLE: return D_UNSATISFIABLE; + default: return D_ERROR; + } + + return D_ERROR; +} + +/*******************************************************************\ + +Function: prop_convt::get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt prop_convt::get(const exprt &expr) const +{ + exprt dest; + + dest.make_nil(); + + tvt value; + + if(expr.type().id()==ID_bool && + !get_bool(expr, value)) + { + switch(value.get_value()) + { + case tvt::TV_TRUE: dest.make_true(); return dest; + case tvt::TV_FALSE: dest.make_false(); return dest; + case tvt::TV_UNKNOWN: dest.make_false(); return dest; // default + } + } + + return dest; +} + +/*******************************************************************\ + +Function: prop_convt::print_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_convt::print_assignment(std::ostream &out) const +{ + for(symbolst::const_iterator it=symbols.begin(); + it!=symbols.end(); + it++) + out << it->first << " = " << prop.l_get(it->second) << std::endl; +} + diff --git a/src/solvers/prop/prop_conv.h b/src/solvers/prop/prop_conv.h new file mode 100644 index 00000000000..1801928f397 --- /dev/null +++ b/src/solvers/prop/prop_conv.h @@ -0,0 +1,108 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +// +// converts the propositional skeleton +// + +#ifndef CPROVER_PROP_CONV_H +#define CPROVER_PROP_CONV_H + +#include +#include + +#include +#include +#include + +#include "prop.h" + +class prop_conv_baset:public decision_proceduret +{ +public: + explicit prop_conv_baset( + const namespacet &_ns, propt &_prop): + decision_proceduret(_ns), prop(_prop) { } + virtual ~prop_conv_baset() { } + + // overloading from decision_proceduret + virtual std::string decision_procedure_text() const + { return "propositional reduction"; } + + // conversion + virtual literalt convert(const exprt &expr)=0; + + inline literalt operator()(const exprt &expr) + { + return convert(expr); + } + + propt ∝ +}; + +class prop_convt:public prop_conv_baset +{ +public: + prop_convt(const namespacet &_ns, propt &_prop): + prop_conv_baset(_ns, _prop), + use_cache(true), + equality_propagation(true) { } + virtual ~prop_convt() { } + + // overloading from decision_proceduret + virtual void set_to(const exprt &expr, bool value); + virtual decision_proceduret::resultt dec_solve(); + virtual void print_assignment(std::ostream &out) const; + + virtual exprt get(const exprt &expr) const; + + // conversion + virtual literalt convert(const exprt &expr); + + // get literal for expression, if available + virtual bool literal(const exprt &expr, literalt &literal) const; + + bool use_cache; + bool equality_propagation; + + friend class prop_conv_store_constraintt; + + virtual void post_process(); + + virtual void clear_cache() + { + cache.clear(); + } + + typedef std::map symbolst; + typedef hash_map_cont cachet; + + const cachet &get_cache() const { return cache; } + const symbolst &get_symbols() const { return symbols; } + +protected: + // get a _boolean_ value from counterexample if not valid + virtual bool get_bool(const exprt &expr, tvt &value) const; + + virtual literalt convert_rest(const exprt &expr); + virtual literalt convert_bool(const exprt &expr); + + virtual bool set_equality_to_true(const exprt &expr); + + // symbols + symbolst symbols; + + virtual literalt get_literal(const irep_idt &symbol); + + // cache + cachet cache; + + virtual void ignoring(const exprt &expr); +}; + +#endif diff --git a/src/solvers/prop/prop_conv_store.cpp b/src/solvers/prop/prop_conv_store.cpp new file mode 100644 index 00000000000..755af383864 --- /dev/null +++ b/src/solvers/prop/prop_conv_store.cpp @@ -0,0 +1,155 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "prop_conv_store.h" + +/*******************************************************************\ + +Function: prop_conv_storet::set_to + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_conv_storet::set_to(const exprt &expr, bool value) +{ + constraintt &constraint=constraints.add_constraint(); + constraint.type=constraintt::SET_TO; + constraint.expr=expr; + constraint.value=value; +} + +/*******************************************************************\ + +Function: prop_conv_storet::convert_rest + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt prop_conv_storet::convert_rest(const exprt &expr) +{ + constraintt &constraint=constraints.add_constraint(); + constraint.type=constraintt::CONVERT_REST; + constraint.expr=expr; + constraint.literal=prop.new_variable(); + return constraint.literal; +} + +/*******************************************************************\ + +Function: prop_conv_storet::constraintst::replay + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_conv_storet::constraintst::replay(prop_convt &dest) const +{ + for(constraint_listt::const_iterator + it=constraint_list.begin(); + it!=constraint_list.end(); + it++) + it->replay(dest); +} + +/*******************************************************************\ + +Function: prop_conv_storet::constraintst::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_conv_storet::constraintst::print(std::ostream &out) const +{ + for(constraint_listt::const_iterator + it=constraint_list.begin(); + it!=constraint_list.end(); + it++) + it->print(out); +} + +/*******************************************************************\ + +Function: prop_conv_store_constraintt::replay + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_conv_store_constraintt::replay(prop_convt &dest) const +{ + switch(type) + { + case SET_TO: + dest.set_to(expr, value); + break; + + case CONVERT_REST: + dest.prop.set_equal(dest.convert_rest(expr), literal); + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: prop_conv_store_constraintt::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void prop_conv_store_constraintt::print(std::ostream &out) const +{ + switch(type) + { + case SET_TO: + out << "SET_TO " << (value?"TRUE":"FALSE") << ": "; + out << expr << std::endl; + break; + + case CONVERT_REST: + out << "CONVERT(" << literal.dimacs() << "): "; + out << expr << std::endl; + break; + + default: + assert(false); + } +} + diff --git a/src/solvers/prop/prop_conv_store.h b/src/solvers/prop/prop_conv_store.h new file mode 100644 index 00000000000..d5e6e9667b1 --- /dev/null +++ b/src/solvers/prop/prop_conv_store.h @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PROP_CONV_STORE_H +#define CPROVER_PROP_CONV_STORE_H + +#include "../sat/cnf_clause_list.h" +#include "prop_conv.h" + +class prop_conv_store_propt +{ +public: + cnf_clause_list_assignmentt prop_store; +}; + +struct prop_conv_store_constraintt +{ + typedef enum { NONE, CONVERT_REST, SET_TO } typet; + typet type; + + exprt expr; + + // for SET_TO + bool value; + + // for CONVERT_REST + literalt literal; + + void replay(prop_convt &dest) const; + void print(std::ostream &out) const; +}; + +class prop_conv_storet: + public prop_conv_store_propt, + public prop_convt +{ +public: + prop_conv_storet(const namespacet &_ns):prop_convt(_ns, prop_store) + { + } + + // overloading + virtual void set_to(const exprt &expr, bool value); + + typedef prop_conv_store_constraintt constraintt; + + class constraintst + { + public: + typedef std::list constraint_listt; + constraint_listt constraint_list; + + constraintt &add_constraint() + { + constraint_list.push_back(constraintt()); + return constraint_list.back(); + } + + void replay(prop_convt &dest) const; + void print(std::ostream &out) const; + }; + + const constraintst &get_constraints() const + { + return constraints; + } + +protected: + // overloading + virtual literalt convert_rest(const exprt &expr); + + constraintst constraints; +}; + +#endif diff --git a/src/solvers/prop/prop_wrapper.h b/src/solvers/prop/prop_wrapper.h new file mode 100644 index 00000000000..8e7647d4034 --- /dev/null +++ b/src/solvers/prop/prop_wrapper.h @@ -0,0 +1,88 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PROP_WRAPPER_H +#define CPROVER_PROP_WRAPPER_H + +#include "prop.h" + +class prop_wrappert:public virtual propt +{ + public: + prop_wrappert(propt &_prop):propt(_prop), p(_prop) { } + virtual ~prop_wrappert() { } + + virtual literalt constant(bool value) + { return p.constant(value); } + + // boolean operators + virtual literalt land(literalt a, literalt b) + { return p.land(a, b); } + + virtual literalt lor(literalt a, literalt b) + { return p.lor(a, b); } + + virtual literalt land(const bvt &bv) + { return p.land(bv); } + + virtual literalt lor(const bvt &bv) + { return p.lor(bv); } + + virtual literalt lnot(literalt a) + { return p.lnot(a); } + + virtual literalt lxor(literalt a, literalt b) + { return p.lxor(a, b); } + + virtual literalt lxor(const bvt &bv) + { return p.lxor(bv); } + + virtual literalt lnand(literalt a, literalt b) + { return p.lnand(a, b); } + + virtual literalt lnor(literalt a, literalt b) + { return p.lnor(a, b); } + + virtual literalt lequal(literalt a, literalt b) + { return p.lequal(a, b); } + + virtual literalt limplies(literalt a, literalt b) + { return p.limplies(a, b); } + + virtual literalt lselect(literalt a, literalt b, literalt c) // a?b:c + { return p.lselect(a, b, c); } + + // constraints + virtual void lcnf(const bvt &bv) + { p.lcnf(bv); } + + virtual void l_set_to(literalt a, bool value) + { p.l_set_to(a, value); } + + // literals + virtual literalt new_variable() + { return p.new_variable(); } + + virtual unsigned no_variables() const + { return p.no_variables(); } + + // solving + virtual const std::string solver_text() + { return p.solver_text(); } + + virtual tvt l_get(literalt a) const + { return p.l_get(a); } + + virtual resultt prop_solve() + { return p.prop_solve(); } + + protected: + propt &p; +}; + +#endif diff --git a/src/solvers/qbf/qbf_bdd_core.cpp b/src/solvers/qbf/qbf_bdd_core.cpp new file mode 100644 index 00000000000..9b5bc2e4171 --- /dev/null +++ b/src/solvers/qbf/qbf_bdd_core.cpp @@ -0,0 +1,610 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include + +#include + +#include +#include +#include +#include + +#include + +#include // CUDD Library + +// FIX FOR THE CUDD LIBRARY + +inline DdNode * +DD::getNode() const +{ + return node; + +} // DD::getNode + + +#include "qbf_bdd_core.h" + +/*******************************************************************\ + +Function: qbf_bdd_certificatet::qbf_bdd_certificatet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_bdd_certificatet::qbf_bdd_certificatet(void) : qdimacs_coret() +{ + bdd_manager=new Cudd(0,0); +} + +/*******************************************************************\ + +Function: qbf_bdd_certificatet::~qbf_bdd_certificatet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_bdd_certificatet::~qbf_bdd_certificatet(void) +{ + for(model_bddst::iterator it=model_bdds.begin(); + it!=model_bdds.end(); + it++) + { + if(*it!=NULL) { delete(*it); *it=NULL; } + } + model_bdds.clear(); + + delete(bdd_manager); + bdd_manager=NULL; +} + +/*******************************************************************\ + +Function: qbf_bdd_certificatet::new_variable + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt qbf_bdd_certificatet::new_variable(void) +{ + literalt l=qdimacs_coret::new_variable(); + + if(!is_quantified(l)) + add_quantifier(quantifiert::EXISTENTIAL, l); + + return l; +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::qbf_bdd_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_bdd_coret::qbf_bdd_coret() : qbf_bdd_certificatet() +{ + matrix=new BDD(); + *matrix=bdd_manager->bddOne(); +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::~qbf_bdd_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_bdd_coret::~qbf_bdd_coret() +{ + for(bdd_variable_mapt::iterator it=bdd_variable_map.begin(); + it!=bdd_variable_map.end(); + it++) + { + if (*it) { delete(*it); *it=NULL; } + } + bdd_variable_map.clear(); + + if(matrix) delete(matrix); + matrix=NULL; +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt qbf_bdd_coret::l_get(literalt a) const +{ + assert(false); + return tvt(false); +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string qbf_bdd_coret::solver_text() +{ + return "QBF/BDD/CORE"; +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt qbf_bdd_coret::prop_solve() +{ + { + std::string msg= + solver_text() + ": "+ + i2string(no_variables())+" variables, "+ + i2string(matrix->nodeCount())+" nodes"; + messaget::status(msg); + } + + model_bdds.resize(no_variables()+1, NULL); + + // Eliminate variables + for(quantifierst::const_reverse_iterator it=quantifiers.rbegin(); + it!=quantifiers.rend(); + it++) + { + unsigned var=it->var_no; + + if(it->type==quantifiert::EXISTENTIAL) + { + #if 0 + std::cout << "BDD E: " << var << ", " << + matrix->nodeCount() << " nodes" << std::endl; + #endif + + BDD* model = new BDD(); + const BDD &varbdd=*bdd_variable_map[var]; + *model = matrix->AndAbstract(varbdd.Xnor(bdd_manager->bddOne()), + varbdd); + model_bdds[var]=model; + + *matrix = matrix->ExistAbstract(*bdd_variable_map[var]); + } + else if(it->type==quantifiert::UNIVERSAL) + { + #if 0 + std::cout << "BDD A: " << var << ", " << + matrix->nodeCount() << " nodes" << std::endl; + #endif + + *matrix = matrix->UnivAbstract(*bdd_variable_map[var]); + } + else + throw ("Unquantified variable"); + } + + if(*matrix==bdd_manager->bddOne()) + { + compress_certificate(); + return P_SATISFIABLE; + } + else if(*matrix==bdd_manager->bddZero()) + { + model_bdds.clear(); + return P_UNSATISFIABLE; + } + else + return P_ERROR; +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::is_in_core + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool qbf_bdd_coret::is_in_core(literalt l) const +{ + throw ("NYI"); +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::m_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qdimacs_coret::modeltypet qbf_bdd_coret::m_get(literalt a) const +{ + throw ("NYI"); +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::new_variable + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt qbf_bdd_coret::new_variable() +{ + literalt res=qbf_bdd_certificatet::new_variable(); + + bdd_variable_map.resize(res.var_no()+1, NULL); + BDD &var=*(new BDD()); + var = bdd_manager->bddVar(); + bdd_variable_map[res.var_no()]=&var; + + return res; +} + +/*******************************************************************\ + +Function: qbf_bdd_coret::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_bdd_coret::lcnf(const bvt &bv) +{ + bvt new_bv; + + if(process_clause(bv, new_bv)) + return; + + BDD clause(bdd_manager->bddZero()); + + for(unsigned long i=0; itype==quantifiert::EXISTENTIAL) + { + const BDD &var=*bdd_variable_map[it->var_no]; + const BDD &model=*model_bdds[it->var_no]; + + if(model==bdd_manager->bddOne() || + model==bdd_manager->bddZero()) + { + for(quantifierst::const_iterator it2=it; + it2!=quantifiers.end(); + it2++) + { + BDD &model2=*model_bdds[it2->var_no]; + + if(model==bdd_manager->bddZero()) + model2=model2.AndAbstract(~var, var); + else + model2=model2.AndAbstract(var, var); + } + } + } + } +} + +/*******************************************************************\ + +Function: qbf_bdd_certificatet::f_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const exprt qbf_bdd_certificatet::f_get(literalt l) +{ + quantifiert q; + if(!find_quantifier(l, q)) + throw ("No model for unquantified variable"); + + // universal? + if(q.type==quantifiert::UNIVERSAL) + { + assert(l.var_no()!=0); + variable_mapt::const_iterator it=variable_map.find(l.var_no()); + + if(it==variable_map.end()) + throw "Variable map error"; + + const exprt &sym=it->second.first; + unsigned index=it->second.second; + + exprt extract_expr("extractbit", typet("bool")); + extract_expr.copy_to_operands(sym); + + typet uint_type("unsignedbv"); + uint_type.set("width", 32); + extract_expr.copy_to_operands(from_integer(index, uint_type)); + + if(l.sign()) extract_expr.negate(); + + return extract_expr; + } + else + { + // skolem functions for existentials + assert(q.type==quantifiert::EXISTENTIAL); + + function_cachet::const_iterator it=function_cache.find(l.var_no()); + if(it!=function_cache.end()) + { + #if 0 + std::cout << "CACHE HIT for " << l.dimacs() << std::endl; + #endif + + if(l.sign()) + return not_exprt(it->second); + else + return it->second; + } + + // no cached function, so construct one + + assert(model_bdds[l.var_no()]!=NULL); + BDD &model=*model_bdds[l.var_no()]; + + #if 0 + std::cout << "Model " << l.var_no() << std::endl; + model.PrintMinterm(); + #endif + + int* cube; + DdGen *generator; +// CUDD_VALUE_TYPE value; + + exprt result=or_exprt(); + + Cudd_ForeachPrime(bdd_manager->getManager(), + model.getNode(), model.getNode(), + generator, + cube) +// Cudd_ForeachCube(bdd_manager->getManager(), model.getNode(), generator, cube, value) + { + exprt prime=and_exprt(); + + #if 0 + std::cout << "CUBE: "; + for(signed i=0; iReadSize(); i++) + std::cout << cube[i]; + std::cout << std::endl; + #endif + + for(signed i=0; iReadSize(); i++) + { + if(quantifiers[i].var_no==l.var_no()) break; // is this sound? + + if(cube[i]!=2) + { + exprt subf=f_get(literalt(quantifiers[i].var_no, (cube[i]==1))); + prime.move_to_operands(subf); + } + } + + simplify_extractbits(prime); + + if(prime.operands().size()==0) + result.copy_to_operands(true_exprt()); + else if(prime.operands().size()==1) + result.move_to_operands(prime.op0()); + else + result.move_to_operands(prime); + } + + cube=NULL; // cube is free'd by nextCube + + exprt final; + + if(result.operands().size()==0) + final=false_exprt(); + else if(result.operands().size()==1) + final=result.op0(); + else final=result; + + function_cache[l.var_no()] = final; + + if(l.sign()) + return not_exprt(final); + else + return final; + } +} + +/*******************************************************************\ + +Function: qbf_bdd_certificatet::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt qbf_bdd_certificatet::l_get(literalt a) const +{ + const BDD &model=*model_bdds[a.var_no()]; + + if(model==bdd_manager->bddZero()) + return tvt(a.sign()?tvt::TV_TRUE:tvt::TV_FALSE); + else if(model==bdd_manager->bddOne()) + return tvt(a.sign()?tvt::TV_FALSE:tvt::TV_TRUE); + else + return tvt(tvt::TV_UNKNOWN); +} diff --git a/src/solvers/qbf/qbf_bdd_core.h b/src/solvers/qbf/qbf_bdd_core.h new file mode 100644 index 00000000000..a51db7228de --- /dev/null +++ b/src/solvers/qbf/qbf_bdd_core.h @@ -0,0 +1,71 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#ifndef CPROVER_QBF_BDD_CORE_H +#define CPROVER_QBF_BDD_CORE_H + +#include +#include + +#include "qdimacs_core.h" + +class Cudd; +class BDD; + +class qbf_bdd_certificatet : public qdimacs_coret +{ +protected: + Cudd* bdd_manager; + + typedef std::vector model_bddst; + model_bddst model_bdds; + + typedef hash_map_cont function_cachet; + function_cachet function_cache; + +public: + qbf_bdd_certificatet(void); + virtual ~qbf_bdd_certificatet(void); + + virtual literalt new_variable(void); + + virtual tvt l_get(literalt a) const; + virtual const exprt f_get(literalt l); +}; + + +class qbf_bdd_coret:public qbf_bdd_certificatet +{ +private: + typedef std::vector bdd_variable_mapt; + bdd_variable_mapt bdd_variable_map; + + BDD *matrix; + +public: + qbf_bdd_coret(); + virtual ~qbf_bdd_coret(); + + virtual literalt new_variable(); + + virtual void lcnf(const bvt &bv); + virtual literalt lor(literalt a, literalt b); + virtual literalt lor(const bvt &bv); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + + virtual bool is_in_core(literalt l) const; + virtual modeltypet m_get(literalt a) const; + +protected: + void compress_certificate(void); +}; + +#endif /* QBF_BDD_CORE_H_ */ diff --git a/src/solvers/qbf/qbf_core.h b/src/solvers/qbf/qbf_core.h new file mode 100644 index 00000000000..7fbe949f7f7 --- /dev/null +++ b/src/solvers/qbf/qbf_core.h @@ -0,0 +1,43 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com, + CM Wintersteiger + +\*******************************************************************/ + +#ifndef CPROVER_QBFCHECK_CORE_H +#define CPROVER_QBFCHECK_CORE_H + +#ifdef HAVE_QBF_CORE +#define QBF_CORE_SKIZZO +#else +#define QBF_CORE_NONE +#endif + +#ifdef QBF_CORE_SQUOLEM + +#include "qbf_squolem_core.h" +typedef qbf_squolem_coret qbf_coret; + +#else +#ifdef QBF_CORE_SKIZZO + +#include "qbf_skizzo_core.h" +typedef qbf_skizzo_coret qbf_coret; + +#else +#ifdef QBF_CORE_BDD + +#include "qbf_bdd_core.h" +typedef qbf_bdd_coret qbf_coret; + +#else + +#error NO QBF SOLVER WITH CORE EXTRACTOR +#endif +#endif +#endif + +#endif diff --git a/src/solvers/qbf/qbf_quantor.cpp b/src/solvers/qbf/qbf_quantor.cpp new file mode 100644 index 00000000000..7acdac6e103 --- /dev/null +++ b/src/solvers/qbf/qbf_quantor.cpp @@ -0,0 +1,176 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include +#include + +#include "qbf_quantor.h" + +/*******************************************************************\ + +Function: qbf_quantort::qbf_quantort + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_quantort::qbf_quantort() +{ +} + +/*******************************************************************\ + +Function: qbf_quantort::~qbf_quantort + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_quantort::~qbf_quantort() +{ +} + +/*******************************************************************\ + +Function: qbf_quantort::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt qbf_quantort::l_get(literalt a) const +{ + assert(false); + return tvt(tvt::TV_UNKNOWN); +} + +/*******************************************************************\ + +Function: qbf_quantort::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string qbf_quantort::solver_text() +{ + return "Quantor"; +} + +/*******************************************************************\ + +Function: qbf_quantort::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt qbf_quantort::prop_solve() +{ + { + std::string msg= + "Quantor: "+ + i2string(no_variables())+" variables, "+ + i2string(no_clauses())+" clauses"; + messaget::status(msg); + } + + std::string qbf_tmp_file="quantor.qdimacs"; + std::string result_tmp_file="quantor.out"; + + { + std::ofstream out(qbf_tmp_file.c_str()); + + // write it + write_qdimacs_cnf(out); + } + + //std::string options=" --equivalences=0"; + std::string options=""; + + // solve it + system(("quantor "+qbf_tmp_file+ + options+ + " -o "+result_tmp_file).c_str()); + + bool result=false; + + // read result + { + std::ifstream in(result_tmp_file.c_str()); + + bool result_found=false; + while(in) + { + std::string line; + + str_getline(in, line); + + if(line!="" && line[line.size()-1]=='\r') + line.resize(line.size()-1); + + if(line=="s TRUE") + { + result=true; + result_found=true; + break; + } + else if(line=="s FALSE") + { + result=false; + result_found=true; + break; + } + } + + if(!result_found) + { + messaget::error("Quantor failed: unknown result"); + return P_ERROR; + } + } + + if(result) + { + messaget::status("Quantor: TRUE"); + return P_SATISFIABLE; + } + else + { + messaget::status("Quantor: FALSE"); + return P_UNSATISFIABLE; + } + + return P_ERROR; +} + diff --git a/src/solvers/qbf/qbf_quantor.h b/src/solvers/qbf/qbf_quantor.h new file mode 100644 index 00000000000..bc418f84071 --- /dev/null +++ b/src/solvers/qbf/qbf_quantor.h @@ -0,0 +1,25 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_QBF_QUANTOR_H +#define CPROVER_QBF_QUANTOR_H + +#include "qdimacs_cnf.h" + +class qbf_quantort:public qdimacs_cnft +{ +public: + qbf_quantort(); + virtual ~qbf_quantort(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; +}; + +#endif diff --git a/src/solvers/qbf/qbf_qube.cpp b/src/solvers/qbf/qbf_qube.cpp new file mode 100644 index 00000000000..5ef4e6d2cb8 --- /dev/null +++ b/src/solvers/qbf/qbf_qube.cpp @@ -0,0 +1,182 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include +#include + +#include + +#include +#include + +#include "qbf_qube.h" + +/*******************************************************************\ + +Function: qbf_qubet::qbf_qubet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_qubet::qbf_qubet() +{ + // skizzo crashes on broken lines + break_lines=false; +} + +/*******************************************************************\ + +Function: qbf_qubet::~qbf_qubet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_qubet::~qbf_qubet() +{ +} + +/*******************************************************************\ + +Function: qbf_qubet::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt qbf_qubet::l_get(literalt a) const +{ + assert(false); + return tvt(false); +} + +/*******************************************************************\ + +Function: qbf_qubet::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string qbf_qubet::solver_text() +{ + return "QuBE"; +} + +/*******************************************************************\ + +Function: qbf_qubet::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt qbf_qubet::prop_solve() +{ + // sKizzo crashes on empty instances + if(no_clauses()==0) + return P_SATISFIABLE; + + { + std::string msg= + "QuBE: "+ + i2string(no_variables())+" variables, "+ + i2string(no_clauses())+" clauses"; + messaget::status(msg); + } + + std::string qbf_tmp_file="qube.qdimacs"; + std::string result_tmp_file="qube.out"; + + { + std::ofstream out(qbf_tmp_file.c_str()); + + // write it + write_qdimacs_cnf(out); + } + + //std::string options=" --equivalences=0"; + std::string options=""; + + // solve it + system(("QuBE "+qbf_tmp_file+ + options+ + " > "+result_tmp_file).c_str()); + + bool result=false; + + // read result + { + std::ifstream in(result_tmp_file.c_str()); + + bool result_found=false; + while(in) + { + std::string line; + + str_getline(in, line); + + if(line!="" && line[line.size()-1]=='\r') + line.resize(line.size()-1); + + if(line=="s cnf 0") + { + result=true; + result_found=true; + break; + } + else if(line=="s cnf 1") + { + result=false; + result_found=true; + break; + } + } + + if(!result_found) + { + messaget::error("QuBE failed: unknown result"); + return P_ERROR; + } + } + + if(result) + { + messaget::status("QuBE: TRUE"); + return P_SATISFIABLE; + } + else + { + messaget::status("QuBE: FALSE"); + return P_UNSATISFIABLE; + } + + return P_ERROR; +} + diff --git a/src/solvers/qbf/qbf_qube.h b/src/solvers/qbf/qbf_qube.h new file mode 100644 index 00000000000..bed1b1b583a --- /dev/null +++ b/src/solvers/qbf/qbf_qube.h @@ -0,0 +1,25 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#ifndef CPROVER_QBF_QUBE_H +#define CPROVER_QBF_QUBE_H + +#include "qdimacs_cnf.h" + +class qbf_qubet:public qdimacs_cnft +{ +public: + qbf_qubet(); + virtual ~qbf_qubet(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; +}; + +#endif diff --git a/src/solvers/qbf/qbf_qube_core.cpp b/src/solvers/qbf/qbf_qube_core.cpp new file mode 100644 index 00000000000..babfa808efc --- /dev/null +++ b/src/solvers/qbf/qbf_qube_core.cpp @@ -0,0 +1,204 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include +#include + +#include + +#include +#include +#include + +#include "qbf_qube_core.h" + +/*******************************************************************\ + +Function: qbf_qube_coret::qbf_qube_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_qube_coret::qbf_qube_coret() : qdimacs_coret() +{ + break_lines=false; + qbf_tmp_file="qube.qdimacs"; +} + +/*******************************************************************\ + +Function: qbf_qube_coret::~qbf_qube_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_qube_coret::~qbf_qube_coret() +{ +} + +/*******************************************************************\ + +Function: qbf_qube_coret::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string qbf_qube_coret::solver_text() +{ + return "QuBE w/ toplevel assignments"; +} + +/*******************************************************************\ + +Function: qbf_qube_coret::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt qbf_qube_coret::prop_solve() +{ + if(no_clauses()==0) + return P_SATISFIABLE; + + { + std::string msg= + "QuBE: "+ + i2string(no_variables())+" variables, "+ + i2string(no_clauses())+" clauses"; + messaget::status(msg); + } + + std::string result_tmp_file="qube.out"; + + { + std::ofstream out(qbf_tmp_file.c_str()); + + // write it + break_lines=false; + write_qdimacs_cnf(out); + } + + std::string options=""; + + // solve it + system(("QuBE " + options + " " + qbf_tmp_file + + " > "+result_tmp_file).c_str()); + + bool result=false; + + // read result + { + std::ifstream in(result_tmp_file.c_str()); + + bool result_found=false; + while(in) + { + std::string line; + + str_getline(in, line); + + if(line!="" && line[line.size()-1]=='\r') + line.resize(line.size()-1); + + if(line[0]=='V') + { + mp_integer b(line.substr(2).c_str()); + if(b<0) + assignment[b.negate().to_ulong()] = false; + else + assignment[b.to_ulong()] = true; + } + else if(line=="s cnf 1") + { + result=true; + result_found=true; + break; + } + else if(line=="s cnf 0") + { + result=false; + result_found=true; + break; + } + } + + if(!result_found) + { + messaget::error("QuBE failed: unknown result"); + return P_ERROR; + } + } + + remove(result_tmp_file.c_str()); + remove(qbf_tmp_file.c_str()); + + if(result) + { + messaget::status("QuBE: TRUE"); + return P_SATISFIABLE; + } + else + { + messaget::status("QuBE: FALSE"); + return P_UNSATISFIABLE; + } +} + +/*******************************************************************\ + +Function: qbf_qube_coret::is_in_core + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool qbf_qube_coret::is_in_core(literalt l) const +{ + throw ("Not suppported"); +} + +/*******************************************************************\ + +Function: qbf_qube_coret::m_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qdimacs_coret::modeltypet qbf_qube_coret::m_get(literalt a) const +{ + throw ("not supported"); +} diff --git a/src/solvers/qbf/qbf_qube_core.h b/src/solvers/qbf/qbf_qube_core.h new file mode 100644 index 00000000000..f282223bcee --- /dev/null +++ b/src/solvers/qbf/qbf_qube_core.h @@ -0,0 +1,57 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#ifndef CPROVER_QBF_QUBE_CORE_H +#define CPROVER_QBF_QUBE_CORE_H + +#include "qdimacs_core.h" + +class qbf_qube_coret : public qdimacs_coret +{ +protected: + std::string qbf_tmp_file; + + typedef std::map assignmentt; + assignmentt assignment; + +public: + qbf_qube_coret(); + virtual ~qbf_qube_coret(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + + virtual bool is_in_core(literalt l) const; + + virtual tvt l_get(literalt a) const + { + unsigned v=a.var_no(); + + assignmentt::const_iterator fit = assignment.find(v); + + if(fit!=assignment.end()) + return a.sign()?tvt(!fit->second) : tvt(fit->second); + else + { + // throw "Missing toplevel assignment from QuBE"; + // We assume this is a don't-care and return unknown + } + + + return tvt(tvt::TV_UNKNOWN); + } + + virtual modeltypet m_get(literalt a) const; + + virtual const exprt f_get(literalt l) + { + throw "Qube does not support full certificates."; + } +}; + +#endif diff --git a/src/solvers/qbf/qbf_skizzo.cpp b/src/solvers/qbf/qbf_skizzo.cpp new file mode 100644 index 00000000000..96a3fe1258d --- /dev/null +++ b/src/solvers/qbf/qbf_skizzo.cpp @@ -0,0 +1,182 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include +#include + +#include "qbf_skizzo.h" + +/*******************************************************************\ + +Function: qbf_skizzot::qbf_skizzot + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_skizzot::qbf_skizzot() +{ + // skizzo crashes on broken lines + break_lines=false; +} + +/*******************************************************************\ + +Function: qbf_skizzot::~qbf_skizzot + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_skizzot::~qbf_skizzot() +{ +} + +/*******************************************************************\ + +Function: qbf_skizzot::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt qbf_skizzot::l_get(literalt a) const +{ + assert(false); + return tvt(false); +} + +/*******************************************************************\ + +Function: qbf_skizzot::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string qbf_skizzot::solver_text() +{ + return "Skizzo"; +} + +/*******************************************************************\ + +Function: qbf_skizzot::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt qbf_skizzot::prop_solve() +{ + // sKizzo crashes on empty instances + if(no_clauses()==0) + return P_SATISFIABLE; + + { + std::string msg= + "Skizzo: "+ + i2string(no_variables())+" variables, "+ + i2string(no_clauses())+" clauses"; + messaget::status(msg); + } + + std::string qbf_tmp_file="sKizzo.qdimacs"; + std::string result_tmp_file="sKizzo.out"; + + { + std::ofstream out(qbf_tmp_file.c_str()); + + // write it + write_qdimacs_cnf(out); + } + + //std::string options=" --equivalences=0"; + std::string options=""; + + // solve it + system(("sKizzo "+qbf_tmp_file+ + options+ + " > "+result_tmp_file).c_str()); + + bool result=false; + + // read result + { + std::ifstream in(result_tmp_file.c_str()); + + bool result_found=false; + while(in) + { + std::string line; + + str_getline(in, line); + + if(line!="" && line[line.size()-1]=='\r') + line.resize(line.size()-1); + + if(line=="The instance evaluates to TRUE.") + { + result=true; + result_found=true; + break; + } + else if(line=="The instance evaluates to FALSE.") + { + result=false; + result_found=true; + break; + } + } + + if(!result_found) + { + messaget::error("Skizzo failed: unknown result"); + return P_ERROR; + } + } + + if(result) + { + messaget::status("Skizzo: TRUE"); + return P_SATISFIABLE; + } + else + { + messaget::status("Skizzo: FALSE"); + return P_UNSATISFIABLE; + } + + return P_ERROR; +} + diff --git a/src/solvers/qbf/qbf_skizzo.h b/src/solvers/qbf/qbf_skizzo.h new file mode 100644 index 00000000000..06a109bb9b7 --- /dev/null +++ b/src/solvers/qbf/qbf_skizzo.h @@ -0,0 +1,25 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_QBF_SKIZZO_H +#define CPROVER_QBF_SKIZZO_H + +#include "qdimacs_cnf.h" + +class qbf_skizzot:public qdimacs_cnft +{ +public: + qbf_skizzot(); + virtual ~qbf_skizzot(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; +}; + +#endif diff --git a/src/solvers/qbf/qbf_skizzo_core.cpp b/src/solvers/qbf/qbf_skizzo_core.cpp new file mode 100644 index 00000000000..d368874fcf5 --- /dev/null +++ b/src/solvers/qbf/qbf_skizzo_core.cpp @@ -0,0 +1,378 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include + +#include + +#include +#include + +#include // CUDD Library + +// FIX FOR THE CUDD LIBRARY + +inline DdNode * +DD::getNode() const +{ + return node; + +} // DD::getNode + +#include + +#include "qbf_skizzo_core.h" + +/*******************************************************************\ + +Function: qbf_skizzo_coret::qbf_skizzo_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_skizzo_coret::qbf_skizzo_coret() : qbf_bdd_certificatet() +{ + // skizzo crashes on broken lines + break_lines=false; + qbf_tmp_file="sKizzo.qdimacs"; +} + +/*******************************************************************\ + +Function: qbf_skizzo_coret::~qbf_skizzo_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_skizzo_coret::~qbf_skizzo_coret() +{ +} + +/*******************************************************************\ + +Function: qbf_skizzo_coret::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string qbf_skizzo_coret::solver_text() +{ + return "Skizzo/Core"; +} + +/*******************************************************************\ + +Function: qbf_skizzo_coret::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt qbf_skizzo_coret::prop_solve() +{ + // sKizzo crashes on empty instances + if(no_clauses()==0) + return P_SATISFIABLE; + + { + std::string msg= + "Skizzo: "+ + i2string(no_variables())+" variables, "+ + i2string(no_clauses())+" clauses"; + messaget::status(msg); + } + + std::string result_tmp_file="sKizzo.out"; + + { + std::ofstream out(qbf_tmp_file.c_str()); + + // write it + break_lines=false; + write_qdimacs_cnf(out); + } + + std::string options=""; + + // solve it + system(("sKizzo -log "+qbf_tmp_file+ + options+ + " > "+result_tmp_file).c_str()); + + bool result=false; + + // read result + { + std::ifstream in(result_tmp_file.c_str()); + + bool result_found=false; + while(in) + { + std::string line; + + str_getline(in, line); + + if(line!="" && line[line.size()-1]=='\r') + line.resize(line.size()-1); + + if(line=="The instance evaluates to TRUE.") + { + result=true; + result_found=true; + break; + } + else if(line=="The instance evaluates to FALSE.") + { + result=false; + result_found=true; + break; + } + } + + if(!result_found) + { + messaget::error("Skizzo failed: unknown result"); + return P_ERROR; + } + } + + remove(result_tmp_file.c_str()); + remove(qbf_tmp_file.c_str()); + + if(result) + { + messaget::status("Skizzo: TRUE"); + + if(get_certificate()) + return P_ERROR; + + return P_SATISFIABLE; + } + else + { + messaget::status("Skizzo: FALSE"); + return P_UNSATISFIABLE; + } +} + +/*******************************************************************\ + +Function: qbf_skizzo_coret::is_in_core + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool qbf_skizzo_coret::is_in_core(literalt l) const +{ + throw ("NYI"); +} + +/*******************************************************************\ + +Function: qbf_skizzo_coret::m_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qdimacs_coret::modeltypet qbf_skizzo_coret::m_get(literalt a) const +{ + throw ("NYI"); +} + +/*******************************************************************\ + +Function: qbf_skizzo_coret::get_certificate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool qbf_skizzo_coret::get_certificate(void) +{ + std::string result_tmp_file="ozziKs.out"; + std::string options="-dump qbm=bdd"; + std::string log_file = qbf_tmp_file + ".sKizzo.log"; + + system(("ozziKs " + options + " " + log_file + + " > "+result_tmp_file).c_str()); + + // read result + bool result=false; + { + std::ifstream in(result_tmp_file.c_str()); + std::string key=" [OK, VALID,"; + + while(in) + { + std::string line; + + str_getline(in, line); + + if(line!="" && line[line.size()-1]=='\r') + line.resize(line.size()-1); + + if(line.compare(0, key.size(), key)==0) + { + result=true; + break; + } + } + } + + if(!result) + { + messaget::error("Skizzo failed: unknown result"); + return true; + } + + remove(result_tmp_file.c_str()); + remove(log_file.c_str()); + + // certificate reconstruction done, now let's load it from the .qbm file + + int n_e; + std::vector e_list; + int e_max=0; + + // check header + result=false; + { + std::ifstream in((qbf_tmp_file+".qbm").c_str()); + std::string key="# existentials["; + + std::string line; + str_getline(in, line); + + assert(line=="# QBM file, 1.3"); + + while(in) + { + str_getline(in, line); + + if(line!="" && line[line.size()-1]=='\r') + line.resize(line.size()-1); + + if(line.compare(0, key.size(), key)==0) + { + result=true; + break; + } + } + + size_t ob=line.find('['); + std::string n_es=line.substr(ob+1, line.find(']')-ob-1); + n_e=atoi(n_es.c_str()); + assert(n_e!=0); + + e_list.resize(n_e); + std::string e_lists=line.substr(line.find(':')+2); + + for(int i=0; ie_max) e_max=cur; + + e_lists = e_lists.substr(space+1); + } + + if(!result) + throw ("Existential mapping from sKizzo missing"); + + in.close(); + + // workaround for long comments + system(("sed -e \"s/^#.*$/# no comment/\" -i "+qbf_tmp_file+".qbm").c_str()); + } + + + { + DdNode **bdds; + std::string bdd_file=qbf_tmp_file+".qbm"; + + // dddmp insists on a non-const string here... + char filename[bdd_file.size()+1]; + strcpy(filename, bdd_file.c_str()); + + bdd_manager->AutodynEnable(CUDD_REORDER_SIFT); + + int nroots = + Dddmp_cuddBddArrayLoad(bdd_manager->getManager(), + DDDMP_ROOT_MATCHLIST, NULL, + DDDMP_VAR_MATCHIDS, NULL, NULL, NULL, + DDDMP_MODE_DEFAULT, + filename, + NULL, + &bdds); + + assert(nroots=2*n_e); // ozziKs documentation guarantees that. + + model_bdds.resize(e_max+1, NULL); + + for(unsigned i=0; i + +#include "qbf_squolem.h" + +/*******************************************************************\ + +Function: qbf_squolemt::qbf_squolemt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_squolemt::qbf_squolemt() : early_decision(false) +{ +} + +/*******************************************************************\ + +Function: qbf_squolemt::~qbf_squolemt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_squolemt::~qbf_squolemt() +{ + squolem.reset(); +} + +/*******************************************************************\ + +Function: qbf_squolemt::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt qbf_squolemt::l_get(literalt a) const +{ + assert(false); +} + +/*******************************************************************\ + +Function: qbf_squolemt::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string qbf_squolemt::solver_text() +{ + return "Squolem"; +} + +/*******************************************************************\ + +Function: qbf_squolemt::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt qbf_squolemt::prop_solve() +{ + { + std::string msg= + "Squolem: "+ + i2string(no_variables())+" variables, "+ + i2string(no_clauses())+" clauses"; + messaget::status(msg); + } + + if(early_decision) return P_UNSATISFIABLE; + +// squolem.options.set_showStatus(true); + squolem.options.set_freeOnExit(true); +// squolem.options.set_useExpansion(true); +// squolem.options.set_predictOnLiteralBound(true); + squolem.options.set_debugFilename("debug.qdimacs"); + squolem.options.set_saveDebugFile(true); + + squolem.endOfOriginals(); + bool result = squolem.decide(); + + if(result) + { + messaget::status("Squolem: VALID"); + return P_SATISFIABLE; + } + else + { + messaget::status("Squolem: INVALID"); + return P_UNSATISFIABLE; + } + + return P_ERROR; +} + +/*******************************************************************\ + +Function: qbf_squolemt::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolemt::lcnf(const bvt &bv) +{ + if(early_decision) return; // we don't need no more... + + bvt new_bv; + + if(process_clause(bv, new_bv)) + return; + + if(new_bv.size()==0) + { + early_decision=true; + return; + } + + std::vector buffer(new_bv.size()); + unsigned long i=0; + do + { + buffer[i]=new_bv[i].dimacs(); + i++; + } + while (i + +#include "qdimacs_cnf.h" + +class qbf_squolemt:public qdimacs_cnft +{ +protected: + Squolem2 squolem; + bool early_decision; + +public: + qbf_squolemt(); + virtual ~qbf_squolemt(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + + virtual void lcnf(const bvt &bv); + virtual void add_quantifier(const quantifiert &quantifier); + virtual void set_quantifier(const quantifiert::typet type, const literalt l); + virtual void set_no_variables(unsigned no); + virtual unsigned no_clauses() const { return squolem.clauses(); } +}; + +#endif /*_CPROVER_QBF_SQUOLEM_H_*/ diff --git a/src/solvers/qbf/qbf_squolem_core.cpp b/src/solvers/qbf/qbf_squolem_core.cpp new file mode 100644 index 00000000000..14d21b8408f --- /dev/null +++ b/src/solvers/qbf/qbf_squolem_core.cpp @@ -0,0 +1,542 @@ +/*******************************************************************\ + +Module: Squolem Backend (with proofs) + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include // uint type for indices + +#include "qbf_squolem_core.h" + +/*******************************************************************\ + +Function: qbf_squolem_coret::qbf_squolem_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_squolem_coret::qbf_squolem_coret() : squolem(NULL) +{ + setup(); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::setup + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolem_coret::setup(void) +{ + quantifiers.clear(); + clauses.clear(); + early_decision=false; + variable_map.clear(); + squolem=new Squolem2(); + +// squolem->options.set_extractCoreVariables(true); +// squolem->options.set_saveCertificate(true); + squolem->options.set_keepModelFunctions(true); + squolem->options.set_keepResolutionProof(false); + squolem->options.set_freeOnExit(true); +// squolem->options.set_useExpansion(true); + squolem->options.set_debugFilename("debug.qdimacs"); + squolem->options.set_saveDebugFile(true); + squolem->options.set_compressModel(true); +// squolem->options.set_showStatus(true); +// squolem->options.set_predictOnLiteralBound(true); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::reset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolem_coret::reset(void) +{ + squolem->reset(); + delete(squolem); + squolem=NULL; + setup(); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::~qbf_squolem_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_squolem_coret::~qbf_squolem_coret() +{ + squolem->reset(); + delete(squolem); + squolem=NULL; +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt qbf_squolem_coret::l_get(literalt a) const +{ + if(a.is_true()) + return tvt(tvt::TV_TRUE); + else if(a.is_false()) + return tvt(tvt::TV_FALSE); + else if(squolem->modelIsTrue(a.var_no())) + return tvt(tvt::TV_TRUE); + else if(squolem->modelIsFalse(a.var_no()) || + squolem->modelIsDontCare(a.var_no())) + return tvt(tvt::TV_FALSE); + else + return tvt(tvt::TV_UNKNOWN); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string qbf_squolem_coret::solver_text() +{ + return "Squolem (Certifying)"; +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt qbf_squolem_coret::prop_solve() +{ + { + std::string msg= + "Squolem: "+ + i2string(no_variables())+" variables, "+ + i2string(no_clauses())+" clauses"; + messaget::status(msg); + } + + squolem->endOfOriginals(); + bool result = squolem->decide(); + + if(result) + { + messaget::status("Squolem: VALID"); + return P_SATISFIABLE; + } + else + { + messaget::status("Squolem: INVALID"); + return P_UNSATISFIABLE; + } + + return P_ERROR; +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::is_in_core + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool qbf_squolem_coret::is_in_core(literalt l) const +{ + return squolem->inCore(l.var_no()); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +qbf_squolem_coret::modeltypet qbf_squolem_coret::m_get(literalt a) const +{ + if(squolem->modelIsTrue(a.var_no())) + return M_TRUE; + else if(squolem->modelIsFalse(a.var_no())) + return M_FALSE; + else if(squolem->modelIsComplex(a.var_no())) + return M_COMPLEX; + else + return M_DONTCARE; +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolem_coret::lcnf(const bvt &bv) +{ + if(early_decision) return; // we don't need no more... + + bvt new_bv; + + if(process_clause(bv, new_bv)) + return; + + if(new_bv.size()==0) + { + early_decision=true; + return; + } + + std::vector buffer(new_bv.size()); + unsigned long i=0; + do + { + buffer[i]=new_bv[i].dimacs(); + i++; + } + while (iaddClause(buffer)) + early_decision=true; +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::add_quantifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolem_coret::add_quantifier(const quantifiert &quantifier) +{ + squolem->quantifyVariableInner(quantifier.var_no, + quantifier.type==quantifiert::UNIVERSAL); + + qdimacs_cnft::add_quantifier(quantifier); // necessary? +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::set_no_variables + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolem_coret::set_no_variables(unsigned no) +{ + squolem->setLastVariable(no+1); + cnft::set_no_variables(no); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::set_quantifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolem_coret::set_quantifier( + const quantifiert::typet type, + const literalt l) +{ + qdimacs_cnft::set_quantifier(type, l); + squolem->requantifyVariable(l.var_no(), type==quantifiert::UNIVERSAL); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::set_debug_filename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolem_coret::set_debug_filename(const std::string &str) +{ + squolem->options.set_debugFilename(str.c_str()); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::write_qdimacs_cnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qbf_squolem_coret::write_qdimacs_cnf(std::ostream &out) +{ + squolem->saveQCNF(out); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::f_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const exprt qbf_squolem_coret::f_get(literalt l) +{ + if(squolem->isUniversal(l.var_no())) + { + assert(l.var_no()!=0); + variable_mapt::const_iterator it=variable_map.find(l.var_no()); + + if(it==variable_map.end()) + throw "Variable map error"; + + const exprt &sym=it->second.first; + unsigned index=it->second.second; + + exprt extract_expr("extractbit", typet("bool")); + extract_expr.copy_to_operands(sym); + typet uint_type("unsignedbv"); + uint_type.set("width", 32); + extract_expr.copy_to_operands(from_integer(index, uint_type)); + + if(l.sign()) extract_expr.negate(); + + return extract_expr; + } + + function_cachet::const_iterator it=function_cache.find(l.var_no()); + if(it!=function_cache.end()) + { + #if 0 + std::cout << "CACHE HIT for " << l.dimacs() << std::endl; + #endif + + if(l.sign()) + return not_exprt(it->second); + else + return it->second; + } + else + { + WitnessStack *wsp = squolem->getModelFunction(Literal(l.dimacs())); + exprt res; + + if(wsp==NULL || wsp->size()==0) + { +// res=exprt("nondet_bool", typet("bool")); + res=false_exprt(); // just set it to zero + } + else if(wsp->pSize<=wsp->nSize) + res=f_get_cnf(wsp); + else + res=f_get_dnf(wsp); + + function_cache[l.var_no()] = res; + + if(l.sign()) + return not_exprt(res); + else + return res; + } +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::f_get_cnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const exprt qbf_squolem_coret::f_get_cnf(WitnessStack *wsp) +{ + Clause *p=wsp->negWits; + + if(!p) return exprt("true", typet("bool")); + + exprt::operandst operands; + + while(p!=NULL) + { + exprt clause=or_exprt(); + + for(unsigned i=0; isize; i++) + { + const Literal &lit=p->literals[i]; + exprt subf = f_get(literalt(var(lit), isPositive(lit))); // negated! + if(find(clause.operands().begin(), clause.operands().end(), subf)== + clause.operands().end()) + clause.move_to_operands(subf); + } + + if(clause.operands().size()==0) + clause=false_exprt(); + else if(clause.operands().size()==1) + { + const exprt tmp=clause.op0(); + clause=tmp; + } + + #if 0 + std::cout << "CLAUSE: " << clause << std::endl; + #endif + + operands.push_back(clause); + + p=p->next; + } + + return and_exprt(operands); +} + +/*******************************************************************\ + +Function: qbf_squolem_coret::f_get_dnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const exprt qbf_squolem_coret::f_get_dnf(WitnessStack *wsp) +{ + Clause *p=wsp->posWits; + + if(!p) return exprt("false", typet("bool")); + + exprt::operandst operands; + + while(p!=NULL) + { + exprt cube=and_exprt(); + + for(unsigned i=0; isize; i++) + { + const Literal &lit=p->literals[i]; + exprt subf = f_get(literalt(var(lit), !isPositive(lit))); + if(find(cube.operands().begin(), cube.operands().end(), subf)== + cube.operands().end()) + cube.move_to_operands(subf); + + simplify_extractbits(cube); + } + + if(cube.operands().size()==0) + cube=true_exprt(); + else if(cube.operands().size()==1) + { + const exprt tmp=cube.op0(); + cube=tmp; + } + + #if 0 + std::cout << "CUBE: " << cube << std::endl; + #endif + + operands.push_back(cube); + + p=p->next; + } + + return or_exprt(operands); +} diff --git a/src/solvers/qbf/qbf_squolem_core.h b/src/solvers/qbf/qbf_squolem_core.h new file mode 100644 index 00000000000..39d1be8a768 --- /dev/null +++ b/src/solvers/qbf/qbf_squolem_core.h @@ -0,0 +1,59 @@ +/*******************************************************************\ + +Module: Squolem Backend (with Proofs) + +Author: CM Wintersteiger + +\*******************************************************************/ + +#ifndef CPROVER_QBF_SQUOLEM_CORE_H +#define CPROVER_QBF_SQUOLEM_CORE_H + +#include +#include + +#include "qdimacs_core.h" + +class qbf_squolem_coret:public qdimacs_coret +{ +protected: + Squolem2* squolem; + bool early_decision; + +public: + qbf_squolem_coret(); + virtual ~qbf_squolem_coret(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + + virtual tvt l_get(literalt a) const; + virtual bool is_in_core(literalt l) const; + + void set_debug_filename(const std::string &str); + + virtual void lcnf(const bvt &bv); + virtual void add_quantifier(const quantifiert &quantifier); + virtual void set_quantifier(const quantifiert::typet type, const literalt l); + virtual void set_no_variables(unsigned no); + virtual unsigned no_clauses() const { return squolem->clauses(); } + + virtual modeltypet m_get(literalt a) const; + + virtual void write_qdimacs_cnf(std::ostream &out); + + void reset(void); + + virtual const exprt f_get(literalt l); + +private: + typedef hash_map_cont function_cachet; + function_cachet function_cache; + + const exprt f_get_cnf(WitnessStack *wsp); + const exprt f_get_dnf(WitnessStack *wsp); + + void setup(void); +}; + +#endif /*_CPROVER_QBF_SQUOLEM_CORE_H_*/ diff --git a/src/solvers/qbf/qdimacs_cnf.cpp b/src/solvers/qbf/qdimacs_cnf.cpp new file mode 100644 index 00000000000..a67c41d3a85 --- /dev/null +++ b/src/solvers/qbf/qdimacs_cnf.cpp @@ -0,0 +1,236 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "qdimacs_cnf.h" + +/*******************************************************************\ + +Function: qdimacs_cnft::write_qdimacs_cnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qdimacs_cnft::write_qdimacs_cnf(std::ostream &out) +{ + write_problem_line(out); + write_prefix(out); + write_clauses(out); +} + +/*******************************************************************\ + +Function: qdimacs_cnft::write_prefix + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qdimacs_cnft::write_prefix(std::ostream &out) const +{ + std::vector quantified; + + quantified.resize(no_variables(), false); + + for(quantifierst::const_iterator + it=quantifiers.begin(); + it!=quantifiers.end(); + it++) + { + const quantifiert &quantifier=*it; + + assert(quantifier.var_novar_no==l.var_no()) + { + it->type=type; + return; + } + + // variable not found - let's add a new quantifier. + add_quantifier(type, l); +} + +/*******************************************************************\ + +Function: qdimacs_cnft::copy_to + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qdimacs_cnft::copy_to(qdimacs_cnft &cnf) const +{ + cnf.set_no_variables(_no_variables); + + for(quantifierst::const_iterator + it=quantifiers.begin(); + it!=quantifiers.end(); + it++) + cnf.add_quantifier(*it); + + for(clausest::const_iterator + it=clauses.begin(); + it!=clauses.end(); + it++) + cnf.lcnf(*it); +} + +/*******************************************************************\ + +Function: qdimacs_cnft::hash + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +size_t qdimacs_cnft::hash() const +{ + size_t result=0; + + for(quantifierst::const_iterator it=quantifiers.begin(); + it!=quantifiers.end(); + it++) + result=((result<<1)^it->hash())-result; + + return result^cnf_clause_listt::hash(); +} + +/*******************************************************************\ + +Function: qdimacs_cnft::is_quantified + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool qdimacs_cnft::is_quantified(const literalt l) const +{ + for(quantifierst::const_iterator it=quantifiers.begin(); + it!=quantifiers.end(); + it++) + if(it->var_no==l.var_no()) + return true; + + return false; +} + +/*******************************************************************\ + +Function: qdimacs_cnft::find_quantifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool qdimacs_cnft::find_quantifier(const literalt l, quantifiert &q) const +{ + for(quantifierst::const_iterator it=quantifiers.begin(); + it!=quantifiers.end(); + it++) + if(it->var_no==l.var_no()) + { + q=*it; + return true; + } + + return false; +} diff --git a/src/solvers/qbf/qdimacs_cnf.h b/src/solvers/qbf/qdimacs_cnf.h new file mode 100644 index 00000000000..5acfa8453c5 --- /dev/null +++ b/src/solvers/qbf/qdimacs_cnf.h @@ -0,0 +1,85 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_QBF_QDIMACS_CNF_H +#define CPROVER_QBF_QDIMACS_CNF_H + +#include +#include + +#include "../sat/dimacs_cnf.h" + +class qdimacs_cnft:public dimacs_cnft +{ +public: + qdimacs_cnft() { } + virtual ~qdimacs_cnft() { } + + virtual void write_qdimacs_cnf(std::ostream &out); + + // dummy functions + + virtual const std::string solver_text() + { + return "QDIMACS CNF"; + } + + class quantifiert + { + public: + typedef enum { NONE, EXISTENTIAL, UNIVERSAL } typet; + typet type; + unsigned var_no; + + quantifiert():type(NONE), var_no(0) + { + } + + quantifiert(typet _type, literalt _l):type(_type), var_no(_l.var_no()) + { + } + + friend bool operator==(const quantifiert &a, const quantifiert &b) + { + return a.type==b.type && a.var_no==b.var_no; + } + + size_t hash() const + { + return var_no^(type<<24); + } + }; + + // quantifiers + typedef std::vector quantifierst; + quantifierst quantifiers; + + virtual void add_quantifier(const quantifiert &quantifier) + { + quantifiers.push_back(quantifier); + } + + void add_quantifier(const quantifiert::typet type, const literalt l) + { + add_quantifier(quantifiert(type, l)); + } + + bool is_quantified(const literalt l) const; + bool find_quantifier(const literalt l, quantifiert &q) const; + + virtual void set_quantifier(const quantifiert::typet type, const literalt l); + void copy_to(qdimacs_cnft &cnf) const; + + friend bool operator==(const qdimacs_cnft &a, const qdimacs_cnft &b); + size_t hash() const; + +protected: + void write_prefix(std::ostream &out) const; +}; + +#endif diff --git a/src/solvers/qbf/qdimacs_core.cpp b/src/solvers/qbf/qdimacs_core.cpp new file mode 100644 index 00000000000..69e67f70f86 --- /dev/null +++ b/src/solvers/qbf/qdimacs_core.cpp @@ -0,0 +1,113 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#include +#include + +//#include + +#include "qdimacs_core.h" + +/*******************************************************************\ + +Function: qdimacs_coret::simplify_extractbits + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void qdimacs_coret::simplify_extractbits(exprt &expr) const +{ + if(expr.id()==ID_and) + { + typedef std::map > used_bits_mapt; + used_bits_mapt used_bits_map; + + forall_operands(it, expr) + { + if(it->id()==ID_extractbit && it->op1().is_constant()) + { + used_bits_map[it->op0()].insert(it->op1()); + } + else if(it->id()==ID_not && + it->op0().id()==ID_extractbit && it->op0().op1().is_constant()) + { + used_bits_map[it->op0().op0()].insert(it->op0().op1()); + } + } + + for(used_bits_mapt::const_iterator it=used_bits_map.begin(); + it!=used_bits_map.end(); + it++) + { + #if 0 + unsigned width; + boolbv_get_width(it->first.type(), width); + + std::string value_string; + value_string.resize(width, '0'); + + if(it->second.size() == width) // all bits extracted from this one! + { + const irep_idt &ident=it->first.get(ID_identifier); + const exprt::operandst &old_operands=expr.operands(); + exprt::operandst new_operands; + + for(exprt::operandst::const_iterator oit=old_operands.begin(); + oit!=old_operands.end(); + oit++) + { + if(oit->id()==ID_extractbit && + oit->op1().is_constant()) + { + if(oit->op0().get(ID_identifier)==ident) + { + const exprt &val_expr=oit->op1(); + mp_integer value; + to_integer(val_expr, value); + value_string[value.to_ulong()] = '1'; + + #if 0 + std::cout << "[" << value << "] = 1" << std::endl; + #endif + + continue; + } + } + else if(oit->id()==ID_not && + oit->op0().id()==ID_extractbit && + oit->op0().op1().is_constant()) + { + if(oit->op0().op0().get(ID_identifier)==ident) + { + // just kick it; the bit in value_string is 0 anyways + continue; + } + } + + new_operands.push_back(*oit); + } + + exprt new_value(ID_constant, it->first.type()); + new_value.set(ID_value, value_string); + new_operands.push_back(equality_exprt(it->first, new_value)); + + #if 0 + std::cout << "FINAL: " << value_string << std::endl; + #endif + + expr.operands() = new_operands; + } + #endif + } + } +} diff --git a/src/solvers/qbf/qdimacs_core.h b/src/solvers/qbf/qdimacs_core.h new file mode 100644 index 00000000000..ea28cc0aeee --- /dev/null +++ b/src/solvers/qbf/qdimacs_core.h @@ -0,0 +1,33 @@ +/*******************************************************************\ + +Module: + +Author: CM Wintersteiger + +\*******************************************************************/ + +#ifndef CPROVER_QDIMACS_CORE_H +#define CPROVER_QDIMACS_CORE_H + +#include + +#include "qdimacs_cnf.h" + +class qdimacs_coret:public qdimacs_cnft +{ +public: + virtual tvt l_get(literalt a) const=0; + virtual bool is_in_core(literalt l) const=0; + + typedef enum { M_TRUE, M_FALSE, M_DONTCARE, M_COMPLEX } modeltypet; + virtual modeltypet m_get(literalt a) const=0; + + typedef std::pair symbol_mapt; + typedef std::map variable_mapt; + variable_mapt variable_map; // variable -> symbol/index map + virtual const exprt f_get(literalt v)=0; + + void simplify_extractbits(exprt &expr) const; +}; + +#endif /*CPROVER_QDIMACS_CORE_H*/ diff --git a/src/solvers/sat/cnf.cpp b/src/solvers/sat/cnf.cpp new file mode 100644 index 00000000000..cbfdc366bdb --- /dev/null +++ b/src/solvers/sat/cnf.cpp @@ -0,0 +1,697 @@ +/*******************************************************************\ + +Module: CNF Generation, via Tseitin + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "cnf.h" +//#define VERBOSE + +/*******************************************************************\ + +Function: cnft::cnft + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cnft::cnft() +{ + // for CNF, we don't use 0 as a matter of principle + _no_variables=1; +} + +/*******************************************************************\ + +Function: cnft::~cnft + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cnft::~cnft() +{ +} + +/*******************************************************************\ + +Function: cnft::gate_and + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnft::gate_and(literalt a, literalt b, literalt o) +{ + // a*b=c <==> (a + o')( b + o')(a'+b'+o) + bvt lits; + + lits.clear(); + lits.reserve(2); + lits.push_back(pos(a)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(2); + lits.push_back(pos(b)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(neg(a)); + lits.push_back(neg(b)); + lits.push_back(pos(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: cnft::gate_or + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnft::gate_or(literalt a, literalt b, literalt o) +{ + // a+b=c <==> (a' + c)( b' + c)(a + b + c') + bvt lits; + + lits.clear(); + lits.reserve(2); + lits.push_back(neg(a)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(2); + lits.push_back(neg(b)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(pos(a)); + lits.push_back(pos(b)); + lits.push_back(neg(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: cnft::gate_xor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnft::gate_xor(literalt a, literalt b, literalt o) +{ + // a xor b = o <==> (a' + b' + o') + // (a + b + o' ) + // (a' + b + o) + // (a + b' + o) + bvt lits; + + lits.clear(); + lits.reserve(3); + lits.push_back(neg(a)); + lits.push_back(neg(b)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(pos(a)); + lits.push_back(pos(b)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(neg(a)); + lits.push_back(pos(b)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(pos(a)); + lits.push_back(neg(b)); + lits.push_back(pos(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: cnft::gate_nand + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnft::gate_nand(literalt a, literalt b, literalt o) +{ + // a Nand b = o <==> (a + o)( b + o)(a' + b' + o') + bvt lits; + + lits.clear(); + lits.reserve(2); + lits.push_back(pos(a)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(2); + lits.push_back(pos(b)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(neg(a)); + lits.push_back(neg(b)); + lits.push_back(neg(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: cnft::gate_nor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnft::gate_nor(literalt a, literalt b, literalt o) +{ + // a Nor b = o <==> (a' + o')( b' + o')(a + b + o) + bvt lits; + + lits.clear(); + lits.reserve(2); + lits.push_back(neg(a)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(2); + lits.push_back(neg(b)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(pos(a)); + lits.push_back(pos(b)); + lits.push_back(pos(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: cnft::gate_equal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnft::gate_equal(literalt a, literalt b, literalt o) +{ + gate_xor(a, b, lnot(o)); +} + +/*******************************************************************\ + +Function: cnft::gate_implies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnft::gate_implies(literalt a, literalt b, literalt o) +{ + gate_or(lnot(a), b, o); +} + +/*******************************************************************\ + +Function: cnft::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt cnft::land(const bvt &bv) +{ + if(bv.size()==0) return const_literal(true); + if(bv.size()==1) return bv[0]; + if(bv.size()==2) return land(bv[0], bv[1]); + + for(unsigned i=0; i s; + + dest.reserve(bv.size()); + + for(bvt::const_iterator it=bv.begin(); it!=bv.end(); it++) + { + if(s.insert(*it).second) + dest.push_back(*it); + } +} + +/*******************************************************************\ + +Function: cnft::process_clause + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cnft::process_clause(const bvt &bv, bvt &dest) +{ + dest.clear(); + + // empty clause! this is UNSAT + if(bv.empty()) return false; + + std::set s; + + dest.reserve(bv.size()); + + for(bvt::const_iterator it=bv.begin(); + it!=bv.end(); + it++) + { + literalt l=*it; + + // we never use index 0 + assert(l.var_no()!=0); + + // we never use 'unused_var_no' + assert(l.var_no()!=literalt::unused_var_no()); + + if(l.is_true()) + return true; // clause satisfied + + if(l.is_false()) + continue; + + if(l.var_no()>=_no_variables) + std::cout << "l.var_no()=" << l.var_no() << " _no_variables=" << _no_variables << std::endl; + assert(l.var_no()<_no_variables); + + // prevent duplicate literals + if(s.insert(l).second) + dest.push_back(l); + + if(s.find(lnot(l))!=s.end()) + return true; // clause satisfied + } + + return false; +} diff --git a/src/solvers/sat/cnf.h b/src/solvers/sat/cnf.h new file mode 100644 index 00000000000..cc60140555d --- /dev/null +++ b/src/solvers/sat/cnf.h @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: CNF Generation, via Tseitin + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_PROP_CNF_H +#define CPROVER_PROP_CNF_H + +#include + +#include + +class cnft:public propt +{ +public: + cnft(); + virtual ~cnft(); + + virtual literalt land(literalt a, literalt b); + virtual literalt lor(literalt a, literalt b); + virtual literalt land(const bvt &bv); + virtual literalt lor(const bvt &bv); + virtual literalt lxor(const bvt &bv); + virtual literalt lnot(literalt a); + virtual literalt lxor(literalt a, literalt b); + virtual literalt lnand(literalt a, literalt b); + virtual literalt lnor(literalt a, literalt b); + virtual literalt lequal(literalt a, literalt b); + virtual literalt limplies(literalt a, literalt b); + virtual literalt lselect(literalt a, literalt b, literalt c); // a?b:c + virtual literalt new_variable(); + virtual unsigned no_variables() const { return _no_variables; } + virtual void set_no_variables(unsigned no) { _no_variables=no; } + virtual unsigned no_clauses() const=0; + + void gate_and(literalt a, literalt b, literalt o); + void gate_or(literalt a, literalt b, literalt o); + void gate_xor(literalt a, literalt b, literalt o); + void gate_nand(literalt a, literalt b, literalt o); + void gate_nor(literalt a, literalt b, literalt o); + void gate_equal(literalt a, literalt b, literalt o); + void gate_implies(literalt a, literalt b, literalt o); + + static void eliminate_duplicates(const bvt &bv, bvt &dest); + +protected: + unsigned _no_variables; + + bool process_clause(const bvt &bv, bvt &dest); + + static bool is_all(const bvt &bv, literalt l) + { + for(unsigned i=0; i + +#include "cnf_clause_list.h" + +/*******************************************************************\ + +Function: cnf_clause_listt::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnf_clause_listt::lcnf(const bvt &bv) +{ + bvt new_bv; + + if(process_clause(bv, new_bv)) + return; + + clauses.push_back(new_bv); +} + +/*******************************************************************\ + +Function: cnf_clause_list_assignmentt::print_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnf_clause_list_assignmentt::print_assignment(std::ostream &out) const +{ + for(unsigned v=1; v clausest; + + clausest &get_clauses() { return clauses; } + + void copy_to(cnft &cnf) const + { + cnf.set_no_variables(_no_variables); + for(clausest::const_iterator + it=clauses.begin(); + it!=clauses.end(); + it++) + cnf.lcnf(*it); + } + + static size_t hash_clause(const bvt &bv) + { + size_t result=0; + for(bvt::const_iterator it=bv.begin(); it!=bv.end(); it++) + result=((result<<2)^it->get())-result; + + return result; + } + + size_t hash() const + { + size_t result=0; + for(clausest::const_iterator it=clauses.begin(); it!=clauses.end(); it++) + result=((result<<2)^hash_clause(*it))-result; + + return result; + } + +protected: + clausest clauses; +}; + +// CNF given as a list of clauses +// PLUS an assignment to the variables + +class cnf_clause_list_assignmentt:public cnf_clause_listt +{ +public: + typedef std::vector assignmentt; + + assignmentt &get_assignment() + { + return assignment; + } + + virtual tvt l_get(literalt literal) const + { + unsigned v=literal.var_no(); + + if(v==0 || v>=assignment.size()) + return tvt(tvt::TV_UNKNOWN); + + tvt r=assignment[v]; + return literal.sign()?!r:r; + } + + virtual void copy_assignment_from(const propt &prop); + void print_assignment(std::ostream &out) const; + +protected: + assignmentt assignment; +}; + +#endif diff --git a/src/solvers/sat/dimacs_cnf.cpp b/src/solvers/sat/dimacs_cnf.cpp new file mode 100644 index 00000000000..fbe1d1e0986 --- /dev/null +++ b/src/solvers/sat/dimacs_cnf.cpp @@ -0,0 +1,143 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "dimacs_cnf.h" + +#include + +/*******************************************************************\ + +Function: dimacs_cnft::dimacs_cnft + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +dimacs_cnft::dimacs_cnft():break_lines(true) +{ +} + +/*******************************************************************\ + +Function: dimacs_cnf_dumpt::dimacs_cnf_dumpt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +dimacs_cnf_dumpt::dimacs_cnf_dumpt(std::ostream &_out):out(_out) +{ +} + +/*******************************************************************\ + +Function: dimacs_cnft::write_dimacs_cnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dimacs_cnft::write_dimacs_cnf(std::ostream &out) +{ + write_problem_line(out); + write_clauses(out); +} + +/*******************************************************************\ + +Function: dimacs_cnft::write_problem_line + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void dimacs_cnft::write_problem_line(std::ostream &out) +{ + out << "p cnf " << no_variables() << " " + << clauses.size() << std::endl; +} + +/*******************************************************************\ + +Function: write_dimacs_clause + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void write_dimacs_clause( + const bvt &clause, + std::ostream &out, + bool break_lines) +{ + for(unsigned j=0; j + +class dimacs_cnft:public cnf_clause_listt +{ +public: + dimacs_cnft(); + virtual ~dimacs_cnft() { } + + virtual void write_dimacs_cnf(std::ostream &out); + + // dummy functions + + virtual const std::string solver_text() + { + return "DIMACS CNF"; + } + +protected: + void write_problem_line(std::ostream &out); + void write_clauses(std::ostream &out); + + bool break_lines; +}; + +class dimacs_cnf_dumpt:public cnft +{ +public: + dimacs_cnf_dumpt(std::ostream &_out); + virtual ~dimacs_cnf_dumpt() { } + + virtual const std::string solver_text() + { + return "DIMACS CNF Dumper"; + } + + virtual void lcnf(const bvt &bv); + + virtual resultt prop_solve() + { + return P_ERROR; + } + + virtual tvt l_get(literalt) const + { + return tvt(tvt::TV_UNKNOWN); + } + + virtual unsigned int no_clauses() const + { + return 0; + } + +protected: + std::ostream &out; +}; + +#endif diff --git a/src/solvers/sat/pbs_dimacs_cnf.cpp b/src/solvers/sat/pbs_dimacs_cnf.cpp new file mode 100644 index 00000000000..1187a4dee2e --- /dev/null +++ b/src/solvers/sat/pbs_dimacs_cnf.cpp @@ -0,0 +1,300 @@ +/*******************************************************************\ + +Module: + +Author: Alex Groce + +\*******************************************************************/ + +#include +#include + +#include +#include + +#include +#include + +#include "pbs_dimacs_cnf.h" + +/*******************************************************************\ + +Function: pbs_dimacs_cnft::write_dimacs_cnf_pb + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void pbs_dimacs_cnft::write_dimacs_pb(std::ostream &out) +{ + double d_sum = 0; + + //std::cout << "enter: No Lit. = " << no_variables () << std::endl; + + for(std::map::const_iterator it=pb_constraintmap.begin(); + it != pb_constraintmap.end (); ++it) { + d_sum += ((*it).second); + } + + if (!optimize) { + out << "# PBType: E" << std::endl; + out << "# PBGoal: " << goal << std::endl; + } else if (!maximize) { + out << "# PBType: SE" << std::endl; + out << "# PBGoal: " << d_sum << std::endl; + out << "# PBObj : MIN" << std::endl; + } else { + out << "# PBType: GE" << std::endl; + out << "# PBGoal: " << 0 << std::endl; + out << "# PBObj : MAX" << std::endl; + } + out << "# NumCoef: " << pb_constraintmap.size() << std::endl; + + for(std::map::const_iterator it=pb_constraintmap.begin(); + it!=pb_constraintmap.end();++it) + { + int dimacs_lit = (*it).first.dimacs(); + out << "v" << dimacs_lit << " c" << ((*it).second) << std::endl; + } + + //std::cout << "exit: No Lit. = " << no_variables () << std::endl; +} + +/*******************************************************************\ + +Function: pbs_dimacs_cnft::pbs_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool pbs_dimacs_cnft::pbs_solve() +{ + //std::cout << "solve: No Lit. = " << no_variables () << std::endl; + + std::string command; + + if(!pbs_path.empty()) { + command += pbs_path; + if (command.substr(command.length(),1) != "/") + command += "/"; + } + + command += "pbs"; + + //std::cout << "PBS COMMAND IS: " << command << std::endl; + /* + if (!(getenv("PBS_PATH") == NULL)) + { + command = getenv("PBS_PATH"); + } + else + { + error ("Unable to read PBS_PATH environment variable.\n"); + return false; + } + */ + + command += " -f temp.cnf"; + + #if 1 + if (optimize) + { + if (binary_search) { + command += " -S 1000 -D 1 -H -I -a"; + } + else { + //std::cout << "NO BINARY SEARCH" << std::endl; + command += " -S 1000 -D 1 -I -a"; + } + } + else + { + command += " -S 1000 -D 1 -a"; + } + #else + command += " -z"; + #endif + + command += " -a > temp.out"; + + system(command.c_str()); + + std::ifstream file("temp.out"); + std::string line; + int v; + bool satisfied = false; + + if(file.fail()) + { + error("Unable to read SAT results!\n"); + return false; + } + + opt_sum = -1; + + while(file && !file.eof ()) + { + str_getline(file,line); + if(strstr(line.c_str(), + "Variable Assignments Satisfying CNF Formula:")!=NULL) + { + //print ("Reading assignments...\n"); + //std::cout << "No literals: " << no_variables() << std::endl; + satisfied = true; + assigned.clear (); + for (size_t i = 0; (file && (i < no_variables())); ++i) + { + file >> v; + if (v > 0) + { + //std::cout << v << " "; + assigned.insert(v); + } + } + //std::cout << std::endl; + //print ("Finished reading assignments.\n"); + } + else if (strstr(line.c_str(),"SAT... SUM") != NULL) + { + //print (line); + sscanf(line.c_str(),"%*s %*s %*s %d", &opt_sum); + } + else if (strstr(line.c_str(),"SAT - All implied") != NULL) + { + //print (line); + sscanf(line.c_str(),"%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d", &opt_sum); + } + else if (strstr(line.c_str(),"SAT... Solution") != NULL) + { + //print(line); + sscanf(line.c_str(),"%*s %*s %*s %d", &opt_sum); + } + else if (strstr(line.c_str(),"Optimal Soln") != NULL) + { + //print(line); + if (strstr(line.c_str(),"time out") != NULL) + { + print (6, "WARNING: TIMED OUT. SOLUTION MAY BE INCORRECT.\n"); + return satisfied; + } + sscanf(line.c_str(),"%*s %*s %*s %d", &opt_sum); + } + } + + return satisfied; +} + +/*******************************************************************\ + +Function: pbs_dimacs_cnft::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt pbs_dimacs_cnft::prop_solve() +{ + std::ofstream file("temp.cnf"); + + write_dimacs_cnf(file); + + std::ofstream pbfile("temp.cnf.pb"); + + write_dimacs_pb(pbfile); + + file.close(); + pbfile.close(); + + std::string msg= + i2string(no_variables())+" variables, "+ + i2string(clauses.size())+" clauses"; + messaget::status(msg); + + bool result=pbs_solve(); + + if (!result) + { + msg="PBS checker: system is UNSATISFIABLE"; + } + else + { + msg="PBS checker: system is SATISFIABLE"; + if (optimize) + msg += " (distance " + i2string(opt_sum) + ")"; + } + + messaget::status(msg); + + if(result) + return P_SATISFIABLE; + else + return P_UNSATISFIABLE; +} + +/*******************************************************************\ + +Function: pbs_dimacs_cnft::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt pbs_dimacs_cnft::l_get(literalt a) const +{ + int dimacs_lit = a.dimacs(); + + //std::cout << a << " / " << dimacs_lit << " = "; + + bool neg = (dimacs_lit < 0); + if(neg) + dimacs_lit = -dimacs_lit; + + std::set::const_iterator f = assigned.find(dimacs_lit); + + if(!neg) + { + if(f == assigned.end()) + { + //std::cout << "FALSE" << std::endl; + return tvt(false); + } + else + { + //std::cout << "TRUE" << std::endl; + return tvt(true); + } + } + else + { + if(f != assigned.end()) + { + //std::cout << "FALSE" << std::endl; + return tvt(false); + } + else + { + //std::cout << "TRUE" << std::endl; + return tvt(true); + } + } + + //std::cout << "ERROR" << std::endl; + return tvt(tvt::TV_UNKNOWN); +} diff --git a/src/solvers/sat/pbs_dimacs_cnf.h b/src/solvers/sat/pbs_dimacs_cnf.h new file mode 100644 index 00000000000..161556d578f --- /dev/null +++ b/src/solvers/sat/pbs_dimacs_cnf.h @@ -0,0 +1,64 @@ +/*******************************************************************\ + +Module: + +Author: Alex Groce + +\*******************************************************************/ + +#ifndef CPROVER_PBS_DIMACS_CNF_H +#define CPROVER_PBS_DIMACS_CNF_H + +#include "dimacs_cnf.h" + +#include +#include + +#include + +class pbs_dimacs_cnft:public dimacs_cnft +{ + public: + pbs_dimacs_cnft(): + optimize(false), + maximize(false), + binary_search(false), + goal(0), + opt_sum(0) + { + } + + virtual ~pbs_dimacs_cnft() + { + } + + virtual void write_dimacs_pb(std::ostream &out); + + bool optimize; + bool maximize; + bool binary_search; + + std::string pbs_path; + + int goal; + int opt_sum; + + std::map pb_constraintmap; + + bool pbs_solve(); + + virtual resultt prop_solve(); + + virtual tvt l_get(literalt a) const; + + // dummy functions + + virtual const std::string solver_text() + { return "PBS - Pseudo Boolean/CNF Solver and Optimizer"; } + + protected: + + std::set assigned; +}; + +#endif diff --git a/src/solvers/sat/read_dimacs_cnf.cpp b/src/solvers/sat/read_dimacs_cnf.cpp new file mode 100644 index 00000000000..b423987e615 --- /dev/null +++ b/src/solvers/sat/read_dimacs_cnf.cpp @@ -0,0 +1,109 @@ +/*******************************************************************\ + +Module: Reading DIMACS CNF + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "read_dimacs_cnf.h" + +//#define VERBOSE + +/*******************************************************************\ + +Function: cnft::read_dimacs_cnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void read_dimacs_cnf(std::istream &in, cnft &dest) +{ +#define DELIMITERS "\t\n\v\f\r " +#define CHAR_DELIMITERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + + bvt new_bv; + std::string line; + + while(getline(in, line)) + { + line += " "; + while(true) + { + if(line.empty()) + break; +#ifdef VERBOSE + std::cout << "begin line " << line << std::endl; +#endif + size_t pos = line.find_first_of(DELIMITERS, 0); +#ifdef VERBOSE + std::cout << "pos " << pos << std::endl; +#endif + size_t pos_char = line.find_first_of(CHAR_DELIMITERS, 0); + if(pos != std::string::npos) + { + std::string decision = line.substr(0, pos); + line.erase(0,pos+1); +#ifdef VERBOSE + std::cout << "i am here\n"; + std::cout << decision << std::endl; + std::cout << "line" << line << std::endl; +#endif + if(!decision.compare(std::string("c"))) + // if(!strcasecmp(decision.c_str(),"c")) + { +#ifdef VERBOSE + std::cout << "c " << std::endl; +#endif + break; + } + if(!decision.compare(std::string("p"))) + // if(!strcasecmp(decision.c_str(),"p")) + { +#ifdef VERBOSE + std::cout << "p " << std::endl; +#endif + break; + } + if(pos_char == std::string::npos) //no char present in the clause + { + int parsed_lit = atoi(decision.c_str()); +#ifdef VERBOSE + std::cout << "parsed_lit " << parsed_lit << " " << std::endl; +#endif + if(parsed_lit == 0) + { + bvt no_dup; + cnft::eliminate_duplicates(new_bv, no_dup); +#ifdef VERBOSE + std::cout << "calling lcnf " << new_bv.size() << std::endl; +#endif + dest.lcnf(no_dup); + new_bv.clear(); + no_dup.clear(); + } + else + { + unsigned var = abs(parsed_lit); //because of the const variable + literalt l; + bool sign = (parsed_lit > 0) ? false : true; + l.set(var, sign); +#ifdef VERBOSE + std::cout << "setting l to " << l.get() << std::endl; +#endif + new_bv.push_back(l); + if(dest.no_variables() <= var) + dest.set_no_variables(var+1); + } + } + } + } + } +} diff --git a/src/solvers/sat/read_dimacs_cnf.h b/src/solvers/sat/read_dimacs_cnf.h new file mode 100644 index 00000000000..13c34b5b84e --- /dev/null +++ b/src/solvers/sat/read_dimacs_cnf.h @@ -0,0 +1,16 @@ +/*******************************************************************\ + +Module: Reading DIMACS CNF + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef __CPROVER_READ_DIMACS_CNF_H +#define __CPROVER_READ_DIMACS_CNF_H + +#include "cnf.h" + +void read_dimacs_cnf(std::istream &in, cnft &dest); + +#endif diff --git a/src/solvers/sat/resolution_proof.cpp b/src/solvers/sat/resolution_proof.cpp new file mode 100644 index 00000000000..c3ed89116a5 --- /dev/null +++ b/src/solvers/sat/resolution_proof.cpp @@ -0,0 +1,72 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "resolution_proof.h" + +/*******************************************************************\ + +Function: resolution_prooft::build_core + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +template +void resolution_prooft::build_core(std::vector &in_core) +{ + std::stack s; + std::vector seen; + + assert(!clauses.empty()); + + seen.resize(clauses.size(), false); + + s.push(clauses.size()-1); + + while(!s.empty()) + { + unsigned int c_id=s.top(); + s.pop(); + + if(seen[c_id]) continue; + seen[c_id]=true; + + const T &c=clauses[c_id]; + + if(c.is_root) + { + for(unsigned i=0; i; diff --git a/src/solvers/sat/resolution_proof.h b/src/solvers/sat/resolution_proof.h new file mode 100644 index 00000000000..ecb62546285 --- /dev/null +++ b/src/solvers/sat/resolution_proof.h @@ -0,0 +1,48 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SOLVERS_SAT_RESOLUTION_PROOF_H +#define CPROVER_SOLVERS_SAT_RESOLUTION_PROOF_H + +#include + +#include + +class clauset +{ +public: + bool is_root; + + // if root, what clause + bvt root_clause; + + unsigned first_clause_id; + + struct stept + { + unsigned pivot_var_no; + unsigned clause_id; + }; + + typedef std::vector stepst; + stepst steps; +}; + +template +class resolution_prooft +{ +public: + typedef std::vector clausest; + clausest clauses; + + void build_core(std::vector &in_core); +}; + +typedef resolution_prooft simple_prooft; + +#endif diff --git a/src/solvers/sat/satcheck.cpp b/src/solvers/sat/satcheck.cpp new file mode 100644 index 00000000000..48daf8f6399 --- /dev/null +++ b/src/solvers/sat/satcheck.cpp @@ -0,0 +1,23 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "satcheck.h" + +// Sanity check + +#ifdef SATCHECK_MINISAT1 +#ifndef HAVE_MINISAT +#error "I expected to have MiniSat 1" +#endif +#endif + +#ifdef SATCHECK_MINISAT2 +#ifndef HAVE_MINISAT2 +#error "I expected to have MiniSat 2" +#endif +#endif diff --git a/src/solvers/sat/satcheck.h b/src/solvers/sat/satcheck.h new file mode 100644 index 00000000000..bf98bb51de0 --- /dev/null +++ b/src/solvers/sat/satcheck.h @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SATCHECK_H +#define CPROVER_SATCHECK_H + +// this picks the "default" SAT solver + +//#define SATCHECK_ZCHAFF +//#define SATCHECK_MINISAT1 +#define SATCHECK_MINISAT2 +//#define SATCHECK_BOOLEFORCE + +#ifdef SATCHECK_ZCHAFF + +#include "satcheck_zchaff.h" + +typedef satcheck_zchafft satcheckt; + +#else +#ifdef SATCHECK_BOOLEFORCE + +#include "satcheck_booleforce.h" + +typedef satcheck_booleforcet satcheckt; + +#else + +#ifdef SATCHECK_MINISAT1 + +#include "satcheck_minisat.h" + +typedef satcheck_minisatt satcheckt; + +#else +#ifdef SATCHECK_MINISAT2 + +#include "satcheck_minisat2.h" + +typedef satcheck_minisatt satcheckt; + +#else +#error NO SAT CHECKER +#endif +#endif +#endif +#endif + +#endif diff --git a/src/solvers/sat/satcheck_booleforce.cpp b/src/solvers/sat/satcheck_booleforce.cpp new file mode 100644 index 00000000000..f2a96b2969a --- /dev/null +++ b/src/solvers/sat/satcheck_booleforce.cpp @@ -0,0 +1,226 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "satcheck_booleforce.h" + +extern "C" { +#include "booleforce.h" +} + +/*******************************************************************\ + +Function: satcheck_booleforcet::satcheck_booleforcet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_booleforcet::satcheck_booleforcet() +{ + booleforce_set_trace(false); +} + +/*******************************************************************\ + +Function: satcheck_booleforce_coret::satcheck_booleforce_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_booleforce_coret::satcheck_booleforce_coret() +{ + booleforce_set_trace(true); +} + +/*******************************************************************\ + +Function: satcheck_booleforce_baset::~satcheck_booleforce_baset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_booleforce_baset::~satcheck_booleforce_baset() +{ + booleforce_reset(); +} + +/*******************************************************************\ + +Function: satcheck_booleforce_baset::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt satcheck_booleforce_baset::l_get(literalt a) const +{ + assert(status==SAT); + + if(a.is_true()) + return tvt(true); + else if(a.is_false()) + return tvt(false); + + tvt result; + unsigned v=a.var_no(); + + assert(v0) + result=tvt(true); + else if(r<0) + result=tvt(false); + else + result=tvt(tvt::TV_UNKNOWN); + + if(a.sign()) result=!result; + + return result; +} + +/*******************************************************************\ + +Function: satcheck_booleforce_Baset::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_booleforce_baset::solver_text() +{ + return std::string("Booleforce version ")+booleforce_version(); +} + +/*******************************************************************\ + +Function: satcheck_booleforce_baset::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_booleforce_baset::lcnf(const bvt &bv) +{ + bvt tmp; + + if(process_clause(bv, tmp)) + return; + + for(unsigned j=0; j +#include + +#include "cnf.h" + +class satcheck_booleforce_baset:public cnf_solvert +{ +public: + virtual ~satcheck_booleforce_baset(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + + virtual void lcnf(const bvt &bv); +}; + +class satcheck_booleforcet:public satcheck_booleforce_baset +{ +public: + satcheck_booleforcet(); +}; + +class satcheck_booleforce_coret:public satcheck_booleforce_baset +{ +public: + satcheck_booleforce_coret(); + + bool is_in_core(literalt l) const; +}; + +#endif diff --git a/src/solvers/sat/satcheck_core.h b/src/solvers/sat/satcheck_core.h new file mode 100644 index 00000000000..5eb98730d3f --- /dev/null +++ b/src/solvers/sat/satcheck_core.h @@ -0,0 +1,43 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SATCHECK_CORE_H +#define CPROVER_SATCHECK_CORE_H + +//#define SATCHECK_CORE_ZCHAFF +//#define SATCHECK_CORE_MINISAT1 +//#define SATCHECK_CORE_BOOLEFORCE + +#ifdef SATCHECK_CORE_ZCHAFF + +#include "satcheck_zcore.h" + +typedef satcheck_zcoret satcheck_coret; + +#else +#ifdef SATCHECK_CORE_BOOLEFORCE + +#include "satcheck_booleforce.h" + +typedef satcheck_booleforce_coret satcheck_coret; + +#else + +#ifdef SATCHECK_CORE_MINISAT1 + +#include "satcheck_minisat.h" + +typedef satcheck_minisat_coret satcheck_coret; + +#else +#error NO SAT CHECKER WITH CORE EXTRACTOR +#endif +#endif +#endif + +#endif diff --git a/src/solvers/sat/satcheck_limmat.cpp b/src/solvers/sat/satcheck_limmat.cpp new file mode 100644 index 00000000000..1dc8a56ba75 --- /dev/null +++ b/src/solvers/sat/satcheck_limmat.cpp @@ -0,0 +1,210 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "satcheck_limmat.h" + +extern "C" { +#include "limmat.h" +} + +//#define DEBUG + +/*******************************************************************\ + +Function: satcheck_limmatt::satcheck_limmatt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_limmatt::satcheck_limmatt() +{ + solver=new_Limmat(NULL); +} + +/*******************************************************************\ + +Function: satcheck_limmatt::~satcheck_limmatt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_limmatt::~satcheck_limmatt() +{ + if(solver!=NULL) delete_Limmat(solver); +} + +/*******************************************************************\ + +Function: satcheck_limmatt::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt satcheck_limmatt::l_get(literalt a) const +{ + if(a.is_true()) + return tvt(true); + else if(a.is_false()) + return tvt(false); + + tvt result; + unsigned v=a.var_no(); + + assert(vsize()+1]; + + for(unsigned j=0; jsize(); j++) + clause[j]=(*it)[j].dimacs(); + + // zero-terminated + clause[it->size()]=0; + + add_Limmat(solver, clause); + + delete clause; + } +} + +/*******************************************************************\ + +Function: satcheck_limmatt::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt satcheck_limmatt::prop_solve() +{ + copy_cnf(); + + { + std::string msg= + i2string(maxvar_Limmat(solver))+" variables, "+ + i2string(clauses_Limmat(solver))+" clauses"; + messaget::status(msg); + } + + int status=sat_Limmat(solver, -1); + + { + std::string msg; + + switch(status) + { + case 0: + msg="SAT checker: negated claim is UNSATISFIABLE, i.e., holds"; + break; + + case 1: + msg="SAT checker: negated claim is SATISFIABLE, i.e., does not hold"; + break; + + default: + msg="SAT checker failed: unknown result"; + break; + } + + messaget::status(msg); + } + + if(status==0) + { + assignment.clear(); + return P_UNSATISFIABLE; + } + + if(status==1) + { + assignment.resize(no_variables()+1, 2); // unknown is default + + for(const int *a=assignment_Limmat(solver); *a!=0; a++) + { + int v=*a; + if(v<0) v=-v; + assert((unsigned)v=0; + } + + return P_SATISFIABLE; + } + + return P_ERROR; +} + diff --git a/src/solvers/sat/satcheck_limmat.h b/src/solvers/sat/satcheck_limmat.h new file mode 100644 index 00000000000..2bec2cba911 --- /dev/null +++ b/src/solvers/sat/satcheck_limmat.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SATCHECK_LIMMAT_H +#define CPROVER_SATCHECK_LIMMAT_H + +#include +#include "dimacs_cnf.h" + +class satcheck_limmatt:public dimacs_cnft +{ + public: + satcheck_limmatt(); + virtual ~satcheck_limmatt(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + + void copy_cnf(); + + protected: + struct Limmat *solver; + std::vector assignment; +}; + +#endif diff --git a/src/solvers/sat/satcheck_minisat.cpp b/src/solvers/sat/satcheck_minisat.cpp new file mode 100644 index 00000000000..27db07072d2 --- /dev/null +++ b/src/solvers/sat/satcheck_minisat.cpp @@ -0,0 +1,534 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include + +#include "satcheck_minisat.h" + +#include +#include + +#ifndef HAVE_MINISAT +#error "Expected HAVE_MINISAT" +#endif + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert(const bvt &bv, vec &dest) +{ + dest.growTo(bv.size()); + + for(unsigned i=0; i &c) + { + resolution_proof.clauses.push_back(clauset()); + resolution_proof.clauses.back().is_root=true; + resolution_proof.clauses.back().root_clause.resize(c.size()); +// resolution_proof.clauses.back().pid = resolution_proof.partition_id; + + for(int i=0; i resolution_proof.no_vars) +// resolution_proof.no_vars = var(c[i]); + } + } + + virtual void chain(const vec &cs, const vec &xs); + + virtual void deleted(ClauseId c) { } + virtual void done() { } + virtual ~minisat_prooft() { } + + simple_prooft resolution_proof; +}; + +/*******************************************************************\ + +Function: minisat_prooft::chain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void minisat_prooft::chain(const vec &cs, const vec &xs) +{ + assert(cs.size()==xs.size()+1); + + resolution_proof.clauses.push_back(clauset()); + clauset &c=resolution_proof.clauses.back(); + + c.is_root=false; + // c.pid = resolution_proof.partition_id; + c.first_clause_id=cs[0]; + c.steps.resize(xs.size()); + + // copy clause IDs + int c_id=resolution_proof.clauses.size(); + + for(int i=0; imodel.size()); + + if(solver->model[a.var_no()]==l_True) + result=tvt(true); + else if(solver->model[a.var_no()]==l_False) + result=tvt(false); + else + result=tvt(tvt::TV_UNKNOWN); + + if(a.sign()) result=!result; + + return result; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_minisat_baset::solver_text() +{ + return "MiniSAT 1.14p"; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::add_variables + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_minisat_baset::add_variables() +{ + + while((unsigned)solver->nVars()newVar(); +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_minisat_baset::lcnf(const bvt &bv) +{ + bvt new_bv; + + if(process_clause(bv, new_bv)) + return; + + // Minisat can't do empty clauses + if(new_bv.empty()) + { + empty_clause_added=true; + return; + } + + add_variables(); + + vec c; + convert(new_bv, c); + + for(unsigned i=0; inVars()); + + solver->addClause(c); + + clause_counter++; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt satcheck_minisat_baset::prop_solve() +{ + assert(status!=ERROR); + + { + std::string msg= + i2string(_no_variables)+" variables, "+ + i2string(solver->nClauses())+" clauses"; + messaget::status(msg); + } + + add_variables(); + + solver->simplifyDB(); + + std::string msg; + + if(empty_clause_added) + { + msg="empty clause: negated claim is UNSATISFIABLE, i.e., holds"; + } + else if(!solver->okay()) + { + msg="SAT checker inconsistent: negated claim is UNSATISFIABLE, i.e., holds"; + } + else + { + vec MiniSat_assumptions; + convert(assumptions, MiniSat_assumptions); + + if(solver->solve(MiniSat_assumptions)) + { + msg="SAT checker: negated claim is SATISFIABLE, i.e., does not hold"; + messaget::status(msg); + status=SAT; + return P_SATISFIABLE; + } + else + msg="SAT checker: negated claim is UNSATISFIABLE, i.e., holds"; + } + + messaget::status(msg); + status=UNSAT; + return P_UNSATISFIABLE; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::set_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_minisat_baset::set_assignment(literalt a, bool value) +{ + unsigned v=a.var_no(); + bool sign=a.sign(); + solver->model.growTo(v+1); + value^=sign; + solver->model[v]=lbool(value); +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::is_in_conflict + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool satcheck_minisat_baset::is_in_conflict(literalt a) const +{ + int v=a.var_no(); + + for(int i=0; iconflict.size(); i++) + { + if(var(solver->conflict[i])==v) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::set_assumptions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_minisat_baset::set_assumptions(const bvt &bv) +{ + assumptions=bv; + + for(bvt::const_iterator it=assumptions.begin(); + it!=assumptions.end(); + it++) + assert(!it->is_constant()); +} + +/*******************************************************************\ + +Function: satcheck_minisatt::satcheck_minisatt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisatt::satcheck_minisatt() +{ + empty_clause_added=false; + solver=new Solver; +} + +/*******************************************************************\ + +Function: satcheck_minisat_prooft::satcheck_minisat_prooft + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisat_prooft::satcheck_minisat_prooft():satcheck_minisatt() +{ + minisat_proof=new minisat_prooft; + proof=new Proof(*minisat_proof); + // solver=new Solver; + solver->proof=proof; +} + +/*******************************************************************\ + +Function: satcheck_minisat_prooft::~satcheck_minisat_prooft + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisat_prooft::~satcheck_minisat_prooft() +{ + delete proof; + delete minisat_proof; +} + +/*******************************************************************\ + +Function: satcheck_minisat_coret::satcheck_minisat_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisat_coret::satcheck_minisat_coret() +{ +} + +/*******************************************************************\ + +Function: satcheck_minisat_coret::~satcheck_minisat_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisat_coret::~satcheck_minisat_coret() +{ +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::~satcheck_minisat_baset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisat_baset::~satcheck_minisat_baset() +{ + delete solver; +} + +/*******************************************************************\ + +Function: satcheck_minisat_prooft::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_minisat_prooft::solver_text() +{ + return "MiniSAT + Proof"; +} + +/*******************************************************************\ + +Function: satcheck_minisat_coret::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt satcheck_minisat_coret::prop_solve() +{ + propt::resultt r; + + r=satcheck_minisat_prooft::prop_solve(); + + if(status==UNSAT) + { + in_core.resize(no_variables(), false); + minisat_proof->resolution_proof.build_core(in_core); + } + + return r; +} + +/*******************************************************************\ + +Function: satcheck_minisat_coret::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_minisat_coret::solver_text() +{ + return "MiniSAT + Core"; +} + +/*******************************************************************\ + +Function: satcheck_minisat_coret::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +simple_prooft &satcheck_minisat_prooft::get_resolution_proof() +{ + return minisat_proof->resolution_proof; +} diff --git a/src/solvers/sat/satcheck_minisat.h b/src/solvers/sat/satcheck_minisat.h new file mode 100644 index 00000000000..ee1c1493587 --- /dev/null +++ b/src/solvers/sat/satcheck_minisat.h @@ -0,0 +1,92 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SATCHECK_MINISAT_H +#define CPROVER_SATCHECK_MINISAT_H + +#include +#include + +#include "cnf.h" +#include "resolution_proof.h" + +class satcheck_minisat_baset:public cnf_solvert +{ +public: + satcheck_minisat_baset():solver(NULL) + { + } + + virtual ~satcheck_minisat_baset(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + + virtual void lcnf(const bvt &bv); + + virtual void set_assignment(literalt a, bool value); + + // extra MiniSat feature: solve with assumptions + virtual void set_assumptions(const bvt &_assumptions); + + // features + virtual bool has_set_assumptions() const { return true; } + virtual bool has_is_in_conflict() const { return true; } + + virtual bool is_in_conflict(literalt l) const; + +protected: + class Solver *solver; + void add_variables(); + bvt assumptions; + bool empty_clause_added; +}; + +class satcheck_minisatt:public satcheck_minisat_baset +{ +public: + satcheck_minisatt(); +}; + +class satcheck_minisat_prooft:public satcheck_minisatt +{ +public: + satcheck_minisat_prooft(); + ~satcheck_minisat_prooft(); + + virtual const std::string solver_text(); + simple_prooft &get_resolution_proof(); + //void set_partition_id(unsigned p_id); + +protected: + class Proof *proof; + class minisat_prooft *minisat_proof; +}; + +class satcheck_minisat_coret:public satcheck_minisat_prooft +{ +public: + satcheck_minisat_coret(); + ~satcheck_minisat_coret(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + + virtual bool has_in_core() const { return true; } + + virtual bool is_in_core(literalt l) const + { + assert(l.var_no() in_core; +}; +#endif diff --git a/src/solvers/sat/satcheck_minisat2.cpp b/src/solvers/sat/satcheck_minisat2.cpp new file mode 100644 index 00000000000..7373c17bfd3 --- /dev/null +++ b/src/solvers/sat/satcheck_minisat2.cpp @@ -0,0 +1,372 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include + +#include "satcheck_minisat2.h" + +#include +#include + +#ifndef HAVE_MINISAT2 +#error "Expected HAVE_MINISAT2" +#endif + +/*******************************************************************\ + +Function: convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void convert(const bvt &bv, Minisat::vec &dest) +{ + dest.growTo(bv.size()); + + for(unsigned i=0; i=(unsigned)solver->model.size()) + return tvt(tvt::TV_UNKNOWN); + + using Minisat::lbool; + + if(solver->model[a.var_no()]==l_True) + result=tvt(true); + else if(solver->model[a.var_no()]==l_False) + result=tvt(false); + else + return tvt(tvt::TV_UNKNOWN); + + if(a.sign()) result=!result; + + return result; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_minisat_baset::solver_text() +{ + return "MiniSAT2 (base)"; +} + +/*******************************************************************\ + +Function: satcheck_minisatt::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_minisatt::solver_text() +{ + return "MiniSAT2 without simplifier"; +} + +/*******************************************************************\ + +Function: satcheck_minisat_simpt::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_minisat_simpt::solver_text() +{ + return "MiniSAT2 with simplifier"; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::add_variables + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_minisat_baset::add_variables() +{ + while((unsigned)solver->nVars()newVar(); +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_minisat_baset::lcnf(const bvt &bv) +{ + bvt new_bv; + + if(process_clause(bv, new_bv)) + return; + + // Minisat can't do empty clauses + if(new_bv.empty()) + { + empty_clause_added=true; + return; + } + + add_variables(); + + Minisat::vec c; + convert(new_bv, c); + + for(unsigned i=0; inVars()); + + solver->addClause(c); + + clause_counter++; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt satcheck_minisat_baset::prop_solve() +{ + assert(status!=ERROR); + + { + std::string msg= + i2string(_no_variables)+" variables, "+ + i2string(solver->nClauses())+" clauses"; + messaget::status(msg); + } + + add_variables(); + + std::string msg; + + if(empty_clause_added) + { + msg="empty clause: negated claim is UNSATISFIABLE, i.e., holds"; + messaget::status(msg); + } + else if(!solver->okay()) + { + msg="SAT checker inconsistent: negated claim is UNSATISFIABLE, i.e., holds"; + messaget::status(msg); + } + else + { + Minisat::vec MiniSat_assumptions; + convert(assumptions, MiniSat_assumptions); + + if(solver->solve(MiniSat_assumptions)) + { + msg="SAT checker: negated claim is SATISFIABLE, i.e., does not hold"; + messaget::status(msg); + assert(solver->model.size()!=0); + status=SAT; + return P_SATISFIABLE; + } + else + { + msg="SAT checker: negated claim is UNSATISFIABLE, i.e., holds"; + messaget::status(msg); + } + } + + status=UNSAT; + return P_UNSATISFIABLE; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::set_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_minisat_baset::set_assignment(literalt a, bool value) +{ + assert(!a.is_constant()); + + unsigned v=a.var_no(); + bool sign=a.sign(); + + // MiniSat2 kills the model in case of UNSAT + solver->model.growTo(v+1); + value^=sign; + solver->model[v]=Minisat::lbool(value); +} + +/*******************************************************************\ + +Function: satcheck_minisatt::satcheck_minisatt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisatt::satcheck_minisatt() +{ + solver=new Minisat::Solver; +} + +/*******************************************************************\ + +Function: satcheck_minisatt::satcheck_minisat_simpt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisat_simpt::satcheck_minisat_simpt() +{ + solver=new Minisat::SimpSolver; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::~satcheck_minisat_baset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_minisat_baset::~satcheck_minisat_baset() +{ + delete solver; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::is_in_conflict + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool satcheck_minisat_baset::is_in_conflict(literalt a) const +{ + int v=a.var_no(); + + for(int i=0; iconflict.size(); i++) + if(var(solver->conflict[i])==v) + return true; + + return false; +} + +/*******************************************************************\ + +Function: satcheck_minisat_baset::set_assumptions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_minisat_baset::set_assumptions(const bvt &bv) +{ + assumptions=bv; + + for(bvt::const_iterator it=assumptions.begin(); + it!=assumptions.end(); + it++) + assert(!it->is_constant()); +} + diff --git a/src/solvers/sat/satcheck_minisat2.h b/src/solvers/sat/satcheck_minisat2.h new file mode 100644 index 00000000000..ad51395ea4a --- /dev/null +++ b/src/solvers/sat/satcheck_minisat2.h @@ -0,0 +1,67 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SATCHECK_MINISAT2_H +#define CPROVER_SATCHECK_MINISAT2_H + +#include +#include + +#include "cnf.h" + +namespace Minisat +{ + class Solver; +}; + +class satcheck_minisat_baset:public cnf_solvert +{ +public: + satcheck_minisat_baset():solver(NULL) + { + empty_clause_added=false; + } + + virtual ~satcheck_minisat_baset(); + + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + + virtual void lcnf(const bvt &bv); + virtual const std::string solver_text(); + virtual void set_assignment(literalt a, bool value); + + // extra MiniSat feature: solve with assumptions + virtual void set_assumptions(const bvt &_assumptions); + + virtual bool is_in_conflict(literalt a) const; + virtual bool has_set_assumptions() const { return true; } + virtual bool has_is_in_conflict() const { return true; } + +protected: + Minisat::Solver *solver; + void add_variables(); + bvt assumptions; + bool empty_clause_added; +}; + +class satcheck_minisatt:public satcheck_minisat_baset +{ +public: + satcheck_minisatt(); + virtual const std::string solver_text(); +}; + +class satcheck_minisat_simpt:public satcheck_minisat_baset +{ +public: + satcheck_minisat_simpt(); + virtual const std::string solver_text(); +}; + +#endif diff --git a/src/solvers/sat/satcheck_smvsat.cpp b/src/solvers/sat/satcheck_smvsat.cpp new file mode 100644 index 00000000000..7f11f1e73a4 --- /dev/null +++ b/src/solvers/sat/satcheck_smvsat.cpp @@ -0,0 +1,362 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include + +#include "satcheck_smvsat.h" + +#include +#include + +/*******************************************************************\ + +Function: satcheck_smvsatt::satcheck_smvsatt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_smvsatt::satcheck_smvsatt() +{ + satsolver= + sat_instance_new_type(SATSOLVERCORE1, no_variables(), true); + + // now we can do l_const + init_const(); +} + +/*******************************************************************\ + +Function: satcheck_smvsat_coret::satcheck_smvsat_coret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_smvsat_coret::satcheck_smvsat_coret() +{ +} + +/*******************************************************************\ + +Function: satcheck_smvsatt::~satcheck_smvsatt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_smvsatt::~satcheck_smvsatt() +{ +} + +/*******************************************************************\ + +Function: satcheck_smvsatt::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt satcheck_smvsatt::l_get(literalt a) const +{ + assert(status==SAT); + + if(a.is_true()) + return tvt(true); + else if(a.is_false()) + return tvt(false); + + tvt result; + unsigned v=a.var_no(); + + switch(sat_instance_value(satsolver, v)) + { + case 0: result=tvt(false); break; + case 1: result=tvt(true); break; + default: result=tvt(tvt::TV_UNKNOWN); break; + } + + if(a.sign()) result=!result; + + return result; +} + +/*******************************************************************\ + +Function: satcheck_smvsatt::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_smvsatt::solver_text() +{ + return std::string("SMVSAT"); +} + +/*******************************************************************\ + +Function: satcheck_smvsatt::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_smvsatt::lcnf(const bvt &bv) +{ + bvt tmp; + + if(process_clause(bv, tmp)) + return; + + int lits[tmp.size()+1]; + + for(unsigned i=0; iset_clause_partition(i, p); + } + + int output=interpolator_satsolver->interpolate(0, 0); + + build_aig(*interpolator_satsolver, output, dest); + + delete interpolator_satsolver; +} + +/*******************************************************************\ + +Function: satcheck_smvsat_interpolatort::build_aig + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_smvsat_interpolatort::build_aig( + struct interpolator &interpolator_satsolver, + int output, + exprt &dest) +{ + std::stack stack; + + stack.push(entry(output, &dest)); + + while(!stack.empty()) + { + entry x=stack.top(); + stack.pop(); + + bool invert=x.g<0; + int n=invert?-x.g:x.g; + + assert(n!=0); + + exprt &e=*x.e; + + if(n==INT_MAX) + e.make_true(); + else if(n<=satsolver->num_variables()) + { // a SAT variable + e.id("symbol"); + e.set("identifier", n); + } + else + { + e.id("and"); + e.operands().resize(2); + + unsigned g0=interpolator_satsolver.aig_arg(n, 0); + unsigned g1=interpolator_satsolver.aig_arg(n, 1); + + stack.push(entry(g0, &e.op0())); + stack.push(entry(g1, &e.op1())); + } + + if(invert) + e.make_not(); + } +} + diff --git a/src/solvers/sat/satcheck_smvsat.h b/src/solvers/sat/satcheck_smvsat.h new file mode 100644 index 00000000000..8c1466a1939 --- /dev/null +++ b/src/solvers/sat/satcheck_smvsat.h @@ -0,0 +1,88 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SATCHECK_SMVSAT_H +#define CPROVER_SATCHECK_SMVSAT_H + +#include +#include + +#include + +#include "cnf.h" + +class satcheck_smvsatt:public cnf_solvert +{ +public: + satcheck_smvsatt(); + virtual ~satcheck_smvsatt(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + virtual void lcnf(const bvt &bv); + +protected: + struct sat_instance *satsolver; +}; + +class satcheck_smvsat_coret:public satcheck_smvsatt +{ +public: + satcheck_smvsat_coret(); + + virtual resultt prop_solve(); + + bool is_in_core(literalt l) const + { + assert(l.var_no() in_core; +}; + +class satcheck_smvsat_interpolatort:public satcheck_smvsatt +{ +public: + satcheck_smvsat_interpolatort():partition_no(0) + { + } + + void set_partition_no(short p) + { + partition_no=p; + } + + void interpolate(exprt &dest); + +protected: + virtual void lcnf(const bvt &bv); + short partition_no; + + std::vector partition_numbers; + + void build_aig( + struct interpolator &interpolator_satsolver, + int output, + exprt &dest); + + struct entry + { + int g; + exprt *e; + + entry(int _g, exprt *_e):g(_g), e(_e) + { + } + }; + +}; + +#endif diff --git a/src/solvers/sat/satcheck_zchaff.cpp b/src/solvers/sat/satcheck_zchaff.cpp new file mode 100644 index 00000000000..e8237047b5a --- /dev/null +++ b/src/solvers/sat/satcheck_zchaff.cpp @@ -0,0 +1,283 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "satcheck_zchaff.h" + +#include + +//#define DEBUG + +/*******************************************************************\ + +Function: satcheck_zchaff_baset::satcheck_zchaff_baset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_zchaff_baset::satcheck_zchaff_baset(CSolver *_solver):solver(_solver) +{ + status=INIT; + solver->set_randomness(0); + solver->set_variable_number(0); +} + +/*******************************************************************\ + +Function: satcheck_zchaff_baset::~satcheck_zchaff_baset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_zchaff_baset::~satcheck_zchaff_baset() +{ +} + +/*******************************************************************\ + +Function: satcheck_zchaff_baset::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt satcheck_zchaff_baset::l_get(literalt a) const +{ + assert(status==SAT); + + if(a.is_true()) + return tvt(true); + else if(a.is_false()) + return tvt(false); + + tvt result; + + assert(a.var_no()variables().size()); + + switch(solver->variable(a.var_no()).value()) + { + case 0: result=tvt(false); break; + case 1: result=tvt(true); break; + default: result=tvt(tvt::TV_UNKNOWN); break; + } + + if(a.sign()) result=!result; + + return result; +} + +/*******************************************************************\ + +Function: satcheck_zchaff_baset::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_zchaff_baset::solver_text() +{ + return solver->version(); +} + +/*******************************************************************\ + +Function: satcheck_zchaff_baset::copy_cnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_zchaff_baset::copy_cnf() +{ + assert(status==INIT); + + // this can only be called once + solver->set_variable_number(no_variables()); + + for(clausest::const_iterator it=clauses.begin(); + it!=clauses.end(); + it++) + solver->add_orig_clause((int *)&((*it)[0]), it->size()); +} + +/*******************************************************************\ + +Function: satcheck_zchaff_baset::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt satcheck_zchaff_baset::prop_solve() +{ + // this is *not* incremental + assert(status==INIT); + + copy_cnf(); + + { + std::string msg= + i2string(solver->num_variables())+" variables, "+ + i2string(solver->clauses().size())+" clauses"; + messaget::status(msg); + } + + SAT_StatusT result=(SAT_StatusT)solver->solve(); + + { + std::string msg; + + switch(result) + { + case UNSATISFIABLE: + msg="SAT checker: negated claim is UNSATISFIABLE, i.e., holds"; + break; + + case SATISFIABLE: + msg="SAT checker: negated claim is SATISFIABLE, i.e., does not hold"; + break; + + case UNDETERMINED: + msg="SAT checker failed: UNDETERMINED"; + break; + + case TIME_OUT: + msg="SAT checker failed: Time out"; + break; + + case MEM_OUT: + msg="SAT checker failed: Out of memory"; + break; + + case ABORTED: + msg="SAT checker failed: ABORTED"; + break; + + default: + msg="SAT checker failed: unknown result"; + break; + } + + messaget::status(msg); + } + + if(result==SATISFIABLE) + { + // see if it is complete + for(unsigned i=1; ivariables().size(); i++) + assert(solver->variables()[i].value()==0 || + solver->variables()[i].value()==1); + } + + #ifdef DEBUG + if(result==SATISFIABLE) + { + for(unsigned i=2; i<(_no_variables*2); i+=2) + cout << "DEBUG L" << i << ":" << get(i) << endl; + } + #endif + + if(result==UNSATISFIABLE) + { + status=UNSAT; + return P_UNSATISFIABLE; + } + + if(result==SATISFIABLE) + { + status=SAT; + return P_SATISFIABLE; + } + + status=ERROR; + + return P_ERROR; +} + +/*******************************************************************\ + +Function: satcheck_zchaff_baset::set_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void satcheck_zchaff_baset::set_assignment(literalt a, bool value) +{ + unsigned v=a.var_no(); + bool sign=a.sign(); + value^=sign; + solver->variables()[v].set_value(value); +} + +/*******************************************************************\ + +Function: satcheck_zchafft::satcheck_zchafft + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_zchafft::satcheck_zchafft(): + satcheck_zchaff_baset(new CSolver) +{ +} + +/*******************************************************************\ + +Function: satcheck_zchafft::~satcheck_zchafft + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_zchafft::~satcheck_zchafft() +{ + delete solver; +} diff --git a/src/solvers/sat/satcheck_zchaff.h b/src/solvers/sat/satcheck_zchaff.h new file mode 100644 index 00000000000..04412cb9e19 --- /dev/null +++ b/src/solvers/sat/satcheck_zchaff.h @@ -0,0 +1,49 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SATCHECK_ZCHAFF_H +#define CPROVER_SATCHECK_ZCHAFF_H + +#include "cnf_clause_list.h" + +// use this only if you want to have something +// derived from CSolver +// otherwise, use satcheck_zchafft + +class satcheck_zchaff_baset:public cnf_clause_listt +{ +public: + satcheck_zchaff_baset(class CSolver *_solver); + virtual ~satcheck_zchaff_baset(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + virtual void set_assignment(literalt a, bool value); + virtual void copy_cnf(); + + class CSolver *zchaff_solver() + { + return solver; + } + +protected: + class CSolver *solver; + + typedef enum { INIT, SAT, UNSAT, ERROR } statust; + statust status; +}; + +class satcheck_zchafft:public satcheck_zchaff_baset +{ + public: + satcheck_zchafft(); + virtual ~satcheck_zchafft(); +}; + +#endif diff --git a/src/solvers/sat/satcheck_zcore.cpp b/src/solvers/sat/satcheck_zcore.cpp new file mode 100644 index 00000000000..72e040d2903 --- /dev/null +++ b/src/solvers/sat/satcheck_zcore.cpp @@ -0,0 +1,166 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include +#include + +#include "satcheck_zcore.h" + +/*******************************************************************\ + +Function: satcheck_zcoret::satcheck_zcoret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_zcoret::satcheck_zcoret() +{ +} + +/*******************************************************************\ + +Function: satcheck_zcoret::~satcheck_zcoret + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +satcheck_zcoret::~satcheck_zcoret() +{ +} + +/*******************************************************************\ + +Function: satcheck_zcoret::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt satcheck_zcoret::l_get(literalt a) const +{ + assert(false); + return tvt(tvt::TV_UNKNOWN); +} + +/*******************************************************************\ + +Function: satcheck_zcoret::solver_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::string satcheck_zcoret::solver_text() +{ + return "ZCore"; +} + +/*******************************************************************\ + +Function: satcheck_zcoret::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt satcheck_zcoret::prop_solve() +{ + { + std::string msg= + i2string(no_variables())+" variables, "+ + i2string(no_clauses())+" clauses"; + messaget::status(msg); + } + + // get the core + std::string cnf_file="cnf.dimacs"; + std::string core_file="unsat_core.cnf"; + std::string trace_file="resolve_trace"; + std::string output_file="cnf.out"; + + { + std::ofstream out(cnf_file.c_str(), std::ios::out); + write_dimacs_cnf(out); + } + + // generate resolve_trace + system(std::string("zchaff_verify "+cnf_file+" > "+output_file).c_str()); + + // get core + system(std::string("zcore "+cnf_file+" "+trace_file+" >> "+output_file).c_str()); + + in_core.clear(); + + // read result + { + std::ifstream in(core_file.c_str()); + + while(true) + { + std::string line; + if(!str_getline(in, line)) break; + + if(!(line.substr(0,1)=="c" || line.substr(0,1)=="p")) + { + const char *p=line.c_str(); + + while(true) + { + int l=atoi(p); + if(l==0) break; + + if(l<0) l=-l; + + in_core.insert(l); + + // next one + const char *q=strchr(p, ' '); + while(*q==' ') q++; + if(q==NULL) break; + p=q; + } + } + } + } + + if(in_core.empty()) + return P_ERROR; + + remove(cnf_file.c_str()); + //remove(core_file.c_str()); + remove(trace_file.c_str()); + //remove(output_file.c_str()); + + return P_UNSATISFIABLE; +} diff --git a/src/solvers/sat/satcheck_zcore.h b/src/solvers/sat/satcheck_zcore.h new file mode 100644 index 00000000000..d0b1bff234d --- /dev/null +++ b/src/solvers/sat/satcheck_zcore.h @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SATCHECK_ZCORE_H +#define CPROVER_SATCHECK_ZCORE_H + +#include + +#include "dimacs_cnf.h" + +class satcheck_zcoret:public dimacs_cnft +{ +public: + satcheck_zcoret(); + virtual ~satcheck_zcoret(); + + virtual const std::string solver_text(); + virtual resultt prop_solve(); + virtual tvt l_get(literalt a) const; + + bool is_in_core(literalt l) const + { + return in_core.find(l.var_no())!=in_core.end(); + } + +protected: + std::set in_core; +}; + +#endif diff --git a/src/solvers/smt1/smt1_conv.cpp b/src/solvers/smt1/smt1_conv.cpp new file mode 100644 index 00000000000..e0c5a23753c --- /dev/null +++ b/src/solvers/smt1/smt1_conv.cpp @@ -0,0 +1,3019 @@ +/*******************************************************************\ + +Module: SMT Backend + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "smt1_conv.h" + +/*******************************************************************\ + +Function: smt1_convt::dec_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt smt1_convt::dec_solve() +{ + smt1_prop.finalize(); + return decision_proceduret::D_ERROR; +} + +/*******************************************************************\ + +Function: smt1_convt::get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt smt1_convt::get(const exprt &expr) const +{ + if(expr.id()==ID_symbol) + { + const irep_idt &id=to_symbol_expr(expr).get_identifier(); + + identifier_mapt::const_iterator it=identifier_map.find(id); + + if(it!=identifier_map.end()) + return it->second.value; + } + + return static_cast(get_nil_irep()); +} + +/*******************************************************************\ + +Function: smt1_convt::set_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::set_value( + identifiert &identifier, + const std::string &v) +{ + identifier.value.make_nil(); + + const typet &type=ns.follow(identifier.type); + + if(type.id()==ID_signedbv || + type.id()==ID_unsignedbv || + type.id()==ID_bv || + type.id()==ID_fixedbv) + { + assert(v.size()==bv_width(type)); + constant_exprt c(type); + c.set_value(v); + identifier.value=c; + } + else if(type.id()==ID_bool) + { + if(v=="1") + identifier.value.make_true(); + else if(v=="0") + identifier.value.make_false(); + } + else if(type.id()==ID_pointer) + { + assert(v.size()==BV_ADDR_BITS+config.ansi_c.pointer_width); + + pointer_logict::pointert p; + p.object=integer2long(binary2integer(std::string(v, 0, BV_ADDR_BITS), false)); + p.offset=binary2integer(std::string(v, BV_ADDR_BITS, std::string::npos), true); + + identifier.value=pointer_logic.pointer_expr(p, type); + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + identifier.value=binary2struct(to_struct_union_type(type), v); + } +} + +/*******************************************************************\ + +Function: smt1_convt::array_index_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet smt1_convt::array_index_type() const +{ + signedbv_typet t; + t.set_width(array_index_bits); + return t; +} + +/*******************************************************************\ + +Function: smt1_convt::array_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::array_index(const exprt &expr) +{ + if(expr.type().id()==ID_integer) return convert_expr(expr, true); + + typet t=array_index_type(); + if(t==expr.type()) return convert_expr(expr, true); + exprt tmp("typecast", t); + tmp.copy_to_operands(expr); + convert_expr(tmp, true); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_address_of_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_address_of_rec(const exprt &expr) +{ + if(expr.id()==ID_symbol || + expr.id()==ID_constant || + expr.id()==ID_string_constant) + { + smt1_prop.out + << "(concat bv" + << pointer_logic.add_object(expr) + << "[" << BV_ADDR_BITS << "]" + << " bv0[" << config.ansi_c.pointer_width << "])"; + } + else if(expr.id()==ID_index) + { + if(expr.operands().size()!=2) + throw "index takes two operands"; + + const exprt &array=to_index_expr(expr).array(); + const exprt &index=to_index_expr(expr).index(); + + if(index.is_zero()) + { + if(array.type().id()==ID_pointer) + convert_expr(array, true); + else if(array.type().id()==ID_array) + convert_address_of_rec(array); + else + assert(false); + } + else + { + // this is really pointer arithmetic + exprt new_index_expr=expr; + new_index_expr.op1()=gen_zero(index.type()); + + exprt address_of_expr("address_of", pointer_typet()); + address_of_expr.type().subtype()=array.type().subtype(); + address_of_expr.copy_to_operands(new_index_expr); + + exprt plus_expr("+", address_of_expr.type()); + plus_expr.copy_to_operands(address_of_expr, index); + + convert_expr(plus_expr, true); + } + } + else if(expr.id()==ID_member) + { + if(expr.operands().size()!=1) + throw "member takes one operand"; + + const member_exprt &member_expr=to_member_expr(expr); + + const exprt &struct_op=member_expr.struct_op(); + const typet &struct_op_type=ns.follow(struct_op.type()); + + if(struct_op_type.id()==ID_struct) + { + const struct_typet &struct_type= + to_struct_type(struct_op_type); + + const irep_idt &component_name= + member_expr.get_component_name(); + + mp_integer offset=member_offset(ns, struct_type, component_name); + + typet index_type("unsignedbv"); + index_type.set("width", config.ansi_c.pointer_width); + + smt1_prop.out << "(bvadd "; + convert_address_of_rec(struct_op); + smt1_prop.out << "(zero_extend[" << BV_ADDR_BITS << "] "; + convert_expr(from_integer(offset, index_type), true); + smt1_prop.out << "))"; + } + else + throw "unexpected type of member operand"; + + } + else + throw "don't know how to take address of: "+expr.id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_byte_extract + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_byte_extract(const exprt &expr) +{ + mp_integer i; + if(to_integer(expr.op1(), i)) + throw "byte_extract takes constant as second parameter"; + + unsigned w=boolbv_width(expr.op0().type()); + + if(w==0) + throw "failed to get width of byte_extract operand"; + + smt1_prop.out << ""; + + mp_integer upper, lower; + + if(expr.id()==ID_byte_extract_little_endian) + { + upper = ((i+1)*8)-1; + lower = i*8; + } + else + { + mp_integer max=w-1; + upper = max-(i*8); + lower = max-((i+1)*8-1); + } + + smt1_prop.out << "(extract[" << upper << ":" << lower << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; +} + +/*******************************************************************\ + +Function: smt1_convt::convert_byte_update + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_byte_update(const exprt &expr) +{ + assert(expr.operands().size()==3); + + // The situation: expr.op0 needs to be split in 3 parts + // |<--- L --->|<--- M --->|<--- R --->| + // where M is the expr.op1'th byte + // We need to output L expr.op2 R + + mp_integer i; + if(to_integer(expr.op1(), i)) + throw "byte_extract takes constant as second parameter"; + + unsigned w=boolbv_width(expr.op0().type()); + + if(w==0) + throw "failed to get width of byte_extract operand"; + + mp_integer upper, lower; // of the byte + mp_integer max=w-1; + if(expr.id()==ID_byte_update_little_endian) + { + upper = ((i+1)*8)-1; + lower = i*8; + } + else + { + upper = max-(i*8); + lower = max-((i+1)*8-1); + } + + if(upper==max) + { + if(lower==0) // there was only one byte + convert_expr(expr.op2(), true); + else // uppermost byte selected, only R needed + { + smt1_prop.out << "(concat "; + convert_expr(expr.op2(), true); + smt1_prop.out << " (extract[" << lower-1 << ":0] "; + convert_expr(expr.op0(), true); + smt1_prop.out << "))"; + } + } + else + { + if(lower==0) // lowermost byte selected, only L needed + { + smt1_prop.out << "(concat "; + smt1_prop.out << "(extract[" << max << ":" << (upper+1) << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ") "; + convert_expr(expr.op2(), true); + smt1_prop.out << ")"; + } + else // byte in the middle selected, L & R needed + { + smt1_prop.out << "(concat (concat "; + smt1_prop.out << "(extract[" << max << ":" << (upper+1) << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ") "; + convert_expr(expr.op2(), true); + smt1_prop.out << ") (extract[" << (lower-1) << ":0] "; + convert_expr(expr.op0(), true); + smt1_prop.out << "))"; + } + } + +} + +/*******************************************************************\ + +Function: smt1_convt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt smt1_convt::convert(const exprt &expr) +{ + assert(expr.type().id()==ID_bool); + + if(expr.is_true()) + return const_literal(true); + else if(expr.is_false()) + return const_literal(false); + + smt1_prop.out << std::endl; + + find_symbols(expr); + + literalt l=smt1_prop.new_variable(); + smt1_prop.out << ":assumption ; convert " << std::endl + << " (iff " << smt1_prop.smt1_literal(l) << " "; + convert_expr(expr, false); + smt1_prop.out << ")" << std::endl; + + return l; +} + +/*******************************************************************\ + +Function: smt1_convt::convert_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string smt1_convt::convert_identifier(const irep_idt &identifier) +{ + std::string s=id2string(identifier), dest; + dest.reserve(s.size()); + + for(std::string::const_iterator + it=s.begin(); + it!=s.end(); + it++) + { + char ch=*it; + + if(isalnum(ch) || ch=='.' || ch=='_') + dest+=ch; + else if(ch==':') + { + std::string::const_iterator next_it(it); + next_it++; + if(next_it!=s.end() && *next_it==':') + { + dest.append("''"); + it=next_it; + } + else + { + dest+='\''; + dest.append(i2string(ch)); + dest+='\''; + } + } + else + { + dest+='\''; + dest.append(i2string(ch)); + dest+='\''; + } + } + + return dest; +} + +/*******************************************************************\ + +Function: smt1_convt::convert_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_expr(const exprt &expr, bool bool_as_bv) +{ + if(expr.id()==ID_symbol) + { + irep_idt id=to_symbol_expr(expr).get_identifier(); + assert(id!=""); + + // boolean symbols may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + smt1_prop.out << convert_identifier(id); + + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()==ID_nondet_symbol) + { + irep_idt id=expr.get(ID_identifier); + assert(id!=""); + + // boolean symbols may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + smt1_prop.out << "nondet_" << convert_identifier(id); + + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()==ID_typecast) + { + convert_typecast(to_typecast_expr(expr), bool_as_bv); + } + else if(expr.id()==ID_struct) + { + convert_struct(expr); + } + else if(expr.id()==ID_union) + { + convert_union(expr); + } + else if(expr.id()==ID_constant) + { + convert_constant(to_constant_expr(expr), bool_as_bv); + } + else if(expr.id()==ID_concatenation || + expr.id()==ID_bitand || + expr.id()==ID_bitor || + expr.id()==ID_bitxor || + expr.id()==ID_bitnand || + expr.id()==ID_bitnor) + { + assert(expr.operands().size()>=2); + + smt1_prop.out << "("; + + if(expr.id()==ID_concatenation) + smt1_prop.out << "concat"; + else if(expr.id()==ID_bitand) + smt1_prop.out << "bvand"; + else if(expr.id()==ID_bitor) + smt1_prop.out << "bvor"; + else if(expr.id()==ID_bitxor) + smt1_prop.out << "bvxor"; + else if(expr.id()==ID_bitnand) + smt1_prop.out << "bvnand"; + else if(expr.id()==ID_bitnor) + smt1_prop.out << "bvnor"; + + forall_operands(it, expr) + { + smt1_prop.out << " "; + convert_expr(*it, true); + } + + smt1_prop.out << ")"; + } + else if(expr.id()==ID_bitnot) + { + assert(expr.operands().size()==1); + smt1_prop.out << "(bvnot "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + } + else if(expr.id()==ID_unary_minus) + { + assert(expr.operands().size()==1); + + if(expr.type().id()==ID_rational) + { + smt1_prop.out << "(- "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + } + else if(expr.type().id()==ID_integer) + { + smt1_prop.out << "(~ "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + } + else + { + smt1_prop.out << "(bvneg "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + } + } + else if(expr.id()==ID_if) + { + assert(expr.operands().size()==3); + + // The SMTLIB standard requires a different operator in a boolean context + if(expr.op1().type().id()==ID_bool && !bool_as_bv) + smt1_prop.out << "(if_then_else "; + else + smt1_prop.out << "(ite "; + + convert_expr(expr.op0(), false); + smt1_prop.out << " "; + convert_expr(expr.op1(), bool_as_bv); + smt1_prop.out << " "; + convert_expr(expr.op2(), bool_as_bv); + smt1_prop.out << ")"; + } + else if(expr.id()==ID_and || + expr.id()==ID_or || + expr.id()==ID_xor) + { + assert(expr.type().id()==ID_bool); + assert(expr.operands().size()>=2); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + if(expr.operands().size()>=2) + { + smt1_prop.out << "(" << expr.id(); + forall_operands(it, expr) + { + smt1_prop.out << " "; + convert_expr(*it, false); + } + smt1_prop.out << ")"; + } + else + assert(false); + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()==ID_implies) + { + assert(expr.type().id()==ID_bool); + assert(expr.operands().size()==2); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + smt1_prop.out << "(implies "; + convert_expr(expr.op0(), false); + smt1_prop.out << " "; + convert_expr(expr.op1(), false); + smt1_prop.out << ")"; + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()==ID_not) + { + assert(expr.operands().size()==1); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + smt1_prop.out << "(not "; + convert_expr(expr.op0(), false); + smt1_prop.out << ")"; + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()==ID_equal || + expr.id()==ID_notequal) + { + assert(expr.operands().size()==2); + assert(expr.op0().type()==expr.op1().type()); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + if(expr.op0().type().id()==ID_bool) + { + if(expr.id()==ID_notequal) + smt1_prop.out << "(xor "; + else + smt1_prop.out << "(iff "; + + convert_expr(expr.op0(), false); + smt1_prop.out << " "; + convert_expr(expr.op1(), false); + smt1_prop.out << ")"; + } + else + { + if(expr.id()==ID_notequal) + { + smt1_prop.out << "(not (= "; + convert_expr(expr.op0(), true); + smt1_prop.out << " "; + convert_expr(expr.op1(), true); + smt1_prop.out << "))"; + } + else + { + smt1_prop.out << "(= "; + convert_expr(expr.op0(), true); + smt1_prop.out << " "; + convert_expr(expr.op1(), true); + smt1_prop.out << ")"; + } + } + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()==ID_le || + expr.id()==ID_lt || + expr.id()==ID_ge || + expr.id()==ID_gt) + { + convert_relation(expr, bool_as_bv); + } + else if(expr.id()==ID_plus) + { + convert_plus(expr); + } + else if(expr.id()==ID_minus) + { + convert_minus(expr); + } + else if(expr.id()==ID_div) + { + convert_div(expr); + } + else if(expr.id()==ID_mod) + { + convert_mod(expr); + } + else if(expr.id()==ID_mult) + { + convert_mul(expr); + } + else if(expr.id()==ID_address_of || + expr.id()=="implicit_address_of" || + expr.id()=="reference_to") + { + assert(expr.operands().size()==1); + assert(expr.type().id()==ID_pointer); + convert_address_of_rec(expr.op0()); + } + else if(expr.id()==ID_array_of) + { + assert(expr.type().id()==ID_array); + assert(expr.operands().size()==1); + + // const array_typet &array_type=to_array_type(expr.type()); + + // not really there in SMT, so we replace it + // this is an over-approximation + array_of_mapt::const_iterator it=array_of_map.find(expr); + assert(it!=array_of_map.end()); + + smt1_prop.out << it->second; + } + else if(expr.id()==ID_index) + { + convert_index(to_index_expr(expr), bool_as_bv); + } + else if(expr.id()==ID_ashr || + expr.id()==ID_lshr || + expr.id()==ID_shl) + { + assert(expr.operands().size()==2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv || + expr.type().id()==ID_bv) + { + if(expr.id()==ID_ashr) + smt1_prop.out << "(bvashr "; + else if(expr.id()==ID_lshr) + smt1_prop.out << "(bvlshr "; + else if(expr.id()==ID_shl) + smt1_prop.out << "(bvshl "; + else + assert(false); + + convert_expr(expr.op0(), true); + smt1_prop.out << " "; + convert_expr(expr.op1(), true); + smt1_prop.out << ")"; + } + else + throw "unsupported type for "+expr.id_string()+ + ": "+expr.type().id_string(); + } + else if(expr.id()==ID_with) + { + convert_with(expr); + } + else if(expr.id()==ID_member) + { + convert_member(to_member_expr(expr), bool_as_bv); + } + else if(expr.id()==ID_pointer_offset) + { + assert(expr.operands().size()==1); + assert(expr.op0().type().id()==ID_pointer); + smt1_prop.out << "(extract[" + << (config.ansi_c.pointer_width-1) + << ":0] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + assert(bv_width(expr.type())==config.ansi_c.pointer_width); + } + else if(expr.id()==ID_pointer_object) + { + assert(expr.operands().size()==1); + assert(expr.op0().type().id()==ID_pointer); + unsigned ext=bv_width(expr.type())-BV_ADDR_BITS; + + if(ext>0) + smt1_prop.out << "(zero_extend[" << ext << "] "; + + smt1_prop.out << "(extract[" + << (config.ansi_c.pointer_width+BV_ADDR_BITS-1) + << ":" + << config.ansi_c.pointer_width << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + + if(ext>0) smt1_prop.out << ")"; + } + else if(expr.id()=="same-object") + { + assert(expr.operands().size()==2); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + smt1_prop.out << "(= (extract[" + << (config.ansi_c.pointer_width+BV_ADDR_BITS-1) + << ":" + << config.ansi_c.pointer_width << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + smt1_prop.out << " (extract[" + << (config.ansi_c.pointer_width+BV_ADDR_BITS-1) + << ":" + << config.ansi_c.pointer_width << "] "; + convert_expr(expr.op1(), true); + smt1_prop.out << "))"; + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()=="is_dynamic_object") + { + convert_is_dynamic_object(expr, bool_as_bv); + } + else if(expr.id()=="invalid-pointer") + { + assert(expr.operands().size()==1); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + smt1_prop.out << "(= (extract[" + << (config.ansi_c.pointer_width+BV_ADDR_BITS-1) + << ":" << config.ansi_c.pointer_width << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ") bv" << pointer_logic.get_invalid_object() + << "[" << BV_ADDR_BITS << "])"; + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()=="pointer_object_has_type") + { + assert(expr.operands().size()==1); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + smt1_prop.out << "false"; // TODO + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); + } + else if(expr.id()==ID_string_constant) + { + exprt tmp; + string2array_mapt::const_iterator fit=string2array_map.find(expr); + assert(fit!=string2array_map.end()); + + convert_expr(fit->second, true); + } + else if(expr.id()==ID_extractbit) + { + assert(expr.operands().size()==2); + + if(expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv) + { + // this may have to be converted + from_bv_begin(expr.type(), bool_as_bv); + + if(expr.op1().is_constant()) + { + mp_integer i; + if(to_integer(expr.op1(), i)) + throw "extractbit: to_integer failed"; + + smt1_prop.out << "(extract[" << i << ":" << i << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + } + else + { + smt1_prop.out << "(extract[0:0] "; + // the arguments of the shift need to have the same width + smt1_prop.out << "(bvlshr "; + convert_expr(expr.op0(), true); + typecast_exprt tmp(expr.op0().type()); + tmp.op0()=expr.op1(); + convert_expr(tmp, true); + smt1_prop.out << "))"; // bvlshr, extract + } + + // this may have to be converted + from_bv_end(expr.type(), bool_as_bv); + } + else + throw "unsupported type for "+expr.id_string()+ + ": "+expr.op0().type().id_string(); + } + else if(expr.id()==ID_replication) + { + assert(expr.operands().size()==2); + + mp_integer times; + if(to_integer(expr.op0(), times)) + throw "replication takes constant as first parameter"; + + smt1_prop.out << "(repeat[" << times << "] "; + convert_expr(expr.op1(), true); // this ensures we have a vector + smt1_prop.out << ")"; + } + else if(expr.id()==ID_byte_extract_little_endian || + expr.id()==ID_byte_extract_big_endian) + { + convert_byte_extract(expr); + } + else if(expr.id()==ID_byte_update_little_endian || + expr.id()==ID_byte_update_big_endian) + { + convert_byte_update(expr); + } + else if(expr.id()==ID_width) + { + unsigned result_width=boolbv_width(expr.type()); + + if(result_width==0) + throw "conversion failed"; + + if(expr.operands().size()!=1) + throw "width expects 1 operand"; + + unsigned op_width=boolbv_width(expr.op0().type()); + + if(op_width==0) + throw "conversion failed"; + + smt1_prop.out << "bv" << op_width/8 << "[" << result_width << "]"; + } + else if(expr.id()==ID_abs) + { + assert(expr.operands().size()==1); + + unsigned result_width=boolbv_width(expr.type()); + + if(result_width==0) + throw "conversion failed"; + + const typet &type=expr.type(); + + if(type.id()==ID_signedbv || + type.id()==ID_fixedbv) + { + smt1_prop.out << "(ite (bvslt "; + convert_expr(expr.op0(), true); + smt1_prop.out << " bv0[" << result_width << "]) "; + smt1_prop.out << "(bvneg "; + convert_expr(expr.op0(), true); + smt1_prop.out << ") "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + } + else if(type.id()==ID_floatbv) + { + smt1_prop.out << "(bvand "; + convert_expr(expr.op0(), true); + smt1_prop.out << " bv" + << (power(2, result_width-1)-1) + << "[" << result_width << "])"; + } + else + throw "abs with unsupported operand type"; + } + else if(expr.id()==ID_isnan) + { + assert(expr.operands().size()==1); + + const typet &op_type=expr.op0().type(); + + if(op_type.id()==ID_fixedbv) + { + from_bool_begin(expr.type(), bool_as_bv); + smt1_prop.out << "false"; + from_bool_end(expr.type(), bool_as_bv); + } + else + throw "isnan with unsupported operand type"; + } + else if(expr.id()==ID_isfinite) + { + if(expr.operands().size()!=1) + throw "isfinite expects one operand"; + + const typet &op_type=expr.op0().type(); + + if(op_type.id()==ID_fixedbv) + { + from_bool_begin(expr.type(), bool_as_bv); + smt1_prop.out << "true"; + from_bool_end(expr.type(), bool_as_bv); + } + else + throw "isfinite with unsupported operand type"; + } + else if(expr.id()==ID_isinf) + { + if(expr.operands().size()!=1) + throw "isinf expects one operand"; + + const typet &op_type=expr.op0().type(); + + if(op_type.id()==ID_fixedbv) + { + from_bool_begin(expr.type(), bool_as_bv); + smt1_prop.out << "false"; + from_bool_end(expr.type(), bool_as_bv); + } + else + throw "isinf with unsupported operand type"; + } + else if(expr.id()==ID_isnormal) + { + if(expr.operands().size()!=1) + throw "isnormal expects one operand"; + + const typet &op_type=expr.op0().type(); + + if(op_type.id()==ID_fixedbv) + { + from_bool_begin(expr.type(), bool_as_bv); + smt1_prop.out << "true"; + from_bool_end(expr.type(), bool_as_bv); + } + else + throw "isnormal with unsupported operand type"; + } + else if(expr.id()=="overflow-+" || + expr.id()=="overflow--") + { + assert(expr.operands().size()==2); + bool subtract=expr.id()=="overflow--"; + + const typet &op_type=expr.op0().type(); + + unsigned width=bv_width(op_type); + + if(op_type.id()==ID_signedbv) + { + // an overflow occurs if the top two bits of the extended sum differ + + from_bool_begin(expr.type(), bool_as_bv); + smt1_prop.out << "(let (?sum ("; + smt1_prop.out << (subtract?"bvsub":"bvadd"); + smt1_prop.out << " (sign_extend[1] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + smt1_prop.out << " (sign_extend[1] "; + convert_expr(expr.op1(), true); + smt1_prop.out << "))) "; // sign_extend, bvadd/sub let2 + smt1_prop.out << "(not (= " + "(extract[" << width << ":" << width << "] ?sum) " + "(extract[" << (width-1) << ":" << (width-1) << "] ?sum)"; + smt1_prop.out << ")))"; // =, not, let + from_bool_end(expr.type(), bool_as_bv); + } + else if(op_type.id()==ID_unsignedbv) + { + // overflow is simply carry-out + from_bv_begin(expr.type(), bool_as_bv); + smt1_prop.out << "(extract[" << width << ":" << width << "] "; + smt1_prop.out << "(" << (subtract?"bvsub":"bvadd"); + smt1_prop.out << " (zero_extend[1] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")"; + smt1_prop.out << " (zero_extend[1] "; + convert_expr(expr.op1(), true); + smt1_prop.out << ")))"; // zero_extend, bvsub/bvadd, extract + from_bv_end(expr.type(), bool_as_bv); + } + else + throw "overflow check on unknown type: "+op_type.id_string(); + } + else if(expr.id()=="overflow-*") + { + assert(expr.operands().size()==2); + throw "not yet implemented: overflow-*"; + } + else if(expr.id()==ID_forall || expr.id()==ID_exists) + { + assert(expr.operands().size()==2); + smt1_prop.out << "(" << expr.id() << " ("; + convert_expr(expr.op0(), bool_as_bv); + smt1_prop.out << " "; + if(expr.op0().type().id()==ID_bool) + smt1_prop.out << "Bool"; + else + convert_type(expr.op0().type()); + smt1_prop.out << ") "; + convert_expr(expr.op1(), bool_as_bv); + smt1_prop.out << ")"; + } + else if(expr.id()==ID_extractbits) + { + assert(expr.operands().size()==3); + + if(expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv || + expr.op0().type().id()==ID_bv || + expr.op0().type().id()==ID_fixedbv) + { + smt1_prop.out << "(extract["; + convert_expr(expr.op1(), bool_as_bv); + smt1_prop.out << ":"; + convert_expr(expr.op2(), bool_as_bv); + smt1_prop.out << "] "; + convert_expr(expr.op0(), bool_as_bv); + smt1_prop.out << ")"; + } + else + throw "unsupported type for "+expr.id_string()+ + ": "+expr.op0().type().id_string(); + } + else + throw "smt1_convt::convert_expr: `"+ + expr.id_string()+"' is unsupported"; +} + +/*******************************************************************\ + +Function: smt1_convt::convert_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_typecast(const typecast_exprt &expr, bool bool_as_bv) +{ + assert(expr.operands().size()==1); + const exprt &op=expr.op0(); + const typet &expr_type=ns.follow(expr.type()); + const typet &op_type=ns.follow(op.type()); + + if(expr_type.id()==ID_bool) + { + // boolean typecasts may have to be converted + from_bool_begin(expr_type, bool_as_bv); + + // this is comparison with zero + if(op_type.id()==ID_signedbv || + op_type.id()==ID_unsignedbv || + op_type.id()==ID_fixedbv || + op_type.id()==ID_pointer) + { + smt1_prop.out << "(not (= "; + convert_expr(op, true); + smt1_prop.out << " "; + convert_expr(gen_zero(op_type), true); + smt1_prop.out << "))"; + } + else + { + throw "TODO typecast1 "+op_type.id_string()+" -> bool"; + } + + // boolean typecasts may have to be converted + from_bool_end(expr_type, bool_as_bv); + } + else if(expr_type.id()==ID_signedbv || + expr_type.id()==ID_unsignedbv || + expr_type.id()==ID_c_enum) + { + unsigned to_width=bv_width(expr_type); + + if(op_type.id()==ID_signedbv || // from signedbv + op_type.id()==ID_unsignedbv || // from unsigedbv + op_type.id()==ID_c_enum) + { + unsigned from_width=bv_width(op_type); + + if(from_width==to_width) + convert_expr(op, true); // ignore + else if(from_widthfrom_integer_bits) + { + smt1_prop.out << "(sign_extend[" << (to_width-from_integer_bits) << "] "; + smt1_prop.out << "(extract[" << (from_width-1) << ":" + << from_fraction_bits << "] "; + convert_expr(op, true); + smt1_prop.out << "))"; + } + else + { + smt1_prop.out << "(extract[" << (from_fraction_bits+to_width-1) + << ":" << from_fraction_bits << "] "; + convert_expr(op, true); + smt1_prop.out << ")"; + } + } + else if(op_type.id()==ID_bool) // from boolean + { + smt1_prop.out << "(ite "; + convert_expr(op, false); + + if(expr_type.id()==ID_fixedbv) + { + fixedbvt fbt(expr); + smt1_prop.out << " (concat bv1[" << fbt.spec.integer_bits << "] " << + "bv0[" << fbt.spec.get_fraction_bits() << "]) " << + "bv0[" << fbt.spec.width << "]"; + } + else + { + smt1_prop.out << " bv1[" << to_width << "]"; + smt1_prop.out << " bv0[" << to_width << "]"; + } + + smt1_prop.out << ")"; + } + else if(op_type.id()==ID_pointer) // from pointer to int + { + unsigned from_width=config.ansi_c.pointer_width; + + if(from_width "+expr_type.id_string(); + } + } + else if(expr_type.id()==ID_fixedbv) // to fixedbv + { + const fixedbv_typet &fixedbv_type=to_fixedbv_type(expr_type); + unsigned to_fraction_bits=fixedbv_type.get_fraction_bits(); + unsigned to_integer_bits=fixedbv_type.get_integer_bits(); + + if(op_type.id()==ID_unsignedbv || + op_type.id()==ID_signedbv || + op_type.id()==ID_c_enum) + { + unsigned from_width=to_bitvector_type(op_type).get_width(); + smt1_prop.out << "(concat "; + + if(from_width==to_integer_bits) + convert_expr(op, true); + else if(from_width>to_integer_bits) + { + smt1_prop.out << "(extract[" << (to_integer_bits-1) << ":" + << to_fraction_bits << "] "; + convert_expr(op, true); + smt1_prop.out << ")"; + } + else + { + assert(from_widthfrom_integer_bits); + smt1_prop.out << "(sign_extend[" + << (to_integer_bits-from_integer_bits) + << "] (extract[" + << (from_width-1) << ":" + << from_fraction_bits + << "] "; + convert_expr(op, true); + smt1_prop.out << "))"; + } + + smt1_prop.out << " "; + + if(to_fraction_bits<=from_fraction_bits) + { + smt1_prop.out << "(extract[" + << (from_fraction_bits-1) << ":" + << (from_fraction_bits-to_fraction_bits) + << "] "; + convert_expr(op, true); + smt1_prop.out << ")"; + } + else + { + assert(to_fraction_bits>from_fraction_bits); + smt1_prop.out << "(concat (extract[" + << (from_fraction_bits-1) << ":0] "; + convert_expr(op, true); + smt1_prop.out << ")" + << " bv0[" << to_fraction_bits-from_fraction_bits + << "])"; + } + + smt1_prop.out << ")"; // concat + } + else + throw "unexpected typecast to fixedbv"; + } + else if(expr_type.id()==ID_pointer) + { + if(op_type.id()==ID_pointer) + { + // this just passes through + convert_expr(op, true); + } + else if(op_type.id()==ID_unsignedbv || + op_type.id()==ID_signedbv) + { + unsigned from_width=bv_width(op_type); + smt1_prop.out << "(concat" + << " bv" << pointer_logic.get_null_object() + << "[" << BV_ADDR_BITS << "] "; + + if(from_width==config.ansi_c.pointer_width) + convert_expr(op, true); + else if(from_widthconfig.ansi_c.pointer_width + { + smt1_prop.out << "(extract[" + << config.ansi_c.pointer_width + << ":0] "; + convert_expr(op, true); + smt1_prop.out << ")"; // extract + } + + smt1_prop.out << ")"; // concat + } + else + throw "TODO typecast3 "+op_type.id_string()+" -> pointer"; + } + else if(expr_type.id()==ID_range) + { + throw "TODO range typecast"; + } + else + throw "TODO typecast4 ? -> "+expr_type.id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_struct + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_struct(const exprt &expr) +{ + const struct_typet &struct_type=to_struct_type(expr.type()); + + const struct_typet::componentst &components= + struct_type.components(); + + assert(components.size()==expr.operands().size()); + + assert(!components.empty()); + + if(components.size()==1) + convert_expr(expr.op0(), true); + else + { + unsigned nr_ops=0; + + for(unsigned i=0; imember_width); + smt1_prop.out << "(concat "; + smt1_prop.out << "bv0[" << (total_width-member_width) << "] "; + convert_expr(op, true); + smt1_prop.out << ")"; + } +} + +/*******************************************************************\ + +Function: smt1_convt::convert_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_constant(const constant_exprt &expr, bool bool_as_bv) +{ + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv || + expr.type().id()==ID_bv || + expr.type().id()==ID_c_enum) + { + mp_integer value; + + if(to_integer(expr, value)) + throw "failed to convert bitvector constant"; + + unsigned width=bv_width(expr.type()); + + if(value<0) value=power(2, width)+value; + + smt1_prop.out << "bv" << value + << "[" << width << "]"; + } + else if(expr.type().id()==ID_fixedbv) + { + fixedbv_spect spec(to_fixedbv_type(expr.type())); + + std::string v_str=id2string(expr.get(ID_value)); + mp_integer v=binary2integer(v_str, false); + + smt1_prop.out << "bv" << v << "[" << spec.width << "]"; + } + else if(expr.type().id()==ID_floatbv) + { + ieee_float_spect spec(to_floatbv_type(expr.type())); + + std::string v_str=id2string(expr.get(ID_value)); + mp_integer v=binary2integer(v_str, false); + + smt1_prop.out << "bv" << v << "[" << spec.width() << "]"; + } + else if(expr.type().id()==ID_pointer) + { + const irep_idt &value=expr.get(ID_value); + + if(value=="NULL") + { + smt1_prop.out << "(concat" + << " bv" << pointer_logic.get_null_object() + << "[" << BV_ADDR_BITS << "]" + << " bv0[" << config.ansi_c.pointer_width + << "])"; + } + else + throw "unknown pointer constant: "+id2string(value); + } + else if(expr.type().id()==ID_bool) + { + if(expr.is_true()) + smt1_prop.out << (bool_as_bv?"bit1":"true"); + else if(expr.is_false()) + smt1_prop.out << (bool_as_bv?"bit0":"false"); + else + throw "unknown boolean constant"; + } + else if(expr.type().id()==ID_array) + { + array_init_mapt::const_iterator it=array_init_map.find(expr); + assert(it!=array_init_map.end()); + + std::string tmp; + tmp = it->second.as_string(); + + assert(expr.operands().size()!=0); + + forall_operands(it, expr) + smt1_prop.out << "(store "; + + smt1_prop.out << it->second; + + unsigned i=0; + forall_operands(it, expr) + { + exprt inx = from_integer(i, unsignedbv_typet(array_index_bits)); + smt1_prop.out << " "; + convert_expr(inx, true); + smt1_prop.out << " "; + convert_expr(*it, true); + smt1_prop.out << ")"; + i++; + } + } + else if(expr.type().id()==ID_rational) + { + std::string value=expr.get(ID_value).as_string(); + size_t pos=value.find("/"); + + if(pos==std::string::npos) + smt1_prop.out << value << ".0"; + else + { + smt1_prop.out << "(/ " << value.substr(0,pos) << ".0 " + << value.substr(pos+1) << ".0)"; + } + } + else if(expr.type().id()==ID_integer || + expr.type().id()==ID_natural) + { + std::string value=expr.get(ID_value).as_string(); + + if(value[0]=='-') + smt1_prop.out << "(~ " << value.substr(1) << ")"; + else + smt1_prop.out << value; + } + else + throw "unknown constant: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_mod + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_mod(const exprt &expr) +{ + assert(expr.operands().size()==2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv) + { + if(expr.type().id()==ID_unsignedbv) + smt1_prop.out << "(bvurem "; + else + smt1_prop.out << "(bvsrem "; + + convert_expr(expr.op0(), true); + smt1_prop.out << " "; + convert_expr(expr.op1(), true); + smt1_prop.out << ")"; + } + else + throw "unsupported type for mod: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_is_dynamic_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_is_dynamic_object(const exprt &expr, bool bool_as_bv) +{ + std::vector dynamic_objects; + pointer_logic.get_dynamic_objects(dynamic_objects); + + assert(expr.operands().size()==1); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + if(dynamic_objects.empty()) + smt1_prop.out << "false"; + else + { + // let is only allowed in formulas + + smt1_prop.out << "(let (?obj (extract[" + << (config.ansi_c.pointer_width+BV_ADDR_BITS-1) + << ":" << config.ansi_c.pointer_width << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ")) "; + + if(dynamic_objects.size()==1) + { + smt1_prop.out << "(= bv" << dynamic_objects.front() + << "[" << BV_ADDR_BITS << "] ?obj)"; + } + else + { + smt1_prop.out << "(or"; + + for(std::vector::const_iterator + it=dynamic_objects.begin(); + it!=dynamic_objects.end(); + it++) + smt1_prop.out << " (= bv" << *it + << "[" << BV_ADDR_BITS << "] ?obj)"; + + smt1_prop.out << ")"; // or + } + + smt1_prop.out << ")"; // let + } + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_relation + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_relation(const exprt &expr, bool bool_as_bv) +{ + assert(expr.operands().size()==2); + + // this may have to be converted + from_bool_begin(expr.type(), bool_as_bv); + + const typet &op_type=expr.op0().type(); + + smt1_prop.out << "("; + + if(op_type.id()==ID_unsignedbv) + { + if(expr.id()==ID_le) + smt1_prop.out << "bvule"; + else if(expr.id()==ID_lt) + smt1_prop.out << "bvult"; + else if(expr.id()==ID_ge) + smt1_prop.out << "bvuge"; + else if(expr.id()==ID_gt) + smt1_prop.out << "bvugt"; + + smt1_prop.out << " "; + convert_expr(expr.op0(), true); + smt1_prop.out << " "; + convert_expr(expr.op1(), true); + } + else if(op_type.id()==ID_signedbv || + op_type.id()==ID_fixedbv) + { + if(expr.id()==ID_le) + smt1_prop.out << "bvsle"; + else if(expr.id()==ID_lt) + smt1_prop.out << "bvslt"; + else if(expr.id()==ID_ge) + smt1_prop.out << "bvsge"; + else if(expr.id()==ID_gt) + smt1_prop.out << "bvsgt"; + + smt1_prop.out << " "; + convert_expr(expr.op0(), true); + smt1_prop.out << " "; + convert_expr(expr.op1(), true); + } + else if(op_type.id()==ID_rational || + op_type.id()==ID_integer) + { + smt1_prop.out << expr.id(); + + smt1_prop.out << " "; + convert_expr(expr.op0(), true); + smt1_prop.out << " "; + convert_expr(expr.op1(), true); + } + else + throw "unsupported type for "+expr.id_string()+ + ": "+op_type.id_string(); + + smt1_prop.out << ")"; + + // this may have to be converted + from_bool_end(expr.type(), bool_as_bv); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_plus + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_plus(const exprt &expr) +{ + assert(expr.operands().size()>=2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv || + expr.type().id()==ID_fixedbv) + { + smt1_prop.out << "(bvadd"; + + forall_operands(it, expr) + { + smt1_prop.out << " "; + convert_expr(*it, true); + } + + smt1_prop.out << ")"; + } + else if(expr.type().id()==ID_pointer) + { + if(expr.operands().size()!=2) + throw "pointer arithmetic with more than two operands"; + + exprt p=expr.op0(), i=expr.op1(); + + if(p.type().id()!="pointer") + p.swap(i); + + if(p.type().id()!="pointer") + throw "unexpected mixture in pointer arithmetic"; + + mp_integer element_size= + pointer_offset_size(ns, expr.type().subtype()); + + smt1_prop.out << "(bvadd "; + convert_expr(p, true); + smt1_prop.out << " "; + smt1_prop.out << "(zero_extend[" << BV_ADDR_BITS << "] "; + + if(element_size>=2) + { + smt1_prop.out << "(bvmul "; + convert_expr(i, true); + smt1_prop.out << " bv" << element_size + << "[" << config.ansi_c.pointer_width << "])"; + } + else + convert_expr(i, true); + + smt1_prop.out << "))"; + } + else if(expr.type().id()==ID_rational || + expr.type().id()==ID_integer) + { + smt1_prop.out << "(+"; + + forall_operands(it, expr) + { + smt1_prop.out << " "; + convert_expr(*it, true); + } + + smt1_prop.out << ")"; + } + else + throw "unsupported type for +: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_minus + + Inputs: + + Outputs: + + Purpose: +e +\*******************************************************************/ + +void smt1_convt::convert_minus(const exprt &expr) +{ + assert(expr.operands().size()==2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv || + expr.type().id()==ID_fixedbv) + { + smt1_prop.out << "(bvsub "; + + if(expr.op0().type().id()==ID_pointer) + smt1_prop.out << "(extract[" << config.ansi_c.pointer_width-1 << ":0] "; + convert_expr(expr.op0(), true); + if(expr.op0().type().id()==ID_pointer) + smt1_prop.out << ")"; + + smt1_prop.out << " "; + + if(expr.op1().type().id()==ID_pointer) + smt1_prop.out << "(extract[" << config.ansi_c.pointer_width-1 << ":0] "; + convert_expr(expr.op1(), true); + if(expr.op1().type().id()==ID_pointer) + smt1_prop.out << ")"; + + smt1_prop.out << ")"; + } + else if(expr.type().id()==ID_pointer) + { + convert_expr(binary_exprt( + expr.op0(), + "+", + unary_minus_exprt(expr.op1(), expr.op1().type()), + expr.type()), + true); + } + else + throw "unsupported type for -: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_div + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_div(const exprt &expr) +{ + assert(expr.operands().size()==2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv) + { + if(expr.type().id()==ID_unsignedbv) + smt1_prop.out << "(bvudiv "; + else + smt1_prop.out << "(bvsdiv "; + + convert_expr(expr.op0(), true); + smt1_prop.out << " "; + convert_expr(expr.op1(), true); + smt1_prop.out << ")"; + } + else if(expr.type().id()==ID_fixedbv) + { + fixedbvt fbt(expr); + unsigned fraction_bits=fbt.spec.get_fraction_bits(); + + smt1_prop.out << "(extract[" << fbt.spec.width-1 << ":0] "; + smt1_prop.out << "(bvsdiv "; + + smt1_prop.out << "(concat "; + convert_expr(expr.op0(), true); + smt1_prop.out << " bv0[" << fraction_bits << "]) "; + + smt1_prop.out << "(sign_extend[" << fraction_bits << "] "; + convert_expr(expr.op1(), true); + smt1_prop.out << ")"; + + smt1_prop.out << "))"; + } + else + throw "unsupported type for /: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_mul + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_mul(const exprt &expr) +{ + assert(expr.operands().size()>=2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv) + { + forall_operands(it, expr) + if(it!=expr.operands().begin()) smt1_prop.out << "(bvmul "; + + exprt::operandst::const_iterator last; + forall_operands(it, expr) + { + if(it!=expr.operands().begin()) + { + convert_expr(*last, true); + smt1_prop.out << " "; + convert_expr(*it, true); + smt1_prop.out << ")"; + } + last = it; + } + } + else if(expr.type().id()==ID_fixedbv) + { + fixedbvt fbt(expr); + unsigned fraction_bits=fbt.spec.get_fraction_bits(); + + smt1_prop.out << "(extract[" << fbt.spec.width+fraction_bits-1 << ":" + << fraction_bits << "] "; + + forall_operands(it, expr) + if(it!=expr.operands().begin()) smt1_prop.out << "(bvmul "; + + exprt::operandst::const_iterator last; + forall_operands(it, expr) + { + smt1_prop.out << "(sign_extend[" << fraction_bits << "] "; + convert_expr(*it, true); + smt1_prop.out << ") "; + + if(it!=expr.operands().begin()) + smt1_prop.out << ")"; + } + + smt1_prop.out << ")"; + } + else if(expr.type().id()==ID_rational) + { + smt1_prop.out << "(*"; + + forall_operands(it, expr) + { + smt1_prop.out << " "; + convert_expr(*it, true); + } + + smt1_prop.out << ")"; + } + else + throw "unsupported type for *: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_with + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_with(const exprt &expr) +{ + assert(expr.operands().size()>=3); + + const typet &expr_type=ns.follow(expr.type()); + + if(expr_type.id()==ID_array) + { + const exprt &array=expr.op0(); + + if(array.id()==ID_member) + { + // arrays in structs are flattened! + const typet &array_type=to_array_type(expr.type()); + const typet &elem_type=array_type.subtype(); + + const member_exprt &member_expr=to_member_expr(array); + const exprt &struct_op=member_expr.struct_op(); + const irep_idt &name=member_expr.get_component_name(); + + unsigned total_width=boolbv_width(struct_op.type()); + + if(total_width==0) + throw "failed to get struct width"; + + unsigned offset=boolbv_width.get_member( + to_struct_type(struct_op.type()), name).offset; + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + throw "failed to get struct member width"; + + unsigned elem_width=boolbv_width(elem_type); + + if(elem_width==0) + throw "failed to get struct width"; + + unsigned array_bits=(offset+width) - offset; + + assert(expr.operands().size()==3); + const exprt &index=expr.operands()[1]; + const exprt &value=expr.operands()[2]; + + smt1_prop.out << "(bvor "; + smt1_prop.out << "(bvand "; + + // this gets us the array + smt1_prop.out << "(extract[" << offset+width-1 << ":" << offset << "] "; + convert_expr(struct_op, true); + smt1_prop.out << ")"; + + // the mask + smt1_prop.out << " (bvneg (bvshl"; + + smt1_prop.out << " (concat"; + smt1_prop.out << " (repeat[" << array_bits-elem_width << "] bv0[1])"; + smt1_prop.out << " (repeat[" << elem_width << "] bv1[1])"; + smt1_prop.out << ")"; // concat + + // shift it to the index + smt1_prop.out << " (zero_extend[" << width-array_index_bits << "]"; + smt1_prop.out << " (bvmul "; + convert_expr(index, true); + smt1_prop.out << " bv" << elem_width << "[" << array_index_bits << "]"; + smt1_prop.out << "))))"; // bvmul, bvshl, bvneg + + smt1_prop.out << ")"; // bvand + + // the new value + smt1_prop.out << " (bvshl (zero_extend[" << array_bits-elem_width << "] "; + convert_expr(value, true); + // shift it to the index + smt1_prop.out << ")"; + smt1_prop.out << " (zero_extend[" << width-array_index_bits << "]"; + smt1_prop.out << " (bvmul "; + convert_expr(index, true); + smt1_prop.out << " bv" << elem_width << "[" << array_index_bits << "]"; + smt1_prop.out << ")))"; // bvmul, bvshl, ze + + smt1_prop.out << ")"; // bvor + } + else + { + smt1_prop.out << "(store "; + + convert_expr(expr.op0(), true); + + for(unsigned i=1; imember_width); + smt1_prop.out << "(concat "; + smt1_prop.out << "(extract[" + << (total_width-1) + << ":" << member_width << "] "; + convert_expr(expr.op0(), true); + smt1_prop.out << ") "; + convert_expr(value, true); + smt1_prop.out << ")"; + } + } + else + { + assert(false); + } + } + else + throw "with expects struct, union, or array type, " + "but got "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_index(const index_exprt &expr, bool bool_as_bv) +{ + assert(expr.operands().size()==2); + + if(expr.array().id()==ID_member) + { + // these were flattened + const typet &array_type=to_array_type(expr.array().type()); + const typet &elem_type=array_type.subtype(); + + const member_exprt &member_expr=to_member_expr(expr.array()); + const exprt &struct_op=member_expr.struct_op(); + //const irep_idt &name=member_expr.get_component_name(); + + unsigned total_width=boolbv_width(struct_op.type()); + + if(total_width==0) + throw "failed to get struct width"; + + //unsigned offset=boolbv_width.get_member( + // to_struct_type(struct_op.type()), name).offset; + + unsigned width=boolbv_width(member_expr.type()); + + if(width==0) + throw "failed to get struct member width"; + + unsigned elem_width=boolbv_width(elem_type); + + if(elem_width==0) + throw "failed to get struct width"; + + smt1_prop.out << "(extract[" << elem_width-1 << ":0] "; + smt1_prop.out << "(bvlshr "; + convert_expr(expr.array(), true); + smt1_prop.out << " (zero_extend[" << width-array_index_bits << "]"; + smt1_prop.out << " (bvmul "; + convert_expr(expr.index(), true); + smt1_prop.out << " bv" << elem_width << "[" << array_index_bits << "]"; + smt1_prop.out << "))))"; + } + else + { + // Booleans out of arrays may have to be converted + from_bv_begin(expr.type(), bool_as_bv); + + smt1_prop.out << "(select "; + convert_expr(expr.array(), true); + smt1_prop.out << " "; + array_index(expr.index()); + smt1_prop.out << ")"; + + // Booleans out of arrays may have to be converted + from_bv_end(expr.type(), bool_as_bv); + } +} + +/*******************************************************************\ + +Function: smt1_convt::convert_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_member(const member_exprt &expr, bool bool_as_bv) +{ + assert(expr.operands().size()==1); + + const member_exprt &member_expr=to_member_expr(expr); + const exprt &struct_op=member_expr.struct_op(); + const typet &struct_op_type=ns.follow(struct_op.type()); + const irep_idt &name=member_expr.get_component_name(); + + // Booleans pulled out of structs may have to be converted + from_bv_begin(expr.type(), bool_as_bv); + + if(struct_op_type.id()==ID_struct) + { + unsigned offset=boolbv_width.get_member( + to_struct_type(struct_op_type), name).offset; + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + throw "failed to get struct member width"; + + smt1_prop.out << "(extract[" + << (offset+width-1) + << ":" + << offset + << "] "; + convert_expr(struct_op, true); + smt1_prop.out << ")"; + } + else if(struct_op_type.id()==ID_union) + { + unsigned width=boolbv_width(expr.type()); + + if(width==0) + throw "failed to get union member width"; + + smt1_prop.out << "(extract[" + << (width-1) + << ":0] "; + convert_expr(struct_op, true); + smt1_prop.out << ")"; + } + else + assert(false); + + // Booleans pulled out of structs may have to be converted + from_bv_end(expr.type(), bool_as_bv); +} + +/*******************************************************************\ + +Function: smt1_convt::convert_overflow + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::convert_overflow(const exprt &expr) +{ +} + +/*******************************************************************\ + +Function: smt1_convt::set_to + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::set_to(const exprt &expr, bool value) +{ + if(expr.id()==ID_and && value) + { + forall_operands(it, expr) + set_to(*it, true); + return; + } + + if(expr.id()==ID_not) + { + assert(expr.operands().size()==1); + return set_to(expr.op0(), !value); + } + + smt1_prop.out << std::endl; + + find_symbols(expr); + + #if 0 + smt1_prop.out << "; CONV: " + << from_expr(expr) << std::endl; + #endif + + smt1_prop.out << ":assumption ; set_to " + << (value?"true":"false") << std::endl + << " "; + + assert(expr.type().id()==ID_bool); + + if(!value) + { + smt1_prop.out << "(not "; + convert_expr(expr, false); + smt1_prop.out << ")"; + } + else + convert_expr(expr, false); + + smt1_prop.out << std::endl; +} + +/*******************************************************************\ + +Function: smt1_convt::find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::find_symbols(const exprt &expr) +{ + find_symbols(expr.type()); + + if(expr.id()==ID_forall || expr.id()==ID_exists) + { + assert(expr.operands().size()==2); + assert(expr.op0().id()==ID_symbol); + + const irep_idt &ident=expr.op0().get(ID_identifier); + quantified_symbols.insert(ident); + find_symbols(expr.op1()); + quantified_symbols.erase(ident); + } + else + forall_operands(it, expr) + find_symbols(*it); + + if(expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol) + { + // we don't track function-typed symbols + if(expr.type().id()==ID_code) + return; + + irep_idt identifier; + + if(expr.id()==ID_symbol) + identifier=to_symbol_expr(expr).get_identifier(); + else + identifier="nondet_"+id2string(expr.get(ID_identifier)); + + if(quantified_symbols.find(identifier)!=quantified_symbols.end()) + return; // Symbol is quantified, i.e., it doesn't require declaration. + + identifiert &id=identifier_map[identifier]; + + if(id.type.is_nil()) + { + id.type=expr.type(); + + if(id.type.id()==ID_bool) + { + smt1_prop.out << ":extrapreds((" + << convert_identifier(identifier) + << "))" << std::endl; + } + else + { + smt1_prop.out << ":extrafuns((" + << convert_identifier(identifier) + << " "; + convert_type(expr.type()); + smt1_prop.out << "))" << std::endl; + } + } + } + else if(expr.id()==ID_array_of) + { + if(array_of_map.find(expr)==array_of_map.end()) + { + irep_idt id="array_of'"+i2string(array_of_map.size()); + smt1_prop.out << "; the following is a poor substitute for lambda i. x" << std::endl; + smt1_prop.out << ":extrafuns((" + << id + << " "; + convert_type(expr.type()); + smt1_prop.out << "))" << std::endl; + + // we can initialize array_ofs if they have + // a constant size and a constant element + if(expr.type().find(ID_size)!=get_nil_irep() && + expr.op0().id()==ID_constant) + { + const array_typet &array_type=to_array_type(expr.type()); + mp_integer size; + + if(!to_integer(array_type.size(), size)) + { + // since we can't use quantifiers, let's enumerate... + for(mp_integer i=0; i rec_stack; + find_symbols_rec(type, rec_stack); +} + +/*******************************************************************\ + +Function: smt1_convt::find_symbols_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_convt::find_symbols_rec( + const typet &type, + std::set &recstack) +{ + if(type.id()==ID_array) + { + const array_typet &array_type=to_array_type(type); + find_symbols(array_type.size()); + find_symbols_rec(array_type.subtype(), recstack); + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + const struct_union_typet::componentst &components= + to_struct_union_type(type).components(); + + for(unsigned i=0; i +#include + +#include + +#include +#include +#include + +#include "smt1_prop.h" + +class smt1_prop_wrappert +{ +public: + smt1_prop_wrappert( + const std::string &_benchmark, + const std::string &_source, + const std::string &_logic, + std::ostream &_out): + smt1_prop(_benchmark, _source, _logic, _out) + { } + +protected: + smt1_propt smt1_prop; +}; + +class smt1_convt: + protected smt1_prop_wrappert, + public prop_convt +{ +public: + smt1_convt( + const namespacet &_ns, + const std::string &_benchmark, + const std::string &_source, + const std::string &_logic, + std::ostream &_out): + smt1_prop_wrappert(_benchmark, _source, _logic, _out), + prop_convt(_ns, smt1_prop), + boolbv_width(_ns), + pointer_logic(_ns), + array_index_bits(32) + { } + + virtual ~smt1_convt() { } + virtual resultt dec_solve(); + +protected: + boolbv_widtht boolbv_width; + + // overloading + virtual literalt convert(const exprt &expr); + virtual void set_to(const exprt &expr, bool value); + virtual exprt get(const exprt &expr) const; + + // new stuff + void convert_expr(const exprt &expr, bool bool_as_bv); + void convert_type(const typet &type); + + // specific expressions go here + void convert_byte_update(const exprt &expr); + void convert_byte_extract(const exprt &expr); + void convert_typecast(const class typecast_exprt &expr, bool bool_as_bv); + void convert_struct(const exprt &expr); + void convert_union(const exprt &expr); + void convert_constant(const class constant_exprt &expr, bool bool_as_bv); + void convert_relation(const exprt &expr, bool bool_as_bv); + void convert_is_dynamic_object(const exprt &expr, bool bool_as_bv); + void convert_plus(const exprt &expr); + void convert_minus(const exprt &expr); + void convert_div(const exprt &expr); + void convert_mul(const exprt &expr); + void convert_mod(const exprt &expr); + void convert_index(const class index_exprt &expr, bool bool_as_bv); + void convert_member(const class member_exprt &expr, bool bool_as_bv); + void convert_overflow(const exprt &expr); + void convert_with(const exprt &expr); + + std::string convert_identifier(const irep_idt &identifier); + + // auxiliary methods + std::set quantified_symbols; + void find_symbols(const exprt &expr); + void find_symbols(const typet &type); + void find_symbols_rec(const typet &type, std::set &recstack); + void flatten_array(const exprt &op); + + // booleans vs. bit-vector[1] + void from_bv_begin(const typet &type, bool bool_as_bv); + void from_bv_end(const typet &type, bool bool_as_bv); + void from_bool_begin(const typet &type, bool bool_as_bv); + void from_bool_end(const typet &type, bool bool_as_bv); + + // arrays + typet array_index_type() const; + void array_index(const exprt &expr); + + // pointers + pointer_logict pointer_logic; + void convert_address_of_rec(const exprt &expr); + + // keeps track of all symbols + struct identifiert + { + typet type; + exprt value; + + identifiert() + { + type.make_nil(); + value.make_nil(); + } + }; + + void set_value( + identifiert &identifier, + const std::string &v); + + typedef hash_map_cont + identifier_mapt; + + identifier_mapt identifier_map; + + unsigned array_index_bits; + + // for replacing 'array_of' + typedef std::map array_of_mapt; + array_of_mapt array_of_map; + + // for replacing 'array initializers' + typedef std::map array_init_mapt; + array_init_mapt array_init_map; + + // for replacing string constants + typedef std::map string2array_mapt; + string2array_mapt string2array_map; + + exprt binary2struct( + const class struct_union_typet &type, + const std::string &binary) const; +}; + +#endif diff --git a/src/solvers/smt1/smt1_dec.cpp b/src/solvers/smt1/smt1_dec.cpp new file mode 100644 index 00000000000..200d522ec80 --- /dev/null +++ b/src/solvers/smt1/smt1_dec.cpp @@ -0,0 +1,598 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "smt1_dec.h" + +/*******************************************************************\ + +Function: smt1_dect::decision_procedure_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string smt1_dect::decision_procedure_text() const +{ + return "SMT "+logic+" using "+ + (solver==BOOLECTOR?"Boolector": + solver==CVC3?"CVC3": + solver==Z3?"Z3": + solver==YICES?"Yices": + "(unknown)"); +} + +/*******************************************************************\ + +Function: smt1_temp_filet::smt1_temp_filet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +smt1_temp_filet::smt1_temp_filet() +{ + temp_out_filename=get_temporary_file("smt1_dec_out_", ""); + + temp_out.open( + temp_out_filename.c_str(), + std::ios_base::out | std::ios_base::trunc + ); +} + +/*******************************************************************\ + +Function: smt1_temp_filet::~smt1_temp_filet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +smt1_temp_filet::~smt1_temp_filet() +{ + temp_out.close(); + + if(temp_out_filename!="") + unlink(temp_out_filename.c_str()); + + if(temp_result_filename!="") + unlink(temp_result_filename.c_str()); +} + +/*******************************************************************\ + +Function: smt1_dect::dec_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt smt1_dect::dec_solve() +{ + post_process(); + + // this closes the SMT benchmark + smt1_prop.finalize(); + temp_out.close(); + + temp_result_filename= + get_temporary_file("smt1_dec_result_", ""); + + std::string command; + + switch(solver) + { + case CVC3: + command = "cvc3 +model -lang smtlib -output-lang smtlib " + + temp_out_filename + + " > " + + temp_result_filename; + break; + + case YICES: + command = "yices -smt -e " + + temp_out_filename + + " > " + + temp_result_filename; + break; + + case BOOLECTOR: + command = "boolector --smt " + + temp_out_filename + + " -fm --output " + + temp_result_filename; + break; + + case Z3: + command = "z3 -m " + + temp_out_filename + + " > " + + temp_result_filename; + break; + + default: + assert(false); + } + + #if defined(__LINUX__) || defined(__APPLE__) + command+=" 2>&1"; + #endif + + system(command.c_str()); + + std::ifstream in(temp_result_filename.c_str()); + + switch(solver) + { + case CVC3: + return read_result_cvc3(in); + + case YICES: + return read_result_yices(in); + + case BOOLECTOR: + return read_result_boolector(in); + + case Z3: + return read_result_z3(in); + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: smt1_dect::read_result_boolector + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt smt1_dect::read_result_boolector(std::istream &in) +{ + std::string line; + + str_getline(in, line); + + if(line=="sat") + { + smt1_prop.reset_assignment(); + + typedef hash_map_cont valuest; + valuest values; + + while(str_getline(in, line)) + { + std::size_t pos=line.find(' '); + if(pos!=std::string::npos) + values[std::string(line, 0, pos)]= + std::string(line, pos+1, std::string::npos); + } + + // Theory variables + + for(identifier_mapt::iterator + it=identifier_map.begin(); + it!=identifier_map.end(); + it++) + { + it->second.value.make_nil(); + std::string conv_id=convert_identifier(it->first); + std::string value=values[conv_id]; + if(value=="") continue; + set_value(it->second, value); + } + + // Booleans + + for(unsigned v=0; v valuest; + valuest values; + + while(str_getline(in, line)) + { + if(line=="sat") + res = D_SATISFIABLE; + else if(line=="unsat") + res = D_UNSATISFIABLE; + else + { + std::size_t pos=line.find(" -> "); + if(pos!=std::string::npos) + values[std::string(line, 0, pos)]= + std::string(line, pos+4, std::string::npos); + } + } + + for(identifier_mapt::iterator + it=identifier_map.begin(); + it!=identifier_map.end(); + it++) + { + it->second.value.make_nil(); + std::string conv_id=convert_identifier(it->first); + std::string value=values[conv_id]; + if(value=="") continue; + +// std::cout << it->first << " := " << value << std::endl; + + exprt e; + if(string_to_expr_z3(it->second.type, value, e)) + { +// std::cout << "E: " << e << std::endl; + it->second.value=e; + } + else + set_value(it->second, value); + } + + // Booleans + for(unsigned v=0; vsecond!=id) fit++; + + if(fit==array_of_map.end()) return false; + + e = fit->first; + + return true; + } + else if(type.id()==ID_rational) + { + constant_exprt result; + result.type()=rational_typet(); + + if(value.substr(0,4)=="val!") + result.set_value(value.substr(4)); + else + result.set_value(value); + + e = result; + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: smt1_dect::read_result_cvc3 + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt smt1_dect::read_result_cvc3(std::istream &in) +{ + std::string line; + decision_proceduret::resultt res = D_ERROR; + + smt1_prop.reset_assignment(); + + typedef hash_map_cont valuest; + valuest values; + + while(str_getline(in, line)) + { + if(line=="sat") + res = D_SATISFIABLE; + else if(line=="unsat") + res = D_UNSATISFIABLE; + else if(line.find("Current scope level")!=std::string::npos || + line.find("Variable Assignment")!=std::string::npos) + ; //ignore + else + { + assert(line.substr(0,13)==" :assumption"); + std::size_t pos=line.find('('); + + if(pos!=std::string::npos) + { + std::string var; + std::string val; + + if(line[pos+1]=='=') + { + std::string ops = line.substr(pos+3, line.length()-pos-4); + std::size_t blank=ops.find(' '); + var = ops.substr(0, blank); + val = ops.substr(blank+1, ops.length()-blank); + + if((var.length()>=4 && var.substr(0,4)=="cvc3") || + (val.length()>=4 && val.substr(0,4)=="cvc3") || + var==val) + continue; + else if((var.substr(0,9)=="array_of'") || + (var.substr(0,2)=="bv" && val.substr(0,2)!="bv")) + { + std::string t=var; var=val; val=t; + } + +// std::cout << "OPS: " << ops << std::endl; + } + else if(line.substr(pos+1,3)=="not") + { + var = line.substr(pos+5, line.length()-pos-6); + val = "false"; + } + else + { + var = line.substr(pos+1, line.length()-pos-2); + assert(var.find(' ')==std::string::npos); + val = "true"; + } + +// std::cout << "VAR: " << var << std::endl; +// std::cout << "VAL: " << val << std::endl; + + values[var]=val; + } + } + } + + for(identifier_mapt::iterator + it=identifier_map.begin(); + it!=identifier_map.end(); + it++) + { + it->second.value.make_nil(); + std::string conv_id=convert_identifier(it->first); + std::string value=values[conv_id]; + if(value=="") continue; + +// std::cout << it->first << " := " << value << std::endl; + + if(value.substr(0,2)=="bv") + { + std::string v=value.substr(2, value.find('[')-2); + size_t p = value.find('[')+1; + std::string w=value.substr(p, value.find(']')-p); + + std::string binary=integer2binary(string2integer(v,10), + string2integer(w,10).to_ulong()); + + set_value(it->second, binary); + } + else if(value=="false") + it->second.value.make_false(); + else if(value=="true") + it->second.value.make_true(); + else if(value.substr(0,8)=="array_of") + { + // We assume that array_of has only concrete arguments... + irep_idt id(value); + array_of_mapt::const_iterator fit=array_of_map.begin(); + while(fit!=array_of_map.end() && fit->second!=id) fit++; + + if(fit!=array_of_map.end()) + it->second.value = fit->first; + } + else + set_value(it->second, value); + } + + // Booleans + for(unsigned v=0; v + +#include "smt1_conv.h" + +class smt1_temp_filet +{ +public: + smt1_temp_filet(); + ~smt1_temp_filet(); + +protected: + std::ofstream temp_out; + std::string temp_out_filename, temp_result_filename; +}; + +class smt1_dect:protected smt1_temp_filet, public smt1_convt +{ +public: + typedef enum { BOOLECTOR, CVC3, YICES, Z3 } solvert; + + smt1_dect( + const namespacet &_ns, + const std::string &_benchmark, + const std::string &_source, + const std::string &_logic, + solvert _solver): + smt1_temp_filet(), + smt1_convt(_ns, _benchmark, _source, _logic, temp_out), + logic(_logic), + solver(_solver) + { + } + + virtual resultt dec_solve(); + virtual std::string decision_procedure_text() const; + +protected: + std::string logic; + solvert solver; + + resultt read_result_boolector(std::istream &in); + resultt read_result_cvc3(std::istream &in); + resultt read_result_yices(std::istream &in); + resultt read_result_z3(std::istream &in); + + bool string_to_expr_z3( + const typet &type, + const std::string &value, exprt &e) const; +}; + +#endif diff --git a/src/solvers/smt1/smt1_prop.cpp b/src/solvers/smt1/smt1_prop.cpp new file mode 100644 index 00000000000..8faec102436 --- /dev/null +++ b/src/solvers/smt1/smt1_prop.cpp @@ -0,0 +1,530 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +Revisions: Roberto Bruttomesso, roberto.bruttomesso@unisi.ch + +\*******************************************************************/ + +#include + +#include + +#include "smt1_prop.h" + +/*******************************************************************\ + +Function: smt1_propt::smt1_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +smt1_propt::smt1_propt( + const std::string &benchmark, + const std::string &source, + const std::string &logic, + std::ostream &_out):out(_out) +{ + out << "(benchmark " << benchmark << std::endl; + out << ":source { " << source << " }" << std::endl; + out << ":status unknown" << std::endl; + out << ":logic " << logic << " ; SMT1" << std::endl; + _no_variables=0; +} + +/*******************************************************************\ + +Function: smt1_propt::~smt1_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +smt1_propt::~smt1_propt() +{ +} + +/*******************************************************************\ + +Function: smt1_propt::finalize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_propt::finalize() +{ + out << std::endl; + out << ":formula true" << std::endl; + out << ") ; benchmark" << std::endl; +} + +/*******************************************************************\ + +Function: smt1_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt smt1_propt::land(const bvt &bv) +{ + out << std::endl; + + literalt l=new_variable(); + + out << ":assumption ; land" << std::endl; + out << " (iff " << smt1_literal(l) << " (and"; + + for(unsigned int i=0; i=assignment.size()) return tvt(tvt::TV_UNKNOWN); + tvt r=assignment[v]; + return literal.sign()?!r:r; +} + +/*******************************************************************\ + +Function: smt1_propt::set_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt1_propt::set_assignment(literalt literal, bool value) +{ + if(literal.is_true() || literal.is_false()) return; + + unsigned v=literal.var_no(); + assert(v + +#include + +class smt1_propt:public propt +{ +public: + smt1_propt( + const std::string &_benchmark, + const std::string &_source, + const std::string &_logic, + std::ostream &_out); + virtual ~smt1_propt(); + + virtual literalt land(literalt a, literalt b); + virtual literalt lor(literalt a, literalt b); + virtual literalt land(const bvt &bv); + virtual literalt lor(const bvt &bv); + virtual literalt lxor(const bvt &bv); + virtual literalt lnot(literalt a); + virtual literalt lxor(literalt a, literalt b); + virtual literalt lnand(literalt a, literalt b); + virtual literalt lnor(literalt a, literalt b); + virtual literalt lequal(literalt a, literalt b); + virtual literalt limplies(literalt a, literalt b); + virtual literalt lselect(literalt a, literalt b, literalt c); // a?b:c + + virtual literalt new_variable(); + virtual unsigned no_variables() const { return _no_variables; } + virtual void set_no_variables(unsigned no) { assert(false); } + + virtual void lcnf(const bvt &bv); + + virtual const std::string solver_text() + { return "SMT"; } + + virtual tvt l_get(literalt literal) const; + virtual void set_assignment(literalt a, bool value); + + virtual propt::resultt prop_solve(); + + virtual void clear() + { + assignment.clear(); + } + + virtual void reset_assignment() + { + assignment.clear(); + assignment.resize(no_variables(), tvt(tvt::TV_UNKNOWN)); + } + + friend class smt1_convt; + friend class smt1_dect; + + void finalize(); + +protected: + unsigned _no_variables; + std::ostream &out; + + std::string smt1_literal(literalt l); + literalt def_smt1_literal(); + + std::vector assignment; +}; + +#endif diff --git a/src/solvers/smt2/smt2_conv.cpp b/src/solvers/smt2/smt2_conv.cpp new file mode 100644 index 00000000000..d1b7f3a9a7f --- /dev/null +++ b/src/solvers/smt2/smt2_conv.cpp @@ -0,0 +1,2516 @@ +/*******************************************************************\ + +Module: SMT Backend + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "smt2_conv.h" + +/*******************************************************************\ + +Function: smt2_convt::dec_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt smt2_convt::dec_solve() +{ + smt2_prop.finalize(); + return decision_proceduret::D_ERROR; +} + +/*******************************************************************\ + +Function: smt2_convt::get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt smt2_convt::get(const exprt &expr) const +{ + if(expr.id()==ID_symbol) + { + const irep_idt &id=to_symbol_expr(expr).get_identifier(); + + identifier_mapt::const_iterator it=identifier_map.find(id); + + if(it!=identifier_map.end()) + return it->second.value; + } + + return static_cast(get_nil_irep()); +} + +/*******************************************************************\ + +Function: smt2_convt::set_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::set_value( + identifiert &identifier, + const std::string &v) +{ + identifier.value.make_nil(); + + const typet &type=ns.follow(identifier.type); + + if(type.id()==ID_signedbv || + type.id()==ID_unsignedbv || + type.id()==ID_bv || + type.id()==ID_fixedbv) + { + assert(v.size()==bv_width(type)); + constant_exprt c(type); + c.set_value(v); + identifier.value=c; + } + else if(type.id()==ID_bool) + { + if(v=="1") + identifier.value.make_true(); + else if(v=="0") + identifier.value.make_false(); + } + else if(type.id()==ID_pointer) + { + // TODO + #if 0 + assert(v.size()==BV_ADDR_BITS+config.ansi_c.pointer_width); + + pointer_logict::pointert p; + p.object=integer2long(binary2integer(std::string(v, 0, BV_ADDR_BITS), false)); + p.offset=binary2integer(std::string(v, BV_ADDR_BITS, std::string::npos), true); + + identifier.value=pointer_logic.pointer_expr(p, type); + #endif + } + else if(type.id()==ID_struct) + { + // TODO + } + else if(type.id()==ID_union) + { + // TODO + } +} + +/*******************************************************************\ + +Function: smt2_convt::array_index_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet smt2_convt::array_index_type() const +{ + signedbv_typet t; + t.set_width(array_index_bits); + return t; +} + +/*******************************************************************\ + +Function: smt2_convt::array_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::array_index(const exprt &expr) +{ + typet t=array_index_type(); + if(t==expr.type()) return convert_expr(expr); + typecast_exprt tmp(t); + tmp.op0()=expr; + convert_expr(tmp); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_address_of_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_address_of_rec(const exprt &expr) +{ + if(expr.id()==ID_symbol || + expr.id()==ID_constant || + expr.id()==ID_string_constant) + { + smt2_prop.out + << "((ind tuple 2) (ind bv" + << pointer_logic.add_object(expr) << " " << BV_ADDR_BITS << ")" + << " (ind bv0 " << config.ansi_c.pointer_width << "))"; + } + else if(expr.id()==ID_index) + { + if(expr.operands().size()!=2) + throw "index takes two operands"; + + const exprt &array=to_index_expr(expr).array(); + const exprt &index=to_index_expr(expr).index(); + + if(index.is_zero()) + { + if(array.type().id()==ID_pointer) + convert_expr(array); + else if(array.type().id()==ID_array) + convert_address_of_rec(array); + else + assert(false); + } + else + { + // this is really pointer arithmetic + exprt new_index_expr=expr; + new_index_expr.op1()=gen_zero(index.type()); + + exprt address_of_expr(ID_address_of, pointer_typet()); + address_of_expr.type().subtype()=array.type().subtype(); + address_of_expr.copy_to_operands(new_index_expr); + + exprt plus_expr(ID_plus, address_of_expr.type()); + plus_expr.copy_to_operands(address_of_expr, index); + + convert_expr(plus_expr); + } + } + else if(expr.id()==ID_member) + { + if(expr.operands().size()!=1) + throw "member takes one operand"; + + const member_exprt &member_expr=to_member_expr(expr); + + const exprt &struct_op=member_expr.struct_op(); + const typet &struct_op_type=ns.follow(struct_op.type()); + + if(struct_op_type.id()==ID_struct) + { + const struct_typet &struct_type= + to_struct_type(struct_op_type); + + const irep_idt &component_name= + member_expr.get_component_name(); + + mp_integer offset=member_offset(ns, struct_type, component_name); + + typet index_type(ID_unsignedbv); + index_type.set(ID_width, config.ansi_c.pointer_width); + + smt2_prop.out << "(bvadd "; + convert_address_of_rec(struct_op); + smt2_prop.out << "((ind zero_extend " << BV_ADDR_BITS << ") "; + convert_expr(from_integer(offset, index_type)); + smt2_prop.out << "))"; // zero_extend, bvadd + } + else + throw "unexpected type of member operand"; + + } + else + throw "don't know how to take address of: "+expr.id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_byte_extract + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_byte_extract(const exprt &expr) +{ + mp_integer i; + if(to_integer(expr.op1(), i)) + throw "byte_extract takes constant as second parameter"; + + boolbv_widtht boolbv_width(ns); + + unsigned w=boolbv_width(expr.op0().type()); + if(w==0) + throw "failed to get width of byte_extract operand"; + + smt2_prop.out << ""; + + mp_integer upper, lower; + + if(expr.id()==ID_byte_extract_little_endian) + { + upper = ((i+1)*8)-1; + lower = i*8; + } + else + { + mp_integer max=w-1; + upper = max-(i*8); + lower = max-((i+1)*8-1); + } + + smt2_prop.out << "((ind extract " << upper << " " << lower << ") "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; +} + +/*******************************************************************\ + +Function: smt2_convt::convert_byte_update + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_byte_update(const exprt &expr) +{ + assert(expr.operands().size()==3); + + // The situation: expr.op0 needs to be split in 3 parts + // |<--- L --->|<--- M --->|<--- R --->| + // where M is the expr.op1'th byte + // We need to output L expr.op2 R + + mp_integer i; + if(to_integer(expr.op1(), i)) + throw "byte_extract takes constant as second parameter"; + + boolbv_widtht boolbv_width(ns); + + unsigned w=boolbv_width(expr.op0().type()); + + if(w==0) + throw "failed to get width of byte_extract operand"; + + mp_integer upper, lower; // of the byte + mp_integer max=w-1; + if(expr.id()==ID_byte_update_little_endian) + { + upper = ((i+1)*8)-1; + lower = i*8; + } + else + { + upper = max-(i*8); + lower = max-((i+1)*8-1); + } + + if(upper==max) + { + if(lower==0) // there was only one byte + convert_expr(expr.op2()); + else // uppermost byte selected, only R needed + { + smt2_prop.out << "(concat "; + convert_expr(expr.op2()); + smt2_prop.out << " ((ind extract " << lower-1 << " 0) "; + convert_expr(expr.op0()); + smt2_prop.out << "))"; + } + } + else + { + if(lower==0) // lowermost byte selected, only L needed + { + smt2_prop.out << "(concat "; + smt2_prop.out << "((ind extract " << max << " " << (upper+1) << ") "; + convert_expr(expr.op0()); + smt2_prop.out << ") "; + convert_expr(expr.op2()); + smt2_prop.out << ")"; + } + else // byte in the middle selected, L & R needed + { + smt2_prop.out << "(concat (concat "; + smt2_prop.out << "((ind extract " << max << " " << (upper+1) << ") "; + convert_expr(expr.op0()); + smt2_prop.out << ") "; + convert_expr(expr.op2()); + smt2_prop.out << ") ((ind extract " << (lower-1) << " 0) "; + convert_expr(expr.op0()); + smt2_prop.out << "))"; + } + } + +} + +/*******************************************************************\ + +Function: smt2_convt::convert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt smt2_convt::convert(const exprt &expr) +{ + assert(expr.type().id()==ID_bool); + + if(expr.is_true()) + return const_literal(true); + else if(expr.is_false()) + return const_literal(false); + + smt2_prop.out << std::endl; + + find_symbols(expr); + + literalt l=smt2_prop.new_variable(); + smt2_prop.out << ":assumption ; convert " << std::endl + << " (= " << smt2_prop.smt2_literal(l) << " "; + convert_expr(expr); + smt2_prop.out << ")" << std::endl; + + return l; +} + +/*******************************************************************\ + +Function: smt2_convt::convert_identifier + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string smt2_convt::convert_identifier(const irep_idt &identifier) +{ + // TODO: need to search for '|' in there + std::string result="|"+id2string(identifier)+"|"; + return result; +} + +/*******************************************************************\ + +Function: smt2_convt::convert_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_expr(const exprt &expr) +{ + if(expr.id()==ID_symbol) + { + irep_idt id=to_symbol_expr(expr).get_identifier(); + assert(id!=""); + + smt2_prop.out << convert_identifier(id); + } + else if(expr.id()==ID_nondet_symbol) + { + irep_idt id=expr.get(ID_identifier); + assert(id!=""); + + smt2_prop.out << "nondet_" << convert_identifier(id); + } + else if(expr.id()==ID_typecast) + { + convert_typecast(to_typecast_expr(expr)); + } + else if(expr.id()==ID_struct) + { + convert_struct(expr); + } + else if(expr.id()==ID_union) + { + convert_union(expr); + } + else if(expr.id()==ID_constant) + { + convert_constant(to_constant_expr(expr)); + } + else if(expr.id()==ID_concatenation || + expr.id()==ID_bitand || + expr.id()==ID_bitor || + expr.id()==ID_bitxor || + expr.id()==ID_bitnand || + expr.id()==ID_bitnor) + { + assert(expr.operands().size()>=2); + + smt2_prop.out << "("; + + if(expr.id()==ID_concatenation) + smt2_prop.out << "concat"; + else if(expr.id()==ID_bitand) + smt2_prop.out << "bvand"; + else if(expr.id()==ID_bitor) + smt2_prop.out << "bvor"; + else if(expr.id()==ID_bitxor) + smt2_prop.out << "bvxor"; + else if(expr.id()==ID_bitnand) + smt2_prop.out << "bvnand"; + else if(expr.id()==ID_bitnor) + smt2_prop.out << "bvnor"; + + forall_operands(it, expr) + { + smt2_prop.out << " "; + convert_expr(*it); + } + + smt2_prop.out << ")"; + } + else if(expr.id()==ID_bitnot) + { + assert(expr.operands().size()==1); + smt2_prop.out << "(bvnot "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + } + else if(expr.id()==ID_unary_minus) + { + assert(expr.operands().size()==1); + + if(expr.type().id()==ID_rational) + { + smt2_prop.out << "(- "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + } + else + { + smt2_prop.out << "(bvneg "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + } + } + else if(expr.id()==ID_if) + { + assert(expr.operands().size()==3); + + smt2_prop.out << "(ite "; + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + smt2_prop.out << " "; + convert_expr(expr.op2()); + smt2_prop.out << ")"; + } + else if(expr.id()==ID_and || + expr.id()==ID_or || + expr.id()==ID_xor) + { + assert(expr.type().id()==ID_bool); + assert(expr.operands().size()>=2); + + if(expr.operands().size()>=2) + { + smt2_prop.out << "(" << expr.id(); + forall_operands(it, expr) + { + smt2_prop.out << " "; + convert_expr(*it); + } + smt2_prop.out << ")"; + } + else + assert(false); + } + else if(expr.id()==ID_implies) + { + assert(expr.type().id()==ID_bool); + assert(expr.operands().size()==2); + + smt2_prop.out << "(implies "; + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + smt2_prop.out << ")"; + } + else if(expr.id()==ID_not) + { + assert(expr.type().id()==ID_bool); + assert(expr.operands().size()==1); + + smt2_prop.out << "(not "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + } + else if(expr.id()==ID_equal || + expr.id()==ID_notequal) + { + assert(expr.operands().size()==2); + assert(expr.op0().type()==expr.op1().type()); + + if(expr.id()==ID_notequal) + { + smt2_prop.out << "(not (= "; + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + smt2_prop.out << "))"; + } + else + { + smt2_prop.out << "(= "; + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + smt2_prop.out << ")"; + } + } + else if(expr.id()==ID_le || + expr.id()==ID_lt || + expr.id()==ID_ge || + expr.id()==ID_gt) + { + convert_relation(expr); + } + else if(expr.id()==ID_plus) + { + convert_plus(expr); + } + else if(expr.id()==ID_minus) + { + convert_minus(expr); + } + else if(expr.id()==ID_div) + { + convert_div(expr); + } + else if(expr.id()==ID_mod) + { + convert_mod(expr); + } + else if(expr.id()==ID_mult) + { + convert_mul(expr); + } + else if(expr.id()==ID_address_of) + { + assert(expr.operands().size()==1); + assert(expr.type().id()==ID_pointer); + convert_address_of_rec(expr.op0()); + } + else if(expr.id()==ID_array_of) + { + assert(expr.type().id()==ID_array); + assert(expr.operands().size()==1); + + // const array_typet &array_type=to_array_type(expr.type()); + + // not really there in SMT, so we replace it + // this is an over-approximation + array_of_mapt::const_iterator it=array_of_map.find(expr); + assert(it!=array_of_map.end()); + + smt2_prop.out << it->second; + } + else if(expr.id()==ID_index) + { + convert_index(to_index_expr(expr)); + } + else if(expr.id()==ID_ashr || + expr.id()==ID_lshr || + expr.id()==ID_shl) + { + assert(expr.operands().size()==2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv || + expr.type().id()==ID_bv) + { + if(expr.id()==ID_ashr) + smt2_prop.out << "(bvashr "; + else if(expr.id()==ID_lshr) + smt2_prop.out << "(bvlshr "; + else if(expr.id()==ID_shl) + smt2_prop.out << "(bvshl "; + else + assert(false); + + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + smt2_prop.out << ")"; + } + else + throw "unsupported type for "+expr.id_string()+ + ": "+expr.type().id_string(); + } + else if(expr.id()==ID_with) + { + convert_with(expr); + } + else if(expr.id()==ID_member) + { + convert_member(to_member_expr(expr)); + } + else if(expr.id()==ID_pointer_offset) + { + assert(expr.operands().size()==1); + assert(expr.op0().type().id()==ID_pointer); + smt2_prop.out << "((ind project 2 2) "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + } + else if(expr.id()==ID_pointer_object) + { + assert(expr.operands().size()==1); + assert(expr.op0().type().id()==ID_pointer); + unsigned ext=bv_width(expr.type())-BV_ADDR_BITS; + + if(ext>0) + smt2_prop.out << "((ind zero_extend " << ext << ") "; + + smt2_prop.out << "((ind project 2 1) "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + + if(ext>0) + smt2_prop.out << ")"; + } + else if(expr.id()=="same-object") + { + assert(expr.operands().size()==2); + + smt2_prop.out << "(= ((ind project 2 1) "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + + smt2_prop.out << " ((ind project 2 1) "; + convert_expr(expr.op1()); + smt2_prop.out << "))"; + } + else if(expr.id()=="is_dynamic_object") + { + convert_is_dynamic_object(expr); + } + else if(expr.id()=="invalid-pointer") + { + assert(expr.operands().size()==1); + + smt2_prop.out << "(= ((ind project 2 1) "; + convert_expr(expr.op0()); + smt2_prop.out << ") (ind bv" << pointer_logic.get_invalid_object() + << " " << BV_ADDR_BITS << "))"; + } + else if(expr.id()=="pointer_object_has_type") + { + assert(expr.operands().size()==1); + + smt2_prop.out << "false"; // TODO + } + else if(expr.id()==ID_string_constant) + { + exprt tmp; + string2array_mapt::const_iterator fit=string2array_map.find(expr); + assert(fit!=string2array_map.end()); + + convert_expr(fit->second); + } + else if(expr.id()==ID_extractbit) + { + assert(expr.operands().size()==2); + + if(expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv || + expr.op0().type().id()==ID_bv || + expr.op0().type().id()==ID_fixedbv) + { + if(expr.op1().is_constant()) + { + mp_integer i; + if(to_integer(expr.op1(), i)) + throw "extractbit: to_integer failed"; + + smt2_prop.out << "(= ((ind extract " << i << " " << i << ") "; + convert_expr(expr.op0()); + smt2_prop.out << ") bit1)"; + } + else + { + smt2_prop.out << "(= ((ind extract 0 0) "; + // the arguments of the shift need to have the same width + smt2_prop.out << "(bvlshr "; + convert_expr(expr.op0()); + typecast_exprt tmp(expr.op0().type()); + tmp.op0()=expr.op1(); + convert_expr(tmp); + smt2_prop.out << ")) bin1)"; // bvlshr, extract, = + } + } + else + throw "unsupported type for "+expr.id_string()+ + ": "+expr.op0().type().id_string(); + } + else if(expr.id()==ID_replication) + { + assert(expr.operands().size()==2); + + mp_integer times; + if(to_integer(expr.op0(), times)) + throw "replication takes constant as first parameter"; + + smt2_prop.out << "((ind repeat " << times << ") "; + // todo: need to deal with boolean + convert_expr(expr.op1()); + smt2_prop.out << ")"; + } + else if(expr.id()==ID_byte_extract_little_endian || + expr.id()==ID_byte_extract_big_endian) + { + convert_byte_extract(expr); + } + else if(expr.id()==ID_byte_update_little_endian || + expr.id()==ID_byte_update_big_endian) + { + convert_byte_update(expr); + } + else if(expr.id()==ID_width) + { + boolbv_widtht boolbv_width(ns); + + unsigned result_width=boolbv_width(expr.type()); + + if(result_width==0) + throw "conversion failed"; + + if(expr.operands().size()!=1) + throw "width expects 1 operand"; + + unsigned op_width=boolbv_width(expr.op0().type()); + + if(op_width==0) + throw "conversion failed"; + + smt2_prop.out << "(ind bv" << op_width/8 + << " " << result_width << ")"; + } + else if(expr.id()==ID_abs) + { + assert(expr.operands().size()==1); + + const typet &type=expr.type(); + + if(type.id()==ID_signedbv) + { + unsigned result_width=to_signedbv_type(type).get_width(); + + smt2_prop.out << "(ite (bvslt "; + convert_expr(expr.op0()); + smt2_prop.out << " (ind bv0 " << result_width << ")) "; + smt2_prop.out << "(bvneg "; + convert_expr(expr.op0()); + smt2_prop.out << ") "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + } + else if(type.id()==ID_fixedbv) + { + unsigned result_width=to_fixedbv_type(type).get_width(); + + smt2_prop.out << "(ite (bvslt "; + convert_expr(expr.op0()); + smt2_prop.out << " (ind bv0 " << result_width << ")) "; + smt2_prop.out << "(bvneg "; + convert_expr(expr.op0()); + smt2_prop.out << ") "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + } + else if(type.id()==ID_floatbv) + { + unsigned result_width=to_floatbv_type(type).get_width(); + smt2_prop.out << "(bvand "; + convert_expr(expr.op0()); + smt2_prop.out << " (ind bv" + << (power(2, result_width-1)-1) + << " " << result_width << "))"; + } + else + throw "abs with unsupported operand type"; + } + else if(expr.id()==ID_isnan) + { + assert(expr.operands().size()==1); + + const typet &op_type=expr.op0().type(); + + if(op_type.id()==ID_fixedbv) + smt2_prop.out << "false"; + else + throw "isnan with unsupported operand type"; + } + else if(expr.id()==ID_isfinite) + { + if(expr.operands().size()!=1) + throw "isfinite expects one operand"; + + const typet &op_type=expr.op0().type(); + + if(op_type.id()==ID_fixedbv) + smt2_prop.out << "true"; + else + throw "isfinite with unsupported operand type"; + } + else if(expr.id()==ID_isinf) + { + if(expr.operands().size()!=1) + throw "isinf expects one operand"; + + const typet &op_type=expr.op0().type(); + + if(op_type.id()==ID_fixedbv) + smt2_prop.out << "false"; + else + throw "isinf with unsupported operand type"; + } + else if(expr.id()==ID_isnormal) + { + if(expr.operands().size()!=1) + throw "isnormal expects one operand"; + + const typet &op_type=expr.op0().type(); + + if(op_type.id()==ID_fixedbv) + smt2_prop.out << "true"; + else + throw "isnormal with unsupported operand type"; + } + else if(expr.id()=="overflow-+" || + expr.id()=="overflow--") + { + assert(expr.operands().size()==2); + assert(expr.type().id()==ID_bool); + + bool subtract=expr.id()=="overflow--"; + const typet &op_type=expr.op0().type(); + unsigned width=bv_width(op_type); + + if(op_type.id()==ID_signedbv) + { + // an overflow occurs if the top two bits of the extended sum differ + smt2_prop.out << "(let ((?sum ("; + smt2_prop.out << (subtract?"bvsub":"bvadd"); + smt2_prop.out << " ((ind sign_extend 1) "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + smt2_prop.out << " ((ind sign_extend 1) "; + convert_expr(expr.op1()); + smt2_prop.out << ")))) "; // sign_extend, bvadd/sub let2 + smt2_prop.out << "(not (= " + "((ind extract " << width << " " << width << ") ?sum) " + "((ind extract " << (width-1) << " " << (width-1) << ") ?sum)"; + smt2_prop.out << ")))"; // =, not, let + } + else if(op_type.id()==ID_unsignedbv) + { + // overflow is simply carry-out + smt2_prop.out << "(= "; + smt2_prop.out << "((ind extract " << width << " " << width << ") "; + smt2_prop.out << "(" << (subtract?"bvsub":"bvadd"); + smt2_prop.out << " ((ind zero_extend 1) "; + convert_expr(expr.op0()); + smt2_prop.out << ")"; + smt2_prop.out << " ((ind zero_extend 1) "; + convert_expr(expr.op1()); + smt2_prop.out << ")))"; // zero_extend, bvsub/bvadd, extract + smt2_prop.out << " bit1)"; // = + } + else + throw "overflow check on unknown type: "+op_type.id_string(); + } + else if(expr.id()=="overflow-*") + { + assert(expr.operands().size()==2); + throw "not yet implemented: overflow-*"; + } + else + throw "smt2_convt::convert_expr: `"+ + expr.id_string()+"' is unsupported"; +} + +/*******************************************************************\ + +Function: smt2_convt::convert_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_typecast(const typecast_exprt &expr) +{ + assert(expr.operands().size()==1); + const exprt &op=expr.op0(); + const typet &expr_type=ns.follow(expr.type()); + const typet &op_type=ns.follow(op.type()); + + if(expr_type.id()==ID_bool) + { + // this is comparison with zero + if(op_type.id()==ID_signedbv || + op_type.id()==ID_unsignedbv || + op_type.id()==ID_fixedbv || + op_type.id()==ID_pointer) + { + smt2_prop.out << "(not (= "; + convert_expr(op); + smt2_prop.out << " "; + convert_expr(gen_zero(op_type)); + smt2_prop.out << "))"; + } + else + { + throw "TODO typecast1 "+op_type.id_string()+" -> bool"; + } + } + else if(expr_type.id()==ID_signedbv || + expr_type.id()==ID_unsignedbv || + expr_type.id()==ID_c_enum) + { + unsigned to_width=bv_width(expr_type); + + if(op_type.id()==ID_signedbv || // from signedbv + op_type.id()==ID_unsignedbv || // from unsigedbv + op_type.id()==ID_c_enum) + { + unsigned from_width=bv_width(op_type); + + if(from_width==to_width) + convert_expr(op); // ignore + else if(from_widthfrom_integer_bits) + { + smt2_prop.out << "((ind sign_extend " + << (to_width-from_integer_bits) << ") "; + smt2_prop.out << "((ind extract " << (from_width-1) << " " + << from_fraction_bits << ") "; + convert_expr(op); + smt2_prop.out << "))"; + } + else + { + smt2_prop.out << "((ind extract " << (from_fraction_bits+to_width-1) + << " " << from_fraction_bits << ") "; + convert_expr(op); + smt2_prop.out << ")"; + } + } + else if(op_type.id()==ID_bool) // from boolean + { + smt2_prop.out << "(ite "; + convert_expr(op); + + if(expr_type.id()==ID_fixedbv) + { + fixedbvt fbt(expr); + smt2_prop.out << " (concat (ind bv1 " + << fbt.spec.integer_bits << ") " << + "(ind bv0 " << fbt.spec.get_fraction_bits() << ")) " << + "(ind bv0 " << fbt.spec.width << ")"; + } + else + { + smt2_prop.out << " (ind bv1 " << to_width << ")"; + smt2_prop.out << " (ind bv0 " << to_width << ")"; + } + + smt2_prop.out << ")"; + } + else if(op_type.id()==ID_pointer) // from pointer to int + { + unsigned from_width=config.ansi_c.pointer_width; + + if(from_width "+expr_type.id_string(); + } + } + else if(expr_type.id()==ID_fixedbv) // to fixedbv + { + const fixedbv_typet &fixedbv_type=to_fixedbv_type(expr_type); + unsigned to_fraction_bits=fixedbv_type.get_fraction_bits(); + unsigned to_integer_bits=fixedbv_type.get_integer_bits(); + + if(op_type.id()==ID_unsignedbv || + op_type.id()==ID_signedbv || + op_type.id()==ID_c_enum) + { + unsigned from_width=to_bitvector_type(op_type).get_width(); + smt2_prop.out << "(concat "; + + if(from_width==to_integer_bits) + convert_expr(op); + else if(from_width>to_integer_bits) + { + smt2_prop.out << "((ind extract " << (to_integer_bits-1) << " " + << to_fraction_bits << ") "; + convert_expr(op); + smt2_prop.out << ")"; + } + else + { + assert(from_widthfrom_integer_bits); + smt2_prop.out << "((ind sign_extend " + << (to_integer_bits-from_integer_bits) + << ") ((ind extract " + << (from_width-1) << " " + << from_fraction_bits + << ") "; + convert_expr(op); + smt2_prop.out << "))"; + } + + smt2_prop.out << " "; + + if(to_fraction_bits<=from_fraction_bits) + { + smt2_prop.out << "((ind extract " + << (from_fraction_bits-1) << " " + << (from_fraction_bits-to_fraction_bits) + << ") "; + convert_expr(op); + smt2_prop.out << ")"; + } + else + { + assert(to_fraction_bits>from_fraction_bits); + smt2_prop.out << "(concat ((ind extract " + << (from_fraction_bits-1) << " 0) "; + convert_expr(op); + smt2_prop.out << ")" + << " (ind bv0 " << to_fraction_bits-from_fraction_bits + << "))"; + } + + smt2_prop.out << ")"; // concat + } + else + throw "unexpected typecast to fixedbv"; + } + else if(expr_type.id()==ID_pointer) + { + if(op_type.id()==ID_pointer) + { + // this just passes through + convert_expr(op); + } + else if(op_type.id()==ID_unsigned || + op_type.id()==ID_signedbv) + { + unsigned from_width=bv_width(op_type); + smt2_prop.out << "(ind tuple 2 " + << "(ind bv" << pointer_logic.get_null_object() + << BV_ADDR_BITS << ") "; + + if(from_width==config.ansi_c.pointer_width) + convert_expr(op); + else if(from_widthconfig.ansi_c.pointer_width + { + smt2_prop.out << "((ind extract " + << config.ansi_c.pointer_width + << " 0) "; + convert_expr(op); + smt2_prop.out << ")"; // extract + } + + smt2_prop.out << ")"; // tuple + } + else + throw "TODO typecast3 "+op_type.id_string()+" -> pointer"; + } + else if(expr_type.id()==ID_range) + { + throw "TODO range typecast"; + } + else + throw "TODO typecast4 ? -> "+expr_type.id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_struct + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_struct(const exprt &expr) +{ + const struct_typet &struct_type=to_struct_type(expr.type()); + + const struct_typet::componentst &components= + struct_type.components(); + + assert(components.size()==expr.operands().size()); + + assert(!components.empty()); + + if(components.size()==1) + convert_expr(expr.op0()); + else + { + smt2_prop.out << "(concat"; + unsigned i=0; + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++, i++) + { + if(it->type().id()!=ID_code) + { + smt2_prop.out << " "; + //const exprt &op=expr.operands()[i]; + + #if 0 + if(op.type().id()==ID_array) + flatten_array(op); + else + convert_expr(op); + #endif + // TODO + } + } + + smt2_prop.out << ")"; + } +} + +/*******************************************************************\ + +Function: smt2_convt::convert_union + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_union(const exprt &expr) +{ + const union_typet &union_type=to_union_type(expr.type()); + + assert(expr.operands().size()==1); + const exprt &op=expr.op0(); + + boolbv_widtht boolbv_width(ns); + + unsigned total_width=boolbv_width(union_type); + + if(total_width==0) + throw "failed to get union width for union"; + + unsigned member_width=boolbv_width(op.type()); + + if(member_width==0) + throw "failed to get union member width for union"; + + if(total_width==member_width) + { + // TODO: deal with boolean + convert_expr(op); + } + else + { + // we will pad with zeros, but non-det would be better + assert(total_width>member_width); + smt2_prop.out << "(concat "; + smt2_prop.out << "(ind bv0 " + << (total_width-member_width) << ") "; + // TODO: deal with boolean + convert_expr(op); + smt2_prop.out << ")"; + } +} + +/*******************************************************************\ + +Function: smt2_convt::convert_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_constant(const constant_exprt &expr) +{ + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv || + expr.type().id()==ID_bv || + expr.type().id()==ID_c_enum) + { + mp_integer value; + + if(to_integer(expr, value)) + throw "failed to convert bitvector constant"; + + unsigned width=bv_width(expr.type()); + + if(value<0) value=power(2, width)+value; + + smt2_prop.out << "(ind bv" << value + << " " << width << ")"; + } + else if(expr.type().id()==ID_fixedbv) + { + fixedbv_spect spec(to_fixedbv_type(expr.type())); + + std::string v_str=id2string(expr.get(ID_value)); + mp_integer v=binary2integer(v_str, false); + + smt2_prop.out << "(ind bv" << v << " " << spec.width << ")"; + } + else if(expr.type().id()==ID_floatbv) + { + ieee_float_spect spec(to_floatbv_type(expr.type())); + + std::string v_str=id2string(expr.get(ID_value)); + mp_integer v=binary2integer(v_str, false); + + smt2_prop.out << "(ind bv" << v << " " << spec.width() << ")"; + } + else if(expr.type().id()==ID_pointer) + { + const irep_idt &value=expr.get(ID_value); + + if(value==ID_NULL) + { + smt2_prop.out << "((ind tuple 2)" + << " (ind bv" << pointer_logic.get_null_object() + << " " << BV_ADDR_BITS << ")" + << " (ind bv0 " << config.ansi_c.pointer_width + << "))"; + } + else + throw "unknown pointer constant: "+id2string(value); + } + else if(expr.type().id()==ID_bool) + { + if(expr.is_true()) + smt2_prop.out << "true"; + else if(expr.is_false()) + smt2_prop.out << "false"; + else + throw "unknown boolean constant"; + } + else if(expr.type().id()==ID_array) + { + array_init_mapt::const_iterator it=array_init_map.find(expr); + assert(it!=array_init_map.end()); + + std::string tmp; + tmp = it->second.as_string(); + + assert(expr.operands().size()!=0); + + forall_operands(it, expr) + smt2_prop.out << "(store "; + + smt2_prop.out << it->second; + + unsigned i=0; + forall_operands(it, expr) + { + exprt inx = from_integer(i, unsignedbv_typet(array_index_bits)); + smt2_prop.out << " "; + convert_expr(inx); + smt2_prop.out << " "; + convert_expr(*it); + smt2_prop.out << ")"; + i++; + } + } + else if(expr.type().id()==ID_rational) + { + std::string value=expr.get(ID_value).as_string(); + size_t pos=value.find("/"); + + if(pos==std::string::npos) + smt2_prop.out << value << ".0"; + else + { + smt2_prop.out << "(/ " << value.substr(0,pos) << ".0 " + << value.substr(pos+1) << ".0)"; + } + } + else if(expr.type().id()==ID_integer) + { + smt2_prop.out << expr.get(ID_value); + } + else + throw "unknown constant: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_mod + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_mod(const exprt &expr) +{ + assert(expr.operands().size()==2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv) + { + if(expr.type().id()==ID_unsignedbv) + smt2_prop.out << "(bvurem "; + else + smt2_prop.out << "(bvsrem "; + + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + smt2_prop.out << ")"; + } + else + throw "unsupported type for mod: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_is_dynamic_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_is_dynamic_object(const exprt &expr) +{ + std::vector dynamic_objects; + pointer_logic.get_dynamic_objects(dynamic_objects); + + assert(expr.operands().size()==1); + + if(dynamic_objects.empty()) + smt2_prop.out << "false"; + else + { + smt2_prop.out << "(let ((?obj ((ind project 2 1) "; + convert_expr(expr.op0()); + smt2_prop.out << "))) "; + + if(dynamic_objects.size()==1) + { + smt2_prop.out << "(= (ind bv" << dynamic_objects.front() + << " " << BV_ADDR_BITS << ") ?obj)"; + } + else + { + smt2_prop.out << "(or"; + + for(std::vector::const_iterator + it=dynamic_objects.begin(); + it!=dynamic_objects.end(); + it++) + smt2_prop.out << " (= (ind bv" << *it + << " " << BV_ADDR_BITS << ") ?obj)"; + + smt2_prop.out << ")"; // or + } + + smt2_prop.out << ")"; // let + } +} + +/*******************************************************************\ + +Function: smt2_convt::convert_relation + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_relation(const exprt &expr) +{ + assert(expr.operands().size()==2); + + const typet &op_type=expr.op0().type(); + + smt2_prop.out << "("; + + if(op_type.id()==ID_unsignedbv) + { + if(expr.id()==ID_le) + smt2_prop.out << "bvule"; + else if(expr.id()==ID_lt) + smt2_prop.out << "bvult"; + else if(expr.id()==ID_ge) + smt2_prop.out << "bvuge"; + else if(expr.id()==ID_gt) + smt2_prop.out << "bvugt"; + + smt2_prop.out << " "; + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + } + else if(op_type.id()==ID_signedbv || + op_type.id()==ID_fixedbv) + { + if(expr.id()==ID_le) + smt2_prop.out << "bvsle"; + else if(expr.id()==ID_lt) + smt2_prop.out << "bvslt"; + else if(expr.id()==ID_ge) + smt2_prop.out << "bvsge"; + else if(expr.id()==ID_gt) + smt2_prop.out << "bvsgt"; + + smt2_prop.out << " "; + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + } + else if(op_type.id()==ID_rational || + op_type.id()==ID_integer) + { + smt2_prop.out << expr.id(); + + smt2_prop.out << " "; + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + } + else + throw "unsupported type for "+expr.id_string()+ + ": "+op_type.id_string(); + + smt2_prop.out << ")"; +} + +/*******************************************************************\ + +Function: smt2_convt::convert_plus + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_plus(const exprt &expr) +{ + assert(expr.operands().size()>=2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv || + expr.type().id()==ID_fixedbv) + { + smt2_prop.out << "(bvadd"; + + forall_operands(it, expr) + { + smt2_prop.out << " "; + convert_expr(*it); + } + + smt2_prop.out << ")"; + } + else if(expr.type().id()==ID_pointer) + { + if(expr.operands().size()!=2) + throw "pointer arithmetic with more than two operands"; + + exprt p=expr.op0(), i=expr.op1(); + + if(p.type().id()!=ID_pointer) + p.swap(i); + + if(p.type().id()!=ID_pointer) + throw "unexpected mixture in pointer arithmetic"; + + mp_integer element_size= + pointer_offset_size(ns, expr.type().subtype()); + + smt2_prop.out << "(bvadd "; + convert_expr(p); + smt2_prop.out << " "; + smt2_prop.out << "((ind zero_extend " << BV_ADDR_BITS << ") "; + + if(element_size>=2) + { + smt2_prop.out << "(bvmul "; + convert_expr(i); + smt2_prop.out << " (ind bv" << element_size + << " " << config.ansi_c.pointer_width << "))"; + } + else + convert_expr(i); + + smt2_prop.out << "))"; + } + else if(expr.type().id()==ID_rational) + { + smt2_prop.out << "(+"; + + forall_operands(it, expr) + { + smt2_prop.out << " "; + convert_expr(*it); + } + + smt2_prop.out << ")"; + } + else + throw "unsupported type for +: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_minus + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_minus(const exprt &expr) +{ + assert(expr.operands().size()==2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv || + expr.type().id()==ID_fixedbv) + { + smt2_prop.out << "(bvsub "; + + if(expr.op0().type().id()==ID_pointer) + smt2_prop.out << "((ind extract " + << config.ansi_c.pointer_width-1 << " 0) "; + convert_expr(expr.op0()); + if(expr.op0().type().id()==ID_pointer) + smt2_prop.out << ")"; + + smt2_prop.out << " "; + + if(expr.op1().type().id()==ID_pointer) + smt2_prop.out << "((ind extract " + << config.ansi_c.pointer_width-1 << " 0) "; + convert_expr(expr.op1()); + if(expr.op1().type().id()==ID_pointer) + smt2_prop.out << ")"; + + smt2_prop.out << ")"; + } + else if(expr.type().id()==ID_pointer) + { + convert_expr(binary_exprt( + expr.op0(), + "+", + unary_minus_exprt(expr.op1(), expr.op1().type()), + expr.type())); + } + else + throw "unsupported type for -: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_div + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_div(const exprt &expr) +{ + assert(expr.operands().size()==2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv) + { + if(expr.type().id()==ID_unsignedbv) + smt2_prop.out << "(bvudiv "; + else + smt2_prop.out << "(bvsdiv "; + + convert_expr(expr.op0()); + smt2_prop.out << " "; + convert_expr(expr.op1()); + smt2_prop.out << ")"; + } + else if(expr.type().id()==ID_fixedbv) + { + fixedbvt fbt(expr); + unsigned fraction_bits=fbt.spec.get_fraction_bits(); + + smt2_prop.out << "((ind extract " << fbt.spec.width-1 << " 0) "; + smt2_prop.out << "(bvsdiv "; + + smt2_prop.out << "(concat "; + convert_expr(expr.op0()); + smt2_prop.out << " (ind bv0 " << fraction_bits << ")) "; + + smt2_prop.out << "((ind sign_extend " << fraction_bits << ") "; + convert_expr(expr.op1()); + smt2_prop.out << ")"; + + smt2_prop.out << "))"; + } + else + throw "unsupported type for /: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_mul + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_mul(const exprt &expr) +{ + assert(expr.operands().size()>=2); + + if(expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv) + { + forall_operands(it, expr) + if(it!=expr.operands().begin()) smt2_prop.out << "(bvmul "; + + exprt::operandst::const_iterator last; + + forall_operands(it, expr) + { + if(it!=expr.operands().begin()) + { + convert_expr(*last); + smt2_prop.out << " "; + convert_expr(*it); + smt2_prop.out << ")"; + } + + last=it; + } + } + else if(expr.type().id()==ID_fixedbv) + { + fixedbvt fbt(expr); + unsigned fraction_bits=fbt.spec.get_fraction_bits(); + + smt2_prop.out << "((ind extract " + << fbt.spec.width+fraction_bits-1 << " " + << fraction_bits << ") "; + + forall_operands(it, expr) + if(it!=expr.operands().begin()) + smt2_prop.out << "(bvmul "; + + exprt::operandst::const_iterator last; + forall_operands(it, expr) + { + smt2_prop.out << "((ind sign_extend " << fraction_bits << ") "; + convert_expr(*it); + smt2_prop.out << ") "; + + if(it!=expr.operands().begin()) + smt2_prop.out << ")"; + } + + smt2_prop.out << ")"; + } + else if(expr.type().id()==ID_rational) + { + smt2_prop.out << "(*"; + + forall_operands(it, expr) + { + smt2_prop.out << " "; + convert_expr(*it); + } + + smt2_prop.out << ")"; + } + else + throw "unsupported type for *: "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_with + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_with(const exprt &expr) +{ + assert(expr.operands().size()>=3); + + const typet &expr_type=ns.follow(expr.type()); + + if(expr_type.id()==ID_array) + { + smt2_prop.out << "(store "; + + convert_expr(expr.op0()); + + for(unsigned i=1; imember_width); + smt2_prop.out << "(concat "; + smt2_prop.out << "((ind extract " + << (total_width-1) + << " " << member_width << ") "; + convert_expr(expr.op0()); + smt2_prop.out << ") "; + // TODO: need to deal with booleans + convert_expr(value); + smt2_prop.out << ")"; + } + } + else + { + assert(false); + } + } + else + throw "with expects struct, union, or array type, " + "but got "+expr.type().id_string(); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_index + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_index(const index_exprt &expr) +{ + assert(expr.operands().size()==2); + + smt2_prop.out << "(select "; + convert_expr(expr.array()); + smt2_prop.out << " "; + array_index(expr.index()); + smt2_prop.out << ")"; +} + +/*******************************************************************\ + +Function: smt2_convt::convert_member + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_member(const member_exprt &expr) +{ + assert(expr.operands().size()==1); + + const member_exprt &member_expr=to_member_expr(expr); + const exprt &struct_op=member_expr.struct_op(); + const typet &struct_op_type=ns.follow(struct_op.type()); + const irep_idt &name=member_expr.get_component_name(); + + if(struct_op_type.id()==ID_struct) + { + const struct_typet &struct_type= + to_struct_type(struct_op_type); + + if(!struct_type.has_component(name)) + throw "failed to find struct member"; + + unsigned number=struct_type.component_number(name); + + smt2_prop.out << "((ind project " + << struct_type.components().size() + << " " << (number+1) << ") "; + convert_expr(struct_op); + smt2_prop.out << ")"; + } + else if(struct_op_type.id()==ID_union) + { + if(expr.type().id()==ID_signedbv || + expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_fixedbv || + expr.type().id()==ID_bv) + { + boolbv_widtht boolbv_width(ns); + + unsigned width=boolbv_width(expr.type()); + + if(width==0) + throw "failed to get union member width"; + + smt2_prop.out << "((ind extract " + << (width-1) + << " 0) "; + convert_expr(struct_op); + smt2_prop.out << ")"; + } + else if(expr.type().id()==ID_bool) + { + smt2_prop.out << "(= "; + smt2_prop.out << "((ind extract 0 0) "; + convert_expr(struct_op); + smt2_prop.out << ")"; + smt2_prop.out << " bit1)"; + } + else + throw "union member not implemented"; + } + else + assert(false); +} + +/*******************************************************************\ + +Function: smt2_convt::convert_overflow + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::convert_overflow(const exprt &expr) +{ +} + +/*******************************************************************\ + +Function: smt2_convt::set_to + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::set_to(const exprt &expr, bool value) +{ + if(expr.id()==ID_and && value) + { + forall_operands(it, expr) + set_to(*it, true); + return; + } + + if(expr.id()==ID_not) + { + assert(expr.operands().size()==1); + return set_to(expr.op0(), !value); + } + + smt2_prop.out << std::endl; + + find_symbols(expr); + + #if 0 + smt2_prop.out << "; CONV: " + << from_expr(expr) << std::endl; + #endif + + smt2_prop.out << ":assumption ; set_to " + << (value?"true":"false") << std::endl + << " "; + + assert(expr.type().id()==ID_bool); + + if(!value) + { + smt2_prop.out << "(not "; + convert_expr(expr); + smt2_prop.out << ")"; + } + else + convert_expr(expr); + + smt2_prop.out << std::endl; +} + +/*******************************************************************\ + +Function: smt2_convt::find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::find_symbols(const exprt &expr) +{ + find_symbols(expr.type()); + + forall_operands(it, expr) + find_symbols(*it); + + if(expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol) + { + // we don't track function-typed symbols + if(expr.type().id()==ID_code) + return; + + irep_idt identifier; + + if(expr.id()==ID_symbol) + identifier=to_symbol_expr(expr).get_identifier(); + else + identifier="nondet_"+id2string(expr.get(ID_identifier)); + + identifiert &id=identifier_map[identifier]; + + if(id.type.is_nil()) + { + id.type=expr.type(); + + if(id.type.id()==ID_bool) + { + smt2_prop.out << ":extrapreds((" + << convert_identifier(identifier) + << "))" << std::endl; + } + else + { + smt2_prop.out << ":extrafuns((" + << convert_identifier(identifier) + << " "; + convert_type(expr.type()); + smt2_prop.out << "))" << std::endl; + } + } + } + else if(expr.id()==ID_array_of) + { + if(array_of_map.find(expr)==array_of_map.end()) + { + irep_idt id="array_of'"+i2string(array_of_map.size()); + smt2_prop.out << "; the following is a poor substitute for lambda i. x" << std::endl; + smt2_prop.out << ":extrafuns((" + << id + << " "; + convert_type(expr.type()); + smt2_prop.out << "))" << std::endl; + + // we can initialize array_ofs if they have + // a constant size and a constant element + if(expr.type().find(ID_size)!=get_nil_irep() && + expr.op0().id()==ID_constant) + { + const array_typet &array_type=to_array_type(expr.type()); + mp_integer size; + + if(!to_integer(array_type.size(), size)) + { + // since we can't use quantifiers, let's enumerate... + for(mp_integer i=0; i recstack; + find_symbols_rec(type, recstack); +} + +/*******************************************************************\ + +Function: smt2_convt::find_symbols_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_convt::find_symbols_rec( + const typet &type, + std::set &recstack) +{ + if(type.id()==ID_array) + { + const array_typet &array_type=to_array_type(type); + find_symbols(array_type.size()); + find_symbols_rec(array_type.subtype(), recstack); + } + else if(type.id()==ID_incomplete_array) + { + find_symbols_rec(type.subtype(), recstack); + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + const struct_union_typet::componentst &components= + to_struct_union_type(type).components(); + + for(unsigned i=0; i +#include + +#include + +#include +#include + +#include "smt2_prop.h" + +class smt2_prop_wrappert +{ +public: + smt2_prop_wrappert( + const std::string &_benchmark, + const std::string &_notes, + const std::string &_logic, + std::ostream &_out): + smt2_prop(_benchmark, _notes, _logic, _out) + { } + +protected: + smt2_propt smt2_prop; +}; + +class smt2_convt: + protected smt2_prop_wrappert, + public prop_convt +{ +public: + smt2_convt( + const namespacet &_ns, + const std::string &_benchmark, + const std::string &_notes, + const std::string &_logic, + std::ostream &_out): + smt2_prop_wrappert(_benchmark, _notes, _logic, _out), + prop_convt(_ns, smt2_prop), + pointer_logic(_ns), + array_index_bits(32) + { } + + virtual ~smt2_convt() { } + virtual resultt dec_solve(); + +protected: + // overloading + virtual literalt convert(const exprt &expr); + virtual void set_to(const exprt &expr, bool value); + virtual exprt get(const exprt &expr) const; + + // new stuff + void convert_expr(const exprt &expr); + void convert_type(const typet &type); + + // specific expressions go here + void convert_byte_update(const exprt &expr); + void convert_byte_extract(const exprt &expr); + void convert_typecast(const class typecast_exprt &expr); + void convert_struct(const exprt &expr); + void convert_union(const exprt &expr); + void convert_constant(const class constant_exprt &expr); + void convert_relation(const exprt &expr); + void convert_is_dynamic_object(const exprt &expr); + void convert_plus(const exprt &expr); + void convert_minus(const exprt &expr); + void convert_div(const exprt &expr); + void convert_mul(const exprt &expr); + void convert_mod(const exprt &expr); + void convert_index(const class index_exprt &expr); + void convert_member(const class member_exprt &expr); + void convert_overflow(const exprt &expr); + void convert_with(const exprt &expr); + + std::string convert_identifier(const irep_idt &identifier); + + // auxiliary methods + void find_symbols(const exprt &expr); + void find_symbols(const typet &type); + void find_symbols_rec(const typet &type, std::set &recstack); + + // arrays + typet array_index_type() const; + void array_index(const exprt &expr); + + // pointers + pointer_logict pointer_logic; + void convert_address_of_rec(const exprt &expr); + + // keeps track of all symbols + struct identifiert + { + typet type; + exprt value; + + identifiert() + { + type.make_nil(); + value.make_nil(); + } + }; + + void set_value( + identifiert &identifier, + const std::string &v); + + typedef hash_map_cont + identifier_mapt; + + identifier_mapt identifier_map; + + unsigned array_index_bits; + + // for replacing 'array_of' + typedef std::map array_of_mapt; + array_of_mapt array_of_map; + + // for replacing 'array initializers' + typedef std::map array_init_mapt; + array_init_mapt array_init_map; + + // for replacing string constants + typedef std::map string2array_mapt; + string2array_mapt string2array_map; +}; + +#endif diff --git a/src/solvers/smt2/smt2_dec.cpp b/src/solvers/smt2/smt2_dec.cpp new file mode 100644 index 00000000000..4e47c61a10b --- /dev/null +++ b/src/solvers/smt2/smt2_dec.cpp @@ -0,0 +1,591 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "smt2_dec.h" + +/*******************************************************************\ + +Function: smt2_dect::decision_procedure_text + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string smt2_dect::decision_procedure_text() const +{ + return "SMT "+logic+" using "+ + (solver==BOOLECTOR?"Boolector": + solver==CVC3?"CVC3": + solver==Z3?"Z3": + solver==YICES?"Yices": + "(unknown)"); +} + +/*******************************************************************\ + +Function: smt2_temp_filet::smt2_temp_filet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +smt2_temp_filet::smt2_temp_filet() +{ + temp_out_filename=get_temporary_file("smt2_dec_out_", ""); + + temp_out.open( + temp_out_filename.c_str(), + std::ios_base::out | std::ios_base::trunc + ); +} + +/*******************************************************************\ + +Function: smt2_temp_filet::~smt2_temp_filet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +smt2_temp_filet::~smt2_temp_filet() +{ + temp_out.close(); + + if(temp_out_filename!="") + unlink(temp_out_filename.c_str()); + + if(temp_result_filename!="") + unlink(temp_result_filename.c_str()); +} + +/*******************************************************************\ + +Function: smt2_dect::dec_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt smt2_dect::dec_solve() +{ + post_process(); + + // this closes the SMT benchmark + smt2_prop.finalize(); + temp_out.close(); + + temp_result_filename= + get_temporary_file("smt2_dec_result_", ""); + + std::string command; + + switch(solver) + { + case CVC3: + command = "cvc3 +model -lang smtlib -output-lang smtlib " + + temp_out_filename + + " > " + + temp_result_filename; + break; + + case YICES: + command = "yices -smt -e " + + temp_out_filename + + " > " + + temp_result_filename; + break; + + case BOOLECTOR: + command = "boolector --smt " + + temp_out_filename + + " -fm --output " + + temp_result_filename; + break; + + case Z3: + command = "z3 -m " + + temp_out_filename + + " > " + + temp_result_filename; + break; + + default: + assert(false); + } + + #if defined(__LINUX__) || defined(__APPLE__) + command+=" 2>&1"; + #endif + + system(command.c_str()); + + std::ifstream in(temp_result_filename.c_str()); + + switch(solver) + { + case CVC3: + return read_result_cvc3(in); + + case YICES: + return read_result_yices(in); + + case BOOLECTOR: + return read_result_boolector(in); + + case Z3: + return read_result_z3(in); + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: smt2_dect::read_result_boolector + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt smt2_dect::read_result_boolector(std::istream &in) +{ + std::string line; + + str_getline(in, line); + + if(line=="sat") + { + smt2_prop.reset_assignment(); + + typedef hash_map_cont valuest; + valuest values; + + while(str_getline(in, line)) + { + std::size_t pos=line.find(' '); + if(pos!=std::string::npos) + values[std::string(line, 0, pos)]= + std::string(line, pos+1, std::string::npos); + } + + // Theory variables + + for(identifier_mapt::iterator + it=identifier_map.begin(); + it!=identifier_map.end(); + it++) + { + it->second.value.make_nil(); + std::string conv_id=convert_identifier(it->first); + std::string value=values[conv_id]; + if(value=="") continue; + set_value(it->second, value); + } + + // Booleans + + for(unsigned v=0; v valuest; + valuest values; + + while(str_getline(in, line)) + { + if(line=="sat") + res = D_SATISFIABLE; + else if(line=="unsat") + res = D_UNSATISFIABLE; + else + { + std::size_t pos=line.find(" -> "); + if(pos!=std::string::npos) + values[std::string(line, 0, pos)]= + std::string(line, pos+4, std::string::npos); + } + } + + for(identifier_mapt::iterator + it=identifier_map.begin(); + it!=identifier_map.end(); + it++) + { + it->second.value.make_nil(); + std::string conv_id=convert_identifier(it->first); + std::string value=values[conv_id]; + if(value=="") continue; + +// std::cout << it->first << " := " << value << std::endl; + + exprt e; + if(string_to_expr_z3(it->second.type, value, e)) + { +// std::cout << "E: " << e << std::endl; + it->second.value=e; + } + else + set_value(it->second, value); + } + + // Booleans + for(unsigned v=0; vsecond!=id) fit++; + + if(fit==array_of_map.end()) return false; + + e = fit->first; + + return true; + } + else if(type.id()==ID_rational) + { + constant_exprt result; + result.type()=rational_typet(); + + if(value.substr(0,4)=="val!") + result.set_value(value.substr(4)); + else + result.set_value(value); + + e = result; + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: smt2_dect::read_result_cvc3 + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt smt2_dect::read_result_cvc3(std::istream &in) +{ + std::string line; + decision_proceduret::resultt res = D_ERROR; + + smt2_prop.reset_assignment(); + + typedef hash_map_cont valuest; + valuest values; + + while(str_getline(in, line)) + { + if(line=="sat") + res = D_SATISFIABLE; + else if(line=="unsat") + res = D_UNSATISFIABLE; + else if(line.find("Current scope level")!=std::string::npos || + line.find("Variable Assignment")!=std::string::npos) + ; //ignore + else + { + assert(line.substr(0,13)==" :assumption"); + std::size_t pos=line.find('('); + + if(pos!=std::string::npos) + { + std::string var; + std::string val; + + if(line[pos+1]=='=') + { + std::string ops = line.substr(pos+3, line.length()-pos-4); + std::size_t blank=ops.find(' '); + var = ops.substr(0, blank); + val = ops.substr(blank+1, ops.length()-blank); + + if((var.length()>=4 && var.substr(0,4)=="cvc3") || + (val.length()>=4 && val.substr(0,4)=="cvc3") || + var==val) + continue; + else if((var.substr(0,9)=="array_of'") || + (var.substr(0,2)=="bv" && val.substr(0,2)!="bv")) + { + std::string t=var; var=val; val=t; + } + +// std::cout << "OPS: " << ops << std::endl; + } + else if(line.substr(pos+1,3)=="not") + { + var = line.substr(pos+5, line.length()-pos-6); + val = "false"; + } + else + { + var = line.substr(pos+1, line.length()-pos-2); + assert(var.find(' ')==std::string::npos); + val = "true"; + } + +// std::cout << "VAR: " << var << std::endl; +// std::cout << "VAL: " << val << std::endl; + + values[var]=val; + } + } + } + + for(identifier_mapt::iterator + it=identifier_map.begin(); + it!=identifier_map.end(); + it++) + { + it->second.value.make_nil(); + std::string conv_id=convert_identifier(it->first); + std::string value=values[conv_id]; + if(value=="") continue; + +// std::cout << it->first << " := " << value << std::endl; + + if(value.substr(0,2)=="bv") + { + std::string v=value.substr(2, value.find('[')-2); + size_t p = value.find('[')+1; + std::string w=value.substr(p, value.find(']')-p); + + std::string binary=integer2binary(string2integer(v,10), + string2integer(w,10).to_ulong()); + + set_value(it->second, binary); + } + else if(value=="false") + it->second.value.make_false(); + else if(value=="true") + it->second.value.make_true(); + else if(value.substr(0,8)=="array_of") + { + // We assume that array_of has only concrete arguments... + irep_idt id(value); + array_of_mapt::const_iterator fit=array_of_map.begin(); + while(fit!=array_of_map.end() && fit->second!=id) fit++; + + if(fit!=array_of_map.end()) + it->second.value = fit->first; + } + else + set_value(it->second, value); + } + + // Booleans + for(unsigned v=0; v + +#include "smt2_conv.h" + +class smt2_temp_filet +{ +public: + smt2_temp_filet(); + ~smt2_temp_filet(); + +protected: + std::ofstream temp_out; + std::string temp_out_filename, temp_result_filename; +}; + +class smt2_dect:protected smt2_temp_filet, public smt2_convt +{ +public: + typedef enum { BOOLECTOR, CVC3, YICES, Z3 } solvert; + + smt2_dect( + const namespacet &_ns, + const std::string &_benchmark, + const std::string &_notes, + const std::string &_logic, + solvert _solver): + smt2_temp_filet(), + smt2_convt(_ns, _benchmark, _notes, _logic, temp_out), + logic(_logic), + solver(_solver) + { + } + + virtual resultt dec_solve(); + virtual std::string decision_procedure_text() const; + +protected: + std::string logic; + solvert solver; + + resultt read_result_boolector(std::istream &in); + resultt read_result_cvc3(std::istream &in); + resultt read_result_yices(std::istream &in); + resultt read_result_z3(std::istream &in); + + bool string_to_expr_z3( + const typet &type, + const std::string &value, exprt &e) const; +}; + +#endif diff --git a/src/solvers/smt2/smt2_prop.cpp b/src/solvers/smt2/smt2_prop.cpp new file mode 100644 index 00000000000..189a2f77e7d --- /dev/null +++ b/src/solvers/smt2/smt2_prop.cpp @@ -0,0 +1,528 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "smt2_prop.h" + +/*******************************************************************\ + +Function: smt2_propt::smt2_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +smt2_propt::smt2_propt( + const std::string &benchmark, + const std::string ¬es, + const std::string &logic, + std::ostream &_out):out(_out) +{ + out << "(benchmark " << benchmark << std::endl; + out << ":notes \"" << notes << "\"" << std::endl; + out << ":status unknown" << std::endl; + out << ":logic " << logic << " ; SMT 2" << std::endl; + _no_variables=0; +} + +/*******************************************************************\ + +Function: smt2_propt::~smt2_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +smt2_propt::~smt2_propt() +{ +} + +/*******************************************************************\ + +Function: smt2_propt::finalize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_propt::finalize() +{ + out << std::endl; + out << ":formula true" << std::endl; + out << ") ; benchmark" << std::endl; +} + +/*******************************************************************\ + +Function: smt2_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt smt2_propt::land(const bvt &bv) +{ + out << std::endl; + + literalt l=new_variable(); + + out << ":assumption ; land" << std::endl; + out << " (= " << smt2_literal(l) << " (and"; + + for(unsigned int i=0; i=assignment.size()) return tvt(tvt::TV_UNKNOWN); + tvt r=assignment[v]; + return literal.sign()?!r:r; +} + +/*******************************************************************\ + +Function: smt2_propt::set_assignment + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void smt2_propt::set_assignment(literalt literal, bool value) +{ + if(literal.is_true() || literal.is_false()) return; + + unsigned v=literal.var_no(); + assert(v + +#include + +class smt2_propt:public propt +{ +public: + smt2_propt( + const std::string &_benchmark, + const std::string &_notes, + const std::string &_logic, + std::ostream &_out); + virtual ~smt2_propt(); + + virtual literalt land(literalt a, literalt b); + virtual literalt lor(literalt a, literalt b); + virtual literalt land(const bvt &bv); + virtual literalt lor(const bvt &bv); + virtual literalt lxor(const bvt &bv); + virtual literalt lnot(literalt a); + virtual literalt lxor(literalt a, literalt b); + virtual literalt lnand(literalt a, literalt b); + virtual literalt lnor(literalt a, literalt b); + virtual literalt lequal(literalt a, literalt b); + virtual literalt limplies(literalt a, literalt b); + virtual literalt lselect(literalt a, literalt b, literalt c); // a?b:c + + virtual literalt new_variable(); + virtual unsigned no_variables() const { return _no_variables; } + virtual void set_no_variables(unsigned no) { assert(false); } + + virtual void lcnf(const bvt &bv); + + virtual const std::string solver_text() + { return "SMT"; } + + virtual tvt l_get(literalt literal) const; + virtual void set_assignment(literalt a, bool value); + + virtual propt::resultt prop_solve(); + + virtual void clear() + { + assignment.clear(); + } + + virtual void reset_assignment() + { + assignment.clear(); + assignment.resize(no_variables(), tvt(tvt::TV_UNKNOWN)); + } + + friend class smt2_convt; + friend class smt2_dect; + + void finalize(); + +protected: + unsigned _no_variables; + std::ostream &out; + + std::string smt2_literal(literalt l); + literalt def_smt2_literal(); + + std::vector assignment; +}; + +#endif diff --git a/src/solvers/z3/z3_capi.cpp b/src/solvers/z3/z3_capi.cpp new file mode 100644 index 00000000000..2a47175a4b6 --- /dev/null +++ b/src/solvers/z3/z3_capi.cpp @@ -0,0 +1,671 @@ +/*******************************************************************\ + +Module: + +Author: + +\*******************************************************************/ + +#include "z3_capi.h" + +/** + \defgroup capi_ex C API examples +*/ +/*@{*/ +/** + @name Auxiliary Functions +*/ +/*@{*/ + +/** + \brief exit gracefully in case of error. +*/ +void exitf(const char* message) +{ + fprintf(stderr,"%s\n", message); + exit(1); +} + +/** + \brief exit if unreachable code was reached. +*/ +void unreachable() +{ + exitf("unreachable code was reached"); +} + +/** + \brief Simpler error handler. + */ +void error_handler(Z3_error_code e) +{ + printf("Error code: %d\n", e); + exitf("incorrect use of Z3"); +} + +static jmp_buf g_catch_buffer; +/** + \brief Low tech exceptions. + + In high-level programming languages, an error handler can throw an exception. +*/ +void throw_z3_error(Z3_error_code c) +{ + longjmp(g_catch_buffer, c); +} + +/** + \brief Create a logical context. + + Enable model construction. Other configuration parameters can be passed in the cfg variable. + + Also enable tracing to stderr and register custom error handler. +*/ +Z3_context z3_capi::mk_context_custom(Z3_config cfg, Z3_error_handler err) +{ + Z3_context ctx; + + Z3_set_param_value(cfg, "MODEL", "true"); + ctx = Z3_mk_context(cfg); +#ifdef TRACING + Z3_trace_to_stderr(ctx); +#endif + Z3_set_error_handler(ctx, err); + + return ctx; +} + +/** + \brief Create a logical context. + + Enable model construction only. + + Also enable tracing to stderr and register standard error handler. +*/ +Z3_context z3_capi::mk_context() +{ + Z3_config cfg; + Z3_context ctx; + cfg = Z3_mk_config(); + Z3_set_param_value(cfg,"SOLVER","true"); + Z3_set_param_value(cfg,"RELEVANCY","0"); + ctx = mk_context_custom(cfg, error_handler); + Z3_del_config(cfg); + return ctx; +} + +/** + \brief Create a variable using the given name and type. +*/ +Z3_ast z3_capi::mk_var(Z3_context ctx, const char * name, Z3_sort ty) +{ + Z3_symbol s = Z3_mk_string_symbol(ctx, name); + return Z3_mk_const(ctx, s, ty); +} + +/** + \brief Create a boolean variable using the given name. +*/ +Z3_ast z3_capi::mk_bool_var(Z3_context ctx, const char * name) +{ + Z3_sort ty = Z3_mk_bool_sort(ctx); + return mk_var(ctx, name, ty); +} + +/** + \brief Create an integer variable using the given name. +*/ +Z3_ast z3_capi::mk_int_var(Z3_context ctx, const char * name) +{ + Z3_sort ty = Z3_mk_int_sort(ctx); + return mk_var(ctx, name, ty); +} + +/** + \brief Create a Z3 integer node using a C int. +*/ +Z3_ast z3_capi::mk_unsigned_int(Z3_context ctx, unsigned int v) +{ + Z3_sort ty = Z3_mk_int_sort(ctx); + return Z3_mk_unsigned_int(ctx, v, ty); +} + +/** + \brief Create a Z3 integer node using a C int. +*/ +Z3_ast z3_capi::mk_int(Z3_context ctx, int v) +{ + Z3_sort ty = Z3_mk_int_sort(ctx); + return Z3_mk_int(ctx, v, ty); +} + +/** + \brief Create a real variable using the given name. +*/ +Z3_ast z3_capi::mk_real_var(Z3_context ctx, const char * name) +{ + Z3_sort ty = Z3_mk_real_sort(ctx); + return mk_var(ctx, name, ty); +} + +/** + \brief Create the unary function application: (f x). +*/ +Z3_ast z3_capi::mk_unary_app(Z3_context ctx, Z3_func_decl f, Z3_ast x) +{ + Z3_ast args[1] = {x}; + return Z3_mk_app(ctx, f, 1, args); +} + +/** + \brief Create the binary function application: (f x y). +*/ +Z3_ast z3_capi::mk_binary_app(Z3_context ctx, Z3_func_decl f, Z3_ast x, Z3_ast y) +{ + Z3_ast args[2] = {x, y}; + return Z3_mk_app(ctx, f, 2, args); +} + +/** + \brief Check whether the logical context is satisfiable, and compare the result with the expected result. + If the context is satisfiable, then display the model. +*/ +Z3_lbool z3_capi::check(Z3_context ctx, Z3_lbool expected_result) +{ + Z3_model m = 0; + Z3_lbool result = Z3_check_and_get_model(ctx, &m); + switch (result) { + case Z3_L_FALSE: + printf("unsat\n"); + break; + case Z3_L_UNDEF: + printf("unknown\n"); + printf("potential model:\n%s\n", Z3_model_to_string(ctx, m)); + break; + case Z3_L_TRUE: + printf("sat\n%s\n", Z3_model_to_string(ctx, m)); + break; + } + if (m) { + Z3_del_model(ctx, m); + } + if (result != expected_result) { + //exitf("unexpected result"); + } + + return result; +} + +/** + \brief Prove that the constraints already asserted into the logical + context implies the given formula. The result of the proof is + displayed. + + Z3 is a satisfiability checker. So, one can prove \c f by showing + that (not f) is unsatisfiable. + + The context \c ctx is not modified by this function. +*/ +void z3_capi::prove(Z3_context ctx, Z3_ast f, Z3_bool is_valid) +{ + Z3_model m; + Z3_ast not_f; + + /* save the current state of the context */ + Z3_push(ctx); + + not_f = Z3_mk_not(ctx, f); + Z3_assert_cnstr(ctx, not_f); + + m = 0; + + switch (Z3_check_and_get_model(ctx, &m)) { + case Z3_L_FALSE: + /* proved */ + printf("valid\n"); + if (!is_valid) { + //exitf("unexpected result"); + } + break; + case Z3_L_UNDEF: + /* Z3 failed to prove/disprove f. */ + printf("unknown\n"); + if (m != 0) { + /* m should be viewed as a potential counterexample. */ + printf("potential counterexample:\n%s\n", Z3_model_to_string(ctx, m)); + } + if (is_valid) { + //exitf("unexpected result"); + } + break; + case Z3_L_TRUE: + /* disproved */ + printf("invalid\n"); + if (m) { + /* the model returned by Z3 is a counterexample */ + printf("counterexample:\n%s\n", Z3_model_to_string(ctx, m)); + } + if (is_valid) { + //exitf("unexpected result"); + } + break; + } + + if (m) { + Z3_del_model(ctx, m); + } + + /* restore context */ + Z3_pop(ctx, 1); +} + +/** + \brief Assert the axiom: function f is injective in the i-th argument. + + The following axiom is asserted into the logical context: + \code + forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i + \endcode + + Where, \c finv is a fresh function declaration. +*/ +void z3_capi::assert_inj_axiom(Z3_context ctx, Z3_func_decl f, unsigned i) +{ + unsigned sz, j; + Z3_sort finv_domain, finv_range; + Z3_func_decl finv; + Z3_sort * types; /* types of the quantified variables */ + Z3_symbol * names; /* names of the quantified variables */ + Z3_ast * xs; /* arguments for the application f(x_0, ..., x_i, ..., x_{n-1}) */ + Z3_ast x_i, fxs, finv_fxs, eq; + Z3_pattern p; + Z3_ast q; + sz = Z3_get_domain_size(ctx, f); + + if (i >= sz) { + exitf("failed to create inj axiom"); + } + + /* declare the i-th inverse of f: finv */ + finv_domain = Z3_get_range(ctx, f); + finv_range = Z3_get_domain(ctx, f, i); + finv = Z3_mk_fresh_func_decl(ctx, "inv", 1, &finv_domain, finv_range); + + /* allocate temporary arrays */ + types = (Z3_sort *) malloc(sizeof(Z3_sort) * sz); + names = (Z3_symbol *) malloc(sizeof(Z3_symbol) * sz); + xs = (Z3_ast *) malloc(sizeof(Z3_ast) * sz); + + /* fill types, names and xs */ + for (j = 0; j < sz; j++) { types[j] = Z3_get_domain(ctx, f, j); }; + for (j = 0; j < sz; j++) { names[j] = Z3_mk_int_symbol(ctx, j); }; + for (j = 0; j < sz; j++) { xs[j] = Z3_mk_bound(ctx, j, types[j]); }; + + x_i = xs[i]; + + /* create f(x_0, ..., x_i, ..., x_{n-1}) */ + fxs = Z3_mk_app(ctx, f, sz, xs); + + /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ + finv_fxs = mk_unary_app(ctx, finv, fxs); + + /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ + eq = Z3_mk_eq(ctx, finv_fxs, x_i); + + /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ + p = Z3_mk_pattern(ctx, 1, &fxs); + printf("pattern: %s\n", Z3_pattern_to_string(ctx, p)); + printf("\n"); + + /* create & assert quantifier */ + q = Z3_mk_forall(ctx, + 0, /* using default weight */ + 1, /* number of patterns */ + &p, /* address of the "array" of patterns */ + sz, /* number of quantified variables */ + types, + names, + eq); + printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, q)); + Z3_assert_cnstr(ctx, q); + + /* free temporary arrays */ + free(types); + free(names); + free(xs); +} + +/** + \brief Assert the axiom: function f is commutative. + + This example uses the SMT-LIB parser to simplify the axiom construction. +*/ +void z3_capi::assert_comm_axiom(Z3_context ctx, Z3_func_decl f) +{ + Z3_sort t; + Z3_symbol f_name, t_name; + Z3_ast q; + + t = Z3_get_range(ctx, f); + + if (Z3_get_domain_size(ctx, f) != 2 || + Z3_get_domain(ctx, f, 0) != t || + Z3_get_domain(ctx, f, 1) != t) { + exitf("function must be binary, and argument types must be equal to return type"); + } + + /* Inside the parser, function f will be referenced using the symbol 'f'. */ + f_name = Z3_mk_string_symbol(ctx, "f"); + + /* Inside the parser, type t will be referenced using the symbol 'T'. */ + t_name = Z3_mk_string_symbol(ctx, "T"); + + Z3_parse_smtlib_string(ctx, + "(benchmark comm :formula (forall (x T) (y T) (= (f x y) (f y x))))", + 1, &t_name, &t, + 1, &f_name, &f); + q = Z3_get_smtlib_formula(ctx, 0); + printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, q)); + Z3_assert_cnstr(ctx, q); +} + +/** + \brief Z3 does not support explicitly tuple updates. They can be easily implemented + as macros. The argument \c t must have tuple type. + A tuple update is a new tuple where field \c i has value \c new_val, and all + other fields have the value of the respective field of \c t. + + update(t, i, new_val) is equivalent to + mk_tuple(proj_0(t), ..., new_val, ..., proj_n(t)) +*/ +Z3_ast z3_capi::mk_tuple_update(Z3_context c, Z3_ast t, unsigned i, Z3_ast new_val) +{ + Z3_sort ty; + Z3_func_decl mk_tuple_decl; + unsigned num_fields, j; + Z3_ast * new_fields; + Z3_ast result; + + ty = Z3_get_sort(c, t); + + if (Z3_get_sort_kind(c, ty) != Z3_DATATYPE_SORT) { + exitf("argument must be a tuple"); + } + + num_fields = Z3_get_tuple_sort_num_fields(c, ty); + + if (i >= num_fields) { + exitf("invalid tuple update, index is too big"); + } + + new_fields = (Z3_ast*) malloc(sizeof(Z3_ast) * num_fields); + for (j = 0; j < num_fields; j++) { + if (i == j) { + /* use new_val at position i */ + new_fields[j] = new_val; + } + else { + /* use field j of t */ + Z3_func_decl proj_decl = Z3_get_tuple_sort_field_decl(c, ty, j); + new_fields[j] = mk_unary_app(c, proj_decl, t); + } + } + mk_tuple_decl = Z3_get_tuple_sort_mk_decl(c, ty); + result = Z3_mk_app(c, mk_tuple_decl, num_fields, new_fields); + free(new_fields); + return result; +} + +/** + \brief Display a symbol in the given output stream. +*/ +void z3_capi::display_symbol(Z3_context c, FILE * out, Z3_symbol s) +{ + switch (Z3_get_symbol_kind(c, s)) { + case Z3_INT_SYMBOL: + fprintf(out, "#%d", Z3_get_symbol_int(c, s)); + break; + case Z3_STRING_SYMBOL: + fprintf(out, Z3_get_symbol_string(c, s)); + break; + default: + unreachable(); + } +} + +/** + \brief Display the given type. +*/ +void z3_capi::display_sort(Z3_context c, FILE * out, Z3_sort ty) +{ + switch (Z3_get_sort_kind(c, ty)) { + case Z3_UNINTERPRETED_SORT: + display_symbol(c, out, Z3_get_sort_name(c, ty)); + break; + case Z3_BOOL_SORT: + fprintf(out, "bool"); + break; + case Z3_INT_SORT: + fprintf(out, "int"); + break; + case Z3_REAL_SORT: + fprintf(out, "real"); + break; + case Z3_BV_SORT: + fprintf(out, "bv%d", Z3_get_bv_sort_size(c, ty)); + break; + case Z3_ARRAY_SORT: + fprintf(out, "["); + display_sort(c, out, Z3_get_array_sort_domain(c, ty)); + fprintf(out, "->"); + display_sort(c, out, Z3_get_array_sort_range(c, ty)); + fprintf(out, "]"); + break; + case Z3_DATATYPE_SORT: + if (Z3_get_datatype_sort_num_constructors(c, ty) != 1) + { + fprintf(out, "%s", Z3_sort_to_string(c,ty)); + break; + } + { + unsigned num_fields = Z3_get_tuple_sort_num_fields(c, ty); + unsigned i; + fprintf(out, "("); + for (i = 0; i < num_fields; i++) { + Z3_func_decl field = Z3_get_tuple_sort_field_decl(c, ty, i); + if (i > 0) { + fprintf(out, ", "); + } + display_sort(c, out, Z3_get_range(c, field)); + } + fprintf(out, ")"); + break; + } + default: + fprintf(out, "unknown["); + display_symbol(c, out, Z3_get_sort_name(c, ty)); + fprintf(out, "]"); + break; + } +} + +/** + \brief Custom ast pretty printer. + + This function demonstrates how to use the API to navigate terms. +*/ +void z3_capi::display_ast(Z3_context c, FILE * out, Z3_ast v) +{ + switch (Z3_get_ast_kind(c, v)) { + case Z3_NUMERAL_AST: { + Z3_sort t; + fprintf(out, Z3_get_numeral_string(c, v)); + t = Z3_get_sort(c, v); + fprintf(out, ":"); + display_sort(c, out, t); + break; + } + case Z3_APP_AST: { + unsigned i; + Z3_app app = Z3_to_app(c, v); + unsigned num_fields = Z3_get_app_num_args(c, app); + Z3_func_decl d = Z3_get_app_decl(c, app); + fprintf(out, Z3_func_decl_to_string(c, d)); + if (num_fields > 0) { + fprintf(out, "["); + for (i = 0; i < num_fields; i++) { + if (i > 0) { + fprintf(out, ", "); + } + display_ast(c, out, Z3_get_app_arg(c, app, i)); + } + fprintf(out, "]"); + } + break; + } + case Z3_QUANTIFIER_AST: { + fprintf(out, "quantifier"); + ; + } + default: + fprintf(out, "#unknown"); + } +} + +/** + \brief Custom function interpretations pretty printer. +*/ +void z3_capi::display_function_interpretations(Z3_context c, FILE * out, Z3_model m) +{ + unsigned num_functions, i; + + //fprintf(out, "function interpretations:\n"); + + num_functions = Z3_get_model_num_funcs(c, m); + for (i = 0; i < num_functions; i++) { + Z3_func_decl fdecl; + Z3_symbol name; + Z3_ast func_else; + unsigned num_entries, j; + + fdecl = Z3_get_model_func_decl(c, m, i); + name = Z3_get_decl_name(c, fdecl); + display_symbol(c, out, name); + fprintf(out, " = {"); + num_entries = Z3_get_model_func_num_entries(c, m, i); + for (j = 0; j < num_entries; j++) { + unsigned num_args, k; + if (j > 0) { + fprintf(out, ", "); + } + num_args = Z3_get_model_func_entry_num_args(c, m, i, j); + fprintf(out, "("); + for (k = 0; k < num_args; k++) { + if (k > 0) { + fprintf(out, ", "); + } + display_ast(c, out, Z3_get_model_func_entry_arg(c, m, i, j, k)); + } + fprintf(out, "|->"); + display_ast(c, out, Z3_get_model_func_entry_value(c, m, i, j)); + fprintf(out, ")"); + } + if (num_entries > 0) { + fprintf(out, ", "); + } + fprintf(out, "(else|->"); + func_else = Z3_get_model_func_else(c, m, i); + display_ast(c, out, func_else); + fprintf(out, ")}\n"); + } +} + +/** + \brief Custom model pretty printer. +*/ +void z3_capi::display_model(Z3_context c, FILE * out, Z3_model m) +{ + unsigned num_constants; + unsigned i; + + num_constants = Z3_get_model_num_constants(c, m); + for (i = 0; i < num_constants; i++) { + Z3_symbol name; + Z3_func_decl cnst = Z3_get_model_constant(c, m, i); + Z3_ast a, v; + Z3_bool ok; + name = Z3_get_decl_name(c, cnst); + display_symbol(c, out, name); + fprintf(out, " = "); + a = Z3_mk_app(c, cnst, 0, 0); + v = a; + ok = Z3_eval(c, m, a, &v); + display_ast(c, out, v); + fprintf(out, "\n"); + } + display_function_interpretations(c, out, m); +} + +/** + \brief Similar to #check, but uses #display_model instead of #Z3_model_to_string. +*/ +Z3_lbool z3_capi::check2(Z3_context ctx, Z3_lbool expected_result) +{ + Z3_model m = 0; + Z3_lbool result = Z3_check_and_get_model(ctx, &m); + switch (result) { + case Z3_L_FALSE: + //printf("unsat\n"); + break; + case Z3_L_UNDEF: + printf("unknown\n"); + printf("potential model:\n"); + //display_model(ctx, stdout, m); + break; + case Z3_L_TRUE: + //printf("sat\n"); + //printf("Counterexample:\n"); + //display_model(ctx, stdout, m); + break; + } + if (m) { + Z3_del_model(ctx, m); + } + if (result != expected_result) { + //exitf("unexpected result"); + } + + return result; +} + +/** + \brief Display Z3 version in the standard output. +*/ +void z3_capi::display_version() +{ + unsigned major, minor, build, revision; + Z3_get_version(&major, &minor, &build, &revision); + printf("Z3 %d.%d.%d.%d\n", major, minor, build, revision); +} +/*@}*/ + +Z3_ast z3_capi::mk_tuple_select(Z3_context c, Z3_ast t, unsigned i) +{ + Z3_type_ast ty; + unsigned num_fields; + + ty = Z3_get_type(c, t); + + if (Z3_get_type_kind(c, ty) != Z3_TUPLE_TYPE) { + exitf("argument must be a tuple"); + } + + num_fields = Z3_get_tuple_type_num_fields(c, ty); + + if (i >= num_fields) { + exitf("invalid tuple select, index is too big"); + } + + Z3_const_decl_ast proj_decl = Z3_get_tuple_type_field_decl(c, ty, i); + return mk_unary_app(c, proj_decl, t); +} diff --git a/src/solvers/z3/z3_capi.h b/src/solvers/z3/z3_capi.h new file mode 100644 index 00000000000..12a6b1d59b7 --- /dev/null +++ b/src/solvers/z3/z3_capi.h @@ -0,0 +1,49 @@ +/*******************************************************************\ + +Module: + +Author: + +\*******************************************************************/ + +#ifndef CPROVER_SOLVERS_Z3_CAPI_H +#define CPROVER_SOLVERS_Z3_CAPI_H + +class z3_capi +{ +public: + z3_capi(); // constructor + ~z3_capi(); // destructor + + #if 0 + Z3_context mk_context(); + Z3_ast mk_var(Z3_context ctx, const char * name, Z3_type_ast ty); + Z3_ast mk_bool_var(Z3_context ctx, const char * name); + Z3_ast mk_int_var(Z3_context ctx, const char * name); + Z3_ast mk_int(Z3_context ctx, int v); + Z3_ast mk_unsigned_int(Z3_context ctx, unsigned int v); + Z3_ast mk_real_var(Z3_context ctx, const char * name); + Z3_ast mk_unary_app(Z3_context ctx, Z3_const_decl_ast f, Z3_ast x); + Z3_ast mk_binary_app(Z3_context ctx, Z3_const_decl_ast f, Z3_ast x, Z3_ast y); + Z3_lbool check(Z3_context ctx, Z3_lbool expected_result); + Z3_lbool check2(Z3_context ctx, Z3_lbool expected_result); + void prove(Z3_context ctx, Z3_ast f, Z3_bool is_valid); + void assert_inj_axiom(Z3_context ctx, Z3_const_decl_ast f, unsigned i); + void display_sort(Z3_context c, FILE * out, Z3_sort ty); + void assert_comm_axiom(Z3_context ctx, Z3_const_decl_ast f); + void display_ast(Z3_context c, FILE * out, Z3_ast v); + Z3_ast mk_tuple_update(Z3_context c, Z3_ast t, unsigned i, Z3_ast new_val); + Z3_ast mk_tuple_select(Z3_context c, Z3_ast t, unsigned i); + void display_symbol(Z3_context c, FILE * out, Z3_symbol s); + void display_type(Z3_context c, FILE * out, Z3_type_ast ty); + void display_function_interpretations(Z3_context c, FILE * out, Z3_model m); + void display_model(Z3_context c, FILE * out, Z3_model m); + void display_version(); + +private: + Z3_context mk_context_custom(Z3_config cfg, Z3_error_handler err); + Z3_context z3_ctx; + #endif +}; + +#endif diff --git a/src/solvers/z3/z3_dec.cpp b/src/solvers/z3/z3_dec.cpp new file mode 100644 index 00000000000..fe7418d5190 --- /dev/null +++ b/src/solvers/z3/z3_dec.cpp @@ -0,0 +1,4185 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#include +#include + +#include +#include +#include + +#include "z3_dec.h" + +/*******************************************************************\ + +Function: z3_dect::z3_dect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +z3_dect::z3_dect() +{ +} + +/*******************************************************************\ + +Function: z3_dect::~z3_dect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +z3_dect::~z3_dect() +{ +} + +/*******************************************************************\ + +Function: z3_dect::dec_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt z3_dect::dec_solve() +{ + status("Solving with SMT solver Z3"); + +#if 0 + status(integer2string(get_number_variables_z3()) + " variables, " + + integer2string(get_number_vcs_z3()) + " verification conditions"); +#endif + + post_process(); + + return read_z3_result(); +} + +/*******************************************************************\ + +Function: z3_dect::set_encoding + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_dect::set_encoding(bool enc) +{ + set_z3_encoding(enc); +} + +/*******************************************************************\ + +Function: z3_dect::set_ecp + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_dect::set_ecp(bool ecp) +{ + set_z3_ecp(ecp); +} + +/*******************************************************************\ + +Function: z3_dect::read_assert + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_dect::read_assert(std::istream &in, std::string &line) +{ +} + +/*******************************************************************\ + +Function: z3_dect::read_z3_result + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +decision_proceduret::resultt z3_dect::read_z3_result() +{ + Z3_lbool result; + + result = check2_z3_properties(); + + if (result==Z3_L_FALSE) + return D_UNSATISFIABLE; + else + return D_SATISFIABLE; +} + + +/******************************************************************* + Module: + + Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + + \*******************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "z3_conv.h" +#include "../ansi-c/c_types.h" + +//#define DEBUG + +/******************************************************************* + Function: z3_convt::print_data_types + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +void z3_convt::print_data_types(Z3_ast operand0, Z3_ast operand1) +{ + Z3_type_ast a, b; + + a = Z3_get_type(z3_ctx, operand0); + std::cout << "operand0 type:" << std::endl; + std::cout << Z3_get_symbol_string(z3_ctx,Z3_get_type_name(z3_ctx, a)) << std::endl; + + b = Z3_get_type(z3_ctx, operand1); + std::cout << "operand1:" << std::endl; + std::cout << Z3_get_symbol_string(z3_ctx,Z3_get_type_name(z3_ctx, b)) << std::endl; +} + +/******************************************************************* + Function: z3_convt::show_bv_size + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +void z3_convt::show_bv_size(Z3_ast operand) +{ + Z3_type_ast a; + + a = Z3_get_type(z3_ctx, operand); + std::cout << "operand type: "; + std::cout << Z3_get_symbol_string(z3_ctx,Z3_get_type_name(z3_ctx, a)) << std::endl; + std::cout << "operand size: "; + std::cout << Z3_get_bv_type_size(z3_ctx, a) << std::endl; +} + +/******************************************************************* + Function: z3_convt::select_pointer + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +const typet z3_convt::select_pointer(const typet &type) +{ + if (is_ptr(type)) + return select_pointer(type.subtype()); + else + return type; +} + +/******************************************************************* + Function: z3_convt::check_all_types + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::check_all_types(const typet &type) +{ + if (type.id()=="bool" || type.id()=="signedbv" || type.id()=="unsignedbv" || + type.id()=="symbol" || type.id()=="empty" || type.id() == "fixedbv" || + type.id()=="array" || type.id()=="struct" || type.id()=="pointer" || + type.id()=="union" || type.id()=="code") + { + return true; + } + + return false; +} + +/******************************************************************* + Function: z3_convt::is_bv + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::is_bv(const typet &type) +{ + if (type.id()=="signedbv" || type.id()=="unsignedbv" || + type.id() == "fixedbv") + return true; + + return false; +} + +/******************************************************************* + Function: z3_convt::is_signed + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::is_signed(const typet &type) +{ + if (type.id()=="signedbv" || type.id()=="fixedbv") + return true; + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_number + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_number(int value, u_int width, bool type) +{ + static Z3_ast number_var; + char val[2]; + + sprintf(val,"%i", value); + + if (type==false) + { + if (int_encoding) + number_var = z3_api.mk_unsigned_int(z3_ctx, atoi(val)); + else + number_var = Z3_mk_unsigned_int(z3_ctx, atoi(val), Z3_mk_bv_type(z3_ctx, width)); + } + else if (type==true) + { + if (int_encoding) + number_var = z3_api.mk_int(z3_ctx, atoi(val)); + else + number_var = Z3_mk_int(z3_ctx, atoi(val), Z3_mk_bv_type(z3_ctx, width)); + } + + return number_var; +} + +/******************************************************************* + Function: z3_convt::set_z3_encoding + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +void z3_convt::set_z3_encoding(bool enc) +{ + int_encoding = enc; +} + +/******************************************************************* + Function: z3_convt::set_z3_ecp + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +void z3_convt::set_z3_ecp(bool ecp) +{ + equivalence_checking = ecp; +} + +/******************************************************************* + Function: z3_convt::check2_z3_properties + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_lbool z3_convt::check2_z3_properties(void) +{ + Z3_model m = 0; + Z3_lbool result; + unsigned num_constants, i; + + result = z3_api.check2(z3_ctx, Z3_L_TRUE); + + if (result == Z3_L_TRUE) + { + Z3_check_and_get_model(z3_ctx, &m); + num_constants = Z3_get_model_num_constants(z3_ctx, m); + + for (i = 0; i < num_constants; i++) + { + std::string variable; + Z3_symbol name; + Z3_ast app, val; + + Z3_func_decl cnst = Z3_get_model_constant(z3_ctx, m, i); + name = Z3_get_decl_name(z3_ctx, cnst); + variable = Z3_get_symbol_string(z3_ctx, name); + app = Z3_mk_app(z3_ctx, cnst, 0, 0); + val = app; + Z3_eval(z3_ctx, m, app, &val); + map_vars.insert(std::pair(variable, val)); + } + } + + z3_prop.map_prop_vars = map_vars; + + return result; +} + +/******************************************************************* + Function: z3_convt::is_ptr + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::is_ptr(const typet &type) +{ + return type.id()=="pointer" || type.id()=="reference"; +} + +/******************************************************************* + Function: z3_convt::select_pointer_value + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::select_pointer_value(Z3_ast object, Z3_ast offset, Z3_ast &bv) +{ + bv = Z3_mk_select(z3_ctx, object, offset); + return false; +} + +/******************************************************************* + Function: z3_convt::create_z3_array_var + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::create_array_type(const typet &type, Z3_type_ast &bv) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + + Z3_type_ast tuple_type, array_of_array_type; + unsigned width; + + if (type.subtype().id() == "bool") + { + if (int_encoding) + bv = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), Z3_mk_bool_type(z3_ctx)); + else + bv = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), Z3_mk_bool_type(z3_ctx)); + } + else if (type.subtype().id() == "fixedbv") + { + if(boolbv_get_width(type.subtype(), width)) + return true; + + if (int_encoding) + bv = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), Z3_mk_real_type(z3_ctx)); + else + bv = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), Z3_mk_bv_type(z3_ctx, width)); + } + else if (type.subtype().id() == "struct") + { + if (create_struct_type(type.subtype(), tuple_type)) + return true; + + if (int_encoding) + bv = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), tuple_type); + else + bv = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), tuple_type); + } + else if (type.subtype().id() == "array") // array of array + { + if (create_array_type(type.subtype(), array_of_array_type)) + return true; + + if (int_encoding) + bv = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), array_of_array_type); + else + bv = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), array_of_array_type); + } + else + { + if (type.subtype().id() == "pointer") + { + if (boolbv_get_width(type.subtype().subtype(), width)) + return true; + } + else + { + if (boolbv_get_width(type.subtype(), width)) + return true; + } + + if (int_encoding) + bv = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), Z3_mk_int_type(z3_ctx)); + else + bv = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), Z3_mk_bv_type(z3_ctx, width)); + } + + return false; +} + +/******************************************************************* + Function: z3_convt::create_type + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::create_type(const typet &type, Z3_type_ast &bv) +{ + unsigned width=config.ansi_c.int_width; + + if (type.id()=="bool") + { + bv = Z3_mk_bool_type(z3_ctx); + } + else if (type.id()=="signedbv" || type.id()=="unsignedbv" || type.id()=="c_enum") + { + if (boolbv_get_width(type, width)) + return true; + + if (int_encoding) + bv = Z3_mk_int_type(z3_ctx); + else + bv = Z3_mk_bv_type(z3_ctx, width); + } + else if (type.id() == "fixedbv") + { + if (boolbv_get_width(type, width)) + return true; + + if (int_encoding) + bv = Z3_mk_real_type(z3_ctx); + else + bv = Z3_mk_bv_type(z3_ctx, width); + } + else if (type.id()=="array") + { + if (type.subtype().id()=="struct") + { + if (create_struct_type(type.subtype(), bv)) + return true; + + if (int_encoding) + { + bv = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), bv); + return false; + } + else + { + bv = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), bv); + return false; + } + } + else if (type.subtype().id()=="array") + { + if (create_type(type.subtype(), bv)) + return true; + + if (int_encoding) + { + bv = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), bv); + return false; + } + else + { + bv = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), bv); + return false; + } + } + else if (type.subtype().id()=="pointer") + { + if (boolbv_get_width(type.subtype().subtype(), width)) + return true; + } + else + { + if (boolbv_get_width(type.subtype(), width)) + return true; + } + + if (int_encoding) + bv = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), Z3_mk_int_type(z3_ctx)); + else + bv = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), Z3_mk_bv_type(z3_ctx, width)); + } + else if (type.id()=="struct" || type.id()=="union") + { + if (create_struct_type(type, bv)) + return true; + } + else if (type.id()=="pointer") + { + if (create_pointer_type(type, bv)) + return true; + } + else + { + return true; + } + + return false; +} + +/******************************************************************* + Function: z3_convt::create_struct_type + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::create_struct_type(const typet &type, Z3_type_ast &bv) +{ + Z3_symbol mk_tuple_name, *proj_names; + std::string struct_name; + Z3_type_ast *proj_types; + Z3_const_decl_ast mk_tuple_decl, *proj_decls; + u_int size_of_struct; + + const struct_typet &struct_type=to_struct_type(type); + + const struct_typet::componentst &components= + struct_type.components(); + + assert(components.size()>0); + size_of_struct = components.size(); + proj_names = new Z3_symbol[size_of_struct]; + proj_types = new Z3_type_ast[size_of_struct]; + proj_decls = new Z3_const_decl_ast[size_of_struct]; + + struct_name = type.get_string("tag"); + mk_tuple_name = Z3_mk_string_symbol(z3_ctx, struct_name.c_str()); + + u_int i=0; + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++,i++) + { + proj_names[i] = Z3_mk_string_symbol(z3_ctx, it->get("name").c_str()); + if (create_type(it->type(), proj_types[i])) + return true; + } + + bv = Z3_mk_tuple_type(z3_ctx, mk_tuple_name, i, proj_names, proj_types, &mk_tuple_decl, proj_decls); + + delete[] proj_names; + delete[] proj_types; + delete[] proj_decls; + + return false; +} + +/******************************************************************* + Function: z3_convt::create_enum_type + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::create_enum_type(const typet &type, Z3_type_ast &bv) +{ + if (int_encoding) + bv = Z3_mk_int_type(z3_ctx); + else + bv = Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width); + + return false; +} + +/******************************************************************* + Function: z3_convt::create_pointer_type + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ +bool z3_convt::create_pointer_type(const typet &type, Z3_type_ast &bv) +{ + Z3_symbol mk_tuple_name, proj_names[2]; + Z3_type_ast proj_types[2]; + Z3_const_decl_ast mk_tuple_decl, proj_decls[2]; + + mk_tuple_name = Z3_mk_string_symbol(z3_ctx, "pointer_tuple"); + proj_names[0] = Z3_mk_string_symbol(z3_ctx, "object"); + + if (is_ptr(type.subtype())) + { + if (create_type(select_pointer(type.subtype()), proj_types[0])) + return true; + } + else if (check_all_types(type.subtype())) + { + if (create_type(type.subtype(), proj_types[0])) + return true; + } + else if (check_all_types(type)) + { + if (create_type(type, proj_types[0])) + return true; + } + else + { + return true; + } + + proj_names[1] = Z3_mk_string_symbol(z3_ctx, "index"); + + if (int_encoding) + proj_types[1] = Z3_mk_int_type(z3_ctx); + else + proj_types[1] = Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width); + + + bv = Z3_mk_tuple_type(z3_ctx, mk_tuple_name, 2, proj_names, proj_types, &mk_tuple_decl, proj_decls); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_identifier + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_identifier(const std::string &identifier, const typet &type, Z3_ast &bv) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + + Z3_type_ast type_var; + unsigned width; + +#ifdef DEBUG + std::cout << "identifier: " << identifier.c_str() << "\n"; +#endif + + if (type.id()=="bool") + { + bv = z3_api.mk_bool_var(z3_ctx, identifier.c_str()); + } + else if (type.id()=="signedbv" || type.id()=="unsignedbv") + { + if (boolbv_get_width(type, width)) + return true; + + if (int_encoding) + bv = z3_api.mk_int_var(z3_ctx, identifier.c_str()); + else + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), Z3_mk_bv_type(z3_ctx, width)); + } + else if (type.id()== "fixedbv") + { + if (boolbv_get_width(type, width)) + return true; + + if (int_encoding) + bv = z3_api.mk_real_var(z3_ctx, identifier.c_str()); + else + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), Z3_mk_bv_type(z3_ctx, width)); + } + else if (type.id()=="array") + { + if (create_array_type(type, type_var)) + return true; + + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), type_var); + } + else if (type.id()=="pointer") + { + if (create_pointer_type(type, type_var)) + return true; + + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), type_var); + } + else if (type.id()=="struct" || type.id()=="union") + { + if (create_struct_type(type, type_var)) + return true; + + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), type_var); + } + else if (type.id()=="c_enum") + { + if (create_enum_type(type, type_var)) + return true; + + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), type_var); + } + else + throw "convert_identifier: " + type.id_string() + " is unsupported"; + + ++number_variables_z3; + + return false; +} + +/*******************************************************************\ + +Function: z3_convt::is_in_cache + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool z3_convt::is_in_cache(const exprt &expr) +{ + bv_cachet::const_iterator cache_result=bv_cache.find(expr); + if(cache_result!=bv_cache.end()) + { + //std::cout << "Cache hit on " << expr.pretty() << "\n"; + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: z3_convt::convert_bv + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool z3_convt::convert_bv(const exprt &expr, Z3_ast &bv) +{ + bv_cachet::const_iterator cache_result=bv_cache.find(expr); + if(cache_result!=bv_cache.end()) + { + //std::cout << "Cache hit on " << expr.pretty() << "\n"; + bv = cache_result->second; + return false; + } + + if (convert_z3_expr(expr, bv)) + return true; + + // insert into cache + bv_cache.insert(std::pair(expr, bv)); + + return false; +} + +/******************************************************************* + Function: z3_convt::read_cache + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::read_cache(const exprt &expr, Z3_ast &bv) +{ + std::string symbol; + + symbol = expr.get_string("identifier"); + + for(z3_cachet::const_iterator it = z3_cache.begin(); + it != z3_cache.end(); it++) + { + if (symbol.compare((*it).second.c_str())==0) + { + //std::cout << "Cache hit on:" << (*it).first.pretty() << "\n"; + if (convert_bv((*it).first, bv)) + return true; + + return false; + } + } + + if (convert_bv(expr, bv)) + return true; + + return false; +} + +/******************************************************************* + Function: z3_convt::write_cache + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::write_cache(const exprt &expr) +{ + std::string symbol, identifier; + + identifier = expr.get_string("identifier"); + + for (std::string::const_iterator it = identifier.begin(); it + != identifier.end(); it++) + { + char ch = *it; + + if (isalnum(ch) || ch == '$' || ch == '?') + { + symbol += ch; + } + else if (ch == '#') + { + z3_cache.insert(std::pair(expr, symbol)); + return false; + } + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_lt + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_lt(const exprt &expr) +{ + assert(expr.operands().size()==2); + static Z3_ast bv, operand[2]; + const exprt &op0=expr.op0(); + const exprt &op1=expr.op1(); + + if (convert_bv(op0, operand[0])) + return Z3_mk_false(z3_ctx); + if (convert_bv(op1, operand[1])) + return Z3_mk_false(z3_ctx); + + if (op0.type().id() == "pointer") + operand[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); + + if (op1.type().id() == "pointer") + operand[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + + if (op0.id() == "typecast" && op0.type().id()=="signedbv") + { + const exprt &object=expr.op0().operands()[0]; + std::string value; + unsigned width; + + if (op1.id()=="constant" && object.type().id()=="unsignedbv") + { + value = integer2string(binary2integer(expr.op1().get_string("value"), false),10); + + if (boolbv_get_width(expr.op1().type(), width)) + return Z3_mk_false(z3_ctx); + + operand[1] = convert_number(atoi(value.c_str()), width, false); + bv = Z3_mk_bvult(z3_ctx, operand[0], operand[1]); + + return bv; + } + } + + if (int_encoding) + { + bv = Z3_mk_lt(z3_ctx, operand[0], operand[1]); + } + else + { + if (op1.type().id()=="signedbv" || op1.type().id()=="fixedbv" + || op1.type().id()=="pointer") + bv = Z3_mk_bvslt(z3_ctx, operand[0], operand[1]); + else if (op1.type().id()=="unsignedbv") + bv = Z3_mk_bvult(z3_ctx, operand[0], operand[1]); + } + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_gt + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ +Z3_ast z3_convt::convert_gt(const exprt &expr) +{ + assert(expr.operands().size()==2); + static Z3_ast bv, operand[2]; + + if (convert_bv(expr.op0(), operand[0])) + return Z3_mk_false(z3_ctx); + if (convert_bv(expr.op1(), operand[1])) + return Z3_mk_false(z3_ctx); + + if (expr.op0().type().id() == "pointer") + operand[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); + + if (expr.op1().type().id() == "pointer") + operand[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + + //workaround: bug in the front-end + //to check it, run example ex10.c of NEC + if (expr.op0().id() == "typecast" && expr.op0().type().id()=="signedbv") + { + const exprt &object=expr.op0().operands()[0]; + std::string value; + unsigned width; + + if (expr.op1().id()=="constant" && object.type().id()=="unsignedbv") + { + value = integer2string(binary2integer(expr.op1().get_string("value"), false),10); + + if (boolbv_get_width(expr.op1().type(), width)) + return Z3_mk_false(z3_ctx); + + operand[1] = convert_number(atoi(value.c_str()), width, false); + bv = Z3_mk_bvugt(z3_ctx, operand[0], operand[1]); + + return bv; + } + } + + if (int_encoding) + { + bv = Z3_mk_gt(z3_ctx, operand[0], operand[1]); + } + else + { + if (expr.op1().type().id()=="signedbv" || expr.op1().type().id()=="fixedbv" + || expr.op1().type().id()=="pointer") + bv = Z3_mk_bvsgt(z3_ctx, operand[0], operand[1]); + else if (expr.op1().type().id()=="unsignedbv") + bv = Z3_mk_bvugt(z3_ctx, operand[0], operand[1]); + } + + return bv; +} + + +/******************************************************************* + Function: z3_convt::convert_le + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_le(const exprt &expr) +{ + assert(expr.operands().size()==2); + static Z3_ast bv, operand[2]; + + if (convert_bv(expr.op0(), operand[0])) + return Z3_mk_false(z3_ctx); + if (convert_bv(expr.op1(), operand[1])) + return Z3_mk_false(z3_ctx); + + if (expr.op0().type().id() == "pointer") + operand[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); + + if (expr.op1().type().id() == "pointer") + operand[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + + if (expr.op0().id() == "typecast" && expr.op0().type().id()=="signedbv") + { + const exprt &object=expr.op0().operands()[0]; + std::string value; + unsigned width; + + if (expr.op1().id()=="constant" && object.type().id()=="unsignedbv") + { + value = integer2string(binary2integer(expr.op1().get_string("value"), false),10); + + if (boolbv_get_width(expr.op1().type(), width)) + return Z3_mk_false(z3_ctx); + + operand[1] = convert_number(atoi(value.c_str()), width, false); + bv = Z3_mk_bvule(z3_ctx, operand[0], operand[1]); + + return bv; + } + } + + if (int_encoding) + { + bv = Z3_mk_le(z3_ctx, operand[0], operand[1]); + } + else + { + if (expr.op1().type().id()=="signedbv" || expr.op1().type().id()=="fixedbv" + || expr.op1().type().id()=="pointer") + bv = Z3_mk_bvsle(z3_ctx, operand[0], operand[1]); + else if (expr.op1().type().id()=="unsignedbv") + bv = Z3_mk_bvule(z3_ctx, operand[0], operand[1]); + } + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_ge + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_ge(const exprt &expr) +{ + assert(expr.operands().size()==2); + static Z3_ast bv, operand[2]; + + if (convert_bv(expr.op0(), operand[0])) + return Z3_mk_false(z3_ctx); + if (convert_bv(expr.op1(), operand[1])) + return Z3_mk_false(z3_ctx); + + if (expr.op0().type().id() == "pointer") + operand[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); + + if (expr.op1().type().id() == "pointer") + operand[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + + if (expr.op0().id() == "typecast" && expr.op0().type().id()=="signedbv") + { + const exprt &object=expr.op0().operands()[0]; + std::string value; + unsigned width; + + if (expr.op1().id()=="constant" && object.type().id()=="unsignedbv") + { + value = integer2string(binary2integer(expr.op1().get_string("value"), false),10); + + if (boolbv_get_width(expr.op1().type(), width)) + return Z3_mk_false(z3_ctx); + + operand[1] = convert_number(atoi(value.c_str()), width, false); + bv = Z3_mk_bvuge(z3_ctx, operand[0], operand[1]); + + return bv; + } + } + + if (int_encoding) + { + bv = Z3_mk_ge(z3_ctx, operand[0], operand[1]); + } + else + { + if (expr.op1().type().id()=="signedbv" || expr.op1().type().id()=="fixedbv" + || expr.op1().type().id()=="pointer") + bv = Z3_mk_bvsge(z3_ctx, operand[0], operand[1]); + else if (expr.op1().type().id()=="unsignedbv") + bv = Z3_mk_bvuge(z3_ctx, operand[0], operand[1]); + } + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_eq + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_eq(const exprt &expr) +{ + assert(expr.operands().size()==2); + static Z3_ast bv, operand[2]; + const exprt &op0=expr.op0(); + const exprt &op1=expr.op1(); + + if (op0.type().id()=="array") + { + if (write_cache(op0)) + return Z3_mk_false(z3_ctx); + } + + if (convert_bv(op0, operand[0])) + return Z3_mk_false(z3_ctx); + + if (convert_bv(op1, operand[1])) + return Z3_mk_false(z3_ctx); + + if (op0.type().id()=="pointer" && op1.type().id()=="pointer") + { + static Z3_ast pointer[2], formula[2]; + + pointer[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 0); + pointer[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 0); + + if (expr.id() == "=") + formula[0] = Z3_mk_eq(z3_ctx, pointer[0], pointer[1]); + else + formula[0] = Z3_mk_distinct(z3_ctx, 2, pointer); + + pointer[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); + pointer[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + + if (expr.id() == "=") + formula[1] = Z3_mk_eq(z3_ctx, pointer[0], pointer[1]); + else + formula[1] = Z3_mk_distinct(z3_ctx, 2, pointer); + + bv = Z3_mk_and(z3_ctx, 2, formula); + + return bv; + } + else + { + if (expr.id() == "=") + bv = Z3_mk_eq(z3_ctx, operand[0], operand[1]); + else + bv = Z3_mk_distinct(z3_ctx, 2, operand); + } + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_invalid + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_invalid(const exprt &expr) +{ + assert(expr.operands().size()==1); + static Z3_ast bv, pointer, operand[2]; + + if (!is_in_cache(expr.op0()) && expr.op0().id()=="typecast") + return Z3_mk_true(z3_ctx); + + if (convert_z3_expr(expr.op0(), pointer)); //return pointer tuple + return Z3_mk_false(z3_ctx); + + if (expr.op0().type().id()=="pointer") + operand[0] = z3_api.mk_tuple_select(z3_ctx, pointer, 1); //pointer index + + operand[1] = convert_number(0, config.ansi_c.int_width, true); + + if (int_encoding) + bv = Z3_mk_ge(z3_ctx, operand[0], operand[1]); + else + bv = Z3_mk_bvsge(z3_ctx, operand[0], operand[1]); + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_same_object + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_same_object(const exprt &expr) +{ + const exprt::operandst &operands=expr.operands(); + static Z3_ast bv, operand[2], formula[2], pointer[2]; + const exprt &op0=expr.op0(); + const exprt &op1=expr.op1(); + + if ((is_in_cache(op0) && !is_in_cache(op1)) + || (!is_in_cache(op0) && !is_in_cache(op1))) + { + //object is not in the cache and generates spurious counter-example + return Z3_mk_false(z3_ctx); + } + else if (op0.id()=="address_of" && op1.id()=="constant") + { + return Z3_mk_false(z3_ctx); //TODO + } + else if (operands.size()==2 && + is_ptr(operands[0].type()) && + is_ptr(operands[1].type())) + { + if (convert_bv(op0, pointer[0])) + return Z3_mk_false(z3_ctx); + if (convert_bv(op1, pointer[1])) + return Z3_mk_false(z3_ctx); + + operand[0] = z3_api.mk_tuple_select(z3_ctx, pointer[0], 0); + operand[1] = z3_api.mk_tuple_select(z3_ctx, pointer[1], 0); + + formula[0] = Z3_mk_eq(z3_ctx, operand[0], operand[1]); + + operand[0] = z3_api.mk_tuple_select(z3_ctx, pointer[0], 1); + operand[1] = z3_api.mk_tuple_select(z3_ctx, pointer[1], 1); + + formula[1] = Z3_mk_eq(z3_ctx, operand[0], operand[1]); + bv = Z3_mk_and(z3_ctx, 2, formula); + } + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_dynamic_object + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_dynamic_object(const exprt &expr) +{ + //TODO: understand how to create a formula to "is_dynamic_object" + //This is wrong. As a consequence, we do not find bugs related to dynamic memory allocation + assert(expr.operands().size()==1); + static Z3_ast bv, operand; + + if (convert_bv(expr.op0(), operand)) + return Z3_mk_false(z3_ctx); + + bv = Z3_mk_true(z3_ctx); + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_overflow_sum + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_overflow_sum(const exprt &expr) +{ + assert(expr.operands().size()==2); + static Z3_ast bv, result[2], operand[2]; + + if (expr.op0().type().id()=="array") + { + if (write_cache(expr.op0())) + return Z3_mk_false(z3_ctx); + } + + if (convert_bv(expr.op0(), operand[0])) + return Z3_mk_false(z3_ctx);; + + if (expr.op0().type().id()=="pointer") + operand[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); + + if (convert_bv(expr.op1(), operand[1])) + return Z3_mk_false(z3_ctx);; + + if (expr.op1().type().id()=="pointer") + operand[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + + if (expr.op0().type().id()=="signedbv" && expr.op1().type().id()=="signedbv") + result[0] = Z3_mk_bvadd_no_overflow(z3_ctx, operand[0], operand[1], 1); + else if (expr.op0().type().id()=="unsignedbv" && expr.op1().type().id()=="unsignedbv") + result[0] = Z3_mk_bvadd_no_overflow(z3_ctx, operand[0], operand[1], 0); + + result[1] = Z3_mk_bvadd_no_underflow(z3_ctx, operand[0], operand[1]); + bv = Z3_mk_not(z3_ctx, Z3_mk_and(z3_ctx, 2, result)); + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_overflow_sub + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_overflow_sub(const exprt &expr) +{ + assert(expr.operands().size()==2); + static Z3_ast bv, result[2], operand[2]; + + if (expr.op0().type().id()=="array") + { + if (write_cache(expr.op0())) + return Z3_mk_false(z3_ctx);; + } + + if (convert_bv(expr.op0(), operand[0])) + return Z3_mk_false(z3_ctx);; + + if (expr.op0().type().id()=="pointer") + operand[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); + + if (convert_bv(expr.op1(), operand[1])) + return Z3_mk_false(z3_ctx);; + + if (expr.op1().type().id()=="pointer") + operand[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + + if (expr.op0().type().id()=="signedbv" && expr.op1().type().id()=="signedbv") + result[1] = Z3_mk_bvsub_no_underflow(z3_ctx, operand[0], operand[1], 1); + else if (expr.op0().type().id()=="unsignedbv" && expr.op1().type().id()=="unsignedbv") + result[1] = Z3_mk_bvsub_no_underflow(z3_ctx, operand[0], operand[1], 0); + + result[0] = Z3_mk_bvsub_no_overflow(z3_ctx, operand[0], operand[1]); + bv = Z3_mk_not(z3_ctx, Z3_mk_and(z3_ctx, 2, result)); + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_overflow_mul + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_overflow_mul(const exprt &expr) +{ + assert(expr.operands().size()==2); + static Z3_ast bv, operand[2]; + + if (expr.op0().type().id()=="array") + { + if (write_cache(expr.op0())) + return Z3_mk_false(z3_ctx);; + } + + if (convert_bv(expr.op0(), operand[0])) + return Z3_mk_false(z3_ctx);; + + if (expr.op0().type().id()=="pointer") + operand[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); + + if (convert_bv(expr.op1(), operand[1])) + return Z3_mk_false(z3_ctx);; + + if (expr.op1().type().id()=="pointer") + operand[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + + if (expr.op0().type().id()=="signedbv" && expr.op1().type().id()=="signedbv") + bv = Z3_mk_not(z3_ctx, Z3_mk_bvmul_no_overflow(z3_ctx, operand[0], operand[1], 1)); + else if (expr.op0().type().id()=="unsignedbv" && expr.op1().type().id()=="unsignedbv") + bv = Z3_mk_not(z3_ctx, Z3_mk_bvmul_no_overflow(z3_ctx, operand[0], operand[1], 0)); + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_overflow_unary + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_overflow_unary(const exprt &expr) +{ + assert(expr.operands().size()==1); + static Z3_ast bv, operand; + + if (convert_bv(expr.op0(), operand)) + return Z3_mk_false(z3_ctx);; + + if (expr.op0().type().id()=="pointer") + operand = z3_api.mk_tuple_select(z3_ctx, operand, 1); + + bv = Z3_mk_not(z3_ctx, Z3_mk_bvneg_no_overflow(z3_ctx, operand)); + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_overflow_typecast + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_overflow_typecast(const exprt &expr) +{ + unsigned bits=atoi(expr.id().c_str()+18); + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()!=1) + throw "operand "+expr.id_string()+" takes one operand"; + + static Z3_ast bv, operand[3], mid, overflow[2], tmp, minus_one, two; + u_int i, result=1, width; + std::string value; + + if (boolbv_get_width(expr.op0().type(), width)) + return Z3_mk_false(z3_ctx); + + if(bits>=width || bits==0) + throw "overflow-typecast got wrong number of bits"; + + assert(bits <= 32); + + for(i = 0; i < bits; i++) + { + if (i==31) + result=(result-1)*2+1; + else if (i<31) + result*=2; + } + + if (is_signed(expr.op0().type())) + value = integer2string(binary2integer(expr.op0().get_string("value"), true),10); + else + value = integer2string(binary2integer(expr.op0().get_string("value"), false),10); + + if (convert_bv(expr.op0(), operand[0])) + return Z3_mk_false(z3_ctx); + + if (expr.op0().type().id()=="signedbv" || expr.op0().type().id()=="fixedbv") + { + tmp = convert_number(result, width, true); + two = convert_number(2, width, true); + minus_one = convert_number(-1, width, true); + mid = Z3_mk_bvsdiv(z3_ctx, tmp, two); + operand[1] = Z3_mk_bvsub(z3_ctx, mid, minus_one); + operand[2] = Z3_mk_bvmul(z3_ctx, operand[1], minus_one); + + overflow[0] = Z3_mk_bvslt(z3_ctx, operand[0], operand[1]); + overflow[1] = Z3_mk_bvsgt(z3_ctx, operand[0], operand[2]); + bv = Z3_mk_not(z3_ctx, Z3_mk_and(z3_ctx, 2, overflow)); + } + else if (expr.op0().type().id()=="unsignedbv") + { + operand[2] = convert_number(0, width, false); + operand[1] = convert_number(result, width, false); + overflow[0] = Z3_mk_bvult(z3_ctx, operand[0], operand[1]); + overflow[1] = Z3_mk_bvugt(z3_ctx, operand[0], operand[2]); + bv = Z3_mk_not(z3_ctx, Z3_mk_and(z3_ctx, 2, overflow)); + } + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_rest_member + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_rest_member(const exprt &expr) +{ + Z3_ast bv; + + if (convert_bv(expr,bv)) + return Z3_mk_false(z3_ctx); + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_rest_index + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +Z3_ast z3_convt::convert_rest_index(const exprt &expr) +{ + //TODO: understand how to create a formula to "index" + + Z3_ast bv, operand0, operand1; + + if (expr.operands().size()!=2) + throw "index takes two operands"; + + const exprt &array=expr.op0(); + const exprt &index=expr.op1(); + + if (convert_bv(array, operand0)) + return Z3_mk_false(z3_ctx); + + if (convert_bv(index, operand1)) + return Z3_mk_false(z3_ctx); + + bv = Z3_mk_select(z3_ctx, operand0, operand1); + + return bv; +} + +/******************************************************************* + Function: z3_convt::convert_rest + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +literalt z3_convt::convert_rest(const exprt &expr) +{ + literalt l = z3_prop.new_variable(); + static Z3_ast formula, constraint; + + if (!assign_z3_expr(expr) && !ignoring_expr) + return l; + + if (expr.id() == "<") + constraint = convert_lt(expr); + else if (expr.id() == ">") + constraint = convert_gt(expr); + else if (expr.id() == "<=") + constraint = convert_le(expr); + else if (expr.id() == ">=") + constraint = convert_ge(expr); + else if (expr.id() == "=" || expr.id() == "notequal") + constraint = convert_eq(expr); + else if (expr.id() == "invalid-pointer") + constraint = convert_invalid(expr); + else if (expr.id() == "same-object") + constraint = convert_same_object(expr); + else if (expr.id() == "is_dynamic_object") + //ignoring(expr); + constraint = convert_dynamic_object(expr); + else if (expr.id() == "overflow-+" && !int_encoding) + constraint = convert_overflow_sum(expr); + else if (expr.id() == "overflow--" && !int_encoding) + constraint = convert_overflow_sub(expr); + else if (expr.id() == "overflow-*" && !int_encoding) + constraint = convert_overflow_mul(expr); + else if (expr.id() == "overflow-unary-" && !int_encoding) + constraint = convert_overflow_unary(expr); + else if(has_prefix(expr.id_string(), "overflow-typecast-") && !int_encoding) + constraint = convert_overflow_typecast(expr); + else if (expr.id() == "member") + constraint = convert_rest_member(expr); + else if (expr.id() == "index") + ignoring(expr); + //constraint = convert_rest_index(expr); //TODO + else if (expr.id()=="pointer_object_has_type") + ignoring(expr); + else if (expr.id() == "is_zero_string") + { + ignoring(expr); + return l; + } + else + throw "convert_z3_expr: " + expr.id_string() + " is unsupported"; + + formula = Z3_mk_iff(z3_ctx, z3_prop.z3_literal(l), constraint); + Z3_assert_cnstr(z3_ctx, formula); + + ++number_vcs_z3; + ++number_variables_z3; + + return l; +} + +/******************************************************************* + Function: z3_convt::convert_rel + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_rel(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==2); + + Z3_ast operand0, operand1; + + if (convert_bv(expr.op0(), operand0)) + return true; + if (convert_bv(expr.op1(), operand1)) + return true; + + if (expr.op0().type().id() == "pointer") + operand0 = z3_api.mk_tuple_select(z3_ctx, operand0, 1); //select pointer index + + if (expr.op1().type().id() == "pointer") + operand1 = z3_api.mk_tuple_select(z3_ctx, operand1, 1); //select pointer index + + const typet &op_type=expr.op0().type(); + + if (int_encoding) + { + if(expr.id()=="<=") + bv = Z3_mk_le(z3_ctx,operand0,operand1); + else if(expr.id()=="<") + bv = Z3_mk_lt(z3_ctx,operand0,operand1); + else if(expr.id()==">=") + bv = Z3_mk_ge(z3_ctx,operand0,operand1); + else if(expr.id()==">") + bv = Z3_mk_gt(z3_ctx,operand0,operand1); + } + else + { + if (op_type.id()=="unsignedbv" || op_type.subtype().id()=="unsignedbv" + || op_type.subtype().id()=="symbol") + { + if(expr.id()=="<=") + bv = Z3_mk_bvule(z3_ctx,operand0,operand1); + else if(expr.id()=="<") + bv = Z3_mk_bvult(z3_ctx,operand0,operand1); + else if(expr.id()==">=") + bv = Z3_mk_bvuge(z3_ctx,operand0,operand1); + else if(expr.id()==">") + bv = Z3_mk_bvugt(z3_ctx,operand0,operand1); + } + else if (op_type.id()=="signedbv" || op_type.id()=="fixedbv" || + op_type.subtype().id()=="signedbv" || op_type.subtype().id()=="fixedbv") + { + if(expr.id()=="<=") + bv = Z3_mk_bvsle(z3_ctx,operand0,operand1); + else if(expr.id()=="<") + bv = Z3_mk_bvslt(z3_ctx,operand0,operand1); + else if(expr.id()==">=") + bv = Z3_mk_bvsge(z3_ctx,operand0,operand1); + else if(expr.id()==">") + bv = Z3_mk_bvsgt(z3_ctx,operand0,operand1); + } + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_typecast + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_typecast(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==1); + const exprt &op=expr.op0(); + + Z3_ast args[2]; + + if (convert_bv(op, bv)) + return true; + + if(expr.type().id()=="bool") + { + if(op.type().id()=="signedbv" || + op.type().id()=="unsignedbv" || + op.type().id()=="pointer") + { + args[0] = bv; + if (int_encoding) + args[1] = z3_api.mk_int(z3_ctx, 0); + else + args[1] = Z3_mk_int(z3_ctx, 0, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width)); + + bv = Z3_mk_distinct(z3_ctx,2,args); + } + else + { + throw "TODO typecast1 "+op.type().id_string()+" -> bool"; + } + } + else if ((expr.type().id()=="signedbv" || expr.type().id()=="unsignedbv" + || expr.type().id()=="fixedbv" || expr.type().id()=="pointer") + && op.type().subtype().id()!="symbol" && expr.type().subtype().id()!="symbol") + { + unsigned to_width; + + if (expr.type().id()=="pointer" && expr.type().subtype().id()!="empty") + { + if (boolbv_get_width(expr.type().subtype(), to_width)) + return true; + } + else if (expr.type().id()=="pointer" && expr.type().subtype().id()=="empty") + { + to_width=config.ansi_c.int_width; + } + else + { + if (boolbv_get_width(expr.type(), to_width)) + return true; + } + + //std::cout << "to_width: " << to_width << "\n"; + + if (op.type().id()=="signedbv" || op.type().id()=="fixedbv" || + op.type().subtype().id()=="signedbv" || op.type().subtype().id()=="fixedbv") + { + unsigned from_width; + + if (op.type().id()=="pointer") + { + if (boolbv_get_width(op.type().subtype(), from_width)) + return true; + } + else + { + if (boolbv_get_width(op.type(), from_width)) + return true; + } + + if(from_width==to_width) + { + if (convert_bv(op, bv)) + return true; + + if (op.type().id()=="pointer") + bv = z3_api.mk_tuple_select(z3_ctx, bv, 0); + } + else if(from_widthto_width) + { + if (convert_bv(op, args[0])) + return true; + + if (op.type().id()=="pointer") + args[0] = z3_api.mk_tuple_select(z3_ctx, args[0], 0); + + if (int_encoding) + bv=args[0]; + else + bv = Z3_mk_extract(z3_ctx, (to_width-1), 0, args[0]); + } + } + else if (op.type().id()=="unsignedbv" || op.type().subtype().id()=="unsignedbv") + { + unsigned from_width; + + if (op.type().id()=="pointer") + { + if (boolbv_get_width(op.type().subtype(), from_width)) + return true; + } + else + { + if (boolbv_get_width(op.type(), from_width)) + return true; + } + + if(from_width==to_width) + { + if (convert_bv(op, bv)) + return true; + + if (op.type().id()=="pointer") + bv = z3_api.mk_tuple_select(z3_ctx, bv, 0); + } + else if(from_widthto_width) + { + if (convert_bv(op, args[0])) + return true; + + if (op.type().id()=="pointer") + args[0] = z3_api.mk_tuple_select(z3_ctx, args[0], 0); + + if (int_encoding) + bv=args[0]; + else + bv = Z3_mk_extract(z3_ctx, (to_width-1), 0, args[0]); + } + } + else if (op.type().id()=="bool") + { + Z3_ast zero=0, one=0; + unsigned width; + + if (boolbv_get_width(expr.type(), width)) + return true; + + if (expr.type().id()=="signedbv" || expr.type().id()=="fixedbv") + { + zero = convert_number(0, width, true); + one = convert_number(1, width, true); + } + else if (expr.type().id()=="unsignedbv") + { + zero = convert_number(0, width, false); + one = convert_number(1, width, false); + } + bv = Z3_mk_ite(z3_ctx, bv, one, zero); + } + else if(op.type().subtype().id()=="empty") + { + unsigned from_width=config.ansi_c.int_width; + Z3_ast object; + + if(from_width==to_width) + { + //new change + if (convert_bv(op, args[0])) + return true; + + bv = z3_api.mk_tuple_select(z3_ctx, args[0], 0); + } + else if(from_widthto_width) + { + if ( convert_bv(op, args[0])) + return true; + + object = z3_api.mk_tuple_select(z3_ctx, args[0], 0); + + if (int_encoding) + bv=args[0]; + else + bv = Z3_mk_extract(z3_ctx, (to_width-1), 0, object); + } + } + else + { + return true; + } + if (expr.type().id()=="pointer") + { + Z3_ast pointer_var; + + if (convert_z3_pointer(expr, "pointer", pointer_var)) + return true; + + bv = z3_api.mk_tuple_update(z3_ctx, pointer_var, 0, bv); + } + } + + if(expr.type().id()=="c_enum") + { + Z3_ast zero, one; + unsigned width; + + if (op.type().id()=="bool") + { + if (boolbv_get_width(expr.type(), width)) + return true; + + zero = convert_number(0, width, true); + one = convert_number(1, width, true); + bv = Z3_mk_ite(z3_ctx, bv, one, zero); + } + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_struct + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_struct(const exprt &expr, Z3_ast &bv) +{ + Z3_ast value; + + const struct_typet &struct_type=to_struct_type(expr.type()); + const struct_typet::componentst &components=struct_type.components(); + u_int i=0; + + assert(components.size()==expr.operands().size()); + + if (convert_identifier(expr.type().get_string("tag"), expr.type(), bv)) + return true; + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++, i++) + { + if (convert_bv(expr.operands()[i], value)) + return true; + + bv = z3_api.mk_tuple_update(z3_ctx, bv, i, value); + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_z3_pointer + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_z3_pointer(const exprt &expr, std::string symbol, Z3_ast &bv) +{ + Z3_type_ast tuple_type; + std::string cte, identifier; + unsigned width; + char val[2]; + + if (check_all_types(expr.type().subtype())) + { + if (expr.type().subtype().id()=="pointer") + { + if (boolbv_get_width(expr.type().subtype().subtype(), width)) + return true; + } + else + { + if (boolbv_get_width(expr.type().subtype(), width)) + return true; + } + + if (create_pointer_type(expr.type().subtype(), tuple_type)) + return true; + } + else if (check_all_types(expr.type())) + { + if (boolbv_get_width(expr.type(), width)) + return true; + + if (create_pointer_type(expr.type(), tuple_type)) + return true; + } + + sprintf(val,"%i", width); + identifier = symbol; + identifier += val; + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), tuple_type); + + if (expr.get("value").compare("NULL") == 0) + { + if (int_encoding) + bv = z3_api.mk_tuple_update(z3_ctx, bv, 1, z3_api.mk_int(z3_ctx,-1)); + else + bv = z3_api.mk_tuple_update(z3_ctx, bv, 1, Z3_mk_int(z3_ctx, -1, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width))); + } + else + { + if (int_encoding) + bv = z3_api.mk_tuple_update(z3_ctx, bv, 1, z3_api.mk_int(z3_ctx, 0)); + else + bv = z3_api.mk_tuple_update(z3_ctx, bv, 1, Z3_mk_int(z3_ctx, 0, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width))); + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_zero_string + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_zero_string(const exprt &expr, Z3_ast &bv) +{ + Z3_type_ast array_type; + + if (create_array_type(expr.type(), array_type)) + return true; + + bv = z3_api.mk_var(z3_ctx, "zero_string", array_type); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_array + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_array(const exprt &expr, Z3_ast &bv) +{ + u_int width=0, i=0; + Z3_type_ast array_type, tuple_type; + Z3_ast array_cte, int_cte, val_cte, tmp_struct; + std::string value_cte; + char i_str[2]; + + width = config.ansi_c.int_width; + + if (expr.type().subtype().id() == "fixedbv") + { + if (boolbv_get_width(expr.type().subtype(), width)) + return true; + + if (int_encoding) + array_type = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), Z3_mk_real_type(z3_ctx)); + else + array_type = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), Z3_mk_bv_type(z3_ctx, width)); + } + else if (expr.type().subtype().id() == "struct") + { + if (create_struct_type(expr.op0().type(), tuple_type)) + return true; + + if (int_encoding) + array_type = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), tuple_type); + else + array_type = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), tuple_type); + + value_cte = "constant" + expr.op0().type().get_string("tag"); + array_cte = z3_api.mk_var(z3_ctx, value_cte.c_str(), array_type); + + i=0; + forall_operands(it, expr) + { + sprintf(i_str,"%i",i); + if (int_encoding) + int_cte = z3_api.mk_int(z3_ctx, atoi(i_str)); + else + int_cte = Z3_mk_int(z3_ctx, atoi(i_str), Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width)); + + if (convert_struct(*it, tmp_struct)) + return true; + + Z3_mk_store(z3_ctx, array_cte, int_cte, tmp_struct); + ++i; + } + + return array_cte; + } + else if (expr.type().subtype().id() == "array") + { + if (boolbv_get_width(expr.type().subtype().subtype(), width)) + return true; + + if (create_array_type(expr.type(), array_type)) + return true; + } + else + { + if (boolbv_get_width(expr.type().subtype(), width)) + return true; + + if (int_encoding) + array_type = Z3_mk_array_type(z3_ctx, Z3_mk_int_type(z3_ctx), Z3_mk_int_type(z3_ctx)); + else + array_type = Z3_mk_array_type(z3_ctx, Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width), Z3_mk_bv_type(z3_ctx, width)); + } + + value_cte = expr.get_string("identifier") + expr.type().subtype().get("width").c_str(); + bv = z3_api.mk_var(z3_ctx, value_cte.c_str(), array_type); + + i=0; + forall_operands(it, expr) + { + sprintf(i_str,"%i",i); + if (int_encoding) + int_cte = z3_api.mk_int(z3_ctx, atoi(i_str)); + else + int_cte = Z3_mk_int(z3_ctx, atoi(i_str), Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width)); + + if (it->type().id()=="array") + { + if (convert_bv(*it, val_cte)) + return true; + } + else + { + if (is_signed(it->type())) + value_cte = integer2string(binary2integer(it->get("value").c_str(), true),10); + else + value_cte = integer2string(binary2integer(it->get("value").c_str(), false),10); + + if (int_encoding) + { + if (it->type().id()=="signedbv") + val_cte = Z3_mk_int(z3_ctx, atoi(value_cte.c_str()), Z3_mk_int_type(z3_ctx)); + else if (it->type().id()=="unsignedbv") + val_cte = Z3_mk_unsigned_int(z3_ctx, atoi(value_cte.c_str()), Z3_mk_int_type(z3_ctx)); + else if (it->type().id()=="fixedbv") + val_cte = Z3_mk_int(z3_ctx, atoi(value_cte.c_str()), Z3_mk_real_type(z3_ctx)); + } + else + { + val_cte = Z3_mk_int(z3_ctx, atoi(value_cte.c_str()), Z3_mk_bv_type(z3_ctx, width)); + } + } + + bv = Z3_mk_store(z3_ctx, bv, int_cte, val_cte); + ++i; + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_constant + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_constant(const exprt &expr, Z3_ast &bv) +{ + std::string value; + unsigned width; + + if (is_signed(expr.type())) + value = integer2string(binary2integer(expr.get_string("value"), true),10); + else + value = integer2string(binary2integer(expr.get_string("value"), false),10); + + if (expr.type().id()=="unsignedbv") + { + if (boolbv_get_width(expr.type(), width)) + return true; + + if (int_encoding) + bv = z3_api.mk_unsigned_int(z3_ctx, atoi(value.c_str())); + else + bv = Z3_mk_unsigned_int(z3_ctx, atoi(value.c_str()), Z3_mk_bv_type(z3_ctx, width)); + } + if (expr.type().id()=="signedbv" || expr.type().id()=="c_enum") + { + if (boolbv_get_width(expr.type(), width)) + return true; + + if (int_encoding) + bv = z3_api.mk_int(z3_ctx, atoi(value.c_str())); + else + bv = Z3_mk_int(z3_ctx, atoi(value.c_str()), Z3_mk_bv_type(z3_ctx, width)); + } + else if (expr.type().id()== "fixedbv") + { + if (boolbv_get_width(expr.type(), width)) + return true; + + if (int_encoding) + bv = Z3_mk_int(z3_ctx, atoi(value.c_str()), Z3_mk_real_type(z3_ctx)); + else + bv = Z3_mk_int(z3_ctx, atoi(value.c_str()), Z3_mk_bv_type(z3_ctx, width)); + } + else if (expr.type().id()== "pointer") + { + if (convert_z3_pointer(expr, value, bv)) + return true; + } + else if (expr.type().id()=="bool") + { + if (expr.is_false()) + bv = Z3_mk_false(z3_ctx); + else if (expr.is_true()) + bv = Z3_mk_true(z3_ctx); + } + else if (expr.type().id()=="array") + { + if (convert_array(expr, bv)) + return true; + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_bitwise + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_bitwise(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()>=2); + Z3_ast *args; + u_int i=0; + + args = new Z3_ast[expr.operands().size()+1]; + + if (convert_bv(expr.op0(), args[0])) + return true; + if (convert_bv(expr.op1(), args[1])) + return true; + + forall_operands(it, expr) + { + if (convert_bv(*it, args[i])) + return true; + + if (i>=1) + { + if (int_encoding) + throw "bitwise operations are not supported"; + else + { + if(expr.id()=="bitand") + args[i+1] = Z3_mk_bvand(z3_ctx, args[i-1], args[i]); + else if(expr.id()=="bitor") + args[i+1] = Z3_mk_bvor(z3_ctx, args[i-1], args[i]); + else if(expr.id()=="bitxor") + args[i+1] = Z3_mk_bvxor(z3_ctx, args[i-1], args[i]); + else if (expr.id()=="bitnand") + args[i+1] = Z3_mk_bvnand(z3_ctx, args[i-1], args[i]); + else if (expr.id()=="bitnor") + args[i+1] = Z3_mk_bvnor(z3_ctx, args[i-1], args[i]); + else if (expr.id()=="bitnxor") + args[i+1] = Z3_mk_bvxnor(z3_ctx, args[i-1], args[i]); + } + } + ++i; + } + + bv=args[i]; + + delete[] args; + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_unary_minus + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_unary_minus(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==1); + Z3_ast args[2]; + + if (convert_bv(expr.op0(), args[0])) + return true; + + if (int_encoding) + { + if (expr.type().id() == "signedbv") + { + args[1] = z3_api.mk_int(z3_ctx,-1); + bv = Z3_mk_mul(z3_ctx, 2, args); + } + else if (expr.type().id() == "fixedbv") + { + args[1] = Z3_mk_int(z3_ctx, -1, Z3_mk_real_type(z3_ctx)); + bv = Z3_mk_mul(z3_ctx, 2, args); + } + } + else + { + bv = Z3_mk_bvneg(z3_ctx, args[0]); + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_if + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_if(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==3); + + Z3_ast operand0, operand1, operand2; + + if (convert_bv(expr.op0(), operand0)) + return true; + + if (convert_bv(expr.op1(), operand1)) + return true; + + if (convert_bv(expr.op2(), operand2)) + return true; + + bv = Z3_mk_ite(z3_ctx, operand0, operand1, operand2); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_logical_ops + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_logical_ops(const exprt &expr, Z3_ast &bv) +{ + assert(expr.type().id()=="bool"); + Z3_ast args[2]; + + if(expr.operands().size()>=2) + { + if (convert_bv(expr.op0(), args[0])) + return true; + if (convert_bv(expr.op1(), args[1])) + return true; + + if(expr.id()=="and") + bv = Z3_mk_and(z3_ctx, 2, args); + else if(expr.id()=="or") + bv = Z3_mk_or(z3_ctx, 2, args); + else if(expr.id()=="xor") + bv = Z3_mk_xor(z3_ctx, args[0], args[1]); + } + else if (expr.operands().size()==1) + { + if (convert_bv(expr.op0(), bv)) + return true; + } + else + assert(false); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_logical_not + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_logical_not(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==1); + Z3_ast operand0; + + if (convert_bv(expr.op0(), operand0)) + return true; + + bv = Z3_mk_not(z3_ctx, operand0); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_equality + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_equality(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==2); + assert(expr.op0().type()==expr.op1().type()); + + Z3_ast args[2]; + + if (convert_bv(expr.op0(), args[0])) + return true; + if (convert_bv(expr.op1(), args[1])) + return true; + + if (expr.id()=="=") + bv = Z3_mk_eq(z3_ctx, args[0], args[1]); + else + bv = Z3_mk_distinct(z3_ctx,2,args); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_add + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_add(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()>=2); + Z3_ast *args; + u_int i=0, size; + + size=expr.operands().size()+1; + args = new Z3_ast[size]; + + if (expr.op0().type().id() == "pointer" || expr.op1().type().id() == "pointer") + { + Z3_ast pointer=0; + + forall_operands(it, expr) + { + if (convert_bv(*it, args[i])) + return true; + + if (it->type().id()=="pointer") + { + pointer = args[i]; + args[i] = z3_api.mk_tuple_select(z3_ctx, pointer, 1); //select pointer index + } + + if (!int_encoding) + { + if (i==1) + { + args[size-1]=Z3_mk_bvadd(z3_ctx, args[0], args[1]); + } + else if (i>1) + { + args[size-1] = Z3_mk_bvadd(z3_ctx, args[size-1], args[i]); + } + } + ++i; + } + + if (int_encoding) + args[i] = Z3_mk_add(z3_ctx, i, args); + + bv = z3_api.mk_tuple_update(z3_ctx, pointer, 1, args[i]); + + if (expr.type().id() == "signedbv") + bv=args[i]; + } + else if (expr.op0().id()=="typecast" || expr.op1().id()=="typecast") + { + assert(expr.operands().size()==2); + if (convert_bv(expr.op0(), args[0])) + return true; + if (convert_bv(expr.op1(), args[1])) + return true; + + if (expr.op0().id()=="typecast") + { + const exprt &offset=expr.op0().operands()[0]; + if (offset.type().id()=="pointer") + args[0] = z3_api.mk_tuple_select(z3_ctx, args[0], 1); //select pointer index + } + + if (expr.op1().id()=="typecast") + { + const exprt &offset=expr.op1().operands()[0]; + if (offset.type().id()=="pointer") + args[1] = z3_api.mk_tuple_select(z3_ctx, args[1], 1); //select pointer index + } + + if (int_encoding) + bv = Z3_mk_add(z3_ctx, 2, args); + else + bv = Z3_mk_bvadd(z3_ctx, args[0], args[1]); + } + else + { + forall_operands(it, expr) + { + if (convert_bv(*it, args[i])) + return true; + + if (!int_encoding) + { + if (i==1) + { + args[size-1]=Z3_mk_bvadd(z3_ctx, args[0], args[1]); + } + else if (i>1) + { + args[size-1] = Z3_mk_bvadd(z3_ctx, args[size-1], args[i]); + } + } + ++i; + } + + if (int_encoding) + args[i] = Z3_mk_add(z3_ctx, i, args); + + bv=args[i]; + } + + delete[] args; + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_sub + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_sub(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()>=2); + Z3_ast *args; + u_int i=0, size; + + size=expr.operands().size()+1; + args = new Z3_ast[size]; + + if (expr.op0().type().id() == "pointer" || expr.op1().type().id() == "pointer") + { + Z3_ast pointer=0; + + forall_operands(it, expr) + { + if (convert_bv(*it, args[i])) + return true; + + if (it->type().id()=="pointer") + { + pointer = args[i]; + args[i] = z3_api.mk_tuple_select(z3_ctx, pointer, 1); //select pointer index + } + + if (!int_encoding) + { + if (i==1) + { + args[size-1]=Z3_mk_bvsub(z3_ctx, args[0], args[1]); + } + else if (i>1) + { + args[size-1] = Z3_mk_bvsub(z3_ctx, args[size-1], args[i]); + } + } + ++i; + } + + if (int_encoding) + args[i] = Z3_mk_sub(z3_ctx, i, args); + + bv = z3_api.mk_tuple_update(z3_ctx, pointer, 1, args[i]); + + if (expr.type().id() == "signedbv") + bv=args[i]; + } + else if (expr.op0().id()=="typecast" || expr.op1().id()=="typecast") + { + assert(expr.operands().size()==2); + + if (convert_bv(expr.op0(), args[0])) + return true; + if (convert_bv(expr.op1(), args[1])) + return true; + + if (expr.op0().id()=="typecast") + { + const exprt &offset=expr.op0().operands()[0]; + if (offset.type().id()=="pointer") + args[0] = z3_api.mk_tuple_select(z3_ctx, args[0], 1); //select pointer index + } + + if (expr.op1().id()=="typecast") + { + const exprt &offset=expr.op1().operands()[0]; + if (offset.type().id()=="pointer") + args[1] = z3_api.mk_tuple_select(z3_ctx, args[1], 1); //select pointer index + } + + if (int_encoding) + bv = Z3_mk_sub(z3_ctx, 2, args); + else + bv = Z3_mk_bvsub(z3_ctx, args[0], args[1]); + } + else + { + forall_operands(it, expr) + { + if (convert_bv(*it, args[i])) + return true; + + if (!int_encoding) + { + if (i==1) + { + args[size-1]=Z3_mk_bvsub(z3_ctx, args[0], args[1]); + } + else if (i>1) + { + args[size-1] = Z3_mk_bvsub(z3_ctx, args[size-1], args[i]); + } + } + ++i; + } + + if (int_encoding) + args[i] = Z3_mk_sub(z3_ctx, i, args); + + bv=args[i]; + } + + delete[] args; + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_div + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_div(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==2); + Z3_ast operand0, operand1; + + if (convert_bv(expr.op0(), operand0)) + return true; + if (convert_bv(expr.op1(), operand1)) + return true; + + if (int_encoding) + { + bv = Z3_mk_div(z3_ctx, operand0, operand1); + } + else + { + if (expr.type().id()=="signedbv" || expr.type().id()=="fixedbv") + bv = Z3_mk_bvsdiv(z3_ctx, operand0, operand1); + else if (expr.type().id()=="unsignedbv") + bv = Z3_mk_bvudiv(z3_ctx, operand0, operand1); + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_mod + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_mod(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==2); + Z3_ast operand0, operand1; + + if (convert_bv(expr.op0(), operand0)) + return true; + if (convert_bv(expr.op1(), operand1)) + return true; + + if (int_encoding) + { + if(expr.type().id()=="signedbv" || expr.type().id()=="unsignedbv") + bv = Z3_mk_mod(z3_ctx, operand0, operand1); + else if (expr.type().id()=="fixedbv") + throw "unsupported type for mod: "+expr.type().id_string(); + } + else + { + if(expr.type().id()=="signedbv") + { + bv = Z3_mk_bvsrem(z3_ctx, operand0, operand1); + } + else if (expr.type().id()=="unsignedbv") + { + bv = Z3_mk_bvurem(z3_ctx, operand0, operand1); + } + else if (expr.type().id()=="fixedbv") + throw "unsupported type for mod: "+expr.type().id_string(); + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_mul + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_mul(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()>=2); + Z3_ast *args; + u_int i=0, size; + + size=expr.operands().size()+1; + args = new Z3_ast[size]; + + if (expr.op0().type().id() == "pointer" || expr.op1().type().id() == "pointer") + { + Z3_ast pointer=0; + + forall_operands(it, expr) + { + if (convert_bv(*it, args[i])) + return true; + + if (it->type().id()=="pointer") + { + pointer = args[i]; + args[i] = z3_api.mk_tuple_select(z3_ctx, pointer, 1); //select pointer index + } + + if (!int_encoding) + { + if (i==1) + { + args[size-1]=Z3_mk_bvmul(z3_ctx, args[0], args[1]); + } + else if (i>1) + { + args[size-1] = Z3_mk_bvmul(z3_ctx, args[size-1], args[i]); + } + } + ++i; + } + + if (int_encoding) + args[i] = Z3_mk_mul(z3_ctx, i, args); + + bv = z3_api.mk_tuple_update(z3_ctx, pointer, 1, args[i]); + } + else if (expr.op0().id()=="typecast" || expr.op1().id()=="typecast") + { + assert(expr.operands().size()==2); + + if (convert_bv(expr.op0(), args[0])) + return true; + if (convert_bv(expr.op1(), args[1])) + return true; + + if (expr.op0().id()=="typecast") + { + const exprt &offset=expr.op0().operands()[0]; + if (offset.type().id()=="pointer") + args[0] = z3_api.mk_tuple_select(z3_ctx, args[0], 1); //select pointer index + } + + if (expr.op1().id()=="typecast") + { + const exprt &offset=expr.op1().operands()[0]; + if (offset.type().id()=="pointer") + args[1] = z3_api.mk_tuple_select(z3_ctx, args[1], 1); //select pointer index + } + + if (int_encoding) + bv = Z3_mk_mul(z3_ctx, 2, args); + else + bv = Z3_mk_bvmul(z3_ctx, args[0], args[1]); + } + else + { + forall_operands(it, expr) + { + if (convert_bv(*it, args[i])) + return true; + + if (!int_encoding) + { + if (i==1) + { + args[size-1]=Z3_mk_bvmul(z3_ctx, args[0], args[1]); + } + else if (i>1) + { + args[size-1] = Z3_mk_bvmul(z3_ctx, args[size-1], args[i]); + } + } + ++i; + } + + if (int_encoding) + args[i] = Z3_mk_mul(z3_ctx, i, args); + + bv=args[i]; + } + + delete[] args; + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_pointer + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_pointer(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==1); + assert(expr.type().id()=="pointer"); + + static Z3_ast pointer_var, pointer; + Z3_type_ast pointer_type; + Z3_ast offset, po, pi; + std::string symbol_name, out; + + if (create_pointer_type(expr.type(), pointer_type)) + return true; + + offset = convert_number(0, config.ansi_c.int_width, true); + + if (expr.op0().id() == "index") + { + const exprt &object=expr.op0().operands()[0]; + const exprt &index=expr.op0().operands()[1]; + + symbol_name = "address_of_index" + object.id_string() + object.get_string("identifier"); + pointer_var = z3_api.mk_var(z3_ctx, symbol_name.c_str(), pointer_type); + + if (object.id()=="zero_string") + { + if (convert_zero_string(object, po)) + return true; + } + else if (object.id()==ID_string_constant) + { + if (convert_bv(object, po)) + return true; + } + else if (object.type().id() == "array" && object.type().subtype().id() != "struct") + { + if (read_cache(object, po)) + return true; + } + else + { + if (convert_bv(object, po)) + return true; + } + + if (convert_bv(index, pi)) + return true; + + if (select_pointer_value(po, pi, pointer)) + return true; + + if (expr.op0().type().id()!="struct") + { + pointer_var = z3_api.mk_tuple_update(z3_ctx, pointer_var, 0, pointer); //update object + bv = z3_api.mk_tuple_update(z3_ctx, pointer_var, 1, pi); //update offset + return false; + } + } + else if (expr.op0().id() == "symbol") + { + if (expr.op0().type().id() == "signedbv" || expr.op0().type().id() == "fixedbv" + || expr.op0().type().id() == "unsignedbv") + { + if (convert_z3_pointer(expr, expr.op0().get_string("identifier"), pointer_var)) + return true; + } + else if (expr.op0().type().id() == "pointer") + { + if (convert_bv(expr.op0(), pointer_var)) + return true; + } + else if (expr.op0().type().id() == "struct") + { + if (expr.type().subtype().id()=="symbol") + return true; + + symbol_name = "address_of_struct" + expr.op0().get_string("identifier"); + pointer_var = z3_api.mk_var(z3_ctx, symbol_name.c_str(), pointer_type); + + if (convert_bv(expr.op0(), pointer)) + return true; + + pointer_var = z3_api.mk_tuple_update(z3_ctx, pointer_var, 0, pointer); //update object + + } + } + else if (expr.op0().id() == "member") + { + const exprt &object=expr.op0().operands()[0]; + + symbol_name = "address_of_member" + object.get_string("identifier"); + pointer_var = z3_api.mk_var(z3_ctx, symbol_name.c_str(), pointer_type); + + if (convert_bv(expr.op0(), pointer)) + return true; + + pointer_var = z3_api.mk_tuple_update(z3_ctx, pointer_var, 0, pointer); //update object + } + + bv = z3_api.mk_tuple_update(z3_ctx, pointer_var, 1, offset); //update offset + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_array_of + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_array_of(const exprt &expr, Z3_ast &bv) +{ + Z3_ast value, index; + Z3_type_ast array_type=0; + const array_typet &array_type_size=to_array_type(expr.type()); + std::string tmp, out, identifier; + static u_int size, j, inc=0; + unsigned width; + + tmp = integer2string(binary2integer(array_type_size.size().get_string("value"), false),10); + size = atoi(tmp.c_str()); + size = (size==0) ? 1 : size; //fill in at least one position + index = convert_number(j, config.ansi_c.int_width, true); + + if (convert_bv(expr.op0(), value)) + return true; + + if (create_array_type(expr.type(), array_type)) + return true; + + if (boolbv_get_width(expr.op0().type(), width)) + return true; + + if (expr.type().subtype().id()=="bool") + { + value = Z3_mk_false(z3_ctx); + identifier = "ARRAY_OF(false)" + width; + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), array_type); + } + else if (expr.type().subtype().id()=="signedbv" || expr.type().subtype().id()=="unsignedbv") + { + ++inc; + identifier = "ARRAY_OF(0)" + width + inc; + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), array_type); + } + else if (expr.type().subtype().id()=="fixedbv") + { + identifier = "ARRAY_OF(0l)" + width; + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), array_type); + } + else if (expr.type().subtype().id()=="pointer") + { + identifier = "ARRAY_OF(0p)" + width; + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), array_type); + value = z3_api.mk_tuple_select(z3_ctx, value, 0); //select pointer object + } + else if (expr.type().id()=="array" && expr.type().subtype().id()=="struct") + { + std::string identifier; + identifier = "array_of_" + expr.op0().type().get_string("tag"); + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), array_type); + } + else if (expr.type().subtype().id()=="array") + { + ++inc; + identifier = "ARRAY_OF(0a)" + width + inc; + out = "identifier: "+ identifier; + bv = z3_api.mk_var(z3_ctx, identifier.c_str(), array_type); + } + + //update array + for (j=0; jwidth_expr) + operand0 = Z3_mk_extract(z3_ctx, (width_expr-1), 0, operand0); + if (width_op1>width_expr) + operand1 = Z3_mk_extract(z3_ctx, (width_expr-1), 0, operand1); + + if (int_encoding) + throw "bitshift operations are not supported yet"; + else + { + if(expr.id()=="ashr") + bv = Z3_mk_bvashr(z3_ctx, operand0, operand1); + else if (expr.id()=="lshr") + bv = Z3_mk_bvlshr(z3_ctx, operand0, operand1); + else if(expr.id()=="shl") + bv = Z3_mk_bvshl(z3_ctx, operand0, operand1); + else + assert(false); + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_abs + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_abs(const exprt &expr, Z3_ast &bv) +{ + unsigned width; + std::string out; + + if (boolbv_get_width(expr.type(), width)) + return true; + + const exprt::operandst &operands=expr.operands(); + + if(operands.size()!=1) + throw "abs takes one operand"; + + const exprt &op0=expr.op0(); + static Z3_ast zero, one, is_negative, val_orig, val_mul; + + out = "width: "+ width; + if (expr.type().id()=="signedbv" || expr.type().id()=="fixedbv") + zero = convert_number(0, width, true); + else if (expr.type().id()=="unsignedbv") + zero = convert_number(0, width, false); + + one = convert_number(-1, width, true); + + if (convert_bv(op0, val_orig)) + return true; + + if (expr.type().id()=="signedbv" || expr.type().id()=="fixedbv") + is_negative = Z3_mk_bvslt(z3_ctx, val_orig, zero); + + val_mul = Z3_mk_bvmul(z3_ctx, val_orig, one); + bv = Z3_mk_ite(z3_ctx, is_negative, val_mul, val_orig); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_with + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_with(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()>=1); + Z3_ast array_var, array_val, operand0, operand1, operand2; + + if (expr.type().id() == "struct" || expr.type().id() == "union") + { + if (convert_bv(expr.op0(), array_var)) + return true; + + if (convert_bv(expr.op2(), array_val)) + return true; + + bv = z3_api.mk_tuple_update(z3_ctx, array_var, + convert_member_name(expr.op0(), expr.op1()), array_val); + } + else + { + if (convert_bv(expr.op0(), operand0)) + return true; + if (convert_bv(expr.op1(), operand1)) + return true; + if (convert_bv(expr.op2(), operand2)) + return true; + + if (expr.op2().type().id() == "pointer") + { + if (select_pointer_value(operand0, operand1, operand2)) //select pointer value + return true; + } + else if (expr.op2().id() == "typecast") + { + if (select_pointer_value(operand0, operand1, operand2)) //select pointer value + return true; + } + + bv = Z3_mk_store(z3_ctx, operand0, operand1, operand2); + } + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_bitnot + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_bitnot(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==1); + Z3_ast operand0; + + if (convert_bv(expr.op0(), operand0)) + return true; + + if (int_encoding) + bv = Z3_mk_not(z3_ctx, operand0); + else + bv = Z3_mk_bvnot(z3_ctx, operand0); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_member_name + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +u_int z3_convt::convert_member_name(const exprt &lhs, const exprt &rhs) +{ + const struct_typet &struct_type=to_struct_type(lhs.type()); + const struct_typet::componentst &components=struct_type.components(); + u_int i=0, resp=0; + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++, i++) + { + if (it->get("name").compare(rhs.get_string("component_name")) == 0) + resp=i; + } + + return resp; +} + +/******************************************************************* + Function: z3_convt::convert_object + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_object(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==2); + Z3_ast args[2]; + + if (convert_bv(expr.op0(), args[0])) + return true; + if (convert_bv(expr.op1(), args[1])) + return true; + + args[0] = z3_api.mk_tuple_select(z3_ctx, args[0], 0); + args[1] = z3_api.mk_tuple_select(z3_ctx, args[1], 0); + + bv = Z3_mk_distinct(z3_ctx, 2, args); + + return false; +} + +/******************************************************************* + Function: z3_convt::select_pointer_offset + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::select_pointer_offset(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==1); + static Z3_ast pointer; + + if (convert_bv(expr.op0(), pointer)) + return true; + + bv = z3_api.mk_tuple_select(z3_ctx, pointer, 1); //select pointer offset + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_member + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_member(const exprt &expr, Z3_ast &bv) +{ + const struct_typet &struct_type=to_struct_type(expr.op0().type()); + const struct_typet::componentst &components=struct_type.components(); + u_int i=0, j=0; + Z3_ast struct_var; + + if (convert_bv(expr.op0(), struct_var)) + return true; + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++, i++) + { + if (it->get("name").compare(expr.get_string("component_name")) == 0) + j=i; + } + + bv = z3_api.mk_tuple_select(z3_ctx, struct_var, j); + + return false; +} + +/******************************************************************* + Function: convert_pointer_object + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_pointer_object(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==1 && is_ptr(expr.op0().type())); + Z3_ast pointer_object=0; + const exprt &object=expr.op0().operands()[0]; + unsigned width, object_width; + + if (boolbv_get_width(expr.type(), width)) + return true; + + + if (expr.op0().id()=="symbol" || expr.op0().id()=="constant") + { + if (convert_bv(expr.op0(), pointer_object)) + return true; + pointer_object = z3_api.mk_tuple_select(z3_ctx, pointer_object, 0); + + if (expr.op0().type().id()=="pointer") + { + if (expr.op0().type().subtype().id()=="empty" || expr.op0().type().subtype().id()=="symbol") + { + object_width = config.ansi_c.int_width; + } + else + { + if (boolbv_get_width(expr.op0().type().subtype(), object_width)) + return true; + } + } + else + { + if (boolbv_get_width(expr.op0().type(), object_width)) + return true; + } + + if (width>object_width) + return Z3_mk_zero_ext(z3_ctx, (width-object_width), pointer_object); + else if (widthtype().id() == "pointer") + { + sprintf(val,"%i",i); + if (int_encoding) + pointer_object = z3_api.mk_int(z3_ctx, atoi(val)); + else + pointer_object = Z3_mk_int(z3_ctx, atoi(val), Z3_mk_bv_type(z3_ctx, config.ansi_c.int_width)); + } + } + } + else + { + if (convert_bv(object, pointer_object)) + return true; + + if (object.type().id()=="signedbv" || object.type().id()=="unsignedbv" + || object.type().id()=="fixedbv") + { + if (boolbv_get_width(object.type(), object_width)) + return true; + + if (width>object_width) + return Z3_mk_zero_ext(z3_ctx, (width-object_width), pointer_object); + else if (widthobject_width) + return Z3_mk_zero_ext(z3_ctx, (width-object_width), pointer_object); + else if (widthobject_width) + return Z3_mk_zero_ext(z3_ctx, (width-object_width), pointer_object); + else if (widthtype().id() == expr.type().id()) + { + if (boolbv_get_width(it->type(), object_width)) + return true; + + if (width==object_width) + { + if (convert_identifier(it->get("name").c_str(), it->type(), bv)) + return true; + + return false; + } + } + else + { + if (boolbv_get_width(it->type(), object_width)) + return true; + + if (width==object_width) + { + if (convert_identifier(it->get("name").c_str(), expr.type(), bv/*pointer_object*/)) + return true; + + return false; + } + } + } + bv = Z3_mk_zero_ext(z3_ctx, (width-object_width), pointer_object); + return false; + } + } + + bv = pointer_object; + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_zero_string_length + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_zero_string_length(const exprt &expr, Z3_ast &bv) +{ + assert(expr.operands().size()==1); + Z3_ast operand; + + if (convert_bv(expr.op0(), operand)) + return true; + + bv = z3_api.mk_tuple_select(z3_ctx, operand, 0); + + return false; +} + +/******************************************************************* + Function: z3_convt::convert_z3_expr + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::convert_z3_expr(const exprt &expr, Z3_ast &bv) +{ + if (expr.id() == "symbol") + return convert_identifier(expr.get_string("identifier"), expr.type(), bv); + else if (expr.id() == "nondet_symbol") + return convert_identifier("nondet$"+expr.get_string("identifier"), expr.type(), bv); + else if (expr.id() == "typecast") + return convert_typecast(expr, bv); + else if (expr.id() == "struct" || expr.id() == "union") + return convert_struct(expr, bv); + else if (expr.id() == "constant") + return convert_constant(expr, bv); + else if (expr.id() == "bitand" || expr.id() == "bitor" || expr.id() == "bitxor" + || expr.id() == "bitnand" || expr.id() == "bitnor" || expr.id() == "bitnxor") + return convert_bitwise(expr, bv); + else if (expr.id() == "bitnot") + return convert_bitnot(expr, bv); + else if (expr.id() == "unary-") + return convert_unary_minus(expr, bv); + else if (expr.id() == "if") + return convert_if(expr, bv); + else if (expr.id() == "and" || expr.id() == "or" || expr.id() == "xor") + return convert_logical_ops(expr, bv); + else if (expr.id() == "not") + return convert_logical_not(expr, bv); + else if (expr.id() == "=" || expr.id() == "notequal") + return convert_equality(expr, bv); + else if (expr.id() == "<=" || expr.id() == "<" || expr.id() == ">=" + || expr.id() == ">") + return convert_rel(expr, bv); + else if (expr.id() == "+") + return convert_add(expr, bv); + else if (expr.id() == "-") + return convert_sub(expr, bv); + else if (expr.id() == "/") + return convert_div(expr, bv); + else if (expr.id() == "mod") + return convert_mod(expr, bv); + else if (expr.id() == "*") + return convert_mul(expr, bv); + else if (expr.id() == "address_of" || expr.id() == "implicit_address_of" + || expr.id() == "reference_to") + return convert_pointer(expr, bv); + else if (expr.id() == "array_of") + return convert_array_of(expr, bv); + else if (expr.id() == "index") + return convert_index(expr, bv); + else if (expr.id() == "ashr" || expr.id() == "lshr" || expr.id() == "shl") + return convert_shift(expr, bv); + else if(expr.id()=="abs") + return convert_abs(expr, bv); + else if (expr.id() == "with") + return convert_with(expr, bv); + else if (expr.id() == "member") + return convert_member(expr, bv); + else if (expr.id()=="zero_string") + return convert_zero_string(expr, bv); + else if (expr.id() == "pointer_offset") + return select_pointer_offset(expr, bv); + else if (expr.id() == "pointer_object") + return convert_pointer_object(expr, bv); + else if (expr.id() == "same-object") + return convert_object(expr, bv); + else if (expr.id() == ID_string_constant) { + exprt tmp; + string2array(expr, tmp); + return convert_bv(tmp, bv); + } else if (expr.id() == "zero_string_length") + return convert_zero_string_length(expr.op0(), bv); + else if (expr.id() == "replication") + assert(expr.operands().size()==2); +#if 0 + else if(expr.id()=="isnan") { + ignoring(expr); + return convert_bv(expr.op0()); + } +#endif + + return true; +} + +/******************************************************************* + Function: z3_convt::assign_z3_expr + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +bool z3_convt::assign_z3_expr(const exprt expr) +{ + u_int size = expr.operands().size(); + + //ignore these IRep expressions for now. I don't know what they mean. + + if ((expr.op0().id()=="index" && expr.op0().type().id() == "pointer" + && expr.op0().type().subtype().id()=="symbol") + && (expr.id()=="invalid-pointer" || expr.id()=="same-object" || expr.id()=="is_dynamic_object")) + { + ignoring(expr); + ignoring_expr=false; + return false; + } + else if (expr.op0().id()=="symbol" && expr.op0().type().id() == "array" + && expr.op0().type().subtype().id()=="pointer" + && expr.op0().type().subtype().subtype().id()=="symbol" + && expr.id()!="is_dynamic_object" && expr.id()!="invalid-pointer") + { + if (expr.op1().id()=="array_of" && expr.op1().type().id() == "array" + && expr.op1().type().subtype().id()=="pointer" + && expr.op1().type().subtype().subtype().id()=="symbol") + { + ignoring(expr); + ignoring_expr=false; + return false; + } + } + else if (size==2 && expr.op1().id() == "unary+") + { + ignoring(expr.op1()); + ignoring_expr=false; + return false; + } + + return true; +} + +/******************************************************************* + Function: z3_convt::set_to + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +void z3_convt::set_to(const exprt &expr, bool value) +{ +#ifdef DEBUG + std::cout << "\n" << __FUNCTION__ << "[" << __LINE__ << "]" << "\n"; +#endif + + if(expr.type().id()!="bool") + { + std::string msg="prop_convt::set_to got " + "non-boolean expression:\n"; + msg+=expr.to_string(); + throw msg; + } + + bool boolean=true; + + forall_operands(it, expr) + if(it->type().id()!="bool") + { + boolean=false; + break; + } + + if(boolean) + { + + if(expr.id()=="not") + { + if(expr.operands().size()==1) + { + set_to(expr.op0(), !value); + return; + } + } + else + { + if(value) + { + // set_to_true + if(expr.id()=="and") + { + forall_operands(it, expr) + set_to_true(*it); + + return; + } + else if(expr.id()=="or") + { + if(expr.operands().size()>0) + { + bvt bv; + bv.reserve(expr.operands().size()); + + forall_operands(it, expr) + bv.push_back(convert(*it)); + prop.lcnf(bv); + return; + } + } + else if(expr.id()=="=>") + { + if(expr.operands().size()==2) + { + bvt bv; + bv.resize(2); + bv[0]=prop.lnot(convert(expr.op0())); + bv[1]=convert(expr.op1()); + prop.lcnf(bv); + return; + } + } + else if(expr.id()=="=") + { + if(!set_equality_to_true(expr)) + return; + } + } + else + { + // set_to_false + if(expr.id()=="=>") // !(a=>b) == (a && !b) + { + if(expr.operands().size()==2) + { + set_to_true(expr.op0()); + set_to_false(expr.op1()); + } + } + else if(expr.id()=="or") // !(a || b) == (!a && !b) + { + forall_operands(it, expr) + set_to_false(*it); + } + } + } + } + + // fall back to convert + prop.l_set_to(convert(expr), value); + + if (value && expr.id() == "and") + { + forall_operands(it, expr) + set_to(*it, true); + return; + } + + if (value && expr.is_true()) + return; + + if (expr.id() == "=" && value) + { + assert(expr.operands().size()==2); + + Z3_ast result, operand[2]; + const exprt &op0=expr.op0(); + const exprt &op1=expr.op1(); + + if (assign_z3_expr(expr) && ignoring_expr) + { + if (convert_bv(op0, operand[0])) + return; + if (convert_bv(op1, operand[1])) + return ; + + if (op0.type().id()=="pointer" && op1.type().id()=="pointer") + { + static Z3_ast pointer[2], formula[2]; + + pointer[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 0); //select object + pointer[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 0); + formula[0] = Z3_mk_eq(z3_ctx, pointer[0], pointer[1]); + + pointer[0] = z3_api.mk_tuple_select(z3_ctx, operand[0], 1); //select index + pointer[1] = z3_api.mk_tuple_select(z3_ctx, operand[1], 1); + formula[1] = Z3_mk_eq(z3_ctx, pointer[0], pointer[1]); + + result = Z3_mk_and(z3_ctx, 2, formula); + Z3_assert_cnstr(z3_ctx, result); + } + else + { + result = Z3_mk_eq(z3_ctx, operand[0], operand[1]); + Z3_assert_cnstr(z3_ctx, result); + } + } + } + ignoring_expr=true; +} + +/******************************************************************* + Function: z3_convt::get_number_vcs_z3 + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +u_int z3_convt::get_number_vcs_z3(void) +{ + return number_vcs_z3; +} + +/******************************************************************* + Function: z3_convt::get_number_variables_z + + Inputs: + + Outputs: + + Purpose: + + \*******************************************************************/ + +u_int z3_convt::get_number_variables_z3(void) +{ + return number_variables_z3; +} diff --git a/src/solvers/z3/z3_dec.h b/src/solvers/z3/z3_dec.h new file mode 100644 index 00000000000..d86d0c54150 --- /dev/null +++ b/src/solvers/z3/z3_dec.h @@ -0,0 +1,186 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#ifndef CPROVER_SOLVERS_Z3_DEC_H +#define CPROVER_SOLVERS_Z3_DEC_H + +/* +#include +#include + +#include +*/ + +#include +#include + +#include "z3_prop.h" + +class z3_prop_wrappert +{ +public: + z3_prop_wrappert() { } + +protected: + z3_propt z3_prop; +}; + +class z3_dect:protected z3_prop_wrappert, public prop_convt +{ +public: + z3_dect(); + ~z3_dect() { } + + /* + Z3_lbool check2_z3_properties(); + void set_z3_encoding(bool enc); + void set_z3_ecp(bool ecp); + */ + + // overloading + virtual exprt get(const exprt &expr) const; + + /* + u_int get_number_variables_z3(void); + u_int get_number_vcs_z3(void); + */ + +protected: + #if 0 + virtual literalt convert_rest(const exprt &expr); + virtual void set_to(const exprt &expr, bool value); + bool assign_z3_expr(const exprt expr); + u_int convert_member_name(const exprt &lhs, const exprt &rhs); + + bool create_array_type(const typet &type, Z3_type_ast &bv); + bool create_type(const typet &type, Z3_type_ast &bv); + bool create_struct_type(const typet &type, Z3_type_ast &bv); + bool create_enum_type(const typet &type, Z3_type_ast &bv); + bool create_pointer_type(const typet &type, Z3_type_ast &bv); + Z3_ast convert_lt(const exprt &expr); + Z3_ast convert_gt(const exprt &expr); + Z3_ast convert_le(const exprt &expr); + Z3_ast convert_ge(const exprt &expr); + Z3_ast convert_eq(const exprt &expr); + Z3_ast convert_invalid(const exprt &expr); + Z3_ast convert_same_object(const exprt &expr); + Z3_ast convert_dynamic_object(const exprt &expr); + Z3_ast convert_overflow_sum(const exprt &expr); + Z3_ast convert_overflow_sub(const exprt &expr); + Z3_ast convert_overflow_mul(const exprt &expr); + Z3_ast convert_overflow_unary(const exprt &expr); + Z3_ast convert_overflow_typecast(const exprt &expr); + Z3_ast convert_rest_index(const exprt &expr); + Z3_ast convert_rest_member(const exprt &expr); + bool convert_rel(const exprt &expr, Z3_ast &bv); + bool convert_typecast(const exprt &expr, Z3_ast &bv); + bool convert_struct(const exprt &expr, Z3_ast &bv); + bool convert_z3_pointer(const exprt &expr, std::string symbol, Z3_ast &bv); + bool convert_zero_string(const exprt &expr, Z3_ast &bv); + bool convert_array(const exprt &expr, Z3_ast &bv); + bool convert_constant(const exprt &expr, Z3_ast &bv); + bool convert_bitwise(const exprt &expr, Z3_ast &bv); + bool convert_unary_minus(const exprt &expr, Z3_ast &bv); + bool convert_if(const exprt &expr, Z3_ast &bv); + bool convert_logical_ops(const exprt &expr, Z3_ast &bv); + bool convert_logical_not(const exprt &expr, Z3_ast &bv); + bool convert_equality(const exprt &expr, Z3_ast &bv); + bool convert_add(const exprt &expr, Z3_ast &bv); + bool convert_sub(const exprt &expr, Z3_ast &bv); + bool convert_div(const exprt &expr, Z3_ast &bv); + bool convert_mod(const exprt &expr, Z3_ast &bv); + bool convert_mul(const exprt &expr, Z3_ast &bv); + bool convert_pointer(const exprt &expr, Z3_ast &bv); + bool convert_array_of(const exprt &expr, Z3_ast &bv); + bool convert_index(const exprt &expr, Z3_ast &bv); + bool convert_shift(const exprt &expr, Z3_ast &bv); + bool convert_abs(const exprt &expr, Z3_ast &bv); + bool convert_with(const exprt &expr, Z3_ast &bv); + bool convert_bitnot(const exprt &expr, Z3_ast &bv); + bool convert_object(const exprt &expr, Z3_ast &bv); + bool select_pointer_offset(const exprt &expr, Z3_ast &bv); + bool convert_member(const exprt &expr, Z3_ast &bv); + bool convert_pointer_object(const exprt &expr, Z3_ast &bv); + bool convert_zero_string_length(const exprt &expr, Z3_ast &bv); + bool select_pointer_value(Z3_ast object, Z3_ast offset, Z3_ast &bv); + bool convert_z3_expr(const exprt &expr, Z3_ast &bv); + + bool convert_identifier(const std::string &identifier, const typet &type, Z3_ast &bv); + bool convert_bv(const exprt &expr, Z3_ast &bv); + + exprt bv_get_rec( + const Z3_ast &bv, + std::vector &unknown, + const bool cache, + const typet &type) const; + + void fill_vector( + const Z3_ast &bv, + std::vector &unknown, + const typet &type) const; + + pointer_logict pointer_logic; + + struct eqstr + { + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) == 0; + } + }; + + typedef hash_map_cont bv_cachet; + bv_cachet bv_cache; + + typedef hash_map_cont z3_cachet; + z3_cachet z3_cache; + + typedef std::map map_varst; + map_varst map_vars; + +private: + std::string itos(int i); + bool is_bv(const typet &type); + bool check_all_types(const typet &type); + bool is_ptr(const typet &type); + bool is_signed(const typet &type); + bool is_in_cache(const exprt &expr); + bool write_cache(const exprt &expr); + const typet select_pointer(const typet &type); + bool read_cache(const exprt &expr, Z3_ast &bv); + void create_z3_context(void); + static std::string ascii2int(char ch); + void print_data_types(Z3_ast operand0, Z3_ast operand1); + void print_location(const exprt &expr); + void show_bv_size(Z3_ast operand); + Z3_ast convert_number(int value, u_int width, bool type); + u_int number_variables_z3, set_to_counter, number_vcs_z3; + z3_capi z3_api; + bool int_encoding, ignoring_expr, equivalence_checking; + Z3_context z3_ctx; + + struct identifiert + { + typet type; + exprt value; + + identifiert() + { + type.make_nil(); + value.make_nil(); + } + }; + + typedef hash_map_cont + identifier_mapt; + + identifier_mapt identifier_map; + #endif +}; + +#endif diff --git a/src/solvers/z3/z3_get.cpp b/src/solvers/z3/z3_get.cpp new file mode 100644 index 00000000000..dc8cd545c39 --- /dev/null +++ b/src/solvers/z3/z3_get.cpp @@ -0,0 +1,292 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "z3_conv.h" + +/*******************************************************************\ + +Function: z3_convt::get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt z3_convt::get(const exprt &expr) const +{ + if(expr.id()=="symbol" || + expr.id()=="nondet_symbol") + { + std::string identifier, tmp; + std::vector unknown; + Z3_ast bv; + + identifier = expr.get_string("identifier"); + + map_varst::const_iterator cache_result=map_vars.find(identifier.c_str()); + if(cache_result!=map_vars.end()) + { + bv = cache_result->second; + return bv_get_rec(bv, unknown, true, expr.type()); + } + + return bv_get_rec(bv, unknown, false, expr.type()); + } + else if(expr.id()=="constant") + return expr; + + return expr; +} + +/*******************************************************************\ + +Function: z3_convt::bv_get_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_convt::fill_vector( + const Z3_ast &bv, + std::vector &unknown, + const typet &type) const +{ + unsigned i, width; + static unsigned int idx; + Z3_app app = Z3_to_app(z3_ctx, bv); + unsigned num_fields = Z3_get_app_num_args(z3_ctx, app); + Z3_ast tmp; + std::string value; + + for (i = 0; i < num_fields; i++) + { + tmp = Z3_get_app_arg(z3_ctx, app, i); + + if (Z3_get_ast_kind(z3_ctx, tmp)==Z3_NUMERAL_AST) + { + boolbv_get_width(type, width); + value= Z3_get_numeral_string(z3_ctx, tmp); + constant_exprt value_expr(type); + value_expr.set_value(integer2binary(string2integer(value),width)); + + if (i==1) + idx = atoi(value.c_str()); + else if (i==2) + if (idx &unknown, + const bool cache, + const typet &type) const +{ + unsigned width; + + if(boolbv_get_width(type, width)) + return nil_exprt(); + + if(type.id()=="bool") + { + std::string value; + size_t found; + + if (cache) + { + Z3_app app = Z3_to_app(z3_ctx, bv); + Z3_func_decl d = Z3_get_app_decl(z3_ctx, app); + value = Z3_func_decl_to_string(z3_ctx, d); + found=value.find("true"); + } + else + { + found=std::string::npos; + } + + if (found!=std::string::npos) + return true_exprt(); + else + return false_exprt(); // default + } + + bvtypet bvtype=get_bvtype(type); + + if(bvtype==IS_UNKNOWN) + { + if(type.id()=="array") + { + unsigned sub_width; + const typet &subtype=type.subtype(); + + if(!boolbv_get_width(subtype, sub_width)) + { + exprt expr; + static exprt::operandst op; + constant_exprt zero_expr(subtype); + + op.reserve(width/sub_width); + unsigned num_fields = Z3_get_app_num_args(z3_ctx, Z3_to_app(z3_ctx, bv)); + + if (num_fields==0) + return nil_exprt(); + + unknown.resize(width/sub_width); + + fill_vector(bv, unknown, subtype); + + if (unknown.size()==0) + return nil_exprt(); + + unsigned int size=unknown.size(); + zero_expr.set_value("0"); + + for(unsigned i=0; i(it->find("type")); + op.push_back(nil_exprt()); + + unsigned sub_width; + if(!boolbv_get_width(subtype, sub_width)) + { + tmp = Z3_get_app_arg(z3_ctx, app, i); + expr=bv_get_rec(tmp, unknown, true, subtype); + if (!expr.is_nil()) + unknown.push_back(expr); + op.back()=unknown.back(); + ++i; + } + } + + exprt dest=exprt(type.id(), type); + dest.operands().swap(op); + + return dest; + } + else if(type.id()=="union") + { + //@TODO: reconstruct the counter-example for unions + return nil_exprt(); + + } + } + + std::string value; + Z3_ast_kind ast_kind; + + ast_kind = Z3_get_ast_kind(z3_ctx, bv); + + if (ast_kind==Z3_NUMERAL_AST) + value= Z3_get_numeral_string(z3_ctx, bv); + else + return nil_exprt(); + + switch(bvtype) + { + case IS_C_ENUM: + { + constant_exprt value_expr(type); + value_expr.set_value(value); + return value_expr; + } + break; + + case IS_UNKNOWN: + break; + + case IS_RANGE: + { + mp_integer int_value=string2integer(value, false); + mp_integer from=string2integer(type.get_string("from")); + + constant_exprt value_expr(type); + value_expr.set_value(integer2string(int_value+from)); + return value_expr; + } + break; + + default: + unsigned width; + boolbv_get_width(type, width); + constant_exprt value_expr(type); + value_expr.set_value(integer2binary(string2integer(value),width)); + return value_expr; + } + + return nil_exprt(); +} + diff --git a/src/solvers/z3/z3_prop.cpp b/src/solvers/z3/z3_prop.cpp new file mode 100644 index 00000000000..f7ccc859779 --- /dev/null +++ b/src/solvers/z3/z3_prop.cpp @@ -0,0 +1,838 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#include +#include +#include + +#include "z3_prop.h" + +/*******************************************************************\ + +Function: z3_propt::z3_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +z3_propt::z3_propt(std::ostream &_out):out(_out) +{ + _no_variables=1; +} + +/*******************************************************************\ + +Function: z3_propt::~z3_propt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +z3_propt::~z3_propt() +{ +} + +/*******************************************************************\ + +Function: z3_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_propt::land(literalt a, literalt b, literalt o) +{ + // a*b=c <==> (a + o')( b + o')(a'+b'+o) + bvt lits; + + lits.clear(); + lits.reserve(2); + lits.push_back(pos(a)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(2); + lits.push_back(pos(b)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(neg(a)); + lits.push_back(neg(b)); + lits.push_back(pos(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: z3_propt::lor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_propt::lor(literalt a, literalt b, literalt o) +{ + // a+b=c <==> (a' + c)( b' + c)(a + b + c') + bvt lits; + + lits.clear(); + lits.reserve(2); + lits.push_back(neg(a)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(2); + lits.push_back(neg(b)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(pos(a)); + lits.push_back(pos(b)); + lits.push_back(neg(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: z3_propt::lxor + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_propt::lxor(literalt a, literalt b, literalt o) +{ + // a xor b = o <==> (a' + b' + o') + // (a + b + o' ) + // (a' + b + o) + // (a + b' + o) + bvt lits; + + lits.clear(); + lits.reserve(3); + lits.push_back(neg(a)); + lits.push_back(neg(b)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(pos(a)); + lits.push_back(pos(b)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(neg(a)); + lits.push_back(pos(b)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(pos(a)); + lits.push_back(neg(b)); + lits.push_back(pos(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: z3_propt::lnand + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_propt::lnand(literalt a, literalt b, literalt o) +{ + // a Nand b = o <==> (a + o)( b + o)(a' + b' + o') + bvt lits; + + lits.clear(); + lits.reserve(2); + lits.push_back(pos(a)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(2); + lits.push_back(pos(b)); + lits.push_back(pos(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(neg(a)); + lits.push_back(neg(b)); + lits.push_back(neg(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_propt::lnor(literalt a, literalt b, literalt o) +{ + // a Nor b = o <==> (a' + o')( b' + o')(a + b + o) + bvt lits; + + lits.clear(); + lits.reserve(2); + lits.push_back(neg(a)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(2); + lits.push_back(neg(b)); + lits.push_back(neg(o)); + lcnf(lits); + + lits.clear(); + lits.reserve(3); + lits.push_back(pos(a)); + lits.push_back(pos(b)); + lits.push_back(pos(o)); + lcnf(lits); +} + +/*******************************************************************\ + +Function: z3_propt::lequal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_propt::lequal(literalt a, literalt b, literalt o) +{ + lxor(a, b, lnot(o)); +} + +/*******************************************************************\ + +Function: z3_propt::limplies + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_propt::limplies(literalt a, literalt b, literalt o) +{ + lor(lnot(a), b, o); +} + +/*******************************************************************\ + +Function: z3_propt::land + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +literalt z3_propt::land(const bvt &bv) +{ + if(bv.size()==0) return const_literal(true); + if(bv.size()==1) return bv[0]; + if(bv.size()==2) return land(bv[0], bv[1]); + + for(unsigned i=0; i s; + + dest.reserve(bv.size()); + + for(bvt::const_iterator it=bv.begin(); it!=bv.end(); it++) + { + if(s.insert(*it).second) + dest.push_back(*it); + } +} + +/*******************************************************************\ + +Function: z3_propt::process_clause + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool z3_propt::process_clause(const bvt &bv, bvt &dest) +{ + dest.clear(); + + // empty clause! this is UNSAT + if(bv.empty()) return false; + + std::set s; + + dest.reserve(bv.size()); + + for(bvt::const_iterator it=bv.begin(); + it!=bv.end(); + it++) + { + literalt l=*it; + + // we never use index 0 + assert(l.var_no()!=0); + + if(l.is_true()) + return true; // clause satisfied + + if(l.is_false()) + continue; + + if(l.var_no()>=_no_variables) + std::cout << "l.var_no()=" << l.var_no() << " _no_variables=" << _no_variables << std::endl; + assert(l.var_no()<_no_variables); + + // prevent duplicate literals + if(s.insert(l).second) + dest.push_back(l); + + if(s.find(lnot(l))!=s.end()) + return true; // clause satisfied + } + + return false; +} + +/*******************************************************************\ + +Function: z3_propt::lcnf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void z3_propt::lcnf(const bvt &bv) +{ + bvt new_bv; + + if(process_clause(bv, new_bv)) + return; + + if (new_bv.size()==0) + return; + + Z3_ast lor_var, args[new_bv.size()]; + unsigned int i=0; + + for(bvt::const_iterator it=new_bv.begin(); it!=new_bv.end(); it++, i++) + args[i] = z3_literal(*it); + + if (i>1) + { + lor_var = Z3_mk_or(z3_ctx, i, args); + Z3_assert_cnstr(z3_ctx, lor_var); + } + else + { + Z3_assert_cnstr(z3_ctx, args[0]); + } +} + +/*******************************************************************\ + +Function: z3_propt::z3_literal + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +Z3_ast z3_propt::z3_literal(literalt l) +{ + Z3_ast literal_l; + std::string literal_s; + + if(l==const_literal(false)) + return Z3_mk_false(z3_ctx); + else if(l==const_literal(true)) + return Z3_mk_true(z3_ctx); + + literal_s = "l"+i2string(l.var_no()); +#ifdef DEBUG + std::cout << "literal_s: " << literal_s << "\n"; +#endif + literal_l = z3_api.mk_bool_var(z3_ctx, literal_s.c_str()); + + if(l.sign()) + { + return Z3_mk_not(z3_ctx, literal_l); + } + + return literal_l; +} + +/*******************************************************************\ + +Function: z3_propt::prop_solve + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +propt::resultt z3_propt::prop_solve() +{ + return P_ERROR; +} + +/*******************************************************************\ + +Function: z3_propt::l_get + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +tvt z3_propt::l_get(literalt a) const +{ + tvt result=tvt(false); + std::string literal; + Z3_ast z3_literal; + size_t found; + + if(a.is_true()) + return tvt(true); + else if(a.is_false()) + return tvt(false); + + literal = "l"+i2string(a.var_no()); + + map_prop_varst::const_iterator cache_result=map_prop_vars.find(literal.c_str()); + if(cache_result!=map_prop_vars.end()) + { + //std::cout << "Cache hit on " << cache_result->first << "\n"; + z3_literal = cache_result->second; + Z3_app app = Z3_to_app(z3_ctx, z3_literal); + Z3_func_decl d = Z3_get_app_decl(z3_ctx, app); + literal = Z3_func_decl_to_string(z3_ctx, d); + + found=literal.find("true"); + + if (found!=std::string::npos) + result=tvt(true); + else + result=tvt(false); + } + + if (a.sign()) result=!result; + + return result; +} diff --git a/src/solvers/z3/z3_prop.h b/src/solvers/z3/z3_prop.h new file mode 100644 index 00000000000..081885ccf43 --- /dev/null +++ b/src/solvers/z3/z3_prop.h @@ -0,0 +1,64 @@ +/*******************************************************************\ + +Module: + +Author: Lucas Cordeiro, lcc08r@ecs.soton.ac.uk + +\*******************************************************************/ + +#ifndef CPROVER_SOLVERS_Z3_PROP_H +#define CPROVER_SOLVERS_Z3_PROP_H + +#include + +#include "z3_capi.h" + +class z3_propt:public propt +{ +public: + z3_propt(); + virtual ~z3_propt(); + + virtual literalt land(literalt a, literalt b); + virtual literalt lor(literalt a, literalt b); + virtual literalt land(const bvt &bv); + virtual literalt lor(const bvt &bv); + virtual literalt lxor(const bvt &bv); + virtual literalt lnot(literalt a); + virtual literalt lxor(literalt a, literalt b); + virtual literalt lnand(literalt a, literalt b); + virtual literalt lnor(literalt a, literalt b); + virtual literalt lequal(literalt a, literalt b); + virtual literalt limplies(literalt a, literalt b); + virtual literalt lselect(literalt a, literalt b, literalt c); // a?b:c + virtual literalt new_variable(); + virtual unsigned no_variables() const { return literal_map.size(); } + virtual void lcnf(const bvt &bv); + + virtual const std::string solver_text() + { return "Z3"; } + + virtual tvt l_get(literalt a) const; + virtual propt::resultt prop_solve(); + + virtual void clear() + { + literal_map.clear(); + } + + void reset_assignment(); + + z3_capi z3_api; + +protected: + struct map_entryt + { + class Z3_ast *ast; + bool value; + }; + + typedef std::vector literal_mapt; + literal_mapt literal_map; +}; + +#endif diff --git a/src/util/Makefile b/src/util/Makefile new file mode 100644 index 00000000000..f0848b64870 --- /dev/null +++ b/src/util/Makefile @@ -0,0 +1,61 @@ +SRC = arith_tools.cpp base_type.cpp cmdline.cpp config.cpp context.cpp \ + expr.cpp expr_util.cpp i2string.cpp irep.cpp language.cpp \ + lispexpr.cpp lispirep.cpp location.cpp message.cpp language_file.cpp \ + mp_arith.cpp namespace.cpp parseoptions.cpp rename.cpp \ + replace_expr.cpp threeval.cpp typecheck.cpp graph.cpp \ + type.cpp cnf_simplify.cpp str_getline.cpp strstream2string.cpp \ + bitvector.cpp parser.cpp map_util.cpp replace_symbol.cpp actuals.cpp \ + get_module.cpp string_hash.cpp string_container.cpp identifier.cpp \ + rational.cpp options.cpp c_misc.cpp simplify_expr.cpp dstring.cpp \ + find_symbols.cpp rational_tools.cpp ui_message.cpp simplify_utils.cpp \ + time_stopping.cpp symbol.cpp \ + type_eq.cpp guard.cpp array_name.cpp gcd.cpp message_stream.cpp \ + substitute.cpp decision_procedure.cpp union_find.cpp pretty_names.cpp \ + xml.cpp xml_irep.cpp std_types.cpp std_code.cpp \ + format_constant.cpp find_macros.cpp ref_expr_set.cpp std_expr.cpp \ + irep_serialization.cpp symbol_serialization.cpp fixedbv.cpp \ + ieee_float.cpp signal_catcher.cpp pointer_offset_size.cpp \ + bv_arithmetic.cpp tempdir.cpp tempfile.cpp timer.cpp \ + irep_ids.cpp + +OBJ = $(SRC:.cpp=$(OBJEXT)) + +INCLUDES= -I .. -I . + +include ../config.inc +include ../common + +all: util$(LIBEXT) ieee_float_test$(EXEEXT) + +ifdef MODULE_FLOATBV + CXXFLAGS += -DHAVE_FLOATBV +endif + +############################################################################### + +irep_ids.h: irep_ids_convert$(EXEEXT) irep_ids.txt + ./irep_ids_convert header < irep_ids.txt > $@ + +irep_ids.inc: irep_ids_convert$(EXEEXT) irep_ids.txt + ./irep_ids_convert table < irep_ids.txt > $@ + +irep_ids.cpp: irep_ids.inc irep_ids.h + +irep_ids_convert$(EXEEXT): irep_ids_convert$(OBJEXT) + $(CXX) $(LINKFLAGS) -o $@ irep_ids_convert$(OBJEXT) + +$(OBJ): irep_ids.h + +util$(LIBEXT): $(OBJ) + $(LINKLIB) + +clean: + rm -f $(OBJ) util$(LIBEXT) + rm -f irep_ids.h irep_ids.inc + rm -f ieee_float_test$(EXEEXT) ieee_float_test$(OBJEXT) + rm -f irep_ids_convert$(EXEEXT) irep_ids_convert$(OBJEXT) + +ieee_float_test$(OBJEXT): irep_ids.h + +ieee_float_test$(EXEEXT): ieee_float_test$(OBJEXT) ieee_float$(OBJEXT) util$(LIBEXT) + $(CXX) $(LINKFLAGS) -o $@ util$(LIBEXT) ieee_float_test$(OBJEXT) ../big-int/bigint$(OBJEXT) diff --git a/src/util/README b/src/util/README new file mode 100644 index 00000000000..7e8ed6d4444 --- /dev/null +++ b/src/util/README @@ -0,0 +1,94 @@ +// +// predefined expressions IDs +// + +Constants + + constant + +Variables/Symbols + + symbol + +Operators for most Types + + typecast + = (equality) + notequal + if + + cond: + operands: alternating sequence of condition, expression + condition is of boolean type + expression of the type of the cond expression + the first match wins + + case: + first operand: comparison element + remaining operands: alternating sequence of expression, expression + first expression is of type of comparison element + second expression of the type of the case expression + + case-unique + as case, but cases are guaranteed to be unique + +Operators for Numbers, Bitvectors + + + + * + / + < + > + <= + >= + mod + unary- + +Boolean Operators + + => + <=> + not + and + or + xor + +Bitwise Operators + + ashr // arithmetic, not cyclic + lshr // not arithmetic, not cyclic + shl // not cyclic + bitnot + bitand + bitor + bitxor + extractbit // two operands, first bv, second integer. result: bool + extractbits // three operands, first bv, second/third integer, result: bv + +Model Checking + + |= + +Software + + Hoare + +Operators for structures + + member // projection which gets + // a member of a structure (operand) + +Pointer Operators + + dereference + address_of + points_to_something + +Misc + + sequent (Gentzen sequent) + + trans // Transition systems: + 0: general constraints, other modules + 1: initial state constraints + 2: transition relation diff --git a/src/util/actuals.cpp b/src/util/actuals.cpp new file mode 100644 index 00000000000..73f2d5d190d --- /dev/null +++ b/src/util/actuals.cpp @@ -0,0 +1,41 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "actuals.h" + +/*******************************************************************\ + +Function: get_actual_map + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void get_actual_map(const exprt &expr, + replace_symbolt &actual_map) +{ + forall_operands(it, expr) + { + if(it->id()!="actual" || + it->operands().size()!=1) + throw "expected actual"; + + const exprt &value=it->op0(); + const irep_idt &identifier=it->get(ID_identifier); + + if(value.id()==ID_type) + actual_map.insert(identifier, value.type()); + else + actual_map.insert(identifier, value); + } +} + diff --git a/src/util/actuals.h b/src/util/actuals.h new file mode 100644 index 00000000000..e6300899375 --- /dev/null +++ b/src/util/actuals.h @@ -0,0 +1,17 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ACTUALS_H +#define CPROVER_ACTUALS_H + +#include "replace_symbol.h" + +void get_actual_map(const exprt &expr, + replace_symbolt &actual_map); + +#endif diff --git a/src/util/arith_tools.cpp b/src/util/arith_tools.cpp new file mode 100644 index 00000000000..966fe0113a3 --- /dev/null +++ b/src/util/arith_tools.cpp @@ -0,0 +1,205 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "arith_tools.h" +#include "bitvector.h" + +/*******************************************************************\ + +Function: to_integer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool to_integer(const exprt &expr, mp_integer &int_value) +{ + if(!expr.is_constant()) return true; + + const std::string &value=expr.get_string(ID_value); + const irep_idt &type_id=expr.type().id(); + + if(type_id==ID_pointer) + { + if(value=="NULL") + { + int_value=0; + return false; + } + } + else if(type_id==ID_integer || + type_id==ID_natural || + type_id==ID_c_enum) + { + int_value=string2integer(value); + return false; + } + else if(type_id==ID_unsignedbv) + { + int_value=binary2integer(value, false); + return false; + } + else if(type_id==ID_signedbv) + { + int_value=binary2integer(value, true); + return false; + } + + return true; +} + +/*******************************************************************\ + +Function: from_integer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt from_integer( + const mp_integer &int_value, + const typet &type) +{ + exprt expr; + + expr.clear(); + expr.type()=type; + expr.id(ID_constant); + + const irep_idt &type_id=type.id(); + + if(type_id==ID_integer) + { + expr.set(ID_value, integer2string(int_value)); + return expr; + } + else if(type_id==ID_natural) + { + if(int_value<0) { expr.make_nil(); return expr; } + expr.set(ID_value, integer2string(int_value)); + return expr; + } + else if(type_id==ID_unsignedbv || type_id==ID_signedbv) + { + expr.set(ID_value, integer2binary(int_value, bv_width(type))); + return expr; + } + else if(type_id==ID_bool) + { + if(int_value==0) + { + expr.make_false(); + return expr; + } + else if(int_value==1) + { + expr.make_true(); + return expr; + } + } + + expr.make_nil(); + return expr; +} + +/*******************************************************************\ + +Function: address_bits + + Inputs: + + Outputs: + + Purpose: ceil(log2(size)) + +\*******************************************************************/ + +mp_integer address_bits(const mp_integer &size) +{ + mp_integer result, x=2; + + for(result=1; x=0); + + if(exponent==0) + return 1; + + mp_integer result=base; + mp_integer count=exponent-1; + + while(count!=0) + { + result*=base; + --count; + } + + return result; +} + +/*******************************************************************\ + +Function: mp_min + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void mp_min(mp_integer &a, const mp_integer &b) +{ + if(ba) a=b; +} diff --git a/src/util/arith_tools.h b/src/util/arith_tools.h new file mode 100644 index 00000000000..81f3231c8ac --- /dev/null +++ b/src/util/arith_tools.h @@ -0,0 +1,26 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_ARITH_TOOLS_H +#define CPROVER_ARITH_TOOLS_H + +#include "expr.h" +#include "mp_arith.h" + +bool to_integer(const exprt &expr, mp_integer &int_value); +exprt from_integer(const mp_integer &int_value, const typet &type); + +// ceil(log2(size)) +mp_integer address_bits(const mp_integer &size); + +mp_integer power(const mp_integer &base, const mp_integer &exponent); + +void mp_min(mp_integer &a, const mp_integer &b); +void mp_max(mp_integer &a, const mp_integer &b); + +#endif diff --git a/src/util/array_name.cpp b/src/util/array_name.cpp new file mode 100644 index 00000000000..3c051d002a3 --- /dev/null +++ b/src/util/array_name.cpp @@ -0,0 +1,46 @@ +/*******************************************************************\ + +Module: Misc Utilities + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "array_name.h" + +/*******************************************************************\ + +Function: goto_checkt::array_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string array_name( + const namespacet &ns, + const exprt &expr) +{ + if(expr.id()==ID_index) + { + if(expr.operands().size()!=2) + throw "index takes two operands"; + + return array_name(ns, expr.op0())+"[]"; + } + else if(expr.id()==ID_symbol) + { + const symbolt &symbol=ns.lookup(expr); + return "array `"+id2string(symbol.base_name)+"'"; + } + else if(expr.id()==ID_string_constant) + { + return "string constant"; + } + + return "array"; +} + diff --git a/src/util/array_name.h b/src/util/array_name.h new file mode 100644 index 00000000000..514cfc8f2ec --- /dev/null +++ b/src/util/array_name.h @@ -0,0 +1,12 @@ +/*******************************************************************\ + +Module: Misc Utilities + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +std::string array_name(const namespacet &ns, const exprt &expr); diff --git a/src/util/base_type.cpp b/src/util/base_type.cpp new file mode 100644 index 00000000000..ecb64689d99 --- /dev/null +++ b/src/util/base_type.cpp @@ -0,0 +1,355 @@ +/*******************************************************************\ + +Module: Base Type Computation + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include "std_types.h" +#include "base_type.h" +#include "union_find.h" + +/*******************************************************************\ + +Function: base_type_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void base_type_rec( + typet &type, const namespacet &ns, std::set &symb) +{ + if(type.id()==ID_symbol) + { + const symbolt *symbol; + + if(!ns.lookup(type.get(ID_identifier), symbol) && + symbol->is_type && + !symbol->type.is_nil()) + { + type=symbol->type; + base_type_rec(type, ns, symb); // recursive call + return; + } + } + //else if(src.id()=="dependent") + // dest=src.find("subtype"); + else if(type.id()==ID_subtype) + { + typet tmp; + tmp.swap(type.subtype()); + type.swap(tmp); + } + else if(type.id()=="predicate") + { + exprt &predicate=static_cast(type.add("predicate")); + base_type_rec(predicate.type(), ns, symb); + assert(predicate.type().id()=="mapping"); + typet tmp; + tmp.swap(predicate.type().subtypes()[0]); + type.swap(tmp); + base_type_rec(type, ns, symb); // recursive call + } + else if(type.id()=="mapping") + { + assert(type.subtypes().size()==2); + base_type_rec(type.subtypes()[0], ns, symb); + base_type_rec(type.subtypes()[1], ns, symb); + } + else if(type.id()==ID_array) + { + base_type_rec(type.subtype(), ns, symb); + } + else if(type.id()==ID_struct || + type.id()==ID_union) + { + irept::subt &components=type.add(ID_components).get_sub(); + + Forall_irep(it, components) + { + typet &subtype=static_cast(it->add(ID_type)); + base_type_rec(subtype, ns, symb); + } + } + else if(type.id()==ID_pointer) + { + typet &subtype=type.subtype(); + + // we need to avoid running into an infinite loop + if(subtype.id()==ID_symbol) + { + const irep_idt &id=to_symbol_type(subtype).get_identifier(); + + if(symb.find(id)!=symb.end()) + return; + + symb.insert(id); + + base_type_rec(subtype, ns, symb); + + symb.erase(id); + } + else + base_type_rec(subtype, ns, symb); + } +} + +/*******************************************************************\ + +Function: base_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void base_type(typet &type, const namespacet &ns) +{ + std::set symb; + base_type_rec(type, ns, symb); +} + +/*******************************************************************\ + +Function: base_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void base_type(exprt &expr, const namespacet &ns) +{ + base_type(expr.type(), ns); + + Forall_operands(it, expr) + base_type(*it, ns); +} + +/*******************************************************************\ + +Function: base_type_eqt::base_type_eq_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool base_type_eqt::base_type_eq_rec( + const typet &type1, + const typet &type2) +{ + if(type1==type2) + return true; + + #if 0 + std::cout << "T1: " << type1.pretty() << std::endl; + std::cout << "T2: " << type2.pretty() << std::endl; + #endif + + // loop avoidance + if(type1.id()==ID_symbol && + type2.id()==ID_symbol) + { + // already in same set? + if(identifiers.make_union( + to_symbol_type(type1).get_identifier(), + to_symbol_type(type2).get_identifier())) + return true; + } + + if(type1.id()==ID_symbol) + { + const symbolt &symbol= + ns.lookup(to_symbol_type(type1).get_identifier()); + + if(!symbol.is_type) + throw "symbol "+id2string(symbol.name)+" is not a type"; + + return base_type_eq_rec(symbol.type, type2); + } + + if(type2.id()==ID_symbol) + { + const symbolt &symbol= + ns.lookup(to_symbol_type(type2).get_identifier()); + + if(!symbol.is_type) + throw "symbol "+id2string(symbol.name)+" is not a type"; + + return base_type_eq_rec(type1, symbol.type); + } + + if(type1.id()!=type2.id()) + return false; + + if(type1.id()==ID_struct || + type1.id()==ID_union) + { + const struct_union_typet::componentst &components1= + to_struct_union_type(type1).components(); + + const struct_union_typet::componentst &components2= + to_struct_union_type(type2).components(); + + if(components1.size()!=components2.size()) + return false; + + for(unsigned i=0; i +#include +#include + +// these go away +void __old_base_type(typet &type, const namespacet &ns); +void __old_base_type(exprt &expr, const namespacet &ns); + +bool base_type_eq( + const typet &type1, + const typet &type2, + const namespacet &ns); + +bool base_type_eq( + const exprt &expr1, + const exprt &expr2, + const namespacet &ns); + +/*******************************************************************\ + + Class: base_type_eqt + + Purpose: + +\*******************************************************************/ + +class base_type_eqt +{ +public: + base_type_eqt(const namespacet &_ns):ns(_ns) + { + } + + bool base_type_eq(const typet &type1, const typet &type2) + { + identifiers.clear(); + return base_type_eq_rec(type1, type2); + } + + bool base_type_eq(const exprt &expr1, const exprt &expr2) + { + identifiers.clear(); + return base_type_eq_rec(expr1, expr2); + } + + virtual ~base_type_eqt() { } + +protected: + const namespacet &ns; + + virtual bool base_type_eq_rec(const typet &type1, const typet &type2); + virtual bool base_type_eq_rec(const exprt &expr1, const exprt &expr2); + + // for loop avoidance + typedef union_find identifierst; + identifierst identifiers; +}; + +#endif diff --git a/src/util/bitvector.cpp b/src/util/bitvector.cpp new file mode 100644 index 00000000000..39d6aaad285 --- /dev/null +++ b/src/util/bitvector.cpp @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "bitvector.h" + +/*******************************************************************\ + +Function: bv_sem + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bv_semt bv_sem(const typet &type) +{ + if(type.id()==ID_bv) + return BV_NONE; + else if(type.id()==ID_unsignedbv) + return BV_UNSIGNED; + else if(type.id()==ID_signedbv) + return BV_SIGNED; + + return BV_UNKNOWN; +} + +/*******************************************************************\ + +Function: bv_width + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned bv_width(const typet &type) +{ + return atoi(type.get(ID_width).c_str()); +} + + diff --git a/src/util/bitvector.h b/src/util/bitvector.h new file mode 100644 index 00000000000..867ba21f86f --- /dev/null +++ b/src/util/bitvector.h @@ -0,0 +1,9 @@ +#include + +typedef enum { BV_UNKNOWN, BV_NONE, BV_SIGNED, BV_UNSIGNED } bv_semt; + +bv_semt bv_sem(const typet &type); + +// depreciated, and will disappear +unsigned bv_width(const typet &type); + diff --git a/src/util/bv_arithmetic.cpp b/src/util/bv_arithmetic.cpp new file mode 100644 index 00000000000..47ad86198f0 --- /dev/null +++ b/src/util/bv_arithmetic.cpp @@ -0,0 +1,476 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include +#include +#include + +#include "bv_arithmetic.h" + +/*******************************************************************\ + +Function: bv_spect::to_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +typet bv_spect::to_type() const +{ + if(is_signed) return signedbv_typet(width); + return unsignedbv_typet(width); +} + +/*******************************************************************\ + +Function: bv_spect::max_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer bv_spect::max_value() const +{ + return is_signed?power(2, width-1)-1: + power(2, width)-1; +} + +/*******************************************************************\ + +Function: bv_spect::min_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer bv_spect::min_value() const +{ + return is_signed?-power(2, width-1): + 0; +} + +/*******************************************************************\ + +Function: bv_spect::from_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_spect::from_type(const typet &type) +{ + if(type.id()==ID_unsignedbv) + is_signed=false; + else if(type.id()==ID_signedbv) + is_signed=true; + else + assert(0); + + width=atoi(type.get(ID_width).c_str()); +} + +/*******************************************************************\ + +Function: bv_arithmetict::print + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_arithmetict::print(std::ostream &out) const +{ + out << to_ansi_c_string(); +} + +/*******************************************************************\ + +Function: bv_arithmetict::format + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string bv_arithmetict::format(const format_spect &format_spec) const +{ + std::string result; + + result=integer2string(value); + + return result; +} + +/*******************************************************************\ + +Function: bv_arithmetict::from_integer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_arithmetict::from_integer(const mp_integer &i) +{ + value=i; + adjust(); +} + +/*******************************************************************\ + +Function: bv_arithmetict::adjust + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_arithmetict::adjust() +{ + mp_integer p=power(2, spec.width); + value%=p; + + if(value>=p/2) + value-=p; +} + +/*******************************************************************\ + +Function: bv_arithmetict::pack + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer bv_arithmetict::pack() const +{ + if(value>=0) return value; + return value+power(2, spec.width); +} + +/*******************************************************************\ + +Function: bv_arithmetict::to_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt bv_arithmetict::to_expr() const +{ + constant_exprt result(spec.to_type()); + result.set_value(integer2binary(value, spec.width)); + return result; +} + +/*******************************************************************\ + +Function: operator /= + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bv_arithmetict &bv_arithmetict::operator /= (const bv_arithmetict &other) +{ + assert(other.spec==spec); + + if(other.value==0) + value=0; + else + value/=other.value; + + return *this; +} + +/*******************************************************************\ + +Function: operator *= + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bv_arithmetict &bv_arithmetict::operator *= (const bv_arithmetict &other) +{ + assert(other.spec==spec); + + value*=other.value; + adjust(); + + return *this; +} + +/*******************************************************************\ + +Function: operator += + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bv_arithmetict &bv_arithmetict::operator += (const bv_arithmetict &other) +{ + assert(other.spec==spec); + + value+=other.value; + adjust(); + + return *this; +} + +/*******************************************************************\ + +Function: operator -= + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bv_arithmetict &bv_arithmetict::operator -= (const bv_arithmetict &other) +{ + assert(other.spec==spec); + + value-=other.value; + adjust(); + + return *this; +} + +/*******************************************************************\ + +Function: operator %= + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bv_arithmetict &bv_arithmetict::operator %= (const bv_arithmetict &other) +{ + assert(other.spec==spec); + + value%=other.value; + adjust(); + + return *this; +} + +/*******************************************************************\ + +Function: operator < + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator < (const bv_arithmetict &a, const bv_arithmetict &b) +{ + return a.value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator > (const bv_arithmetict &a, const bv_arithmetict &b) +{ + return a.value>b.value; +} + +/*******************************************************************\ + +Function: operator >= + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator >=(const bv_arithmetict &a, const bv_arithmetict &b) +{ + return a.value>=b.value; +} + +/*******************************************************************\ + +Function: operator == + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator ==(const bv_arithmetict &a, const bv_arithmetict &b) +{ + return a.value==b.value; +} + +/*******************************************************************\ + +Function: operator == + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator ==(const bv_arithmetict &a, int i) +{ + return a.value==i; +} + +/*******************************************************************\ + +Function: operator != + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator !=(const bv_arithmetict &a, const bv_arithmetict &b) +{ + return a.value!=b.value; +} + +/*******************************************************************\ + +Function: bv_arithmetict::change_spec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_arithmetict::change_spec(const bv_spect &dest_spec) +{ + spec=dest_spec; + adjust(); +} + +/*******************************************************************\ + +Function: bv_arithmetict::from_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void bv_arithmetict::from_expr(const exprt &expr) +{ + assert(expr.is_constant()); + spec=expr.type(); + value=binary2integer(expr.get_string(ID_value), spec.is_signed); +} diff --git a/src/util/bv_arithmetic.h b/src/util/bv_arithmetic.h new file mode 100644 index 00000000000..bb7e2d3046f --- /dev/null +++ b/src/util/bv_arithmetic.h @@ -0,0 +1,128 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_BV_ARITHMETIC_H +#define CPROVER_BV_ARITHMETIC_H + +#include + +#include +#include +#include + +class bv_spect +{ +public: + unsigned width; + bool is_signed; + + bv_spect(const typet &type) + { + from_type(type); + } + + void from_type(const class typet &type); + + bv_spect():width(0), is_signed(false) + { + } + + mp_integer max_value() const; + mp_integer min_value() const; + + typet to_type() const; +}; + +class bv_arithmetict +{ +public: + bv_spect spec; + + bv_arithmetict(const bv_spect &_spec): + spec(_spec), value(0) + { + } + + bv_arithmetict():value(0) + { + } + + bv_arithmetict(const exprt &expr) + { + from_expr(expr); + } + + void negate(); + + void make_zero() + { + value=0; + } + + bool is_zero() const { return value==0; } + + // performs conversion to bit-vector format + void from_integer(const mp_integer &i); + + // performs conversion from ieee floating point format + void change_spec(const bv_spect &dest_spec); + mp_integer to_integer() const { return value; } + + void print(std::ostream &out) const; + + std::string to_ansi_c_string() const + { + return format(format_spect()); + } + + std::string format(const format_spect &format_spec) const; + + // expressions + exprt to_expr() const; + void from_expr(const exprt &expr); + + bv_arithmetict &operator /= (const bv_arithmetict &other); + bv_arithmetict &operator *= (const bv_arithmetict &other); + bv_arithmetict &operator += (const bv_arithmetict &other); + bv_arithmetict &operator -= (const bv_arithmetict &other); + bv_arithmetict &operator %= (const bv_arithmetict &other); + + friend bool operator < (const bv_arithmetict &a, const bv_arithmetict &b); + friend bool operator <=(const bv_arithmetict &a, const bv_arithmetict &b); + friend bool operator > (const bv_arithmetict &a, const bv_arithmetict &b); + friend bool operator >=(const bv_arithmetict &a, const bv_arithmetict &b); + friend bool operator ==(const bv_arithmetict &a, const bv_arithmetict &b); + friend bool operator !=(const bv_arithmetict &a, const bv_arithmetict &b); + friend bool operator ==(const bv_arithmetict &a, int i); + + friend std::ostream& operator << (std::ostream &out, const bv_arithmetict &f) + { + return out << f.to_ansi_c_string(); + } + + // turn into natural number representation + void unpack(const mp_integer &i) { value=i; adjust(); } + mp_integer pack() const; + +protected: + // we store negative numbers as such + mp_integer value; + + // puts the value back into its range + void adjust(); +}; + +bool operator < (const bv_arithmetict &a, const bv_arithmetict &b); +bool operator <=(const bv_arithmetict &a, const bv_arithmetict &b); +bool operator > (const bv_arithmetict &a, const bv_arithmetict &b); +bool operator >=(const bv_arithmetict &a, const bv_arithmetict &b); +bool operator ==(const bv_arithmetict &a, const bv_arithmetict &b); +bool operator !=(const bv_arithmetict &a, const bv_arithmetict &b); +std::ostream& operator << (std::ostream &, const bv_arithmetict &); + +#endif diff --git a/src/util/c_misc.cpp b/src/util/c_misc.cpp new file mode 100644 index 00000000000..0b026eabc28 --- /dev/null +++ b/src/util/c_misc.cpp @@ -0,0 +1,110 @@ +/*******************************************************************\ + +Module: ANSI-C Misc Utilities + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +/*******************************************************************\ + +Function: MetaChar + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void MetaChar(std::string &out, char c, bool inString) +{ + switch (c) + { + case '\'': + if (inString) + out+="'"; + else + out+="\\'"; + break; + + case '"': + if (inString) + out+="\\\""; + else + out+="\""; + break; + + case '\0': + out+="\\0"; + break; + + case '\\': + out+="\\\\"; + break; + + case '\n': + out+="\\n"; + break; + + case '\t': + out+="\\t"; + break; + + case '\r': + out+="\\r"; + break; + + case '\f': + out+="\\f"; + break; + + case '\b': + out+="\\b"; + break; + + case '\v': + out+="\\v"; + break; + + case '\a': + out+="\\a"; + break; + + default: + // Show low and high ascii as octal + if ((c < ' ') || (c >= 127)) + { + char octbuf[8]; + sprintf(octbuf, "%03o", (unsigned char) c); + out+="\\"; + out+=octbuf; + } + else + out+=c; + break; + } +} + +/*******************************************************************\ + +Function: MetaString + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void MetaString(std::string &out, const std::string &in) +{ + for(unsigned i=0; i + +void MetaChar(std::string &out, char c, bool inString); +void MetaString(std::string &out, const std::string &in); + +#endif diff --git a/src/util/cmdline.cpp b/src/util/cmdline.cpp new file mode 100644 index 00000000000..6eb838a2560 --- /dev/null +++ b/src/util/cmdline.cpp @@ -0,0 +1,354 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include "cmdline.h" + +/*******************************************************************\ + +Function: cmdlinet::cmdlinet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cmdlinet::cmdlinet() +{ +} + +/*******************************************************************\ + +Function: cmdlinet::~cmdlinet + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +cmdlinet::~cmdlinet() +{ + clear(); +} + +/*******************************************************************\ + +Function: cmdlinet::clear + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cmdlinet::clear() +{ + options.clear(); + args.clear(); +} + +/*******************************************************************\ + +Function: cmdlinet::isset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cmdlinet::isset(char option) const +{ + int i=getoptnr(option); + if(i<0) return false; + return options[i].isset; +} + +/*******************************************************************\ + +Function: cmdlinet::isset + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool cmdlinet::isset(const char *option) const +{ + int i=getoptnr(option); + if(i<0) return false; + return options[i].isset; +} + +/*******************************************************************\ + +Function: cmdlinet::getval + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const char *cmdlinet::getval(char option) const +{ + int i=getoptnr(option); + if(i<0) return ""; + if(options[i].values.empty()) return ""; + return options[i].values.front().c_str(); +} + +/*******************************************************************\ + +Function: cmdlinet::set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cmdlinet::set(const std::string &option) +{ + int i=getoptnr(option); + if(i<0) return; // ignore + options[i].isset=true; +} + +/*******************************************************************\ + +Function: cmdlinet::set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cmdlinet::set(const std::string &option, const std::string &value) +{ + int i=getoptnr(option); + if(i<0) return; // ignore + options[i].isset=true; + options[i].values.push_back(value); +} + +/*******************************************************************\ + +Function: cmdlinet::get_values + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::list &cmdlinet::get_values(char option) const +{ + int i=getoptnr(option); + assert(i>=0); + return options[i].values; +} + +/*******************************************************************\ + +Function: cmdlinet::getval + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const char *cmdlinet::getval(const char *option) const +{ + int i=getoptnr(option); + if(i<0) return ""; + if(options[i].values.empty()) return ""; + return options[i].values.front().c_str(); +} + +/*******************************************************************\ + +Function: cmdlinet::get_values + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::list& cmdlinet::get_values(const std::string &option) const +{ + int i=getoptnr(option); + assert(i>=0); + return options[i].values; +} + +/*******************************************************************\ + +Function: cmdlinet::getoptnr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int cmdlinet::getoptnr(char option) const +{ + for(unsigned i=0; i +#include +#include + +class cmdlinet +{ +public: + virtual bool parse(int argc, const char **argv, const char *optstring); + + const char *getval(char option) const; + const char *getval(const char *option) const; + const std::list &get_values(const std::string &option) const; + const std::list &get_values(char option) const; + + virtual bool isset(char option) const; + virtual bool isset(const char *option) const; + virtual void set(const std::string &option); + virtual void set(const std::string &option, const std::string &value); + virtual void clear(); + + typedef std::vector argst; + argst args; + + cmdlinet(); + virtual ~cmdlinet(); + +protected: + struct optiont + { + bool isset, hasval, islong; + char optchar; + std::string optstring; + std::list values; + }; + + std::vector options; + + int getoptnr(char option) const; + int getoptnr(const std::string &option) const; +}; + +#endif diff --git a/src/util/cnf_simplify.cpp b/src/util/cnf_simplify.cpp new file mode 100644 index 00000000000..79e033a2a14 --- /dev/null +++ b/src/util/cnf_simplify.cpp @@ -0,0 +1,246 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "cnf_simplify.h" + +void cnf_propagate_not(exprt &expr); +void cnf_join_binary(exprt &expr); +void propagate_not(exprt &expr); + +/*******************************************************************\ + +Function: cnf_simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnf_simplify(exprt &expr) +{ + cnf_propagate_not(expr); + cnf_join_binary(expr); +} + +#if 0 +/*******************************************************************\ + +Function: + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnf_join_binary(exprt &expr) +{ + Forall_operands(it, expr) + cnf_join_binary(*it); + + if(expr.id()==ID_and || expr.id()==ID_or || expr.id()==ID_xor || + expr.id()==ID_bitand || expr.id()==ID_bitor || expr.id()==ID_bitxor) + { + exprt tmp; + + if(expr.operands().size()==1) + { + tmp.swap(expr.op0()); + expr.swap(tmp); + } + else + { + unsigned count=0; + + forall_operands(it, expr) + { + if(it->id()==expr.id()) + count+=it->operands().size(); + else + count++; + } + + tmp.operands().reserve(count); + + Forall_operands(it, expr) + { + if(it->id()==expr.id()) + { + Forall_operands(it2, *it) + tmp.move_to_operands(*it2); + } + else + tmp.move_to_operands(*it); + } + + expr.operands().swap(tmp.operands()); + } + } +} +#endif + +/*******************************************************************\ + +Function: cnf_join_binary_collect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnf_join_binary_collect(exprt &expr, exprt::operandst &list) +{ + Forall_operands(it, expr) + { + if(it->id()==expr.id() && it->type()==expr.type()) + cnf_join_binary_collect(*it, list); + else + { + list.resize(list.size()+1); + list.back().swap(*it); + } + } +} + +/*******************************************************************\ + +Function: cnf_join_binary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnf_join_binary(exprt &expr) +{ + if(expr.id()==ID_and || expr.id()==ID_or || expr.id()==ID_xor || + expr.id()==ID_bitand || expr.id()==ID_bitor || expr.id()==ID_bitxor) + { + exprt::operandst list; + + cnf_join_binary_collect(expr, list); + + if(list.size()==1) + expr.swap(list.front()); + else + expr.operands().swap(list); + } + + Forall_operands(it, expr) + cnf_join_binary(*it); +} + +/*******************************************************************\ + +Function: cnf_propagate_not + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void cnf_propagate_not(exprt &expr) +{ + if(expr.id()==ID_not) + { + if(expr.operands().size()==1) + { + exprt tmp; + + tmp.swap(expr.op0()); + propagate_not(tmp); + expr.swap(tmp); + } + } + else if(expr.id()==ID_nor) + { + expr.id(ID_or); + propagate_not(expr); + } + else if(expr.id()==ID_nand) + { + expr.id(ID_and); + propagate_not(expr); + } + else if(expr.id()==ID_implies) + { + if(expr.operands().size()==2) + { + expr.id(ID_or); + propagate_not(expr.op0()); + } + } + + Forall_operands(it, expr) + cnf_propagate_not(*it); +} + +/*******************************************************************\ + +Function: propagate_not + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void propagate_not(exprt &expr) +{ + if(expr.id()==ID_and || expr.id()==ID_or) + { + if(expr.id()==ID_and) + expr.id(ID_or); + else // or + expr.id(ID_and); + + Forall_operands(it, expr) + propagate_not(*it); + } + else if(expr.id()==ID_nor) + expr.id(ID_or); + else if(expr.id()==ID_nand) + expr.id(ID_and); + else if(expr.id()==ID_not) + { + assert(expr.operands().size()==1); + exprt tmp; + tmp.swap(expr.op0()); + expr.swap(tmp); + } + else if(expr.id()==ID_equal) + expr.id(ID_notequal); + else if(expr.id()==ID_notequal) + expr.id(ID_equal); + else + { + exprt tmp; + expr.swap(tmp); + expr.id(ID_not); + expr.type()=tmp.type(); + expr.move_to_operands(tmp); + } +} diff --git a/src/util/cnf_simplify.h b/src/util/cnf_simplify.h new file mode 100644 index 00000000000..af95f0239b0 --- /dev/null +++ b/src/util/cnf_simplify.h @@ -0,0 +1,3 @@ +#include "expr.h" + +void cnf_simplify(exprt &expr); diff --git a/src/util/collection.h b/src/util/collection.h new file mode 100644 index 00000000000..f0dd991549b --- /dev/null +++ b/src/util/collection.h @@ -0,0 +1,191 @@ +/*******************************************************************\ + +Module: String Storage + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_STRING_COLLECTION_H +#define CPROVER_STRING_COLLECTION_H + +#include "string_hash.h" +#include "hash_cont.h" + +template +class collection +{ +public: + typedef T valuet; + + typedef hash_map_cont member_mapt; + typedef std::vector member_vectort; + + typedef typename member_mapt::const_iterator const_iterator; + + const_iterator begin() const + { + return member_map.begin(); + } + + const_iterator end() const + { + return member_map.end(); + } + + class membert + { + public: + membert() + { + } + + friend bool operator < (const membert &a, const membert &b) + { + return a.nr result= + member_map.insert(std::pair(x, member_vector.size())); + + if(result.second) + member_vector.push_back(x); + + return membert(result.first->second); + } + + const T &operator [] (const membert m) const + { + return member_vector[m.nr]; + } + + const membert operator [] (const T &x) + { + return member(x); + } + + void clear() + { + member_map.clear(); + member_vector.clear(); + } + +protected: + member_mapt member_map; + member_vectort member_vector; +}; + +template +class collection_set +{ +public: + typedef T collectiont; + + collection_set():collection(NULL) + { + } + + void insert(unsigned nr) + { + if(members.size() memberst; + memberst members; + + void set_collection(collectiont &_collection) + { + if(collection==NULL) + collection=&_collection; + else + assert(collection==&_collection); + } + + collectiont *collection; +}; + +typedef collection_set > string_collection_set; + +#endif diff --git a/src/util/config.cpp b/src/util/config.cpp new file mode 100644 index 00000000000..567a3167e82 --- /dev/null +++ b/src/util/config.cpp @@ -0,0 +1,459 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "namespace.h" +#include "config.h" +#include "context.h" +#include "arith_tools.h" + +configt config; + +/*******************************************************************\ + +Function: configt::ansi_ct::set_16 + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void configt::ansi_ct::set_16() +{ + set_LP32(); +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_32 + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void configt::ansi_ct::set_32() +{ + set_ILP32(); +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_64 + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void configt::ansi_ct::set_64() +{ + set_LP64(); +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_LP64 + + Inputs: + + Outputs: + + Purpose: int=32, long=64, pointer=64 + +\*******************************************************************/ + +void configt::ansi_ct::set_LP64() +{ + int_width=4*8; + long_int_width=8*8; + char_width=1*8; + short_int_width=2*8; + long_long_int_width=8*8; + pointer_width=8*8; + single_width=4*8; + double_width=8*8; + long_double_width=8*8; + char_is_unsigned=false; + wchar_t_width=4*8; + alignment=1; +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_ILP64 + + Inputs: + + Outputs: + + Purpose: int=64, long=64, pointer=64 + +\*******************************************************************/ + +void configt::ansi_ct::set_ILP64() +{ + int_width=8*8; + long_int_width=8*8; + char_width=1*8; + short_int_width=2*8; + long_long_int_width=8*8; + pointer_width=8*8; + single_width=4*8; + double_width=8*8; + long_double_width=8*8; + char_is_unsigned=false; + wchar_t_width=4*8; + alignment=1; +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_LLP64 + + Inputs: + + Outputs: + + Purpose: int=32, long=32, pointer=64 + +\*******************************************************************/ + +void configt::ansi_ct::set_LLP64() +{ + int_width=4*8; + long_int_width=4*8; + char_width=1*8; + short_int_width=2*8; + long_long_int_width=8*8; + pointer_width=8*8; + single_width=4*8; + double_width=8*8; + long_double_width=8*8; + char_is_unsigned=false; + wchar_t_width=4*8; + alignment=1; +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_ILP32 + + Inputs: + + Outputs: + + Purpose: int=32, long=32, pointer=32 + +\*******************************************************************/ + +void configt::ansi_ct::set_ILP32() +{ + int_width=4*8; + long_int_width=4*8; + char_width=1*8; + short_int_width=2*8; + long_long_int_width=8*8; + pointer_width=4*8; + single_width=4*8; + double_width=8*8; + long_double_width=8*8; + char_is_unsigned=false; + wchar_t_width=4*8; + alignment=1; +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_LP32 + + Inputs: + + Outputs: + + Purpose: int=16, long=32, pointer=32 + +\*******************************************************************/ + +void configt::ansi_ct::set_LP32() +{ + int_width=2*8; + long_int_width=4*8; + char_width=1*8; + short_int_width=2*8; + long_long_int_width=8*8; + pointer_width=4*8; + single_width=4*8; + double_width=8*8; + long_double_width=8*8; + char_is_unsigned=false; + wchar_t_width=4*8; + alignment=1; +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool configt::set(const cmdlinet &cmdline) +{ + // defaults -- we match the architecture we have ourselves + + if(sizeof(long int)==8) + ansi_c.set_64(); + else + ansi_c.set_32(); + + #ifdef HAVE_FLOATBV + ansi_c.use_fixed_for_float=false; + #else + ansi_c.use_fixed_for_float=true; + #endif + + ansi_c.endianess=ansi_ct::NO_ENDIANESS; + ansi_c.os=ansi_ct::NO_OS; + ansi_c.arch=ansi_ct::NO_ARCH; + ansi_c.lib=configt::ansi_ct::LIB_NONE; + ansi_c.rounding_mode=ieee_floatt::ROUND_TO_EVEN; + + #ifdef _WIN32 + ansi_c.mode=ansi_ct::MODE_VISUAL_STUDIO; + #else + ansi_c.mode=ansi_ct::MODE_GCC; + #endif + + if(cmdline.isset("16")) + ansi_c.set_16(); + + if(cmdline.isset("32")) + ansi_c.set_32(); + + if(cmdline.isset("64")) + ansi_c.set_64(); + + if(cmdline.isset("LP64")) + ansi_c.set_LP64(); // int=32, long=64, pointer=64 + + if(cmdline.isset("ILP64")) + ansi_c.set_ILP64(); // int=64, long=64, pointer=64 + + if(cmdline.isset("LLP64")) + ansi_c.set_LLP64(); // int=32, long=32, pointer=64 + + if(cmdline.isset("ILP32")) + ansi_c.set_ILP32(); // int=32, long=32, pointer=32 + + if(cmdline.isset("LP32")) + ansi_c.set_LP32(); // int=16, long=32, pointer=32 + + if(cmdline.isset("function")) + main=cmdline.getval("function"); + + if(cmdline.isset('D')) + ansi_c.defines=cmdline.get_values('D'); + + if(cmdline.isset('I')) + ansi_c.include_paths=cmdline.get_values('I'); + + if(cmdline.isset("include")) + ansi_c.include_files=cmdline.get_values("include"); + + if(cmdline.isset("floatbv")) + ansi_c.use_fixed_for_float=false; + + if(cmdline.isset("fixedbv")) + ansi_c.use_fixed_for_float=true; + + if(cmdline.isset("i386-linux")) + { + ansi_c.mode=ansi_ct::MODE_GCC; + ansi_c.os=configt::ansi_ct::OS_LINUX; + ansi_c.arch=configt::ansi_ct::ARCH_I386; + ansi_c.endianess=configt::ansi_ct::IS_LITTLE_ENDIAN; + ansi_c.lib=configt::ansi_ct::LIB_FULL; + } + + if(cmdline.isset("i386-win32") || + cmdline.isset("win32")) + { + ansi_c.mode=ansi_ct::MODE_VISUAL_STUDIO; + ansi_c.os=configt::ansi_ct::OS_WIN; + ansi_c.arch=configt::ansi_ct::ARCH_I386; + ansi_c.endianess=configt::ansi_ct::IS_LITTLE_ENDIAN; + ansi_c.lib=configt::ansi_ct::LIB_FULL; + ansi_c.set_32(); + ansi_c.wchar_t_width=2*8; + } + + if(cmdline.isset("winx64")) + { + ansi_c.mode=ansi_ct::MODE_VISUAL_STUDIO; + ansi_c.os=configt::ansi_ct::OS_WIN; + ansi_c.arch=configt::ansi_ct::ARCH_X86_64; + ansi_c.endianess=configt::ansi_ct::IS_LITTLE_ENDIAN; + ansi_c.lib=configt::ansi_ct::LIB_FULL; + ansi_c.set_64(); + ansi_c.wchar_t_width=2*8; + } + + if(cmdline.isset("i386-macos")) + { + ansi_c.mode=ansi_ct::MODE_GCC; + ansi_c.os=configt::ansi_ct::OS_MACOS; + ansi_c.arch=configt::ansi_ct::ARCH_I386; + ansi_c.endianess=configt::ansi_ct::IS_LITTLE_ENDIAN; + ansi_c.lib=configt::ansi_ct::LIB_FULL; + } + + if(cmdline.isset("ppc-macos")) + { + ansi_c.mode=ansi_ct::MODE_GCC; + ansi_c.os=configt::ansi_ct::OS_MACOS; + ansi_c.arch=configt::ansi_ct::ARCH_PPC; + ansi_c.endianess=configt::ansi_ct::IS_BIG_ENDIAN; + ansi_c.lib=configt::ansi_ct::LIB_FULL; + } + + if(cmdline.isset("no-arch")) + { + ansi_c.os=configt::ansi_ct::NO_OS; + ansi_c.arch=configt::ansi_ct::NO_ARCH; + ansi_c.endianess=configt::ansi_ct::NO_ENDIANESS; + ansi_c.lib=configt::ansi_ct::LIB_NONE; + } + else if(ansi_c.os==configt::ansi_ct::NO_OS) + { + // this is the default + ansi_c.os=configt::ansi_ct::OS_LINUX; + ansi_c.arch=configt::ansi_ct::ARCH_I386; + ansi_c.endianess=configt::ansi_ct::IS_LITTLE_ENDIAN; + ansi_c.lib=configt::ansi_ct::LIB_FULL; + #ifdef _WIN32 + ansi_c.os=configt::ansi_ct::OS_WIN; + ansi_c.wchar_t_width=2*8; + #elif __APPLE__ + ansi_c.os=configt::ansi_ct::OS_MACOS; + #elif __x86_64__ + ansi_c.arch=configt::ansi_ct::ARCH_X86_64; + #endif + } + + if(cmdline.isset("string-abstraction")) + ansi_c.string_abstraction=true; + else + ansi_c.string_abstraction=false; + + if(cmdline.isset("no-library")) + ansi_c.lib=configt::ansi_ct::LIB_NONE; + + if(cmdline.isset("little-endian")) + ansi_c.endianess=configt::ansi_ct::IS_LITTLE_ENDIAN; + + if(cmdline.isset("big-endian")) + ansi_c.endianess=configt::ansi_ct::IS_BIG_ENDIAN; + + if(cmdline.isset("little-endian") && + cmdline.isset("big-endian")) + return true; + + if(cmdline.isset("unsigned-char")) + ansi_c.char_is_unsigned=true; + + if(cmdline.isset("round-to-even") || + cmdline.isset("round-to-nearest")) + ansi_c.rounding_mode=ieee_floatt::ROUND_TO_EVEN; + + if(cmdline.isset("round-to-plus-inf")) + ansi_c.rounding_mode=ieee_floatt::ROUND_TO_PLUS_INF; + + if(cmdline.isset("round-to-minus-inf")) + ansi_c.rounding_mode=ieee_floatt::ROUND_TO_MINUS_INF; + + if(cmdline.isset("round-to-zero")) + ansi_c.rounding_mode=ieee_floatt::ROUND_TO_ZERO; + + return false; +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_from_context + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +int configt::ansi_ct::from_ns(const namespacet &ns, const std::string &what) +{ + const irep_idt id="c::__CPROVER_architecture_"+what; + const symbolt *symbol; + + if(ns.lookup(id, symbol)) + throw "failed to find "+id2string(id); + + mp_integer int_value; + + if(to_integer(symbol->value, int_value)) + throw "failed to convert "+id2string(id); + + return integer2long(int_value); +} + +/*******************************************************************\ + +Function: configt::ansi_ct::set_from_context + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void configt::ansi_ct::set_from_context(const contextt &context) +{ + namespacet ns(context); + + int_width=from_ns(ns, "int_width"); + long_int_width=from_ns(ns, "long_int_width"); + long_int_width=from_ns(ns, "long_int_width"); + char_width=from_ns(ns, "char_width"); + short_int_width=from_ns(ns, "short_int_width"); + long_long_int_width=from_ns(ns, "long_long_int_width"); + pointer_width=from_ns(ns, "pointer_width"); + single_width=from_ns(ns, "single_width"); + double_width=from_ns(ns, "double_width"); + long_double_width=from_ns(ns, "long_double_width"); + char_is_unsigned=from_ns(ns, "char_is_unsigned"); + wchar_t_width=from_ns(ns, "wchar_t_width"); + alignment=from_ns(ns, "alignment"); + use_fixed_for_float=from_ns(ns, "fixed_for_float"); + endianess=(endianesst)from_ns(ns, "endianess"); +} + diff --git a/src/util/config.h b/src/util/config.h new file mode 100644 index 00000000000..4ec883830aa --- /dev/null +++ b/src/util/config.h @@ -0,0 +1,93 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_UTIL_CONFIG_H +#define CPROVER_UTIL_CONFIG_H + +#include "cmdline.h" +#include "ieee_float.h" + +class configt +{ +public: + struct ansi_ct + { + // for ANSI-C + unsigned int_width; + unsigned long_int_width; + unsigned char_width; + unsigned short_int_width; + unsigned long_long_int_width; + unsigned pointer_width; + unsigned single_width; + unsigned double_width; + unsigned long_double_width; + unsigned wchar_t_width; + + bool char_is_unsigned; + bool use_fixed_for_float; + + ieee_floatt::rounding_modet rounding_mode; + + void set_16(); + void set_32(); + void set_64(); + + // http://www.unix.org/version2/whatsnew/lp64_wp.html + void set_LP64(); // int=32, long=64, pointer=64 + void set_ILP64(); // int=64, long=64, pointer=64 + void set_LLP64(); // int=32, long=32, pointer=64 + void set_ILP32(); // int=32, long=32, pointer=32 + void set_LP32(); // int=16, long=32, pointer=32 + + void set_from_context(const class contextt &context); + + // minimum alignment (in structs) measured in bytes + unsigned alignment; + + typedef enum { NO_ENDIANESS, IS_LITTLE_ENDIAN, IS_BIG_ENDIAN } endianesst; + endianesst endianess; + + typedef enum { NO_OS, OS_LINUX, OS_MACOS, OS_WIN } ost; + ost os; + + typedef enum { NO_ARCH, ARCH_I386, ARCH_PPC, ARCH_X86_64 } archt; + archt arch; + + typedef enum { NO_MODE, MODE_ANSI, MODE_GCC, MODE_VISUAL_STUDIO, + MODE_CODEWARRIOR, MODE_ARM } modet; + modet mode; + + std::list defines; + std::list undefines; + std::list preprocessor_options; + std::list include_paths; + std::list include_files; + + typedef enum { LIB_NONE, LIB_FULL } libt; + libt lib; + bool string_abstraction; + + protected: + int from_ns(const class namespacet &ns, const std::string &what); + } ansi_c; + + struct verilogt + { + std::list include_paths; + } verilog; + + // this is the function to start executing + std::string main; + + bool set(const cmdlinet &cmdline); +}; + +extern configt config; + +#endif diff --git a/src/util/context.cpp b/src/util/context.cpp new file mode 100644 index 00000000000..507911587e0 --- /dev/null +++ b/src/util/context.cpp @@ -0,0 +1,213 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "context.h" + +/*******************************************************************\ + +Function: contextt::value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const irept &contextt::value(const irep_idt &name) const +{ + symbolst::const_iterator it=symbols.find(name); + if(it==symbols.end()) return get_nil_irep(); + return it->second.value; +} + +/*******************************************************************\ + +Function: contextt::add + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool contextt::add(const symbolt &symbol) +{ + if(!symbols.insert(std::pair(symbol.name, symbol)).second) + return true; + + symbol_base_map.insert(std::pair(symbol.base_name, symbol.name)); + symbol_module_map.insert(std::pair(symbol.module, symbol.name)); + + return false; +} + +/*******************************************************************\ + +Function: contextt::move + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool contextt::move(symbolt &symbol, symbolt *&new_symbol) +{ + symbolt tmp; + + std::pair result= + symbols.insert(std::pair(symbol.name, tmp)); + + if(!result.second) + { + new_symbol=&result.first->second; + return true; + } + + symbol_base_map.insert(std::pair(symbol.base_name, symbol.name)); + symbol_module_map.insert(std::pair(symbol.module, symbol.name)); + + result.first->second.swap(symbol); + new_symbol=&result.first->second; + + return false; +} + +/*******************************************************************\ + +Function: contextt::remove + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool contextt::remove(const irep_idt &name) +{ + symbolst::iterator entry=symbols.find(name); + + if(entry==symbols.end()) + return true; + + for(symbol_base_mapt::iterator + it=symbol_base_map.lower_bound(entry->second.base_name), + it_end=symbol_base_map.upper_bound(entry->second.base_name); + it!=it_end; + ++it) + if(it->second==name) + { + symbol_base_map.erase(it); + break; + } + + for(symbol_module_mapt::iterator + it=symbol_module_map.lower_bound(entry->second.module), + it_end=symbol_module_map.upper_bound(entry->second.module); + it!=it_end; + ++it) + if(it->second==name) + { + symbol_module_map.erase(it); + break; + } + + symbols.erase(entry); + + return false; +} + +/*******************************************************************\ + +Function: contextt::show + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void contextt::show(std::ostream &out) const +{ + out << std::endl << "Symbols:" << std::endl; + + forall_symbols(it, symbols) + out << it->second; +} + +/*******************************************************************\ + +Function: contextt::lookup + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const symbolt &contextt::lookup(const irep_idt &identifier) const +{ + symbolst::const_iterator it=symbols.find(identifier); + + if(it==symbols.end()) + throw "symbol "+id2string(identifier)+" not found"; + + return it->second; +} + +/*******************************************************************\ + +Function: contextt::lookup + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +symbolt &contextt::lookup(const irep_idt &identifier) +{ + symbolst::iterator it=symbols.find(identifier); + + if(it==symbols.end()) + throw "symbol "+id2string(identifier)+" not found"; + + return it->second; +} + +/*******************************************************************\ + +Function: operator << + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::ostream &operator << (std::ostream &out, const contextt &context) +{ + context.show(out); + return out; +} diff --git a/src/util/context.h b/src/util/context.h new file mode 100644 index 00000000000..4624d017d58 --- /dev/null +++ b/src/util/context.h @@ -0,0 +1,91 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CONTEXT_H +#define CPROVER_CONTEXT_H + +#include + +#include + +#include +#include +#include +#include + +#define forall_symbols(it, expr) \ + for(contextt::symbolst::const_iterator it=(expr).begin(); \ + it!=(expr).end(); it++) + +#define Forall_symbols(it, expr) \ + for(contextt::symbolst::iterator it=(expr).begin(); \ + it!=(expr).end(); it++) + +typedef std::multimap symbol_base_mapt; +typedef std::multimap symbol_module_mapt; + +#define forall_symbol_base_map(it, expr, base_name) \ + for(symbol_base_mapt::const_iterator it=(expr).lower_bound(base_name), \ + it_end=(expr).upper_bound(base_name); \ + it!=it_end; it++) + +#define forall_symbol_module_map(it, expr, module) \ + for(symbol_module_mapt::const_iterator it=(expr).lower_bound(module), \ + it_end=(expr).upper_bound(module); \ + it!=it_end; it++) + +class contextt +{ +public: + typedef hash_map_cont symbol_tablet; + typedef symbol_tablet symbolst; + + symbolst symbols; + symbol_base_mapt symbol_base_map; + symbol_module_mapt symbol_module_map; + + bool add(const symbolt &symbol); + + bool move(symbolt &symbol, symbolt *&new_symbol); + + // this will go away, use add instead + bool move(symbolt &symbol) + { symbolt *new_symbol; return move(symbol, new_symbol); } + + void clear() + { + symbols.clear(); + symbol_base_map.clear(); + symbol_module_map.clear(); + } + + bool remove(const irep_idt &name); + + void show(std::ostream &out) const; + + const irept &value(const irep_idt &name) const; + + void swap(contextt &other) + { + symbols.swap(other.symbols); + symbol_base_map.swap(other.symbol_base_map); + symbol_module_map.swap(other.symbol_module_map); + } + + bool has_symbol(const irep_idt &name) const + { + return symbols.find(name)!=symbols.end(); + } + + symbolt &lookup(const irep_idt &identifier); + const symbolt &lookup(const irep_idt &identifier) const; +}; + +std::ostream &operator << (std::ostream &out, const contextt &context); + +#endif diff --git a/src/util/cout_message.h b/src/util/cout_message.h new file mode 100644 index 00000000000..fa67b9b9686 --- /dev/null +++ b/src/util/cout_message.h @@ -0,0 +1,42 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_COUT_MESSAGE_H + +#define CPROVER_COUT_MESSAGE_H + +#include +#include + +class cout_message_handlert:public message_handlert +{ +public: + virtual void print(unsigned level, const std::string &message) + { std::cout << message << std::endl; } +}; + +class cerr_message_handlert:public message_handlert +{ +public: + virtual void print(unsigned level, const std::string &message) + { std::cerr << message << std::endl; } +}; + +class console_message_handlert:public message_handlert +{ +public: + virtual void print(unsigned level, const std::string &message) + { + if(level>1) + std::cout << message << std::endl; + else + std::cerr << message << std::endl; + } +}; + +#endif diff --git a/src/util/cprover_prefix.h b/src/util/cprover_prefix.h new file mode 100644 index 00000000000..c9865626f4d --- /dev/null +++ b/src/util/cprover_prefix.h @@ -0,0 +1,4 @@ +#define CPROVER_PREFIX "c::__CPROVER_" +#define CPROVER_FKT_PREFIX "c::__CPROVER_fkt_" +#define CPROVER_MACRO_PREFIX "c::__CPROVER_macro_" + diff --git a/src/util/decision_procedure.cpp b/src/util/decision_procedure.cpp new file mode 100644 index 00000000000..c5b362e9ce4 --- /dev/null +++ b/src/util/decision_procedure.cpp @@ -0,0 +1,29 @@ +/*******************************************************************\ + +Module: Decision Procedure Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "decision_procedure.h" + +/*******************************************************************\ + +Function: decision_proceduret::in_core + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool decision_proceduret::in_core(const exprt &expr) +{ + assert(false); + return true; +} diff --git a/src/util/decision_procedure.h b/src/util/decision_procedure.h new file mode 100644 index 00000000000..ee4b312f8e2 --- /dev/null +++ b/src/util/decision_procedure.h @@ -0,0 +1,55 @@ +/*******************************************************************\ + +Module: Decision Procedure Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_DECISION_PROCEDURE_H +#define CPROVER_DECISION_PROCEDURE_H + +#include +#include +#include + +class decision_proceduret:public messaget +{ +public: + explicit decision_proceduret(const namespacet &_ns):ns(_ns) + { + } + + // get a value from satisfying instance if satisfiable + // returns nil if not available + virtual exprt get(const exprt &expr) const=0; + + // print satisfying assignment + virtual void print_assignment(std::ostream &out) const=0; + + // add constraints + // the expression must be of Boolean type + virtual void set_to(const exprt &expr, bool value)=0; + + void set_to_true(const exprt &expr) + { set_to(expr, true); } + + void set_to_false(const exprt &expr) + { set_to(expr, false); } + + // solve the problem + typedef enum { D_TAUTOLOGY, D_SATISFIABLE, + D_UNSATISFIABLE, D_ERROR } resultt; + + virtual resultt dec_solve()=0; + + virtual bool in_core(const exprt &expr); + + // return a textual description of the decision procedure + virtual std::string decision_procedure_text() const=0; + +protected: + const namespacet &ns; +}; + +#endif diff --git a/src/util/dstring.cpp b/src/util/dstring.cpp new file mode 100644 index 00000000000..64d72bc42fd --- /dev/null +++ b/src/util/dstring.cpp @@ -0,0 +1,10 @@ +/*******************************************************************\ + +Module: Container for C-Strings + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "dstring.h" + diff --git a/src/util/dstring.h b/src/util/dstring.h new file mode 100644 index 00000000000..83e50ce1d1f --- /dev/null +++ b/src/util/dstring.h @@ -0,0 +1,146 @@ +/*******************************************************************\ + +Module: Container for C-Strings + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef DSTRING_H +#define DSTRING_H + +#include + +#include "string_container.h" + +class dstring +{ +public: + // this is safe for static objects + inline dstring():no(0) + { + } + + // this is safe for static objects + // the 2nd argument is to avoid accidental conversions + inline dstring(unsigned _no, unsigned):no(_no) + { + } + + // this one is not safe for static objects + inline dstring(const char *s):no(string_container[s]) + { + } + + // this one is not safe for static objects + inline dstring(const std::string &s):no(string_container[s]) + { + } + + // access + + inline bool empty() const + { + return no==0; // string 0 is exactly the empty string + } + + inline char operator[](unsigned i) const + { + return as_string()[i]; + } + + // warning! the address returned is not stable + inline const char *c_str() const + { + return as_string().c_str(); + } + + inline size_t size() const + { + return as_string().size(); + } + + // ordering -- not the same as lexicographical ordering + + inline bool operator< (const dstring &b) const { return no (const std::string &b) const { return as_string()> b; } + bool operator<=(const std::string &b) const { return as_string()<=b; } + bool operator>=(const std::string &b) const { return as_string()>=b; } + + int compare(const dstring &b) const + { + if(no==b.no) return 0; // equal + return as_string().compare(b.as_string()); + } + + inline friend bool ordering(const dstring &a, const dstring &b) + { + return a.no +#include + +#include + +#include "mp_arith.h" +#include "fixedbv.h" +#include "ieee_float.h" +#include "expr.h" +#include "rational.h" +#include "rational_tools.h" +#include "arith_tools.h" + +/*******************************************************************\ + +Function: exprt::move_to_operands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::move_to_operands(exprt &expr) +{ + operandst &op=operands(); + op.push_back(static_cast(get_nil_irep())); + op.back().swap(expr); +} + +/*******************************************************************\ + +Function: exprt::move_to_operands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::move_to_operands(exprt &e1, exprt &e2) +{ + operandst &op=operands(); + #ifndef USE_LIST + op.reserve(op.size()+2); + #endif + op.push_back(static_cast(get_nil_irep())); + op.back().swap(e1); + op.push_back(static_cast(get_nil_irep())); + op.back().swap(e2); +} + +/*******************************************************************\ + +Function: exprt::move_to_operands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::move_to_operands(exprt &e1, exprt &e2, exprt &e3) +{ + operandst &op=operands(); + #ifndef USE_LIST + op.reserve(op.size()+3); + #endif + op.push_back(static_cast(get_nil_irep())); + op.back().swap(e1); + op.push_back(static_cast(get_nil_irep())); + op.back().swap(e2); + op.push_back(static_cast(get_nil_irep())); + op.back().swap(e3); +} + +/*******************************************************************\ + +Function: exprt::copy_to_operands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::copy_to_operands(const exprt &expr) +{ + operands().push_back(expr); +} + +/*******************************************************************\ + +Function: exprt::copy_to_operands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::copy_to_operands(const exprt &e1, const exprt &e2) +{ + operandst &op=operands(); + #ifndef USE_LIST + op.reserve(op.size()+2); + #endif + op.push_back(e1); + op.push_back(e2); +} + +/*******************************************************************\ + +Function: exprt::copy_to_operands + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::copy_to_operands(const exprt &e1, const exprt &e2, + const exprt &e3) +{ + operandst &op=operands(); + #ifndef USE_LIST + op.reserve(op.size()+3); + #endif + op.push_back(e1); + op.push_back(e2); + op.push_back(e3); +} + +/*******************************************************************\ + +Function: exprt::make_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::make_typecast(const typet &_type) +{ + exprt new_expr(ID_typecast); + + new_expr.move_to_operands(*this); + new_expr.set(ID_type, _type); + + swap(new_expr); +} + +/*******************************************************************\ + +Function: exprt::make_not + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::make_not() +{ + if(is_true()) + { + make_false(); + return; + } + else if(is_false()) + { + make_true(); + return; + } + + exprt new_expr; + + if(id()==ID_not && operands().size()==1) + { + new_expr.swap(operands().front()); + } + else + { + new_expr=exprt(ID_not, type()); + new_expr.move_to_operands(*this); + } + + swap(new_expr); +} + +/*******************************************************************\ + +Function: exprt::is_constant + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::is_constant() const +{ + return id()==ID_constant; +} + +/*******************************************************************\ + +Function: exprt::is_true + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::is_true() const +{ + return is_constant() && + type().id()==ID_bool && + get(ID_value)!=ID_false; +} + +/*******************************************************************\ + +Function: exprt::is_false + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::is_false() const +{ + return is_constant() && + type().id()==ID_bool && + get(ID_value)==ID_false; +} + +/*******************************************************************\ + +Function: exprt::make_bool + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::make_bool(bool value) +{ + *this=exprt(ID_constant, typet(ID_bool)); + set(ID_value, value?ID_true:ID_false); +} + +/*******************************************************************\ + +Function: exprt::make_true + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::make_true() +{ + *this=exprt(ID_constant, typet(ID_bool)); + set(ID_value, ID_true); +} + +/*******************************************************************\ + +Function: exprt::make_false + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::make_false() +{ + *this=exprt(ID_constant, typet(ID_bool)); + set(ID_value, ID_false); +} + +/*******************************************************************\ + +Function: operator< + + Inputs: + + Outputs: + + Purpose: defines ordering on expressions for canonicalization + +\*******************************************************************/ + +bool operator<(const exprt &X, const exprt &Y) +{ + return (irept &)X < (irept &)Y; +} + +/*******************************************************************\ + +Function: exprt::negate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::negate() +{ + const irep_idt &type_id=type().id(); + + if(type_id==ID_bool) + make_not(); + else + { + if(is_constant()) + { + const irep_idt &value=get(ID_value); + + if(type_id==ID_integer) + { + set(ID_value, integer2string(-string2integer(id2string(value)))); + } + else if(type_id==ID_unsignedbv) + { + mp_integer int_value=binary2integer(id2string(value), false); + typet _type=type(); + *this=from_integer(-int_value, _type); + } + else if(type_id==ID_signedbv) + { + mp_integer int_value=binary2integer(id2string(value), true); + typet _type=type(); + *this=from_integer(-int_value, _type); + } + else if(type_id==ID_fixedbv) + { + fixedbvt fixedbv_value=fixedbvt(*this); + fixedbv_value.negate(); + *this=fixedbv_value.to_expr(); + } + else if(type_id==ID_floatbv) + { + ieee_floatt ieee_float_value=ieee_floatt(*this); + ieee_float_value.negate(); + *this=ieee_float_value.to_expr(); + } + else + { + make_nil(); + assert(false); + } + } + else + { + if(id()==ID_unary_minus) + { + exprt tmp; + assert(operands().size()==1); + tmp.swap(op0()); + swap(tmp); + } + else + { + exprt tmp(ID_unary_minus, type()); + tmp.move_to_operands(*this); + swap(tmp); + } + } + } +} + +/*******************************************************************\ + +Function: exprt::is_boolean + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::is_boolean() const +{ + return type().id()==ID_bool; +} + +/*******************************************************************\ + +Function: exprt::is_zero + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::is_zero() const +{ + if(is_constant()) + { + const irep_idt value=get_string(ID_value); + const irep_idt &type_id=type().id_string(); + + if(type_id==ID_integer || type_id==ID_natural) + { + mp_integer int_value=string2integer(id2string(value)); + if(int_value==0) return true; + } + else if(type_id==ID_rational) + { + rationalt rat_value; + if(to_rational(*this, rat_value)) assert(false); + return rat_value.is_zero(); + } + else if(type_id==ID_unsignedbv || type_id==ID_signedbv) + { + mp_integer int_value=binary2integer(id2string(value), false); + if(int_value==0) return true; + } + else if(type_id==ID_fixedbv) + { + if(fixedbvt(*this)==0) return true; + } + else if(type_id==ID_floatbv) + { + if(ieee_floatt(*this)==0) return true; + } + else if(type_id==ID_pointer) + { + if(value==ID_NULL) return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: exprt::is_one + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::is_one() const +{ + if(is_constant()) + { + const std::string &value=get_string(ID_value); + const irep_idt &type_id=type().id_string(); + + if(type_id==ID_integer || type_id==ID_natural) + { + mp_integer int_value=string2integer(value); + if(int_value==1) return true; + } + else if(type_id==ID_rational) + { + rationalt rat_value; + if(to_rational(*this, rat_value)) assert(false); + return rat_value.is_one(); + } + else if(type_id==ID_unsignedbv || type_id==ID_signedbv) + { + mp_integer int_value=binary2integer(value, false); + if(int_value==1) return true; + } + else if(type_id==ID_fixedbv) + { + if(fixedbvt(*this)==1) + return true; + } + else if(type_id==ID_floatbv) + { + if(ieee_floatt(*this)==1) + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: exprt::sum + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::sum(const exprt &expr) +{ + if(!is_constant() || !expr.is_constant()) return true; + if(type()!=expr.type()) return true; + + const irep_idt &type_id=type().id(); + + if(type_id==ID_integer || type_id==ID_natural) + { + set(ID_value, integer2string( + string2integer(get_string(ID_value))+ + string2integer(expr.get_string(ID_value)))); + return false; + } + else if(type_id==ID_rational) + { + rationalt a, b; + if(!to_rational(*this, a) && !to_rational(expr, b)) + { + exprt a_plus_b=from_rational(a+b); + set(ID_value, a_plus_b.get_string(ID_value)); + return false; + } + } + else if(type_id==ID_unsignedbv || type_id==ID_signedbv) + { + set(ID_value, integer2binary( + binary2integer(get_string(ID_value), false)+ + binary2integer(expr.get_string(ID_value), false), + atoi(type().get(ID_width).c_str()))); + return false; + } + else if(type_id==ID_fixedbv) + { + set(ID_value, integer2binary( + binary2integer(get_string(ID_value), false)+ + binary2integer(expr.get_string(ID_value), false), + atoi(type().get(ID_width).c_str()))); + return false; + } + else if(type_id==ID_floatbv) + { + ieee_floatt f(*this); + f+=ieee_floatt(expr); + *this=f.to_expr(); + return false; + } + + return true; +} + +/*******************************************************************\ + +Function: exprt::mul + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::mul(const exprt &expr) +{ + if(!is_constant() || !expr.is_constant()) return true; + if(type()!=expr.type()) return true; + + const irep_idt &type_id=type().id(); + + if(type_id==ID_integer || type_id==ID_natural) + { + set(ID_value, integer2string( + string2integer(get_string(ID_value))* + string2integer(expr.get_string(ID_value)))); + return false; + } + else if(type_id==ID_rational) + { + rationalt a, b; + if(!to_rational(*this, a) && !to_rational(expr, b)) + { + exprt a_mul_b=from_rational(a*b); + set(ID_value, a_mul_b.get_string(ID_value)); + return false; + } + } + else if(type_id==ID_unsignedbv || type_id==ID_signedbv) + { + set(ID_value, integer2binary( + binary2integer(get_string(ID_value), false)* + binary2integer(expr.get_string(ID_value), false), + atoi(type().get(ID_width).c_str()))); + return false; + } + else if(type_id==ID_fixedbv) + { + fixedbvt f(*this); + f*=fixedbvt(expr); + *this=f.to_expr(); + return false; + } + else if(type_id==ID_floatbv) + { + ieee_floatt f(*this); + f*=ieee_floatt(expr); + *this=f.to_expr(); + return false; + } + + return true; +} + +/*******************************************************************\ + +Function: exprt::subtract + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool exprt::subtract(const exprt &expr) +{ + if(!is_constant() || !expr.is_constant()) return true; + + if(type()!=expr.type()) return true; + + const irep_idt &type_id=type().id(); + + if(type_id==ID_integer || type_id==ID_natural) + { + set(ID_value, integer2string( + string2integer(get_string(ID_value))- + string2integer(expr.get_string(ID_value)))); + return false; + } + else if(type_id==ID_rational) + { + rationalt a, b; + if(!to_rational(*this, a) && !to_rational(expr, b)) + { + exprt a_minus_b=from_rational(a-b); + set(ID_value, a_minus_b.get_string(ID_value)); + return false; + } + } + else if(type_id==ID_unsignedbv || type_id==ID_signedbv) + { + set(ID_value, integer2binary( + binary2integer(get_string(ID_value), false)- + binary2integer(expr.get_string(ID_value), false), + atoi(type().get(ID_width).c_str()))); + return false; + } + + return true; +} + +/*******************************************************************\ + +Function: exprt::find_location + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const locationt &exprt::find_location() const +{ + const locationt &l=location(); + + if(l.is_not_nil()) return l; + + forall_operands(it, (*this)) + { + const locationt &l=it->find_location(); + if(l.is_not_nil()) return l; + } + + return static_cast(get_nil_irep()); +} + +/*******************************************************************\ + +Function: exprt::visit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::visit(expr_visitort &visitor) +{ + std::stack stack; + + stack.push(this); + + while(!stack.empty()) + { + exprt &expr=*stack.top(); + stack.pop(); + + visitor(expr); + + Forall_operands(it, expr) + stack.push(&(*it)); + } +} + +/*******************************************************************\ + +Function: exprt::visit + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void exprt::visit(const_expr_visitort &visitor) const +{ + std::stack stack; + + stack.push(this); + + while(!stack.empty()) + { + const exprt &expr=*stack.top(); + stack.pop(); + + visitor(expr); + + forall_operands(it, expr) + stack.push(&(*it)); + } +} diff --git a/src/util/expr.h b/src/util/expr.h new file mode 100644 index 00000000000..9a01691d44c --- /dev/null +++ b/src/util/expr.h @@ -0,0 +1,191 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_EXPR_H +#define CPROVER_EXPR_H + +#include "type.h" +#include "location.h" + +#define forall_operands(it, expr) \ + if((expr).has_operands()) \ + for(exprt::operandst::const_iterator it=(expr).operands().begin(); \ + it!=(expr).operands().end(); it++) + +#define Forall_operands(it, expr) \ + if((expr).has_operands()) \ + for(exprt::operandst::iterator it=(expr).operands().begin(); \ + it!=(expr).operands().end(); it++) + +#define forall_expr(it, expr) \ + for(exprt::operandst::const_iterator it=(expr).begin(); \ + it!=(expr).end(); it++) + +#define Forall_expr(it, expr) \ + for(exprt::operandst::iterator it=(expr).begin(); \ + it!=(expr).end(); it++) + +#define forall_expr_list(it, expr) \ + for(expr_listt::const_iterator it=(expr).begin(); \ + it!=(expr).end(); it++) + +#define Forall_expr_list(it, expr) \ + for(expr_listt::iterator it=(expr).begin(); \ + it!=(expr).end(); it++) + +class exprt:public irept +{ +public: + #ifdef USE_LIST + typedef std::list operandst; + #else + typedef std::vector operandst; + #endif + + // constructors + exprt() { } + explicit exprt(const irep_idt &_id):irept(_id) { } + exprt(const irep_idt &_id, const typet &_type):irept(_id) { type()=_type; } + + typet &type() { return static_cast(add(ID_type)); } + const typet &type() const { return static_cast(find(ID_type)); } + + bool has_operands() const + { return !find(ID_operands).is_nil(); } + + operandst &operands() + { return (operandst &)(add(ID_operands).get_sub()); } + + const operandst &operands() const + { return (const operandst &)(find(ID_operands).get_sub()); } + + exprt &op0() + { return operands().front(); } + + exprt &op1() + #ifdef USE_LIST + { return *(++operands().begin()); } + #else + { return operands()[1]; } + #endif + + exprt &op2() + #ifdef USE_LIST + { return *(++ ++operands().begin()); } + #else + { return operands()[2]; } + #endif + + exprt &op3() + #ifdef USE_LIST + { return *(++ ++ ++operands().begin()); } + #else + { return operands()[3]; } + #endif + + const exprt &op0() const + { return operands().front(); } + + const exprt &op1() const + #ifdef USE_LIST + { return *(++operands().begin()); } + #else + { return operands()[1]; } + #endif + + const exprt &op2() const + #ifdef USE_LIST + { return *(++ ++operands().begin()); } + #else + { return operands()[2]; } + #endif + + const exprt &op3() const + #ifdef USE_LIST + { return *(++ ++ ++operands().begin()); } + #else + { return operands()[3]; } + #endif + + void reserve_operands(unsigned n) + #ifdef USE_LIST + { } + #else + { operands().reserve(n) ; } + #endif + + void move_to_operands(exprt &expr); // destroys expr + void move_to_operands(exprt &e1, exprt &e2); // destroys e1, e2 + void move_to_operands(exprt &e1, exprt &e2, exprt &e3); // destroys e1, e2, e3 + void copy_to_operands(const exprt &expr); // does not destroy expr + void copy_to_operands(const exprt &e1, const exprt &e2); // does not destroy expr + void copy_to_operands(const exprt &e1, const exprt &e2, const exprt &e3); // does not destroy expr + + void make_typecast(const typet &_type); + void make_not(); + + void make_true(); + void make_false(); + void make_bool(bool value); + void negate(); + bool sum(const exprt &expr); + bool mul(const exprt &expr); + bool subtract(const exprt &expr); + + bool is_constant() const; + bool is_true() const; + bool is_false() const; + bool is_zero() const; + bool is_one() const; + bool is_boolean() const; + + friend bool operator<(const exprt &X, const exprt &Y); + + const locationt &find_location() const; + + const locationt &location() const + { + return static_cast(find(ID_C_location)); + } + + locationt &location() + { + return static_cast(add(ID_C_location)); + } + + exprt &add_expr(const irep_idt &name) + { + return static_cast(add(name)); + } + + const exprt &find_expr(const irep_idt &name) const + { + return static_cast(find(name)); + } + + void visit(class expr_visitort &visitor); + void visit(class const_expr_visitort &visitor) const; +}; + +typedef std::list expr_listt; + +class expr_visitort +{ +public: + virtual ~expr_visitort() { } + virtual void operator()(exprt &expr) { } +}; + +class const_expr_visitort +{ +public: + virtual ~const_expr_visitort() { } + virtual void operator()(const exprt &expr) { } +}; + +#endif diff --git a/src/util/expr_util.cpp b/src/util/expr_util.cpp new file mode 100644 index 00000000000..6dd6454d1cd --- /dev/null +++ b/src/util/expr_util.cpp @@ -0,0 +1,454 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "expr_util.h" +#include "fixedbv.h" +#include "ieee_float.h" +#include "bitvector.h" + +/*******************************************************************\ + +Function: gen_zero + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt gen_zero(const typet &type) +{ + exprt result; + + const irep_idt type_id=type.id(); + + result=constant_exprt(type); + + if(type_id==ID_rational || + type_id==ID_real || + type_id==ID_integer || + type_id==ID_natural || + type_id==ID_complex || + type_id==ID_c_enum) + { + result.set(ID_value, ID_0); + } + else if(type_id==ID_unsignedbv || + type_id==ID_signedbv || + type_id==ID_verilogbv || + type_id==ID_floatbv || + type_id==ID_fixedbv) + { + std::string value; + unsigned width=bv_width(type); + + for(unsigned i=0; i + +#include "find_macros.h" + +/*******************************************************************\ + +Function: find_macros + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_macros( + const exprt &src, + const namespacet &ns, + find_macros_sett &dest) +{ + std::stack stack; + + // use stack, these may be nested deeply + stack.push(&src); + + while(!stack.empty()) + { + const exprt &e=*stack.top(); + stack.pop(); + + if(e.id()==ID_symbol || + e.id()==ID_next_symbol) + { + const irep_idt &identifier=e.get(ID_identifier); + + const symbolt &symbol=ns.lookup(identifier); + + if(symbol.is_macro) + { + // inserted? + if(dest.insert(identifier).second) + stack.push(&symbol.value); + } + } + else + { + forall_operands(it, e) + stack.push(&(*it)); + } + } +} diff --git a/src/util/find_macros.h b/src/util/find_macros.h new file mode 100644 index 00000000000..09018eb7710 --- /dev/null +++ b/src/util/find_macros.h @@ -0,0 +1,23 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_FIND_MACROS_H +#define CPROVER_FIND_MACROS_H + +#include "hash_cont.h" +#include "expr.h" +#include "namespace.h" + +typedef hash_set_cont find_macros_sett; + +void find_macros( + const exprt &src, + const namespacet &ns, + find_macros_sett &dest); + +#endif diff --git a/src/util/find_symbols.cpp b/src/util/find_symbols.cpp new file mode 100644 index 00000000000..53216344bc0 --- /dev/null +++ b/src/util/find_symbols.cpp @@ -0,0 +1,283 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "std_types.h" + +#include "find_symbols.h" + +typedef enum { F_TYPE, F_EXPR, F_BOTH } kindt; + +/*******************************************************************\ + +Function: find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_symbols( + const exprt &src, + find_symbols_sett &dest) +{ + find_symbols(src, dest, true, true); +} + +/*******************************************************************\ + +Function: find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_symbols( + const exprt &src, + find_symbols_sett &dest, + bool current, + bool next) +{ + if((src.id()==ID_symbol && current) || + (src.id()==ID_next_symbol && next)) + dest.insert(src.get(ID_identifier)); + else + { + forall_operands(it, src) + find_symbols(*it, dest, current, next); + } +} + +/*******************************************************************\ + +Function: has_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool has_symbol( + const exprt &src, + const find_symbols_sett &symbols, + bool current, + bool next) +{ + if((src.id()==ID_symbol && current) || + (src.id()==ID_next_symbol && next)) + return symbols.count(src.get(ID_identifier))!=0; + else + { + forall_operands(it, src) + if(has_symbol(*it, symbols, current, next)) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: has_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool has_symbol( + const exprt &src, + const find_symbols_sett &symbols) +{ + return has_symbol(src, symbols, true, true); +} + +/*******************************************************************\ + +Function: find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_symbols( + const exprt &src, + std::set &dest) +{ + if(src.id()==ID_symbol || src.id()==ID_next_symbol) + dest.insert(src); + else + { + forall_operands(it, src) + find_symbols(*it, dest); + } +} + +/*******************************************************************\ + +Function: find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_symbols(kindt kind, const typet &src, find_symbols_sett &dest); + +void find_symbols(kindt kind, const exprt &src, find_symbols_sett &dest) +{ + forall_operands(it, src) + find_symbols(kind, *it, dest); + + find_symbols(kind, src.type(), dest); + + if(kind==F_BOTH || kind==F_EXPR) + if(src.id()==ID_symbol || + src.id()==ID_next_symbol) + dest.insert(src.get(ID_identifier)); +} + +/*******************************************************************\ + +Function: find_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_symbols(kindt kind, const typet &src, find_symbols_sett &dest) +{ + if(src.has_subtype()) + find_symbols(kind, src.subtype(), dest); + + forall_subtypes(it, src) + find_symbols(kind, *it, dest); + + if(src.id()==ID_struct || + src.id()==ID_union) + { + const struct_union_typet &struct_union_type=to_struct_union_type(src); + const struct_union_typet::componentst &components=struct_union_type.components(); + + for(struct_union_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + find_symbols(kind, *it, dest); + } + else if(src.id()==ID_code) + { + const code_typet &code_type=to_code_type(src); + find_symbols(kind, code_type.return_type(), dest); + const code_typet::argumentst &arguments=code_type.arguments(); + for(code_typet::argumentst::const_iterator + it=arguments.begin(); + it!=arguments.end(); + it++) + find_symbols(kind, *it, dest); + } + else if(src.id()==ID_symbol) + dest.insert(src.get(ID_identifier)); + else if(src.id()==ID_array) + { + // do the size -- the subtype is already done + find_symbols(kind, to_array_type(src).size(), dest); + } +} + +/*******************************************************************\ + +Function: find_type_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_type_symbols(const exprt &src, find_symbols_sett &dest) +{ + find_symbols(F_TYPE, src, dest); +} + +/*******************************************************************\ + +Function: find_type_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_type_symbols(const typet &src, find_symbols_sett &dest) +{ + find_symbols(F_TYPE, src, dest); +} + +/*******************************************************************\ + +Function: find_type_and_expr_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_type_and_expr_symbols(const exprt &src, find_symbols_sett &dest) +{ + find_symbols(F_BOTH, src, dest); +} + +/*******************************************************************\ + +Function: find_type_and_expr_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_type_and_expr_symbols(const typet &src, find_symbols_sett &dest) +{ + find_symbols(F_BOTH, src, dest); +} + diff --git a/src/util/find_symbols.h b/src/util/find_symbols.h new file mode 100644 index 00000000000..6c1e110fb21 --- /dev/null +++ b/src/util/find_symbols.h @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_FIND_SYMBOLS_H +#define CPROVER_FIND_SYMBOLS_H + +#include + +#include "hash_cont.h" +#include "string_hash.h" +#include "expr.h" + +typedef hash_set_cont find_symbols_sett; + +void find_symbols( + const exprt &src, + find_symbols_sett &dest); + +void find_symbols( + const exprt &src, + find_symbols_sett &dest, + bool current, + bool next); + +void find_symbols( + const exprt &src, + std::set &dest); + +bool has_symbol( + const exprt &src, + find_symbols_sett &symbols); + +void find_type_symbols( + const typet &src, + find_symbols_sett &dest); + +void find_type_symbols( + const exprt &src, + find_symbols_sett &dest); + +void find_type_and_expr_symbols( + const typet &src, + find_symbols_sett &dest); + +void find_type_and_expr_symbols( + const exprt &src, + find_symbols_sett &dest); + +#endif diff --git a/src/util/fixedbv.cpp b/src/util/fixedbv.cpp new file mode 100644 index 00000000000..662d37e9da1 --- /dev/null +++ b/src/util/fixedbv.cpp @@ -0,0 +1,298 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "std_types.h" +#include "fixedbv.h" +#include "arith_tools.h" + +/*******************************************************************\ + +Function: fixedbv_spect::fixedbv_spect + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +fixedbv_spect::fixedbv_spect(const fixedbv_typet &type) +{ + integer_bits=type.get_integer_bits(); + width=type.get_width(); +} + +/*******************************************************************\ + +Function: fixedbvt::fixedbvt + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +fixedbvt::fixedbvt(const exprt &expr) +{ + from_expr(expr); +} + +/*******************************************************************\ + +Function: fixedbvt::from_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void fixedbvt::from_expr(const exprt &expr) +{ + spec=to_fixedbv_type(expr.type()); + v=binary2integer(id2string(expr.get(ID_value)), true); +} + +/*******************************************************************\ + +Function: fixedbvt::from_integer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void fixedbvt::from_integer(const mp_integer &i) +{ + v=i*power(2, spec.get_fraction_bits()); +} + +/*******************************************************************\ + +Function: fixedbvt::to_integer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer fixedbvt::to_integer() const +{ + // this rounds to zero, i.e., we just divide + return v/power(2, spec.get_fraction_bits()); +} + +/*******************************************************************\ + +Function: fixedbvt::to_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt fixedbvt::to_expr() const +{ + fixedbv_typet type; + type.set_width(spec.width); + type.set_integer_bits(spec.integer_bits); + exprt expr=exprt(ID_constant, type); + assert(spec.width!=0); + expr.set(ID_value, integer2binary(v, spec.width)); + return expr; +} + +/*******************************************************************\ + +Function: fixedbvt::round + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void fixedbvt::round(const fixedbv_spect &dest_spec) +{ + unsigned old_fraction_bits=spec.width-spec.integer_bits; + unsigned new_fraction_bits=dest_spec.width-dest_spec.integer_bits; + + mp_integer result; + + if(new_fraction_bits>old_fraction_bits) + result=v*power(2, new_fraction_bits-old_fraction_bits); + else if(new_fraction_bits=p) + { + if(v<0) --div; else ++div; + } + + result=div; + } + + v=result; + spec=dest_spec; +} + +/*******************************************************************\ + +Function: fixedbvt::negate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void fixedbvt::negate() +{ + v=-v; +} + +/*******************************************************************\ + +Function: fixedbvt::operator* + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +fixedbvt &fixedbvt::operator*=(const fixedbvt &o) +{ + v*=o.v; + + fixedbv_spect old_spec=spec; + + spec.width+=o.spec.width; + spec.integer_bits+=o.spec.integer_bits; + + round(old_spec); + + return *this; +} + +/*******************************************************************\ + +Function: fixedbvt::operator/= + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +fixedbvt &fixedbvt::operator/=(const fixedbvt &o) +{ + v*=power(2, o.spec.get_fraction_bits()); + v/=o.v; + + return *this; +} + +/*******************************************************************\ + +Function: fixedbvt::operator== + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool fixedbvt::operator==(int i) const +{ + return v==power(2, spec.get_fraction_bits())*i; +} + +/*******************************************************************\ + +Function: fixedbvt::format + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string fixedbvt::format( + const format_spect &format_spec) const +{ + std::string dest; + unsigned fraction_bits=spec.get_fraction_bits(); + + mp_integer int_value=v; + mp_integer factor=power(2, fraction_bits); + + if(int_value.is_negative()) + { + dest+="-"; + int_value.negate(); + } + + std::string base_10_string= + integer2string(int_value*power(10, fraction_bits)/factor); + + while(base_10_string.size()<=fraction_bits) + base_10_string="0"+base_10_string; + + std::string integer_part= + std::string(base_10_string, 0, base_10_string.size()-fraction_bits); + + std::string fraction_part= + std::string(base_10_string, base_10_string.size()-fraction_bits); + + dest+=integer_part; + + // strip trailing zeros + while(!fraction_part.empty() && + fraction_part[fraction_part.size()-1]=='0') + fraction_part.resize(fraction_part.size()-1); + + if(!fraction_part.empty()) + dest+="."+fraction_part; + + while(dest.size() +#include + +class fixedbv_spect +{ +public: + unsigned integer_bits, width; + + fixedbv_spect():integer_bits(0), width(0) + { + } + + fixedbv_spect(unsigned _width, unsigned _integer_bits): + integer_bits(_integer_bits), width(_width) + { + } + + fixedbv_spect(const class fixedbv_typet &type); + + inline unsigned get_fraction_bits() const + { + return width-integer_bits; + } +}; + +class fixedbvt +{ +public: + fixedbv_spect spec; + + fixedbvt():v(0) + { + } + + explicit fixedbvt(const class exprt &expr); + + void from_integer(const mp_integer &i); + mp_integer to_integer() const; // this rounds to zero + void from_expr(const class exprt &expr); + exprt to_expr() const; + void round(const fixedbv_spect &dest_spec); + + std::string to_ansi_c_string() const + { + return format(format_spect()); + } + + std::string format(const format_spect &format_spec) const; + + bool operator == (int i) const; + bool is_zero() const + { + return v==0; + } + + void negate(); + + fixedbvt &operator /= (const fixedbvt &other); + fixedbvt &operator *= (const fixedbvt &other); + fixedbvt &operator += (const fixedbvt &other); + fixedbvt &operator -= (const fixedbvt &other); + + friend bool operator < (const fixedbvt &a, const fixedbvt &b) { return a.v (const fixedbvt &a, const fixedbvt &b) { return a.v>b.v; } + friend bool operator >=(const fixedbvt &a, const fixedbvt &b) { return a.v>=b.v; } + friend bool operator ==(const fixedbvt &a, const fixedbvt &b) { return a.v==b.v; } + friend bool operator !=(const fixedbvt &a, const fixedbvt &b) { return a.v!=b.v; } + + const mp_integer &get_value() const { return v; } + void set_value(const mp_integer &_v) { v=_v; } + +protected: + // negative values stored as such + mp_integer v; +}; + +bool operator < (const fixedbvt &a, const fixedbvt &b); +bool operator <=(const fixedbvt &a, const fixedbvt &b); +bool operator > (const fixedbvt &a, const fixedbvt &b); +bool operator >=(const fixedbvt &a, const fixedbvt &b); +bool operator ==(const fixedbvt &a, const fixedbvt &b); +bool operator !=(const fixedbvt &a, const fixedbvt &b); + +#endif diff --git a/src/util/format_constant.cpp b/src/util/format_constant.cpp new file mode 100644 index 00000000000..24bba50f9c0 --- /dev/null +++ b/src/util/format_constant.cpp @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "format_constant.h" +#include "arith_tools.h" +#include "fixedbv.h" +#include "ieee_float.h" + +/*******************************************************************\ + +Function: format_constantt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string format_constantt::operator()(const exprt &expr) +{ + if(expr.is_constant()) + { + if(expr.type().id()==ID_natural || + expr.type().id()==ID_integer || + expr.type().id()==ID_unsignedbv || + expr.type().id()==ID_signedbv) + { + mp_integer i; + if(to_integer(expr, i)) return "(number conversion failed)"; + + return integer2string(i); + } + else if(expr.type().id()==ID_fixedbv) + { + return fixedbvt(expr).format(*this); + } + else if(expr.type().id()==ID_floatbv) + { + return ieee_floatt(expr).format(*this); + } + } + else if(expr.id()==ID_string_constant) + return expr.get_string(ID_value); + + return "(format-constant failed: "+expr.id_string()+")"; +} + diff --git a/src/util/format_constant.h b/src/util/format_constant.h new file mode 100644 index 00000000000..c4a79947c4f --- /dev/null +++ b/src/util/format_constant.h @@ -0,0 +1,21 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_FORMAT_CONSTANT_H +#define CPROVER_FORMAT_CONSTANT_H + +#include "expr.h" +#include "format_spec.h" + +class format_constantt:public format_spect +{ +public: + std::string operator()(const exprt &expr); +}; + +#endif diff --git a/src/util/format_spec.h b/src/util/format_spec.h new file mode 100644 index 00000000000..f0e997955fd --- /dev/null +++ b/src/util/format_spec.h @@ -0,0 +1,24 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_FORMAT_SPEC_H +#define CPROVER_FORMAT_SPEC_H + +class format_spect +{ +public: + format_spect():min_width(0), precision(6), zero_padding(false) + { + } + + unsigned min_width; + unsigned precision; + bool zero_padding; +}; + +#endif diff --git a/src/util/gcd.cpp b/src/util/gcd.cpp new file mode 100644 index 00000000000..3bec67d5d64 --- /dev/null +++ b/src/util/gcd.cpp @@ -0,0 +1,47 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "gcd.h" + +/*******************************************************************\ + +Function: gcd + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer gcd(const mp_integer &_a, const mp_integer &_b) +{ + mp_integer a=_a, b=_b; + + if(a.is_negative()) a.negate(); + if(b.is_negative()) b.negate(); + + if(a==1) return a; + if(b==1) return b; + if(a==b) return a; + + while(!b.is_zero()) + { + mp_integer tmp_b=b; + b=a%b; + assert(b + +mp_integer gcd(const mp_integer &a, const mp_integer &b); + +#endif diff --git a/src/util/get_module.cpp b/src/util/get_module.cpp new file mode 100644 index 00000000000..dcba9fb5836 --- /dev/null +++ b/src/util/get_module.cpp @@ -0,0 +1,132 @@ +/*******************************************************************\ + +Module: Find module symbol using name + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "get_module.h" +#include "message_stream.h" + +/*******************************************************************\ + +Function: get_module_by_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const symbolt &get_module_by_name( + const contextt &context, + const std::string &module, + message_handlert &message_handler) +{ + symbolptr_listt symbolptr_list; + message_streamt message_stream(message_handler); + + forall_symbol_base_map(it, context.symbol_base_map, module) + { + contextt::symbolst::const_iterator it2=context.symbols.find(it->second); + + if(it2==context.symbols.end()) + continue; + + const symbolt &s=it2->second; + + if(s.is_type || s.free_var || s.type.id()!="module") + continue; + + symbolptr_list.push_back(&s); + } + + if(symbolptr_list.empty()) + { + message_stream.str << "module `" << module << "' not found"; + message_stream.error(); + throw 0; + } + else if(symbolptr_list.size()>=2) + { + message_stream.str << "module `" << module << "' does not uniquely resolve:" << std::endl; + + forall_symbolptr_list(it, symbolptr_list) + message_stream.str << " " << (*it)->name << std::endl; + + message_stream.error(); + throw 0; + } + + // symbolptr_list has exactly one element + + return *symbolptr_list.front(); +} + +/*******************************************************************\ + +Function: get_module + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const symbolt &get_module( + const contextt &context, + const std::string &module, + message_handlert &message_handler) +{ + if(module!="") + return get_module_by_name(context, module, message_handler); + + symbolptr_listt symbolptr_list, main_symbolptr_list; + message_streamt message_stream(message_handler); + + forall_symbols(it, context.symbols) + { + const symbolt &s=it->second; + + if(s.type.id()!="module") + continue; + + // this is our default + if(s.base_name=="main") + return get_module_by_name(context, "main", message_handler); + + symbolptr_list.push_back(&s); + } + + if(symbolptr_list.empty()) + { + message_stream.str << "no module found"; + message_stream.error(); + throw 0; + } + else if(symbolptr_list.size()>=2) + { + message_stream.str << "multiple modules found, please select one:" << std::endl; + + forall_symbolptr_list(it, symbolptr_list) + message_stream.str << " " << (*it)->pretty_name << std::endl; + + message_stream.error(); + throw 0; + } + + // symbolptr_list has exactly one element + + const symbolt &symbol=*symbolptr_list.front(); + + message_stream.str << "Using module `" << symbol.pretty_name << "'"; + message_stream.status(); + + return symbol; +} + diff --git a/src/util/get_module.h b/src/util/get_module.h new file mode 100644 index 00000000000..f51a7096dbf --- /dev/null +++ b/src/util/get_module.h @@ -0,0 +1,15 @@ +/*******************************************************************\ + +Module: Find module symbol using name + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +const symbolt &get_module( + const contextt &context, + const std::string &module, + message_handlert &message_handler); diff --git a/src/util/graph.cpp b/src/util/graph.cpp new file mode 100644 index 00000000000..4fc794a2fb1 --- /dev/null +++ b/src/util/graph.cpp @@ -0,0 +1,10 @@ +/*******************************************************************\ + +Module: A Template Class for Graphs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "graph.h" + diff --git a/src/util/graph.h b/src/util/graph.h new file mode 100644 index 00000000000..ecd56e710d0 --- /dev/null +++ b/src/util/graph.h @@ -0,0 +1,682 @@ +/*******************************************************************\ + +Module: A Template Class for Graphs + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef __GRAPH_H +#define __GRAPH_H + +#include +#include +#include +#include +#include +#include + +class empty_nodet +{ +}; + +template +class graph_nodet +{ +public: + typedef E edget; + typedef std::map edgest; + + edgest in, out; + + void add_in(unsigned n) + { + in.insert(std::pair(n, edget())); + } + + void add_out(unsigned n) + { + out.insert(std::pair(n, edget())); + } + + void erase_in(unsigned n) + { + in.erase(n); + } + + void erase_out(unsigned n) + { + out.erase(n); + } +}; + +template +void intersection( + const typename graph_nodet::edgest &a, + const typename graph_nodet::edgest &b, + typename graph_nodet::edgest &dest) +{ + typename graph_nodet::edgest::const_iterator + it_a=a.begin(), + it_b=b.begin(); + + while(it_a!=a.end() && it_b!=b.end()) + { + if(*it_a==*it_b) + { + dest.insert(*it_a); + it_a++; + it_b++; + } + else if(*it_a<*it_b) + it_a++; + else // *it_a>*it_b + it_b++; + } +} + +template +class graph +{ +public: + typedef N nodet; + typedef typename nodet::edgest edgest; + typedef std::vector nodest; + typedef typename nodet::edget edget; + +protected: + nodest nodes; + +public: + unsigned add_node() + { + unsigned no=nodes.size(); + nodes.push_back(nodet()); + return no; + } + + void swap(graph &other) + { + nodes.swap(other.nodes); + } + + bool has_edge(unsigned i, unsigned j) const + { + return nodes[i].out.find(j)!=nodes[i].out.end(); + } + + inline const nodet &operator[](unsigned n) const + { + return nodes[n]; + } + + inline nodet &operator[](unsigned n) + { + return nodes[n]; + } + + inline void resize(unsigned s) + { + nodes.resize(s); + } + + inline unsigned size() const + { + return nodes.size(); + } + + inline const edgest &in(unsigned n) const + { + return nodes[n].in; + } + + inline const edgest &out(unsigned n) const + { + return nodes[n].out; + } + + void add_edge(unsigned a, unsigned b) + { + nodes[a].add_out(b); + nodes[b].add_in(a); + } + + void remove_edge(unsigned a, unsigned b) + { + nodes[a].erase_out(a); + nodes[b].erase_in(a); + } + + edget &edge(unsigned a, unsigned b) + { + return nodes[a].out[b]; + } + + void add_undirected_edge(unsigned a, unsigned b); + void remove_undirected_edge(unsigned a, unsigned b); + void remove_in_edges(unsigned n); + void remove_out_edges(unsigned n); + + void remove_edges(unsigned n) + { + remove_in_edges(n); + remove_out_edges(n); + } + + void clear() + { + nodes.clear(); + } + + typedef std::list patht; + + void shortest_path( + unsigned src, + unsigned dest, + patht &path); + + void visit_reachable(unsigned src); + + void make_chordal(); + + // return value: number of connected subgraphs + unsigned connected_subgraphs( + std::vector &subgraph_nr); + + // return value: number of SCCs + unsigned SCCs(std::vector &subgraph_nr); + + void output_dot(std::ostream &out) const; + void output_dot_node(std::ostream &out, unsigned n) const; + +protected: + class tarjant + { + public: + std::vector visited; + std::vector depth; + std::vector lowlink; + std::vector in_scc; + std::stack scc_stack; + std::vector &subgraph_nr; + + unsigned scc_count, max_dfs; + + tarjant(unsigned n, std::vector &_subgraph_nr): + subgraph_nr(_subgraph_nr) + { + visited.resize(n, false); + depth.resize(n, 0); + lowlink.resize(n, 0); + in_scc.resize(n, false); + max_dfs=scc_count=0; + subgraph_nr.resize(n, 0); + } + }; + + void tarjan(class tarjant &t, unsigned v); +}; + +/*******************************************************************\ + +Function: graph::add_undirected_edge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void graph::add_undirected_edge(unsigned a, unsigned b) +{ + assert(a +void graph::remove_undirected_edge(unsigned a, unsigned b) +{ + nodet &na=nodes[a]; + nodet &nb=nodes[b]; + na.out.erase(b); + nb.out.erase(a); + na.in.erase(b); + nb.in.erase(a); +} + +/*******************************************************************\ + +Function: graph::remove_in_edges + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void graph::remove_in_edges(unsigned n) +{ + nodet &node=nodes[n]; + + // delete all incoming edges + for(typename edgest::const_iterator + it=node.in.begin(); + it!=node.in.end(); + it++) + nodes[it->first].erase_out(n); + + node.in.clear(); +} + +/*******************************************************************\ + +Function: graph::remove_out_edges + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void graph::remove_out_edges(unsigned n) +{ + nodet &node=nodes[n]; + + // delete all outgoing edges + for(typename edgest::const_iterator + it=node.out.begin(); + it!=node.out.end(); + it++) + nodes[it->first].erase_in(n); + + node.out.clear(); +} + +/*******************************************************************\ + +Function: graph::shortest_path + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void graph::shortest_path( + unsigned src, + unsigned dest, + patht &path) +{ + std::vector visited; + std::vector distance; + std::vector previous; + + // initialization + visited.resize(nodes.size(), false); + distance.resize(nodes.size(), (unsigned)(-1)); + previous.resize(nodes.size(), 0); + + distance[src]=0; + visited[src]=true; + + // does BFS, not Dijkstra + // we hope the graph is sparse + std::vector frontier_set, new_frontier_set; + + frontier_set.reserve(nodes.size()); + + frontier_set.push_back(src); + + unsigned d=0; + bool found=false; + + while(!frontier_set.empty() && !found) + { + d++; + + new_frontier_set.clear(); + new_frontier_set.reserve(nodes.size()); + + for(std::vector::const_iterator + f_it=frontier_set.begin(); + f_it!=frontier_set.end() && !found; + f_it++) + { + unsigned i=*f_it; + nodet &n=nodes[i]; + + // do all neighbors + for(typename edgest::iterator + o_it=n.out.begin(); + o_it!=n.out.end() && !found; + o_it++) + { + unsigned o=o_it->first; + + if(!visited[o]) + { + distance[o]=d; + previous[o]=i; + visited[o]=true; + + if(o==dest) + found=true; + else + new_frontier_set.push_back(o); + } + } + } + + frontier_set.swap(new_frontier_set); + } + + // compute path + // walk towards 0-distance node + path.clear(); + + // reachable at all? + if(distance[dest]==(unsigned)(-1)) + return; // nah + + while(true) + { + path.push_front(dest); + if(distance[dest]==0) break; // we are there + dest=previous[dest]; + } +} + +/*******************************************************************\ + +Function: graph::visit_reachable + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void graph::visit_reachable(unsigned src) +{ + // DFS + + std::stack s; + s.push(src); + + while(!s.empty()) + { + unsigned n=s.top(); + s.pop(); + + nodet &node=nodes[n]; + node.visited=true; + + for(typename edgest::const_iterator + it=node.out.begin(); + it!=node.out.end(); + it++) + if(!nodes[*it].visited) + s.push(*it); + } +} + +/*******************************************************************\ + +Function: graph::connected_subgraphs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +unsigned graph::connected_subgraphs( + std::vector &subgraph_nr) +{ + std::vector visited; + + visited.resize(nodes.size(), false); + subgraph_nr.resize(nodes.size(), 0); + + unsigned nr=0; + + for(unsigned src=0; src s; + s.push(src); + + while(!s.empty()) + { + unsigned n=s.top(); + s.pop(); + + visited[n]=true; + subgraph_nr[n]=nr; + + const nodet &node=nodes[n]; + + for(typename edgest::const_iterator + it=node.out.begin(); + it!=node.out.end(); + it++) + if(!visited[*it]) + s.push(*it); + } + + nr++; + } + + return nr; +} + +/*******************************************************************\ + +Function: graph::tarjan + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void graph::tarjan(tarjant &t, unsigned v) +{ + t.scc_stack.push(v); + t.in_scc[v]=true; + t.depth[v]=t.max_dfs; + t.lowlink[v]=t.max_dfs; + t.visited[v]=true; + t.max_dfs++; + + const nodet &node=nodes[v]; + for(typename edgest::const_iterator + it=node.out.begin(); + it!=node.out.end(); + it++) + { + unsigned vp=it->first; + if(!t.visited[vp]) + { + tarjan(t, vp); + t.lowlink[v]=std::min(t.lowlink[v], t.lowlink[vp]); + } + else if(t.in_scc[vp]) + t.lowlink[v]=std::min(t.lowlink[v], t.depth[vp]); + } + + // check if root of SCC + if(t.lowlink[v]==t.depth[v]) + { + while(true) + { + assert(!t.scc_stack.empty()); + unsigned vp=t.scc_stack.top(); + t.scc_stack.pop(); + t.in_scc[vp]=false; + t.subgraph_nr[vp]=t.scc_count; + if(vp==v) break; + } + + t.scc_count++; + } +} + +/*******************************************************************\ + +Function: graph::SCCs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +unsigned graph::SCCs(std::vector &subgraph_nr) +{ + tarjant t(nodes.size(), subgraph_nr); + + for(unsigned v0=0; v0 +void graph::make_chordal() +{ + graph tmp(*this); + + // This assumes an undirected graph. + // 1. remove all nodes in tmp, reconnecting the remaining ones + // 2. the chordal graph is the old one plus the new edges + + for(unsigned i=0; iadd_undirected_edge(*it1, *it2); + } + } + + // remove node from tmp graph + tmp.remove_edges(i); + } +} + +/*******************************************************************\ + +Function: graph::output_dot + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +template +void graph::output_dot(std::ostream &out) const +{ + for(unsigned n=0; n +void graph::output_dot_node(std::ostream &out, unsigned n) const +{ + const nodet &node=nodes[n]; + + for(typename edgest::const_iterator + it=node.out.begin(); + it!=node.out.end(); + it++) + out << n << " -> " << it->first << std::endl; +} + +#endif diff --git a/src/util/group.h b/src/util/group.h new file mode 100644 index 00000000000..9893788bc32 --- /dev/null +++ b/src/util/group.h @@ -0,0 +1,41 @@ +#include +#include + +class groupt:protected std::vector +{ + protected: + typedef std::vector baset; + + public: + unsigned rep(unsigned a) + { + return (*this)[a]; + } + + void merge(unsigned a, unsigned b) + { + unsigned rep_a=rep(a), + rep_b=rep(b); + + unsigned new_rep=std::min(rep_a, rep_b); + + (*this)[a]=new_rep; + (*this)[b]=new_rep; + + if(rep_a!=a || rep_b!=b) + for(unsigned i=0; iis_boolean()) + throw "guard is expected to be Boolean"; + dest.copy_to_operands(*it); + } + + return dest; +} + +/*******************************************************************\ + +Function: guardt::add + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void guardt::add(const exprt &expr) +{ + if(expr.id()==ID_and && expr.type().id()==ID_bool) + { + forall_operands(it, expr) + add(*it); + + return; + } + + if(expr.is_true()) + { + } + else + guard_list.push_back(expr); +} + +/*******************************************************************\ + +Function: operator -= + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +guardt &operator -= (guardt &g1, const guardt &g2) +{ + guardt::guard_listt::const_iterator it2=g2.guard_list.begin(); + + while(!g1.guard_list.empty() && + it2!=g2.guard_list.end() && + g1.guard_list.front()==*it2) + { + g1.guard_list.pop_front(); + it2++; + } + + return g1; +} + +/*******************************************************************\ + +Function: operator |= + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +guardt &operator |= (guardt &g1, const guardt &g2) +{ + if(g2.is_false()) return g1; + if(g1.is_false()) { g1.guard_list=g2.guard_list; return g1; } + + // find common prefix + guardt::guard_listt::iterator it1=g1.guard_list.begin(); + guardt::guard_listt::const_iterator it2=g2.guard_list.begin(); + + while(it1!=g1.guard_list.end()) + { + if(it2==g2.guard_list.end()) + break; + + if(*it1!=*it2) + break; + + it1++; + it2++; + } + + if(it2==g2.guard_list.end()) return g1; + + // end of common prefix + exprt and_expr1, and_expr2; + and_expr1=g1.as_expr(it1); + and_expr2=g2.as_expr(it2); + + g1.guard_list.erase(it1, g1.guard_list.end()); + + exprt tmp(and_expr2); + tmp.make_not(); + + if(tmp!=and_expr1) + { + if(and_expr1.is_true() || and_expr2.is_true()) + { + } + else + g1.add(or_exprt(and_expr1, and_expr2)); + } + + return g1; +} + +/*******************************************************************\ + +Function: operator << + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::ostream &operator << (std::ostream &out, const guardt &g) +{ + forall_expr_list(it, g.guard_list) + out << "*** " << it->pretty() << std::endl; + return out; +} + +/*******************************************************************\ + +Function: guardt::is_false + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool guardt::is_false() const +{ + forall_guard(it, guard_list) + if(it->is_false()) + return true; + + return false; +} diff --git a/src/util/guard.h b/src/util/guard.h new file mode 100644 index 00000000000..692ef7efbc6 --- /dev/null +++ b/src/util/guard.h @@ -0,0 +1,93 @@ +/*******************************************************************\ + +Module: Guard Data Structure + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_GUARD_H +#define CPROVER_GUARD_H + +#include + +#include + +class guardt +{ +public: + typedef expr_listt guard_listt; + + void add(const exprt &expr); + + void append(const guardt &guard) + { + for(guard_listt::const_iterator it=guard.guard_list.begin(); + it!=guard.guard_list.end(); + it++) + add(*it); + } + + exprt as_expr(guard_listt::const_iterator it) const; + + exprt as_expr() const + { + return as_expr(guard_list.begin()); + } + + void guard_expr(exprt &dest) const; + + bool empty() const { return guard_list.empty(); } + bool is_true() const { return empty(); } + bool is_false() const; + + void make_true() + { + guard_list.clear(); + } + + void make_false() + { + guard_list.clear(); + guard_list.push_back(exprt()); + guard_list.back().make_false(); + } + + friend guardt &operator -= (guardt &g1, const guardt &g2); + friend guardt &operator |= (guardt &g1, const guardt &g2); + + void swap(guardt &g) + { + guard_list.swap(g.guard_list); + } + + friend std::ostream &operator << (std::ostream &out, const guardt &g); + + unsigned size() const + { + return guard_list.size(); + } + + void resize(unsigned s) + { + guard_list.resize(s); + } + + const guard_listt &get_guard_list() const + { + return guard_list; + } + +protected: + guard_listt guard_list; +}; + +#define Forall_guard(it, guard_list) \ + for(guardt::guard_listt::iterator it=(guard_list).begin(); \ + it!=(guard_list).end(); it++) + +#define forall_guard(it, guard_list) \ + for(guardt::guard_listt::const_iterator it=(guard_list).begin(); \ + it!=(guard_list).end(); it++) + +#endif diff --git a/src/util/hash_cont.h b/src/util/hash_cont.h new file mode 100644 index 00000000000..0f3f043af5e --- /dev/null +++ b/src/util/hash_cont.h @@ -0,0 +1,75 @@ +/*******************************************************************\ + +Module: STL Hash map / set + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_HASH_CONT_H +#define CPROVER_HASH_CONT_H + +// you need to pick one of the following three options + +// #define STL_HASH_NONE +// #define STL_HASH_STDEXT +// #define STL_HASH_GNU +#define STL_HASH_TR1 + +#if defined(STL_HASH_NONE) + +#include +#include + +template +typedef std::map hash_map_cont; + +template +typedef std::set hash_set_cont; + +template +typedef std::multiset hash_multiset_cont; + +#elif defined(STL_HASH_STDEXT) + +#include +#include + +// for Visual Studio >= 2003 + +#define hash_map_cont stdext::hash_map +#define hash_set_cont stdext::hash_set +#define hash_multiset_cont stdext::hash_multiset + +#elif defined(STL_HASH_GNU) + +#include +#include + +// for new g++ libraries >= 3.2 + +#define hash_map_cont __gnu_cxx::hash_map +#define hash_set_cont __gnu_cxx::hash_set +#define hash_multiset_cont __gnu_cxx::hash_multiset + +#elif defined(STL_HASH_TR1) + +#ifdef _MSC_VER +#include +#include +#else +#include +#include +#endif + +#define hash_map_cont std::tr1::unordered_map +#define hash_set_cont std::tr1::unordered_set +#define hash_multiset_cont std::tr1::unordered_multiset + +#else + +#error Please select hash container option + +#endif + +#endif diff --git a/src/util/i2string.cpp b/src/util/i2string.cpp new file mode 100644 index 00000000000..0ef7f37227b --- /dev/null +++ b/src/util/i2string.cpp @@ -0,0 +1,150 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#define USE_SPRINTF + +#ifdef USE_SPRINTF + +#include +#include + +#include "i2string.h" + +#else + +#include + +#include "i2string.h" +#include "strstream2string.h" + +#endif + +/*******************************************************************\ + +Function: i2string + + Inputs: signed integer + + Outputs: string class + + Purpose: convert signed integer to string class + +\*******************************************************************/ + +std::string i2string(int i) +{ + #ifdef USE_SPRINTF + char buffer[100]; + sprintf(buffer, "%d", i); + return buffer; + #else + std::ostringstream strInt; + + strInt << i; + std::string str; + strstream2string(strInt, str); + + return str; + #endif +} + +/*******************************************************************\ + +Function: i2string + + Inputs: signed long integer + + Outputs: string class + + Purpose: convert signed integer to string class + +\*******************************************************************/ + +std::string i2string(signed long int i) +{ + #ifdef USE_SPRINTF + char buffer[100]; + #ifdef _WIN32 + #ifdef __MINGW32_VERSION + snprintf(buffer, sizeof(buffer), "%ld", i); + #else + sprintf_s(buffer, sizeof(buffer), "%ld", i); + #endif + #else + snprintf(buffer, sizeof(buffer), "%ld", i); + #endif + return buffer; + #else + std::ostringstream strInt; + + strInt << i; + std::string str; + strstream2string(strInt, str); + + return str; + #endif +} + +/*******************************************************************\ + +Function: i2string + + Inputs: unsigned integer + + Outputs: string class + + Purpose: convert unsigned integer to string class + +\*******************************************************************/ + +std::string i2string(unsigned i) +{ + #ifdef USE_SPRINTF + char buffer[100]; + sprintf(buffer, "%u", i); + return buffer; + #else + std::ostringstream strInt; + + strInt << i; + std::string str; + strstream2string(strInt, str); + + return str; + #endif +} + +/*******************************************************************\ + +Function: i2string + + Inputs: unsigned long integer + + Outputs: string class + + Purpose: convert unsigned integer to string class + +\*******************************************************************/ + +std::string i2string(unsigned long int i) +{ + #ifdef USE_SPRINTF + char buffer[100]; + sprintf(buffer, "%lu", i); + return buffer; + #else + std::ostringstream strInt; + + strInt << i; + std::string str; + strstream2string(strInt, str); + + return str; + #endif +} + diff --git a/src/util/i2string.h b/src/util/i2string.h new file mode 100644 index 00000000000..e22ba4159ed --- /dev/null +++ b/src/util/i2string.h @@ -0,0 +1,14 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +std::string i2string(int i); +std::string i2string(signed long int i); +std::string i2string(unsigned i); +std::string i2string(unsigned long int i); diff --git a/src/util/identifier.cpp b/src/util/identifier.cpp new file mode 100644 index 00000000000..371c25b904d --- /dev/null +++ b/src/util/identifier.cpp @@ -0,0 +1,71 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "identifier.h" + +/*******************************************************************\ + +Function: identifiert::as_string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string identifiert::as_string() const +{ + std::string result; + + for(componentst::const_iterator it=components.begin(); + it!=components.end(); it++) + { + if(it!=components.begin()) result+=ID_SEPARATOR; + result+=*it; + } + + return result; +} + +/*******************************************************************\ + +Function: identifiert::parse + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void identifiert::parse(const std::string &s) +{ + std::string component; + + for(unsigned i=0; i +#include + +#define ID_SEPARATOR "::" + +class identifiert +{ +public: + identifiert(const std::string &s) + { parse(s); } + + identifiert() + { } + + std::string as_string() const; + + typedef std::vector componentst; + componentst components; + +protected: + void parse(const std::string &s); +}; + +#endif diff --git a/src/util/ieee_float.cpp b/src/util/ieee_float.cpp new file mode 100644 index 00000000000..fba7dc9b6ab --- /dev/null +++ b/src/util/ieee_float.cpp @@ -0,0 +1,1493 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "ieee_float.h" + +/*******************************************************************\ + +Function: ieee_float_spect::bias + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer ieee_float_spect::bias() const +{ + return power(2, e-1)-1; +} + +/*******************************************************************\ + +Function: ieee_float_spect::to_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +floatbv_typet ieee_float_spect::to_type() const +{ + floatbv_typet result; + result.set_f(f); + result.set_width(width()); + return result; +} + +/*******************************************************************\ + +Function: ieee_float_spect::max_exponent + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer ieee_float_spect::max_exponent() const +{ + return power(2, e)-1; +} + +/*******************************************************************\ + +Function: ieee_float_spect::max_fraction + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer ieee_float_spect::max_fraction() const +{ + return power(2, f)-1; +} + +/*******************************************************************\ + +Function: ieee_float_spect::from_type + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ieee_float_spect::from_type(const floatbv_typet &type) +{ + unsigned width=type.get_width(); + f=type.get_f(); + assert(f!=0); + assert(f0) + { + result+='.'; + for(unsigned i=0; i=0) + { + result+=integer2string(_fraction*power(2, _exponent)); + + // add zeros, if needed + if(format_spec.precision>0) + { + result+='.'; + for(unsigned i=0; iformat_spec.precision) + { + mp_integer r=power(10, position-format_spec.precision); + mp_integer remainder=_fraction%r; + _fraction/=r; + // not sure if this is the right kind of rounding here + if(remainder>=r/2) ++_fraction; + position=format_spec.precision; + } + + std::string tmp=integer2string(_fraction); + + // pad with zeros from the front, if needed + while(mp_integer(tmp.size())<=position) tmp="0"+tmp; + + unsigned dot=tmp.size()-integer2long(position); + result+=std::string(tmp, 0, dot)+'.'; + result+=std::string(tmp, dot, std::string::npos); + + // append zeros if needed + for(mp_integer i=position; i