Skip to content

Commit c3dd308

Browse files
committed
Float constant propagation must observe rounding modes
Detect assignments to `__CPROVER_rounding_mode` and invalidate all floating-point constants in the value map. This ensures soundness by preventing the reuse of constants computed with a different rounding mode, at the cost of potentially missing some optimization opportunities. Fixes: #350
1 parent 4fe3ade commit c3dd308

File tree

5 files changed

+149
-0
lines changed

5 files changed

+149
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <assert.h>
2+
3+
int main(void)
4+
{
5+
__CPROVER_rounding_mode = 0;
6+
float rounded_up = 1.0f / 10.0f;
7+
__CPROVER_rounding_mode = 1;
8+
float rounded_down = 1.0f / 10.0f;
9+
assert(rounded_up - 0.1f >= 0);
10+
assert(rounded_down - 0.1f < 0);
11+
return 0;
12+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
main.c
3+
--constant-propagator --inline --add-library
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring

src/analyses/constant_propagator.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Author: Peter Schrammel
1919
#endif
2020

2121
#include <util/arith_tools.h>
22+
#include <util/bitvector_types.h>
2223
#include <util/c_types.h>
2324
#include <util/cprover_prefix.h>
2425
#include <util/expr_util.h>
@@ -166,6 +167,17 @@ void constant_propagator_domaint::transform(
166167
{
167168
const exprt &lhs = from->assign_lhs();
168169
const exprt &rhs = from->assign_rhs();
170+
171+
// If we're assigning to the rounding mode, invalidate all
172+
// floating-point constants as they may have been computed
173+
// with a different rounding mode
174+
if(
175+
lhs.id() == ID_symbol &&
176+
to_symbol_expr(lhs).get_identifier() == rounding_mode_identifier())
177+
{
178+
values.invalidate_floatbv_constants(ns);
179+
}
180+
169181
assign_rec(values, lhs, rhs, ns, cp, true);
170182
}
171183
else if(from->is_assume())
@@ -495,6 +507,28 @@ void constant_propagator_domaint::valuest::set_dirty_to_top(
495507
}
496508
}
497509

510+
void constant_propagator_domaint::valuest::invalidate_floatbv_constants(
511+
const namespacet &ns)
512+
{
513+
typedef replace_symbolt::expr_mapt expr_mapt;
514+
expr_mapt &expr_map = replace_const.get_expr_map();
515+
516+
for(expr_mapt::iterator it = expr_map.begin(); it != expr_map.end();)
517+
{
518+
const irep_idt id = it->first;
519+
const symbolt &symbol = ns.lookup(id);
520+
521+
// Remove any variable with floating-point type, as its value
522+
// may have been computed with a different rounding mode
523+
if(symbol.type.id() == ID_floatbv)
524+
{
525+
it = replace_const.erase(it);
526+
}
527+
else
528+
it++;
529+
}
530+
}
531+
498532
void constant_propagator_domaint::valuest::output(
499533
std::ostream &out,
500534
const namespacet &ns) const

src/analyses/constant_propagator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ class constant_propagator_domaint:public ai_domain_baset
118118

119119
void set_dirty_to_top(const dirtyt &dirty, const namespacet &ns);
120120

121+
void invalidate_floatbv_constants(const namespacet &ns);
122+
121123
bool is_constant(const exprt &expr, const namespacet &ns) const;
122124

123125
bool is_constant(const irep_idt &id, const namespacet &ns) const;

unit/analyses/constant_propagator.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ Author: Diffblue Ltd
77
\*******************************************************************/
88

99
#include <util/arith_tools.h>
10+
#include <util/bitvector_types.h>
1011
#include <util/c_types.h>
1112
#include <util/mathematical_types.h>
1213
#include <util/prefix.h>
14+
#include <util/std_expr.h>
1315

1416
#include <analyses/constant_propagator.h>
1517
#include <ansi-c/goto-conversion/goto_convert_functions.h>
@@ -331,3 +333,95 @@ SCENARIO("constant_propagator", "[core][analyses][constant_propagator]")
331333
}
332334
}
333335
}
336+
337+
SCENARIO(
338+
"constant_propagator_rounding_mode",
339+
"[core][analyses][constant_propagator]")
340+
{
341+
GIVEN(
342+
"A GOTO program with floating-point operations and rounding mode changes")
343+
{
344+
goto_modelt goto_model;
345+
namespacet ns(goto_model.symbol_table);
346+
347+
// Create the program:
348+
// __CPROVER_rounding_mode = 0;
349+
// float f1 = 1.0f / 10.0f;
350+
// __CPROVER_rounding_mode = 1;
351+
// float f2 = 1.0f / 10.0f;
352+
353+
symbolt rounding_mode{"__CPROVER_rounding_mode", integer_typet(), ID_C};
354+
symbolt local_f1{"f1", float_type(), ID_C};
355+
symbolt local_f2{"f2", float_type(), ID_C};
356+
357+
code_blockt code(
358+
{code_declt(local_f1.symbol_expr()),
359+
code_declt(local_f2.symbol_expr()),
360+
code_assignt(
361+
rounding_mode.symbol_expr(), constant_exprt("0", integer_typet())),
362+
code_assignt(
363+
local_f1.symbol_expr(),
364+
div_exprt(
365+
constant_exprt("1", float_type()),
366+
constant_exprt("10", float_type()))),
367+
code_assignt(
368+
rounding_mode.symbol_expr(), constant_exprt("1", integer_typet())),
369+
code_assignt(
370+
local_f2.symbol_expr(),
371+
div_exprt(
372+
constant_exprt("1", float_type()),
373+
constant_exprt("10", float_type())))});
374+
375+
symbolt main_function_symbol{"main", code_typet({}, empty_typet()), ID_C};
376+
main_function_symbol.value = code;
377+
378+
goto_model.symbol_table.add(rounding_mode);
379+
goto_model.symbol_table.add(local_f1);
380+
goto_model.symbol_table.add(local_f2);
381+
goto_model.symbol_table.add(main_function_symbol);
382+
383+
goto_convert(goto_model, null_message_handler);
384+
385+
const goto_functiont &main_function = goto_model.get_goto_function("main");
386+
387+
// Find the instruction after the second rounding mode assignment
388+
goto_programt::const_targett test_instruction =
389+
main_function.body.instructions.begin();
390+
int rounding_mode_assignments = 0;
391+
while(test_instruction != main_function.body.instructions.end())
392+
{
393+
if(
394+
test_instruction->is_assign() &&
395+
test_instruction->assign_lhs() == rounding_mode.symbol_expr())
396+
{
397+
++rounding_mode_assignments;
398+
if(rounding_mode_assignments == 2)
399+
{
400+
++test_instruction;
401+
break;
402+
}
403+
}
404+
++test_instruction;
405+
}
406+
407+
REQUIRE(test_instruction != main_function.body.instructions.end());
408+
409+
WHEN("We apply constant propagation")
410+
{
411+
constant_propagator_ait constant_propagator(main_function);
412+
constant_propagator(main_function_symbol.name, main_function, ns);
413+
414+
THEN(
415+
"The propagator should NOT have f1 as a constant, because "
416+
"the rounding mode changed")
417+
{
418+
const auto &final_domain = constant_propagator[test_instruction];
419+
420+
// f1 should not be constant at this point because the rounding
421+
// mode changed after it was computed
422+
REQUIRE_FALSE(
423+
final_domain.values.is_constant(local_f1.symbol_expr(), ns));
424+
}
425+
}
426+
}
427+
}

0 commit comments

Comments
 (0)