Skip to content

Commit

Permalink
Support checked Shl/Shr on SIMD types
Browse files Browse the repository at this point in the history
  • Loading branch information
David Stygstra committed Jun 20, 2015
1 parent 4b42cbd commit 875f50a
Show file tree
Hide file tree
Showing 11 changed files with 468 additions and 11 deletions.
63 changes: 52 additions & 11 deletions src/librustc_trans/trans/expr.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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,
Expand Down
23 changes: 23 additions & 0 deletions 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 <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));
}
23 changes: 23 additions & 0 deletions 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 <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));
}
23 changes: 23 additions & 0 deletions 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 <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));
}
49 changes: 49 additions & 0 deletions 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 <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)));
}
23 changes: 23 additions & 0 deletions 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 <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));
}
23 changes: 23 additions & 0 deletions 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 <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));
}
23 changes: 23 additions & 0 deletions 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 <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));
}
49 changes: 49 additions & 0 deletions 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 <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)));
}
26 changes: 26 additions & 0 deletions 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 <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 comments on commit 875f50a

Please sign in to comment.