Skip to content

Commit 85d72be

Browse files
author
Enrico Steffinlongo
committed
Fix the way shadow memory performs max aggregation
Fix missing aggregation when the shadow memory element type is a non-compound bitvector of size > 1 byte.
1 parent a54bc5b commit 85d72be

File tree

2 files changed

+99
-96
lines changed

2 files changed

+99
-96
lines changed

src/goto-symex/shadow_memory_util.cpp

Lines changed: 93 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ Author: Peter Schrammel
1515
#include <util/bitvector_expr.h>
1616
#include <util/byte_operators.h>
1717
#include <util/c_types.h>
18+
#include <util/config.h>
1819
#include <util/format_expr.h>
1920
#include <util/invariant.h>
2021
#include <util/namespace.h>
2122
#include <util/pointer_offset_size.h>
2223
#include <util/ssa_expr.h>
2324
#include <util/std_expr.h>
2425

26+
#include <solvers/flattening/boolbv_width.h>
2527

2628
// TODO: change DEBUG_SM to DEBUG_SHADOW_MEMORY (it also appears in other files)
2729

@@ -428,119 +430,120 @@ exprt compute_or_over_cells(
428430
return or_values(values, field_type);
429431
}
430432

431-
// TODO: doxygen?
432-
static void
433-
max_element(const exprt &element, const typet &field_type, exprt &max)
433+
/// Create an expression comparing the element at `expr_iterator` with the
434+
/// following elements of the collection (or `nil_exprt`if none) and return a
435+
/// pair `(condition, element)` such that if the condition is `true`, then the
436+
/// element is the max of the collection in the interval denoted by
437+
/// `expr_iterator` and `end`.
438+
/// \param expr_iterator the iterator pointing at the element to compare to.
439+
/// \param end the end of collection iterator.
440+
/// \return A pair (cond, elem) containing the condition and the max element.
441+
std::pair<exprt, exprt> compare_to_collection(
442+
const std::vector<exprt>::const_iterator &expr_iterator,
443+
const std::vector<exprt>::const_iterator &end)
434444
{
435-
exprt value = typecast_exprt::conditional_cast(element, field_type);
436-
if(max.is_nil())
445+
// We need at least an element in the collection
446+
INVARIANT(expr_iterator != end, "Cannot compute max of an empty collection");
447+
const exprt &current_expr = *expr_iterator;
448+
449+
// Iterator for the other elements in the collection in the interval denoted
450+
// by `expr_iterator` and `end`.
451+
std::vector<exprt>::const_iterator expr_to_compare_to =
452+
std::next(expr_iterator);
453+
if(expr_to_compare_to == end)
437454
{
438-
max = value;
455+
return {nil_exprt{}, current_expr};
439456
}
440-
else
457+
458+
std::vector<exprt> comparisons;
459+
for(; expr_to_compare_to != end; ++expr_to_compare_to)
441460
{
442-
max = if_exprt(binary_predicate_exprt(value, ID_gt, max), value, max);
461+
// Compare the current element with the n-th following it
462+
comparisons.emplace_back(
463+
binary_predicate_exprt(current_expr, ID_gt, *expr_to_compare_to));
443464
}
465+
466+
return {and_exprt(comparisons), current_expr};
444467
}
445468

446-
// TODO: doxygen?
447-
static void max_over_bytes(
448-
const exprt &value,
449-
const typet &type,
450-
const typet &field_type,
451-
exprt &max)
469+
/// Combine each (condition, value) element in the input collection into a
470+
/// if-then-else expression such as
471+
/// (cond_1 ? val_1 : (cond_2 ? val_2 : ... : val_n))
472+
/// \param conditions_and_values collection containing codnition-value pairs
473+
/// \return the combined max expression
474+
static exprt combine_condition_and_max_values(
475+
const std::vector<std::pair<exprt, exprt>> &conditions_and_values)
452476
{
453-
const size_t size = to_bitvector_type(type).get_width() / 8;
454-
max_element(value, field_type, max);
455-
for(size_t i = 1; i < size; ++i)
477+
// We need at least one element
478+
INVARIANT(
479+
conditions_and_values.size() > 0,
480+
"Cannot compute max of an empty collection");
481+
482+
// We use reverse-iterator, so the last element is the one in the last else
483+
// case.
484+
auto reverse_ite = conditions_and_values.rbegin();
485+
486+
// The last element must have `nil_exprt` as condition
487+
INVARIANT(
488+
reverse_ite->first == nil_exprt{},
489+
"Last element of condition-value list must have nil_exprt condition.");
490+
491+
exprt res = std::move(reverse_ite->second);
492+
493+
for(++reverse_ite; reverse_ite != conditions_and_values.rend(); ++reverse_ite)
456494
{
457-
max_element(
458-
lshr_exprt(value, from_integer(8 * i, size_type())), field_type, max);
495+
res = if_exprt(reverse_ite->first, reverse_ite->second, res);
459496
}
497+
498+
return res;
460499
}
461500

