CBMC
remove_returns.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Remove function return values
4 
5 Author: Daniel Kroening
6 
7 Date: 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 {
29 public:
30  explicit remove_returnst(symbol_table_baset &_symbol_table):
31  symbol_table(_symbol_table)
32  {
33  }
34 
35  void operator()(
36  goto_functionst &goto_functions);
37 
38  void operator()(
39  goto_model_functiont &model_function,
40  function_is_stubt function_is_stub);
41 
42  void restore(
43  goto_functionst &goto_functions);
44 
45 protected:
47 
48  void replace_returns(
49  const irep_idt &function_id,
51 
52  bool do_function_calls(
53  function_is_stubt function_is_stub,
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>
63  get_or_create_return_value_symbol(const irep_idt &function_id);
64 };
65 
66 std::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();
72  if(symbol_table.has_symbol(symbol_name))
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 =
85  id2string(function_symbol.base_name) + RETURN_VALUE_SUFFIX;
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:
93  new_symbol.type.set(ID_C_no_initialization_required, true);
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
107  symbolt &function_symbol = *symbol_table.get_writeable(function_id);
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);
133  instruction.clear(goto_program_instruction_typet::ASSIGN);
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 
150  function_is_stubt function_is_stub,
151  goto_programt &goto_program)
152 {
153  bool requires_update = false;
154 
155  Forall_goto_program_instructions(i_it, goto_program)
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?
168  if(does_function_call_return(*i_it))
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 
195  goto_programt::targett t_a = goto_program.insert_after(
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() &&
230  function_id != CPROVER_PREFIX SHADOW_MEMORY_FIELD_DECL &&
231  function_id != CPROVER_PREFIX SHADOW_MEMORY_GET_FIELD &&
233  };
234 
235  replace_returns(gf_entry.first, gf_entry.second);
236  if(do_function_calls(function_is_stub, gf_entry.second.body))
237  goto_functions.compute_location_numbers(gf_entry.second.body);
238  }
239 }
240 
242  goto_model_functiont &model_function,
243  function_is_stubt function_is_stub)
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 
279  goto_model_functiont &goto_model_function,
280  function_is_stubt function_is_stub)
281 {
282  remove_returnst rr(goto_model_function.get_symbol_table());
283  rr(goto_model_function, function_is_stub);
284 }
285 
287 void remove_returns(goto_modelt &goto_model)
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=
301  symbol_table.symbols.find(rv_name);
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;
307  symbol_table.erase(rv_it);
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 
342  Forall_goto_program_instructions(i_it, goto_program)
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 
401 void restore_returns(goto_modelt &goto_model)
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 
413 return_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 
421 {
423 }
424 
425 bool 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 }
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.
const typet & return_type() const
Definition: std_types.h:689
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
typet & type()
Return the type of the expression.
Definition: expr.h:84
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
const irep_idt & get_function_id()
Get function id.
Definition: goto_model.h:239
goto_functionst::goto_functiont & get_goto_function()
Get GOTO function.
Definition: goto_model.h:232
void compute_location_numbers()
Re-number our goto_function.
Definition: goto_model.h:216
journalling_symbol_tablet & get_symbol_table()
Get symbol table.
Definition: goto_model.h:225
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.
Definition: goto_program.h:181
const exprt & call_lhs() const
Get the lhs of a FUNCTION_CALL (may be nil)
Definition: goto_program.h:286
const exprt & call_function() const
Get the function that is called for FUNCTION_CALL.
Definition: goto_program.h:272
A generic container class for the GOTO intermediate representation of one function.
Definition: goto_program.h:73
instructionst instructions
The list of instructions in the goto program.
Definition: goto_program.h:622
static instructiont make_set_return_value(exprt return_value, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:874
static instructiont make_dead(const symbol_exprt &symbol, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:971
instructionst::iterator targett
Definition: goto_program.h:614
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.
Definition: goto_program.h:708
void set(const irep_idt &name, const irep_idt &value)
Definition: irep.h:408
bool is_not_nil() const
Definition: irep.h:368
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Definition: namespace.cpp:148
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 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.
const symbolt & lookup_ref(const irep_idt &name) const
Find a symbol in the symbol table for read-only access.
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.
virtual symbolt * get_writeable(const irep_idt &name)=0
Find a symbol in the symbol table for read-write access.
Symbol table entry.
Definition: symbol.h:28
irep_idt base_name
Base (non-scoped) name.
Definition: symbol.h:46
irep_idt module
Name of module the symbol belongs to.
Definition: symbol.h:43
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 mode
Language mode.
Definition: symbol.h:49
static exprt conditional_cast(const exprt &expr, const typet &type)
Definition: std_expr.h:2076
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
Definition: goto_program.h:46
const std::string & id2string(const irep_idt &d)
Definition: irep.h:40
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
Definition: remove_skip.cpp:87
Program Transformation.
Symex Shadow Memory Instrumentation.
#define SHADOW_MEMORY_SET_FIELD
Definition: shadow_memory.h:25
#define SHADOW_MEMORY_GET_FIELD
Definition: shadow_memory.h:24
#define SHADOW_MEMORY_FIELD_DECL
Definition: shadow_memory.h:21
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