Skip to content

Commit 9201345

Browse files
tedinskiadpaco-awsdanielsn
committed
Introduce simd_shuffle intrinsic (#417)
* introduce simd_shuffle intrinsic * add links to cbmc issues in simd_shuffle comments * add assert for arguments length to simd instrinsic functions * typo fix Co-authored-by: Adrian Palacios <[email protected]> * separate simd_shuffle test from other tests Co-authored-by: Adrian Palacios <[email protected]> Co-authored-by: Daniel Schwartz-Narbonne <[email protected]>
1 parent 76691fb commit 9201345

File tree

5 files changed

+118
-1
lines changed

5 files changed

+118
-1
lines changed

compiler/rustc_codegen_llvm/src/gotoc/cbmc/goto_program/expr.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ pub enum ExprValue {
146146
op: UnaryOperand,
147147
e: Expr,
148148
},
149+
/// `vec_typ x = >>> {elems0, elems1 ...} <<<`
150+
Vector {
151+
elems: Vec<Expr>,
152+
},
149153
}
150154

151155
/// Binary operators. The names are the same as in the Irep representation.
@@ -373,6 +377,21 @@ impl Expr {
373377
expr!(Array { elems }, typ)
374378
}
375379

380+
pub fn vector_expr(typ: Type, elems: Vec<Expr>) -> Self {
381+
if let Type::Vector { size, typ: value_typ } = typ.clone() {
382+
assert_eq!(size as usize, elems.len());
383+
assert!(
384+
elems.iter().all(|x| x.typ == *value_typ),
385+
"Vector type and value types don't match: \n{:?}\n{:?}",
386+
typ,
387+
elems
388+
);
389+
} else {
390+
unreachable!("Can't make a vector_val with non-vector target type {:?}", typ);
391+
}
392+
expr!(Vector { elems }, typ)
393+
}
394+
376395
/// `(__CPROVER_bool) >>> true/false <<<`. True/False as a single bit boolean.
377396
pub fn bool_constant(c: bool) -> Self {
378397
expr!(BoolConstant(c), Type::bool())

compiler/rustc_codegen_llvm/src/gotoc/cbmc/goto_program/symtab_transformer/transformer.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ pub trait Transformer: Sized {
266266
ExprValue::Typecast(child) => self.transform_expr_typecast(typ, child),
267267
ExprValue::Union { value, field } => self.transform_expr_union(typ, value, field),
268268
ExprValue::UnOp { op, e } => self.transform_expr_un_op(typ, op, e),
269+
ExprValue::Vector { elems } => self.transform_expr_vector(typ, elems),
269270
}
270271
.with_location(e.location().clone())
271272
}
@@ -282,6 +283,13 @@ pub trait Transformer: Sized {
282283
Expr::array_expr(transformed_typ, transformed_elems)
283284
}
284285

286+
/// Transform a vector expr (`vec_typ x[] = >>> {elems0, elems1 ...} <<<`)
287+
fn transform_expr_vector(&self, typ: &Type, elems: &[Expr]) -> Expr {
288+
let transformed_typ = self.transform_type(typ);
289+
let transformed_elems = elems.iter().map(|elem| self.transform_expr(elem)).collect();
290+
Expr::vector_expr(transformed_typ, transformed_elems)
291+
}
292+
285293
/// Transforms an array of expr (`typ x[width] = >>> {elem} <<<`)
286294
fn transform_expr_array_of(&self, typ: &Type, elem: &Expr) -> Expr {
287295
let transformed_typ = self.transform_type(typ);

compiler/rustc_codegen_llvm/src/gotoc/cbmc/irep/to_irep.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ impl ToIrep for ExprValue {
175175
sub: vec![e.to_irep(mm), Expr::int_constant(*offset, Type::ssize_t()).to_irep(mm)],
176176
named_sub: btree_map![],
177177
},
178-
179178
ExprValue::CBoolConstant(i) => Irep {
180179
id: IrepId::Constant,
181180
sub: vec![],
@@ -292,6 +291,11 @@ impl ToIrep for ExprValue {
292291
ExprValue::UnOp { op, e } => {
293292
Irep { id: op.to_irep_id(), sub: vec![e.to_irep(mm)], named_sub: btree_map![] }
294293
}
294+
ExprValue::Vector { elems } => Irep {
295+
id: IrepId::Vector,
296+
sub: elems.iter().map(|x| x.to_irep(mm)).collect(),
297+
named_sub: btree_map![],
298+
},
295299
}
296300
}
297301
}

compiler/rustc_codegen_llvm/src/gotoc/mir_to_goto/codegen/intrinsic.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ impl<'tcx> GotocCtx<'tcx> {
217217
}};
218218
}
219219

