CBMC
goto_clean_expr.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Program Transformation
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
11 
12 #include "goto_convert_class.h"
13 
14 #include <util/expr_util.h>
15 #include <util/fresh_symbol.h>
16 #include <util/pointer_expr.h>
17 #include <util/std_expr.h>
18 #include <util/symbol.h>
19 
21  const exprt &expr,
22  goto_programt &dest,
23  const irep_idt &mode)
24 {
25  const source_locationt source_location=expr.find_source_location();
26 
27  symbolt &new_symbol = get_fresh_aux_symbol(
28  expr.type(),
30  "literal",
31  source_location,
32  mode,
33  symbol_table);
35  new_symbol.value=expr;
36 
37  // The value might depend on a variable, thus
38  // generate code for this.
39 
40  symbol_exprt result=new_symbol.symbol_expr();
41  result.add_source_location()=source_location;
42 
43  // The lifetime of compound literals is really that of
44  // the block they are in.
45  if(!new_symbol.is_static_lifetime)
46  copy(code_declt(result), DECL, dest);
47 
48  code_assignt code_assign(result, expr);
49  code_assign.add_source_location()=source_location;
50  convert(code_assign, dest, mode);
51 
52  // now create a 'dead' instruction
53  if(!new_symbol.is_static_lifetime)
54  {
55  code_deadt code_dead(result);
56  targets.scope_stack.add(std::move(code_dead), {});
57  }
58 
59  return result;
60 }
61 
68 {
69  if(
70  expr.id() == ID_side_effect || expr.id() == ID_compound_literal ||
71  expr.id() == ID_comma)
72  {
73  return true;
74  }
75 
76  // We can't flatten quantified expressions by introducing new literals for
77  // conditional expressions. This is because the body of the quantified
78  // may refer to bound variables, which are not visible outside the scope
79  // of the quantifier.
80  //
81  // For example, the following transformation would not be valid:
82  //
83  // forall (i : int) (i == 0 || i > 10)
84  //
85  // transforming to
86  //
87  // g1 = (i == 0)
88  // g2 = (i > 10)
89  // forall (i : int) (g1 || g2)
90  if(expr.id()==ID_forall || expr.id()==ID_exists)
91  return false;
92 
93  for(const auto &op : expr.operands())
94  {
95  if(needs_cleaning(op))
96  return true;
97  }
98 
99  return false;
100 }
101 
104 {
105  PRECONDITION(
106  expr.id() == ID_and || expr.id() == ID_or || expr.id() == ID_implies);
108  expr.is_boolean(),
109  expr.find_source_location(),
110  "'",
111  expr.id(),
112  "' must be Boolean, but got ",
114 
115  // re-write "a ==> b" into a?b:1
116  if(auto implies = expr_try_dynamic_cast<implies_exprt>(expr))
117  {
118  expr = if_exprt{std::move(implies->lhs()),
119  std::move(implies->rhs()),
120  true_exprt{},
121  bool_typet{}};
122  return;
123  }
124 
125  // re-write "a && b" into nested a?b:0
126  // re-write "a || b" into nested a?1:b
127 
128  exprt tmp;
129 
130  if(expr.id()==ID_and)
131  tmp=true_exprt();
132  else // ID_or
133  tmp=false_exprt();
134 
135  exprt::operandst &ops=expr.operands();
136 
137  // start with last one
138  for(exprt::operandst::reverse_iterator
139  it=ops.rbegin();
140  it!=ops.rend();
141  ++it)
142  {
143  exprt &op=*it;
144 
146  op.is_boolean(),
147  "boolean operators must have only boolean operands",
148  expr.find_source_location());
149 
150  if(expr.id()==ID_and)
151  {
152  if_exprt if_e(op, tmp, false_exprt());
153  tmp.swap(if_e);
154  continue;
155  }
156  if(expr.id() == ID_or)
157  {
158  if_exprt if_e(op, true_exprt(), tmp);
159  tmp.swap(if_e);
160  continue;
161  }
162  UNREACHABLE;
163  }
164 
165  expr.swap(tmp);
166 }
167 
169  exprt &expr,
170  goto_programt &dest,
171  const irep_idt &mode,
172  bool result_is_used)
173 {
174  // this cleans:
175  // && || ==> ?: comma (control-dependency)
176  // function calls
177  // object constructors like arrays, string constants, structs
178  // ++ -- (pre and post)
179  // compound assignments
180  // compound literals
181 
182  if(!needs_cleaning(expr))
183  return;
184 
185  if(expr.id() == ID_and || expr.id() == ID_or || expr.id() == ID_implies)
186  {
187  // rewrite into ?:
188  rewrite_boolean(expr);
189 
190  // recursive call
191  clean_expr(expr, dest, mode, result_is_used);
192  return;
193  }
194  else if(expr.id()==ID_if)
195  {
196  // first clean condition
197  clean_expr(to_if_expr(expr).cond(), dest, mode, true);
198 
199  // possibly done now
200  if(!needs_cleaning(to_if_expr(expr).true_case()) &&
201  !needs_cleaning(to_if_expr(expr).false_case()))
202  return;
203 
204  // copy expression
205  if_exprt if_expr=to_if_expr(expr);
206 
208  if_expr.cond().is_boolean(),
209  "condition for an 'if' must be boolean",
210  if_expr.find_source_location());
211 
212  const source_locationt source_location=expr.find_source_location();
213 
214  #if 0
215  // We do some constant-folding here, to mimic
216  // what typical compilers do.
217  {
218  exprt tmp_cond=if_expr.cond();
219  simplify(tmp_cond, ns);
220  if(tmp_cond.is_true())
221  {
222  clean_expr(if_expr.true_case(), dest, result_is_used);
223  expr=if_expr.true_case();
224  return;
225  }
226  else if(tmp_cond.is_false())
227  {
228  clean_expr(if_expr.false_case(), dest, result_is_used);
229  expr=if_expr.false_case();
230  return;
231  }
232  }
233  #endif
234 
235  goto_programt tmp_true;
236  clean_expr(if_expr.true_case(), tmp_true, mode, result_is_used);
237 
238  goto_programt tmp_false;
239  clean_expr(if_expr.false_case(), tmp_false, mode, result_is_used);
240 
241  if(result_is_used)
242  {
243  symbolt &new_symbol =
244  new_tmp_symbol(expr.type(), "if_expr", dest, source_location, mode);
245 
246  code_assignt assignment_true;
247  assignment_true.lhs()=new_symbol.symbol_expr();
248  assignment_true.rhs()=if_expr.true_case();
249  assignment_true.add_source_location()=source_location;
250  convert(assignment_true, tmp_true, mode);
251 
252  code_assignt assignment_false;
253  assignment_false.lhs()=new_symbol.symbol_expr();
254  assignment_false.rhs()=if_expr.false_case();
255  assignment_false.add_source_location()=source_location;
256  convert(assignment_false, tmp_false, mode);
257 
258  // overwrites expr
259  expr=new_symbol.symbol_expr();
260  }
261  else
262  {
263  // preserve the expressions for possible later checks
264  if(if_expr.true_case().is_not_nil())
265  {
266  // add a (void) type cast so that is_skip catches it in case the
267  // expression is just a constant
268  code_expressiont code_expression(
269  typecast_exprt(if_expr.true_case(), empty_typet()));
270  convert(code_expression, tmp_true, mode);
271  }
272 
273  if(if_expr.false_case().is_not_nil())
274  {
275  // add a (void) type cast so that is_skip catches it in case the
276  // expression is just a constant
277  code_expressiont code_expression(
278  typecast_exprt(if_expr.false_case(), empty_typet()));
279  convert(code_expression, tmp_false, mode);
280  }
281 
282  expr=nil_exprt();
283  }
284 
285  // generate guard for argument side-effects
287  if_expr.cond(),
288  source_location,
289  tmp_true,
290  if_expr.true_case().source_location(),
291  tmp_false,
292  if_expr.false_case().source_location(),
293  dest,
294  mode);
295 
296  return;
297  }
298  else if(expr.id()==ID_comma)
299  {
300  if(result_is_used)
301  {
302  exprt result;
303 
304  Forall_operands(it, expr)
305  {
306  bool last=(it==--expr.operands().end());
307 
308  // special treatment for last one
309  if(last)
310  {
311  result.swap(*it);
312  clean_expr(result, dest, mode, true);
313  }
314  else
315  {
316  clean_expr(*it, dest, mode, false);
317 
318  // remember these for later checks
319  if(it->is_not_nil())
320  convert(code_expressiont(*it), dest, mode);
321  }
322  }
323 
324  expr.swap(result);
325  }
326  else // result not used
327  {
328  Forall_operands(it, expr)
329  {
330  clean_expr(*it, dest, mode, false);
331 
332  // remember as expression statement for later checks
333  if(it->is_not_nil())
334  convert(code_expressiont(*it), dest, mode);
335  }
336 
337  expr=nil_exprt();
338  }
339 
340  return;
341  }
342  else if(expr.id()==ID_typecast)
343  {
344  typecast_exprt &typecast = to_typecast_expr(expr);
345 
346  // preserve 'result_is_used'
347  clean_expr(typecast.op(), dest, mode, result_is_used);
348 
349  if(typecast.op().is_nil())
350  expr.make_nil();
351 
352  return;
353  }
354  else if(expr.id()==ID_side_effect)
355  {
356  // some of the side-effects need special treatment!
357  const irep_idt statement=to_side_effect_expr(expr).get_statement();
358 
359  if(statement==ID_gcc_conditional_expression)
360  {
361  // need to do separately
362  remove_gcc_conditional_expression(expr, dest, mode);
363  return;
364  }
365  else if(statement==ID_statement_expression)
366  {
367  // need to do separately to prevent that
368  // the operands of expr get 'cleaned'
370  to_side_effect_expr(expr), dest, mode, result_is_used);
371  return;
372  }
373  else if(statement==ID_assign)
374  {
375  // we do a special treatment for x=f(...)
376  INVARIANT(
377  expr.operands().size() == 2,
378  "side-effect assignment expressions must have two operands");
379 
380  auto &side_effect_assign = to_side_effect_expr_assign(expr);
381 
382  if(
383  side_effect_assign.rhs().id() == ID_side_effect &&
384  to_side_effect_expr(side_effect_assign.rhs()).get_statement() ==
385  ID_function_call)
386  {
387  clean_expr(side_effect_assign.lhs(), dest, mode);
388  exprt lhs = side_effect_assign.lhs();
389 
390  const bool must_use_rhs = assignment_lhs_needs_temporary(lhs);
391  if(must_use_rhs)
392  {
394  to_side_effect_expr_function_call(side_effect_assign.rhs()),
395  dest,
396  mode,
397  true);
398  }
399 
400  // turn into code
401  exprt new_lhs = skip_typecast(lhs);
403  side_effect_assign.rhs(), new_lhs.type());
404  code_assignt assignment(std::move(new_lhs), new_rhs);
405  assignment.add_source_location()=expr.source_location();
406  convert_assign(assignment, dest, mode);
407 
408  if(result_is_used)
409  expr = must_use_rhs ? new_rhs : lhs;
410  else
411  expr.make_nil();
412  return;
413  }
414  }
415  }
416  else if(expr.id()==ID_forall || expr.id()==ID_exists)
417  {
419  !has_subexpr(expr, ID_side_effect),
420  "the front-end should check quantified expressions for side-effects");
421  }
422  else if(expr.id()==ID_address_of)
423  {
424  address_of_exprt &addr = to_address_of_expr(expr);
425  clean_expr_address_of(addr.object(), dest, mode);
426  return;
427  }
428 
429  // TODO: evaluation order
430 
431  Forall_operands(it, expr)
432  clean_expr(*it, dest, mode);
433 
434  if(expr.id()==ID_side_effect)
435  {
437  to_side_effect_expr(expr), dest, mode, result_is_used, false);
438  }
439  else if(expr.id()==ID_compound_literal)
440  {
441  // This is simply replaced by the literal
443  expr.operands().size() == 1, "ID_compound_literal has a single operand");
444  expr = to_unary_expr(expr).op();
445  }
446 }
447 
449  exprt &expr,
450  goto_programt &dest,
451  const irep_idt &mode)
452 {
453  // The address of object constructors can be taken,
454  // which is re-written into the address of a variable.
455 
456  if(expr.id()==ID_compound_literal)
457  {
459  expr.operands().size() == 1, "ID_compound_literal has a single operand");
460  clean_expr(to_unary_expr(expr).op(), dest, mode);
461  expr = make_compound_literal(to_unary_expr(expr).op(), dest, mode);
462  }
463  else if(expr.id()==ID_string_constant)
464  {
465  // Leave for now, but long-term these might become static symbols.
466  // LLVM appears to do precisely that.
467  }
468  else if(expr.id()==ID_index)
469  {
470  index_exprt &index_expr = to_index_expr(expr);
471  clean_expr_address_of(index_expr.array(), dest, mode);
472  clean_expr(index_expr.index(), dest, mode);
473  }
474  else if(expr.id()==ID_dereference)
475  {
477  clean_expr(deref_expr.pointer(), dest, mode);
478  }
479  else if(expr.id()==ID_comma)
480  {
481  // Yes, one can take the address of a comma expression.
482  // Treatment is similar to clean_expr() above.
483 
484  exprt result;
485 
486  Forall_operands(it, expr)
487  {
488  bool last=(it==--expr.operands().end());
489 
490  // special treatment for last one
491  if(last)
492  result.swap(*it);
493  else
494  {
495  clean_expr(*it, dest, mode, false);
496 
497  // get any side-effects
498  if(it->is_not_nil())
499  convert(code_expressiont(*it), dest, mode);
500  }
501  }
502 
503  expr.swap(result);
504 
505  // do again
506  clean_expr_address_of(expr, dest, mode);
507  }
508  else if(expr.id() == ID_side_effect)
509  {
510  remove_side_effect(to_side_effect_expr(expr), dest, mode, true, true);
511  }
512  else
513  Forall_operands(it, expr)
514  clean_expr_address_of(*it, dest, mode);
515 }
516 
518  exprt &expr,
519  goto_programt &dest,
520  const irep_idt &mode)
521 {
522  {
523  auto &binary_expr = to_binary_expr(expr);
524 
525  // first remove side-effects from condition
526  clean_expr(to_binary_expr(expr).op0(), dest, mode);
527 
528  // now we can copy op0 safely
529  if_exprt if_expr(
530  typecast_exprt::conditional_cast(binary_expr.op0(), bool_typet()),
531  binary_expr.op0(),
532  binary_expr.op1(),
533  expr.type());
534  if_expr.add_source_location() = expr.source_location();
535 
536  expr.swap(if_expr);
537  }
538 
539  // there might still be junk in expr.op2()
540  clean_expr(expr, dest, mode);
541 }
@ AUTOMATIC_LOCAL
Allocate local objects with automatic lifetime.
Operator to return the address of an object.
Definition: pointer_expr.h:540
exprt & object()
Definition: pointer_expr.h:549
The Boolean type.
Definition: std_types.h:36
A goto_instruction_codet representing an assignment in the program.
A goto_instruction_codet representing the removal of a local variable going out of scope.
A goto_instruction_codet representing the declaration of a local variable.
codet representation of an expression statement.
Definition: std_code.h:1394
Operator to dereference a pointer.
Definition: pointer_expr.h:834
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 & find_source_location() const
Get a source_locationt from the expression or from its operands (non-recursively).
Definition: expr.cpp:147
std::vector< exprt > operandst
Definition: expr.h:58
bool is_true() const
Return whether the expression is a constant representing true.
Definition: expr.cpp:27
exprt & op1()
Definition: expr.h:136
source_locationt & add_source_location()
Definition: expr.h:236
bool is_boolean() const
Return whether the expression represents a Boolean.
Definition: expr.h:224
const source_locationt & source_location() const
Definition: expr.h:231
bool is_false() const
Return whether the expression is a constant representing false.
Definition: expr.cpp:34
typet & type()
Return the type of the expression.
Definition: expr.h:84
exprt & op0()
Definition: expr.h:133
operandst & operands()
Definition: expr.h:94
The Boolean constant false.
Definition: std_expr.h:3064
void remove_function_call(side_effect_expr_function_callt &expr, goto_programt &dest, const irep_idt &mode, bool result_is_used)
symbol_table_baset & symbol_table
void convert_assign(const code_assignt &code, goto_programt &dest, const irep_idt &mode)
void copy(const codet &code, goto_program_instruction_typet type, goto_programt &dest)
void generate_ifthenelse(const exprt &cond, const source_locationt &, goto_programt &true_case, const source_locationt &, goto_programt &false_case, const source_locationt &, goto_programt &dest, const irep_idt &mode)
if(guard) true_case; else false_case;
std::string tmp_symbol_prefix
void remove_side_effect(side_effect_exprt &expr, goto_programt &dest, const irep_idt &mode, bool result_is_used, bool address_taken)
struct goto_convertt::targetst targets
symbolt & new_tmp_symbol(const typet &type, const std::string &suffix, goto_programt &dest, const source_locationt &, const irep_idt &mode)
void remove_statement_expression(side_effect_exprt &expr, goto_programt &dest, const irep_idt &mode, bool result_is_used)
symbol_exprt make_compound_literal(const exprt &expr, goto_programt &dest, const irep_idt &mode)
void clean_expr(exprt &expr, goto_programt &dest, const irep_idt &mode, bool result_is_used=true)
static bool needs_cleaning(const exprt &expr)
Returns 'true' for expressions that may change the program state.
void clean_expr_address_of(exprt &expr, goto_programt &dest, const irep_idt &mode)
void convert(const codet &code, goto_programt &dest, const irep_idt &mode)
converts 'code' and appends the result to 'dest'
void remove_gcc_conditional_expression(exprt &expr, goto_programt &dest, const irep_idt &mode)
void rewrite_boolean(exprt &dest)
re-write boolean operators into ?:
static bool assignment_lhs_needs_temporary(const exprt &lhs)
A generic container class for the GOTO intermediate representation of one function.
Definition: goto_program.h:73
The trinary if-then-else operator.
Definition: std_expr.h:2370
exprt & true_case()
Definition: std_expr.h:2397
exprt & false_case()
Definition: std_expr.h:2407
exprt & cond()
Definition: std_expr.h:2387
Array index operator.
Definition: std_expr.h:1465
exprt & array()
Definition: std_expr.h:1495
exprt & index()
Definition: std_expr.h:1505
bool is_not_nil() const
Definition: irep.h:368
const irep_idt & id() const
Definition: irep.h:384
void make_nil()
Definition: irep.h:442
void swap(irept &irep)
Definition: irep.h:430
bool is_nil() const
Definition: irep.h:364
mstreamt & result() const
Definition: message.h:409
The NIL expression.
Definition: std_expr.h:3073
void add(const codet &destructor, std::optional< goto_programt::targett > declaration)
Adds a destructor/declaration pair to the current stack, attaching it to the current node.
Definition: scope_tree.cpp:11
const irep_idt & get_statement() const
Definition: std_code.h:1472
Expression to hold a symbol (variable)
Definition: std_expr.h:131
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
exprt value
Initial value of symbol.
Definition: symbol.h:34
The Boolean constant true.
Definition: std_expr.h:3055
Semantic type conversion.
Definition: std_expr.h:2068
static exprt conditional_cast(const exprt &expr, const typet &type)
Definition: std_expr.h:2076
const exprt & op() const
Definition: std_expr.h:391
#define Forall_operands(it, expr)
Definition: expr.h:27
const exprt & skip_typecast(const exprt &expr)
find the expression nested inside typecasts, if any
Definition: expr_util.cpp:219
bool has_subexpr(const exprt &expr, const std::function< bool(const exprt &)> &pred)
returns true if the expression has a subexpression that satisfies pred
Definition: expr_util.cpp:141
Deprecated expression utility functions.
symbolt & get_fresh_aux_symbol(const typet &type, const std::string &name_prefix, const std::string &basename_prefix, const source_locationt &source_location, const irep_idt &symbol_mode, const namespacet &ns, symbol_table_baset &symbol_table)
Installs a fresh-named symbol with respect to the given namespace ns with the requested name pattern ...
Fresh auxiliary symbol creation.
Program Transformation.
@ DECL
Definition: goto_program.h:47
API to expression classes for Pointers.
const dereference_exprt & to_dereference_expr(const exprt &expr)
Cast an exprt to a dereference_exprt.
Definition: pointer_expr.h:890
const address_of_exprt & to_address_of_expr(const exprt &expr)
Cast an exprt to an address_of_exprt.
Definition: pointer_expr.h:577
exprt deref_expr(const exprt &expr)
Wraps a given expression into a dereference_exprt unless it is an address_of_exprt in which case it j...
bool simplify(exprt &expr, const namespacet &ns)
#define PRECONDITION_WITH_DIAGNOSTICS(CONDITION,...)
Definition: invariant.h:464
#define UNREACHABLE
This should be used to mark dead code.
Definition: invariant.h:525
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:534
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
#define DATA_INVARIANT_WITH_DIAGNOSTICS(CONDITION, REASON,...)
Definition: invariant.h:535
side_effect_exprt & to_side_effect_expr(exprt &expr)
Definition: std_code.h:1506
side_effect_expr_function_callt & to_side_effect_expr_function_call(exprt &expr)
Definition: std_code.h:1739
side_effect_expr_assignt & to_side_effect_expr_assign(exprt &expr)
Definition: std_code.h:1618
API to expression classes.
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition: std_expr.h:2450
const typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
Definition: std_expr.h:2102
const binary_exprt & to_binary_expr(const exprt &expr)
Cast an exprt to a binary_exprt.
Definition: std_expr.h:715
const unary_exprt & to_unary_expr(const exprt &expr)
Cast an exprt to a unary_exprt.
Definition: std_expr.h:426
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1533
Symbol table entry.