CBMC
adjust_float_expressions.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Symbolic Execution
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
11 
13 
14 #include <util/arith_tools.h>
15 #include <util/cprover_prefix.h>
16 #include <util/expr_util.h>
17 #include <util/floatbv_expr.h>
18 #include <util/ieee_float.h>
19 #include <util/std_expr.h>
20 #include <util/symbol.h>
21 
22 #include "goto_model.h"
23 
25 {
26  return CPROVER_PREFIX "rounding_mode";
27 }
28 
32 static bool have_to_adjust_float_expressions(const exprt &expr)
33 {
34  if(expr.id()==ID_floatbv_plus ||
35  expr.id()==ID_floatbv_minus ||
36  expr.id()==ID_floatbv_mult ||
37  expr.id()==ID_floatbv_div ||
38  expr.id()==ID_floatbv_div ||
39  expr.id()==ID_floatbv_rem ||
40  expr.id()==ID_floatbv_typecast)
41  return false;
42 
43  const typet &type = expr.type();
44 
45  if(
46  type.id() == ID_floatbv ||
47  (type.id() == ID_complex &&
48  to_complex_type(type).subtype().id() == ID_floatbv))
49  {
50  if(
51  expr.id() == ID_plus || expr.id() == ID_minus || expr.id() == ID_mult ||
52  expr.id() == ID_div)
53  return true;
54  }
55 
56  if(expr.id()==ID_typecast)
57  {
58  const typecast_exprt &typecast_expr=to_typecast_expr(expr);
59 
60  const typet &src_type=typecast_expr.op().type();
61  const typet &dest_type=typecast_expr.type();
62 
63  if(dest_type.id()==ID_floatbv &&
64  src_type.id()==ID_floatbv)
65  return true;
66  else if(
67  dest_type.id() == ID_floatbv &&
68  (src_type.id() == ID_c_bit_field || src_type.id() == ID_signedbv ||
69  src_type.id() == ID_unsignedbv || src_type.id() == ID_c_enum_tag))
70  return true;
71  else if(
72  (dest_type.id() == ID_signedbv || dest_type.id() == ID_unsignedbv ||
73  dest_type.id() == ID_c_enum_tag || dest_type.id() == ID_c_bit_field) &&
74  src_type.id() == ID_floatbv)
75  return true;
76  }
77 
78  for(const auto &op : expr.operands())
79  {
81  return true;
82  }
83 
84  return false;
85 }
86 
87 void adjust_float_expressions(exprt &expr, const exprt &rounding_mode)
88 {
90  return;
91 
92  // recursive call
93  // Note that we do the recursion twice here; once in
94  // `have_to_adjust_float_expressions` and once here. Presumably this is to
95  // avoid breaking sharing (calling the non-const operands() function breaks
96  // sharing)
97  for(auto &op : expr.operands())
98  adjust_float_expressions(op, rounding_mode);
99 
100  const typet &type = expr.type();
101 
102  if(
103  type.id() == ID_floatbv ||
104  (type.id() == ID_complex &&
105  to_complex_type(type).subtype().id() == ID_floatbv))
106  {
107  if(
108  expr.id() == ID_plus || expr.id() == ID_minus || expr.id() == ID_mult ||
109  expr.id() == ID_div)
110  {
112  expr.operands().size() >= 2,
113  "arithmetic operations must have two or more operands");
114 
115  // make sure we have binary expressions
116  if(expr.operands().size()>2)
117  {
118  expr=make_binary(expr);
119  CHECK_RETURN(expr.operands().size() == 2);
120  }
121 
122  // now add rounding mode
123  expr.id(expr.id()==ID_plus?ID_floatbv_plus:
124  expr.id()==ID_minus?ID_floatbv_minus:
125  expr.id()==ID_mult?ID_floatbv_mult:
126  expr.id()==ID_div?ID_floatbv_div:
127  irep_idt());
128 
129  expr.operands().resize(3);
130  to_ieee_float_op_expr(expr).rounding_mode() = rounding_mode;
131  }
132  }
133 
134  if(expr.id()==ID_typecast)
135  {
136  const typecast_exprt &typecast_expr=to_typecast_expr(expr);
137 
138  const typet &src_type=typecast_expr.op().type();
139  const typet &dest_type=typecast_expr.type();
140 
141  if(dest_type.id()==ID_floatbv &&
142  src_type.id()==ID_floatbv)
143  {
144  // Casts from bigger to smaller float-type might round.
145  // For smaller to bigger it is strictly redundant but
146  // we leave this optimisation until later to simplify
147  // the representation.
148  expr.id(ID_floatbv_typecast);
149  expr.operands().resize(2);
150  to_floatbv_typecast_expr(expr).rounding_mode() = rounding_mode;
151  }
152  else if(
153  dest_type.id() == ID_floatbv &&
154  (src_type.id() == ID_signedbv || src_type.id() == ID_unsignedbv ||
155  src_type.id() == ID_c_enum_tag || src_type.id() == ID_c_bit_field))
156  {
157  // casts from integer to float-type might round
158  expr.id(ID_floatbv_typecast);
159  expr.operands().resize(2);
160  to_floatbv_typecast_expr(expr).rounding_mode() = rounding_mode;
161  }
162  else if(
163  dest_type.id() == ID_floatbv &&
164  (src_type.id() == ID_c_bool || src_type.id() == ID_bool))
165  {
166  // casts from bool or c_bool to float-type do not need rounding
167  }
168  else if(
169  (dest_type.id() == ID_signedbv || dest_type.id() == ID_unsignedbv ||
170  dest_type.id() == ID_c_enum_tag || dest_type.id() == ID_c_bit_field) &&
171  src_type.id() == ID_floatbv)
172  {
173  // In C, casts from float to integer always round to zero,
174  // irrespectively of the rounding mode that is currently set.
175  // We will have to consider other programming languages
176  // eventually.
177 
178  /* ISO 9899:1999
179  * 6.3.1.4 Real floating and integer
180  * 1 When a finite value of real floating type is converted
181  * to an integer type other than _Bool, the fractional part
182  * is discarded (i.e., the value is truncated toward zero).
183  */
184  expr.id(ID_floatbv_typecast);
185  expr.operands().resize(2);
187  from_integer(ieee_floatt::ROUND_TO_ZERO, rounding_mode.type());
188  }
189  }
190 }
191 
193 {
195  return;
196 
197  symbol_exprt rounding_mode =
198  ns.lookup(rounding_mode_identifier()).symbol_expr();
199 
200  rounding_mode.add_source_location() = expr.source_location();
201 
202  adjust_float_expressions(expr, rounding_mode);
203 }
204 
206  goto_functionst::goto_functiont &goto_function,
207  const namespacet &ns)
208 {
209  for(auto &i : goto_function.body.instructions)
210  i.transform([&ns](exprt expr) -> std::optional<exprt> {
212  {
213  adjust_float_expressions(expr, ns);
214  return expr;
215  }
216  else
217  return {};
218  });
219 }
220 
222  goto_functionst &goto_functions,
223  const namespacet &ns)
224 {
225  for(auto &gf_entry : goto_functions.function_map)
226  adjust_float_expressions(gf_entry.second, ns);
227 }
228 
230 {
231  namespacet ns(goto_model.symbol_table);
233 }
static bool have_to_adjust_float_expressions(const exprt &expr)
Iterate over an expression and check it or any of its subexpressions are floating point operations th...
irep_idt rounding_mode_identifier()
Return the identifier of the program symbol used to store the current rounding mode.
void adjust_float_expressions(exprt &expr, const exprt &rounding_mode)
Replaces arithmetic operations and typecasts involving floating point numbers with their equivalent f...
Symbolic Execution.
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:38
Base class for all expressions.
Definition: expr.h:56
source_locationt & add_source_location()
Definition: expr.h:236
const source_locationt & source_location() const
Definition: expr.h:231
typet & type()
Return the type of the expression.
Definition: expr.h:84
operandst & operands()
Definition: expr.h:94
A collection of goto functions.
function_mapt function_map
::goto_functiont goto_functiont
symbol_tablet symbol_table
Symbol table.
Definition: goto_model.h:31
goto_functionst goto_functions
GOTO functions.
Definition: goto_model.h:34
exprt & rounding_mode()
Definition: floatbv_expr.h:395
const irep_idt & id() const
Definition: irep.h:384
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
Expression to hold a symbol (variable)
Definition: std_expr.h:131
Semantic type conversion.
Definition: std_expr.h:2068
The type of an expression, extends irept.
Definition: type.h:29
const exprt & op() const
Definition: std_expr.h:391
#define CPROVER_PREFIX
exprt make_binary(const exprt &expr)
splits an expression with >=3 operands into nested binary expressions
Definition: expr_util.cpp:38
Deprecated expression utility functions.
API to expression classes for floating-point arithmetic.
const ieee_float_op_exprt & to_ieee_float_op_expr(const exprt &expr)
Cast an exprt to an ieee_float_op_exprt.
Definition: floatbv_expr.h:425
const floatbv_typecast_exprt & to_floatbv_typecast_expr(const exprt &expr)
Cast an exprt to a floatbv_typecast_exprt.
Definition: floatbv_expr.h:68
Symbol Table + CFG.
#define CHECK_RETURN(CONDITION)
Definition: invariant.h:495
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:534
API to expression classes.
const typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
Definition: std_expr.h:2102
const complex_typet & to_complex_type(const typet &type)
Cast a typet to a complex_typet.
Definition: std_types.h:1146
Symbol table entry.