CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
remove_returns.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Remove function return values
4
5Author: Daniel Kroening
6
7Date: September 2009
8
9\*******************************************************************/
10
13
14#include "remove_returns.h"
15
16#include <util/std_code.h>
17#include <util/std_expr.h>
18#include <util/suffix.h>
19
21
22#include "goto_model.h"
23#include "remove_skip.h"
24
25#define RETURN_VALUE_SUFFIX "#return_value"
26
28{
29public:
34
35 void operator()(
36 goto_functionst &goto_functions);
37
38 void operator()(
41
42 void restore(
43 goto_functionst &goto_functions);
44
45protected:
47
48 void replace_returns(
49 const irep_idt &function_id,
51
54 goto_programt &goto_program);
55
56 bool
57 restore_returns(const irep_idt &function_id, goto_programt &goto_program);
58
60 goto_programt &goto_program);
61
62 std::optional<symbol_exprt>
64};
65
66std::optional<symbol_exprt>
68{
69 const namespacet ns(symbol_table);
70 const auto symbol_expr = return_value_symbol(function_id, ns);
71 const auto symbol_name = symbol_expr.get_identifier();
73 return symbol_expr;
74
75 const symbolt &function_symbol = symbol_table.lookup_ref(function_id);
76 const typet &return_type = to_code_type(function_symbol.type).return_type();
77
78 if(return_type == empty_typet())
79 return {};
80
81 auxiliary_symbolt new_symbol;
82 new_symbol.is_static_lifetime = true;
83 new_symbol.module = function_symbol.module;
84 new_symbol.base_name =
86 new_symbol.name = symbol_name;
87 new_symbol.mode = function_symbol.mode;
88 // If we're creating this for the first time, the target function cannot have
89 // been remove_return'd yet, so this will still be the "true" return type:
90 new_symbol.type = return_type;
91 // Return-value symbols will always be written before they are read, so there
92 // is no need for __CPROVER_initialize to do anything:
94
95 symbol_table.add(new_symbol);
96 return new_symbol.symbol_expr();
97}
98
103 const irep_idt &function_id,
105{
106 // look up the function symbol
108
109 // returns something but void?
110 if(to_code_type(function_symbol.type).return_type() == empty_typet())
111 return;
112
113 // add return_value symbol to symbol_table, if not already created:
114 const auto return_symbol = get_or_create_return_value_symbol(function_id);
115
116 goto_programt &goto_program = function.body;
117
118 for(auto &instruction : goto_program.instructions)
119 {
120 if(instruction.is_set_return_value())
121 {
122 INVARIANT(
123 instruction.code().operands().size() == 1,
124 "return instructions should have one operand");
125
126 if(return_symbol.has_value())
127 {
128 // replace "return x;" by "fkt#return_value=x;"
129 code_assignt assignment(*return_symbol, instruction.return_value());
130
131 // now turn the `return' into `assignment'
132 auto labels = std::move(instruction.labels);
134 instruction.code_nonconst() = std::move(assignment);
135 instruction.labels = std::move(labels);
136 }
137 else
138 instruction.turn_into_skip();
139 }
140 }
141}
142
151 goto_programt &goto_program)
152{
153 bool requires_update = false;
154
156 {
157 if(i_it->is_function_call())
158 {
159 INVARIANT(
160 i_it->call_function().id() == ID_symbol,
161 "indirect function calls should have been removed prior to running "
162 "remove-returns");
163
164 const irep_idt function_id =
165 to_symbol_expr(i_it->call_function()).get_identifier();
166
167 // Do we return anything?
169 {
170 // replace "lhs=f(...)" by
171 // "f(...); lhs=f#return_value; DEAD f#return_value;"
172
173 exprt rhs;
174
175 bool is_stub = function_is_stub(function_id);
176 std::optional<symbol_exprt> return_value;
177
178 if(!is_stub)
179 return_value = get_or_create_return_value_symbol(function_id);
180
181 if(return_value.has_value())
182 {
183 // The return type in the definition of the function may differ
184 // from the return type in the declaration. We therefore do a
185 // cast.
187 *return_value, i_it->call_lhs().type());
188 }
189 else
190 {
192 i_it->call_lhs().type(), i_it->source_location());
193 }
194
196 i_it,
198 code_assignt(i_it->call_lhs(), rhs), i_it->source_location()));
199
200 // fry the previous assignment
201 i_it->call_lhs().make_nil();
202
203 if(return_value.has_value())
204 {
205 goto_program.insert_after(
206 t_a,
207 goto_programt::make_dead(*return_value, i_it->source_location()));
208 }
209
210 requires_update = true;
211 }
212 }
213 }
214
215 return requires_update;
216}
217
219{
220 for(auto &gf_entry : goto_functions.function_map)
221 {
222 // NOLINTNEXTLINE
223 auto function_is_stub = [&goto_functions](const irep_idt &function_id) {
224 auto findit = goto_functions.function_map.find(function_id);
225 INVARIANT(
226 findit != goto_functions.function_map.end(),
227 "called function `" + id2string(function_id) +
228 "' should have an entry in the function map");
229 return !findit->second.body_available() &&
233 };
234
235 replace_returns(gf_entry.first, gf_entry.second);
237 goto_functions.compute_location_numbers(gf_entry.second.body);
238 }
239}
240
244{
245 goto_functionst::goto_functiont &goto_function =
246 model_function.get_goto_function();
247
248 // If this is a stub it doesn't have a corresponding #return_value,
249 // not any return instructions to alter:
250 if(goto_function.body.empty())
251 return;
252
253 replace_returns(model_function.get_function_id(), goto_function);
254 if(do_function_calls(function_is_stub, goto_function.body))
255 model_function.compute_location_numbers();
256}
257
260 symbol_table_baset &symbol_table,
261 goto_functionst &goto_functions)
262{
263 remove_returnst rr(symbol_table);
264 rr(goto_functions);
265}
266
285
288{
289 remove_returnst rr(goto_model.symbol_table);
290 rr(goto_model.goto_functions);
291}
292
295 const irep_idt &function_id,
296 goto_programt &goto_program)
297{
298 // do we have X#return_value?
299 auto rv_name = return_value_identifier(function_id);
300 symbol_tablet::symbolst::const_iterator rv_it=
302 if(rv_it==symbol_table.symbols.end())
303 return true;
304
305 // remove the return_value symbol from the symbol_table
306 irep_idt rv_name_id=rv_it->second.name;
308
309 bool did_something = false;
310
311 for(auto &instruction : goto_program.instructions)
312 {
313 if(instruction.is_assign())
314 {
315 if(
316 instruction.assign_lhs().id() != ID_symbol ||
317 to_symbol_expr(instruction.assign_lhs()).get_identifier() != rv_name_id)
318 {
319 continue;
320 }
321
322 // replace "fkt#return_value=x;" by "return x;"
323 const exprt rhs = instruction.assign_rhs();
325 rhs, instruction.source_location());
326 did_something = true;
327 }
328 }
329
330 if(did_something)
331 remove_skip(goto_program);
332
333 return false;
334}
335
338 goto_programt &goto_program)
339{
341
343 {
344 if(i_it->is_function_call())
345 {
346 // ignore function pointers
347 if(i_it->call_function().id() != ID_symbol)
348 continue;
349
350 const irep_idt function_id =
351 to_symbol_expr(i_it->call_function()).get_identifier();
352
353 // find "f(...); lhs=f#return_value; DEAD f#return_value;"
354 // and revert to "lhs=f(...);"
355 goto_programt::instructionst::iterator next = std::next(i_it);
356
357 INVARIANT(
358 next!=goto_program.instructions.end(),
359 "non-void function call must be followed by #return_value read");
360
361 if(!next->is_assign())
362 continue;
363
364 const auto rv_symbol = return_value_symbol(function_id, ns);
365 if(next->assign_rhs() != rv_symbol)
366 continue;
367
368 // restore the previous assignment
369 i_it->call_lhs() = next->assign_lhs();
370
371 // remove the assignment and subsequent dead
372 // i_it remains valid
373 next=goto_program.instructions.erase(next);
374 INVARIANT(
375 next!=goto_program.instructions.end() && next->is_dead(),
376 "read from #return_value should be followed by DEAD #return_value");
377 // i_it remains valid
378 goto_program.instructions.erase(next);
379 }
380 }
381}
382
384{
385 // restore all types first
386 bool unmodified=true;
387 for(auto &gf_entry : goto_functions.function_map)
388 {
389 unmodified =
390 restore_returns(gf_entry.first, gf_entry.second.body) && unmodified;
391 }
392
393 if(!unmodified)
394 {
395 for(auto &gf_entry : goto_functions.function_map)
396 undo_function_calls(gf_entry.second.body);
397 }
398}
399
402{
403 remove_returnst rr(goto_model.symbol_table);
404 rr.restore(goto_model.goto_functions);
405}
406
408{
409 return id2string(identifier) + RETURN_VALUE_SUFFIX;
410}
411
413return_value_symbol(const irep_idt &identifier, const namespacet &ns)
414{
415 const symbolt &function_symbol = ns.lookup(identifier);
416 const typet &return_type = to_code_type(function_symbol.type).return_type();
417 return symbol_exprt(return_value_identifier(identifier), return_type);
418}
419
424
425bool is_return_value_symbol(const symbol_exprt &symbol_expr)
426{
427 return is_return_value_identifier(symbol_expr.get_identifier());
428}
429
431{
432 return to_code_type(function_call.call_function().type()).return_type() !=
433 empty_typet() &&
434 function_call.call_lhs().is_not_nil();
435}
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Internally generated symbol table entryThis is a symbol generated as part of translation to or modifi...
Definition symbol.h:153
A goto_instruction_codet representing an assignment in the program.
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
The empty type.
Definition std_types.h:51
Base class for all expressions.
Definition expr.h:56
const source_locationt & source_location() const
Definition expr.h:231
A collection of goto functions.
function_mapt function_map
::goto_functiont goto_functiont
void compute_location_numbers()
Interface providing access to a single function in a GOTO model, plus its associated symbol table.
Definition goto_model.h:190
symbol_tablet symbol_table
Symbol table.
Definition goto_model.h:31
goto_functionst goto_functions
GOTO functions.
Definition goto_model.h:34
This class represents an instruction in the GOTO intermediate representation.
const exprt & call_lhs() const
Get the lhs of a FUNCTION_CALL (may be nil)
const exprt & call_function() const
Get the function that is called for FUNCTION_CALL.
A generic container class for the GOTO intermediate representation of one function.
instructionst instructions
The list of instructions in the goto program.
static instructiont make_set_return_value(exprt return_value, const source_locationt &l=source_locationt::nil())
static instructiont make_dead(const symbol_exprt &symbol, const source_locationt &l=source_locationt::nil())
instructionst::iterator targett
static instructiont make_assignment(const code_assignt &_code, const source_locationt &l=source_locationt::nil())
Create an assignment instruction.
targett insert_after(const_targett target)
Insertion after the instruction pointed-to by the given instruction iterator target.
void set(const irep_idt &name, const irep_idt &value)
Definition irep.h:412
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
bool restore_returns(const irep_idt &function_id, goto_programt &goto_program)
turns an assignment to fkt::return_value back into 'return x'
void operator()(goto_functionst &goto_functions)
std::optional< symbol_exprt > get_or_create_return_value_symbol(const irep_idt &function_id)
symbol_table_baset & symbol_table
remove_returnst(symbol_table_baset &_symbol_table)
void undo_function_calls(goto_programt &goto_program)
turns f(...); lhs=f::return_value; into lhs=f(...)
void replace_returns(const irep_idt &function_id, goto_functionst::goto_functiont &function)
turns 'return x' into an assignment to fkt::return_value
void restore(goto_functionst &goto_functions)
bool do_function_calls(function_is_stubt function_is_stub, goto_programt &goto_program)
turns x=f(...) into f(...); lhs=f::return_value;
A side_effect_exprt that returns a non-deterministically chosen value.
Definition std_code.h:1520
Expression to hold a symbol (variable)
Definition std_expr.h:131
const irep_idt & get_identifier() const
Definition std_expr.h:160
The symbol table base class interface.
virtual symbolt * get_writeable(const irep_idt &name)=0
Find a symbol in the symbol table for read-write access.
virtual void erase(const symbolst::const_iterator &entry)=0
Remove a symbol from the symbol table.
const symbolst & symbols
Read-only field, used to look up symbols given their names.
bool has_symbol(const irep_idt &name) const
Check whether a symbol exists in the symbol table.
bool add(const symbolt &symbol)
Add a new symbol to the symbol table.
const symbolt & lookup_ref(const irep_idt &name) const
Find a symbol in the symbol table for read-only access.
Symbol table entry.
Definition symbol.h:28
bool is_static_lifetime
Definition symbol.h:70
class symbol_exprt symbol_expr() const
Produces a symbol_exprt for a symbol.
Definition symbol.cpp:121
typet type
Type of symbol.
Definition symbol.h:31
irep_idt name
The unique identifier.
Definition symbol.h:40
irep_idt irep_idt base_name
Name of module the symbol belongs to.
Definition symbol.h:46
irep_idt mode
Language mode.
Definition symbol.h:49
static exprt conditional_cast(const exprt &expr, const typet &type)
Definition std_expr.h:2081
The type of an expression, extends irept.
Definition type.h:29
#define CPROVER_PREFIX
Symbol Table + CFG.
#define Forall_goto_program_instructions(it, program)
@ ASSIGN
const std::string & id2string(const irep_idt &d)
Definition irep.h:44
void restore_returns(goto_modelt &goto_model)
restores return statements
#define RETURN_VALUE_SUFFIX
symbol_exprt return_value_symbol(const irep_idt &identifier, const namespacet &ns)
produces the symbol that is used to store the return value of the function with the given identifier
bool is_return_value_identifier(const irep_idt &id)
Returns true if id is a special return-value symbol produced by return_value_identifier.
bool does_function_call_return(const goto_programt::instructiont &function_call)
Check if the function_call returns anything.
void remove_returns(symbol_table_baset &symbol_table, goto_functionst &goto_functions)
removes returns
bool is_return_value_symbol(const symbol_exprt &symbol_expr)
Returns true if symbol_expr is a special return-value symbol produced by return_value_symbol.
irep_idt return_value_identifier(const irep_idt &identifier)
produces the identifier that is used to store the return value of the function with the given identif...
Replace function returns by assignments to global variables.
std::function< bool(const irep_idt &)> function_is_stubt
void remove_skip(goto_programt &goto_program, goto_programt::targett begin, goto_programt::targett end)
remove unnecessary skip statements
Program Transformation.
Symex Shadow Memory Instrumentation.
#define SHADOW_MEMORY_SET_FIELD
#define SHADOW_MEMORY_GET_FIELD
#define SHADOW_MEMORY_FIELD_DECL
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
API to expression classes.
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:272
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition std_types.h:788
bool has_suffix(const std::string &s, const std::string &suffix)
Definition suffix.h:17