Skip to content

Commit c99dcc8

Browse files
authored
Merge pull request #6647 from tautschnig/feature/saturating-arithmetic
Add saturating addition/subtraction
2 parents 57c59fc + 10ea245 commit c99dcc8

File tree

20 files changed

+744
-5
lines changed

20 files changed

+744
-5
lines changed

regression/cbmc/SIMD1/main.c

+44
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#include <assert.h>
2+
#include <limits.h>
3+
24
#ifdef _MSC_VER
35
# include <intrin.h>
6+
# ifdef _WIN64
7+
# define _mm_extract_pi16(a, b) _mm_extract_epi16(a, b)
8+
# endif
49
#else
510
# include <immintrin.h>
611
#endif
@@ -10,5 +15,44 @@ int main()
1015
__m128i values = _mm_setr_epi32(0x1234, 0x2345, 0x3456, 0x4567);
1116
int val1 = _mm_extract_epi32(values, 0);
1217
assert(val1 == 0x1234);
18+
19+
#ifndef _WIN64
20+
__m64 a = _mm_setr_pi16(SHRT_MIN, 10, SHRT_MIN + 1, SHRT_MAX);
21+
__m64 b = _mm_set_pi16(1, 1, 10, 1);
22+
__m64 result = _mm_subs_pi16(a, b);
23+
#else
24+
__m128i a = _mm_setr_epi16(SHRT_MIN, 10, SHRT_MIN + 1, SHRT_MAX, 0, 0, 0, 0);
25+
__m128i b = _mm_set_epi16(0, 0, 0, 0, 1, 1, 10, 1);
26+
__m128i result = _mm_subs_epi16(a, b);
27+
#endif
28+
short s1 = _mm_extract_pi16(result, 0);
29+
assert(s1 == SHRT_MIN);
30+
short s2 = _mm_extract_pi16(result, 1);
31+
assert(s2 == 0);
32+
short s3 = _mm_extract_pi16(result, 2);
33+
assert(s3 == SHRT_MIN);
34+
short s4 = _mm_extract_pi16(result, 3);
35+
assert(s4 == SHRT_MAX - 1);
36+
37+
#ifndef _WIN64
38+
result = _mm_adds_pi16(a, b);
39+
#else
40+
result = _mm_adds_epi16(a, b);
41+
#endif
42+
s1 = _mm_extract_pi16(result, 0);
43+
assert(s1 == SHRT_MIN + 1);
44+
s2 = _mm_extract_pi16(result, 1);
45+
assert(s2 == 20);
46+
s3 = _mm_extract_pi16(result, 2);
47+
assert(s3 == SHRT_MIN + 2);
48+
s4 = _mm_extract_pi16(result, 3);
49+
assert(s4 == SHRT_MAX);
50+
51+
__m128i x = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, 0);
52+
__m128i y = _mm_setr_epi16(1, 0, 0, 0, 0, 0, 0, 0);
53+
__m128i result128 = _mm_subs_epu16(x, y);
54+
short s = _mm_extract_epi16(result128, 0);
55+
assert(s == 0);
56+
1357
return 0;
1458
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <limits.h>
2+
3+
int main()
4+
{
5+
__CPROVER_assert(
6+
__CPROVER_saturating_minus(INT_MIN, 1) == INT_MIN,
7+
"subtracting from INT_MIN");
8+
__CPROVER_assert(
9+
__CPROVER_saturating_plus(LONG_MAX, 1l) == LONG_MAX, "adding to LONG_MAX");
10+
__CPROVER_assert(
11+
__CPROVER_saturating_minus(-1, INT_MIN) == INT_MAX, "no overflow");
12+
__CPROVER_assert(
13+
__CPROVER_saturating_plus(ULONG_MAX, 1ul) == ULONG_MAX,
14+
"adding to ULONG_MAX");
15+
__CPROVER_assert(
16+
__CPROVER_saturating_minus(10ul, ULONG_MAX) == 0, "subtracting ULONG_MAX");
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--program-only
4+
ASSERT\(__CPROVER_saturating_minus\(.*\)
5+
ASSERT\(__CPROVER_saturating_plus\(.*\)
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--show-goto-functions
4+
ASSERT saturating-\(.*\)
5+
ASSERT saturating\+\(.*\)
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE broken-smt-backend
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

regression/validate-trace-xml-schema/check.py

+2
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@
2929
['enum_is_in_range', 'format.desc'],
3030
['r_w_ok9', 'simplify.desc'],
3131
['reachability-slice-interproc2', 'test.desc'],
32+
['saturating_arithmetric', 'output-goto.desc'],
3233
# this one wants show-properties instead producing a trace
3334
['show_properties1', 'test.desc'],
3435
# program-only instead of trace
3536
['vla1', 'program-only.desc'],
3637
['Pointer_Arithmetic19', 'test.desc'],
3738
['Quantifiers-simplify', 'simplify_not_forall.desc'],
3839
['array-cell-sensitivity15', 'test.desc'],
40+
['saturating_arithmetric', 'output-formula.desc'],
3941
# these test for invalid command line handling
4042
['bad_option', 'test_multiple.desc'],
4143
['bad_option', 'test.desc'],

src/ansi-c/c_typecheck_base.h

+2
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ class c_typecheck_baset:
205205
exprt typecheck_builtin_overflow(
206206
side_effect_expr_function_callt &expr,
207207
const irep_idt &arith_op);
208+
exprt
209+
typecheck_saturating_arithmetic(const side_effect_expr_function_callt &expr);
208210
virtual optionalt<symbol_exprt> typecheck_gcc_polymorphic_builtin(
209211
const irep_idt &identifier,
210212
const exprt::operandst &arguments,

src/ansi-c/c_typecheck_expr.cpp

+57-3
Original file line numberDiff line numberDiff line change
@@ -1967,6 +1967,15 @@ void c_typecheck_baset::typecheck_side_effect_function_call(
19671967

19681968
return;
19691969
}
1970+
else if(
1971+
identifier == CPROVER_PREFIX "saturating_minus" ||
1972+
identifier == CPROVER_PREFIX "saturating_plus")
1973+
{
1974+
exprt result = typecheck_saturating_arithmetic(expr);
1975+
expr.swap(result);
1976+
1977+
return;
1978+
}
19701979
else if(
19711980
auto gcc_polymorphic = typecheck_gcc_polymorphic_builtin(
19721981
identifier, expr.arguments(), f_op.source_location()))
@@ -3298,6 +3307,35 @@ exprt c_typecheck_baset::typecheck_builtin_overflow(
32983307
expr.source_location()};
32993308
}
33003309

3310+
exprt c_typecheck_baset::typecheck_saturating_arithmetic(
3311+
const side_effect_expr_function_callt &expr)
3312+
{
3313+
const irep_idt &identifier = to_symbol_expr(expr.function()).get_identifier();
3314+
3315+
// check function signature
3316+
if(expr.arguments().size() != 2)
3317+
{
3318+
std::ostringstream error_message;
3319+
error_message << expr.source_location().as_string() << ": " << identifier
3320+
<< " takes exactly two arguments, but "
3321+
<< expr.arguments().size() << " were provided";
3322+
throw invalid_source_file_exceptiont{error_message.str()};
3323+
}
3324+
3325+
exprt result;
3326+
if(identifier == CPROVER_PREFIX "saturating_minus")
3327+
result = saturating_minus_exprt{expr.arguments()[0], expr.arguments()[1]};
3328+
else if(identifier == CPROVER_PREFIX "saturating_plus")
3329+
result = saturating_plus_exprt{expr.arguments()[0], expr.arguments()[1]};
3330+
else
3331+
UNREACHABLE;
3332+
3333+
typecheck_expr_binary_arithmetic(result);
3334+
result.add_source_location() = expr.source_location();
3335+
3336+
return result;
3337+
}
3338+
33013339
/// Typecheck the parameters in a function call expression, and where
33023340
/// necessary, make implicit casts around parameters explicit.
33033341
void c_typecheck_baset::typecheck_function_call_arguments(
@@ -3499,9 +3537,15 @@ void c_typecheck_baset::typecheck_expr_binary_arithmetic(exprt &expr)
34993537
return;
35003538
}
35013539

3502-
// promote!
3503-
3504-
implicit_typecast_arithmetic(op0, op1);
3540+
if(expr.id() == ID_saturating_minus || expr.id() == ID_saturating_plus)
3541+
{
3542+
implicit_typecast(op1, o_type0);
3543+
}
3544+
else
3545+
{
3546+
// promote!
3547+
implicit_typecast_arithmetic(op0, op1);
3548+
}
35053549

35063550
const typet &type0 = op0.type();
35073551
const typet &type1 = op1.type();
@@ -3562,6 +3606,16 @@ void c_typecheck_baset::typecheck_expr_binary_arithmetic(exprt &expr)
35623606
}
35633607
}
35643608
}
3609+
else if(expr.id() == ID_saturating_minus || expr.id() == ID_saturating_plus)
3610+
{
3611+
if(
3612+
type0 == type1 &&
3613+
(type0.id() == ID_signedbv || type0.id() == ID_unsignedbv))
3614+
{
3615+
expr.type() = type0;
3616+
return;
3617+
}
3618+
}
35653619

