Skip to content

Commit 7299745

Browse files
authored
Restore support for some SIMD arithmetic intrinsics (rust-lang#1931)
1 parent fc80db6 commit 7299745

File tree

6 files changed

+137
-124
lines changed

6 files changed

+137
-124
lines changed

kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs

Lines changed: 62 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -274,40 +274,6 @@ impl<'tcx> GotocCtx<'tcx> {
274274
}};
275275
}
276276

277-
// Intrinsics which encode a SIMD arithmetic operation with overflow check.
278-
// We expand the overflow check because CBMC overflow operations don't accept array as
279-
// argument.
280-
macro_rules! _codegen_simd_with_overflow_check {
281-
($op:ident, $overflow:ident) => {{
282-
let a = fargs.remove(0);
283-
let b = fargs.remove(0);
284-
let mut check = Expr::bool_false();
285-
if let Type::Vector { size, .. } = a.typ() {
286-
let a_size = size;
287-
if let Type::Vector { size, .. } = b.typ() {
288-
let b_size = size;
289-
assert_eq!(a_size, b_size, "Expected same length vectors",);
290-
for i in 0..*a_size {
291-
// create expression
292-
let index = Expr::int_constant(i, Type::ssize_t());
293-
let v_a = a.clone().index_array(index.clone());
294-
let v_b = b.clone().index_array(index);
295-
check = check.or(v_a.$overflow(v_b));
296-
}
297-
}
298-
}
299-
let check_stmt = self.codegen_assert(
300-
check.not(),
301-
PropertyClass::ArithmeticOverflow,
302-
format!("attempt to compute {} which would overflow", intrinsic).as_str(),
303-
loc,
304-
);
305-
let res = a.$op(b);
306-
let expr_place = self.codegen_expr_to_place(p, res);
307-
Stmt::block(vec![expr_place, check_stmt], loc)
308-
}};
309-
}
310-
311277
// Intrinsics which encode a simple wrapping arithmetic operation
312278
macro_rules! codegen_wrapping_op {
313279
($f:ident) => {{ codegen_intrinsic_binop!($f) }};
@@ -606,9 +572,14 @@ impl<'tcx> GotocCtx<'tcx> {
606572
"saturating_sub" => codegen_intrinsic_binop_with_mm!(saturating_sub),
607573
"sinf32" => codegen_simple_intrinsic!(Sinf),
608574
"sinf64" => codegen_simple_intrinsic!(Sin),
609-
"simd_add" => {
610-
unstable_codegen!(codegen_simd_with_overflow_check!(plus, add_overflow_p))
611-
}
575+
"simd_add" => self.codegen_simd_op_with_overflow(
576+
Expr::plus,
577+
Expr::add_overflow_p,
578+
fargs,
579+
intrinsic,
580+
p,
581+
loc,
582+
),
612583
"simd_and" => codegen_intrinsic_binop!(bitand),
613584
"simd_div" => unstable_codegen!(codegen_intrinsic_binop!(div)),
614585
"simd_eq" => self.codegen_simd_cmp(Expr::vector_eq, fargs, p, span, farg_types, ret_ty),
@@ -624,7 +595,14 @@ impl<'tcx> GotocCtx<'tcx> {
624595
}
625596
"simd_le" => self.codegen_simd_cmp(Expr::vector_le, fargs, p, span, farg_types, ret_ty),
626597
"simd_lt" => self.codegen_simd_cmp(Expr::vector_lt, fargs, p, span, farg_types, ret_ty),
627-
"simd_mul" => unstable_codegen!(codegen_simd_with_overflow_check!(mul, mul_overflow_p)),
598+
"simd_mul" => self.codegen_simd_op_with_overflow(
599+
Expr::mul,
600+
Expr::mul_overflow_p,
601+
fargs,
602+
intrinsic,
603+
p,
604+
loc,
605+
),
628606
"simd_ne" => {
629607
self.codegen_simd_cmp(Expr::vector_neq, fargs, p, span, farg_types, ret_ty)
630608
}
@@ -642,7 +620,14 @@ impl<'tcx> GotocCtx<'tcx> {
642620
}
643621
}
644622
// "simd_shuffle#" => handled in an `if` preceding this match
645-
"simd_sub" => unstable_codegen!(codegen_simd_with_overflow_check!(sub, sub_overflow_p)),
623+
"simd_sub" => self.codegen_simd_op_with_overflow(
624+
Expr::sub,
625+
Expr::sub_overflow_p,
626+
fargs,
627+
intrinsic,
628+
p,
629+
loc,
630+
),
646631
"simd_xor" => codegen_intrinsic_binop!(bitxor),
647632
"size_of" => codegen_intrinsic_const!(),
648633
"size_of_val" => codegen_size_align!(size),
@@ -1432,6 +1417,44 @@ impl<'tcx> GotocCtx<'tcx> {
14321417
self.codegen_expr_to_place(p, e)
14331418
}
14341419

1420+
/// Intrinsics which encode a SIMD arithmetic operation with overflow check.
1421+
/// We expand the overflow check because CBMC overflow operations don't accept array as
1422+
/// argument.
1423+
fn codegen_simd_op_with_overflow<F: FnOnce(Expr, Expr) -> Expr, G: Fn(Expr, Expr) -> Expr>(
1424+
&mut self,
1425+
op_fun: F,
1426+
overflow_fun: G,
1427+
mut fargs: Vec<Expr>,
1428+
intrinsic: &str,
1429+
p: &Place<'tcx>,
1430+
loc: Location,
1431+
) -> Stmt {
1432+
let a = fargs.remove(0);
1433+
let b = fargs.remove(0);
1434+
1435+
let a_size = a.typ().len().unwrap();
1436+
let b_size = b.typ().len().unwrap();
1437+
assert_eq!(a_size, b_size, "expected same length vectors");
1438+
1439+
let mut check = Expr::bool_false();
1440+
for i in 0..a_size {
1441+
// create expression
1442+
let index = Expr::int_constant(i, Type::ssize_t());
1443+
let v_a = a.clone().index_array(index.clone());
1444+
let v_b = b.clone().index_array(index);
1445+
check = check.or(overflow_fun(v_a, v_b));
1446+
}
1447+
let check_stmt = self.codegen_assert_assume(
1448+
check.not(),
1449+
PropertyClass::ArithmeticOverflow,
1450+
format!("attempt to compute {} which would overflow", intrinsic).as_str(),
1451+
loc,
1452+
);
1453+
let res = op_fun(a, b);
1454+
let expr_place = self.codegen_expr_to_place(p, res);
1455+
Stmt::block(vec![expr_place, check_stmt], loc)
1456+
}
1457+
14351458
/// simd_shuffle constructs a new vector from the elements of two input vectors,
14361459
/// choosing values according to an input array of indexes.
14371460
///
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FAILURE\
2+
attempt to compute simd_add which would overflow
3+
FAILURE\
4+
attempt to compute simd_sub which would overflow
5+
FAILURE\
6+
attempt to compute simd_mul which would overflow
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! This test ensures we detect overflows in SIMD arithmetic operations
5+
#![feature(repr_simd, platform_intrinsics)]
6+
7+
#[repr(simd)]
8+
#[allow(non_camel_case_types)]
9+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10+
pub struct i8x2(i8, i8);
11+
12+
extern "platform-intrinsic" {
13+
fn simd_add<T>(x: T, y: T) -> T;
14+
fn simd_sub<T>(x: T, y: T) -> T;
15+
fn simd_mul<T>(x: T, y: T) -> T;
16+
}
17+
18+
#[kani::proof]
19+
fn main() {
20+
let a = kani::any();
21+
let b = kani::any();
22+
let simd_a = i8x2(a, a);
23+
let simd_b = i8x2(b, b);
24+
25+
unsafe {
26+
let _ = simd_add(simd_a, simd_b);
27+
let _ = simd_sub(simd_a, simd_b);
28+
let _ = simd_mul(simd_a, simd_b);
29+
}
30+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! This test doesn't work because support for SIMD intrinsics isn't available
5+
//! at the moment in Kani. Support to be added in
6+
//! <https://github.com/model-checking/kani/issues/1148>
7+
#![feature(repr_simd, platform_intrinsics)]
8+
9+
#[repr(simd)]
10+
#[allow(non_camel_case_types)]
11+
#[derive(Clone, Copy, PartialEq, Eq)]
12+
pub struct i8x2(i8, i8);
13+
14+
extern "platform-intrinsic" {
15+
fn simd_add<T>(x: T, y: T) -> T;
16+
fn simd_sub<T>(x: T, y: T) -> T;
17+
fn simd_mul<T>(x: T, y: T) -> T;
18+
}
19+
20+
macro_rules! verify_no_overflow {
21+
($cf: ident, $uf: ident) => {{
22+
let a: i8 = kani::any();
23+
let b: i8 = kani::any();
24+
let checked = a.$cf(b);
25+
kani::assume(checked.is_some());
26+
let simd_a = i8x2(a, a);
27+
let simd_b = i8x2(b, b);
28+
let unchecked: i8x2 = unsafe { $uf(simd_a, simd_b) };
29+
assert!(checked.unwrap() == unchecked.0);
30+
assert!(checked.unwrap() == unchecked.1);
31+
}};
32+
}
33+
34+
#[kani::proof]
35+
fn test_simd_ops() {
36+
verify_no_overflow!(checked_add, simd_add);
37+
verify_no_overflow!(checked_sub, simd_sub);
38+
verify_no_overflow!(checked_mul, simd_mul);
39+
}

tests/kani/Intrinsics/SIMD/Operators/main_fixme.rs

Lines changed: 0 additions & 45 deletions
This file was deleted.

tests/kani/Intrinsics/SIMD/Operators/overflow.rs

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)