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 
20 #include "destructor.h"
21 
23  const exprt &expr,
24  goto_programt &dest,
25  const irep_idt &mode)
26 {
27  const source_locationt source_location = expr.find_source_location();
28 
29  symbolt &new_symbol = get_fresh_aux_symbol(
30  expr.type(),
32  "literal",
33  source_location,
34  mode,
35  symbol_table);
37  new_symbol.value = expr;
38 
39  // The value might depend on a variable, thus
40  // generate code for this.
41 
42  symbol_exprt result = new_symbol.symbol_expr();
43  result.add_source_location() = source_location;
44 
45  // The lifetime of compound literals is really that of
46  // the block they are in.
47  if(!new_symbol.is_static_lifetime)
48  copy(code_declt(result), DECL, dest);
49 
50  code_assignt code_assign(result, expr);
51  code_assign.add_source_location() = source_location;
52  convert(code_assign, dest, mode);
53 
54  // now create a 'dead' instruction
55  if(!new_symbol.is_static_lifetime)
56  {
57  code_deadt code_dead(result);
58  targets.scope_stack.add(std::move(code_dead), {});
59  }
60 
61  return result;
62 }
63 
70 {
71  if(
72  expr.id() == ID_side_effect || expr.id() == ID_compound_literal ||
73  expr.id() == ID_comma)
74  {
75  return true;
76  }
77 
78  // We can't flatten quantified expressions by introducing new literals for
79  // conditional expressions. This is because the body of the quantified
80  // may refer to bound variables, which are not visible outside the scope
81  // of the quantifier.
82  //
83  // For example, the following transformation would not be valid:
84  //
85  // forall (i : int) (i == 0 || i > 10)
86  //
87  // transforming to
88  //
89  // g1 = (i == 0)
90  // g2 = (i > 10)
91  // forall (i : int) (g1 || g2)
92  if(expr.id() == ID_forall || expr.id() == ID_exists)
93  return false;
94 
95  for(const auto &op : expr.operands())
96  {
97  if(needs_cleaning(op))
98  return true;
99  }
100 
101  return false;
102 }
103 
106 {
107  PRECONDITION(
108  expr.id() == ID_and || expr.id() == ID_or || expr.id() == ID_implies);
110  expr.is_boolean(),
111  expr.find_source_location(),
112  "'",
113  expr.id(),
114  "' must be Boolean, but got ",
116 
117  const source_locationt source_location = expr.find_source_location();
118 
119  // re-write "a ==> b" into a?b:1
120  if(auto implies = expr_try_dynamic_cast<implies_exprt>(expr))
121  {
122  expr = if_exprt{
123  std::move(implies->lhs()),
124  std::move(implies->rhs()),
125  true_exprt{}.with_source_location(source_location),
126  bool_typet{}};
127  return;
128  }
129 
130  // re-write "a && b" into nested a?b:0
131  // re-write "a || b" into nested a?1:b
132 
133  exprt tmp;
134 
135  if(expr.id() == ID_and)
136  tmp = true_exprt();
137  else // ID_or
138  tmp = false_exprt();
139 
140  tmp.add_source_location() = source_location;
141 
142  exprt::operandst &ops = expr.operands();
143 
144  // start with last one
145  for(exprt::operandst::reverse_iterator it = ops.rbegin(); it != ops.rend();
146  ++it)
147  {
148  exprt &op = *it;
149 
151  op.is_boolean(),
152  "boolean operators must have only boolean operands",
153  source_location);
154 
155  if(expr.id() == ID_and)
156  {
157  exprt if_e =
158  if_exprt{op, tmp, false_exprt{}.with_source_location(source_location)}
159  .with_source_location(source_location);
160  tmp.swap(if_e);
161  continue;
162  }
163  if(expr.id() == ID_or)
164  {
165  exprt if_e =
166  if_exprt{op, true_exprt{}.with_source_location(source_location), tmp}
167  .with_source_location(source_location);
168  tmp.swap(if_e);
169  continue;
170  }
171  UNREACHABLE;
172  }
173 
174  expr.swap(tmp);
175 }
176 
178  exprt &expr,
179  const irep_idt &mode,
180  bool result_is_used)
181 {
182  // this cleans:
183  // && || ==> ?: comma (control-dependency)
184  // function calls
185  // object constructors like arrays, string constants, structs
186  // ++ -- (pre and post)
187  // compound assignments
188  // compound literals
189 
190  if(!needs_cleaning(expr))
191  return {};
192 
193  if(expr.id() == ID_and || expr.id() == ID_or || expr.id() == ID_implies)
194  {
195  // rewrite into ?:
196  rewrite_boolean(expr);
197 
198  // recursive call
199  return clean_expr(expr, mode, result_is_used);
200  }
201  else if(expr.id() == ID_if)
202  {
203  // first clean condition
204  clean_expr_resultt side_effects =
205  clean_expr(to_if_expr(expr).cond(), mode, true);
206 
207  // possibly done now
208  if(
209  !needs_cleaning(to_if_expr(expr).true_case()) &&
210  !needs_cleaning(to_if_expr(expr).false_case()))
211  {
212  return side_effects;
213  }
214 
215  // copy expression
216  if_exprt if_expr = to_if_expr(expr);
217 
219  if_expr.cond().is_boolean(),
220  "condition for an 'if' must be boolean",
221  if_expr.find_source_location());
222 
223  const source_locationt source_location = expr.find_source_location();
224 
225 #if 0
226  // We do some constant-folding here, to mimic
227  // what typical compilers do.
228  {
229  exprt tmp_cond=if_expr.cond();
230  simplify(tmp_cond, ns);
231  if(tmp_cond.is_true())
232  {
233  clean_expr(if_expr.true_case(), dest, result_is_used);
234  expr=if_expr.true_case();
235  return;
236  }
237  else if(tmp_cond.is_false())
238  {
239  clean_expr(if_expr.false_case(), dest, result_is_used);
240  expr=if_expr.false_case();
241  return;
242  }
243  }
244 #endif
245 
246  clean_expr_resultt tmp_true(
247  clean_expr(if_expr.true_case(), mode, result_is_used));
248 
249  clean_expr_resultt tmp_false(
250  clean_expr(if_expr.false_case(), mode, result_is_used));
251 
252  if(result_is_used)
253  {
254  symbolt &new_symbol = new_tmp_symbol(
255  expr.type(),
256  "if_expr",
257  side_effects.side_effects,
258  source_location,
259  mode);
260 
261  code_assignt assignment_true;
262  assignment_true.lhs() = new_symbol.symbol_expr();
263  assignment_true.rhs() = if_expr.true_case();
264  assignment_true.add_source_location() = source_location;
265  convert(assignment_true, tmp_true.side_effects, mode);
266 
267  code_assignt assignment_false;
268  assignment_false.lhs() = new_symbol.symbol_expr();
269  assignment_false.rhs() = if_expr.false_case();
270  assignment_false.add_source_location() = source_location;
271  convert(assignment_false, tmp_false.side_effects, mode);
272 
273  // overwrites expr
274  expr = new_symbol.symbol_expr();
275  }
276  else
277  {
278  // preserve the expressions for possible later checks
279  if(if_expr.true_case().is_not_nil())
280  {
281  // add a (void) type cast so that is_skip catches it in case the
282  // expression is just a constant
283  code_expressiont code_expression(
284  typecast_exprt(if_expr.true_case(), empty_typet()));
285  convert(code_expression, tmp_true.side_effects, mode);
286  }
287 
288  if(if_expr.false_case().is_not_nil())
289  {
290  // add a (void) type cast so that is_skip catches it in case the
291  // expression is just a constant
292  code_expressiont code_expression(
293  typecast_exprt(if_expr.false_case(), empty_typet()));
294  convert(code_expression, tmp_false.side_effects, mode);
295  }
296 
297  expr = nil_exprt();
298  }
299 
300  // generate guard for argument side-effects
302  if_expr.cond(),
303  source_location,
304  tmp_true.side_effects,
305  if_expr.true_case().source_location(),
306  tmp_false.side_effects,
307  if_expr.false_case().source_location(),
308  side_effects.side_effects,
309  mode);
310 
311  destruct_locals(tmp_false.temporaries, side_effects.side_effects, ns);
312  destruct_locals(tmp_true.temporaries, side_effects.side_effects, ns);
313  destruct_locals(side_effects.temporaries, side_effects.side_effects, ns);
314  side_effects.temporaries.clear();
315 
316  if(expr.is_not_nil())
317  side_effects.add_temporary(to_symbol_expr(expr).get_identifier());
318 
319  return side_effects;
320  }
321  else if(expr.id() == ID_comma)
322  {
323  clean_expr_resultt side_effects;
324 
325  if(result_is_used)
326  {
327  exprt result;
328 
329  Forall_operands(it, expr)
330  {
331  bool last = (it == --expr.operands().end());
332 
333  // special treatment for last one
334  if(last)
335  {
336  result.swap(*it);
337  side_effects.add(clean_expr(result, mode, true));
338  }
339  else
340  {
341  side_effects.add(clean_expr(*it, mode, false));
342 
343  // remember these for later checks
344  if(it->is_not_nil())
345  convert(code_expressiont(*it), side_effects.side_effects, mode);
346  }
347  }
348 
349  expr.swap(result);
350  }
351  else // result not used
352  {
353  Forall_operands(it, expr)
354  {
355  side_effects.add(clean_expr(*it, mode, false));
356 
357  // remember as expression statement for later checks
358  if(it->is_not_nil())
359  convert(code_expressiont(*it), side_effects.side_effects, mode);
360  }
361 
362  expr = nil_exprt();
363  }
364 
365  return side_effects;
366  }
367  else if(expr.id() == ID_typecast)
368  {
369  typecast_exprt &typecast = to_typecast_expr(expr);
370 
371  // preserve 'result_is_used'
372  clean_expr_resultt side_effects =
373  clean_expr(typecast.op(), mode, result_is_used);
374 
375  if(typecast.op().is_nil())
376  expr.make_nil();
377 
378  return side_effects;
379  }
380  else if(expr.id() == ID_side_effect)
381  {
382  // some of the side-effects need special treatment!
383  const irep_idt statement = to_side_effect_expr(expr).get_statement();
384 
385  if(statement == ID_gcc_conditional_expression)
386  {
387  // need to do separately
388  return remove_gcc_conditional_expression(expr, mode);
389  }
390  else if(statement == ID_statement_expression)
391  {
392  // need to do separately to prevent that
393  // the operands of expr get 'cleaned'
395  to_side_effect_expr(expr), mode, result_is_used);
396  }
397  else if(statement == ID_assign)
398  {
399  // we do a special treatment for x=f(...)
400  INVARIANT(
401  expr.operands().size() == 2,
402  "side-effect assignment expressions must have two operands");
403 
404  auto &side_effect_assign = to_side_effect_expr_assign(expr);
405 
406  if(
407  side_effect_assign.rhs().id() == ID_side_effect &&
408  to_side_effect_expr(side_effect_assign.rhs()).get_statement() ==
409  ID_function_call)
410  {
411  clean_expr_resultt side_effects =
412  clean_expr(side_effect_assign.lhs(), mode);
413  exprt lhs = side_effect_assign.lhs();
414 
415  const bool must_use_rhs = assignment_lhs_needs_temporary(lhs);
416  if(must_use_rhs)
417  {
418  side_effects.add(remove_function_call(
419  to_side_effect_expr_function_call(side_effect_assign.rhs()),
420  mode,
421  true));
422  }
423 
424  // turn into code
425  exprt new_lhs = skip_typecast(lhs);
427  side_effect_assign.rhs(), new_lhs.type());
428  code_assignt assignment(std::move(new_lhs), new_rhs);
429  assignment.add_source_location() = expr.source_location();
430  convert_assign(assignment, side_effects.side_effects, mode);
431 
432  if(result_is_used)
433  expr = must_use_rhs ? new_rhs : lhs;
434  else
435  expr.make_nil();
436 
437  return side_effects;
438  }
439  }
440  }
441  else if(expr.id() == ID_forall || expr.id() == ID_exists)
442  {
444  !has_subexpr(expr, ID_side_effect),
445  "the front-end should check quantified expressions for side-effects");
446  }
447  else if(expr.id() == ID_address_of)
448  {
449  address_of_exprt &addr = to_address_of_expr(expr);
450  return clean_expr_address_of(addr.object(), mode);
451  }
452 
453  clean_expr_resultt side_effects;
454 
455  // TODO: evaluation order
456 
457  Forall_operands(it, expr)
458  side_effects.add(clean_expr(*it, mode));
459 
460  if(expr.id() == ID_side_effect)
461  {
462  side_effects.add(remove_side_effect(
463  to_side_effect_expr(expr), mode, result_is_used, false));
464  }
465  else if(expr.id() == ID_compound_literal)
466  {
467  // This is simply replaced by the literal
469  expr.operands().size() == 1, "ID_compound_literal has a single operand");
470  expr = to_unary_expr(expr).op();
471  }
472 
473  return side_effects;
474 }
475 
478 {
479  clean_expr_resultt side_effects;
480 
481  // The address of object constructors can be taken,
482  // which is re-written into the address of a variable.
483 
484  if(expr.id() == ID_compound_literal)
485  {
487  expr.operands().size() == 1, "ID_compound_literal has a single operand");
488  side_effects.add(clean_expr(to_unary_expr(expr).op(), mode));
489  expr = make_compound_literal(
490  to_unary_expr(expr).op(), side_effects.side_effects, mode);
491  }
492  else if(expr.id() == ID_string_constant)
493  {
494  // Leave for now, but long-term these might become static symbols.
495  // LLVM appears to do precisely that.
496  }
497  else if(expr.id() == ID_index)
498  {
499  index_exprt &index_expr = to_index_expr(expr);
500  side_effects.add(clean_expr_address_of(index_expr.array(), mode));
501  side_effects.add(clean_expr(index_expr.index(), mode));
502  }
503  else if(expr.id() == ID_dereference)
504  {
506  side_effects.add(clean_expr(deref_expr.pointer(), mode));
507  }
508  else if(expr.id() == ID_comma)
509  {
510  // Yes, one can take the address of a comma expression.
511  // Treatment is similar to clean_expr() above.
512 
513  exprt result;
514 
515  Forall_operands(it, expr)
516  {
517  bool last = (it == --expr.operands().end());
518 
519  // special treatment for last one
520  if(last)
521  result.swap(*it);
522  else
523  {
524  side_effects.add(clean_expr(*it, mode, false));
525 
526  // get any side-effects
527  if(it->is_not_nil())
528  convert(code_expressiont(*it), side_effects.side_effects, mode);
529  }
530  }
531 
532  expr.swap(result);
533 
534  // do again
535  side_effects.add(clean_expr_address_of(expr, mode));
536  }
537  else if(expr.id() == ID_side_effect)
538  {
539  side_effects.add(
540  remove_side_effect(to_side_effect_expr(expr), mode, true, true));
541  }
542  else
543  Forall_operands(it, expr)
544  side_effects.add(clean_expr_address_of(*it, mode));
545 
546  return side_effects;
547 }
548 
551  exprt &expr,
552  const irep_idt &mode)
553 {
554  clean_expr_resultt side_effects;
555 
556  {
557  auto &binary_expr = to_binary_expr(expr);
558 
559  // first remove side-effects from condition
560  side_effects = clean_expr(to_binary_expr(expr).op0(), mode);
561 
562  // now we can copy op0 safely
563  if_exprt if_expr(
564  typecast_exprt::conditional_cast(binary_expr.op0(), bool_typet()),
565  binary_expr.op0(),
566  binary_expr.op1(),
567  expr.type());
568  if_expr.add_source_location() = expr.source_location();
569 
570  expr.swap(if_expr);
571  }
572 
573  // there might still be junk in expr.op2()
574  side_effects.add(clean_expr(expr, mode));
575 
576  return side_effects;
577 }
@ 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
exprt & with_source_location(source_locationt location) &
Add the source location from location, if it is non-nil.
Definition: expr.h:101
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:3082
clean_expr_resultt clean_expr_address_of(exprt &expr, const irep_idt &mode)
symbol_table_baset & symbol_table
void convert_assign(const code_assignt &code, goto_programt &dest, const irep_idt &mode)
clean_expr_resultt remove_gcc_conditional_expression(exprt &expr, 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
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)
symbol_exprt make_compound_literal(const exprt &expr, goto_programt &dest, const irep_idt &mode)
clean_expr_resultt remove_side_effect(side_effect_exprt &expr, const irep_idt &mode, bool result_is_used, bool address_taken)
static bool needs_cleaning(const exprt &expr)
Returns 'true' for expressions that may change the program state.
void convert(const codet &code, goto_programt &dest, const irep_idt &mode)
converts 'code' and appends the result to 'dest'
clean_expr_resultt remove_function_call(side_effect_expr_function_callt &expr, const irep_idt &mode, bool result_is_used)
clean_expr_resultt remove_statement_expression(side_effect_exprt &expr, const irep_idt &mode, bool result_is_used)
clean_expr_resultt clean_expr(exprt &expr, const irep_idt &mode, bool result_is_used=true)
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:2380
exprt & true_case()
Definition: std_expr.h:2407
exprt & false_case()
Definition: std_expr.h:2417
exprt & cond()
Definition: std_expr.h:2397
Array index operator.
Definition: std_expr.h:1470
exprt & array()
Definition: std_expr.h:1500
exprt & index()
Definition: std_expr.h:1510
bool is_not_nil() const
Definition: irep.h:372
const irep_idt & id() const
Definition: irep.h:388
void make_nil()
Definition: irep.h:446
void swap(irept &irep)
Definition: irep.h:434
bool is_nil() const
Definition: irep.h:368
mstreamt & result() const
Definition: message.h:401
The NIL expression.
Definition: std_expr.h:3091
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:3073
Semantic type conversion.
Definition: std_expr.h:2073
static exprt conditional_cast(const exprt &expr, const typet &type)
Definition: std_expr.h:2081
const exprt & op() const
Definition: std_expr.h:391
void destruct_locals(const std::list< irep_idt > &vars, goto_programt &dest, const namespacet &ns)
Definition: destructor.cpp:62
Destructor Calls.
#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:193
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:115
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:2460
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition: std_expr.h:272
const typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
Definition: std_expr.h:2107
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:1538
std::list< irep_idt > temporaries
Identifiers of temporaries introduced while cleaning an expression.
void add(clean_expr_resultt &&other)
void add_temporary(const irep_idt &id)
goto_programt side_effects
Statements implementing side effects of the expression that was subject to cleaning.
Symbol table entry.