35663620
error().source_location = expr.source_location();
35673621
error() << "operator '" << expr.id() << "' not defined for types '"

src/ansi-c/expr2c.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -3977,6 +3977,8 @@ optionalt<std::string> expr2ct::convert_function(const exprt &src)
39773977
{ID_object_size, "OBJECT_SIZE"},
39783978
{ID_pointer_object, "POINTER_OBJECT"},
39793979
{ID_pointer_offset, "POINTER_OFFSET"},
3980+
{ID_saturating_minus, CPROVER_PREFIX "saturating_minus"},
3981+
{ID_saturating_plus, CPROVER_PREFIX "saturating_plus"},
39803982
{ID_r_ok, "R_OK"},
39813983
{ID_w_ok, "W_OK"},
39823984
{ID_rw_ok, "RW_OK"},

src/ansi-c/library/gcc.c

+85
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,24 @@ __CPROVER_HIDE:;
192192
return size <= sizeof(__CPROVER_size_t);
193193
}
194194

195+
/* FUNCTION: __builtin_ia32_vec_ext_v4hi */
196+
197+
typedef short __gcc_v4hi __attribute__((__vector_size__(8)));
198+
199+
short __builtin_ia32_vec_ext_v4hi(__gcc_v4hi vec, int offset)
200+
{
201+
return *((short *)&vec + offset);
202+
}
203+
204+
/* FUNCTION: __builtin_ia32_vec_ext_v8hi */
205+
206+
typedef short __gcc_v8hi __attribute__((__vector_size__(16)));
207+
208+
short __builtin_ia32_vec_ext_v8hi(__gcc_v8hi vec, int offset)
209+
{
210+
return *((short *)&vec + offset);
211+
}
212+
195213
/* FUNCTION: __builtin_ia32_vec_ext_v4si */
196214

