Skip to content

Commit d61193e

Browse files
committed
Simplify more cases of extractbits over concatenation
We may be able to drop some of the operands of a concatenation.
1 parent 6df204e commit d61193e

2 files changed

Lines changed: 138 additions & 11 deletions

File tree

src/util/simplify_expr_int.cpp

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,30 +1205,64 @@ simplify_exprt::simplify_extractbits(const extractbits_exprt &expr)
12051205

12061206
return std::move(*result);
12071207
}
1208+
else if(*end == 0 && *start + 1 == *width)
1209+
{
1210+
typecast_exprt tc{expr.src(), expr.type()};
1211+
return changed(simplify_typecast(tc));
1212+
}
12081213
else if(expr.src().id() == ID_concatenation)
12091214
{
12101215
// the most-significant bit comes first in an concatenation_exprt, hence we
12111216
// count down
12121217
mp_integer offset = *width;
12131218

1219+
exprt::operandst new_operands;
1220+
new_operands.reserve(expr.src().operands().size());
1221+
mp_integer new_index = *end;
1222+
mp_integer new_concat_width = 0;
1223+
12141224
for(const auto &op : expr.src().operands())
12151225
{
12161226
auto op_width = pointer_offset_bits(op.type(), ns);
12171227

12181228
if(!op_width.has_value() || *op_width <= 0)
12191229
return unchanged(expr);
12201230

1221-
if(*start < offset && offset <= *end + *op_width)
1231+
// current value of offset is the index (within the concatenated
1232+
// expression) of the most-significant bit of op plus 1
1233+
if(*end > offset)
1234+
{
1235+
// keep checking that remaining operands have a positive width, so
1236+
// incrementally update new_index and do not use `break`
1237+
new_index += *op_width;
1238+
}
1239+
else if(offset - *op_width <= *start)
12221240
{
1223-
extractbits_exprt result = expr;
1224-
result.src() = op;
1225-
result.index() =
1226-
from_integer(*end - (offset - *op_width), expr.index().type());
1227-
return changed(simplify_extractbits(result));
1241+
new_operands.push_back(op);
1242+
new_concat_width += *op_width;
12281243
}
12291244

12301245
offset -= *op_width;
12311246
}
1247+
1248+
if(new_operands.size() == 1)
1249+
{
1250+
extractbits_exprt result = expr;
1251+
result.src() = new_operands.front();
1252+
result.index() = from_integer(new_index, expr.index().type());
1253+
return changed(simplify_extractbits(result));
1254+
}
1255+
else if(
1256+
!new_operands.empty() &&
1257+
new_operands.size() < expr.src().operands().size())
1258+
{
1259+
extractbits_exprt result = expr;
1260+
result.src().operands() = new_operands;
1261+
to_bitvector_type(result.src().type())
1262+
.set_width(numeric_cast_v<std::size_t>(new_concat_width));
1263+
result.index() = from_integer(new_index, expr.index().type());
1264+
return changed(simplify_extractbits(result));
1265+
}
12321266
}
12331267
else if(auto eb_src = expr_try_dynamic_cast<extractbits_exprt>(expr.src()))
12341268
{
@@ -1242,11 +1276,6 @@ simplify_exprt::simplify_extractbits(const extractbits_exprt &expr)
12421276
return changed(simplify_extractbits(result));
12431277
}
12441278
}
1245-
else if(*end == 0 && *start + 1 == *width)
1246-
{
1247-
typecast_exprt tc{expr.src(), expr.type()};
1248-
return changed(simplify_typecast(tc));
1249-
}
12501279

12511280
return unchanged(expr);
12521281
}

