Skip to content

Commit ded48a5

Browse files
committed
Byte extract and extract bits can be constant
When all operands are constants and the extract is fully within bounds of the source object then the result is constant. This permits constant-propagating byte-extract operations from pointers that are address-of.
1 parent ca15201 commit ded48a5

File tree

18 files changed

+137
-49
lines changed

18 files changed

+137
-49
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--show-vcc
4+
main::1::m!0@1#1 = .*byte_extract_(big|little)_endian\(cast\(44, signedbv\[\d+\]\*\), 0, signedbv\[\d+\]\).*
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring

src/analyses/constant_propagator.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ void constant_propagator_domaint::assign_rec(
101101
exprt tmp = rhs;
102102
partial_evaluate(dest_values, tmp, ns);
103103

104-
if(dest_values.is_constant(tmp))
104+
if(dest_values.is_constant(tmp, ns))
105105
{
106106
DATA_INVARIANT(
107107
ns.lookup(s).type == tmp.type(),
@@ -394,7 +394,7 @@ bool constant_propagator_domaint::two_way_propagate_rec(
394394
// two-way propagation
395395
valuest copy_values=values;
396396
assign_rec(copy_values, lhs, rhs, ns, cp, false);
397-
if(!values.is_constant(rhs) || values.is_constant(lhs))
397+
if(!values.is_constant(rhs, ns) || values.is_constant(lhs, ns))
398398
assign_rec(values, rhs, lhs, ns, cp, false);
399399
change = values.meet(copy_values, ns);
400400
}
@@ -420,9 +420,10 @@ bool constant_propagator_domaint::ai_simplify(
420420
class constant_propagator_is_constantt : public is_constantt
421421
{
422422
public:
423-
explicit constant_propagator_is_constantt(
424-
const replace_symbolt &replace_const)
425-
: replace_const(replace_const)
423+
constant_propagator_is_constantt(
424+
const replace_symbolt &replace_const,
425+
const namespacet &ns)
426+
: is_constantt(ns), replace_const(replace_const)
426427
{
427428
}
428429

@@ -443,14 +444,18 @@ class constant_propagator_is_constantt : public is_constantt
443444
const replace_symbolt &replace_const;
444445
};
445446

446-
bool constant_propagator_domaint::valuest::is_constant(const exprt &expr) const
447+
bool constant_propagator_domaint::valuest::is_constant(
448+
const exprt &expr,
449+
const namespacet &ns) const
447450
{
448-
return constant_propagator_is_constantt(replace_const)(expr);
451+
return constant_propagator_is_constantt(replace_const, ns)(expr);
449452
}
450453

451-
bool constant_propagator_domaint::valuest::is_constant(const irep_idt &id) const
454+
bool constant_propagator_domaint::valuest::is_constant(
455+
const irep_idt &id,
456+
const namespacet &ns) const
452457
{
453-
return constant_propagator_is_constantt(replace_const).is_constant(id);
458+
return constant_propagator_is_constantt(replace_const, ns).is_constant(id);
454459
}
455460

456461
/// Do not call this when iterating over replace_const.expr_map!
@@ -658,7 +663,7 @@ bool constant_propagator_domaint::partial_evaluate(
658663
// if the current rounding mode is top we can
659664
// still get a non-top result by trying all rounding
660665
// modes and checking if the results are all the same
661-
if(!known_values.is_constant(rounding_mode_identifier()))
666+
if(!known_values.is_constant(rounding_mode_identifier(), ns))
662667
return partial_evaluate_with_all_rounding_modes(known_values, expr, ns);
663668

664669
return replace_constants_and_simplify(known_values, expr, ns);

src/analyses/constant_propagator.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ class constant_propagator_domaint:public ai_domain_baset
123123

124124
void set_dirty_to_top(const dirtyt &dirty, const namespacet &ns);
125125

126-
bool is_constant(const exprt &expr) const;
126+
bool is_constant(const exprt &expr, const namespacet &ns) const;
127127

128-
bool is_constant(const irep_idt &id) const;
128+
bool is_constant(const irep_idt &id, const namespacet &ns) const;
129129

130130
bool is_empty() const
131131
{

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4394,13 +4394,11 @@ void c_typecheck_baset::typecheck_side_effect_assignment(
43944394
class is_compile_time_constantt : public is_constantt
43954395
{
43964396
public:
4397-
explicit is_compile_time_constantt(const namespacet &ns) : ns(ns)
4397+
explicit is_compile_time_constantt(const namespacet &ns) : is_constantt(ns)
43984398
{
43994399
}
44004400

44014401
protected:
4402-
const namespacet &ns;
4403-
44044402
bool is_constant(const exprt &e) const override
44054403
{
44064404
if(e.id() == ID_infinity)

src/goto-instrument/contracts/contracts.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ void code_contractst::check_apply_loop_contracts(
217217

218218
// If the set contains pairs (i, a[i]),
219219
// we widen them to (i, __CPROVER_POINTER_OBJECT(a))
220-
widen_assigns(to_havoc);
220+
widen_assigns(to_havoc, ns);
221221

222222
log.debug() << "No loop assigns clause provided. Inferred targets: {";
223223
// Add inferred targets to the loop assigns clause.

src/goto-instrument/contracts/utils.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,11 @@ bool is_assigns_clause_replacement_tracking_comment(const irep_idt &comment)
246246
std::string::npos;
247247
}
248248

249-
void widen_assigns(assignst &assigns)
249+
void widen_assigns(assignst &assigns, const namespacet &ns)
250250
{
251251
assignst result;
252252

253-
havoc_utils_is_constantt is_constant(assigns);
253+
havoc_utils_is_constantt is_constant(assigns, ns);
254254

255255
for(const auto &e : assigns)
256256
{

src/goto-instrument/contracts/utils.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class havoc_if_validt : public havoc_utilst
3434
{
3535
public:
3636
havoc_if_validt(const assignst &mod, const namespacet &ns)
37-
: havoc_utilst(mod), ns(ns)
37+
: havoc_utilst(mod, ns), ns(ns)
3838
{
3939
}
4040

@@ -185,7 +185,7 @@ bool is_assigns_clause_replacement_tracking_comment(const irep_idt &comment);
185185
/// with a non-constant offset, e.g. a[i] or *(b+i) with a non-constant `i`,
186186
/// then replace it by the entire underlying object. Otherwise, e.g. for a[i] or
187187
/// *(b+i) when `i` is a known constant, keep the expression in the result.
188-
void widen_assigns(assignst &assigns);
188+
void widen_assigns(assignst &assigns, const namespacet &ns);
189189

190190
/// This function recursively searches \p expression to find nested or
191191
/// non-nested quantified expressions. When a quantified expression is found,

src/goto-instrument/havoc_loops.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ class havoc_loopst
2727

2828
havoc_loopst(
2929
function_assignst &_function_assigns,
30-
goto_functiont &_goto_function)
30+
goto_functiont &_goto_function,
31+
const namespacet &ns)
3132
: goto_function(_goto_function),
3233
local_may_alias(_goto_function),
3334
function_assigns(_function_assigns),
34-
natural_loops(_goto_function.body)
35+
natural_loops(_goto_function.body),
36+
ns(ns)
3537
{
3638
havoc_loops();
3739
}
@@ -41,6 +43,7 @@ class havoc_loopst
4143
local_may_aliast local_may_alias;
4244
function_assignst &function_assigns;
4345
natural_loops_mutablet natural_loops;
46+
const namespacet &ns;
4447

4548
typedef std::set<exprt> assignst;
4649
typedef const natural_loops_mutablet::natural_loopt loopt;
@@ -66,7 +69,7 @@ void havoc_loopst::havoc_loop(
6669

6770
// build the havocking code
6871
goto_programt havoc_code;
69-
havoc_utilst havoc_gen(assigns);
72+
havoc_utilst havoc_gen(assigns, ns);
7073
havoc_gen.append_full_havoc_code(loop_head->source_location(), havoc_code);
7174

7275
// Now havoc at the loop head. Use insert_swap to
@@ -118,5 +121,8 @@ void havoc_loops(goto_modelt &goto_model)
118121
function_assignst function_assigns(goto_model.goto_functions);
119122

120123
for(auto &gf_entry : goto_model.goto_functions.function_map)
121-
havoc_loopst(function_assigns, gf_entry.second);
124+
{
125+
havoc_loopst(
126+
function_assigns, gf_entry.second, namespacet{goto_model.symbol_table});
127+
}
122128
}

src/goto-instrument/havoc_utils.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ typedef std::set<exprt> assignst;
2626
class havoc_utils_is_constantt : public is_constantt
2727
{
2828
public:
29-
explicit havoc_utils_is_constantt(const assignst &mod) : assigns(mod)
29+
explicit havoc_utils_is_constantt(const assignst &mod, const namespacet &ns)
30+
: is_constantt(ns), assigns(mod)
3031
{
3132
}
3233

@@ -47,7 +48,8 @@ class havoc_utils_is_constantt : public is_constantt
4748
class havoc_utilst
4849
{
4950
public:
50-
explicit havoc_utilst(const assignst &mod) : assigns(mod), is_constant(mod)
51+
explicit havoc_utilst(const assignst &mod, const namespacet &ns)
52+
: assigns(mod), is_constant(mod, ns)
5153
{
5254
}
5355

src/goto-instrument/k_induction.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,16 @@ class k_inductiont
2828
goto_functiont &_goto_function,
2929
bool _base_case,
3030
bool _step_case,
31-
unsigned _k)
31+
unsigned _k,
32+
const namespacet &ns)
3233
: function_id(_function_id),
3334
goto_function(_goto_function),
3435
local_may_alias(_goto_function),
3536
natural_loops(_goto_function.body),
3637
base_case(_base_case),
3738
step_case(_step_case),
38-
k(_k)
39+
k(_k),
40+
ns(ns)
3941
{
4042
k_induction();
4143
}
@@ -49,6 +51,8 @@ class k_inductiont
4951
const bool base_case, step_case;
5052
const unsigned k;
5153

54+
const namespacet &ns;
55+
5256
void k_induction();
5357

5458
void process_loop(
@@ -97,7 +101,7 @@ void k_inductiont::process_loop(
97101

98102
// build the havocking code
99103
goto_programt havoc_code;
100-
havoc_utilst havoc_gen(assigns);
104+
havoc_utilst havoc_gen(assigns, ns);
101105
havoc_gen.append_full_havoc_code(loop_head->source_location(), havoc_code);
102106

103107
// unwind to get k+1 copies
@@ -165,5 +169,13 @@ void k_induction(
165169
unsigned k)
166170
{
167171
for(auto &gf_entry : goto_model.goto_functions.function_map)
168-
k_inductiont(gf_entry.first, gf_entry.second, base_case, step_case, k);
172+
{
173+
k_inductiont(
174+
gf_entry.first,
175+
gf_entry.second,
176+
base_case,
177+
step_case,
178+
k,
179+
namespacet{goto_model.symbol_table});
180+
}
169181
}

src/goto-symex/goto_state.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ void goto_statet::apply_condition(
9191
if(is_ssa_expr(rhs))
9292
std::swap(lhs, rhs);
9393

94-
if(is_ssa_expr(lhs) && goto_symex_is_constantt()(rhs))
94+
if(is_ssa_expr(lhs) && goto_symex_is_constantt(ns)(rhs))
9595
{
9696
const ssa_exprt &ssa_lhs = to_ssa_expr(lhs);
9797
INVARIANT(
@@ -141,7 +141,7 @@ void goto_statet::apply_condition(
141141
if(is_ssa_expr(rhs))
142142
std::swap(lhs, rhs);
143143

144-
if(!is_ssa_expr(lhs) || !goto_symex_is_constantt()(rhs))
144+
if(!is_ssa_expr(lhs) || !goto_symex_is_constantt(ns)(rhs))
145145
return;
146146

147147
if(rhs.is_true())

src/goto-symex/goto_symex_is_constant.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Author: Michael Tautschig, [email protected]
1717

1818
class goto_symex_is_constantt : public is_constantt
1919
{
20+
public:
21+
explicit goto_symex_is_constantt(const namespacet &ns) : is_constantt(ns)
22+
{
23+
}
24+
2025
protected:
2126
bool is_constant(const exprt &expr) const override
2227
{

src/goto-symex/goto_symex_state.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ renamedt<ssa_exprt, L2> goto_symex_statet::assignment(
110110
"pointer handling for concurrency is unsound");
111111

112112
// Update constant propagation map -- the RHS is L2
113-
if(!is_shared && record_value && goto_symex_is_constantt()(rhs))
113+
if(!is_shared && record_value && goto_symex_is_constantt(ns)(rhs))
114114
{
115115
const auto propagation_entry = propagation.find(l1_identifier);
116116
if(!propagation_entry.has_value())

src/goto-symex/symex_goto.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ static optionalt<renamedt<exprt, L2>> try_evaluate_pointer_comparison(
204204
if(!symbol_expr_lhs)
205205
return {};
206206

207-
if(!goto_symex_is_constantt()(rhs))
207+
if(!goto_symex_is_constantt(ns)(rhs))
208208
return {};
209209

210210
return try_evaluate_pointer_comparison(

src/util/expr_util.cpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ Author: Daniel Kroening, [email protected]
99
#include "expr_util.h"
1010

1111
#include "arith_tools.h"
12+
#include "bitvector_expr.h"
13+
#include "byte_operators.h"
1214
#include "c_types.h"
1315
#include "config.h"
1416
#include "expr_iterator.h"
1517
#include "namespace.h"
1618
#include "pointer_expr.h"
17-
#include "std_expr.h"
19+
#include "pointer_offset_size.h"
1820

1921
#include <algorithm>
2022
#include <unordered_set>
@@ -238,7 +240,6 @@ bool is_constantt::is_constant(const exprt &expr) const
238240
expr.id() == ID_plus || expr.id() == ID_mult || expr.id() == ID_array ||
239241
expr.id() == ID_with || expr.id() == ID_struct || expr.id() == ID_union ||
240242
expr.id() == ID_empty_union ||
241-
// byte_update works, byte_extract may be out-of-bounds
242243
expr.id() == ID_byte_update_big_endian ||
243244
expr.id() == ID_byte_update_little_endian)
244245
{
@@ -247,6 +248,46 @@ bool is_constantt::is_constant(const exprt &expr) const
247248
return is_constant(e);
248249
});
249250
}
251+
else if(auto be = expr_try_dynamic_cast<byte_extract_exprt>(expr))
252+
{
253+
if(!is_constant(be->op()) || !be->offset().is_constant())
254+
return false;
255+
256+
const auto op_bits = pointer_offset_bits(be->op().type(), ns);
257+
if(!op_bits.has_value())
258+
return false;
259+
260+
const auto extract_bits = pointer_offset_bits(be->type(), ns);
261+
if(!extract_bits.has_value())
262+
return false;
263+
264+
const mp_integer offset_bits =
265+
numeric_cast_v<mp_integer>(to_constant_expr(be->offset())) *
266+
be->get_bits_per_byte();
267+
268+
return offset_bits + *extract_bits <= *op_bits;
269+
}
270+
else if(auto eb = expr_try_dynamic_cast<extractbits_exprt>(expr))
271+
{
272+
if(
273+
!is_constant(eb->src()) || !eb->lower().is_constant() ||
274+
!eb->upper().is_constant())
275+
{
276+
return false;
277+
}
278+
279+
const auto src_bits = pointer_offset_bits(eb->src().type(), ns);
280+
if(!src_bits.has_value())
281+
return false;
282+
283+
const mp_integer lower_bound =
284+
numeric_cast_v<mp_integer>(to_constant_expr(eb->lower()));
285+
const mp_integer upper_bound =
286+
numeric_cast_v<mp_integer>(to_constant_expr(eb->upper()));
287+
288+
return lower_bound >= 0 && lower_bound <= upper_bound &&
289+
upper_bound < src_bits;
290+
}
250291

251292
return false;
252293
}

src/util/expr_util.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,19 @@ const exprt &skip_typecast(const exprt &expr);
8888
class is_constantt
8989
{
9090
public:
91+
explicit is_constantt(const namespacet &ns) : ns(ns)
92+
{
93+
}
94+
9195
/// returns true iff the expression can be considered constant
9296
bool operator()(const exprt &e) const
9397
{
9498
return is_constant(e);
9599
}
96100

97101
protected:
102+
const namespacet &ns;
103+
98104
virtual bool is_constant(const exprt &) const;
99105
virtual bool is_constant_address_of(const exprt &) const;
100106
};

0 commit comments

Comments
 (0)