Skip to content

Commit 365bd7b

Browse files
committed
SMT-LIB2: shifts with wider shift distances
This fixes the case of converting shifts where the shift distance is wider than the shift operand to SMT-LIB2. SMT-LIB2 requires that the width of the shift operand and the shift distance is the same. To not lose leading bits of the shift distance, use the wider one of the two as the width of the shift, and truncate the result when needed.
1 parent af0b511 commit 365bd7b

File tree

2 files changed

+131
-24
lines changed

2 files changed

+131
-24
lines changed

src/solvers/smt2/smt2_conv.cpp

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,21 +1801,21 @@ void smt2_convt::convert_expr(const exprt &expr)
18011801
type.id()==ID_signedbv ||
18021802
type.id()==ID_bv)
18031803
{
1804-
if(shift_expr.id() == ID_ashr)
1805-
out << "(bvashr ";
1806-
else if(shift_expr.id() == ID_lshr)
1807-
out << "(bvlshr ";
1808-
else if(shift_expr.id() == ID_shl)
1809-
out << "(bvshl ";
1810-
else
1811-
UNREACHABLE;
1812-
1813-
convert_expr(shift_expr.op());
1814-
out << " ";
1815-
18161804
// SMT2 requires the shift distance to have the same width as
18171805
// the value that is shifted -- odd!
18181806

1807+
auto emit_operator = [this](const exprt &shift_expr)
1808+
{
1809+
if(shift_expr.id() == ID_ashr)
1810+
out << "(bvashr ";
1811+
else if(shift_expr.id() == ID_lshr)
1812+
out << "(bvlshr ";
1813+
else if(shift_expr.id() == ID_shl)
1814+
out << "(bvshl ";
1815+
else
1816+
UNREACHABLE;
1817+
};
1818+
18191819
const auto &distance_type = shift_expr.distance().type();
18201820
if(distance_type.id() == ID_integer || distance_type.id() == ID_natural)
18211821
{
@@ -1825,29 +1825,70 @@ void smt2_convt::convert_expr(const exprt &expr)
18251825
// shift distance must be bit vector
18261826
std::size_t width_op0 = boolbv_width(shift_expr.op().type());
18271827
exprt tmp=from_integer(i, unsignedbv_typet(width_op0));
1828+
1829+
emit_operator(shift_expr);
1830+
convert_expr(shift_expr.op());
1831+
out << ' ';
18281832
convert_expr(tmp);
1833+
out << ')'; // bv*sh
18291834
}
18301835
else if(
18311836
distance_type.id() == ID_signedbv ||
18321837
distance_type.id() == ID_unsignedbv ||
18331838
distance_type.id() == ID_c_enum || distance_type.id() == ID_c_bool)
18341839
{
1835-
std::size_t width_op0 = boolbv_width(shift_expr.op().type());
1836-
std::size_t width_op1 = boolbv_width(distance_type);
1840+
std::size_t width_op = boolbv_width(shift_expr.op().type());
1841+
std::size_t width_distance = boolbv_width(distance_type);
1842+
std::size_t width_result = boolbv_width(shift_expr.type());
1843+
1844+
if(width_result != width_op)
1845+
UNEXPECTEDCASE(
1846+
"unsupported result width for " + shift_expr.id_string());
18371847

1838-
if(width_op0==width_op1)
1848+
// we use the larger of the two widths
1849+
if(width_op == width_distance)
1850+
{
1851+
emit_operator(shift_expr);
1852+
convert_expr(shift_expr.op());
1853+
out << ' ';
18391854
convert_expr(shift_expr.distance());
1840-
else if(width_op0>width_op1)
1855+
out << ')'; // bv*sh
1856+
}
1857+
else if(width_op > width_distance)
18411858
{
1842-
out << "((_ zero_extend " << width_op0-width_op1 << ") ";
1859+
emit_operator(shift_expr);
1860+
convert_expr(shift_expr.op());
1861+
out << ' ';
1862+
out << "((_ zero_extend " << width_op - width_distance << ") ";
18431863
convert_expr(shift_expr.distance());
1844-
out << ")"; // zero_extend
1864+
out << ')'; // zero_extend
1865+
out << ')'; // bv*sh
18451866
}
1846-
else // width_op0<width_op1
1867+
else // width_op<width_distance -- distance is wider
18471868
{
1848-
out << "((_ extract " << width_op0-1 << " 0) ";
1869+
// enlarge the shift to match the distance operand,
1870+
// then extract again
1871+
out << "((_ extract " << width_op - 1 << " 0) ";
1872+
emit_operator(shift_expr);
1873+
1874+
// use sign extension when needed
1875+
if(shift_expr.op().type().id() == ID_signedbv)
1876+
{
1877+
out << "((_ sign_extend " << width_distance - width_op << ") ";
1878+
convert_expr(shift_expr.op());
1879+
out << ')'; // sign_extend
1880+
}
1881+
else
1882+
{
1883+
out << "((_ zero_extend " << width_distance - width_op << ") ";
1884+
convert_expr(shift_expr.op());
1885+
out << ')'; // zero_extend
1886+
}
1887+
1888+
out << ' ';
18491889
convert_expr(shift_expr.distance());
1850-
out << ")"; // extract
1890+
out << ')'; // bv*sh
1891+
out << ')'; // extract
18511892
}
18521893
}
18531894
else
@@ -1856,8 +1897,6 @@ void smt2_convt::convert_expr(const exprt &expr)
18561897
"unsupported distance type for " + shift_expr.id_string() + ": " +
18571898
distance_type.id_string());
18581899
}
1859-
1860-
out << ")"; // bv*sh
18611900
}
18621901
else
18631902
UNEXPECTEDCASE(

unit/solvers/smt2/smt2_conv.cpp

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,76 @@
11
// Author: Diffblue Ltd.
22

3-
#include <testing-utils/use_catch.h>
3+
#include <util/bitvector_expr.h>
4+
#include <util/namespace.h>
5+
#include <util/symbol_table.h>
46

57
#include <solvers/smt2/smt2_conv.h>
8+
#include <testing-utils/use_catch.h>
9+
10+
#include <sstream>
11+
12+
/// helper class for testing smt2_convt
13+
class smt2_conv_testt : public smt2_convt
14+
{
15+
public:
16+
smt2_conv_testt(const namespacet &_ns, std::ostream &_out)
17+
: smt2_convt{_ns, "", "", "", smt2_convt::solvert::GENERIC, _out}
18+
{
19+
}
20+
21+
void convert_expr_public(const exprt &expr)
22+
{
23+
convert_expr(expr);
24+
}
25+
};
26+
27+
static std::string smt2(const exprt &expr)
28+
{
29+
symbol_tablet symbol_table;
30+
namespacet ns{symbol_table};
31+
std::ostringstream out;
32+
smt2_conv_testt smt2_conv{ns, out};
33+
std::size_t length = out.str().size();
34+
smt2_conv.convert_expr_public(expr);
35+
return std::string{out.str(), length, std::string::npos};
36+
}
37+
38+
TEST_CASE("smt2_convt conversion for shifts", "[core][solvers][smt2]")
39+
{
40+
GIVEN("A shift expression with equal-with operands")
41+
{
42+
auto op = symbol_exprt{"op", unsignedbv_typet{8}};
43+
auto distance = symbol_exprt{"dist", unsignedbv_typet{8}};
44+
auto shift = shl_exprt{op, distance};
45+
REQUIRE(smt2(shift) == "(bvshl op dist)");
46+
}
47+
48+
GIVEN("A shift expression with an unsigned shift operand and wider distance")
49+
{
50+
auto op = symbol_exprt{"op", unsignedbv_typet{8}};
51+
auto distance = symbol_exprt{"dist", unsignedbv_typet{10}};
52+
auto shift = shl_exprt{op, distance};
53+
REQUIRE(
54+
smt2(shift) == "((_ extract 7 0) (bvshl ((_ zero_extend 2) op) dist))");
55+
}
56+
57+
GIVEN("A shift expression with a signed shift operand and wider distance")
58+
{
59+
auto op = symbol_exprt{"op", signedbv_typet{8}};
60+
auto distance = symbol_exprt{"dist", unsignedbv_typet{10}};
61+
auto shift = ashr_exprt{op, distance};
62+
REQUIRE(
63+
smt2(shift) == "((_ extract 7 0) (bvashr ((_ sign_extend 2) op) dist))");
64+
}
65+
66+
GIVEN("A shift expression with a wider shift operand")
67+
{
68+
auto op = symbol_exprt{"op", unsignedbv_typet{10}};
69+
auto distance = symbol_exprt{"dist", unsignedbv_typet{8}};
70+
auto shift = shl_exprt{op, distance};
71+
REQUIRE(smt2(shift) == "(bvshl op ((_ zero_extend 2) dist))");
72+
}
73+
}
674

775
TEST_CASE(
876
"smt2_convt::convert_identifier character escaping.",

0 commit comments

Comments
 (0)