462-
// TODO: doxygen?
463-
static void max_elements(
464-
exprt element,
465-
const typet &field_type,
466-
const namespacet &ns,
467-
const messaget &log,
468-
const bool is_union,
469-
exprt &max)
501+
/// Create an expression encoding the max operation over the collection `values`
502+
/// \param values an `exprt` that encodes the max of `values`
503+
/// \return an `exprt` encoding the max operation over the collection `values`
504+
static exprt create_max_expr(const std::vector<exprt> &values)
470505
{
471-
element = conditional_cast_floatbv_to_unsignedbv(element);
472-
if(element.type().id() == ID_unsignedbv || element.type().id() == ID_signedbv)
473-
{
474-
if(is_union)
475-
{
476-
max_over_bytes(element, element.type(), field_type, max);
477-
}
478-
else
479-
{
480-
max_element(element, field_type, max);
481-
}
482-
}
483-
else
506+
std::vector<std::pair<exprt, exprt>> rows;
507+
rows.reserve(values.size());
508+
for(auto byte_it = values.begin(); byte_it != values.end(); ++byte_it)
484509
{
485-
exprt value =
486-
compute_max_over_cells(element, field_type, ns, log, is_union);
487-
max_element(value, field_type, max);
510+
// Create a pair condition-element where the condition is the comparison of
511+
// the element with all the ones contained in the rest of the collection.
512+
rows.emplace_back(compare_to_collection(byte_it, values.end()));
488513
}
514+
515+
return combine_condition_and_max_values(rows);
489516
}
490517

491518
exprt compute_max_over_cells(
492519
const exprt &expr,
493520
const typet &field_type,
494-
const namespacet &ns,
495-
const messaget &log,
496-
const bool is_union)
521+
const namespacet &ns)
497522
{
498-
const typet type = ns.follow(expr.type());
523+
// Compute the bit-width of the type of `expr`.
524+
std::size_t size = boolbv_widtht{ns}(expr.type());
499525

500-
if(type.id() == ID_struct || type.id() == ID_union)
526+
// Compute how many bytes are in `expr`
527+
std::size_t byte_count = size / config.ansi_c.char_width;
528+
529+
// Extract each byte of `expr` by using byte_extract.
530+
std::vector<exprt> extracted_bytes;
531+
extracted_bytes.reserve(byte_count);
532+
for(std::size_t i = 0; i < byte_count; ++i)
501533
{
502-
exprt max = nil_exprt();
503-
for(const auto &component : to_struct_union_type(type).components())
504-
{
505-
if(component.get_is_padding())
506-
{
507-
continue;
508-
}
509-
max_elements(
510-
member_exprt(expr, component), field_type, ns, log, is_union, max);
511-
}
512-
return max;
534+
extracted_bytes.emplace_back(make_byte_extract(
535+
expr, from_integer(i, unsigned_long_int_type()), field_type));
513536
}
514-
else if(type.id() == ID_array)
515-
{
516-
const array_typet &array_type = to_array_type(type);
517-
if(array_type.size().is_constant())
518-
{
519-
exprt max = nil_exprt();
520-
const mp_integer size =
521-
numeric_cast_v<mp_integer>(to_constant_expr(array_type.size()));
522-
for(mp_integer index = 0; index < size; ++index)
523-
{
524-
max_elements(
525-
index_exprt(expr, from_integer(index, index_type())),
526-
field_type,
527-
ns,
528-
log,
529-
is_union,
530-
max);
531-
}
532-
return max;
533-
}
534-
else
535-
{
536-
log.warning()
537-
<< "Shadow memory: cannot compute max over variable-size array "
538-
<< format(expr) << messaget::eom;
539-
}
540-
}
541-
// TODO: This is incorrect when accessing non-0 offsets of scalars.
542-
return typecast_exprt::conditional_cast(
543-
conditional_cast_floatbv_to_unsignedbv(expr), field_type);
537+
538+
// Compute the max of the bytes extracted from `expr`.
539+
exprt max_expr = create_max_expr(extracted_bytes);
540+
541+
INVARIANT(
542+
max_expr.type() == field_type,
543+
"Aggregated max value type must be the same as shadow memory field's "
544+
"type.");
545+
546+
return max_expr;
544547
}
545548

546549
// TODO: doxygen?
@@ -819,10 +822,7 @@ std::vector<std::pair<exprt, exprt>> get_shadow_dereference_candidates(
819822
else
820823
{
821824
// Value is of other (bitvector) type, so aggregate with max
822-
value = typecast_exprt::conditional_cast(
823-
compute_max_over_cells(
824-
shadow_dereference.value, field_type, ns, log, is_union),
825-
lhs_type);
825+
value = compute_max_over_cells(shadow_dereference.value, field_type, ns);
826826
}
827827

828828
const exprt base_cond = get_matched_base_cond(

src/goto-symex/shadow_memory_util.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,15 @@ exprt compute_or_over_cells(
156156
/// Performs aggregation of the shadow memory field value over multiple cells
157157
/// for fields whose type is a signed/unsigned bitvector (where the value is
158158
/// arbitrary up until the max represented by the bitvector size).
159+
/// \param expr the expression to extract the max from
160+
/// \param field_type the type of the shadow memory field to return
161+
/// \param ns the namespace to perform type-lookups into
162+
/// \return the aggregated max byte-sized value contained in expr
163+
/// Note that the expr type size must be known at compile time.
159164
exprt compute_max_over_cells(
160165
const exprt &expr,
161166
const typet &field_type,
162-
const namespacet &ns,
163-
const messaget &log,
164-
const bool is_union);
167+
const namespacet &ns);
165168

166169
/// Build an if-then-else chain from a vector containing pairs of expressions.
167170
/// \param conds_values Contains pairs <e1, e2>, where `e1` is going to be

0 commit comments

Comments
 (0)