Skip to content

Commit 2ba7118

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 d932d6f commit 2ba7118

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
@@ -100,7 +100,7 @@ void constant_propagator_domaint::assign_rec(
100100
exprt tmp = rhs;
101101
partial_evaluate(dest_values, tmp, ns);
102102

103-
if(dest_values.is_constant(tmp))
103+
if(dest_values.is_constant(tmp, ns))
104104
{
105105
DATA_INVARIANT(
106106
ns.lookup(s).type == tmp.type(),
@@ -393,7 +393,7 @@ bool constant_propagator_domaint::two_way_propagate_rec(
393393
// two-way propagation
394394
valuest copy_values=values;
395395
assign_rec(copy_values, lhs, rhs, ns, cp, false);
396-
if(!values.is_constant(rhs) || values.is_constant(lhs))
396+
if(!values.is_constant(rhs, ns) || values.is_constant(lhs, ns))
397397
assign_rec(values, rhs, lhs, ns, cp, false);
398398
change = values.meet(copy_values, ns);
399399
}
@@ -419,9 +419,10 @@ bool constant_propagator_domaint::ai_simplify(
419419
class constant_propagator_is_constantt : public is_constantt
420420
{
421421
public:
422-
explicit constant_propagator_is_constantt(
423-
const replace_symbolt &replace_const)
424-
: replace_const(replace_const)
422+
constant_propagator_is_constantt(
423+
const replace_symbolt &replace_const,
424+
const namespacet &ns)
425+
: is_constantt(ns), replace_const(replace_const)
425426
{
426427
}
427428

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

445-
bool constant_propagator_domaint::valuest::is_constant(const exprt &expr) const
446+
bool constant_propagator_domaint::valuest::is_constant(
447+
const exprt &expr,
448+
const namespacet &ns) const
446449
{
447-
return constant_propagator_is_constantt(replace_const)(expr);
450+
return constant_propagator_is_constantt(replace_const, ns)(expr);
448451
}
449452

450-
bool constant_propagator_domaint::valuest::is_constant(const irep_idt &id) const
453+
bool constant_propagator_domaint::valuest::is_constant(
454+
const irep_idt &id,
455+
const namespacet &ns) const
451456
{
452-
return constant_propagator_is_constantt(replace_const).is_constant(id);
457+
return constant_propagator_is_constantt(replace_const, ns).is_constant(id);
453458
}
454459

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

663668
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
@@ -213,7 +213,7 @@ void code_contractst::check_apply_loop_contracts(
213213

214214
// If the set contains pairs (i, a[i]),
215215
// we widen them to (i, __CPROVER_POINTER_OBJECT(a))
216-
widen_assigns(to_havoc);
216+
widen_assigns(to_havoc, ns);
217217

218218
log.debug() << "No loop assigns clause provided. Inferred targets: {";
219219
// 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
@@ -247,11 +247,11 @@ bool is_assigns_clause_replacement_tracking_comment(const irep_idt &comment)
247247
std::string::npos;
248248
}
249249

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

254-
havoc_utils_is_constantt is_constant(assigns);
254+
havoc_utils_is_constantt is_constant(assigns, ns);
255255

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

src/goto-instrument/contracts/utils.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class havoc_if_validt : public havoc_utilst
2424
{
2525
public:
2626
havoc_if_validt(const assignst &mod, const namespacet &ns)
27-
: havoc_utilst(mod), ns(ns)
27+
: havoc_utilst(mod, ns), ns(ns)
2828
{
2929
}
3030

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

180180
/// This function recursively searches \p expression to find nested or
181181
/// 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
@@ -27,7 +27,8 @@ typedef std::set<exprt> assignst;
2727
class havoc_utils_is_constantt : public is_constantt
2828
{
2929
public:
30-
explicit havoc_utils_is_constantt(const assignst &mod) : assigns(mod)
30+
explicit havoc_utils_is_constantt(const assignst &mod, const namespacet &ns)
31+
: is_constantt(ns), assigns(mod)
3132
{
3233
}
3334

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

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)