Skip to content

Commit 94d18d2

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 4fe3ade commit 94d18d2

File tree

2 files changed

+149
-1
lines changed

2 files changed

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

0 commit comments

Comments
 (0)