Skip to content

Commit a136328

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 15eb10a commit a136328

File tree

4 files changed

+143
-25
lines changed

4 files changed

+143
-25
lines changed

src/solvers/flattening/boolbv_shift.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ bvt boolbvt::convert_shift(const binary_exprt &expr)
5353

5454
std::size_t distance;
5555

56+
// no attempt is made to handle negative shift distances
5657
if(i<0 || i>std::numeric_limits<signed>::max())
5758
distance=0;
5859
else
@@ -66,6 +67,7 @@ bvt boolbvt::convert_shift(const binary_exprt &expr)
6667
}
6768
else
6869
{
70+
// no attempt is made to handle negative shift distances
6971
const bvt &distance=convert_bv(expr.op1());
7072
return bv_utils.shift(op, shift, distance);
7173
}

src/solvers/smt2/smt2_conv.cpp

Lines changed: 66 additions & 24 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,73 @@ 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);
1837-
1838-
if(width_op0==width_op1)
1840+
// No attempt is made to handle negative shift distances.
1841+
// The shift distance is always interpreted as binary value,
1842+
// irrespectively of its type.
1843+
std::size_t width_op = boolbv_width(shift_expr.op().type());
1844+
std::size_t width_distance = boolbv_width(distance_type);
1845+
std::size_t width_result = boolbv_width(shift_expr.type());
1846+
1847+
if(width_result != width_op)
1848+
UNEXPECTEDCASE(
1849+
"unsupported result width for " + shift_expr.id_string());
1850+
1851+
// we use the larger of the two widths
1852+
if(width_op == width_distance)
1853+
{
1854+
emit_operator(shift_expr);
1855+
convert_expr(shift_expr.op());
1856+
out << ' ';
18391857
convert_expr(shift_expr.distance());
1840-
else if(width_op0>width_op1)
1858+
out << ')'; // bv*sh
1859+
}
1860+
else if(width_op > width_distance)
18411861
{
1842-
out << "((_ zero_extend " << width_op0-width_op1 << ") ";
1862+
emit_operator(shift_expr);
1863+
convert_expr(shift_expr.op());
1864+
out << ' ';
1865+
out << "((_ zero_extend " << width_op - width_distance << ") ";
18431866
convert_expr(shift_expr.distance());
1844-
out << ")"; // zero_extend
1867+
out << ')'; // zero_extend
1868+
out << ')'; // bv*sh
18451869
}
1846-
else // width_op0<width_op1
1870+
else // width_op<width_distance -- distance is wider
18471871
{
1848-
out << "((_ extract " << width_op0-1 << " 0) ";
1872+
// enlarge the shift to match the distance operand,
1873+
// then extract again
1874+
out << "((_ extract " << width_op - 1 << " 0) ";
1875+
emit_operator(shift_expr);
1876+
1877+
// use sign extension when needed
1878+
if(shift_expr.op().type().id() == ID_signedbv)
1879+
{
1880+
out << "((_ sign_extend " << width_distance - width_op << ") ";
1881+
convert_expr(shift_expr.op());
1882+
out << ')'; // sign_extend
1883+
}
1884+
else
1885+
{
1886+
out << "((_ zero_extend " << width_distance - width_op << ") ";
1887+
convert_expr(shift_expr.op());
1888+
out << ')'; // zero_extend
1889+
}
1890+
1891+
out << ' ';
18491892
convert_expr(shift_expr.distance());
1850-
out << ")"; // extract
1893+
out << ')'; // bv*sh
1894+
out << ')'; // extract
18511895
}
18521896
}
18531897
else
@@ -1856,8 +1900,6 @@ void smt2_convt::convert_expr(const exprt &expr)
18561900
"unsupported distance type for " + shift_expr.id_string() + ": " +
18571901
distance_type.id_string());
18581902
}
1859-
1860-
out << ")"; // bv*sh
18611903
}
18621904
else
18631905
UNEXPECTEDCASE(

src/util/bitvector_expr.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ inline shift_exprt &to_shift_expr(exprt &expr)
482482
}
483483

484484
/// \brief Left shift
485+
/// Shift distances may exceed the width of the shifted operand.
486+
/// Shifts with negative shift distance do not have meaning.
485487
class shl_exprt : public shift_exprt
486488
{
487489
public:
@@ -526,6 +528,8 @@ inline shl_exprt &to_shl_expr(exprt &expr)
526528
}
527529

528530
/// \brief Arithmetic right shift
531+
/// Shift distances may exceed the width of the shifted operand.
532+
/// Shifts with negative shift distance do not have meaning.
529533
class ashr_exprt : public shift_exprt
530534
{
531535
public:
@@ -547,6 +551,8 @@ inline bool can_cast_expr<ashr_exprt>(const exprt &base)
547551
}
548552

549553
/// \brief Logical right shift
554+
/// Shift distances may exceed the width of the shifted operand.
555+
/// Shifts with negative shift distance do not have meaning.
550556
class lshr_exprt : public shift_exprt
551557
{
552558
public:

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)