197215
typedef int __gcc_v4si __attribute__((__vector_size__(16)));
@@ -227,3 +245,70 @@ float __builtin_ia32_vec_ext_v4sf(__gcc_v4sf vec, int offset)
227245
{
228246
return *((float *)&vec + offset);
229247
}
248+
249+
/* FUNCTION: __builtin_ia32_psubsw128 */
250+
251+
#ifndef LIBRARY_CHECK
252+
typedef short __gcc_v8hi __attribute__((__vector_size__(16)));
253+
#else
254+
__gcc_v8hi __CPROVER_saturating_minus();
255+
#endif
256+
257+
__gcc_v8hi __builtin_ia32_psubsw128(__gcc_v8hi a, __gcc_v8hi b)
258+
{
259+
return __CPROVER_saturating_minus(a, b);
260+
}
261+
262+
/* FUNCTION: __builtin_ia32_psubusw128 */
263+
264+
#ifndef LIBRARY_CHECK
265+
typedef short __gcc_v8hi __attribute__((__vector_size__(16)));
266+
#endif
267+
268+
__gcc_v8hi __builtin_ia32_psubusw128(__gcc_v8hi a, __gcc_v8hi b)
269+
{
270+
typedef unsigned short v8hi_u __attribute__((__vector_size__(16)));
271+
return (__gcc_v8hi)__CPROVER_saturating_minus((v8hi_u)a, (v8hi_u)b);
272+
}
273+
274+
/* FUNCTION: __builtin_ia32_paddsw */
275+
276+
#ifndef LIBRARY_CHECK
277+
typedef short __gcc_v4hi __attribute__((__vector_size__(8)));
278+
#else
279+
__gcc_v4hi __CPROVER_saturating_plus();
280+
#endif
281+
282+
__gcc_v4hi __builtin_ia32_paddsw(__gcc_v4hi a, __gcc_v4hi b)
283+
{
284+
return __CPROVER_saturating_plus(a, b);
285+
}
286+
287+
/* FUNCTION: __builtin_ia32_psubsw */
288+
289+
#ifndef LIBRARY_CHECK
290+
typedef short __gcc_v4hi __attribute__((__vector_size__(8)));
291+
#else
292+
__gcc_v4hi __CPROVER_saturating_minus_v4hi(__gcc_v4hi, __gcc_v4hi);
293+
# define __CPROVER_saturating_minus __CPROVER_saturating_minus_v4hi
294+
#endif
295+
296+
__gcc_v4hi __builtin_ia32_psubsw(__gcc_v4hi a, __gcc_v4hi b)
297+
{
298+
return __CPROVER_saturating_minus(a, b);
299+
}
300+
301+
#ifdef LIBRARY_CHECK
302+
# undef __CPROVER_saturating_minus
303+
#endif
304+
305+
/* FUNCTION: __builtin_ia32_vec_init_v4hi */
306+
307+
#ifndef LIBRARY_CHECK
308+
typedef short __gcc_v4hi __attribute__((__vector_size__(8)));
309+
#endif
310+
311+
__gcc_v4hi __builtin_ia32_vec_init_v4hi(short e0, short e1, short e2, short e3)
312+
{
313+
return (__gcc_v4hi){e0, e1, e2, e3};
314+
}

0 commit comments

Comments
 (0)