220+
if let Some(stripped) = intrinsic.strip_prefix("simd_shuffle") {
221+
let n: u64 = stripped.parse().unwrap();
222+
return self.codegen_intrinsic_simd_shuffle(fargs, p, cbmc_ret_ty, n);
223+
}
224+
220225
match intrinsic {
221226
"abort" => Stmt::assert_false("abort intrinsic", loc),
222227
"add_with_overflow" => codegen_op_with_overflow!(add_overflow),
@@ -401,6 +406,7 @@ impl<'tcx> GotocCtx<'tcx> {
401406
codegen_intrinsic_binop!(lshr)
402407
}
403408
}
409+
// "simd_shuffle#" => handled in an `if` preceding this match
404410
"simd_sub" => codegen_intrinsic_binop!(sub),
405411
"simd_xor" => codegen_intrinsic_binop!(bitxor),
406412
"size_of" => codegen_intrinsic_const!(),
@@ -814,6 +820,7 @@ impl<'tcx> GotocCtx<'tcx> {
814820
cbmc_ret_ty: Type,
815821
loc: Location,
816822
) -> Stmt {
823+
assert!(fargs.len() == 3, "simd_insert had unexpected arguments {:?}", fargs);
817824
let vec = fargs.remove(0);
818825
let index = fargs.remove(0);
819826
let newval = fargs.remove(0);
@@ -829,4 +836,47 @@ impl<'tcx> GotocCtx<'tcx> {
829836
loc,
830837
)
831838
}
839+
840+
/// simd_shuffle constructs a new vector from the elements of two input vectors,
841+
/// choosing values according to an input array of indexes.
842+
///
843+
/// This code mimics CBMC's `shuffle_vector_exprt::lower()` here:
844+
/// https://github.com/diffblue/cbmc/blob/develop/src/ansi-c/c_expr.cpp
845+
///
846+
/// We can't use shuffle_vector_exprt because it's not understood by the CBMC backend,
847+
/// it's immediately lowered by the C frontend.
848+
/// Issue: https://github.com/diffblue/cbmc/issues/6297
849+
pub fn codegen_intrinsic_simd_shuffle(
850+
&mut self,
851+
mut fargs: Vec<Expr>,
852+
p: &Place<'tcx>,
853+
cbmc_ret_ty: Type,
854+
n: u64,
855+
) -> Stmt {
856+
assert!(fargs.len() == 3, "simd_shuffle had unexpected arguments {:?}", fargs);
857+
// vector, size n: translated as vector types which cbmc treats as arrays
858+
let vec1 = fargs.remove(0);
859+
let vec2 = fargs.remove(0);
860+
// [u32; n]: translated wrapped in a struct
861+
let indexes = fargs.remove(0);
862+
863+
// An unsigned type here causes an invariant violation in CBMC.
864+
// Issue: https://github.com/diffblue/cbmc/issues/6298
865+
let st_rep = Type::ssize_t();
866+
let n_rep = Expr::int_constant(n, st_rep.clone());
867+
868+
// P = indexes.expanded_map(v -> if v < N then vec1[v] else vec2[v-N])
869+
let elems = (0..n)
870+
.map(|i| {
871+
let i = Expr::int_constant(i, st_rep.clone());
872+
// Must not use `indexes.index(i)` directly, because codegen wraps arrays in struct
873+
let v = self.codegen_idx_array(indexes.clone(), i).cast_to(st_rep.clone());
874+
let cond = v.clone().lt(n_rep.clone());
875+
let t = vec1.clone().index(v.clone());
876+
let e = vec2.clone().index(v.sub(n_rep.clone()));
877+
cond.ternary(t, e)
878+
})
879+
.collect();
880+
self.codegen_expr_to_place(p, Expr::vector_expr(cbmc_ret_ty, elems))
881+
}
832882
}

src/test/cbmc/SIMD/Shuffle/main.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
#![feature(repr_simd, platform_intrinsics)]
4+
5+
#[repr(simd)]
6+
#[allow(non_camel_case_types)]
7+
#[derive(Clone, Copy, PartialEq, Eq)]
8+
pub struct i64x2(i64, i64);
9+
10+
#[repr(simd)]
11+
#[allow(non_camel_case_types)]
12+
#[derive(Clone, Copy, PartialEq, Eq)]
13+
pub struct i64x4(i64, i64, i64, i64);
14+
15+
extern "platform-intrinsic" {
16+
fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
17+
fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U;
18+
}
19+
20+
fn main() {
21+
{
22+
let y = i64x2(0, 1);
23+
let z = i64x2(1, 2);
24+
const I: [u32; 2] = [1, 2];
25+
let x: i64x2 = unsafe { simd_shuffle2(y, z, I) };
26+
assert!(x.0 == 1);
27+
assert!(x.1 == 1);
28+
}
29+
{
30+
let a = i64x4(1, 2, 3, 4);
31+
let b = i64x4(5, 6, 7, 8);
32+
const I: [u32; 4] = [1, 3, 5, 7];
33+
let c: i64x4 = unsafe { simd_shuffle4(a, b, I) };
34+
assert!(c == i64x4(2, 4, 6, 8));
35+
}
36+
}

0 commit comments

Comments
 (0)