void goto_convertt::make_temp_symbol( exprt &expr, goto_programt &dest) { const locationt location=expr.find_location(); symbolt &new_symbol=new_tmp_symbol(expr.type()); code_assignt assignment; assignment.lhs()=symbol_expr(new_symbol); assignment.rhs()=expr; assignment.location()=location; convert(assignment, dest); expr=symbol_expr(new_symbol); }
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() == "pointer" && expr.op1().type().id() == "pointer") { // add same-object subgoal if (!options.get_bool_option("no-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); } } }
void goto_checkt::div_by_zero_check(const exprt &expr, const guardt &guard) { if (options.get_bool_option("no-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("notequal", bool_typet()); inequality.copy_to_operands(expr.op1(), zero); add_guarded_claim(inequality, "division by zero", "division-by-zero", expr.find_location(), guard); }
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() != "floatbv") return; if (expr.id() != "+" && expr.id() != "*" && expr.id() != "/" && expr.id() != "-") return; // add nan subgoal exprt isnan("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); }
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() != "signedbv") return; // add overflow subgoal exprt overflow("overflow-" + expr.id_string(), bool_typet()); overflow.operands() = expr.operands(); if (expr.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().width().c_str()); unsigned old_width = atoi(old_type.width().c_str()); if (old_type.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); }
symbol_exprt goto_convertt::make_compound_literal( const exprt &expr, goto_programt &dest) { const locationt location=expr.find_location(); symbolt new_symbol; symbolt *symbol_ptr; do { // The lifetime of compound literals is really that of // the block they are in. new_symbol.base_name="literal$"+i2string(++temporary_counter); new_symbol.name=tmp_symbol_prefix+id2string(new_symbol.base_name); new_symbol.is_lvalue=true; new_symbol.is_thread_local=false; new_symbol.is_static_lifetime=true; new_symbol.is_file_local=true; new_symbol.value=expr; new_symbol.type=expr.type(); new_symbol.location=location; } while(symbol_table.move(new_symbol, symbol_ptr)); // The value might depend on a variable, thus // generate code for this. symbol_exprt result=symbol_ptr->symbol_expr(); result.location()=location; code_assignt code_assign(result, expr); code_assign.location()=location; convert(code_assign, dest); return result; }
void goto_program_dereferencet::dereference_rec( exprt &expr, guardt &guard, const value_set_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; i<expr.operands().size(); i++) { exprt &op=expr.operands()[i]; if(!op.is_boolean()) throw expr.id_string()+" takes Boolean operands only, but got "+ op.pretty(); if(dereference.has_dereference(op)) dereference_rec(op, guard, value_set_dereferencet::READ); if(expr.id()==ID_or) { exprt tmp(op); tmp.make_not(); guard.add(tmp); } else guard.add(op); } guard.resize(old_guards); return; } else if(expr.id()==ID_if) { if(expr.operands().size()!=3) throw "if takes three arguments"; if(!expr.op0().is_boolean()) { std::string msg= "first argument of if must be boolean, but got " +expr.op0().to_string(); throw msg; } dereference_rec(expr.op0(), guard, value_set_dereferencet::READ); bool o1=dereference.has_dereference(expr.op1()); bool o2=dereference.has_dereference(expr.op2()); if(o1) { unsigned old_guard=guard.size(); guard.add(expr.op0()); dereference_rec(expr.op1(), guard, mode); guard.resize(old_guard); } if(o2) { unsigned old_guard=guard.size(); exprt tmp(expr.op0()); tmp.make_not(); guard.add(tmp); dereference_rec(expr.op2(), guard, mode); guard.resize(old_guard); } return; } if(expr.id()==ID_address_of || expr.id()=="reference_to") { // turn &*p to p // this has *no* side effect! assert(expr.operands().size()==1); if(expr.op0().id()==ID_dereference || expr.op0().id()=="implicit_dereference") { assert(expr.op0().operands().size()==1); exprt tmp; tmp.swap(expr.op0().op0()); if(tmp.type()!=expr.type()) tmp.make_typecast(expr.type()); expr.swap(tmp); } } Forall_operands(it, expr) dereference_rec(*it, guard, mode); if(expr.id()==ID_dereference) { if(expr.operands().size()!=1) throw "dereference expects one operand"; dereference_location=expr.find_location(); exprt tmp=dereference.dereference( expr.op0(), guard, mode); expr.swap(tmp); } else if(expr.id()=="implicit_dereference") { // old stuff assert(false); } else if(expr.id()==ID_index) { // this is old stuff and will go away if(expr.operands().size()!=2) throw "index expects two operands"; if(expr.op0().type().id()==ID_pointer) { dereference_location=expr.find_location(); exprt tmp1(ID_plus, expr.op0().type()); tmp1.operands().swap(expr.operands()); exprt tmp2=dereference.dereference(tmp1, guard, mode); tmp2.swap(expr); } } }
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()!="symbol") { err_location(function); throw "function_call expects symbol as function operand, " "but got `"+function.id_string()+"'"; } const irep_idt &identifier=function.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. 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()) { err_location(function); str << "failed to find function `" << identifier << "'"; throw 0; } 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) { inlined_funcs.insert(identifier.as_string()); for (std::set<std::string>::const_iterator it2 = f.inlined_funcs.begin(); it2 != f.inlined_funcs.end(); it2++) { inlined_funcs.insert(*it2); } recursion_sett::iterator recursion_it= recursion_set.insert(identifier).first; 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); // set local variables Forall_goto_program_instructions(it, tmp) it->local_variables.insert(target->local_variables.begin(), target->local_variables.end()); if(f.type.hide()) { const locationt &new_location=function.find_location(); Forall_goto_program_instructions(it, tmp) { if(new_location.is_not_nil()) { // can't just copy, e.g., due to comments field it->location.id(""); // not NIL it->location.set_file(new_location.get_file()); it->location.set_line(new_location.get_line()); it->location.set_column(new_location.get_column()); it->location.set_function(new_location.get_function()); } } } // do this recursively goto_inline_rec(tmp, full); // set up location instruction for function call target->type=LOCATION; target->code = expr2tc(); goto_programt::targett next_target(target); next_target++; dest.instructions.splice(next_target, tmp.instructions); target=next_target; recursion_set.erase(recursion_it); }
void goto_checkt::bounds_check(const exprt &expr, const guardt &guard) { if (options.get_bool_option("no-bounds-check")) return; if (expr.id() != "index") return; if (expr.operands().size() != 2) throw "index takes two operands"; // Don't bounds check the initial index of argv in the "main" function; it's // always correct, and just adds needless claims. In the past a "no bounds // check" attribute in old irep handled this. if (expr.op0().id_string() == "symbol" && expr.op0().identifier() == "c::argv'" && expr.op1().id_string() == "symbol" && expr.op1().identifier() == "c::argc'") return; if (expr.op0().id_string() == "symbol" && expr.op0().identifier() == "c::envp'" && expr.op1().id_string() == "symbol" && expr.op1().identifier() == "c::envp_size'") return; typet array_type = ns.follow(expr.op0().type()); if (array_type.id() == "pointer") return; // done by the pointer code else if (array_type.id() == "incomplete_array") { std::cerr << expr.pretty() << std::endl; throw "index got incomplete array"; } else if (!array_type.is_array()) throw "bounds check expected array type, got " + array_type.id_string(); // Otherwise, if there's a dereference in the array source, this bounds check // should be performed by the symex-time dereferencing code, as the base thing // being accessed may be anything. if (has_dereference(expr.op0())) return; std::string name = "array bounds violated: " + array_name(expr.op0()); const exprt &index = expr.op1(); if (index.type().id() != "unsignedbv") { // we undo typecasts to signedbv if (index.id() == "typecast" && index.operands().size() == 1 && index.op0().type().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(">=", bool_typet()); inequality.copy_to_operands(index, zero); add_guarded_claim(inequality, name + " lower bound", "array bounds", expr.find_location(), guard); } } } { if (array_type.size_irep().is_nil()) throw "index array operand of wrong type"; const exprt &size = (const exprt &) array_type.size_irep(); if (size.id() != "infinity") { exprt inequality("<", 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); } } }
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 // compound literals 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) { // first clean condition clean_expr(to_if_expr(expr).cond(), dest, true); // possibly done now if(!needs_cleaning(to_if_expr(expr).true_case()) && !needs_cleaning(to_if_expr(expr).false_case())) return; // copy expression if_exprt if_expr=to_if_expr(expr); if(!if_expr.cond().is_boolean()) throw "first argument of `if' must be boolean, but got " +if_expr.cond().to_string(); const locationt location=expr.find_location(); // We do some constant-folding here, to mimic // what typical compilers do. { exprt tmp_cond=if_expr.cond(); simplify(tmp_cond, ns); if(tmp_cond.is_true()) { clean_expr(if_expr.true_case(), dest, result_is_used); expr=if_expr.true_case(); return; } else if(tmp_cond.is_false()) { clean_expr(if_expr.false_case(), dest, result_is_used); expr=if_expr.false_case(); return; } } goto_programt tmp_true; clean_expr(if_expr.true_case(), tmp_true, result_is_used); goto_programt tmp_false; clean_expr(if_expr.false_case(), tmp_false, result_is_used); if(result_is_used) { symbolt &new_symbol= new_tmp_symbol(expr.type(), "if_expr", dest, location); code_assignt assignment_true; assignment_true.lhs()=new_symbol.symbol_expr(); assignment_true.rhs()=if_expr.true_case(); assignment_true.location()=location; convert(assignment_true, tmp_true); code_assignt assignment_false; assignment_false.lhs()=new_symbol.symbol_expr(); assignment_false.rhs()=if_expr.false_case(); assignment_false.location()=location; convert(assignment_false, tmp_false); // overwrites expr expr=new_symbol.symbol_expr(); } else { // preserve the expressions for possible later checks if(if_expr.true_case().is_not_nil()) { code_expressiont code_expression(if_expr.true_case()); convert(code_expression, tmp_true); } if(if_expr.false_case().is_not_nil()) { code_expressiont code_expression(if_expr.false_case()); convert(code_expression, tmp_false); } expr=nil_exprt(); } // generate guard for argument side-effects generate_ifthenelse( if_expr.cond(), tmp_true, tmp_false, location, dest); return; } else if(expr.id()==ID_comma) { if(result_is_used) { exprt result; Forall_operands(it, expr) { bool last=(it==--expr.operands().end()); // special treatment for last one if(last) { result.swap(*it); clean_expr(result, dest, true); } else { clean_expr(*it, dest, false); // remember these for later checks if(it->is_not_nil()) convert(code_expressiont(*it), dest); } } expr.swap(result); } else // result not used {