Skip to content

Commit e6015a2

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 e6015a2

File tree

2 files changed

+157
-1
lines changed

2 files changed

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

0 commit comments

Comments
 (0)