Skip to content

Commit 6cd090c

Browse files
committed
Simplify shifts of concatenations
We may be able to drop some of the operands of the concatenation and use all-zeros instead.
1 parent 6df204e commit 6cd090c

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

src/util/simplify_expr_int.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,54 @@ simplify_exprt::simplify_shifts(const shift_exprt &expr)
10331033
if(*distance == 0)
10341034
return expr.op();
10351035

1036+
if(
1037+
expr.op().id() == ID_concatenation &&
1038+
(expr.id() == ID_lshr || expr.id() == ID_shl))
1039+
{
1040+
mp_integer offset = to_bitvector_type(expr.op().type()).get_width();
1041+
mp_integer bits_seen = 0;
1042+
for(std::size_t i = 0; i < expr.op().operands().size(); ++i)
1043+
{
1044+
if(expr.id() == ID_lshr && offset == distance)
1045+
{
1046+
exprt::operandst new_operands;
1047+
new_operands.reserve(i + 1);
1048+
new_operands.push_back(
1049+
from_integer(0, bv_typet{numeric_cast_v<std::size_t>(*distance)}));
1050+
new_operands.insert(
1051+
new_operands.end(),
1052+
expr.op().operands().begin(),
1053+
expr.op().operands().begin() + i);
1054+
concatenation_exprt new_concat = to_concatenation_expr(expr.op());
1055+
new_concat.operands() = std::move(new_operands);
1056+
return changed(simplify_concatenation(new_concat));
1057+
}
1058+
1059+
auto op_width = pointer_offset_bits(expr.op().operands()[i].type(), ns);
1060+
1061+
if(!op_width.has_value() || *op_width <= 0)
1062+
return unchanged(expr);
1063+
1064+
offset -= *op_width;
1065+
bits_seen += *op_width;
1066+
1067+
if(expr.id() == ID_shl && bits_seen == distance)
1068+
{
1069+
exprt::operandst new_operands;
1070+
new_operands.reserve(expr.op().operands().size() - i);
1071+
new_operands.insert(
1072+
new_operands.end(),
1073+
expr.op().operands().begin() + i + 1,
1074+
expr.op().operands().end());
1075+
new_operands.push_back(
1076+
from_integer(0, bv_typet{numeric_cast_v<std::size_t>(*distance)}));
1077+
concatenation_exprt new_concat = to_concatenation_expr(expr.op());
1078+
new_concat.operands() = std::move(new_operands);
1079+
return changed(simplify_concatenation(new_concat));
1080+
}
1081+
}
1082+
}
1083+
10361084
auto value = numeric_cast<mp_integer>(expr.op());
10371085

10381086
if(

unit/util/simplify_expr.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,3 +652,85 @@ 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+

0 commit comments

Comments
 (0)