unit/util/simplify_expr.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,3 +652,101 @@ TEST_CASE("Simplify quantifier", "[core][util]")
652652
REQUIRE(simplify_expr(forall_exprt{a, true_exprt{}}, ns) == true_exprt{});
653653
}
654654
}
655+
656+
657+
TEST_CASE("Simplify double typecast over bv types", "[core][util]")
658+
{
659+
const symbol_tablet symbol_table;
660+
const namespacet ns{symbol_table};
661+
662+
// (unsigned)(signed)x where x is unsigned and same width -> x
663+
const unsignedbv_typet u32{32};
664+
const signedbv_typet s32{32};
665+
const symbol_exprt x{"x", u32};
666+
const typecast_exprt inner{x, s32};
667+
const typecast_exprt outer{inner, u32};
668+
REQUIRE(simplify_expr(outer, ns) == x);
669+
}
670+
671+
672+
TEST_CASE("Simplify (x * n) % n", "[core][util]")
673+
{
674+
const symbol_tablet symbol_table;
675+
const namespacet ns{symbol_table};
676+
677+
const unsignedbv_typet u32{32};
678+
const symbol_exprt x{"x", u32};
679+
const constant_exprt n = from_integer(7, u32);
680+
const mult_exprt mul{x, n};
681+
const mod_exprt mod{mul, n};
682+
const auto result = simplify_expr(mod, ns);
683+
REQUIRE(result == from_integer(0, u32));
684+
}
685+
686+
687+
TEST_CASE("Simplify all-zero constant in bitand", "[core][util]")
688+
{
689+
const symbol_tablet symbol_table;
690+
const namespacet ns{symbol_table};
691+
692+
const bv_typet bv8{8};
693+
const symbol_exprt x{"x", bv8};
694+
const constant_exprt zero = bv8.all_zeros_expr();
695+
const bitand_exprt band{x, zero};
696+
const auto result = simplify_expr(band, ns);
697+
REQUIRE(result == zero);
698+
}
699+
700+
701+
TEST_CASE("Simplify bitand over concatenation and constant", "[core][util]")
702+
{
703+
const symbol_tablet symbol_table;
704+
const namespacet ns{symbol_table};
705+
706+
// (concat(a, b)) & 0x00FF -> concat(0x00, b & 0xFF)
707+
const bv_typet bv8{8};
708+
const bv_typet bv16{16};
709+
const symbol_exprt a{"a", bv8};
710+
const symbol_exprt b{"b", bv8};
711+
const concatenation_exprt cat{{a, b}, bv16};
712+
const constant_exprt mask = from_integer(0xFF, bv16);
713+
const bitand_exprt band{cat, mask};
714+
const auto result = simplify_expr(band, ns);
715+
// Result should not contain the original bitand over concatenation
716+
REQUIRE((result.id() != ID_bitand || result != band));
717+
}
718+
719+
720+
TEST_CASE("Simplify shift of concatenation", "[core][util]")
721+
{
722+
const symbol_tablet symbol_table;
723+
const namespacet ns{symbol_table};
724+
725+
// (concat(a, b)) >> 8 where a and b are 8-bit
726+
const unsignedbv_typet u8{8};
727+
const unsignedbv_typet u16{16};
728+
const symbol_exprt a{"a", u8};
729+
const symbol_exprt b{"b", u8};
730+
const concatenation_exprt cat{{a, b}, u16};
731+
const lshr_exprt shift{cat, from_integer(8, u16)};
732+
const auto result = simplify_expr(shift, ns);
733+
// Should simplify — the result should not be a shift of a concatenation
734+
REQUIRE(result.id() != ID_lshr);
735+
}
736+
737+
738+
TEST_CASE("Simplify extractbits over concatenation", "[core][util]")
739+
{
740+
const symbol_tablet symbol_table;
741+
const namespacet ns{symbol_table};
742+
743+
// extractbits(concat(a, b), 0, u8) where a,b are 8-bit -> b
744+
const unsignedbv_typet u8{8};
745+
const unsignedbv_typet u16{16};
746+
const symbol_exprt a{"a", u8};
747+
const symbol_exprt b{"b", u8};
748+
const concatenation_exprt cat{{a, b}, u16};
749+
const extractbits_exprt extract{cat, from_integer(0, u16), u8};
750+
const auto result = simplify_expr(extract, ns);
751+
REQUIRE(result == b);
752+
}

0 commit comments

Comments
 (0)