Skip to content

Commit 913adba

Browse files
authored
Rollup merge of rust-lang#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 :/
2 parents 414e1d8 + d87b403 commit 913adba

File tree

7 files changed

+147
-1
lines changed

7 files changed

+147
-1
lines changed

src/librustc_llvm/ffi.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,9 @@ extern "C" {
12421242
IsNaN: bool)
12431243
-> ValueRef;
12441244

1245+
pub fn LLVMRustBuildMinNum(B: BuilderRef, LHS: ValueRef, LHS: ValueRef) -> ValueRef;
1246+
pub fn LLVMRustBuildMaxNum(B: BuilderRef, LHS: ValueRef, LHS: ValueRef) -> ValueRef;
1247+
12451248
pub fn LLVMBuildIsNull(B: BuilderRef, Val: ValueRef, Name: *const c_char) -> ValueRef;
12461249
pub fn LLVMBuildIsNotNull(B: BuilderRef, Val: ValueRef, Name: *const c_char) -> ValueRef;
12471250
pub fn LLVMBuildPtrDiff(B: BuilderRef,

src/librustc_trans/builder.rs

+21
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
910910
}
911911
}
912912

913+
pub fn minnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
914+
self.count_insn("minnum");
915+
unsafe {
916+
let instr = llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs);
917+
if instr.is_null() {
918+
bug!("LLVMRustBuildMinNum is not available in LLVM version < 6.0");
919+
}
920+
instr
921+
}
922+
}
923+
pub fn maxnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
924+
self.count_insn("maxnum");
925+
unsafe {
926+
let instr = llvm::LLVMRustBuildMaxNum(self.llbuilder, lhs, rhs);
927+
if instr.is_null() {
928+
bug!("LLVMRustBuildMaxNum is not available in LLVM version < 6.0");
929+
}
930+
instr
931+
}
932+
}
933+
913934
pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef {
914935
self.count_insn("select");
915936
unsafe {

src/librustc_trans/intrinsic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1426,6 +1426,8 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
14261426
simd_and: TyUint, TyInt => and;
14271427
simd_or: TyUint, TyInt => or;
14281428
simd_xor: TyUint, TyInt => xor;
1429+
simd_fmax: TyFloat => maxnum;
1430+
simd_fmin: TyFloat => minnum;
14291431
}
14301432
span_bug!(span, "unknown SIMD intrinsic");
14311433
}

src/librustc_typeck/check/intrinsic.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,8 @@ pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
355355
}
356356
"simd_add" | "simd_sub" | "simd_mul" | "simd_rem" |
357357
"simd_div" | "simd_shl" | "simd_shr" |
358-
"simd_and" | "simd_or" | "simd_xor" => {
358+
"simd_and" | "simd_or" | "simd_xor" |
359+
"simd_fmin" | "simd_fmax" => {
359360
(1, vec![param(0), param(0)], param(0))
360361
}
361362
"simd_insert" => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),

src/rustllvm/RustWrapper.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -1492,3 +1492,23 @@ LLVMRustBuildVectorReduceFMax(LLVMBuilderRef, LLVMValueRef, bool) {
14921492
return nullptr;
14931493
}
14941494
#endif
1495+
1496+
#if LLVM_VERSION_GE(6, 0)
1497+
extern "C" LLVMValueRef
1498+
LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
1499+
return wrap(unwrap(B)->CreateMinNum(unwrap(LHS),unwrap(RHS)));
1500+
}
1501+
extern "C" LLVMValueRef
1502+
LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
1503+
return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS)));
1504+
}
1505+
#else
1506+
extern "C" LLVMValueRef
1507+
LLVMRustBuildMinNum(LLVMBuilderRef, LLVMValueRef, LLVMValueRef) {
1508+
return nullptr;
1509+
}
1510+
extern "C" LLVMValueRef
1511+
LLVMRustBuildMaxNum(LLVMBuilderRef, LLVMValueRef, LLVMValueRef) {
1512+
return nullptr;
1513+
}
1514+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-emscripten
12+
// min-llvm-version 6.0
13+
14+
// compile-flags: -C no-prepopulate-passes
15+
16+
#![crate_type = "lib"]
17+
18+
#![feature(repr_simd, platform_intrinsics)]
19+
#[allow(non_camel_case_types)]
20+
21+
#[repr(simd)]
22+
#[derive(Copy, Clone, PartialEq, Debug)]
23+
pub struct f32x4(pub f32, pub f32, pub f32, pub f32);
24+
25+
extern "platform-intrinsic" {
26+
fn simd_fmin<T>(x: T, y: T) -> T;
27+
fn simd_fmax<T>(x: T, y: T) -> T;
28+
}
29+
30+
// CHECK-LABEL: @fmin
31+
#[no_mangle]
32+
pub unsafe fn fmin(a: f32x4, b: f32x4) -> f32x4 {
33+
// CHECK: call <4 x float> @llvm.minnum.v4f32
34+
simd_fmin(a, b)
35+
}
36+
37+
// FIXME(49261)
38+
// // CHECK-LABEL: @fmax
39+
// #[no_mangle]
40+
// pub unsafe fn fmax(a: f32x4, b: f32x4) -> f32x4 {
41+
// // CHECK: call <4 x float> @llvm.maxnum.v4f32
42+
// simd_fmax(a, b)
43+
// }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-emscripten
12+
// min-llvm-version 6.0
13+
14+
// Test that the simd_f{min,max} intrinsics produce the correct results.
15+
16+
#![feature(repr_simd, platform_intrinsics)]
17+
#[allow(non_camel_case_types)]
18+
19+
#[repr(simd)]
20+
#[derive(Copy, Clone, PartialEq, Debug)]
21+
struct f32x4(pub f32, pub f32, pub f32, pub f32);
22+
23+
extern "platform-intrinsic" {
24+
fn simd_fmin<T>(x: T, y: T) -> T;
25+
fn simd_fmax<T>(x: T, y: T) -> T;
26+
}
27+
28+
fn main() {
29+
let x = f32x4(1.0, 2.0, 3.0, 4.0);
30+
let y = f32x4(2.0, 1.0, 4.0, 3.0);
31+
let nan = ::std::f32::NAN;
32+
let n = f32x4(nan, nan, nan, nan);
33+
34+
unsafe {
35+
let min0 = simd_fmin(x, y);
36+
let min1 = simd_fmin(y, x);
37+
assert_eq!(min0, min1);
38+
let e = f32x4(1.0, 1.0, 3.0, 3.0);
39+
assert_eq!(min0, e);
40+
let minn = simd_fmin(x, n);
41+
assert_eq!(minn, x);
42+
let minn = simd_fmin(y, n);
43+
assert_eq!(minn, y);
44+
45+
// FIXME(49261)
46+
let max0 = simd_fmax(x, y);
47+
let max1 = simd_fmax(y, x);
48+
assert_eq!(max0, max1);
49+
let e = f32x4(2.0, 2.0, 4.0, 4.0);
50+
assert_eq!(max0, e);
51+
let maxn = simd_fmax(x, n);
52+
assert_eq!(maxn, x);
53+
let maxn = simd_fmax(y, n);
54+
assert_eq!(maxn, y);
55+
}
56+
}

0 commit comments

Comments
 (0)