|
12 | 12 | #include <analyses/call_graph_helpers.h> |
13 | 13 | #include <unordered_set> |
14 | 14 |
|
| 15 | +/// Remove assignment of non-dets to values if they can't be reached |
| 16 | +/// Mark instances of function call followed by assignment of non-det to |
| 17 | +/// function's return value |
15 | 18 | static void build_NONDET_retvals_replacements( |
16 | 19 | const goto_modelt &model, |
17 | 20 | const namespacet &ns, |
18 | 21 | replacements_of_NONDET_retvalst &replacements) |
19 | 22 | { |
20 | 23 | for(const auto &elem : model.goto_functions.function_map) |
21 | 24 | { |
22 | | - const auto & instructions=elem.second.body.instructions; |
23 | | - auto instr_it=instructions.begin(); |
24 | | - if(instr_it==instructions.end()) |
25 | | - continue; |
26 | | - for(auto next_instr_it=std::next(instr_it); |
27 | | - next_instr_it!=instructions.end(); |
28 | | - instr_it = next_instr_it, ++next_instr_it) |
| 25 | + const goto_programt::instructionst &instructions = |
| 26 | + elem.second.body.instructions; |
| 27 | + for(auto instr_it = instructions.begin(); instr_it != instructions.end(); |
| 28 | + ++instr_it) |
29 | 29 | { |
30 | | - if(instr_it->type != FUNCTION_CALL || next_instr_it->type != ASSIGN) |
| 30 | + auto next_instr_it = std::next(instr_it); |
| 31 | + if( |
| 32 | + next_instr_it != instructions.end() && |
| 33 | + instr_it->type == FUNCTION_CALL && next_instr_it->type == ASSIGN) |
31 | 34 | { |
32 | | - if(instr_it->type == ASSIGN) |
33 | | - { |
34 | | - const code_assignt &assign = to_code_assign(instr_it->code); |
35 | | - if(assign.rhs().id() == ID_side_effect) |
| 35 | + const code_assignt &asgn = to_code_assign(next_instr_it->code); |
| 36 | + if(asgn.rhs().id() != ID_side_effect) |
| 37 | + continue; |
| 38 | + const side_effect_exprt see = to_side_effect_expr(asgn.rhs()); |
| 39 | + if(see.get_statement() != ID_nondet) |
| 40 | + continue; |
| 41 | + |
| 42 | + const code_function_callt &fn_call = |
| 43 | + to_code_function_call(instr_it->code); |
| 44 | + if(fn_call.function().id() != ID_symbol) |
| 45 | + continue; |
| 46 | + const irep_idt &callee_ident = |
| 47 | + to_symbol_expr(fn_call.function()).get_identifier(); |
| 48 | + |
| 49 | + std::string callee_return_value_symbol = |
| 50 | + id2string(callee_ident) + RETURN_VALUE_SUFFIX; |
| 51 | + if(!ns.get_symbol_table().has_symbol(callee_return_value_symbol)) |
| 52 | + continue; |
| 53 | + |
| 54 | + replacements.insert( |
36 | 55 | { |
37 | | - const side_effect_exprt side_effect = |
38 | | - to_side_effect_expr(assign.rhs()); |
39 | | - if(side_effect.get_statement() == ID_nondet) |
40 | | - { |
41 | | - // See if we can see that this instruction is definitely |
42 | | - // dead. Iterate backwards through the program from the current |
43 | | - // instruction, looking for an unconditional GOTO or a target. |
44 | | - // If we find an unconditional GOTO first then the instruction |
45 | | - // is dead. If we find a target first then we must assume that |
46 | | - // it can be reached. If we reach the beginning before finding |
47 | | - // either then the instruction is reachable. |
48 | | - bool is_dead = true; |
49 | | - for(auto sit = instr_it; |
50 | | - sit != instructions.begin(); |
51 | | - sit=std::prev(sit)) |
52 | | - { |
53 | | - if(sit->is_target()) |
54 | | - { |
55 | | - is_dead = false; |
56 | | - break; |
57 | | - } |
58 | | - if(sit->type == GOTO && sit->guard.is_true()) |
59 | | - break; |
60 | | - } |
61 | | - if(is_dead || side_effect.type().id() == ID_pointer) |
62 | | - { |
63 | | - // Create a new static variable to put on the rhs of this |
64 | | - // assignment. When the instruction is dead then this is |
65 | | - // sound. Otherwise this is not sound, but it is |
66 | | - // preferable to having a pointer that could point to |
67 | | - // anything. A better solution would be to use the |
68 | | - // replace_java_nondet pass or similar. |
69 | | - static unsigned long counter = 0UL; |
70 | | - std::stringstream sstr; |
71 | | - sstr << "@__CPROVER_NONDET_dead_replace_" << ++counter; |
72 | | - symbolt symbol; |
73 | | - symbol.type = |
74 | | - side_effect.type().id() == ID_pointer ? |
75 | | - to_pointer_type(side_effect.type()).subtype() : |
76 | | - side_effect.type(); |
77 | | - symbol.name = sstr.str(); |
78 | | - symbol.base_name = symbol.name; |
79 | | - symbol.mode = ID_java; |
80 | | - symbol.pretty_name = symbol.name; |
81 | | - symbol.is_static_lifetime = true; |
82 | | - const_cast<symbol_tablet&>(model.symbol_table).insert(symbol); |
83 | | - replacements.insert({instr_it, symbol.symbol_expr()}); |
84 | | - } |
85 | | - } |
86 | | - } |
87 | | - } |
88 | | - continue; |
| 56 | + next_instr_it, |
| 57 | + ns.lookup(callee_return_value_symbol).symbol_expr() |
| 58 | + }); |
| 59 | + } |
| 60 | + else if(instr_it->type == ASSIGN) |
| 61 | + { |
| 62 | + const code_assignt &assign = to_code_assign(instr_it->code); |
| 63 | + if(assign.rhs().id() != ID_side_effect) |
| 64 | + continue; |
| 65 | + const side_effect_exprt side_effect = to_side_effect_expr(assign.rhs()); |
| 66 | + if(side_effect.get_statement() != ID_nondet) |
| 67 | + continue; |
| 68 | + if(side_effect.type().id() != ID_pointer) |
| 69 | + continue; |
| 70 | + // Create a new static variable to put on the rhs of this |
| 71 | + // assignment. When the instruction is dead then this is sound. |
| 72 | + // Otherwise this is not sound, but it is preferable to having a |
| 73 | + // pointer that could point to anything. A better solution would be |
| 74 | + // to use the convert_nondet pass or similar. |
| 75 | + static unsigned long counter = 0UL; |
| 76 | + std::stringstream sstr; |
| 77 | + sstr << "@__CPROVER_NONDET_dead_replace_" << ++counter; |
| 78 | + symbolt symbol; |
| 79 | + symbol.type = |
| 80 | + side_effect.type().id() == ID_pointer |
| 81 | + ? to_pointer_type(side_effect.type()).subtype() |
| 82 | + : side_effect.type(); |
| 83 | + symbol.name = sstr.str(); |
| 84 | + symbol.base_name = symbol.name; |
| 85 | + symbol.mode = ID_java; |
| 86 | + symbol.pretty_name = symbol.name; |
| 87 | + symbol.is_static_lifetime = true; |
| 88 | + const_cast<symbol_tablet &>(model.symbol_table).insert(symbol); |
| 89 | + replacements.insert({ instr_it, symbol.symbol_expr() }); |
89 | 90 | } |
90 | | - const code_function_callt &fn_call=to_code_function_call(instr_it->code); |
91 | | - if(fn_call.function().id()!=ID_symbol) |
92 | | - continue; |
93 | | - const irep_idt &callee_ident= |
94 | | - to_symbol_expr(fn_call.function()).get_identifier(); |
95 | | - |
96 | | - const code_assignt &asgn=to_code_assign(next_instr_it->code); |
97 | | - if(asgn.rhs().id()!=ID_side_effect) |
98 | | - continue; |
99 | | - const side_effect_exprt see=to_side_effect_expr(asgn.rhs()); |
100 | | - if(see.get_statement()!=ID_nondet) |
101 | | - continue; |
102 | | - replacements.insert( |
103 | | - { |
104 | | - next_instr_it, |
105 | | - ns.lookup(id2string(callee_ident) + RETURN_VALUE_SUFFIX).symbol_expr() |
106 | | - }); |
107 | 91 | } |
108 | 92 | } |
109 | 93 | } |
|
0 commit comments