Auto merge of #49249 - gnzlbg:simd_minmax, r=alexcrichton

implement minmax intrinsics

This adds the `simd_{fmin,fmax}` intrinsics, which do a vertical (lane-wise) `min`/`max` for floating point vectors that's equivalent to Rust's `min`/`max` for `f32`/`f64`.

It might make sense to make `{f32,f64}::{min,max}` use the `minnum` and `minmax` intrinsics as well.

---

~~HELP: I need some help with these. Either I should go to sleep or there must be something that I must be missing. AFAICT I am calling the `maxnum` builder correctly, yet rustc/LLVM seem to insert a call to `llvm.minnum` there instead...~~ EDIT: Rust's LLVM version is too old :/
This commit is contained in:
bors 2018-03-27 04:46:32 +00:00
commit 31ae4f9fde
7 changed files with 148 additions and 1 deletions

View File

@ -1248,6 +1248,9 @@ extern "C" {
IsNaN: bool)
-> ValueRef;
pub fn LLVMRustBuildMinNum(B: BuilderRef, LHS: ValueRef, LHS: ValueRef) -> ValueRef;
pub fn LLVMRustBuildMaxNum(B: BuilderRef, LHS: ValueRef, LHS: ValueRef) -> ValueRef;
pub fn LLVMBuildIsNull(B: BuilderRef, Val: ValueRef, Name: *const c_char) -> ValueRef;
pub fn LLVMBuildIsNotNull(B: BuilderRef, Val: ValueRef, Name: *const c_char) -> ValueRef;
pub fn LLVMBuildPtrDiff(B: BuilderRef,

View File

@ -917,6 +917,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
pub fn minnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
self.count_insn("minnum");
unsafe {
let instr = llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs);
if instr.is_null() {
bug!("LLVMRustBuildMinNum is not available in LLVM version < 6.0");
}
instr
}
}
pub fn maxnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
self.count_insn("maxnum");
unsafe {
let instr = llvm::LLVMRustBuildMaxNum(self.llbuilder, lhs, rhs);
if instr.is_null() {
bug!("LLVMRustBuildMaxNum is not available in LLVM version < 6.0");
}
instr
}
}
pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef {
self.count_insn("select");
unsafe {

View File

@ -1432,6 +1432,8 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
simd_and: TyUint, TyInt => and;
simd_or: TyUint, TyInt => or;
simd_xor: TyUint, TyInt => xor;
simd_fmax: TyFloat => maxnum;
simd_fmin: TyFloat => minnum;
}
span_bug!(span, "unknown SIMD intrinsic");
}

View File

@ -355,7 +355,8 @@ pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
"simd_add" | "simd_sub" | "simd_mul" | "simd_rem" |
"simd_div" | "simd_shl" | "simd_shr" |
"simd_and" | "simd_or" | "simd_xor" => {
"simd_and" | "simd_or" | "simd_xor" |
"simd_fmin" | "simd_fmax" => {
(1, vec![param(0), param(0)], param(0))
}
"simd_insert" => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),

View File

@ -1503,3 +1503,23 @@ LLVMBuildExactUDiv(LLVMBuilderRef B, LLVMValueRef LHS,
return wrap(unwrap(B)->CreateExactUDiv(unwrap(LHS), unwrap(RHS), Name));
}
#endif
#if LLVM_VERSION_GE(6, 0)
extern "C" LLVMValueRef
LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
return wrap(unwrap(B)->CreateMinNum(unwrap(LHS),unwrap(RHS)));
}
extern "C" LLVMValueRef
LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS)));
}
#else
extern "C" LLVMValueRef
LLVMRustBuildMinNum(LLVMBuilderRef, LLVMValueRef, LLVMValueRef) {
return nullptr;
}
extern "C" LLVMValueRef
LLVMRustBuildMaxNum(LLVMBuilderRef, LLVMValueRef, LLVMValueRef) {
return nullptr;
}
#endif

View File

@ -0,0 +1,43 @@
// Copyright 2016 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.
// ignore-emscripten
// min-llvm-version 6.0
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
#![feature(repr_simd, platform_intrinsics)]
#[allow(non_camel_case_types)]
#[repr(simd)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct f32x4(pub f32, pub f32, pub f32, pub f32);
extern "platform-intrinsic" {
fn simd_fmin<T>(x: T, y: T) -> T;
fn simd_fmax<T>(x: T, y: T) -> T;
}
// CHECK-LABEL: @fmin
#[no_mangle]
pub unsafe fn fmin(a: f32x4, b: f32x4) -> f32x4 {
// CHECK: call <4 x float> @llvm.minnum.v4f32
simd_fmin(a, b)
}
// FIXME(49261)
// // C_HECK-LABEL: @fmax
// #[no_mangle]
// pub unsafe fn fmax(a: f32x4, b: f32x4) -> f32x4 {
// // C_HECK: call <4 x float> @llvm.maxnum.v4f32
// simd_fmax(a, b)
// }

View File

@ -0,0 +1,57 @@
// 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.
// ignore-emscripten
// min-llvm-version 6.0
// error-pattern: panicked
// Test that the simd_f{min,max} intrinsics produce the correct results.
#![feature(repr_simd, platform_intrinsics)]
#[allow(non_camel_case_types)]
#[repr(simd)]
#[derive(Copy, Clone, PartialEq, Debug)]
struct f32x4(pub f32, pub f32, pub f32, pub f32);
extern "platform-intrinsic" {
fn simd_fmin<T>(x: T, y: T) -> T;
fn simd_fmax<T>(x: T, y: T) -> T;
}
fn main() {
let x = f32x4(1.0, 2.0, 3.0, 4.0);
let y = f32x4(2.0, 1.0, 4.0, 3.0);
let nan = ::std::f32::NAN;
let n = f32x4(nan, nan, nan, nan);
unsafe {
let min0 = simd_fmin(x, y);
let min1 = simd_fmin(y, x);
assert_eq!(min0, min1);
let e = f32x4(1.0, 1.0, 3.0, 3.0);
assert_eq!(min0, e);
let minn = simd_fmin(x, n);
assert_eq!(minn, x);
let minn = simd_fmin(y, n);
assert_eq!(minn, y);
// FIXME(49261)
let max0 = simd_fmax(x, y);
let max1 = simd_fmax(y, x);
assert_eq!(max0, max1);
let e = f32x4(2.0, 2.0, 4.0, 4.0);
assert_eq!(max0, e);
let maxn = simd_fmax(x, n);
assert_eq!(maxn, x);
let maxn = simd_fmax(y, n);
assert_eq!(maxn, y);
}
}