Skip to content

Commit cd4d55c

Browse files
committed
Add saturating arithmetic support to SMT2 incremental solver
Implements conversion of saturating_plus and saturating_minus expressions to SMT formulas for both signed and unsigned bit-vector types. Fixes: #8070
1 parent 1505c36 commit cd4d55c

File tree

2 files changed

+161
-1
lines changed

2 files changed

+161
-1
lines changed

regression/cbmc/saturating_arithmetric/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE no-new-smt
1+
CORE
22
main.c
33

44
^EXIT=0$

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,154 @@ static smt_termt convert_expr_to_smt(
13101310
mult_overflow.pretty());
13111311
}
13121312

1313+
static smt_termt convert_expr_to_smt(
1314+
const saturating_plus_exprt &saturating_plus,
1315+
const sub_expression_mapt &converted)
1316+
{
1317+
const auto &op_type = saturating_plus.type();
1318+
const smt_termt &left = converted.at(saturating_plus.lhs());
1319+
const smt_termt &right = converted.at(saturating_plus.rhs());
1320+
1321+
if(const auto signed_type = type_try_dynamic_cast<signedbv_typet>(op_type))
1322+
{
1323+
const std::size_t width = signed_type->get_width();
1324+
1325+
// Compute sum with one extra bit using sign extension
1326+
const auto extended_left = smt_bit_vector_theoryt::sign_extend(1)(left);
1327+
const auto extended_right = smt_bit_vector_theoryt::sign_extend(1)(right);
1328+
const auto sum = smt_bit_vector_theoryt::add(extended_left, extended_right);
1329+
1330+
// Extract the top bit (sign bit of extended result) and the original sign
1331+
// bit
1332+
const auto top_bit = smt_bit_vector_theoryt::extract(width, width)(sum);
1333+
const auto sign_bit =
1334+
smt_bit_vector_theoryt::extract(width - 1, width - 1)(sum);
1335+
1336+
// If the top two bits are equal, no overflow occurred
1337+
const auto no_overflow = smt_core_theoryt::equal(top_bit, sign_bit);
1338+
1339+
// Extract the result (lower bits)
1340+
const auto result = smt_bit_vector_theoryt::extract(width - 1, 0)(sum);
1341+
1342+
// If overflow occurred, check if it's positive or negative overflow
1343+
const auto is_positive_overflow =
1344+
smt_core_theoryt::equal(top_bit, smt_bit_vector_constant_termt{0, 1});
1345+
1346+
// Return MAX if positive overflow, MIN if negative overflow, otherwise
1347+
// result
1348+
return smt_core_theoryt::if_then_else(
1349+
no_overflow,
1350+
result,
1351+
smt_core_theoryt::if_then_else(
1352+
is_positive_overflow,
1353+
smt_bit_vector_constant_termt{power(2, width - 1) - 1, width}, // MAX
1354+
smt_bit_vector_constant_termt{
1355+
power(2, width - 1), width})); // MIN (as unsigned representation)
1356+
}
1357+
else if(
1358+
const auto unsigned_type = type_try_dynamic_cast<unsignedbv_typet>(op_type))
1359+
{
1360+
const std::size_t width = unsigned_type->get_width();
1361+
1362+
// Compute sum with one extra bit using zero extension
1363+
const auto extended_left = smt_bit_vector_theoryt::zero_extend(1)(left);
1364+
const auto extended_right = smt_bit_vector_theoryt::zero_extend(1)(right);
1365+
const auto sum = smt_bit_vector_theoryt::add(extended_left, extended_right);
1366+
1367+
// Check if the top bit is set (overflow occurred)
1368+
const auto overflow_bit =
1369+
smt_bit_vector_theoryt::extract(width, width)(sum);
1370+
const auto no_overflow = smt_core_theoryt::equal(
1371+
overflow_bit, smt_bit_vector_constant_termt{0, 1});
1372+
1373+
// Extract the result (lower bits)
1374+
const auto result = smt_bit_vector_theoryt::extract(width - 1, 0)(sum);
1375+
1376+
// Return MAX if overflow, otherwise result
1377+
return smt_core_theoryt::if_then_else(
1378+
no_overflow,
1379+
result,
1380+
smt_bit_vector_constant_termt{power(2, width) - 1, width}); // MAX
1381+
}
1382+
UNIMPLEMENTED_FEATURE(
1383+
"Generation of SMT formula for saturating plus expression: " +
1384+
saturating_plus.pretty());
1385+
}
1386+
1387+
static smt_termt convert_expr_to_smt(
1388+
const saturating_minus_exprt &saturating_minus,
1389+
const sub_expression_mapt &converted)
1390+
{
1391+
const auto &op_type = saturating_minus.type();
1392+
const smt_termt &left = converted.at(saturating_minus.lhs());
1393+
const smt_termt &right = converted.at(saturating_minus.rhs());
1394+
1395+
if(const auto signed_type = type_try_dynamic_cast<signedbv_typet>(op_type))
1396+
{
1397+
const std::size_t width = signed_type->get_width();
1398+
1399+
// Compute difference with one extra bit using sign extension
1400+
const auto extended_left = smt_bit_vector_theoryt::sign_extend(1)(left);
1401+
const auto extended_right = smt_bit_vector_theoryt::sign_extend(1)(right);
1402+
const auto diff =
1403+
smt_bit_vector_theoryt::subtract(extended_left, extended_right);
1404+
1405+
// Extract the top bit (sign bit of extended result) and the original sign
1406+
// bit
1407+
const auto top_bit = smt_bit_vector_theoryt::extract(width, width)(diff);
1408+
const auto sign_bit =
1409+
smt_bit_vector_theoryt::extract(width - 1, width - 1)(diff);
1410+
1411+
// If the top two bits are equal, no overflow occurred
1412+
const auto no_overflow = smt_core_theoryt::equal(top_bit, sign_bit);
1413+
1414+
// Extract the result (lower bits)
1415+
const auto result = smt_bit_vector_theoryt::extract(width - 1, 0)(diff);
1416+
1417+
// If overflow occurred, check if it's positive or negative overflow
1418+
const auto is_positive_overflow =
1419+
smt_core_theoryt::equal(top_bit, smt_bit_vector_constant_termt{0, 1});
1420+
1421+
// Return MAX if positive overflow, MIN if negative overflow, otherwise
1422+
// result
1423+
return smt_core_theoryt::if_then_else(
1424+
no_overflow,
1425+
result,
1426+
smt_core_theoryt::if_then_else(
1427+
is_positive_overflow,
1428+
smt_bit_vector_constant_termt{power(2, width - 1) - 1, width}, // MAX
1429+
smt_bit_vector_constant_termt{
1430+
power(2, width - 1), width})); // MIN (as unsigned representation)
1431+
}
1432+
else if(
1433+
const auto unsigned_type = type_try_dynamic_cast<unsignedbv_typet>(op_type))
1434+
{
1435+
const std::size_t width = unsigned_type->get_width();
1436+
1437+
// Compute difference with one extra bit using zero extension
1438+
const auto extended_left = smt_bit_vector_theoryt::zero_extend(1)(left);
1439+
const auto extended_right = smt_bit_vector_theoryt::zero_extend(1)(right);
1440+
const auto diff =
1441+
smt_bit_vector_theoryt::subtract(extended_left, extended_right);
1442+
1443+
// Check if the top bit is set (underflow occurred)
1444+
const auto underflow_bit =
1445+
smt_bit_vector_theoryt::extract(width, width)(diff);
1446+
const auto no_underflow = smt_core_theoryt::equal(
1447+
underflow_bit, smt_bit_vector_constant_termt{0, 1});
1448+
1449+
// Extract the result (lower bits)
1450+
const auto result = smt_bit_vector_theoryt::extract(width - 1, 0)(diff);
1451+
1452+
// Return MIN (0) if underflow, otherwise result
1453+
return smt_core_theoryt::if_then_else(
1454+
no_underflow, result, smt_bit_vector_constant_termt{0, width}); // MIN
1455+
}
1456+
UNIMPLEMENTED_FEATURE(
1457+
"Generation of SMT formula for saturating minus expression: " +
1458+
saturating_minus.pretty());
1459+
}
1460+
13131461
static smt_termt convert_expr_to_smt(
13141462
const pointer_object_exprt &pointer_object,
13151463
const sub_expression_mapt &converted)
@@ -1774,6 +1922,18 @@ static smt_termt dispatch_expr_to_smt_conversion(
17741922
{
17751923
return convert_expr_to_smt(*shl_overflow, converted);
17761924
}
1925+
if(
1926+
const auto saturating_plus =
1927+
expr_try_dynamic_cast<saturating_plus_exprt>(expr))
1928+
{
1929+
return convert_expr_to_smt(*saturating_plus, converted);
1930+
}
1931+
if(
1932+
const auto saturating_minus =
1933+
expr_try_dynamic_cast<saturating_minus_exprt>(expr))
1934+
{
1935+
return convert_expr_to_smt(*saturating_minus, converted);
1936+
}
17771937
if(const auto array_construction = expr_try_dynamic_cast<array_exprt>(expr))
17781938
{
17791939
return convert_expr_to_smt(*array_construction, converted);

0 commit comments

Comments
 (0)