diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index 600cb6491ad2f..dba2e918f6f3a 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -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, diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index 5e2d32b359698..db803ca8209d9 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -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 { diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 2be29c0836075..5c67f8091141b 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -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"); } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index da37cec31cf44..377e3a891840f 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -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)), diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 0ef9643f4cab4..df8602d0803a4 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -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 diff --git a/src/test/codegen/simd-intrinsic-float-minmax.rs b/src/test/codegen/simd-intrinsic-float-minmax.rs new file mode 100644 index 0000000000000..6663b841808f1 --- /dev/null +++ b/src/test/codegen/simd-intrinsic-float-minmax.rs @@ -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 or the MIT license +// , 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(x: T, y: T) -> T; + fn simd_fmax(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) +// } diff --git a/src/test/run-fail/simd-intrinsic-float-minmax.rs b/src/test/run-fail/simd-intrinsic-float-minmax.rs new file mode 100644 index 0000000000000..f4fb8e12250b5 --- /dev/null +++ b/src/test/run-fail/simd-intrinsic-float-minmax.rs @@ -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 or the MIT license +// , 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(x: T, y: T) -> T; + fn simd_fmax(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); + } +}