|
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