Auto merge of #26198 - stygstra:issue-24258, r=huonw
When overflow checking on `<<` and `>>` was added for integers, the `<<` and `>>` operations broke for SIMD types (`u32x4`, `i16x8`, etc.). This PR implements checked shifts on SIMD types. Fixes #24258.
This commit is contained in:
commit
cca281781f
|
@ -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;
|
||||
|
@ -2458,12 +2458,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),
|
||||
|
@ -2479,9 +2477,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
|
||||
|
@ -2507,7 +2542,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 {
|
||||
|
@ -2519,8 +2561,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,
|
||||
|
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(core_simd)]
|
||||
|
||||
use std::simd::i32x4;
|
||||
|
||||
// (Work around constant-evaluation)
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
let _x = i32x4(1, 0, 0, 0) << id(i32x4(32, 0, 0, 0));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(core_simd)]
|
||||
|
||||
use std::simd::i32x4;
|
||||
|
||||
// (Work around constant-evaluation)
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
let _x = i32x4(1, 0, 0, 0) << id(i32x4(-1, 0, 0, 0));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(core_simd)]
|
||||
|
||||
use std::simd::u64x2;
|
||||
|
||||
// (Work around constant-evaluation)
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
let _x = u64x2(1, 0) << id(u64x2(64, 0));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread '<main>' 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<T>(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)));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(core_simd)]
|
||||
|
||||
use std::simd::i32x4;
|
||||
|
||||
// (Work around constant-evaluation)
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
let _x = i32x4(-1, 0, 0, 0) >> id(i32x4(32, 0, 0, 0));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(core_simd)]
|
||||
|
||||
use std::simd::i32x4;
|
||||
|
||||
// (Work around constant-evaluation)
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
let _x = i32x4(0, 0, 0, -1) >> id(i32x4(0, 0, 0, -1));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(core_simd)]
|
||||
|
||||
use std::simd::i64x2;
|
||||
|
||||
// (Work around constant-evaluation)
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
let _x = i64x2(0, -1) >> id(i64x2(0, 64));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread '<main>' 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<T>(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)));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T>(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)));
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T>(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);
|
||||
}
|
Loading…
Reference in New Issue