From 875f50a8ee18297d7dc10e5fd7a200d902921f04 Mon Sep 17 00:00:00 2001 From: David Stygstra Date: Tue, 9 Jun 2015 17:45:45 -0400 Subject: [PATCH] Support checked Shl/Shr on SIMD types --- src/librustc_trans/trans/expr.rs | 63 ++++++-- src/test/run-fail/overflowing-simd-lsh-1.rs | 23 +++ src/test/run-fail/overflowing-simd-lsh-2.rs | 23 +++ src/test/run-fail/overflowing-simd-lsh-3.rs | 23 +++ src/test/run-fail/overflowing-simd-lsh-4.rs | 49 +++++++ src/test/run-fail/overflowing-simd-rsh-1.rs | 23 +++ src/test/run-fail/overflowing-simd-rsh-2.rs | 23 +++ src/test/run-fail/overflowing-simd-rsh-3.rs | 23 +++ src/test/run-fail/overflowing-simd-rsh-4.rs | 49 +++++++ src/test/run-pass/issue-24258.rs | 26 ++++ src/test/run-pass/simd-shift-near-oflo.rs | 154 ++++++++++++++++++++ 11 files changed, 468 insertions(+), 11 deletions(-) create mode 100644 src/test/run-fail/overflowing-simd-lsh-1.rs create mode 100644 src/test/run-fail/overflowing-simd-lsh-2.rs create mode 100644 src/test/run-fail/overflowing-simd-lsh-3.rs create mode 100644 src/test/run-fail/overflowing-simd-lsh-4.rs create mode 100644 src/test/run-fail/overflowing-simd-rsh-1.rs create mode 100644 src/test/run-fail/overflowing-simd-rsh-2.rs create mode 100644 src/test/run-fail/overflowing-simd-rsh-3.rs create mode 100644 src/test/run-fail/overflowing-simd-rsh-4.rs create mode 100644 src/test/run-pass/issue-24258.rs create mode 100644 src/test/run-pass/simd-shift-near-oflo.rs diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 48deceeeef7..92b034e9118 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -52,7 +52,7 @@ pub use self::Dest::*; use self::lazy_binop_ty::*; use back::abi; -use llvm::{self, ValueRef}; +use llvm::{self, ValueRef, TypeKind}; use middle::check_const; use middle::def; use middle::lang_items::CoerceUnsizedTraitLangItem; @@ -2455,12 +2455,10 @@ impl OverflowOpViaInputCheck { // (since that is where the 32/64 distinction is relevant) but // the mask's type must match the RHS type (since they will // both be fed into a and-binop) - let invert_mask = !shift_mask_val(lhs_llty); - let invert_mask = C_integral(rhs_llty, invert_mask, true); + let invert_mask = shift_mask_val(bcx, lhs_llty, rhs_llty, true); let outer_bits = And(bcx, rhs, invert_mask, binop_debug_loc); - let cond = ICmp(bcx, llvm::IntNE, outer_bits, - C_integral(rhs_llty, 0, false), binop_debug_loc); + let cond = build_nonzero_check(bcx, outer_bits, binop_debug_loc); let result = match *self { OverflowOpViaInputCheck::Shl => build_unchecked_lshift(bcx, lhs, rhs, binop_debug_loc), @@ -2476,9 +2474,46 @@ impl OverflowOpViaInputCheck { } } -fn shift_mask_val(llty: Type) -> u64 { - // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. - llty.int_width() - 1 +fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + llty: Type, + mask_llty: Type, + invert: bool) -> ValueRef { + let kind = llty.kind(); + match kind { + TypeKind::Integer => { + // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. + let val = llty.int_width() - 1; + if invert { + C_integral(mask_llty, !val, true) + } else { + C_integral(mask_llty, val, false) + } + }, + TypeKind::Vector => { + let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert); + VectorSplat(bcx, mask_llty.vector_length(), mask) + }, + _ => panic!("shift_mask_val: expected Integer or Vector, found {:?}", kind), + } +} + +// Check if an integer or vector contains a nonzero element. +fn build_nonzero_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + value: ValueRef, + binop_debug_loc: DebugLoc) -> ValueRef { + let llty = val_ty(value); + let kind = llty.kind(); + match kind { + TypeKind::Integer => ICmp(bcx, llvm::IntNE, value, C_null(llty), binop_debug_loc), + TypeKind::Vector => { + // Check if any elements of the vector are nonzero by treating + // it as a wide integer and checking if the integer is nonzero. + let width = llty.vector_length() as u64 * llty.element_type().int_width(); + let int_value = BitCast(bcx, value, Type::ix(bcx.ccx(), width)); + build_nonzero_check(bcx, int_value, binop_debug_loc) + }, + _ => panic!("build_nonzero_check: expected Integer or Vector, found {:?}", kind), + } } // To avoid UB from LLVM, these two functions mask RHS with an @@ -2504,7 +2539,14 @@ fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let rhs = base::cast_shift_expr_rhs(bcx, ast::BinOp_::BiShr, lhs, rhs); // #1877, #10183: Ensure that input is always valid let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); - let is_signed = ty::type_is_signed(lhs_t); + let tcx = bcx.tcx(); + let is_simd = ty::type_is_simd(tcx, lhs_t); + let intype = if is_simd { + ty::simd_type(tcx, lhs_t) + } else { + lhs_t + }; + let is_signed = ty::type_is_signed(intype); if is_signed { AShr(bcx, lhs, rhs, binop_debug_loc) } else { @@ -2516,8 +2558,7 @@ fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, rhs: ValueRef, debug_loc: DebugLoc) -> ValueRef { let rhs_llty = val_ty(rhs); - let mask = shift_mask_val(rhs_llty); - And(bcx, rhs, C_integral(rhs_llty, mask, false), debug_loc) + And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc) } fn with_overflow_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, oop: OverflowOp, info: NodeIdAndSpan, diff --git a/src/test/run-fail/overflowing-simd-lsh-1.rs b/src/test/run-fail/overflowing-simd-lsh-1.rs new file mode 100644 index 00000000000..a3bce00ee07 --- /dev/null +++ b/src/test/run-fail/overflowing-simd-lsh-1.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'shift operation overflowed' +// compile-flags: -C debug-assertions + +#![feature(core_simd)] + +use std::simd::i32x4; + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn main() { + let _x = i32x4(1, 0, 0, 0) << id(i32x4(32, 0, 0, 0)); +} diff --git a/src/test/run-fail/overflowing-simd-lsh-2.rs b/src/test/run-fail/overflowing-simd-lsh-2.rs new file mode 100644 index 00000000000..e119bd03c88 --- /dev/null +++ b/src/test/run-fail/overflowing-simd-lsh-2.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'shift operation overflowed' +// compile-flags: -C debug-assertions + +#![feature(core_simd)] + +use std::simd::i32x4; + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn main() { + let _x = i32x4(1, 0, 0, 0) << id(i32x4(-1, 0, 0, 0)); +} diff --git a/src/test/run-fail/overflowing-simd-lsh-3.rs b/src/test/run-fail/overflowing-simd-lsh-3.rs new file mode 100644 index 00000000000..4fb7fa958f0 --- /dev/null +++ b/src/test/run-fail/overflowing-simd-lsh-3.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'shift operation overflowed' +// compile-flags: -C debug-assertions + +#![feature(core_simd)] + +use std::simd::u64x2; + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn main() { + let _x = u64x2(1, 0) << id(u64x2(64, 0)); +} diff --git a/src/test/run-fail/overflowing-simd-lsh-4.rs b/src/test/run-fail/overflowing-simd-lsh-4.rs new file mode 100644 index 00000000000..2fc177ced9d --- /dev/null +++ b/src/test/run-fail/overflowing-simd-lsh-4.rs @@ -0,0 +1,49 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'shift operation overflowed' +// compile-flags: -C debug-assertions + +// This function is checking that our automatic truncation does not +// sidestep the overflow checking. + +#![feature(core_simd)] + +use std::simd::i8x16; + +fn eq_i8x16(i8x16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15): i8x16, + i8x16(y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13, y14, y15): i8x16) + -> bool +{ + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) + && (x4 == y4) && (x5 == y5) && (x6 == y6) && (x7 == y7) + && (x8 == y8) && (x9 == y9) && (x10 == y10) && (x11 == y11) + && (x12 == y12) && (x13 == y13) && (x14 == y14) && (x15 == y15) +} + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn main() { + // this signals overflow when checking is on + let x = i8x16(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + << id(i8x16(17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + // ... but when checking is off, the fallback will truncate the + // input to its lower three bits (= 1). Note that this is *not* + // the behavior of the x86 processor for 8- and 16-bit types, + // but it is necessary to avoid undefined behavior from LLVM. + // + // We check that here, by ensuring the result has only been + // shifted by one place; if overflow checking is turned off, then + // this assertion will pass (and the compiletest driver will + // report that the test did not produce the error expected above). + assert!(eq_i8x16(x, i8x16(2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))); +} diff --git a/src/test/run-fail/overflowing-simd-rsh-1.rs b/src/test/run-fail/overflowing-simd-rsh-1.rs new file mode 100644 index 00000000000..dffd627a084 --- /dev/null +++ b/src/test/run-fail/overflowing-simd-rsh-1.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'shift operation overflowed' +// compile-flags: -C debug-assertions + +#![feature(core_simd)] + +use std::simd::i32x4; + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn main() { + let _x = i32x4(-1, 0, 0, 0) >> id(i32x4(32, 0, 0, 0)); +} diff --git a/src/test/run-fail/overflowing-simd-rsh-2.rs b/src/test/run-fail/overflowing-simd-rsh-2.rs new file mode 100644 index 00000000000..2852e147f83 --- /dev/null +++ b/src/test/run-fail/overflowing-simd-rsh-2.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'shift operation overflowed' +// compile-flags: -C debug-assertions + +#![feature(core_simd)] + +use std::simd::i32x4; + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn main() { + let _x = i32x4(0, 0, 0, -1) >> id(i32x4(0, 0, 0, -1)); +} diff --git a/src/test/run-fail/overflowing-simd-rsh-3.rs b/src/test/run-fail/overflowing-simd-rsh-3.rs new file mode 100644 index 00000000000..057eaa3f91a --- /dev/null +++ b/src/test/run-fail/overflowing-simd-rsh-3.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'shift operation overflowed' +// compile-flags: -C debug-assertions + +#![feature(core_simd)] + +use std::simd::i64x2; + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn main() { + let _x = i64x2(0, -1) >> id(i64x2(0, 64)); +} diff --git a/src/test/run-fail/overflowing-simd-rsh-4.rs b/src/test/run-fail/overflowing-simd-rsh-4.rs new file mode 100644 index 00000000000..a850fff6919 --- /dev/null +++ b/src/test/run-fail/overflowing-simd-rsh-4.rs @@ -0,0 +1,49 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'shift operation overflowed' +// compile-flags: -C debug-assertions + +// This function is checking that our (type-based) automatic +// truncation does not sidestep the overflow checking. + +#![feature(core_simd)] + +use std::simd::i8x16; + +fn eq_i8x16(i8x16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15): i8x16, + i8x16(y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13, y14, y15): i8x16) + -> bool +{ + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) + && (x4 == y4) && (x5 == y5) && (x6 == y6) && (x7 == y7) + && (x8 == y8) && (x9 == y9) && (x10 == y10) && (x11 == y11) + && (x12 == y12) && (x13 == y13) && (x14 == y14) && (x15 == y15) +} + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn main() { + // this signals overflow when checking is on + let x = i8x16(2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + >> id(i8x16(17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + // ... but when checking is off, the fallback will truncate the + // input to its lower three bits (= 1). Note that this is *not* + // the behavior of the x86 processor for 8- and 16-bit types, + // but it is necessary to avoid undefined behavior from LLVM. + // + // We check that here, by ensuring the result is not zero; if + // overflow checking is turned off, then this assertion will pass + // (and the compiletest driver will report that the test did not + // produce the error expected above). + assert!(eq_i8x16(x, i8x16(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))); +} diff --git a/src/test/run-pass/issue-24258.rs b/src/test/run-pass/issue-24258.rs new file mode 100644 index 00000000000..f56c3fefbe8 --- /dev/null +++ b/src/test/run-pass/issue-24258.rs @@ -0,0 +1,26 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C debug-assertions + +#![feature(core_simd)] + +use std::simd::u32x4; + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn eq_u32x4(u32x4(x0, x1, x2, x3): u32x4, u32x4(y0, y1, y2, y3): u32x4) -> bool { + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) +} + +fn main() { + assert!(eq_u32x4(u32x4(1, 1, 1, 1) << id(u32x4(1, 1, 1, 1)), u32x4(2, 2, 2, 2))); +} diff --git a/src/test/run-pass/simd-shift-near-oflo.rs b/src/test/run-pass/simd-shift-near-oflo.rs new file mode 100644 index 00000000000..fee4637f07f --- /dev/null +++ b/src/test/run-pass/simd-shift-near-oflo.rs @@ -0,0 +1,154 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C debug-assertions + +// Check that we do *not* overflow on a number of edge cases. +// (compare with test/run-fail/overflowing-{lsh,rsh}*.rs) + +#![feature(core_simd)] + +use std::simd::{i8x16, i16x8, i32x4, i64x2, u8x16, u16x8, u32x4, u64x2}; + +// (Work around constant-evaluation) +fn id(x: T) -> T { x } + +fn single_i8x16(x: i8) -> i8x16 { i8x16(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x) } +fn single_u8x16(x: u8) -> u8x16 { u8x16(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x) } +fn single_i16x8(x: i16) -> i16x8 { i16x8(0, 0, 0, 0, 0, 0, 0, x) } +fn single_u16x8(x: u16) -> u16x8 { u16x8(0, 0, 0, 0, 0, 0, 0, x) } +fn single_i32x4(x: i32) -> i32x4 { i32x4(0, 0, 0, x) } +fn single_u32x4(x: u32) -> u32x4 { u32x4(0, 0, 0, x) } +fn single_i64x2(x: i64) -> i64x2 { i64x2(0, x) } +fn single_u64x2(x: u64) -> u64x2 { u64x2(0, x) } + +fn eq_i8x16(i8x16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15): i8x16, + i8x16(y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13, y14, y15): i8x16) + -> bool { + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) + && (x4 == y4) && (x5 == y5) && (x6 == y6) && (x7 == y7) + && (x8 == y8) && (x9 == y9) && (x10 == y10) && (x11 == y11) + && (x12 == y12) && (x13 == y13) && (x14 == y14) && (x15 == y15) +} +fn eq_u8x16(u8x16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15): u8x16, + u8x16(y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13, y14, y15): u8x16) + -> bool { + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) + && (x4 == y4) && (x5 == y5) && (x6 == y6) && (x7 == y7) + && (x8 == y8) && (x9 == y9) && (x10 == y10) && (x11 == y11) + && (x12 == y12) && (x13 == y13) && (x14 == y14) && (x15 == y15) +} +fn eq_i16x8(i16x8(x0, x1, x2, x3, x4, x5, x6, x7): i16x8, + i16x8(y0, y1, y2, y3, y4, y5, y6, y7): i16x8) -> bool { + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) + && (x4 == y4) && (x5 == y5) && (x6 == y6) && (x7 == y7) +} +fn eq_u16x8(u16x8(x0, x1, x2, x3, x4, x5, x6, x7): u16x8, + u16x8(y0, y1, y2, y3, y4, y5, y6, y7): u16x8) -> bool { + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) + && (x4 == y4) && (x5 == y5) && (x6 == y6) && (x7 == y7) +} +fn eq_i32x4(i32x4(x0, x1, x2, x3): i32x4, i32x4(y0, y1, y2, y3): i32x4) -> bool { + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) +} +fn eq_u32x4(u32x4(x0, x1, x2, x3): u32x4, u32x4(y0, y1, y2, y3): u32x4) -> bool { + (x0 == y0) && (x1 == y1) && (x2 == y2) && (x3 == y3) +} +fn eq_i64x2(i64x2(x0, x1): i64x2, i64x2(y0, y1): i64x2) -> bool { + (x0 == y0) && (x1 == y1) +} +fn eq_u64x2(u64x2(x0, x1): u64x2, u64x2(y0, y1): u64x2) -> bool { + (x0 == y0) && (x1 == y1) +} + +fn main() { + test_left_shift(); + test_right_shift(); +} + +fn test_left_shift() { + // negative rhs can panic, but values in [0,N-1] are okay for iN + + macro_rules! tests { + ($single:ident, $eq:ident, $max_rhs:expr, $expect:expr) => { { + let x = $single(1) << id($single(0)); + assert!($eq(x, $single(1))); + let x = $single(1) << id($single($max_rhs)); + assert!($eq(x, $single($expect))); + // high-order bits on LHS are silently discarded without panic. + let x = $single(3) << id($single($max_rhs)); + assert!($eq(x, $single($expect))); + } } + } + + let x = single_i8x16(1) << id(single_i8x16(0)); + assert!(eq_i8x16(x, single_i8x16(1))); + let x = single_u8x16(1) << id(single_u8x16(0)); + assert!(eq_u8x16(x, single_u8x16(1))); + let x = single_i8x16(1) << id(single_i8x16(7)); + assert!(eq_i8x16(x, single_i8x16(std::i8::MIN))); + let x = single_u8x16(1) << id(single_u8x16(7)); + assert!(eq_u8x16(x, single_u8x16(0x80))); + // high-order bits on LHS are silently discarded without panic. + let x = single_i8x16(3) << id(single_i8x16(7)); + assert!(eq_i8x16(x, single_i8x16(std::i8::MIN))); + let x = single_u8x16(3) << id(single_u8x16(7)); + assert!(eq_u8x16(x, single_u8x16(0x80))); + + // above is (approximately) expanded from: + tests!(single_i8x16, eq_i8x16, 7, std::i8::MIN); + tests!(single_u8x16, eq_u8x16, 7, 0x80_u8); + + tests!(single_i16x8, eq_i16x8, 15, std::i16::MIN); + tests!(single_u16x8, eq_u16x8, 15, 0x8000_u16); + + tests!(single_i32x4, eq_i32x4, 31, std::i32::MIN); + tests!(single_u32x4, eq_u32x4, 31, 0x8000_0000_u32); + + tests!(single_i64x2, eq_i64x2, 63, std::i64::MIN); + tests!(single_u64x2, eq_u64x2, 63, 0x8000_0000_0000_0000_u64); +} + +fn test_right_shift() { + // negative rhs can panic, but values in [0,N-1] are okay for iN + + macro_rules! tests { + ($single_i:ident, $eq_i:ident, $single_u:ident, $eq_u:ident, + $max_rhs:expr, $signbit_i:expr, $highbit_i:expr, $highbit_u:expr) => { { + let x = $single_i(1) >> id($single_i(0)); + assert!($eq_i(x, $single_i(1))); + let x = $single_u(1) >> id($single_u(0)); + assert!($eq_u(x, $single_u(1))); + let x = $single_u($highbit_i) >> id($single_u($max_rhs-1)); + assert!($eq_u(x, $single_u(1))); + let x = $single_u($highbit_u) >> id($single_u($max_rhs)); + assert!($eq_u(x, $single_u(1))); + // sign-bit is carried by arithmetic right shift + let x = $single_i($signbit_i) >> id($single_i($max_rhs)); + assert!($eq_i(x, $single_i(-1))); + // low-order bits on LHS are silently discarded without panic. + let x = $single_u($highbit_i + 1) >> id($single_u($max_rhs-1)); + assert!($eq_u(x, $single_u(1))); + let x = $single_u($highbit_u + 1) >> id($single_u($max_rhs)); + assert!($eq_u(x, $single_u(1))); + let x = $single_i($signbit_i + 1) >> id($single_i($max_rhs)); + assert!($eq_i(x, $single_i(-1))); + } } + } + + tests!(single_i8x16, eq_i8x16, single_u8x16, eq_u8x16, + 7, std::i8::MIN, 0x40_u8, 0x80_u8); + tests!(single_i16x8, eq_i16x8, single_u16x8, eq_u16x8, + 15, std::i16::MIN, 0x4000_u16, 0x8000_u16); + tests!(single_i32x4, eq_i32x4, single_u32x4, eq_u32x4, + 31, std::i32::MIN, 0x4000_0000_u32, 0x8000_0000_u32); + tests!(single_i64x2, eq_i64x2, single_u64x2, eq_u64x2, + 63, std::i64::MIN, 0x4000_0000_0000_0000_u64, 0x8000_0000_0000_0000_u64); +}